breakpoints; thread/frame selection that should work; TODO: is slow
This commit is contained in:
57
example_cus.txt
Normal file
57
example_cus.txt
Normal file
@@ -0,0 +1,57 @@
|
||||
Symctx: Module: file = "/usr/lib64/ld-linux-x86-64.so.2", arch = "x86_64"
|
||||
CompileUnit: id = {0x0000012c}, file = "/usr/src/debug/glibc-2.37-4.fc38.x86_64/elf/rtld.c", language = "c11"
|
||||
Function: id = {0x0002de35}, name = "_dl_start", range = [0x000000000001c050-0x000000000001c786)
|
||||
FuncType: id = {0x0002de35}, byte-size = 0, decl = rtld.c:520:1, compiler_type = "Elf64_Addr (void *)"
|
||||
Blocks: id = {0x0002de35}, range = [0x0001c050-0x0001c786)
|
||||
LineEntry: [0x000000000001c050-0x000000000001c084): /usr/src/debug/glibc-2.37-4.fc38.x86_64/elf/rtld.c:521:1, is_prologue_end = TRUE
|
||||
Symbol: id = {0x000000c5}, range = [0x000000000001c050-0x000000000001c786), name="_dl_start"
|
||||
|
||||
Module: (x86_64) /usr/lib64/ld-linux-x86-64.so.2
|
||||
CU: 0x2c7a330: CompileUnit{0x0000012c}, language = "c11", file = '/usr/src/debug/glibc-2.37-4.fc38.x86_64/elf/rtld.c'
|
||||
0x2570850: Function{0x0002de35}, demangled = _dl_start, type = 0x7f645c1a2ad0
|
||||
0x2570888: Block{0x0002de35}, ranges = [0x0001c050-0x0001c786)
|
||||
0x29f93d0: Block{0x0002de65}, parent = {0x0002de35}, ranges = [0x0001c2a8-0x0001c500) [0x0001c528-0x0001c528) [0x0001c5e4-0x0001c610) [0x0001c63e-0x0001c64a) [0x0001c6ad-0x0001c6cc)
|
||||
0x291f950: Block{0x0002de80}, parent = {0x0002de65}, ranges = [0x0001c2a8-0x0001c327)
|
||||
0x25658f0: Block{0x0002ded3}, parent = {0x0002de80}, ranges = [0x0001c2d8-0x0001c2db) [0x0001c2df-0x0001c2e6) [0x0001c2eb-0x0001c310) [0x0001c314-0x0001c31b)
|
||||
0x25710a0: Block{0x0002deec}, parent = {0x0002ded3}, ranges = [0x0001c2f2-0x0001c310)
|
||||
0x2571120: Block{0x0002df0b}, parent = {0x0002de65}, ranges = [0x0001c327-0x0001c500) [0x0001c5e4-0x0001c610) [0x0001c63e-0x0001c64a) [0x0001c6ad-0x0001c6cc)
|
||||
0x2578790: Block{0x0002df61}, parent = {0x0002df0b}, ranges = [0x0001c39c-0x0001c3fb) [0x0001c63e-0x0001c645)
|
||||
0x2578830: Block{0x0002df8a}, parent = {0x0002df0b}, ranges = [0x0001c3fb-0x0001c500) [0x0001c5e4-0x0001c610) [0x0001c645-0x0001c64a) [0x0001c6ad-0x0001c6cc)
|
||||
0x2570390: Block{0x0002dfa5}, parent = {0x0002df8a}, name = "elf_dynamic_do_Rela", decl = do-rel.h:43:1, ranges = [0x0001c403-0x0001c414) [0x0001c417-0x0001c4de) [0x0001c4e7-0x0001c500) [0x0001c5e4-0x0001c610) [0x0001c6ad-0x0001c6cc)
|
||||
0x2584e30: Block{0x0002e006}, parent = {0x0002dfa5}, ranges = [0x0001c403-0x0001c414) [0x0001c417-0x0001c4de) [0x0001c4e7-0x0001c500) [0x0001c5e4-0x0001c610) [0x0001c6ad-0x0001c6cc)
|
||||
0x2571df0: Block{0x0002e045}, parent = {0x0002e006}, ranges = [0x0001c403-0x0001c410) [0x0001c470-0x0001c4d5) [0x0001c5e4-0x0001c610)
|
||||
0x2571e70: Block{0x0002e062}, parent = {0x0002e045}, name = "elf_machine_rela", decl = dl-machine.h:245:1, ranges = [0x0001c403-0x0001c410) [0x0001c47f-0x0001c482) [0x0001c48a-0x0001c4d5) [0x0001c5e4-0x0001c610)
|
||||
0x2579eb0: Block{0x0002e0b3}, parent = {0x0002e062}, ranges = [0x0001c403-0x0001c410) [0x0001c47f-0x0001c482) [0x0001c48a-0x0001c4d5) [0x0001c5e4-0x0001c610)
|
||||
0x29b2080: Block{0x0002e0c0}, parent = {0x0002e0b3}, ranges = [0x0001c403-0x0001c410) [0x0001c48f-0x0001c4d5) [0x0001c5e4-0x0001c610)
|
||||
0x29b21c0: Block{0x0002e0e1}, parent = {0x0002e006}, name = "dl_relocate_ld", decl = _itoa.h:77:1, ranges = [0x0001c434-0x0001c434) [0x0001c4ee-0x0001c500)
|
||||
0x257fc90: Block{0x0002e107}, parent = {0x0002e006}, name = "elf_machine_rela_relative", decl = dl-machine.h:475:1, ranges = [0x0001c44e-0x0001c45c) [0x0001c460-0x0001c466) [0x0001c6ad-0x0001c6cc)
|
||||
0x256e590: Block{0x0002e134}, parent = {0x0002e107}, ranges = [0x0001c44e-0x0001c45c) [0x0001c460-0x0001c466) [0x0001c6ad-0x0001c6cc)
|
||||
0x256e670: Block{0x0002e185}, parent = {0x0002df0b}, name = "dl_relocate_ld", decl = _itoa.h:77:1, ranges = [0x0001c32b-0x0001c32d) [0x0001c33a-0x0001c33d) [0x0001c35b-0x0001c35b)
|
||||
0x256e7b0: Block{0x0002e1aa}, parent = {0x0002de65}, name = "elf_machine_runtime_setup", decl = dl-machine.h:61:1, ranges = [0x0001c2a8-0x0001c2a8)
|
||||
0x256e8d0: Block{0x0002e1f4}, parent = {0x0002de35}, name = "rtld_timer_start", decl = rtld.c:83:1, ranges = [0x0001c08f-0x0001c091) [0x0001c098-0x0001c09c) [0x0001c0a3-0x0001c0a6) [0x0001c0ad-0x0001c0b4)
|
||||
0x256c4d0: Block{0x0002e216}, parent = {0x0002de35}, name = "elf_machine_load_address", decl = dl-machine.h:43:1, ranges = [0x0001c0b4-0x0001c0b4)
|
||||
0x256c5d0: Block{0x0002e236}, parent = {0x0002de35}, name = "elf_machine_dynamic", decl = dl-machine.h:51:1, ranges = [0x0001c0b4-0x0001c0b4)
|
||||
0x256c6f0: Block{0x0002e250}, parent = {0x0002de35}, name = "elf_get_dynamic_info", decl = get-dynamic-info.h:29:1, ranges = [0x0001c0b4-0x0001c0bb) [0x0001c0c2-0x0001c2a8) [0x0001c500-0x0001c528) [0x0001c610-0x0001c63e) [0x0001c64a-0x0001c688) [0x0001c6cc-0x0001c786)
|
||||
0x256c830: Block{0x0002e287}, parent = {0x0002e250}, ranges = [0x0001c0b4-0x0001c0bb) [0x0001c0c2-0x0001c2a8) [0x0001c500-0x0001c528) [0x0001c610-0x0001c63e) [0x0001c64a-0x0001c688) [0x0001c6cc-0x0001c786)
|
||||
0x2584640: Block{0x0002e297}, parent = {0x0002e287}, ranges = [0x0001c0b4-0x0001c0bb) [0x0001c0c2-0x0001c158) [0x0001c500-0x0001c528)
|
||||
0x2584740: Block{0x0002e2ab}, parent = {0x0002e297}, ranges = [0x0001c0cb-0x0001c0fd) [0x0001c10a-0x0001c158) [0x0001c500-0x0001c528)
|
||||
0x2584820: Block{0x0002e2bf}, parent = {0x0002e287}, name = "dl_relocate_ld", decl = _itoa.h:77:1, ranges = [0x0001c158-0x0001c15f)
|
||||
0x2584920: Block{0x0002e2ea}, parent = {0x0002e287}, ranges = [0x0001c173-0x0001c216) [0x0001c610-0x0001c610) [0x0001c64a-0x0001c656)
|
||||
0x25849e0: Block{0x0002e4a2}, parent = {0x0002de35}, name = "_dl_start_final", decl = rtld.c:451:1, ranges = [0x0001c534-0x0001c5ac) [0x0001c688-0x0001c6a8)
|
||||
0x2584b20: Block{0x0002e4c3}, parent = {0x0002e4a2}, ranges = [0x0001c534-0x0001c5ac) [0x0001c688-0x0001c6a8)
|
||||
0x2495c10: Block{0x0002e4d3}, parent = {0x0002e4c3}, name = "rtld_timer_start", decl = rtld.c:83:1, ranges = [0x0001c542-0x0001c544) [0x0001c54e-0x0001c55c)
|
||||
0x2495d70: Block{0x0002e4f5}, parent = {0x0002e4c3}, ranges = [0x0001c688-0x0001c6a8)
|
||||
0x2495df0: Block{0x0002e50a}, parent = {0x0002e4f5}, name = "rtld_timer_stop", decl = rtld.c:89:1, ranges = [0x0001c688-0x0001c68a) [0x0001c691-0x0001c6a3)
|
||||
0x2495f50: Block{0x0002e536}, parent = {0x0002e50a}, ranges = [0x0001c688-0x0001c68a) [0x0001c691-0x0001c6a3)
|
||||
|
||||
|
||||
Module: (x86_64) /home/klee/projects/dbgui/tmp/main
|
||||
CU: 0x7f26dc135960: CompileUnit{0x00000001}, language = "c++14", file = '/home/klee/projects/dbgui/tmp/sec.cpp'
|
||||
0x7f26dc13e5d0: Function{0x0000046f}, mangled = _Z9helper_fnv, demangled = helper_fn(), type = 0x7f26dc145a80
|
||||
0x7f26dc13e608: Block{0x0000046f}, ranges = [0x0040115a-0x00401176)
|
||||
|
||||
|
||||
Module: (x86_64) /home/klee/projects/dbgui/tmp/main
|
||||
CU: 0x7f26dc135870: CompileUnit{0x00000000}, language = "c++14", file = '/home/klee/projects/dbgui/tmp/main.cpp'
|
||||
0x7f26dc13cea0: Function{0x000000a0}, demangled = main, type = 0x7f26dc19cbb0
|
||||
0x7f26dc13ced8: Block{0x000000a0}, ranges = [0x00401126-0x0040115a)
|
||||
@@ -58,3 +58,17 @@ void Backend::send_data_result(BackToFront::DataResult &&info)
|
||||
BackToFront::MsgType::data_result, std::move(info));
|
||||
this->send_msg(std::move(msg));
|
||||
}
|
||||
void Backend::send_selected_thread_changed(uint16_t idx)
|
||||
{
|
||||
auto msg = std::make_unique<BackToFront::Msg>(
|
||||
BackToFront::MsgType::selected_thread_changed,
|
||||
BackToFront::SelectedThreadChanged{.idx = idx});
|
||||
this->send_msg(std::move(msg));
|
||||
}
|
||||
void Backend::send_selected_frame_changed(uint16_t idx)
|
||||
{
|
||||
auto msg = std::make_unique<BackToFront::Msg>(
|
||||
BackToFront::MsgType::selected_frame_changed,
|
||||
BackToFront::SelectedFrameChanged{.idx = idx});
|
||||
this->send_msg(std::move(msg));
|
||||
}
|
||||
@@ -18,9 +18,9 @@ namespace dbgui::backend
|
||||
virtual void start() = 0;
|
||||
|
||||
// returns whether the command was submitted
|
||||
virtual bool step_into() = 0;
|
||||
virtual bool step_into(bool source_step) = 0;
|
||||
// TODO: allow these two to not be implemented or emulated by the base class or frontend?
|
||||
virtual bool step_over() = 0;
|
||||
virtual bool step_over(bool source_step) = 0;
|
||||
virtual bool step_out() = 0;
|
||||
|
||||
virtual void cont() = 0;
|
||||
@@ -30,8 +30,13 @@ namespace dbgui::backend
|
||||
virtual void remove_data_node(uint64_t id) = 0;
|
||||
|
||||
virtual void add_breakpoint(uint64_t addr, size_t id) = 0;
|
||||
virtual bool add_breakpoint(const char *file, uint32_t line, size_t id) = 0;
|
||||
virtual void remove_breakpoint(size_t id) = 0;
|
||||
|
||||
// TODO: need generation to prevent race?
|
||||
virtual void select_thread(uint16_t idx) = 0;
|
||||
virtual void select_frame(uint16_t idx) = 0;
|
||||
|
||||
auto retrieve_msg_for_frontend()
|
||||
-> std::optional<std::unique_ptr<BackToFront::Msg>>
|
||||
{
|
||||
@@ -63,6 +68,8 @@ namespace dbgui::backend
|
||||
void send_frame_changed(BackToFront::FrameChanged &&);
|
||||
void send_frame_removed(BackToFront::FrameRemoved &&);
|
||||
void send_data_result(BackToFront::DataResult &&);
|
||||
void send_selected_thread_changed(uint16_t idx);
|
||||
void send_selected_frame_changed(uint16_t idx);
|
||||
|
||||
private:
|
||||
std::mutex _back_front_msg_mutex;
|
||||
|
||||
@@ -8,12 +8,16 @@
|
||||
#include <lldb/API/SBValue.h>
|
||||
#include <lldb/API/SBInstructionList.h>
|
||||
#include <lldb/API/SBInstruction.h>
|
||||
#include <lldb/API/SBBreakpoint.h>
|
||||
#include <lldb/API/SBBreakpointLocation.h>
|
||||
#include <filesystem>
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
using namespace dbgui::backend;
|
||||
|
||||
namespace
|
||||
@@ -24,9 +28,15 @@ LLDBBackend::~LLDBBackend() {}
|
||||
LLDBBackend::LLDBBackend(std::string filename)
|
||||
{
|
||||
_filename = filename;
|
||||
char buf[256];
|
||||
buf[0] = '\0';
|
||||
pthread_getname_np(pthread_self(), buf, sizeof(buf));
|
||||
// name lldb threads
|
||||
pthread_setname_np(pthread_self(), "lldb_internal_thread");
|
||||
lldb::SBDebugger::Initialize();
|
||||
_instance = lldb::SBDebugger::Create(false);
|
||||
_target = _instance.CreateTarget(filename.c_str());
|
||||
pthread_setname_np(pthread_self(), buf);
|
||||
}
|
||||
|
||||
void LLDBBackend::start()
|
||||
@@ -36,9 +46,19 @@ void LLDBBackend::start()
|
||||
|
||||
auto error = lldb::SBError();
|
||||
auto listener = lldb::SBListener();
|
||||
char buf[256];
|
||||
buf[0] = '\0';
|
||||
{
|
||||
pthread_getname_np(pthread_self(), buf, sizeof(buf));
|
||||
// name lldb threads
|
||||
pthread_setname_np(pthread_self(), "lldb_internal_thread");
|
||||
}
|
||||
_process = _target.Launch(listener, argv, nullptr, nullptr, nullptr, nullptr,
|
||||
cwd.c_str(), lldb::LaunchFlags::eLaunchFlagNone,
|
||||
true, error);
|
||||
{
|
||||
pthread_setname_np(pthread_self(), buf);
|
||||
}
|
||||
|
||||
_msg_thread = std::thread{[this]() {
|
||||
auto ptr = this->shared_from_this();
|
||||
@@ -48,6 +68,7 @@ void LLDBBackend::start()
|
||||
|
||||
void LLDBBackend::run_msg_loop()
|
||||
{
|
||||
pthread_setname_np(pthread_self(), "msg_loop");
|
||||
std::thread event_thread{[this]() { this->wait_for_debug_events(); }};
|
||||
|
||||
while (true)
|
||||
@@ -59,6 +80,7 @@ void LLDBBackend::run_msg_loop()
|
||||
void LLDBBackend::wait_for_debug_events()
|
||||
{
|
||||
using namespace lldb;
|
||||
pthread_setname_np(pthread_self(), "lldb_msg_loop");
|
||||
auto listener = _instance.GetListener();
|
||||
|
||||
while (true)
|
||||
@@ -329,7 +351,38 @@ void LLDBBackend::dump_threads()
|
||||
printf("Symctx: %.*s\n", static_cast<int>(stream.GetSize()),
|
||||
stream.GetData());
|
||||
|
||||
auto list = _target.FindTypes("test::MyType");
|
||||
/*auto mod = symctx.GetModule();
|
||||
auto cu = symctx.GetCompileUnit();
|
||||
auto block = symctx.GetBlock();
|
||||
auto le = symctx.GetLineEntry();
|
||||
|
||||
stream.Clear();
|
||||
mod.GetDescription(stream);
|
||||
printf("Module: %.*s\n", static_cast<int>(stream.GetSize()),
|
||||
stream.GetData());
|
||||
|
||||
stream.Clear();
|
||||
cu.GetDescription(stream);
|
||||
printf("CU: %.*s\n", static_cast<int>(stream.GetSize()), stream.GetData());
|
||||
|
||||
stream.Clear();
|
||||
block.GetDescription(stream);
|
||||
printf("Block: %.*s\n", static_cast<int>(stream.GetSize()),
|
||||
stream.GetData());
|
||||
|
||||
if (!le.IsValid())
|
||||
{
|
||||
printf("LE not valid\n");
|
||||
}
|
||||
/*uint32_t line, col;
|
||||
line = le.GetLine();
|
||||
col = le.GetColumn();
|
||||
printf("LE: %u:%u\n", line, col);*
|
||||
stream.Clear();
|
||||
le.GetDescription(stream);
|
||||
printf("LE: %.*s\n", static_cast<int>(stream.GetSize()), stream.GetData());*/
|
||||
|
||||
/*auto list = _target.FindTypes("test::MyType");
|
||||
printf("List len: %lu\n", list.GetSize());
|
||||
auto len = list.GetSize();
|
||||
for (uint32_t i = 0; i < len; ++i)
|
||||
@@ -340,9 +393,9 @@ void LLDBBackend::dump_threads()
|
||||
|
||||
printf("Type %u: %.*s\n", i, static_cast<int>(stream.GetSize()),
|
||||
stream.GetData());
|
||||
}
|
||||
}*/
|
||||
|
||||
auto pc = frame.GetPC();
|
||||
/*auto pc = frame.GetPC();
|
||||
|
||||
stream.Clear();
|
||||
//auto sc =
|
||||
@@ -366,7 +419,7 @@ void LLDBBackend::dump_threads()
|
||||
{
|
||||
start = pc;
|
||||
end = start + 0x100;
|
||||
}
|
||||
}*/
|
||||
|
||||
/*auto buf = std::vector<uint8_t>{};
|
||||
buf.resize(end - start);
|
||||
@@ -399,7 +452,7 @@ void LLDBBackend::dump_threads()
|
||||
}
|
||||
}
|
||||
|
||||
bool LLDBBackend::step_into()
|
||||
bool LLDBBackend::step_into(bool source_step)
|
||||
{
|
||||
std::lock_guard g{_data_lock};
|
||||
if (!_process)
|
||||
@@ -414,15 +467,18 @@ bool LLDBBackend::step_into()
|
||||
}
|
||||
|
||||
// TODO: figure out what the runmodes mean
|
||||
// TODO: this is source step
|
||||
// thread.StepInto();
|
||||
|
||||
if (source_step)
|
||||
{
|
||||
thread.StepInto();
|
||||
} else
|
||||
{
|
||||
thread.StepInstruction(false);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LLDBBackend::step_over()
|
||||
bool LLDBBackend::step_over(bool source_step)
|
||||
{
|
||||
std::lock_guard g{_data_lock};
|
||||
if (!_process)
|
||||
@@ -436,9 +492,13 @@ bool LLDBBackend::step_over()
|
||||
return false;
|
||||
}
|
||||
|
||||
//thread.StepOver();
|
||||
|
||||
if (source_step)
|
||||
{
|
||||
thread.StepOver();
|
||||
} else
|
||||
{
|
||||
thread.StepInstruction(true);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -457,6 +517,7 @@ bool LLDBBackend::step_out()
|
||||
return false;
|
||||
}
|
||||
thread.StepOut();
|
||||
// TODO: allow step out of frame?
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -485,6 +546,54 @@ void LLDBBackend::pause()
|
||||
_process->Stop();
|
||||
}
|
||||
|
||||
void LLDBBackend::select_thread(uint16_t idx)
|
||||
{
|
||||
std::lock_guard g{_data_lock};
|
||||
if (!_process)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_threads.size() <= idx)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
printf("Set selected thread: %lu (%u)", _threads[idx].id, idx);
|
||||
_process->SetSelectedThreadByID(_threads[idx].id);
|
||||
auto thread = _process->GetSelectedThread();
|
||||
thread.SetSelectedFrame(0);
|
||||
|
||||
this->check_thread_changes();
|
||||
this->check_frame_changes();
|
||||
// TODO: these can be a lot more selective
|
||||
this->check_data_changes();
|
||||
}
|
||||
|
||||
void LLDBBackend::select_frame(uint16_t idx)
|
||||
{
|
||||
std::lock_guard g{_data_lock};
|
||||
if (!_process)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_frames.size() <= idx)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto thread = _process->GetSelectedThread();
|
||||
uint64_t id = thread.GetThreadID();
|
||||
printf("ID: %lu\n", id);
|
||||
thread.SetSelectedFrame(_frames[idx].lldb_idx);
|
||||
uint64_t id2 = thread.GetThreadID();
|
||||
printf("ID2: %lu\n", id2);
|
||||
this->check_frame_changes();
|
||||
// TODO: these can be a lot more selective
|
||||
this->check_data_changes();
|
||||
}
|
||||
|
||||
void LLDBBackend::check_reg_changes()
|
||||
{
|
||||
auto thread = _process->GetSelectedThread();
|
||||
@@ -569,6 +678,7 @@ void LLDBBackend::check_thread_changes()
|
||||
this->send_thread_removed(BackToFront::ThreadRemoved{i});
|
||||
}
|
||||
_threads.clear();
|
||||
this->send_selected_thread_changed(0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -576,6 +686,7 @@ void LLDBBackend::check_thread_changes()
|
||||
// TODO: work with SBThread::GetIndexID instead of relying
|
||||
// on the index order?
|
||||
uint16_t cache_idx = 0;
|
||||
auto sel_thread = _process->GetSelectedThread();
|
||||
for (uint32_t i = 0; i < len; ++i)
|
||||
{
|
||||
auto thread = _process->GetThreadAtIndex(i);
|
||||
@@ -584,14 +695,44 @@ void LLDBBackend::check_thread_changes()
|
||||
continue;
|
||||
}
|
||||
|
||||
if (thread.GetThreadID() == sel_thread.GetThreadID())
|
||||
{
|
||||
if (_selected_thread != cache_idx)
|
||||
{
|
||||
_selected_thread = cache_idx;
|
||||
this->send_selected_thread_changed(cache_idx);
|
||||
// reset selected frame
|
||||
if (_selected_frame != 0)
|
||||
{
|
||||
this->send_selected_frame_changed(0);
|
||||
thread.SetSelectedFrame(0);
|
||||
}
|
||||
_selected_frame = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// this causes lldb to compute a proper stacktrace apparently
|
||||
thread.GetCurrentExceptionBacktrace();
|
||||
|
||||
auto frame = thread.GetFrameAtIndex(0);
|
||||
if (_threads.size() <= cache_idx)
|
||||
{
|
||||
_threads.push_back(
|
||||
Thread{.id = thread.GetThreadID(),
|
||||
auto thread_name = std::string{};
|
||||
auto lldb_thread_name = thread.GetName();
|
||||
if (lldb_thread_name)
|
||||
{
|
||||
// it is not valid to construct a string from a nullptr?
|
||||
thread_name = lldb_thread_name;
|
||||
}
|
||||
auto frame_name = std::string{};
|
||||
if (auto frame_str = frame.GetDisplayFunctionName(); frame_str)
|
||||
{
|
||||
frame_name = frame_str;
|
||||
}
|
||||
_threads.push_back(Thread{.id = thread.GetThreadID(),
|
||||
.ip = frame.GetPC(),
|
||||
.name = thread.GetName(),
|
||||
.cur_display_fn = frame.GetDisplayFunctionName()});
|
||||
.name = thread_name,
|
||||
.cur_display_fn = frame_name});
|
||||
|
||||
auto &info = _threads.back();
|
||||
switch (thread.GetStopReason())
|
||||
@@ -642,13 +783,21 @@ void LLDBBackend::check_thread_changes()
|
||||
cache_entry.ip = frame.GetPC();
|
||||
send_change = true;
|
||||
}
|
||||
auto name = std::string_view{thread.GetName()};
|
||||
auto name = std::string_view{};
|
||||
if (auto lldb_name = thread.GetName(); lldb_name)
|
||||
{
|
||||
name = lldb_name;
|
||||
}
|
||||
if (name != cache_entry.name)
|
||||
{
|
||||
change_entry.name = name;
|
||||
send_change = true;
|
||||
}
|
||||
auto display_name = std::string_view{frame.GetDisplayFunctionName()};
|
||||
auto display_name = std::string_view{};
|
||||
if (auto lldb_name = frame.GetDisplayFunctionName(); lldb_name)
|
||||
{
|
||||
display_name = lldb_name;
|
||||
}
|
||||
if (display_name != cache_entry.cur_display_fn)
|
||||
{
|
||||
change_entry.cur_display_fn = display_name;
|
||||
@@ -703,7 +852,7 @@ void LLDBBackend::check_thread_changes()
|
||||
for (size_t i = 0; i < rem_count; ++i)
|
||||
{
|
||||
this->send_thread_removed(
|
||||
BackToFront::ThreadRemoved{_threads.size() - i - 1});
|
||||
BackToFront::ThreadRemoved{_threads.size() - 1});
|
||||
_threads.pop_back();
|
||||
}
|
||||
}
|
||||
@@ -716,19 +865,26 @@ void LLDBBackend::check_frame_changes()
|
||||
if (!thread.IsValid())
|
||||
{
|
||||
// TODO: cleanup
|
||||
printf("Current thread not valid\n");
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t id = thread.GetThreadID();
|
||||
printf("ID3: %lu\n", id);
|
||||
|
||||
size_t len = thread.GetNumFrames();
|
||||
uint16_t cache_idx = 0;
|
||||
size_t selected_idx = 0;
|
||||
printf("Num frames: %lu\n", len);
|
||||
for (size_t i = 0; i < len; ++i)
|
||||
{
|
||||
auto frame = thread.GetFrameAtIndex(i);
|
||||
if (!frame.IsValid())
|
||||
{
|
||||
printf("Frame %lu invalid\n", i);
|
||||
continue;
|
||||
}
|
||||
printf("Looking at frame %lu\n", i);
|
||||
|
||||
if (thread.GetSelectedFrame().IsEqual(frame))
|
||||
{
|
||||
@@ -736,14 +892,23 @@ void LLDBBackend::check_frame_changes()
|
||||
}
|
||||
|
||||
auto ip = frame.GetPC();
|
||||
auto name = std::string_view{frame.GetDisplayFunctionName()};
|
||||
auto name = std::string_view{};
|
||||
if (auto lldb_name = frame.GetDisplayFunctionName(); lldb_name)
|
||||
{
|
||||
// dont construct from nullptr
|
||||
name = lldb_name;
|
||||
}
|
||||
printf(" IP: %lu, Name: %.*s\n", ip, static_cast<int>(name.size()),
|
||||
name.data());
|
||||
if (_frames.size() <= cache_idx)
|
||||
{
|
||||
_frames.push_back(Frame{
|
||||
.ip = ip,
|
||||
.lldb_idx = i,
|
||||
.display_name = std::string{name},
|
||||
});
|
||||
|
||||
printf("Frame %u (%lu) is new\n", cache_idx, i);
|
||||
this->send_frame_changed(BackToFront::FrameChanged{
|
||||
.ip = ip, .idx = cache_idx, .display_name = std::string{name}});
|
||||
cache_idx++;
|
||||
@@ -751,22 +916,40 @@ void LLDBBackend::check_frame_changes()
|
||||
}
|
||||
|
||||
auto &cache_entry = _frames[cache_idx];
|
||||
printf(" Cached IP: %lu, Name: %s\n", cache_entry.ip,
|
||||
cache_entry.display_name.c_str());
|
||||
if (cache_entry.ip != ip || cache_entry.display_name != name)
|
||||
{
|
||||
printf("Frame %u (%lu) changed\n", cache_idx, i);
|
||||
this->send_frame_changed(BackToFront::FrameChanged{
|
||||
.ip = ip, .idx = cache_idx, .display_name = std::string{name}});
|
||||
cache_entry.ip = ip;
|
||||
if (cache_entry.display_name != name)
|
||||
{
|
||||
cache_entry.display_name = name;
|
||||
}
|
||||
}
|
||||
if (cache_entry.lldb_idx != i)
|
||||
{
|
||||
cache_entry.lldb_idx = i;
|
||||
}
|
||||
cache_idx++;
|
||||
}
|
||||
|
||||
if (_selected_frame != selected_idx)
|
||||
{
|
||||
this->send_selected_frame_changed(selected_idx);
|
||||
_selected_frame = selected_idx;
|
||||
}
|
||||
|
||||
printf("cache_idx: %u frame_size: %lu\n", cache_idx, _frames.size());
|
||||
if (_frames.size() > cache_idx)
|
||||
{
|
||||
// here cache_idx == num_frames
|
||||
size_t rem_count = _frames.size() - cache_idx;
|
||||
for (size_t i = 0; i < rem_count; ++i)
|
||||
{
|
||||
this->send_frame_removed(
|
||||
BackToFront::FrameRemoved{_frames.size() - i - 1});
|
||||
this->send_frame_removed(BackToFront::FrameRemoved{_frames.size() - 1});
|
||||
_frames.pop_back();
|
||||
}
|
||||
}
|
||||
@@ -786,6 +969,7 @@ void LLDBBackend::add_data_node(const data::DataNode &node)
|
||||
_data_dag.add_node(node.id);
|
||||
|
||||
// add child nodes
|
||||
// TODO: make this somehow automatic
|
||||
switch (node.type)
|
||||
{
|
||||
using enum data::DataNode::Type;
|
||||
@@ -804,6 +988,17 @@ void LLDBBackend::add_data_node(const data::DataNode &node)
|
||||
_data_dag.add_edge(node.id, src_id);
|
||||
break;
|
||||
}
|
||||
case line_entry:
|
||||
{
|
||||
auto src_id = std::get<data::LineEntry>(node.data).src_id;
|
||||
if (!_data_dag.nodes.contains(src_id))
|
||||
{
|
||||
printf("Invalid add sequence\n");
|
||||
exit(1);
|
||||
}
|
||||
_data_dag.add_edge(node.id, src_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_dag_linear_valid = false;
|
||||
@@ -934,6 +1129,30 @@ dbgui::data::DataResult LLDBBackend::calc_data_res(const data::DataNode &node)
|
||||
.type = data::TypeInfo{.type = data::TypeInfo::Type::u64},
|
||||
.data = _reg_sets[info.set].values[info.idx]};
|
||||
}
|
||||
case frame_ip:
|
||||
{
|
||||
auto thread = _process->GetSelectedThread();
|
||||
auto frame = thread.GetSelectedFrame();
|
||||
if (!thread.IsValid() || !frame.IsValid())
|
||||
{
|
||||
return data::DataResult{
|
||||
.id = node.id,
|
||||
.success = false,
|
||||
};
|
||||
}
|
||||
|
||||
uint64_t pc = frame.GetPC();
|
||||
std::vector<uint8_t> out{};
|
||||
out.resize(sizeof(uint64_t));
|
||||
*reinterpret_cast<uint64_t *>(out.data()) = pc;
|
||||
|
||||
return data::DataResult{
|
||||
.id = node.id,
|
||||
.success = true,
|
||||
.type = data::TypeInfo{.type = data::TypeInfo::Type::u64},
|
||||
.data = std::move(out),
|
||||
};
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -961,6 +1180,15 @@ dbgui::data::DataResult LLDBBackend::calc_data_res(const data::DataNode &node)
|
||||
auto sc = _target.ResolveSymbolContextForAddress(
|
||||
SBAddress{pc, _target}, eSymbolContextFunction | eSymbolContextSymbol);
|
||||
|
||||
auto le = sc.GetLineEntry();
|
||||
if (le.IsValid())
|
||||
{
|
||||
SBStream stream{};
|
||||
le.GetDescription(stream);
|
||||
printf("LE: %.*s\n", static_cast<int>(stream.GetSize()),
|
||||
stream.GetData());
|
||||
}
|
||||
|
||||
uint64_t start, end;
|
||||
if (sc.GetFunction().IsValid())
|
||||
{
|
||||
@@ -1007,9 +1235,21 @@ dbgui::data::DataResult LLDBBackend::calc_data_res(const data::DataNode &node)
|
||||
for (size_t i = 0; i < inst_list.GetSize(); ++i)
|
||||
{
|
||||
auto inst = inst_list.GetInstructionAtIndex(i);
|
||||
const auto mnem = std::string_view{inst.GetMnemonic(_target)};
|
||||
const auto op = std::string_view{inst.GetOperands(_target)};
|
||||
const auto comm = std::string_view{inst.GetComment(_target)};
|
||||
auto mnem = std::string_view{};
|
||||
auto op = std::string_view{};
|
||||
auto comm = std::string_view{};
|
||||
if (auto mnem_str = inst.GetMnemonic(_target); mnem_str)
|
||||
{
|
||||
mnem = mnem_str;
|
||||
}
|
||||
if (auto op_str = inst.GetOperands(_target); op_str)
|
||||
{
|
||||
op = op_str;
|
||||
}
|
||||
if (auto comm_str = inst.GetComment(_target); comm_str)
|
||||
{
|
||||
comm = comm_str;
|
||||
}
|
||||
auto addr = inst.GetAddress().GetLoadAddress(_target);
|
||||
const auto len = inst.GetByteSize();
|
||||
|
||||
@@ -1017,6 +1257,12 @@ dbgui::data::DataResult LLDBBackend::calc_data_res(const data::DataNode &node)
|
||||
//inst.GetDescription(stream);
|
||||
//printf("Got inst: %.*s\n", stream.GetSize(), stream.GetData());
|
||||
|
||||
if (comm.size() > 255)
|
||||
{
|
||||
// TODO: better handling
|
||||
comm = comm.substr(0, 255);
|
||||
}
|
||||
|
||||
if (mnem.size() > 255 || op.size() > 255 || comm.size() > 255)
|
||||
{
|
||||
printf("Instruction length limits exceeded:\n");
|
||||
@@ -1042,6 +1288,77 @@ dbgui::data::DataResult LLDBBackend::calc_data_res(const data::DataNode &node)
|
||||
std::copy(comm.begin(), comm.end(), out.begin() + insert_idx);
|
||||
}
|
||||
|
||||
return data::DataResult{
|
||||
.id = node.id,
|
||||
.success = true,
|
||||
.type = data::TypeInfo{.type = data::TypeInfo::Type::custom},
|
||||
.data = std::move(out)};
|
||||
}
|
||||
case line_entry:
|
||||
{
|
||||
using namespace lldb;
|
||||
size_t addr_id = std::get<data::LineEntry>(node.data).src_id;
|
||||
const auto res_it =
|
||||
std::find_if(_cached_data_results.begin(), _cached_data_results.end(),
|
||||
[addr_id](const auto &el) { return el.id == addr_id; });
|
||||
if (res_it == _cached_data_results.end() || !res_it->success)
|
||||
{
|
||||
return data::DataResult{.id = node.id, .success = false};
|
||||
}
|
||||
|
||||
// TODO: for now only accept u64
|
||||
if (res_it->type.type != data::TypeInfo::Type::u64)
|
||||
{
|
||||
return data::DataResult{.id = node.id, .success = false};
|
||||
}
|
||||
|
||||
const auto addr = *reinterpret_cast<uint64_t *>(res_it->data.data());
|
||||
|
||||
auto sc = _target.ResolveSymbolContextForAddress(SBAddress{addr, _target},
|
||||
eSymbolContextLineEntry);
|
||||
|
||||
auto le = sc.GetLineEntry();
|
||||
auto file_spec = le.GetFileSpec();
|
||||
|
||||
/*auto stream = SBStream{};
|
||||
sc.GetDescription(stream);
|
||||
printf("SC DBG: %.*s\n", static_cast<int>(stream.GetSize()),
|
||||
stream.GetData());
|
||||
stream.Clear();
|
||||
le.GetDescription(stream);
|
||||
printf("LE DBG: %.*s\n", static_cast<int>(stream.GetSize()),
|
||||
stream.GetData());
|
||||
stream.Clear();
|
||||
file_spec.GetDescription(stream);
|
||||
printf("FS DBG: %.*s\n", static_cast<int>(stream.GetSize()),
|
||||
stream.GetData());*/
|
||||
if (!le.IsValid() || !file_spec.IsValid())
|
||||
{
|
||||
return data::DataResult{.id = node.id, .success = false};
|
||||
}
|
||||
|
||||
// TODO: for now the instlist is serialized in a custom format
|
||||
// use a type structure for it later?
|
||||
// it's basically an array of
|
||||
// struct LineEntry {
|
||||
// uint32_t line_no;
|
||||
// uint32_t name_len;
|
||||
// char file_name[];
|
||||
// };
|
||||
|
||||
// we can't know how long the path actually is without at least calling GetPath twice
|
||||
// because it will return the smaller of the buffer size or actual path len
|
||||
char path_buf[512];
|
||||
uint32_t path_len = file_spec.GetPath(path_buf, sizeof(path_buf));
|
||||
|
||||
printf("Calculated LE: %.*s\n", static_cast<int>(path_len), path_buf);
|
||||
|
||||
std::vector<uint8_t> out;
|
||||
out.resize(8 + path_len);
|
||||
*reinterpret_cast<uint32_t *>(out.data()) = le.GetLine();
|
||||
*reinterpret_cast<uint32_t *>(out.data() + 4) = path_len;
|
||||
std::memcpy(out.data() + 8, path_buf, path_len);
|
||||
|
||||
return data::DataResult{
|
||||
.id = node.id,
|
||||
.success = true,
|
||||
@@ -1074,6 +1391,53 @@ void LLDBBackend::add_breakpoint(uint64_t addr, size_t id)
|
||||
_breakpoints.push_back(Breakpoint{.id = id, .lldb_id = bp.GetID()});
|
||||
}
|
||||
|
||||
bool LLDBBackend::add_breakpoint(const char *file, uint32_t line, size_t id)
|
||||
{
|
||||
using namespace lldb;
|
||||
std::lock_guard g{_data_lock};
|
||||
if (!_process)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (std::find_if(_breakpoints.begin(), _breakpoints.end(),
|
||||
[id](const auto &el) { return el.id == id; })
|
||||
!= _breakpoints.end())
|
||||
{
|
||||
printf("Trying to register same breakpoint id\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// TODO: try false?
|
||||
auto file_spec = SBFileSpec{file, true};
|
||||
auto mod_list = SBFileSpecList{};
|
||||
|
||||
auto bp =
|
||||
_target.BreakpointCreateByLocation(file_spec, line, 0, 0, mod_list, true);
|
||||
if (!bp.IsValid())
|
||||
{
|
||||
printf("BP not valid\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
/*auto num = bp.GetNumLocations();
|
||||
auto res_num = bp.GetNumResolvedLocations();
|
||||
printf("Num: %lu, Res: %lu\n", num, res_num);
|
||||
auto stream = SBStream{};
|
||||
for (size_t i = 0; i < num; ++i)
|
||||
{
|
||||
stream.Clear();
|
||||
auto loc = bp.GetLocationAtIndex(i);
|
||||
loc.GetDescription(stream, eDescriptionLevelVerbose);
|
||||
|
||||
printf("Loc %lu: %.*s\n", i, static_cast<int>(stream.GetSize()),
|
||||
stream.GetData());
|
||||
}*/
|
||||
|
||||
_breakpoints.push_back(Breakpoint{.id = id, .lldb_id = bp.GetID()});
|
||||
return true;
|
||||
}
|
||||
|
||||
void LLDBBackend::remove_breakpoint(size_t id)
|
||||
{
|
||||
std::lock_guard g{_data_lock};
|
||||
|
||||
@@ -37,6 +37,7 @@ namespace dbgui::backend
|
||||
struct Frame
|
||||
{
|
||||
uint64_t ip;
|
||||
size_t lldb_idx;
|
||||
std::string display_name;
|
||||
};
|
||||
|
||||
@@ -52,8 +53,8 @@ namespace dbgui::backend
|
||||
|
||||
void start() override;
|
||||
|
||||
bool step_into() override;
|
||||
bool step_over() override;
|
||||
bool step_into(bool source_step) override;
|
||||
bool step_over(bool source_step) override;
|
||||
bool step_out() override;
|
||||
|
||||
void cont() override;
|
||||
@@ -63,8 +64,12 @@ namespace dbgui::backend
|
||||
void remove_data_node(uint64_t id) override;
|
||||
|
||||
void add_breakpoint(uint64_t addr, size_t id) override;
|
||||
bool add_breakpoint(const char *file, uint32_t line, size_t id) override;
|
||||
void remove_breakpoint(size_t id) override;
|
||||
|
||||
void select_thread(uint16_t idx);
|
||||
void select_frame(uint16_t idx);
|
||||
|
||||
private:
|
||||
void run_msg_loop();
|
||||
void wait_for_debug_events();
|
||||
@@ -95,6 +100,7 @@ namespace dbgui::backend
|
||||
std::vector<Thread> _threads = {};
|
||||
std::vector<Frame> _frames = {};
|
||||
uint16_t _selected_frame = 0;
|
||||
uint16_t _selected_thread = 0;
|
||||
|
||||
util::DAG _data_dag = {};
|
||||
std::vector<data::DataNode> _data_nodes = {};
|
||||
|
||||
12
src/data.h
12
src/data.h
@@ -42,6 +42,7 @@ namespace dbgui::data
|
||||
enum class Type : uint8_t
|
||||
{
|
||||
reg,
|
||||
frame_ip, // binds the selected frame
|
||||
// TODO: special IP/SP source? so that scope selection can apply to that?
|
||||
// variable,
|
||||
// const,
|
||||
@@ -65,21 +66,30 @@ namespace dbgui::data
|
||||
size_t src_id;
|
||||
};
|
||||
|
||||
struct LineEntry
|
||||
{
|
||||
// Node that provides address to resolve
|
||||
// must be u64
|
||||
size_t src_id;
|
||||
};
|
||||
|
||||
struct DataNode
|
||||
{
|
||||
enum class Type : uint8_t
|
||||
{
|
||||
data_source,
|
||||
disassemble,
|
||||
line_entry,
|
||||
};
|
||||
|
||||
size_t id;
|
||||
Type type;
|
||||
std::variant<DataSource, Disassemble> data;
|
||||
std::variant<DataSource, Disassemble, LineEntry> data;
|
||||
};
|
||||
|
||||
// TODO: this should allow only updating part of arrays
|
||||
// and moving in it
|
||||
// TODO: also allow inlining of small data (e.g smallvec or union)
|
||||
struct DataResult
|
||||
{
|
||||
// TODO: needs indicator that data was failed to be retrieved
|
||||
|
||||
@@ -25,6 +25,7 @@ Frontend::Frontend()
|
||||
_windows.push_back(Window::create_frames(window_id++));
|
||||
_windows.push_back(Window::create_disas(window_id++));
|
||||
_windows.push_back(Window::create_bp(window_id++));
|
||||
_windows.push_back(Window::create_source(window_id++));
|
||||
}
|
||||
|
||||
void Frontend::run_frame()
|
||||
@@ -132,7 +133,6 @@ void Frontend::draw_header()
|
||||
{
|
||||
if (ImGui::BeginMenuBar())
|
||||
{
|
||||
const auto orig_cursor_x = ImGui::GetCursorPosX();
|
||||
if (this->target)
|
||||
{
|
||||
switch (this->target->state)
|
||||
@@ -168,9 +168,6 @@ void Frontend::draw_header()
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
|
||||
// TODO: this depends on font-size, we just want to make sure the the other buttons don't flicker even if the state is changed
|
||||
ImGui::SetCursorPosX(orig_cursor_x + 71.f);
|
||||
|
||||
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical, 2.f);
|
||||
|
||||
const auto is_paused =
|
||||
@@ -185,14 +182,14 @@ void Frontend::draw_header()
|
||||
|
||||
if (ImGui::Button("Step Over"))
|
||||
{
|
||||
if (this->target->backend->step_over())
|
||||
if (this->target->backend->step_over(!this->target->step_instruction))
|
||||
{
|
||||
// TODO: already disable the UI into running mode
|
||||
}
|
||||
}
|
||||
if (ImGui::Button("Step Into"))
|
||||
{
|
||||
if (this->target->backend->step_into())
|
||||
if (this->target->backend->step_into(!this->target->step_instruction))
|
||||
{
|
||||
// TODO: already disable the UI into running mode
|
||||
}
|
||||
@@ -225,6 +222,28 @@ void Frontend::draw_header()
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
|
||||
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical, 2.f);
|
||||
|
||||
if (!this->target)
|
||||
{
|
||||
ImGui::Button("Step Mode: Source");
|
||||
} else
|
||||
{
|
||||
if (this->target->step_instruction)
|
||||
{
|
||||
if (ImGui::Button("Step Mode: Instruction"))
|
||||
{
|
||||
this->target->step_instruction = false;
|
||||
}
|
||||
} else
|
||||
{
|
||||
if (ImGui::Button("Step Mode: Source"))
|
||||
{
|
||||
this->target->step_instruction = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndMenuBar();
|
||||
}
|
||||
ImGui::End();
|
||||
@@ -334,6 +353,25 @@ void Frontend::handle_msgs()
|
||||
{
|
||||
window.handle_data_res(result);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case selected_frame_changed:
|
||||
{
|
||||
if (this->target)
|
||||
{
|
||||
this->target->selected_frame =
|
||||
std::get<BackToFront::SelectedFrameChanged>(msg->data).idx;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case selected_thread_changed:
|
||||
{
|
||||
if (this->target)
|
||||
{
|
||||
this->target->selected_thread =
|
||||
std::get<BackToFront::SelectedThreadChanged>(msg->data).idx;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -386,7 +424,7 @@ void Frontend::handle_reg_change(const BackToFront::RegsChanged &info)
|
||||
void Frontend::handle_thread_remove(const BackToFront::ThreadRemoved &info)
|
||||
{
|
||||
auto &threads = this->target->threads;
|
||||
if (threads.size() >= info.idx)
|
||||
if (threads.size() <= info.idx)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -439,7 +477,7 @@ void Frontend::handle_thread_change(BackToFront::ThreadChange &&info)
|
||||
void Frontend::handle_frame_remove(const BackToFront::FrameRemoved &info)
|
||||
{
|
||||
auto &frames = this->target->frames;
|
||||
if (frames.size() >= info.idx)
|
||||
if (frames.size() <= info.idx)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -458,6 +496,8 @@ void Frontend::handle_frame_change(BackToFront::FrameChanged &&info)
|
||||
auto &frame = frames[info.idx];
|
||||
if (!frame)
|
||||
{
|
||||
printf("Adding new frame at %u with name '%s'\n", info.idx,
|
||||
info.display_name.c_str());
|
||||
frame = Target::Frame{
|
||||
.ip = info.ip,
|
||||
.display_name = info.display_name,
|
||||
@@ -465,6 +505,8 @@ void Frontend::handle_frame_change(BackToFront::FrameChanged &&info)
|
||||
return;
|
||||
}
|
||||
|
||||
printf("Updating frame at %u with name '%s'\n", info.idx,
|
||||
info.display_name.c_str());
|
||||
frame->ip = info.ip;
|
||||
frame->display_name = info.display_name;
|
||||
}
|
||||
@@ -41,11 +41,41 @@ namespace dbgui::frontend
|
||||
std::string display_name;
|
||||
};
|
||||
|
||||
// TODO: need a way for the backend to report where the breakpoint was actually placed
|
||||
// (might have been moved) or if it could be placed at all
|
||||
// iow let the backend resolve the address + possible file location for the breakpoint
|
||||
// and then display it
|
||||
// this should be needed anyways when you want to add expression-based breakpoints, e.g. 'main'
|
||||
struct Breakpoint
|
||||
{
|
||||
struct FileLoc
|
||||
{
|
||||
std::string name;
|
||||
uint32_t line;
|
||||
};
|
||||
|
||||
bool removed = false;
|
||||
// TODO: srcloc
|
||||
uint64_t addr;
|
||||
std::variant<uint64_t, FileLoc> data;
|
||||
|
||||
bool at_addr(uint64_t addr) const
|
||||
{
|
||||
return this->data.index() == 0
|
||||
&& std::get<uint64_t>(this->data) == addr;
|
||||
}
|
||||
|
||||
bool at_file_loc(std::string_view file, uint32_t line) const
|
||||
{
|
||||
if (this->data.index() != 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const auto &loc = std::get<FileLoc>(this->data);
|
||||
if (loc.line == line && loc.name == file)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
Target(std::string filename);
|
||||
@@ -55,6 +85,9 @@ namespace dbgui::frontend
|
||||
uint64_t id;
|
||||
size_t data_node_id = 0;
|
||||
Arch arch;
|
||||
bool step_instruction = false;
|
||||
uint16_t selected_thread = 0;
|
||||
uint16_t selected_frame = 0;
|
||||
|
||||
std::vector<RegSet> reg_sets;
|
||||
std::vector<std::optional<Thread>> threads;
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
#include "frontend.h"
|
||||
#include <algorithm>
|
||||
#include "imgui_internal.h"
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
|
||||
using namespace dbgui;
|
||||
using namespace dbgui::frontend;
|
||||
@@ -23,6 +25,9 @@ bool Window::draw(Frontend &frontend)
|
||||
case breakpoints:
|
||||
return std::get<BreakpointWindow>(this->data).draw(frontend);
|
||||
break;
|
||||
case source:
|
||||
return std::get<SourceWindow>(this->data).draw(frontend);
|
||||
break;
|
||||
default: printf("Unhandled window draw: %u\n", this->type); exit(1);
|
||||
}
|
||||
}
|
||||
@@ -73,6 +78,16 @@ Window Window::create_bp(size_t id)
|
||||
.data = BreakpointWindow{.id = id_str, .open = true}};
|
||||
}
|
||||
|
||||
Window Window::create_source(size_t id)
|
||||
{
|
||||
auto id_str = std::string{"Source##"};
|
||||
id_str.append(std::to_string(id));
|
||||
|
||||
return Window{.type = WindowType::source,
|
||||
.data =
|
||||
SourceWindow{.id = id_str, .open = true, .first = true}};
|
||||
}
|
||||
|
||||
bool RegWindow::draw(const Frontend &frontend)
|
||||
{
|
||||
//ImGui::SetNextWindowDockID(frontend.dock_id, ImGuiCond_Appearing);
|
||||
@@ -202,7 +217,8 @@ bool ThreadWindow::draw(const Frontend &frontend)
|
||||
}
|
||||
|
||||
if (ImGui::BeginTable("table", 3,
|
||||
ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg))
|
||||
ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg
|
||||
| ImGuiTableFlags_ScrollX))
|
||||
{
|
||||
ImGui::TableSetupColumn("ID");
|
||||
ImGui::TableSetupColumn("Name");
|
||||
@@ -222,6 +238,14 @@ bool ThreadWindow::draw(const Frontend &frontend)
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
|
||||
if (frontend.target->selected_thread == idx)
|
||||
{
|
||||
// TODO: style coloring
|
||||
const auto col =
|
||||
ImGui::GetColorU32(ImGui::GetStyleColorVec4(ImGuiCol_Button));
|
||||
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, col);
|
||||
}
|
||||
|
||||
std::snprintf(buf, sizeof(buf), "%lu", thread->id);
|
||||
ImGui::Text(buf);
|
||||
ImGui::TableNextColumn();
|
||||
@@ -237,6 +261,45 @@ bool ThreadWindow::draw(const Frontend &frontend)
|
||||
std::snprintf(buf, sizeof(buf), "%lX", thread->ip);
|
||||
ImGui::Text(buf);
|
||||
}
|
||||
|
||||
auto min_pos =
|
||||
ImGui::TableGetCellBgRect(ImGui::GetCurrentTable(), 0).GetTL();
|
||||
auto max_pos =
|
||||
ImGui::TableGetCellBgRect(ImGui::GetCurrentTable(), 2).GetBR();
|
||||
|
||||
/*if (idx & 1)
|
||||
{
|
||||
ImGui::GetWindowDrawList()->AddRect(min_pos, max_pos,
|
||||
IM_COL32(255, 0, 0, 255));
|
||||
} else
|
||||
{
|
||||
ImGui::GetWindowDrawList()->AddRect(min_pos, max_pos,
|
||||
IM_COL32(0, 0, 255, 255));
|
||||
}*/
|
||||
|
||||
// TODO: better scrollbar handling
|
||||
auto win_pos = ImGui::GetWindowPos();
|
||||
auto min = ImGui::GetWindowContentRegionMin();
|
||||
auto max = ImGui::GetWindowContentRegionMax();
|
||||
min.x += win_pos.x;
|
||||
min.y += win_pos.y;
|
||||
max.x += win_pos.x;
|
||||
max.y += win_pos.y;
|
||||
|
||||
auto win = ImGui::GetCurrentWindow();
|
||||
if (win->ScrollbarY)
|
||||
{
|
||||
max.x -= win->ScrollbarSizes.x;
|
||||
}
|
||||
|
||||
ImGui::PushClipRect(min, max, false);
|
||||
if (frontend.target->selected_thread != idx
|
||||
&& ImGui::IsMouseClicked(ImGuiMouseButton_Left)
|
||||
&& ImGui::IsMouseHoveringRect(min_pos, max_pos, true))
|
||||
{
|
||||
frontend.target->backend->select_thread(idx);
|
||||
}
|
||||
ImGui::PopClipRect();
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
@@ -273,7 +336,8 @@ bool FrameWindow::draw(const Frontend &frontend)
|
||||
}
|
||||
|
||||
if (ImGui::BeginTable("table", 1,
|
||||
ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg))
|
||||
ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg
|
||||
| ImGuiTableFlags_ScrollX))
|
||||
{
|
||||
ImGui::TableSetupColumn("Location");
|
||||
ImGui::TableHeadersRow();
|
||||
@@ -291,6 +355,14 @@ bool FrameWindow::draw(const Frontend &frontend)
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
|
||||
if (frontend.target->selected_frame == idx)
|
||||
{
|
||||
// TODO: style coloring
|
||||
const auto col =
|
||||
ImGui::GetColorU32(ImGui::GetStyleColorVec4(ImGuiCol_Button));
|
||||
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, col);
|
||||
}
|
||||
|
||||
if (frame->display_name != "")
|
||||
{
|
||||
ImGui::Text(frame->display_name.c_str());
|
||||
@@ -299,6 +371,23 @@ bool FrameWindow::draw(const Frontend &frontend)
|
||||
std::snprintf(buf, sizeof(buf), "%lX", frame->ip);
|
||||
ImGui::Text(buf);
|
||||
}
|
||||
|
||||
auto min_pos =
|
||||
ImGui::TableGetCellBgRect(ImGui::GetCurrentTable(), 0).GetTL();
|
||||
auto max_pos =
|
||||
ImGui::TableGetCellBgRect(ImGui::GetCurrentTable(), 0).GetBR();
|
||||
max_pos.y += ImGui::GetTextLineHeight();
|
||||
#if 0
|
||||
ImGui::GetWindowDrawList()->AddRect(min_pos, max_pos,
|
||||
IM_COL32(255, 0, 0, 255));
|
||||
#endif
|
||||
if (frontend.target->selected_frame != idx
|
||||
&& ImGui::IsMouseClicked(ImGuiMouseButton_Left)
|
||||
&& ImGui::IsMouseHoveringRect(min_pos, max_pos, true))
|
||||
{
|
||||
printf("Select frame %lu\n", idx);
|
||||
frontend.target->backend->select_frame(idx);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
@@ -348,7 +437,7 @@ bool DisasmWindow::draw(Frontend &frontend)
|
||||
|
||||
if (first)
|
||||
{
|
||||
auto found = false;
|
||||
/*auto found = false;
|
||||
uint16_t set_idx, reg_idx;
|
||||
for (size_t i = 0; i < frontend.target->reg_sets.size(); ++i)
|
||||
{
|
||||
@@ -375,16 +464,16 @@ bool DisasmWindow::draw(Frontend &frontend)
|
||||
this->ip_unsuccessful = true;
|
||||
this->disas_unsuccessful = true;
|
||||
return false;
|
||||
}
|
||||
}*/
|
||||
|
||||
this->ip_src_id = frontend.target->data_node_id++;
|
||||
this->disas_src_id = frontend.target->data_node_id++;
|
||||
frontend.target->backend->add_data_node(data::DataNode{
|
||||
.id = this->ip_src_id,
|
||||
frontend.target->backend->add_data_node(
|
||||
data::DataNode{.id = this->ip_src_id,
|
||||
.type = data::DataNode::Type::data_source,
|
||||
.data = data::DataSource{
|
||||
.type = data::DataSource::Type::reg,
|
||||
.data = data::DataSource::Reg{.set = set_idx, .idx = reg_idx}}});
|
||||
.type = data::DataSource::Type::frame_ip,
|
||||
}});
|
||||
|
||||
frontend.target->backend->add_data_node(
|
||||
data::DataNode{.id = this->disas_src_id,
|
||||
@@ -418,7 +507,7 @@ bool DisasmWindow::draw(Frontend &frontend)
|
||||
auto &bps = frontend.target->breakpoints;
|
||||
const auto bp_it =
|
||||
std::find_if(bps.begin(), bps.end(),
|
||||
[inst](const auto &el) { return el.addr == inst.addr; });
|
||||
[inst](const auto &el) { return el.at_addr(inst.addr); });
|
||||
const auto is_bp = bp_it != bps.end() && !bp_it->removed;
|
||||
if (is_bp)
|
||||
{
|
||||
@@ -430,9 +519,16 @@ bool DisasmWindow::draw(Frontend &frontend)
|
||||
auto end_pos = pos;
|
||||
end_pos.x += 10.f;
|
||||
end_pos.y += 10.f;
|
||||
ImGui::GetWindowDrawList()->AddRectFilled(pos, end_pos,
|
||||
|
||||
pos.x += 5.f;
|
||||
pos.y += 5.f;
|
||||
|
||||
ImGui::GetWindowDrawList()->AddCircleFilled(pos, 5.f,
|
||||
IM_COL32(255, 0, 0, 255));
|
||||
}
|
||||
|
||||
// TODO: write custom code to catch mouse clicks in the whole cell, including padding
|
||||
// to make targeting easier
|
||||
if (ImGui::InvisibleButton("Break",
|
||||
ImVec2{10.f, ImGui::GetTextLineHeight()}))
|
||||
{
|
||||
@@ -449,10 +545,10 @@ bool DisasmWindow::draw(Frontend &frontend)
|
||||
if (idx == bps.size())
|
||||
{
|
||||
bps.push_back(
|
||||
Target::Breakpoint{.removed = false, .addr = inst.addr});
|
||||
Target::Breakpoint{.removed = false, .data = inst.addr});
|
||||
} else
|
||||
{
|
||||
bps[idx] = Target::Breakpoint{.removed = false, .addr = inst.addr};
|
||||
bps[idx] = Target::Breakpoint{.removed = false, .data = inst.addr};
|
||||
}
|
||||
frontend.target->backend->add_breakpoint(inst.addr, idx);
|
||||
} else
|
||||
@@ -535,10 +631,11 @@ bool BreakpointWindow::draw(Frontend &frontend)
|
||||
}
|
||||
|
||||
if (ImGui::BeginTable("table", 2,
|
||||
ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg))
|
||||
ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg
|
||||
| ImGuiTableFlags_SizingFixedFit))
|
||||
{
|
||||
ImGui::TableSetupColumn("ID");
|
||||
ImGui::TableSetupColumn("Address");
|
||||
ImGui::TableSetupColumn("Location");
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
auto &bps = frontend.target->breakpoints;
|
||||
@@ -558,7 +655,14 @@ bool BreakpointWindow::draw(Frontend &frontend)
|
||||
ImGui::Text("%zu", idx);
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
ImGui::Text("%lX", bp.addr);
|
||||
if (auto addr = std::get_if<uint64_t>(&bp.data))
|
||||
{
|
||||
ImGui::Text("%lX", *addr);
|
||||
} else
|
||||
{
|
||||
const auto &loc = std::get<Target::Breakpoint::FileLoc>(bp.data);
|
||||
ImGui::Text("%s:%u", loc.name.c_str(), loc.line);
|
||||
}
|
||||
|
||||
auto min_pos =
|
||||
ImGui::TableGetCellBgRect(ImGui::GetCurrentTable(), 0).GetTL();
|
||||
@@ -593,6 +697,277 @@ bool BreakpointWindow::draw(Frontend &frontend)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SourceWindow::draw(Frontend &frontend)
|
||||
{
|
||||
if (!ImGui::Begin(this->id.c_str()))
|
||||
{
|
||||
ImGui::End();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!frontend.target)
|
||||
{
|
||||
this->first = true;
|
||||
this->file_name = "";
|
||||
this->lines.clear();
|
||||
this->file_data.clear();
|
||||
ImGui::End();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (frontend.target->state == TargetState::stopped
|
||||
|| frontend.target->state == TargetState::startup)
|
||||
{
|
||||
this->file_name = "";
|
||||
this->lines.clear();
|
||||
this->file_data.clear();
|
||||
ImGui::End();
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: we need to clean up the initializations/sync of the DAG somehow
|
||||
if (first)
|
||||
{
|
||||
/*auto found = false;
|
||||
uint16_t set_idx, reg_idx;
|
||||
for (size_t i = 0; i < frontend.target->reg_sets.size(); ++i)
|
||||
{
|
||||
const auto &set = frontend.target->reg_sets[i];
|
||||
for (size_t j = 0; j < set.regs.size(); ++j)
|
||||
{
|
||||
if (frontend.target->arch == Arch::x86_64 && set.regs[j].name == "rip")
|
||||
{
|
||||
found = true;
|
||||
set_idx = i;
|
||||
reg_idx = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
ImGui::End();
|
||||
this->file_name = "";
|
||||
this->lines.clear();
|
||||
this->file_data.clear();
|
||||
return false;
|
||||
}*/
|
||||
|
||||
this->ip_src_id = frontend.target->data_node_id++;
|
||||
this->line_entry_src_id = frontend.target->data_node_id++;
|
||||
frontend.target->backend->add_data_node(
|
||||
data::DataNode{.id = this->ip_src_id,
|
||||
.type = data::DataNode::Type::data_source,
|
||||
.data = data::DataSource{
|
||||
.type = data::DataSource::Type::frame_ip,
|
||||
}});
|
||||
|
||||
frontend.target->backend->add_data_node(
|
||||
data::DataNode{.id = this->line_entry_src_id,
|
||||
.type = data::DataNode::Type::line_entry,
|
||||
.data = data::LineEntry{.src_id = this->ip_src_id}});
|
||||
|
||||
first = false;
|
||||
}
|
||||
|
||||
if (!this->file_name.empty() && this->file_data.empty())
|
||||
{
|
||||
// parse file
|
||||
auto file =
|
||||
std::ifstream{this->file_name, std::ios::binary | std::ios::ate};
|
||||
if (file.is_open())
|
||||
{
|
||||
const auto size = file.tellg();
|
||||
if (size != 0)
|
||||
{
|
||||
this->file_data.resize(size);
|
||||
file.seekg(std::ios::beg);
|
||||
file.read(this->file_data.data(), this->file_data.size());
|
||||
|
||||
auto view =
|
||||
std::string_view{this->file_data.data(), this->file_data.size()};
|
||||
assert(this->lines.empty());
|
||||
while (true)
|
||||
{
|
||||
// TODO: better line handling?
|
||||
const auto pos = view.find_first_of('\n');
|
||||
if (pos == std::string_view::npos)
|
||||
{
|
||||
this->lines.push_back(view);
|
||||
break;
|
||||
}
|
||||
|
||||
this->lines.push_back(view.substr(0, pos));
|
||||
view.remove_prefix(pos + 1);
|
||||
}
|
||||
} else
|
||||
{
|
||||
printf("File is empty: %s\n", this->file_name.c_str());
|
||||
// prevent spamming the open call
|
||||
this->file_data.resize(1);
|
||||
}
|
||||
} else
|
||||
{
|
||||
printf("Couldn't open file: %s\n", this->file_name.c_str());
|
||||
// prevent spamming the open call
|
||||
this->file_data.resize(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (this->file_name.empty())
|
||||
{
|
||||
//auto txt_pos = ImGui::GetWindowPos();
|
||||
auto txt_pos = ImGui::GetWindowContentRegionMin();
|
||||
auto max = ImGui::GetWindowContentRegionMax();
|
||||
txt_pos.x += (max.x - txt_pos.x) * 0.5f;
|
||||
txt_pos.y += (max.y - txt_pos.y) * 0.5f;
|
||||
|
||||
const auto txt = "No source available";
|
||||
const auto txt_size = ImGui::CalcTextSize(txt);
|
||||
txt_pos.x -= txt_size.x * 0.5f;
|
||||
txt_pos.y -= txt_size.y * 0.5f;
|
||||
|
||||
ImGui::SetCursorPos(txt_pos);
|
||||
ImGui::TextDisabled(txt);
|
||||
}
|
||||
|
||||
auto pad = ImGui::GetStyle().CellPadding;
|
||||
pad.x += 10.f;
|
||||
|
||||
// TODO: use ImGuiListClipper to work with larger files
|
||||
if (ImGui::BeginTable("table", 3,
|
||||
ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollY
|
||||
| ImGuiTableFlags_PadOuterX
|
||||
| ImGuiTableFlags_ScrollX))
|
||||
{
|
||||
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, 10.f);
|
||||
ImGui::TableSetupColumn("LineNo");
|
||||
ImGui::TableSetupColumn("LineContent");
|
||||
//ImGui::TableHeadersRow();
|
||||
|
||||
for (size_t idx = 0; idx < this->lines.size(); ++idx)
|
||||
{
|
||||
//printf("Inst at %lx with '%s'\n", inst.addr, inst.fmt_str.c_str());
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
|
||||
ImGui::PushID(idx);
|
||||
auto &bps = frontend.target->breakpoints;
|
||||
const auto bp_it =
|
||||
std::find_if(bps.begin(), bps.end(), [this, idx](const auto &el) {
|
||||
return el.at_file_loc(this->file_name, idx + 1);
|
||||
});
|
||||
const auto is_bp = bp_it != bps.end() && !bp_it->removed;
|
||||
if (is_bp)
|
||||
{
|
||||
auto win_pos = ImGui::GetWindowPos();
|
||||
auto pos = ImGui::GetCursorPos();
|
||||
pos.x += win_pos.x;
|
||||
pos.y += win_pos.y - ImGui::GetScrollY();
|
||||
pos.y += (ImGui::GetTextLineHeight() - 10.f) / 2.f;
|
||||
auto end_pos = pos;
|
||||
end_pos.x += 10.f;
|
||||
end_pos.y += 10.f;
|
||||
|
||||
pos.x += 5.f;
|
||||
pos.y += 5.f;
|
||||
|
||||
ImGui::GetWindowDrawList()->AddCircleFilled(pos, 5.f,
|
||||
IM_COL32(255, 0, 0, 255));
|
||||
}
|
||||
|
||||
if (this->line == idx + 1)
|
||||
{
|
||||
auto win_pos = ImGui::GetWindowPos();
|
||||
auto pos = ImGui::GetCursorPos();
|
||||
pos.x += win_pos.x;
|
||||
pos.y += win_pos.y - ImGui::GetScrollY();
|
||||
pos.y += (ImGui::GetTextLineHeight() - 10.f) / 2.f;
|
||||
auto p2 = pos;
|
||||
auto p3 = pos;
|
||||
p2.x += 10.f;
|
||||
p2.y += 5.f;
|
||||
p3.y += 10.f;
|
||||
|
||||
ImGui::GetWindowDrawList()->AddTriangleFilled(
|
||||
pos, p2, p3, IM_COL32(227, 197, 103, 255));
|
||||
}
|
||||
|
||||
// TODO: write custom code to catch mouse clicks in the whole cell, including padding
|
||||
// to make targeting easier
|
||||
if (ImGui::InvisibleButton("Break",
|
||||
ImVec2{10.f, ImGui::GetTextLineHeight()}))
|
||||
{
|
||||
if (!is_bp)
|
||||
{
|
||||
size_t bp_idx = 0;
|
||||
for (; bp_idx < bps.size(); ++bp_idx)
|
||||
{
|
||||
if (bps[bp_idx].removed)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (frontend.target->backend->add_breakpoint(this->file_name.c_str(),
|
||||
idx + 1, bp_idx))
|
||||
{
|
||||
auto bp =
|
||||
Target::Breakpoint{.removed = false,
|
||||
.data = Target::Breakpoint::FileLoc{
|
||||
.name = this->file_name,
|
||||
.line = static_cast<uint32_t>(idx + 1)}};
|
||||
if (bp_idx == bps.size())
|
||||
{
|
||||
bps.push_back(bp);
|
||||
} else
|
||||
{
|
||||
bps[bp_idx] = bp;
|
||||
}
|
||||
}
|
||||
} else
|
||||
{
|
||||
frontend.target->backend->remove_breakpoint(bp_it - bps.begin());
|
||||
bp_it->removed = true;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
if (this->line == idx + 1)
|
||||
{
|
||||
// TODO: color config
|
||||
//const auto col =
|
||||
// ImGui::GetColorU32(ImGui::GetStyleColorVec4(ImGuiCol_Button));
|
||||
//ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, col);
|
||||
if (this->line_changed)
|
||||
{
|
||||
ImGui::SetScrollHereY();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Text("%lu ", idx + 1);
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
const auto line = this->lines[idx];
|
||||
ImGui::Text("%.*s", static_cast<int>(line.size()), line.data());
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
|
||||
this->line_changed = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
void Window::handle_data_res(const BackToFront::DataResult &result)
|
||||
{
|
||||
switch (this->type)
|
||||
@@ -602,6 +977,9 @@ void Window::handle_data_res(const BackToFront::DataResult &result)
|
||||
case disassembly:
|
||||
std::get<DisasmWindow>(this->data).handle_data_res(result);
|
||||
break;
|
||||
case source:
|
||||
std::get<SourceWindow>(this->data).handle_data_res(result);
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
@@ -707,3 +1085,77 @@ void DisasmWindow::handle_data_res(
|
||||
this->max_mnem_len = max_mnem_len;
|
||||
this->max_op_len = max_op_len;
|
||||
}
|
||||
|
||||
void SourceWindow::handle_data_res(
|
||||
const BackToFront::DataResult &result_wrapper)
|
||||
{
|
||||
const auto &result = result_wrapper.result;
|
||||
if (result.id == this->ip_src_id)
|
||||
{
|
||||
// should not need to care
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.id != this->line_entry_src_id)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!result.success || result.type.type != data::TypeInfo::Type::custom)
|
||||
{
|
||||
this->lines.clear();
|
||||
this->file_data.clear();
|
||||
this->file_name.clear();
|
||||
this->line = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// parse line entry
|
||||
// struct LineEntry {
|
||||
// uint32_t line_no;
|
||||
// uint32_t name_len;
|
||||
// char file_name[];
|
||||
// };
|
||||
|
||||
if (result.data.size() < 8)
|
||||
{
|
||||
this->lines.clear();
|
||||
this->file_data.clear();
|
||||
this->file_name.clear();
|
||||
this->line = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
const auto line = *reinterpret_cast<const uint32_t *>(result.data.data());
|
||||
const auto name_len =
|
||||
*reinterpret_cast<const uint32_t *>(result.data.data() + 4);
|
||||
if (result.data.size() < 8 + name_len)
|
||||
{
|
||||
this->lines.clear();
|
||||
this->file_data.clear();
|
||||
this->file_name.clear();
|
||||
this->line = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
const auto file_view = std::string_view{
|
||||
reinterpret_cast<const char *>(result.data.data() + 8), name_len};
|
||||
|
||||
printf("New LE: %.*s:%u", static_cast<int>(file_view.size()),
|
||||
file_view.data(), line);
|
||||
|
||||
if (this->file_name != file_view)
|
||||
{
|
||||
// force reload
|
||||
this->lines.clear();
|
||||
this->file_data.clear();
|
||||
this->file_name = file_view;
|
||||
this->line_changed = true;
|
||||
}
|
||||
|
||||
if (this->line != line)
|
||||
{
|
||||
this->line = line;
|
||||
this->line_changed = true;
|
||||
}
|
||||
}
|
||||
@@ -107,11 +107,28 @@ namespace dbgui::frontend
|
||||
bool open;
|
||||
};
|
||||
|
||||
struct SourceWindow
|
||||
{
|
||||
bool draw(Frontend &);
|
||||
void handle_data_res(const BackToFront::DataResult &result);
|
||||
|
||||
std::string id;
|
||||
bool open;
|
||||
bool first;
|
||||
bool line_changed;
|
||||
|
||||
size_t ip_src_id, line_entry_src_id;
|
||||
std::string file_name;
|
||||
uint32_t line;
|
||||
std::vector<char> file_data;
|
||||
std::vector<std::string_view> lines;
|
||||
};
|
||||
|
||||
struct Window
|
||||
{
|
||||
WindowType type;
|
||||
std::variant<std::monostate, RegWindow, ThreadWindow, FrameWindow,
|
||||
DisasmWindow, BreakpointWindow>
|
||||
DisasmWindow, BreakpointWindow, SourceWindow>
|
||||
data;
|
||||
|
||||
// if true, window is closed and should be deleted
|
||||
@@ -122,6 +139,7 @@ namespace dbgui::frontend
|
||||
static Window create_frames(size_t window_id);
|
||||
static Window create_disas(size_t window_id);
|
||||
static Window create_bp(size_t window_id);
|
||||
static Window create_source(size_t window_id);
|
||||
|
||||
void handle_data_res(const BackToFront::DataResult &result);
|
||||
};
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <stdio.h>
|
||||
#define GL_SILENCE_DEPRECATION
|
||||
#include <GLFW/glfw3.h> // Will drag system OpenGL headers
|
||||
#include <pthread.h>
|
||||
|
||||
#include "frontend/frontend.h"
|
||||
|
||||
@@ -23,6 +24,8 @@ static void glfw_error_callback(int error, const char *description)
|
||||
// Main code
|
||||
int main(int, char **)
|
||||
{
|
||||
pthread_setname_np(pthread_self(), "render_thread");
|
||||
|
||||
glfwSetErrorCallback(glfw_error_callback);
|
||||
if (!glfwInit())
|
||||
return 1;
|
||||
|
||||
15
src/msg.h
15
src/msg.h
@@ -71,6 +71,8 @@ namespace dbgui
|
||||
frame_removed,
|
||||
// TODO: frame_moved, frame_added, etc?
|
||||
data_result,
|
||||
selected_frame_changed,
|
||||
selected_thread_changed,
|
||||
};
|
||||
|
||||
struct StateChange
|
||||
@@ -137,12 +139,23 @@ namespace dbgui
|
||||
data::DataResult result;
|
||||
};
|
||||
|
||||
struct SelectedFrameChanged
|
||||
{
|
||||
uint16_t idx;
|
||||
};
|
||||
|
||||
struct SelectedThreadChanged
|
||||
{
|
||||
uint16_t idx;
|
||||
};
|
||||
|
||||
struct Msg
|
||||
{
|
||||
MsgType type;
|
||||
std::variant<std::monostate, StateChange, IPChange, InitialProcessInfo,
|
||||
RegsChanged, ThreadChange, ThreadRemoved, FrameChanged,
|
||||
FrameRemoved, DataResult>
|
||||
FrameRemoved, DataResult, SelectedFrameChanged,
|
||||
SelectedThreadChanged>
|
||||
data;
|
||||
};
|
||||
} // namespace BackToFront
|
||||
|
||||
@@ -71,7 +71,8 @@ namespace dbgui::util
|
||||
}
|
||||
}
|
||||
|
||||
if (out_edges.empty())
|
||||
// TODO: ghetto fix for linearize
|
||||
if (out_edges.empty() && nodes.contains(from))
|
||||
{
|
||||
root_nodes.emplace(from);
|
||||
}
|
||||
@@ -93,6 +94,8 @@ namespace dbgui::util
|
||||
std::erase(out_edges, id);
|
||||
if (out_edges.empty())
|
||||
{
|
||||
// TODO: ghetto fix for linearize
|
||||
if (nodes.contains(el))
|
||||
root_nodes.insert(el);
|
||||
}
|
||||
}
|
||||
@@ -125,7 +128,7 @@ namespace dbgui::util
|
||||
|
||||
for (auto id : self_copy.root_nodes)
|
||||
{
|
||||
out.push_back(insert_idx);
|
||||
out.push_back(id);
|
||||
}
|
||||
|
||||
while (insert_idx < out.size())
|
||||
|
||||
@@ -1 +1,4 @@
|
||||
make sure all the ImGui::Text stuff is converted to TextUnformatted unless formatting is needed
|
||||
- make sure all the ImGui::Text stuff is converted to TextUnformatted unless formatting is needed
|
||||
- have a panel to create custom types (C++ to DWARF through compiler or smth for now) for custom casting
|
||||
- memory + cpu statistics like VS
|
||||
- visualization for branches (draw arror if branch is taken (or in both cases?))
|
||||
Reference in New Issue
Block a user