aboutsummaryrefslogtreecommitdiff
path: root/src/debugger
diff options
context:
space:
mode:
authorhachem <im@hachem.wtf>2026-08-28 21:32:31 +0200
committerhachem <im@hachem.wtf>2026-08-28 21:32:31 +0200
commit86afee9b5c4954fda84d500b85328cca18ee3de4 (patch)
tree14730cde527583a77be20a55f04542412fb8fb3e /src/debugger
parentf3c010f6cea20335d9697c1c2f657e71c70fb1dc (diff)
feat: breakpoint conditions and skip counts
Diffstat (limited to 'src/debugger')
-rw-r--r--src/debugger/debugger.cpp83
-rw-r--r--src/debugger/debugger.h2
-rw-r--r--src/debugger/types.h3
3 files changed, 81 insertions, 7 deletions
diff --git a/src/debugger/debugger.cpp b/src/debugger/debugger.cpp
index c5cc615..00bdda7 100644
--- a/src/debugger/debugger.cpp
+++ b/src/debugger/debugger.cpp
@@ -45,7 +45,11 @@ namespace Hsdbg
llvm_prefix / "bin" / "debugserver",
};
#elif defined(HSDBG_LINUX)
- return { llvm_prefix / "bin" / "lldb-server" };
+ return {
+ llvm_prefix / "bin" / "lldb-server",
+ "/usr/bin/lldb-server",
+ "/usr/local/bin/lldb-server",
+ };
#else
return {};
#endif
@@ -74,7 +78,12 @@ namespace Hsdbg
return;
}
- Log::warn("debugger: no debug server found, launching a target will fail");
+ Log::warn("debugger: no debug server found, leaving lldb to locate one");
+ }
+
+ 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 path_of(lldb::SBFileSpec spec) -> std::filesystem::path
@@ -181,11 +190,6 @@ namespace Hsdbg
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);
@@ -425,6 +429,19 @@ namespace Hsdbg
if (spec.stop_at_entry)
info.SetLaunchFlags(info.GetLaunchFlags() | lldb::eLaunchFlagStopAtEntry);
+ // lldb counts an ignore count down as hits are consumed, so a rerun has
+ // to start it over or it would only ever skip once
+ for (const Breakpoint& breakpoint : m_breakpoints)
+ {
+ if (breakpoint.backend_id == 0)
+ continue;
+
+ lldb::SBBreakpoint source = m_session->target.FindBreakpointByID(breakpoint.backend_id);
+
+ if (source.IsValid())
+ source.SetIgnoreCount(breakpoint.ignore_count);
+ }
+
set_state(TargetState::Launching);
lldb::SBError error;
@@ -745,6 +762,51 @@ namespace Hsdbg
return true;
}
+ auto Debugger::set_breakpoint_condition(uint32_t id, std::string_view condition) -> bool
+ {
+ Breakpoint* breakpoint = find_breakpoint(id);
+ if (breakpoint == nullptr)
+ return false;
+
+ breakpoint->condition = condition;
+
+ if (breakpoint->backend_id != 0 && m_session->target.IsValid())
+ {
+ lldb::SBBreakpoint source = m_session->target.FindBreakpointByID(breakpoint->backend_id);
+
+ if (source.IsValid())
+ source.SetCondition(breakpoint->condition.c_str());
+ }
+
+ if (breakpoint->condition.empty())
+ Log::info("debugger: breakpoint {} is unconditional", id);
+ else
+ Log::info("debugger: breakpoint {} stops when '{}'", id, breakpoint->condition);
+
+ return true;
+ }
+
+ auto Debugger::set_breakpoint_ignore_count(uint32_t id, uint32_t count) -> bool
+ {
+ Breakpoint* breakpoint = find_breakpoint(id);
+ if (breakpoint == nullptr)
+ return false;
+
+ breakpoint->ignore_count = count;
+
+ if (breakpoint->backend_id != 0 && m_session->target.IsValid())
+ {
+ lldb::SBBreakpoint source = m_session->target.FindBreakpointByID(breakpoint->backend_id);
+
+ if (source.IsValid())
+ source.SetIgnoreCount(count);
+ }
+
+ Log::info("debugger: breakpoint {} ignores {} hits", id, count);
+
+ return true;
+ }
+
auto Debugger::clear_breakpoints() -> void
{
if (m_session->target.IsValid())
@@ -1190,6 +1252,13 @@ namespace Hsdbg
created.SetEnabled(breakpoint.enabled);
+ // a breakpoint can be conditioned before any target exists to carry it
+ if (!breakpoint.condition.empty())
+ created.SetCondition(breakpoint.condition.c_str());
+
+ if (breakpoint.ignore_count != 0)
+ created.SetIgnoreCount(breakpoint.ignore_count);
+
breakpoint.backend_id = created.GetID();
read_back(m_session->target, created, breakpoint);
diff --git a/src/debugger/debugger.h b/src/debugger/debugger.h
index c7e5324..82a993a 100644
--- a/src/debugger/debugger.h
+++ b/src/debugger/debugger.h
@@ -45,6 +45,8 @@ namespace Hsdbg
auto add_function_breakpoint(std::string_view function) -> uint32_t;
auto remove_breakpoint(uint32_t id) -> bool;
auto set_breakpoint_enabled(uint32_t id, bool enabled) -> bool;
+ auto set_breakpoint_condition(uint32_t id, std::string_view condition) -> bool;
+ auto set_breakpoint_ignore_count(uint32_t id, uint32_t count) -> bool;
auto clear_breakpoints() -> void;
auto find_breakpoint(uint32_t id) -> Breakpoint*;
auto breakpoints() const -> std::span<const Breakpoint> { return m_breakpoints; }
diff --git a/src/debugger/types.h b/src/debugger/types.h
index 0023694..dec74e5 100644
--- a/src/debugger/types.h
+++ b/src/debugger/types.h
@@ -86,6 +86,9 @@ namespace Hsdbg
std::string function;
uint64_t address = 0;
std::string condition;
+
+ // how many hits lldb lets through before it starts stopping
+ uint32_t ignore_count = 0;
uint32_t hit_count = 0;
bool enabled = true;
bool resolved = false;