breakpoints; thread/frame selection that should work; TODO: is slow

This commit is contained in:
T0b1
2023-06-08 01:07:57 +02:00
parent 78e7f5cca7
commit b393f3dd28
14 changed files with 1098 additions and 73 deletions

57
example_cus.txt Normal file
View 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)

View File

@@ -57,4 +57,18 @@ void Backend::send_data_result(BackToFront::DataResult &&info)
auto msg = std::make_unique<BackToFront::Msg>(
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));
}

View File

@@ -18,10 +18,10 @@ 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_out() = 0;
virtual bool step_over(bool source_step) = 0;
virtual bool step_out() = 0;
virtual void cont() = 0;
virtual void pause() = 0;
@@ -29,8 +29,13 @@ namespace dbgui::backend
virtual void add_data_node(const data::DataNode &) = 0;
virtual void remove_data_node(uint64_t id) = 0;
virtual void add_breakpoint(uint64_t addr, size_t id) = 0;
virtual void remove_breakpoint(size_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;

View File

@@ -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();
thread.StepInstruction(false);
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();
thread.StepInstruction(true);
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(),
.ip = frame.GetPC(),
.name = thread.GetName(),
.cur_display_fn = frame.GetDisplayFunctionName()});
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_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())
{
@@ -1006,17 +1234,35 @@ 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 addr = inst.GetAddress().GetLoadAddress(_target);
const auto len = inst.GetByteSize();
auto inst = inst_list.GetInstructionAtIndex(i);
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();
//SBStream stream{};
//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};

View File

@@ -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 = {};

View File

@@ -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

View File

@@ -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)
@@ -155,7 +155,7 @@ void Frontend::draw_header()
}
break;
case running:
if (ImGui::Button("Pause"))
if (ImGui::Button("Pause "))
{
this->target->backend->pause();
}
@@ -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;
}

View File

@@ -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;

View File

@@ -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,
.type = data::DataNode::Type::data_source,
.data = data::DataSource{
.type = data::DataSource::Type::reg,
.data = data::DataSource::Reg{.set = set_idx, .idx = reg_idx}}});
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->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,
IM_COL32(255, 0, 0, 255));
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;
}
}
@@ -706,4 +1084,78 @@ 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;
}
}

View File

@@ -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);
};

View File

@@ -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;

View File

@@ -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

View File

@@ -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,7 +94,9 @@ namespace dbgui::util
std::erase(out_edges, id);
if (out_edges.empty())
{
root_nodes.insert(el);
// 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())

View File

@@ -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?))