From ac3718b12bdcf31cbb10c3af0b4e6f73cadbcf8c Mon Sep 17 00:00:00 2001 From: T0b1 Date: Fri, 2 Jun 2023 02:06:49 +0200 Subject: [PATCH] regs and we can step into! --- .clang-format | 88 +++++ .gitignore | 1 + CMakeLists.txt | 4 +- known_bugs.txt | 1 + src/backend/backend.cpp | 24 +- src/backend/debug_backend.h | 68 ++-- src/backend/lldb/lldb_backend.cpp | 555 +++++++++++++++++++++++++++++- src/backend/lldb/lldb_backend.h | 62 +++- src/frontend/frontend.cpp | 382 ++++++++++++++------ src/frontend/frontend.h | 56 +-- src/frontend/target.h | 39 +++ src/frontend/window.cpp | 101 ++++++ src/frontend/window.h | 74 ++-- src/main.cpp | 219 ++++++------ src/msg.h | 99 +++++- tmp/main | Bin 0 -> 23320 bytes tmp/main.c | 5 + 17 files changed, 1434 insertions(+), 344 deletions(-) create mode 100644 .clang-format create mode 100644 known_bugs.txt create mode 100644 src/frontend/target.h create mode 100644 src/frontend/window.cpp create mode 100755 tmp/main create mode 100644 tmp/main.c diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..e7de6a6 --- /dev/null +++ b/.clang-format @@ -0,0 +1,88 @@ +--- +AccessModifierOffset: '0' +AlignAfterOpenBracket: Align +AlignConsecutiveMacros: 'true' +AlignConsecutiveAssignments: 'true' +AlignConsecutiveDeclarations: 'false' +AlignEscapedNewlines: Left +AlignOperands: 'true' +AlignTrailingComments: 'true' +AllowAllArgumentsOnNextLine: 'true' +AllowAllConstructorInitializersOnNextLine: 'true' +AllowAllParametersOfDeclarationOnNextLine: 'true' +AllowShortBlocksOnASingleLine: 'true' +AllowShortCaseLabelsOnASingleLine: 'true' +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: 'false' +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: 'false' +AlwaysBreakTemplateDeclarations: 'Yes' +BinPackArguments: 'true' +BinPackParameters: 'true' +BreakBeforeBinaryOperators: NonAssignment +BreakBeforeBraces: Custom +BraceWrapping: + AfterCaseLabel: true + AfterClass: true + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterStruct: true + AfterUnion: true + AfterExternBlock: true + BeforeCatch: false + BeforeElse: false + SplitEmptyFunction: false + SplitEmptyRecord: false + SplitEmptyNamespace: false + AfterControlStatement: true +# TODO: comes in clang-format 10 +#BraceWrappingAfterControlStatementStyle: MultiLine +BreakBeforeTernaryOperators: 'true' +BreakConstructorInitializers: BeforeColon +BreakInheritanceList: BeforeColon +CompactNamespaces: 'false' +ConstructorInitializerAllOnOneLineOrOnePerLine: 'false' +ContinuationIndentWidth: '2' +Cpp11BracedListStyle: 'true' +DerivePointerAlignment: 'false' +DisableFormat: 'false' +ExperimentalAutoDetectBinPacking: 'false' +FixNamespaceComments: 'true' +IncludeBlocks: Preserve +IndentCaseLabels: 'true' +IndentPPDirectives: None +IndentWidth: '2' +IndentWrappedFunctionNames: 'true' +KeepEmptyLinesAtTheStartOfBlocks: 'false' +Language: Cpp +MaxEmptyLinesToKeep: '1' +NamespaceIndentation: All +PointerAlignment: Right +ReflowComments: 'false' +SortIncludes: 'false' +SortUsingDeclarations: 'false' +SpaceAfterCStyleCast: 'false' +SpaceAfterLogicalNot: 'false' +SpaceAfterTemplateKeyword: 'false' +SpaceBeforeAssignmentOperators: 'true' +SpaceBeforeCpp11BracedList: 'false' +SpaceBeforeCtorInitializerColon: 'true' +SpaceBeforeInheritanceColon: 'true' +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: 'true' +SpaceInEmptyParentheses: 'false' +SpacesBeforeTrailingComments: '2' +SpacesInAngles: false +SpacesInCStyleCastParentheses: 'false' +SpacesInContainerLiterals: 'false' +SpacesInParentheses: 'false' +SpacesInSquareBrackets: 'false' +Standard: Cpp11 +TabWidth: '2' +UseTab: ForIndentation +CommentPragmas: '^\\.+' + +... \ No newline at end of file diff --git a/.gitignore b/.gitignore index 5cc22a4..3c19046 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ build/ .vscode/ +.cache/ imgui.ini diff --git a/CMakeLists.txt b/CMakeLists.txt index 8140703..ceb68d2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,11 +23,12 @@ set(DBGUI_HEADERS src/backend/debug_backend.h src/backend/lldb/lldb_backend.h src/frontend/frontend.h + src/frontend/target.h src/frontend/window.h src/msg.h) set(DBGUI_SOURCES - src/main.cpp src/frontend/frontend.cpp + src/main.cpp src/frontend/frontend.cpp src/frontend/window.cpp src/backend/backend.cpp src/backend/lldb/lldb_backend.cpp ${IMGUI_SOURCES} @@ -35,6 +36,5 @@ set(DBGUI_SOURCES add_executable(dbgui ${DBGUI_SOURCES}) - target_link_libraries(dbgui ${OPENGL_LIBRARIES} glfw) target_link_libraries(dbgui lldb) \ No newline at end of file diff --git a/known_bugs.txt b/known_bugs.txt new file mode 100644 index 0000000..df2aa7e --- /dev/null +++ b/known_bugs.txt @@ -0,0 +1 @@ +# because of LLDB SB, currently impossible to step over/into on an assembly level if a frame has source information available \ No newline at end of file diff --git a/src/backend/backend.cpp b/src/backend/backend.cpp index 9cb9517..14a648b 100644 --- a/src/backend/backend.cpp +++ b/src/backend/backend.cpp @@ -2,6 +2,26 @@ using namespace dbgui::backend; -Backend::~Backend() { - +Backend::~Backend() {} + +void Backend::send_state_change(TargetState new_state, StateChangeReason reason) +{ + auto msg = std::make_unique( + BackToFront::MsgType::state_change, + BackToFront::StateChange{new_state, reason}); + this->send_msg(std::move(msg)); +} + +void Backend::send_proc_info(BackToFront::InitialProcessInfo &&info) +{ + auto msg = std::make_unique( + BackToFront::MsgType::initial_proc_info, std::move(info)); + this->send_msg(std::move(msg)); +} + +void Backend::send_reg_change(BackToFront::RegsChanged &&info) +{ + auto msg = std::make_unique( + BackToFront::MsgType::regs_changed, std::move(info)); + this->send_msg(std::move(msg)); } \ No newline at end of file diff --git a/src/backend/debug_backend.h b/src/backend/debug_backend.h index b6ace70..4df78c3 100644 --- a/src/backend/debug_backend.h +++ b/src/backend/debug_backend.h @@ -8,39 +8,49 @@ #include "msg.h" -namespace dbgui::backend { +namespace dbgui::backend +{ - enum class TargetState : uint8_t { - stopped, - running, - paused, - }; + struct Backend : std::enable_shared_from_this + { + virtual ~Backend(); - struct Backend { - virtual ~Backend(); + virtual void start() = 0; - virtual void start() = 0; + // returns whether the command was submitted + virtual bool step_into() = 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; - auto retrieve_msg_for_frontend() -> std::optional> { - auto g = std::lock_guard(_back_front_msg_mutex); - if (_back_front_msgs.empty()) { - return {}; - } + auto retrieve_msg_for_frontend() + -> std::optional> + { + auto g = std::lock_guard(_back_front_msg_mutex); + if (_back_front_msgs.empty()) + { + return {}; + } - auto result = std::move(_back_front_msgs[0]); - _back_front_msgs.erase(_back_front_msgs.begin()); - // std::make_optional(std::move(result)) - return result; - } + auto result = std::move(_back_front_msgs[0]); + _back_front_msgs.erase(_back_front_msgs.begin()); + // std::make_optional(std::move(result)) + return result; + } - protected: - void send_msg(std::unique_ptr msg) { - auto g = std::lock_guard(_back_front_msg_mutex); - _back_front_msgs.emplace_back(std::move(msg)); - } + protected: + void send_msg(std::unique_ptr msg) + { + auto g = std::lock_guard(_back_front_msg_mutex); + _back_front_msgs.emplace_back(std::move(msg)); + } - private: - std::mutex _back_front_msg_mutex; - std::vector> _back_front_msgs; - }; -} \ No newline at end of file + void send_state_change(TargetState new_state, StateChangeReason reason); + void send_proc_info(BackToFront::InitialProcessInfo &&); + void send_reg_change(BackToFront::RegsChanged &&); + + private: + std::mutex _back_front_msg_mutex; + std::vector> _back_front_msgs; + }; +} // namespace dbgui::backend \ No newline at end of file diff --git a/src/backend/lldb/lldb_backend.cpp b/src/backend/lldb/lldb_backend.cpp index f8f3831..0560486 100644 --- a/src/backend/lldb/lldb_backend.cpp +++ b/src/backend/lldb/lldb_backend.cpp @@ -1,24 +1,555 @@ #include "lldb_backend.h" #include +#include +#include +#include +#include +#include +#include #include +#include +#include +#include using namespace dbgui::backend; -LLDBBackend::~LLDBBackend() { - +namespace +{} + +LLDBBackend::~LLDBBackend() {} + +LLDBBackend::LLDBBackend(std::string filename) +{ + _filename = filename; + lldb::SBDebugger::Initialize(); + _instance = lldb::SBDebugger::Create(false); + _target = _instance.CreateTarget(filename.c_str()); } -LLDBBackend::LLDBBackend(std::string filename) { - _filename = filename; - _instance = lldb::SBDebugger::Create(false); - _target = _instance.CreateTarget(filename.c_str()); +void LLDBBackend::start() +{ + const char *argv[2] = {_filename.c_str(), nullptr}; + const auto cwd = std::filesystem::current_path(); + + auto error = lldb::SBError(); + auto listener = lldb::SBListener(); + _process = _target.Launch(listener, argv, nullptr, nullptr, nullptr, nullptr, + cwd.c_str(), lldb::LaunchFlags::eLaunchFlagNone, + true, error); + + _msg_thread = std::thread{[this]() { + auto ptr = this->shared_from_this(); + static_cast(ptr.get())->run_msg_loop(); + }}; } -void LLDBBackend::start() { - const char* argv[2] = {_filename.c_str(), nullptr}; - const auto cwd = std::filesystem::current_path(); +void LLDBBackend::run_msg_loop() +{ + std::thread event_thread{[this]() { this->wait_for_debug_events(); }}; - auto error = lldb::SBError(); - auto listener = lldb::SBListener(); - _process = _target.Launch(listener, argv, nullptr, nullptr, nullptr, nullptr, cwd.c_str(), lldb::LaunchFlags::eLaunchFlagNone, true, error); + while (true) + { + std::this_thread::sleep_for(std::chrono::milliseconds{1000}); + } +} + +void LLDBBackend::wait_for_debug_events() +{ + using namespace lldb; + auto listener = _instance.GetListener(); + + while (true) + { + auto event = lldb::SBEvent(); + if (listener.WaitForEvent(0xFFFFFFFF, event)) + { + std::lock_guard g{_data_lock}; + printf("Got Event:\n"); + auto stream = lldb::SBStream{}; + if (event.GetDescription(stream)) + { + printf(" Desc: %.*s\n", static_cast(stream.GetSize()), + stream.GetData()); + } else + { + printf(" Failed to get stream\n"); + } + + if (SBProcess::EventIsProcessEvent(event)) + { + // Check if state changed + auto state = SBProcess::GetStateFromEvent(event); + // TODO: get state from process if we can't get it from the event? + if (state != StateType::eStateInvalid) + { + this->handle_state_change(state); + } + } else + { + printf(" Unknown event source!\n"); + } + } + } +} + +void LLDBBackend::handle_state_change(lldb::StateType state) +{ + using namespace lldb; + if (_first_run) + { + _first_run = false; + // TODO: do initialization + // TODO: we should only do this when the process was stopped, no? + auto proc_info = BackToFront::InitialProcessInfo{}; + auto reg_infos = std::vector{}; + this->prepare_proc_info(proc_info, reg_infos); + this->dump_threads(); + + this->send_proc_info(std::move(proc_info)); + for (size_t i = 0; i < reg_infos.size(); ++i) + { + this->send_reg_change(std::move(reg_infos[i])); + } + reg_infos.clear(); + + if (state == StateType::eStateStopped) + { + this->send_state_change(TargetState::paused, + StateChangeReason::initial_entry); + return; + } + } + + switch (state) + { + case eStateStopped: + this->check_reg_changes(); + this->send_state_change(TargetState::paused, StateChangeReason::unknown); + break; + case eStateRunning: + case eStateStepping: + this->send_state_change(TargetState::running, StateChangeReason::unknown); + return; + default: printf("Unknown StateType %u encountered\n", state); exit(1); + } +} + +std::array x86_64_gpr = { + "rax", "rbx", "rcx", "rdx", "rdi", "rsi", "rbp", "rsp", + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", + "rip", "rflags", "cs", "fs", "gs", "ss", "ds", "es"}; + +std::array x86_64_fp_special = { + "fctrl", "fstat", "ftag", "fop", "fiseg", "fioff", "fip", + "foseg", "fooff", "foseg", "fooff", "fdp", "mxcsr", "mxcsrmask"}; + +std::array x86_64_fp = {"st0", "st1", "st2", "st3", + "st4", "st5", "st6", "st7"}; + +std::array x86_64_xmm = { + "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", + "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15"}; + +std::array x86_64_ymm = { + "ymm0", "ymm1", "ymm2", "ymm3", "ymm4", "ymm5", "ymm6", "ymm7", + "ymm8", "ymm9", "ymm10", "ymm11", "ymm12", "ymm13", "ymm14", "ymm15"}; + +std::array x86_64_zmm = { + "zmm0", "zmm1", "zmm2", "zmm3", "zmm4", "zmm5", "zmm6", "zmm7", + "zmm8", "zmm9", "zmm10", "zmm11", "zmm12", "zmm13", "zmm14", "zmm15"}; + +void LLDBBackend::prepare_proc_info( + BackToFront::InitialProcessInfo &info, + std::vector ®_infos) +{ + using namespace lldb; + info.pid = _process->GetProcessID(); + const auto target_triple = + std::string_view{_process->GetProcessInfo().GetTriple()}; + printf("Target Triple: %s\n", target_triple.data()); + if (target_triple.starts_with("x86_64-")) + { + info.arch = Arch::x86_64; + } /* else if (target_triple.starts_with("x86-")) { + // TODO: is this the right triple? + info.arch = Arch::x86; + }*/ + else + { + printf("Arch not supported!\n"); + exit(1); + } + + auto frame = _process->GetThreadAtIndex(0).GetFrameAtIndex(0); + const auto regs = frame.GetRegisters(); + /*const auto len = regs.GetSize(); + for (size_t i = 0; i < len; ++i) { + auto reg_or_set = regs.GetValueAtIndex(i); + if (reg_or_set.GetValueType() == eValueTypeRegister) { + printf("Got register %s (%s) with value: %lX\n", reg_or_set.GetName(), reg_or_set.GetTypeName(), reg_or_set.GetValueAsUnsigned(0)); + info.reg_names.emplace_back(reg_or_set.GetName()); + continue; + } + printf("Got register set %s\n", reg_or_set.GetName()); + const auto is_gp_set = std::string_view{reg_or_set.GetName()} == "General Purpose Registers"; + for (uint32_t child_idx = 0; child_idx < reg_or_set.GetNumChildren(); ++child_idx) { + auto reg = reg_or_set.GetChildAtIndex(child_idx); + printf("Got register %s (%s) with value: %lX\n", reg.GetName(), reg.GetTypeName(), reg.GetValueAsUnsigned(0)); + if (is_gp_set && info.arch == Arch::x86_64 && reg.GetByteSize() < 8) { + // skip non-full regs here + printf(" -> Skipped\n"); + continue; + } + info.reg_names.emplace_back(reg.GetName()); + } + }*/ + + if (info.arch == Arch::x86_64) + { + { + auto reg_set = RegSet{}; + auto info_set = BackToFront::InitialProcessInfo::RegSet{}; + auto reg_info = BackToFront::RegsChanged{}; + reg_info.set_idx = 0; + + info_set.name = "GPR"; + reg_set.set_name = "General Purpose Registers"; + auto lldb_set = regs.GetFirstValueByName("General Purpose Registers"); + for (size_t i = 0; i < x86_64_gpr.size(); ++i) + { + auto val = lldb_set.GetChildMemberWithName(x86_64_gpr[i]); + if (!val.IsValid()) + { + printf("GPR %s not found!\n", x86_64_gpr[i]); + exit(1); + } + info_set.reg_names.push_back(x86_64_gpr[i]); + reg_set.names.push_back(x86_64_gpr[i]); + reg_set.values.push_back({}); + + auto data = val.GetData(); + auto len = data.GetByteSize(); + reg_set.values.back().resize(data.GetByteSize()); + auto error = SBError{}; + data.ReadRawData(error, 0, reg_set.values.back().data(), len); + + reg_info.changes.push_back( + std::make_pair((uint16_t)i, reg_set.values.back())); + } + + _reg_sets.push_back(reg_set); + info.reg_sets.push_back(info_set); + reg_infos.push_back(reg_info); + } + } +} + +void LLDBBackend::dump_threads() +{ + using namespace lldb; + const auto thread_count = _process->GetNumThreads(); + + for (size_t i = 0; i < thread_count; ++i) + { + auto thread = _process->GetThreadAtIndex(i); + + auto stop_reason = thread.GetStopReason(); + switch (stop_reason) + { + case eStopReasonSignal: + { + auto signal_num = thread.GetStopReasonDataAtIndex(0); + printf("Thread %lu: TID: %lu, Stop Reason: Signal: %lu\n", i, + thread.GetThreadID(), signal_num); + break; + } + default: + printf("Thread %lu: TID: %lu, Stop Reason: %lu\n", i, + thread.GetThreadID(), stop_reason); + } + } + + auto sel = _process->GetSelectedThread(); + if (sel.IsValid()) + { + printf("Selected Thread: %lu\n", sel.GetThreadID()); + } else + { + printf("Selected thread not valid\n"); + } +} + +bool LLDBBackend::step_into() +{ + std::lock_guard g{_data_lock}; + if (!_process) + { + return false; + } + + auto thread = _process->GetSelectedThread(); + if (!thread.IsValid()) + { + return false; + } + + // TODO: figure out what the runmodes mean + thread.StepInto(); + + return false; +} + +bool LLDBBackend::step_over() +{ + std::lock_guard g{_data_lock}; + if (!_process) + { + return false; + } + + auto thread = _process->GetSelectedThread(); + if (!thread.IsValid()) + { + return false; + } + thread.StepOver(); + + return false; +} + +bool LLDBBackend::step_out() +{ + std::lock_guard g{_data_lock}; + if (!_process) + { + return false; + } + + auto thread = _process->GetSelectedThread(); + if (!thread.IsValid()) + { + return false; + } + thread.StepOut(); + + return false; +} + +void LLDBBackend::check_reg_changes() +{ + auto thread = _process->GetSelectedThread(); + if (!thread.IsValid()) + { + // TODO: try to find another thread to select + return; + } + + // TODO: figure out if 0 is always the lowest one? + auto frame = thread.GetFrameAtIndex(0); + const auto regs = frame.GetRegisters(); + uint8_t tmp_buf[64]; + for (size_t set_idx = 0; set_idx < _reg_sets.size(); ++set_idx) + { + auto &set = _reg_sets[set_idx]; + auto lldb_set = regs.GetFirstValueByName(set.set_name.c_str()); + if (!lldb_set.IsValid()) + { + printf("Failed to get set %s for frame\n", set.set_name.c_str()); + continue; + } + + auto change_info = BackToFront::RegsChanged{.set_idx = set_idx}; + + assert(set.names.size() == set.values.size()); + for (size_t i = 0; i < set.names.size(); ++i) + { + auto val = lldb_set.GetChildMemberWithName(set.names[i]); + if (!val.IsValid()) + { + printf("Failed to get %s for frame\n", set.names[i]); + continue; + } + + auto data = val.GetData(); + const auto len = data.GetByteSize(); + if (len != set.values[i].size()) + { + set.values[i].resize(len); + // TODO: error handling + auto error = lldb::SBError{}; + data.ReadRawData(error, 0, set.values[i].data(), len); + change_info.changes.emplace_back(std::make_pair(i, set.values[i])); + continue; + } + + if (len > sizeof(tmp_buf)) + { + printf("Register too large\n"); + exit(1); + } + + // TODO: error handling + auto error = lldb::SBError{}; + data.ReadRawData(error, 0, tmp_buf, len); + if (std::memcmp(tmp_buf, set.values[i].data(), len)) + { + std::copy(tmp_buf, tmp_buf + len, set.values[i].begin()); + change_info.changes.emplace_back(std::make_pair(i, set.values[i])); + } + } + + if (!change_info.changes.empty()) + { + this->send_reg_change(std::move(change_info)); + } + } +} + +/* +Reg output for x64 +Got register set General Purpose Registers +Got register rax (unsigned long) with value: 0 +Got register rbx (unsigned long) with value: 0 +Got register rcx (unsigned long) with value: 0 +Got register rdx (unsigned long) with value: 0 +Got register rdi (unsigned long) with value: 0 +Got register rsi (unsigned long) with value: 0 +Got register rbp (unsigned long) with value: 0 +Got register rsp (unsigned long) with value: 7FFF30F924E0 +Got register r8 (unsigned long) with value: 0 +Got register r9 (unsigned long) with value: 0 +Got register r10 (unsigned long) with value: 0 +Got register r11 (unsigned long) with value: 0 +Got register r12 (unsigned long) with value: 0 +Got register r13 (unsigned long) with value: 0 +Got register r14 (unsigned long) with value: 0 +Got register r15 (unsigned long) with value: 0 +Got register rip (unsigned long) with value: 7FD5ABB9B3B0 +Got register rflags (unsigned long) with value: 200 +Got register cs (unsigned long) with value: 33 +Got register fs (unsigned long) with value: 0 +Got register gs (unsigned long) with value: 0 +Got register ss (unsigned long) with value: 2B +Got register ds (unsigned long) with value: 0 +Got register es (unsigned long) with value: 0 +Got register eax (unsigned int) with value: 0 +Got register ebx (unsigned int) with value: 0 +Got register ecx (unsigned int) with value: 0 +Got register edx (unsigned int) with value: 0 +Got register edi (unsigned int) with value: 0 +Got register esi (unsigned int) with value: 0 +Got register ebp (unsigned int) with value: 0 +Got register esp (unsigned int) with value: 30F924E0 +Got register r8d (unsigned int) with value: 0 +Got register r9d (unsigned int) with value: 0 +Got register r10d (unsigned int) with value: 0 +Got register r11d (unsigned int) with value: 0 +Got register r12d (unsigned int) with value: 0 +Got register r13d (unsigned int) with value: 0 +Got register r14d (unsigned int) with value: 0 +Got register r15d (unsigned int) with value: 0 +Got register ax (unsigned short) with value: 0 +Got register bx (unsigned short) with value: 0 +Got register cx (unsigned short) with value: 0 +Got register dx (unsigned short) with value: 0 +Got register di (unsigned short) with value: 0 +Got register si (unsigned short) with value: 0 +Got register bp (unsigned short) with value: 0 +Got register sp (unsigned short) with value: 24E0 +Got register r8w (unsigned short) with value: 0 +Got register r9w (unsigned short) with value: 0 +Got register r10w (unsigned short) with value: 0 +Got register r11w (unsigned short) with value: 0 +Got register r12w (unsigned short) with value: 0 +Got register r13w (unsigned short) with value: 0 +Got register r14w (unsigned short) with value: 0 +Got register r15w (unsigned short) with value: 0 +Got register ah (unsigned char) with value: 0 +Got register bh (unsigned char) with value: 0 +Got register ch (unsigned char) with value: 0 +Got register dh (unsigned char) with value: 0 +Got register al (unsigned char) with value: 0 +Got register bl (unsigned char) with value: 0 +Got register cl (unsigned char) with value: 0 +Got register dl (unsigned char) with value: 0 +Got register dil (unsigned char) with value: 0 +Got register sil (unsigned char) with value: 0 +Got register bpl (unsigned char) with value: 0 +Got register spl (unsigned char) with value: E0 +Got register r8l (unsigned char) with value: 0 +Got register r9l (unsigned char) with value: 0 +Got register r10l (unsigned char) with value: 0 +Got register r11l (unsigned char) with value: 0 +Got register r12l (unsigned char) with value: 0 +Got register r13l (unsigned char) with value: 0 +Got register r14l (unsigned char) with value: 0 +Got register r15l (unsigned char) with value: 0 +Got register set Floating Point Registers +Got register fctrl (unsigned short) with value: 37F +Got register fstat (unsigned short) with value: 0 +Got register ftag (unsigned short) with value: FFFF +Got register fop (unsigned short) with value: 0 +Got register fiseg (unsigned int) with value: 0 +Got register fioff (unsigned int) with value: 0 +Got register fip (unsigned long) with value: 0 +Got register foseg (unsigned int) with value: 0 +Got register fooff (unsigned int) with value: 0 +Got register fdp (unsigned long) with value: 0 +Got register mxcsr (unsigned int) with value: 1F80 +Got register mxcsrmask (unsigned int) with value: 2FFFF +Got register st0 (unsigned char __attribute__((ext_vector_type(10)))) with value: 0 +Got register st1 (unsigned char __attribute__((ext_vector_type(10)))) with value: 0 +Got register st2 (unsigned char __attribute__((ext_vector_type(10)))) with value: 0 +Got register st3 (unsigned char __attribute__((ext_vector_type(10)))) with value: 0 +Got register st4 (unsigned char __attribute__((ext_vector_type(10)))) with value: 0 +Got register st5 (unsigned char __attribute__((ext_vector_type(10)))) with value: 0 +Got register st6 (unsigned char __attribute__((ext_vector_type(10)))) with value: 0 +Got register st7 (unsigned char __attribute__((ext_vector_type(10)))) with value: 0 +Got register mm0 (unsigned long) with value: 0 +Got register mm1 (unsigned long) with value: 0 +Got register mm2 (unsigned long) with value: 0 +Got register mm3 (unsigned long) with value: 0 +Got register mm4 (unsigned long) with value: 0 +Got register mm5 (unsigned long) with value: 0 +Got register mm6 (unsigned long) with value: 0 +Got register mm7 (unsigned long) with value: 0 +Got register xmm0 (unsigned char __attribute__((ext_vector_type(16)))) with value: 0 +Got register xmm1 (unsigned char __attribute__((ext_vector_type(16)))) with value: 0 +Got register xmm2 (unsigned char __attribute__((ext_vector_type(16)))) with value: 0 +Got register xmm3 (unsigned char __attribute__((ext_vector_type(16)))) with value: 0 +Got register xmm4 (unsigned char __attribute__((ext_vector_type(16)))) with value: 0 +Got register xmm5 (unsigned char __attribute__((ext_vector_type(16)))) with value: 0 +Got register xmm6 (unsigned char __attribute__((ext_vector_type(16)))) with value: 0 +Got register xmm7 (unsigned char __attribute__((ext_vector_type(16)))) with value: 0 +Got register xmm8 (unsigned char __attribute__((ext_vector_type(16)))) with value: 0 +Got register xmm9 (unsigned char __attribute__((ext_vector_type(16)))) with value: 0 +Got register xmm10 (unsigned char __attribute__((ext_vector_type(16)))) with value: 0 +Got register xmm11 (unsigned char __attribute__((ext_vector_type(16)))) with value: 0 +Got register xmm12 (unsigned char __attribute__((ext_vector_type(16)))) with value: 0 +Got register xmm13 (unsigned char __attribute__((ext_vector_type(16)))) with value: 0 +Got register xmm14 (unsigned char __attribute__((ext_vector_type(16)))) with value: 0 +Got register xmm15 (unsigned char __attribute__((ext_vector_type(16)))) with value: 0 +Got register set Advanced Vector Extensions +Got register ymm0 (unsigned char __attribute__((ext_vector_type(32)))) with value: 0 +Got register ymm1 (unsigned char __attribute__((ext_vector_type(32)))) with value: 0 +Got register ymm2 (unsigned char __attribute__((ext_vector_type(32)))) with value: 0 +Got register ymm3 (unsigned char __attribute__((ext_vector_type(32)))) with value: 0 +Got register ymm4 (unsigned char __attribute__((ext_vector_type(32)))) with value: 0 +Got register ymm5 (unsigned char __attribute__((ext_vector_type(32)))) with value: 0 +Got register ymm6 (unsigned char __attribute__((ext_vector_type(32)))) with value: 0 +Got register ymm7 (unsigned char __attribute__((ext_vector_type(32)))) with value: 0 +Got register ymm8 (unsigned char __attribute__((ext_vector_type(32)))) with value: 0 +Got register ymm9 (unsigned char __attribute__((ext_vector_type(32)))) with value: 0 +Got register ymm10 (unsigned char __attribute__((ext_vector_type(32)))) with value: 0 +Got register ymm11 (unsigned char __attribute__((ext_vector_type(32)))) with value: 0 +Got register ymm12 (unsigned char __attribute__((ext_vector_type(32)))) with value: 0 +Got register ymm13 (unsigned char __attribute__((ext_vector_type(32)))) with value: 0 +Got register ymm14 (unsigned char __attribute__((ext_vector_type(32)))) with value: 0 +Got register ymm15 (unsigned char __attribute__((ext_vector_type(32)))) with value: 0 + +*/ + +namespace +{ + using namespace dbgui; } \ No newline at end of file diff --git a/src/backend/lldb/lldb_backend.h b/src/backend/lldb/lldb_backend.h index f007537..e48ea7f 100644 --- a/src/backend/lldb/lldb_backend.h +++ b/src/backend/lldb/lldb_backend.h @@ -5,19 +5,55 @@ #include #include #include +#include +#include -namespace dbgui::backend { - struct LLDBBackend : Backend { - // TODO: source_init_file: false - LLDBBackend(std::string filename); - virtual ~LLDBBackend(); +namespace dbgui::backend +{ + struct LLDBBackend : Backend + { + struct RegSet + { + std::string set_name; + // map from debugger reg idx to frontend reg idx + // std::vector idx_map; + std::vector names; + // TODO: make this data structure not garbage + std::vector> values; + }; - void start() override; + // TODO: source_init_file: false + LLDBBackend(std::string filename); + virtual ~LLDBBackend(); - private: - std::string _filename; - lldb::SBDebugger _instance; - lldb::SBTarget _target; - std::optional _process; - }; -} \ No newline at end of file + void start() override; + + bool step_into() override; + bool step_over() override; + bool step_out() override; + + private: + void run_msg_loop(); + void wait_for_debug_events(); + + void handle_state_change(lldb::StateType); + + void prepare_proc_info(BackToFront::InitialProcessInfo &, + std::vector &); + void dump_threads(); + void check_reg_changes(); + + std::string _filename; + lldb::SBDebugger _instance; + + std::mutex _data_lock; + lldb::SBTarget _target; + std::optional _process; + std::thread _msg_thread; + + // process state + bool _first_run = true; + TargetState _state = TargetState::stopped; + std::vector _reg_sets = {}; + }; +} // namespace dbgui::backend \ No newline at end of file diff --git a/src/frontend/frontend.cpp b/src/frontend/frontend.cpp index 920934c..062f2f8 100644 --- a/src/frontend/frontend.cpp +++ b/src/frontend/frontend.cpp @@ -1,4 +1,5 @@ #include "frontend.h" +#include "backend/lldb/lldb_backend.h" #include "imgui.h" #include "imgui_internal.h" @@ -6,134 +7,287 @@ using namespace dbgui; using namespace dbgui::frontend; -void Frontend::run_frame() { - this->draw_open_popup(); - this->draw_header(); - - // main window - float height = ImGui::GetFrameHeight(); - - const auto vp_size = ImGui::GetMainViewport()->Size; - const auto win_pos = ImVec2{0, height*2}; - const auto win_size = ImVec2{vp_size.x, vp_size.y - height*3}; - - const auto win_flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoMove | - ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoSavedSettings; - - ImGui::SetNextWindowPos(win_pos, ImGuiCond_Always); - ImGui::SetNextWindowSize(win_size, ImGuiCond_Always); - - if (ImGui::Begin("Test", nullptr, win_flags)) { - _dock_id = ImGui::GetID("MyDockSpace"); - ImGui::DockSpace(_dock_id, ImVec2{0, 0}, ImGuiDockNodeFlags_PassthruCentralNode); - - ImGui::End(); - } - - this->draw_status(); +Target::Target(std::string filename) +{ + state = TargetState::startup; + this->filename = filename; + id = 0; + backend = std::make_shared(this->filename.c_str()); } -void Frontend::draw_header() { - ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_MenuBar; - float height = ImGui::GetFrameHeight(); +void Frontend::run_frame() +{ + this->handle_msgs(); + this->draw_open_popup(); + this->draw_header(); - if (ImGui::BeginMainMenuBar()) { - if (ImGui::BeginMenu("File")) { - if (ImGui::MenuItem("Open")) { - _draw_open_popup = true; - ImGui::OpenPopup(_open_popup_id); - } + // main window + float height = ImGui::GetFrameHeight(); - ImGui::EndMenu(); - } - ImGui::EndMainMenuBar(); - } + const auto vp_size = ImGui::GetMainViewport()->Size; + const auto win_pos = ImVec2{0, height * 2}; + const auto win_size = ImVec2{vp_size.x, vp_size.y - height * 3}; - if (ImGui::BeginViewportSideBar("##SecondaryMenuBar", NULL, ImGuiDir_Up, height, window_flags)) { - if (ImGui::BeginMenuBar()) { - const auto orig_cursor_x = ImGui::GetCursorPosX(); - if (_target) { - switch (_target->state) { - using enum backend::TargetState; + const auto win_flags = + ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoNav + | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollWithMouse + | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoSavedSettings; - case stopped: - ImGui::Button("Run"); - break; - case paused: - ImGui::Button("Continue"); - break; - case running: - ImGui::Button("Pause"); - break; - } - } else { - ImGui::BeginDisabled(); - ImGui::Button("Continue"); - ImGui::EndDisabled(); - } + ImGui::SetNextWindowPos(win_pos, ImGuiCond_Always); + ImGui::SetNextWindowSize(win_size, ImGuiCond_Always); - // 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); + if (ImGui::Begin("Test", nullptr, win_flags)) + { + this->dock_id = ImGui::GetID("MyDockSpace"); + ImGui::DockSpace(this->dock_id, ImVec2{0, 0}, + ImGuiDockNodeFlags_PassthruCentralNode); - ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical, 2.f); + for (auto &window : _windows) + { + window.draw(*this); + } - const auto is_paused = _target && _target->state == backend::TargetState::paused; - const auto is_running = _target && _target->state == backend::TargetState::running; + ImGui::End(); + } - if (!is_paused) { - ImGui::BeginDisabled(); - } - - ImGui::Button("Step Over"); - ImGui::Button("Step Into"); - ImGui::Button("Step Out"); - - if (!is_paused) { - ImGui::EndDisabled(); - } - - ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical, 2.f); - - if (!is_paused && !is_running) { - ImGui::BeginDisabled(); - } - - ImGui::Button("Stop"); - ImGui::Button("Restart"); - - if (!is_paused && !is_running) { - ImGui::EndDisabled(); - } - - ImGui::EndMenuBar(); - } - ImGui::End(); - } + this->draw_status(); } -void Frontend::draw_status() { - ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_MenuBar; - float height = ImGui::GetFrameHeight(); +void Frontend::draw_header() +{ + ImGuiWindowFlags window_flags = + ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings + | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_MenuBar; + float height = ImGui::GetFrameHeight(); - if (ImGui::BeginViewportSideBar("##MainStatusBar", NULL, ImGuiDir_Down, height, window_flags)) { - if (ImGui::BeginMenuBar()) { - ImGui::Text("Happy status bar"); - ImGui::EndMenuBar(); - } - ImGui::End(); - } + if (ImGui::BeginMainMenuBar()) + { + if (ImGui::BeginMenu("File")) + { + if (ImGui::MenuItem("Open")) + { + _draw_open_popup = true; + ImGui::OpenPopup(_open_popup_id); + } + + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("Window")) + { + if (ImGui::MenuItem("Registers")) + { + _windows.push_back(Window::create_regs(this->window_id++)); + } + ImGui::EndMenu(); + } + ImGui::EndMainMenuBar(); + } + + if (ImGui::BeginViewportSideBar("##SecondaryMenuBar", NULL, ImGuiDir_Up, + height, window_flags)) + { + if (ImGui::BeginMenuBar()) + { + const auto orig_cursor_x = ImGui::GetCursorPosX(); + if (this->target) + { + switch (this->target->state) + { + using enum TargetState; + + case startup: + ImGui::BeginDisabled(); + // buttons shouldn't flicker that way + ImGui::Button("Continue"); + ImGui::EndDisabled(); + break; + + case stopped: ImGui::Button("Run"); break; + case paused: ImGui::Button("Continue"); break; + case running: ImGui::Button("Pause"); break; + } + } else + { + ImGui::BeginDisabled(); + ImGui::Button("Continue"); + 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 = + this->target && this->target->state == TargetState::paused; + const auto is_running = + this->target && this->target->state == TargetState::running; + + if (!is_paused) + { + ImGui::BeginDisabled(); + } + + if (ImGui::Button("Step Over")) {} + if (ImGui::Button("Step Into")) + { + if (this->target->backend->step_into()) + { + // TODO: already disable the UI into running mode + } + } + ImGui::Button("Step Out"); + + if (!is_paused) + { + ImGui::EndDisabled(); + } + + ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical, 2.f); + + if (!is_paused && !is_running) + { + ImGui::BeginDisabled(); + } + + ImGui::Button("Stop"); + ImGui::Button("Restart"); + + if (!is_paused && !is_running) + { + ImGui::EndDisabled(); + } + + ImGui::EndMenuBar(); + } + ImGui::End(); + } } -void Frontend::draw_open_popup() { - if (!_open_popup_id) { - _open_popup_id = ImGui::GetID("Open Executable##OpenPopup"); - } - if (ImGui::BeginPopupModal("Open Executable##OpenPopup", &_draw_open_popup, ImGuiWindowFlags_AlwaysAutoResize)) { - ImGui::InputText("File Path", _open_popup_name_buf.data(), _open_popup_name_buf.size()); +void Frontend::draw_status() +{ + ImGuiWindowFlags window_flags = + ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings + | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_MenuBar; + float height = ImGui::GetFrameHeight(); - if (ImGui::Button("Start")) { - - } - ImGui::EndPopup(); - } + if (ImGui::BeginViewportSideBar("##MainStatusBar", NULL, ImGuiDir_Down, + height, window_flags)) + { + if (ImGui::BeginMenuBar()) + { + ImGui::Text("Happy status bar"); + ImGui::EndMenuBar(); + } + ImGui::End(); + } +} + +void Frontend::draw_open_popup() +{ + if (!_open_popup_id) + { + _open_popup_id = ImGui::GetID("Open Executable##OpenPopup"); + } + if (ImGui::BeginPopupModal("Open Executable##OpenPopup", &_draw_open_popup, + ImGuiWindowFlags_AlwaysAutoResize)) + { + ImGui::InputText("File Path", _open_popup_name_buf.data(), + _open_popup_name_buf.size()); + + if (ImGui::Button("Start")) + { + this->target = Target{_open_popup_name_buf.data()}; + this->target->backend->start(); + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } +} + +void Frontend::handle_msgs() +{ + if (!this->target) + { + return; + } + + while (true) + { + auto opt = this->target->backend->retrieve_msg_for_frontend(); + if (!opt) + { + break; + } + auto *msg = opt->get(); + + printf("Got msg %u\n", msg->type); + + switch (msg->type) + { + using enum BackToFront::MsgType; + + case state_change: + { + const auto &info = std::get(msg->data); + this->handle_state_change(info); + break; + } + case ip_change: + // TODO + break; + case initial_proc_info: + this->handle_proc_info( + std::move(std::get(msg->data))); + break; + case regs_changed: + this->handle_reg_change(std::get(msg->data)); + break; + } + } +} + +void Frontend::handle_state_change(const BackToFront::StateChange &info) +{ + this->target->state = info.new_state; + + // TODO: display in status bar + printf("State changed to %u because %u\n", info.new_state, info.reason); +} + +void Frontend::handle_proc_info(BackToFront::InitialProcessInfo &&info) +{ + this->target->arch = info.arch; + this->target->id = info.pid; + + auto &sets = this->target->reg_sets; + printf("RegSet Size: %lu\n", info.reg_sets.size()); + for (size_t set_idx = 0; set_idx < info.reg_sets.size(); ++set_idx) + { + sets.emplace_back(); + auto &set = sets.back(); + printf("Got set %s\n", info.reg_sets[set_idx].name.c_str()); + set.name = std::move(info.reg_sets[set_idx].name); + + for (size_t i = 0; i < info.reg_sets[set_idx].reg_names.size(); ++i) + { + set.regs.emplace_back(); + printf(" Got reg %s\n", info.reg_sets[set_idx].reg_names[i].c_str()); + set.regs.back().name = std::move(info.reg_sets[set_idx].reg_names[i]); + } + } +} + +void Frontend::handle_reg_change(const BackToFront::RegsChanged &info) +{ + auto &set = this->target->reg_sets[info.set_idx]; + for (const auto &[idx, val] : info.changes) + { + auto ® = set.regs[idx]; + reg.bytes.resize(val.size()); + + // TODO: opt for uint64? + std::copy(val.begin(), val.end(), reg.bytes.begin()); + } } \ No newline at end of file diff --git a/src/frontend/frontend.h b/src/frontend/frontend.h index 20e53e6..f3e8506 100644 --- a/src/frontend/frontend.h +++ b/src/frontend/frontend.h @@ -5,39 +5,47 @@ #include #include #include +#include #include "imgui.h" +#include "frontend/target.h" #include "backend/debug_backend.h" +#include "frontend/window.h" -namespace dbgui::frontend { +namespace dbgui::frontend +{ - struct Target { - backend::TargetState state = backend::TargetState::stopped; - std::string filename; - uint64_t id; + struct Frontend + { + Frontend() + { + _open_popup_name_buf.resize(512); + const char *str = "/home/klee/projects/dbgui/tmp/main"; + std::memcpy(_open_popup_name_buf.data(), str, strlen(str) + 1); + } - std::unique_ptr backend = nullptr; - }; + void run_frame(); - struct Frontend { - Frontend() { - _open_popup_name_buf.resize(512); - } + std::optional target = {}; + ImGuiID dock_id = 0; + uint64_t window_id = 0; - void run_frame(); + private: + void draw_header(); + void draw_open_popup(); + void draw_status(); - private: - void draw_header(); - void draw_open_popup(); - void draw_status(); + void handle_msgs(); + void handle_state_change(const BackToFront::StateChange &); + void handle_proc_info(BackToFront::InitialProcessInfo &&); + void handle_reg_change(const BackToFront::RegsChanged &); - bool _draw_second = false; - ImGuiID _dock_id = 0; + bool _draw_second = false; - bool _draw_open_popup = false; - ImGuiID _open_popup_id = 0; - std::vector _open_popup_name_buf = {}; + bool _draw_open_popup = false; + ImGuiID _open_popup_id = 0; + std::vector _open_popup_name_buf = {}; - std::optional _target = {}; - }; -} \ No newline at end of file + std::vector _windows = {}; + }; +} // namespace dbgui::frontend \ No newline at end of file diff --git a/src/frontend/target.h b/src/frontend/target.h new file mode 100644 index 0000000..8e5d39b --- /dev/null +++ b/src/frontend/target.h @@ -0,0 +1,39 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "backend/debug_backend.h" + +namespace dbgui::frontend +{ + struct Target + { + struct Reg + { + std::string name; + // TODO: handle registers as sets of 64bit? + std::vector bytes; + }; + + struct RegSet + { + std::string name; + std::vector regs; + }; + + Target(std::string filename); + + TargetState state = TargetState::stopped; + std::string filename; + uint64_t id; + Arch arch; + + std::vector reg_sets; + + std::shared_ptr backend = nullptr; + }; +} // namespace dbgui::frontend \ No newline at end of file diff --git a/src/frontend/window.cpp b/src/frontend/window.cpp new file mode 100644 index 0000000..4d2ed93 --- /dev/null +++ b/src/frontend/window.cpp @@ -0,0 +1,101 @@ +#include "window.h" +#include "frontend.h" + +using namespace dbgui; +using namespace dbgui::frontend; + +void Window::draw(const Frontend &frontend) +{ + switch (this->type) + { + using enum WindowType; + + case regs: std::get(this->data).draw(frontend); break; + default: printf("Unhandled window draw: %u\n", this->type); exit(1); + } +} + +Window Window::create_regs(size_t id) +{ + auto id_str = std::string{"Registers##"}; + id_str.append(std::to_string(id)); + + return Window{.type = WindowType::regs, + .data = RegWindow{.id = id_str, .open = true}}; +} + +void RegWindow::draw(const Frontend &frontend) +{ + ImGui::SetNextWindowDockID(frontend.dock_id, ImGuiCond_Appearing); + if (!ImGui::Begin(this->id.c_str())) + { + return; + } + + if (!frontend.target) + { + ImGui::End(); + return; + } + + if (ImGui::BeginTable("table", 1, + ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) + { + const auto &sets = frontend.target->reg_sets; + for (size_t set_idx = 0; set_idx < sets.size(); ++set_idx) + { + const auto &set = sets[set_idx]; + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::Text(set.name.c_str()); + + char buf[20]; + std::snprintf(buf, sizeof(buf), "nested_%lu", set_idx); + if (ImGui::BeginTable(buf, 2, + ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) + { + for (size_t i = 0; i < set.regs.size(); ++i) + { + const auto ® = set.regs[i]; + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::Text(reg.name.c_str()); + ImGui::TableNextColumn(); + + if (reg.bytes.size() == 0) + { + ImGui::Text(""); + continue; + } + + switch (reg.bytes.size()) + { + case 1: std::snprintf(buf, sizeof(buf), "%X", reg.bytes[0]); break; + case 2: + std::snprintf( + buf, sizeof(buf), "%X", + *reinterpret_cast(reg.bytes.data())); + break; + case 4: + std::snprintf( + buf, sizeof(buf), "%X", + *reinterpret_cast(reg.bytes.data())); + break; + case 8: + std::snprintf( + buf, sizeof(buf), "%lX", + *reinterpret_cast(reg.bytes.data())); + break; + default: std::snprintf(buf, sizeof(buf), ""); break; + } + + ImGui::Text(buf); + } + ImGui::EndTable(); + } + } + ImGui::EndTable(); + } + + ImGui::End(); +} \ No newline at end of file diff --git a/src/frontend/window.h b/src/frontend/window.h index 006a81e..53edba0 100644 --- a/src/frontend/window.h +++ b/src/frontend/window.h @@ -3,29 +3,40 @@ #include #include #include +#include +#include "msg.h" +#include "frontend/target.h" -namespace dbgui::frontend { - enum class WindowType : uint8_t { - regs, - source, - memory, - variables, - stack, - threads, - disassembly, - }; +namespace dbgui::frontend +{ + struct Frontend; - struct RegWindow { - enum class RegValType : uint8_t { - flag, - u64, - u128, - u256, - u512, - }; + enum class WindowType : uint8_t + { + regs, + source, + memory, + variables, + stack, + threads, + disassembly, + }; - struct Reg { - std::string name; + struct RegWindow + { + enum class RegValType : uint8_t + { + flag, + u64, + u128, + u256, + u512, + }; + + // TODO: store last_drawn val and which regs we should draw + /*struct Reg { + uint16_t set_idx; + uint16_t reg_idx; RegValType type; union { bool bval; @@ -36,12 +47,21 @@ namespace dbgui::frontend { }; }; - std::vector regs; + std::vector regs;*/ - void draw(); - }; + void draw(const Frontend &); - struct Window { - WindowType type; - }; -} \ No newline at end of file + std::string id; + bool open; + }; + + struct Window + { + WindowType type; + std::variant data; + + void draw(const Frontend &); + + static Window create_regs(size_t window_id); + }; +} // namespace dbgui::frontend \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index d4b3ae1..de9af68 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -10,136 +10,143 @@ #include "backends/imgui_impl_opengl3.h" #include #define GL_SILENCE_DEPRECATION -#include // Will drag system OpenGL headers +#include // Will drag system OpenGL headers #include "frontend/frontend.h" -static void glfw_error_callback(int error, const char* description) +static void glfw_error_callback(int error, const char *description) { - fprintf(stderr, "GLFW Error %d: %s\n", error, description); + fprintf(stderr, "GLFW Error %d: %s\n", error, description); } // Main code -int main(int, char**) +int main(int, char **) { - glfwSetErrorCallback(glfw_error_callback); - if (!glfwInit()) - return 1; + glfwSetErrorCallback(glfw_error_callback); + if (!glfwInit()) + return 1; - // Decide GL+GLSL versions - // GL 3.0 + GLSL 130 - const char* glsl_version = "#version 130"; - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only - glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // 3.0+ only + // Decide GL+GLSL versions + // GL 3.0 + GLSL 130 + const char *glsl_version = "#version 130"; + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // 3.0+ only - // Create window with graphics context - GLFWwindow* window = glfwCreateWindow(1280, 720, "Dear ImGui GLFW+OpenGL3 example", nullptr, nullptr); - if (window == nullptr) - return 1; - glfwMakeContextCurrent(window); - glfwSwapInterval(1); // Enable vsync + // Create window with graphics context + GLFWwindow *window = glfwCreateWindow( + 1280, 720, "Dear ImGui GLFW+OpenGL3 example", nullptr, nullptr); + if (window == nullptr) + return 1; + glfwMakeContextCurrent(window); + glfwSwapInterval(1); // Enable vsync - // Setup Dear ImGui context - IMGUI_CHECKVERSION(); - ImGui::CreateContext(); - ImGuiIO& io = ImGui::GetIO(); (void)io; - io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls - io.ConfigFlags &= ~ImGuiConfigFlags_NavEnableGamepad; // Disable Gamepad Controls - io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking - io.ConfigFlags &= ~ImGuiConfigFlags_ViewportsEnable; // Disable Multi-Viewport / Platform Windows - //io.ConfigViewportsNoAutoMerge = true; - //io.ConfigViewportsNoTaskBarIcon = true; + // Setup Dear ImGui context + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO &io = ImGui::GetIO(); + (void)io; + io.ConfigFlags |= + ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags &= + ~ImGuiConfigFlags_NavEnableGamepad; // Disable Gamepad Controls + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking + io.ConfigFlags &= + ~ImGuiConfigFlags_ViewportsEnable; // Disable Multi-Viewport / Platform Windows + //io.ConfigViewportsNoAutoMerge = true; + //io.ConfigViewportsNoTaskBarIcon = true; - // Setup Dear ImGui style - ImGui::StyleColorsDark(); - //ImGui::StyleColorsLight(); + // Setup Dear ImGui style + ImGui::StyleColorsDark(); + //ImGui::StyleColorsLight(); - // When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones. - ImGuiStyle& style = ImGui::GetStyle(); - if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) - { - style.WindowRounding = 0.0f; - style.Colors[ImGuiCol_WindowBg].w = 1.0f; - } + // When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones. + ImGuiStyle &style = ImGui::GetStyle(); + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + style.WindowRounding = 0.0f; + style.Colors[ImGuiCol_WindowBg].w = 1.0f; + } - // Setup Platform/Renderer backends - ImGui_ImplGlfw_InitForOpenGL(window, true); - ImGui_ImplOpenGL3_Init(glsl_version); + // Setup Platform/Renderer backends + ImGui_ImplGlfw_InitForOpenGL(window, true); + ImGui_ImplOpenGL3_Init(glsl_version); - // Load Fonts - // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. - // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. - // - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit). - // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. - // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering. - // - Read 'docs/FONTS.md' for more instructions and details. - // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! - // - Our Emscripten build process allows embedding fonts to be accessible at runtime from the "fonts/" folder. See Makefile.emscripten for details. - //io.Fonts->AddFontDefault(); - //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf", 18.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); - //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, nullptr, io.Fonts->GetGlyphRangesJapanese()); - //IM_ASSERT(font != nullptr); - io.Fonts->AddFontFromMemoryCompressedBase85TTF(roboto_medium_compressed_data_base85, 16.0f); + // Load Fonts + // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. + // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. + // - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit). + // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. + // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering. + // - Read 'docs/FONTS.md' for more instructions and details. + // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! + // - Our Emscripten build process allows embedding fonts to be accessible at runtime from the "fonts/" folder. See Makefile.emscripten for details. + //io.Fonts->AddFontDefault(); + //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf", 18.0f); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); + //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, nullptr, io.Fonts->GetGlyphRangesJapanese()); + //IM_ASSERT(font != nullptr); + io.Fonts->AddFontFromMemoryCompressedBase85TTF( + roboto_medium_compressed_data_base85, 16.0f); - // Our state - bool show_demo_window = true; - bool show_another_window = false; - ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); + // Our state + bool show_demo_window = true; + bool show_another_window = false; + ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); - auto frontend = dbgui::frontend::Frontend{}; + auto frontend = dbgui::frontend::Frontend{}; - // Main loop - while (!glfwWindowShouldClose(window)) - { - // Poll and handle events (inputs, window resize, etc.) - // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. - // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data. - // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data. - // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. - glfwPollEvents(); + // Main loop + while (!glfwWindowShouldClose(window)) + { + // Poll and handle events (inputs, window resize, etc.) + // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. + // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data. + // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data. + // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. + glfwPollEvents(); - // Start the Dear ImGui frame - ImGui_ImplOpenGL3_NewFrame(); - ImGui_ImplGlfw_NewFrame(); - ImGui::NewFrame(); + // Start the Dear ImGui frame + ImGui_ImplOpenGL3_NewFrame(); + ImGui_ImplGlfw_NewFrame(); + ImGui::NewFrame(); - frontend.run_frame(); + frontend.run_frame(); - // Rendering - ImGui::Render(); - int display_w, display_h; - glfwGetFramebufferSize(window, &display_w, &display_h); - glViewport(0, 0, display_w, display_h); - glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w); - glClear(GL_COLOR_BUFFER_BIT); - ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + // Rendering + ImGui::Render(); + int display_w, display_h; + glfwGetFramebufferSize(window, &display_w, &display_h); + glViewport(0, 0, display_w, display_h); + glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, + clear_color.z * clear_color.w, clear_color.w); + glClear(GL_COLOR_BUFFER_BIT); + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); - // Update and Render additional Platform Windows - // (Platform functions may change the current OpenGL context, so we save/restore it to make it easier to paste this code elsewhere. - // For this specific demo app we could also call glfwMakeContextCurrent(window) directly) - if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) - { - GLFWwindow* backup_current_context = glfwGetCurrentContext(); - ImGui::UpdatePlatformWindows(); - ImGui::RenderPlatformWindowsDefault(); - glfwMakeContextCurrent(backup_current_context); - } + // Update and Render additional Platform Windows + // (Platform functions may change the current OpenGL context, so we save/restore it to make it easier to paste this code elsewhere. + // For this specific demo app we could also call glfwMakeContextCurrent(window) directly) + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + GLFWwindow *backup_current_context = glfwGetCurrentContext(); + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); + glfwMakeContextCurrent(backup_current_context); + } - glfwSwapBuffers(window); - } + glfwSwapBuffers(window); + } - // Cleanup - ImGui_ImplOpenGL3_Shutdown(); - ImGui_ImplGlfw_Shutdown(); - ImGui::DestroyContext(); + // Cleanup + ImGui_ImplOpenGL3_Shutdown(); + ImGui_ImplGlfw_Shutdown(); + ImGui::DestroyContext(); - glfwDestroyWindow(window); - glfwTerminate(); + glfwDestroyWindow(window); + glfwTerminate(); - return 0; + return 0; } diff --git a/src/msg.h b/src/msg.h index cbdab0c..0d59678 100644 --- a/src/msg.h +++ b/src/msg.h @@ -3,24 +3,93 @@ #include #include #include +#include -namespace dbgui { - struct FrontToBackMsg { +namespace dbgui +{ + enum class TargetState : uint8_t + { + startup, + stopped, + running, + paused, + }; - }; + enum class StateChangeReason : uint8_t + { + unknown, + initial_entry, + breakpoint, + exception, + // ... + }; - struct BackToFrontMsg { - enum class Type : uint8_t { - log - }; + enum class Arch : uint8_t + { + unknown, + x86_64, + // x86 + }; - struct LogMsg { - // TODO: level + namespace FrontToBack + { + enum class MsgType : uint8_t + { + }; - std::string msg; - }; + struct Msg + { + MsgType type; + std::variant data; + }; + } // namespace FrontToBack - Type type; - std::variant data; - }; -} \ No newline at end of file + namespace BackToFront + { + enum class MsgType : uint8_t + { + state_change, + ip_change, + initial_proc_info, + regs_changed, + }; + + struct StateChange + { + TargetState new_state; + StateChangeReason reason; + }; + + struct IPChange + { + uint64_t new_ip; + }; + + struct InitialProcessInfo + { + struct RegSet + { + std::string name; + std::vector reg_names; + }; + + uint64_t pid; + Arch arch; + std::vector reg_sets; + }; + + struct RegsChanged + { + size_t set_idx; + std::vector>> changes; + }; + + struct Msg + { + MsgType type; + std::variant + data; + }; + } // namespace BackToFront +} // namespace dbgui \ No newline at end of file diff --git a/tmp/main b/tmp/main new file mode 100755 index 0000000000000000000000000000000000000000..d740caeb8467e03a2464fda68ae8f8c27e30b85c GIT binary patch literal 23320 zcmeHPZEPGz8J;^kiBposNl0Qypqt>9B$YQlCj8g3_jfmWYoW?>qCp z=iT*g6qN`{H6!i5JJ0(uGtbVBZ+FJ?rQX55bSfn{rNtqEIiuNlEvS zB|ao}h}D2J!eL1bN_EBZQd%ikHj!NeNc3yL3n{IT+#z!UN>GVwh!XvllSP+Go+Bko z(X7&5_?6*|Y4yAdpyM8JI?|`1yBI z-cj;vHe?vlOnkCzRj^>m<$W6bke6Fce49tOo(cEiW#W@1WyQXNH*xU5zCymO;1#QL zZF8Lm+YTNuDZk>U@Z20isvnO9`%09m8 z;y!roJhkYjYZtO7UuZfngP=b1=YfTbpUo~@&Ypbr+R?Gz%wp!1>`O1L-e`&1mT!UA zUnk8CcYUR9e||Z8=3J9yiR^uN_BWqH6D(rwa|qOyCeNIoTsGctmnD87?Z5oG6bsj~ z&%b_u_WA1#+0={Ki#Pm@^$2SyLewAYd*Z%5P$^N}d@Q?g(1uK(%tJ<4?!TnbGy|G}cL@V9`HSfSt)u2Oo4Cdk(>f*Kh0u~$>nYztejlJbuZP}F zd4Bi%AmuGu|BUa8m{|YTjamuwiwzbEwYD4N?nVou2+?-#iCc7GNy95=#3SIG+efIipA1|SB$mg zXGO)`+duq(m{_%U`57ZTjo*wmgyP-{%f)k#+PQ*LnHF;Ww^Yme?afWvv*nWS=6tVI zMACVTpm=@5BVz-74~ZK3Yz@biE6yK$(C!)??FFmhBcq^S9kSWG2G`H?_a+r{C*C%( zF=V$>)v@e41#hZ|D$Td%JFb6Y-_ZSAk0;m<^mwAj@Bj4gC3Yh{p6Kxe`+*)$^mwAj z6aD*JzwhYx%XiEBJpNgFFaJK_?@ufZY%nJM{RF<%-Tv<<%ri?vlCAkx{q6SieU!cn z@;B7~HQ#u<-wWdjey7CmvAAsxJSU;=lu^$4yQQ+dH}<C6mRKj}Nn-E=XaG3>ZDISt1ROrs#@e4sz@JXQf0=+^PQd?>fVThW1{k}G)?-WfD9)5+mxDzU6 zNn^;$^ocaqXPdzPGY+QTfWamhPq@GF{Lv2)hyKIQ(LLn!8sHcw+OF$l&vS~*)xQAW znBKag+_g~9rZmhvMf#h_mHmua5++U1+0KM#7h%@S%*A20Tg=Dd=5)!A!_huTXI6^^ z?@_lfpXhiq15@r4Rh>PUK26vjx|8^JaUzg*^#3 z+jh!jXCCV9m*>S~*_m{=B9@`dn z&qGJLh6cI;h6NsWZ4gt`t7w8=39 z&8KmZf|;K$LJ>;(WnrS`^KQ9>Gntk!N0`u^W}ug-+Q@HQn} zk0pPCQoNQ1OJ4DQoxC24)Y6u(QGNstTtfwaW@YLmf8L+cBAkVMpWzJ1gKz}>c|S_t zr?NF*i@=hhc_r%SX!);z27WcmK8QyAp2GXt zM(sJz^0(lF-+HLK*WZgQ+J}|xFg7O6v-|^Kc<KbrW=sKSX_U8 zzi7Ng{{{$%G0D6hn*ATHBkF?7XSoh8klPbv*EDX)t8oh0x=kr~oo`%w>V2PLpg7&H zGA;CA<8#~r2lK|ft_6(St~}KF2SZA3;1@Gea=qCWAJaJhpNuFA`Q3<(K}P-m4Kh~s AQUCw| literal 0 HcmV?d00001 diff --git a/tmp/main.c b/tmp/main.c new file mode 100644 index 0000000..15be7df --- /dev/null +++ b/tmp/main.c @@ -0,0 +1,5 @@ +#include + +int main(int argc, char* argv[]) { + return 0; +}