diff options
| author | hachem <im@hachem.wtf> | 2026-08-28 16:46:18 +0200 |
|---|---|---|
| committer | hachem <im@hachem.wtf> | 2026-08-28 16:46:18 +0200 |
| commit | f3c010f6cea20335d9697c1c2f657e71c70fb1dc (patch) | |
| tree | 1c09477e4e2619f24f1f33bfca8f0684eadd3d16 /src | |
| parent | af616a407627d87a787ae78cc36c58ac4ed3ccad (diff) | |
feat: local variables, registers
Diffstat (limited to 'src')
| -rw-r--r-- | src/debugger/debugger.cpp | 157 | ||||
| -rw-r--r-- | src/debugger/debugger.h | 1 |
2 files changed, 153 insertions, 5 deletions
diff --git a/src/debugger/debugger.cpp b/src/debugger/debugger.cpp index 8d3595c..c5cc615 100644 --- a/src/debugger/debugger.cpp +++ b/src/debugger/debugger.cpp @@ -176,6 +176,63 @@ namespace Hsdbg return message != nullptr && message[0] != '\0' ? message : fallback; } + // a local can be a whole object graph, and every level costs a read from + // the inferior, so stop well before anything recursive gets interesting + constexpr uint32_t MAX_VARIABLE_DEPTH = 3; + constexpr uint32_t MAX_VARIABLE_CHILDREN = 64; + + auto text_or(const char* text, std::string_view fallback) -> std::string + { + return text != nullptr && text[0] != '\0' ? std::string(text) : std::string(fallback); + } + + auto frame_of(lldb::SBProcess& process, uint64_t thread_id, uint32_t frame_index) -> lldb::SBFrame + { + lldb::SBThread thread = process.GetThreadByID(thread_id); + + if (!thread.IsValid()) + return {}; + + return thread.GetFrameAtIndex(frame_index); + } + + auto display_of(lldb::SBValue& value) -> std::string + { + const char* text = value.GetValue(); + const char* summary = value.GetSummary(); + + if (text != nullptr && summary != nullptr) + return std::format("{} {}", text, summary); + + if (text != nullptr) + return text; + + return summary != nullptr ? summary : ""; + } + + auto to_variable(lldb::SBValue value, uint32_t depth) -> Variable + { + Variable variable; + variable.name = text_or(value.GetName(), "?"); + variable.type = text_or(value.GetDisplayTypeName(), ""); + variable.value = display_of(value); + + if (depth >= MAX_VARIABLE_DEPTH) + return variable; + + const uint32_t children = value.GetNumChildren(MAX_VARIABLE_CHILDREN); + + for (uint32_t index = 0; index < children; ++index) + { + lldb::SBValue child = value.GetChildAtIndex(index); + + if (child.IsValid()) + variable.children.push_back(to_variable(child, depth + 1)); + } + + return variable; + } + auto name_of(lldb::SBFrame& frame) -> std::string { if (const char* display = frame.GetDisplayFunctionName(); display != nullptr) @@ -705,15 +762,51 @@ namespace Hsdbg auto Debugger::evaluate(std::string_view expression) -> Result<std::string> { - (void)expression; - return not_implemented(); + if (const Result<void> ready = require_stopped(); !ready) + return std::unexpected(ready.error()); + + lldb::SBFrame frame = frame_of(m_session->process, m_selected_thread, m_selected_frame); + + if (!frame.IsValid()) + return fail("no frame selected"); + + const std::string text(expression); + lldb::SBValue value = frame.EvaluateExpression(text.c_str()); + + if (lldb::SBError error = value.GetError(); error.Fail()) + return fail("{}", message_of(error, "could not evaluate")); + + const std::string display = display_of(value); + + if (display.empty()) + return fail("'{}' has no value", text); + + const std::string type = text_or(value.GetDisplayTypeName(), ""); + + return type.empty() ? display : std::format("({}) {}", type, display); } auto Debugger::read_memory(uint64_t address, size_t size) -> Result<std::vector<uint8_t>> { - (void)address; - (void)size; - return not_implemented(); + if (!is_alive(m_session->process)) + return fail("no running process"); + + std::vector<uint8_t> buffer(size); + + lldb::SBError error; + const size_t read = m_session->process.ReadMemory(address, buffer.data(), size, error); + + if (error.Fail()) + { + return fail("could not read {} bytes at {:#x}: {}", + size, + address, + message_of(error, "unknown error")); + } + + buffer.resize(read); + + return buffer; } auto Debugger::select_thread(uint64_t thread_id) -> bool @@ -730,6 +823,7 @@ namespace Hsdbg m_session->process.SetSelectedThreadByID(m_selected_thread); refresh_call_stack(); + refresh_frame_data(); return true; } @@ -749,6 +843,8 @@ namespace Hsdbg thread.SetSelectedFrame(frame_index); } + refresh_frame_data(); + return true; } @@ -812,8 +908,11 @@ namespace Hsdbg switch (lldb::SBProcess::GetStateFromEvent(event)) { + // frame data belongs to a frame that no longer exists case lldb::eStateRunning: case lldb::eStateStepping: + m_locals.clear(); + m_registers.clear(); set_state(TargetState::Running); break; @@ -927,6 +1026,7 @@ namespace Hsdbg m_selected_frame = 0; refresh_call_stack(); + refresh_frame_data(); set_state(TargetState::Stopped); @@ -1006,6 +1106,53 @@ namespace Hsdbg } } + auto Debugger::refresh_frame_data() -> void + { + m_locals.clear(); + m_registers.clear(); + + if (!is_alive(m_session->process)) + return; + + lldb::SBFrame frame = frame_of(m_session->process, m_selected_thread, m_selected_frame); + + if (!frame.IsValid()) + return; + + lldb::SBValueList variables = frame.GetVariables(true, true, false, true); + + for (uint32_t index = 0; index < variables.GetSize(); ++index) + { + lldb::SBValue value = variables.GetValueAtIndex(index); + + if (value.IsValid()) + m_locals.push_back(to_variable(value, 0)); + } + + lldb::SBValueList sets = frame.GetRegisters(); + + for (uint32_t set_index = 0; set_index < sets.GetSize(); ++set_index) + { + lldb::SBValue set = sets.GetValueAtIndex(set_index); + const uint32_t count = set.GetNumChildren(); + + for (uint32_t index = 0; index < count; ++index) + { + lldb::SBValue source = set.GetChildAtIndex(index); + + // vector registers do not fit the one word a Register holds + if (!source.IsValid() || source.GetByteSize() > sizeof(uint64_t)) + continue; + + Register entry; + entry.name = text_or(source.GetName(), "?"); + entry.value = source.GetValueAsUnsigned(); + + m_registers.push_back(std::move(entry)); + } + } + } + auto Debugger::resolve_breakpoint(Breakpoint& breakpoint) -> void { if (!m_session->target.IsValid()) diff --git a/src/debugger/debugger.h b/src/debugger/debugger.h index adbf91d..c7e5324 100644 --- a/src/debugger/debugger.h +++ b/src/debugger/debugger.h @@ -96,6 +96,7 @@ namespace Hsdbg auto on_stopped() -> void; auto on_exited() -> void; auto refresh_call_stack() -> void; + auto refresh_frame_data() -> void; std::unique_ptr<Session> m_session; |
