diff options
| author | hachem <im@hachem.wtf> | 2026-08-27 14:17:34 +0200 |
|---|---|---|
| committer | hachem <im@hachem.wtf> | 2026-08-27 14:17:34 +0200 |
| commit | 705e3914e4e820c3ac92892c32c93479691a09e7 (patch) | |
| tree | bad8932559620328da060ea45245fcabec2332f2 /src/debugger/debugger.cpp | |
| parent | 90398614bcc9e1c1bde7a0f65a0db66173a1d8b4 (diff) | |
add: debugger interface
Diffstat (limited to 'src/debugger/debugger.cpp')
| -rw-r--r-- | src/debugger/debugger.cpp | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/src/debugger/debugger.cpp b/src/debugger/debugger.cpp new file mode 100644 index 0000000..7cd2d01 --- /dev/null +++ b/src/debugger/debugger.cpp @@ -0,0 +1,266 @@ +#include "debugger/debugger.h" + +#include "core/log.h" + +#include <algorithm> +#include <source_location> + +namespace Hsdbg +{ + namespace + { + // every call site reports itself, so the stubs stay one line each until + // there is an lldb session behind them + auto not_implemented(std::source_location location = std::source_location::current()) + -> std::unexpected<std::string> + { + Log::warn("{} is not implemented yet", location.function_name()); + return fail("{} is not implemented yet", location.function_name()); + } + } + + Debugger::Debugger() + { + Log::info("debugger: ready"); + } + + Debugger::~Debugger() + { + unload_target(); + } + + auto Debugger::load_target(const std::filesystem::path& executable) -> Result<void> + { + std::error_code error; + + if (!std::filesystem::exists(executable, error)) + return fail("'{}' does not exist", executable.string()); + + if (!std::filesystem::is_regular_file(executable, error)) + return fail("'{}' is not a file", executable.string()); + + m_target_path = std::filesystem::absolute(executable, error); + if (error) + m_target_path = executable; + + m_process_id = 0; + m_stop_reason = StopReason::None; + set_state(TargetState::Loaded); + + Log::info("debugger: loaded target '{}'", m_target_path.string()); + + return {}; + } + + auto Debugger::unload_target() -> void + { + m_target_path.clear(); + m_process_id = 0; + m_stop_reason = StopReason::None; + + m_threads.clear(); + m_call_stack.clear(); + m_locals.clear(); + m_registers.clear(); + m_console_output.clear(); + + m_selected_thread = 0; + m_selected_frame = 0; + + for (Breakpoint& breakpoint : m_breakpoints) + { + breakpoint.resolved = false; + breakpoint.hit_count = 0; + breakpoint.address = 0; + } + + set_state(TargetState::NoTarget); + } + + auto Debugger::launch(const LaunchSpec& spec) -> Result<void> + { + (void)spec; + return not_implemented(); + } + + auto Debugger::attach(uint64_t process_id) -> Result<void> + { + (void)process_id; + return not_implemented(); + } + + auto Debugger::detach() -> Result<void> + { + return not_implemented(); + } + + auto Debugger::terminate() -> Result<void> + { + return not_implemented(); + } + + auto Debugger::resume() -> Result<void> + { + return not_implemented(); + } + + auto Debugger::pause() -> Result<void> + { + return not_implemented(); + } + + auto Debugger::step_over(StepMode mode) -> Result<void> + { + (void)mode; + return not_implemented(); + } + + auto Debugger::step_into(StepMode mode) -> Result<void> + { + (void)mode; + return not_implemented(); + } + + auto Debugger::step_out() -> Result<void> + { + return not_implemented(); + } + + auto Debugger::run_to(const std::filesystem::path& file, uint32_t line) -> Result<void> + { + (void)file; + (void)line; + return not_implemented(); + } + + auto Debugger::add_breakpoint(const std::filesystem::path& file, uint32_t line) -> uint32_t + { + const auto existing = std::ranges::find_if(m_breakpoints, [&](const Breakpoint& candidate) + { + return candidate.file == file && candidate.line == line; + }); + + if (existing != m_breakpoints.end()) + return existing->id; + + Breakpoint breakpoint; + breakpoint.id = m_next_breakpoint_id++; + breakpoint.file = file; + breakpoint.line = line; + + m_breakpoints.push_back(std::move(breakpoint)); + + Log::info("debugger: breakpoint {} at {}:{}", + m_breakpoints.back().id, + file.filename().string(), + line); + + return m_breakpoints.back().id; + } + + auto Debugger::add_function_breakpoint(std::string_view function) -> uint32_t + { + const auto existing = std::ranges::find_if(m_breakpoints, [&](const Breakpoint& candidate) + { + return candidate.function == function && candidate.line == 0; + }); + + if (existing != m_breakpoints.end()) + return existing->id; + + Breakpoint breakpoint; + breakpoint.id = m_next_breakpoint_id++; + breakpoint.function = function; + + m_breakpoints.push_back(std::move(breakpoint)); + + Log::info("debugger: breakpoint {} at {}()", m_breakpoints.back().id, function); + + return m_breakpoints.back().id; + } + + auto Debugger::remove_breakpoint(uint32_t id) -> bool + { + const auto entry = std::ranges::find(m_breakpoints, id, &Breakpoint::id); + if (entry == m_breakpoints.end()) + return false; + + m_breakpoints.erase(entry); + + Log::info("debugger: removed breakpoint {}", id); + + return true; + } + + auto Debugger::set_breakpoint_enabled(uint32_t id, bool enabled) -> bool + { + Breakpoint* breakpoint = find_breakpoint(id); + if (breakpoint == nullptr) + return false; + + breakpoint->enabled = enabled; + + return true; + } + + auto Debugger::clear_breakpoints() -> void + { + m_breakpoints.clear(); + } + + auto Debugger::find_breakpoint(uint32_t id) -> Breakpoint* + { + const auto entry = std::ranges::find(m_breakpoints, id, &Breakpoint::id); + + return entry == m_breakpoints.end() ? nullptr : &*entry; + } + + auto Debugger::evaluate(std::string_view expression) -> Result<std::string> + { + (void)expression; + return not_implemented(); + } + + auto Debugger::read_memory(uint64_t address, size_t size) -> Result<std::vector<uint8_t>> + { + (void)address; + (void)size; + return not_implemented(); + } + + auto Debugger::select_thread(uint64_t thread_id) -> bool + { + const auto entry = std::ranges::find(m_threads, thread_id, &Thread::id); + if (entry == m_threads.end()) + return false; + + m_selected_thread = thread_id; + m_selected_frame = 0; + + return true; + } + + auto Debugger::select_frame(uint32_t frame_index) -> bool + { + if (frame_index >= m_call_stack.size()) + return false; + + m_selected_frame = frame_index; + + return true; + } + + auto Debugger::update() -> void + { + } + + auto Debugger::set_state(TargetState state) -> void + { + if (m_state == state) + return; + + Log::debug("debugger: {} -> {}", to_string(m_state), to_string(state)); + + m_state = state; + } +} |
