[Lldb-commits] [lldb] 378f248 - [lldb] Add SB API to make a breakpoint a hardware breakpoint (#146602)
via lldb-commits
lldb-commits at lists.llvm.org
Thu Jul 3 11:17:22 PDT 2025
Author: Jonas Devlieghere
Date: 2025-07-03T11:17:19-07:00
New Revision: 378f248934d603e3fba9d958d2db997814d057d5
URL: https://github.com/llvm/llvm-project/commit/378f248934d603e3fba9d958d2db997814d057d5
DIFF: https://github.com/llvm/llvm-project/commit/378f248934d603e3fba9d958d2db997814d057d5.diff
LOG: [lldb] Add SB API to make a breakpoint a hardware breakpoint (#146602)
This adds SBBreakpoint::SetIsHardware, allowing clients to mark an
existing breakpoint as a hardware breakpoint purely through the API.
This is safe to do after creation, as the hardware/software distinction
doesn't affect how breakpoint locations are selected.
In some cases (e.g. when writing a trap instruction would alter program
behavior), it's important to use hardware breakpoints. Ideally, we’d
extend the various `Create` methods to support this, but given their
number, this patch limits the scope to the post-creation API. As a
workaround, users can also rely on target.require-hardware-breakpoint or
use the `breakpoint set` command.
rdar://153528045
Added:
lldb/test/API/functionalities/breakpoint/hardware_breakpoints/simple_hw_breakpoints/Makefile
lldb/test/API/functionalities/breakpoint/hardware_breakpoints/simple_hw_breakpoints/TestSimpleHWBreakpoints.py
lldb/test/API/functionalities/breakpoint/hardware_breakpoints/simple_hw_breakpoints/main.c
Modified:
lldb/include/lldb/API/SBBreakpoint.h
lldb/include/lldb/Breakpoint/Breakpoint.h
lldb/include/lldb/Breakpoint/BreakpointLocation.h
lldb/source/API/SBBreakpoint.cpp
lldb/source/Breakpoint/Breakpoint.cpp
lldb/source/Breakpoint/BreakpointLocation.cpp
Removed:
################################################################################
diff --git a/lldb/include/lldb/API/SBBreakpoint.h b/lldb/include/lldb/API/SBBreakpoint.h
index e08df3b6d5ab0..18ed3e7226d3b 100644
--- a/lldb/include/lldb/API/SBBreakpoint.h
+++ b/lldb/include/lldb/API/SBBreakpoint.h
@@ -148,6 +148,11 @@ class LLDB_API SBBreakpoint {
bool IsHardware() const;
+ /// Make this breakpoint a hardware breakpoint. This will replace all existing
+ /// breakpoint locations with hardware breakpoints. Returns an error if this
+ /// fails, e.g. when there aren't enough hardware resources available.
+ lldb::SBError SetIsHardware(bool is_hardware);
+
// Can only be called from a ScriptedBreakpointResolver...
SBError
AddLocation(SBAddress &address);
diff --git a/lldb/include/lldb/Breakpoint/Breakpoint.h b/lldb/include/lldb/Breakpoint/Breakpoint.h
index f623a2e0c295b..b200a1e4893df 100644
--- a/lldb/include/lldb/Breakpoint/Breakpoint.h
+++ b/lldb/include/lldb/Breakpoint/Breakpoint.h
@@ -519,6 +519,8 @@ class Breakpoint : public std::enable_shared_from_this<Breakpoint>,
bool IsHardware() const { return m_hardware; }
+ llvm::Error SetIsHardware(bool is_hardware);
+
lldb::BreakpointResolverSP GetResolver() { return m_resolver_sp; }
lldb::SearchFilterSP GetSearchFilter() { return m_filter_sp; }
diff --git a/lldb/include/lldb/Breakpoint/BreakpointLocation.h b/lldb/include/lldb/Breakpoint/BreakpointLocation.h
index 3592291bb2d06..f4fe2e9a9abfc 100644
--- a/lldb/include/lldb/Breakpoint/BreakpointLocation.h
+++ b/lldb/include/lldb/Breakpoint/BreakpointLocation.h
@@ -69,7 +69,7 @@ class BreakpointLocation
// The next section deals with various breakpoint options.
/// If \a enabled is \b true, enable the breakpoint, if \b false disable it.
- void SetEnabled(bool enabled);
+ bool SetEnabled(bool enabled);
/// Check the Enable/Disable state.
///
diff --git a/lldb/source/API/SBBreakpoint.cpp b/lldb/source/API/SBBreakpoint.cpp
index 87fadbcec4f26..397afc1f10f94 100644
--- a/lldb/source/API/SBBreakpoint.cpp
+++ b/lldb/source/API/SBBreakpoint.cpp
@@ -781,6 +781,18 @@ bool SBBreakpoint::IsHardware() const {
return false;
}
+lldb::SBError SBBreakpoint::SetIsHardware(bool is_hardware) {
+ LLDB_INSTRUMENT_VA(this, is_hardware);
+
+ BreakpointSP bkpt_sp = GetSP();
+ if (bkpt_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ bkpt_sp->GetTarget().GetAPIMutex());
+ return SBError(Status::FromError(bkpt_sp->SetIsHardware(is_hardware)));
+ }
+ return SBError();
+}
+
BreakpointSP SBBreakpoint::GetSP() const { return m_opaque_wp.lock(); }
// This is simple collection of breakpoint id's and their target.
diff --git a/lldb/source/Breakpoint/Breakpoint.cpp b/lldb/source/Breakpoint/Breakpoint.cpp
index b308644825dad..8fc93cc8e0e51 100644
--- a/lldb/source/Breakpoint/Breakpoint.cpp
+++ b/lldb/source/Breakpoint/Breakpoint.cpp
@@ -61,7 +61,7 @@ Breakpoint::Breakpoint(Target &new_target, const Breakpoint &source_bp)
Breakpoint::~Breakpoint() = default;
BreakpointSP Breakpoint::CopyFromBreakpoint(TargetSP new_target,
- const Breakpoint& bp_to_copy_from) {
+ const Breakpoint &bp_to_copy_from) {
if (!new_target)
return BreakpointSP();
@@ -163,7 +163,7 @@ lldb::BreakpointSP Breakpoint::CreateFromStructuredData(
std::make_shared<SearchFilterForUnconstrainedSearches>(target_sp);
else {
filter_sp = SearchFilter::CreateFromStructuredData(target_sp, *filter_dict,
- create_error);
+ create_error);
if (create_error.Fail()) {
error = Status::FromErrorStringWithFormat(
"Error creating breakpoint filter from data: %s.",
@@ -174,7 +174,7 @@ lldb::BreakpointSP Breakpoint::CreateFromStructuredData(
std::unique_ptr<BreakpointOptions> options_up;
StructuredData::Dictionary *options_dict;
- Target& target = *target_sp;
+ Target &target = *target_sp;
success = breakpoint_dict->GetValueForKeyAsDictionary(
BreakpointOptions::GetSerializationKey(), options_dict);
if (success) {
@@ -192,8 +192,8 @@ lldb::BreakpointSP Breakpoint::CreateFromStructuredData(
success = breakpoint_dict->GetValueForKeyAsBoolean(
Breakpoint::GetKey(OptionNames::Hardware), hardware);
- result_sp = target.CreateBreakpoint(filter_sp, resolver_sp, false,
- hardware, true);
+ result_sp =
+ target.CreateBreakpoint(filter_sp, resolver_sp, false, hardware, true);
if (result_sp && options_up) {
result_sp->m_options = *options_up;
@@ -251,6 +251,45 @@ const lldb::TargetSP Breakpoint::GetTargetSP() {
bool Breakpoint::IsInternal() const { return LLDB_BREAK_ID_IS_INTERNAL(m_bid); }
+llvm::Error Breakpoint::SetIsHardware(bool is_hardware) {
+ if (is_hardware == m_hardware)
+ return llvm::Error::success();
+
+ // Disable all non-hardware breakpoint locations.
+ std::vector<BreakpointLocationSP> locations;
+ for (BreakpointLocationSP location_sp : m_locations.BreakpointLocations()) {
+ if (!location_sp || !location_sp->IsEnabled())
+ continue;
+
+ lldb::BreakpointSiteSP breakpoint_site_sp =
+ location_sp->GetBreakpointSite();
+ if (!breakpoint_site_sp ||
+ breakpoint_site_sp->GetType() == BreakpointSite::eHardware)
+ continue;
+
+ locations.push_back(location_sp);
+ location_sp->SetEnabled(false);
+ }
+
+ // Toggle the hardware mode.
+ m_hardware = is_hardware;
+
+ // Re-enable all breakpoint locations.
+ size_t num_failures = 0;
+ for (BreakpointLocationSP location_sp : locations) {
+ if (!location_sp->SetEnabled(true))
+ num_failures++;
+ }
+
+ if (num_failures != 0)
+ return llvm::createStringError(
+ "%ull out of %ull breakpoint locations left disabled because they "
+ "couldn't be converted to hardware",
+ num_failures, locations.size());
+
+ return llvm::Error::success();
+}
+
BreakpointLocationSP Breakpoint::AddLocation(const Address &addr,
bool *new_location) {
return m_locations.AddLocation(addr, m_resolve_indirect_symbols,
@@ -952,8 +991,7 @@ void Breakpoint::GetResolverDescription(Stream *s) {
m_resolver_sp->GetDescription(s);
}
-bool Breakpoint::GetMatchingFileLine(ConstString filename,
- uint32_t line_number,
+bool Breakpoint::GetMatchingFileLine(ConstString filename, uint32_t line_number,
BreakpointLocationCollection &loc_coll) {
// TODO: To be correct, this method needs to fill the breakpoint location
// collection
@@ -1010,19 +1048,32 @@ void Breakpoint::SendBreakpointChangedEvent(
const char *Breakpoint::BreakpointEventTypeAsCString(BreakpointEventType type) {
switch (type) {
- case eBreakpointEventTypeInvalidType: return "invalid";
- case eBreakpointEventTypeAdded: return "breakpoint added";
- case eBreakpointEventTypeRemoved: return "breakpoint removed";
- case eBreakpointEventTypeLocationsAdded: return "locations added";
- case eBreakpointEventTypeLocationsRemoved: return "locations removed";
- case eBreakpointEventTypeLocationsResolved: return "locations resolved";
- case eBreakpointEventTypeEnabled: return "breakpoint enabled";
- case eBreakpointEventTypeDisabled: return "breakpoint disabled";
- case eBreakpointEventTypeCommandChanged: return "command changed";
- case eBreakpointEventTypeConditionChanged: return "condition changed";
- case eBreakpointEventTypeIgnoreChanged: return "ignore count changed";
- case eBreakpointEventTypeThreadChanged: return "thread changed";
- case eBreakpointEventTypeAutoContinueChanged: return "autocontinue changed";
+ case eBreakpointEventTypeInvalidType:
+ return "invalid";
+ case eBreakpointEventTypeAdded:
+ return "breakpoint added";
+ case eBreakpointEventTypeRemoved:
+ return "breakpoint removed";
+ case eBreakpointEventTypeLocationsAdded:
+ return "locations added";
+ case eBreakpointEventTypeLocationsRemoved:
+ return "locations removed";
+ case eBreakpointEventTypeLocationsResolved:
+ return "locations resolved";
+ case eBreakpointEventTypeEnabled:
+ return "breakpoint enabled";
+ case eBreakpointEventTypeDisabled:
+ return "breakpoint disabled";
+ case eBreakpointEventTypeCommandChanged:
+ return "command changed";
+ case eBreakpointEventTypeConditionChanged:
+ return "condition changed";
+ case eBreakpointEventTypeIgnoreChanged:
+ return "ignore count changed";
+ case eBreakpointEventTypeThreadChanged:
+ return "thread changed";
+ case eBreakpointEventTypeAutoContinueChanged:
+ return "autocontinue changed";
};
llvm_unreachable("Fully covered switch above!");
}
@@ -1060,7 +1111,7 @@ void Breakpoint::BreakpointEventData::Dump(Stream *s) const {
BreakpointEventType event_type = GetBreakpointEventType();
break_id_t bkpt_id = GetBreakpoint()->GetID();
s->Format("bkpt: {0} type: {1}", bkpt_id,
- BreakpointEventTypeAsCString(event_type));
+ BreakpointEventTypeAsCString(event_type));
}
const Breakpoint::BreakpointEventData *
diff --git a/lldb/source/Breakpoint/BreakpointLocation.cpp b/lldb/source/Breakpoint/BreakpointLocation.cpp
index 2a00c94eac7e7..1d11f3a54b67f 100644
--- a/lldb/source/Breakpoint/BreakpointLocation.cpp
+++ b/lldb/source/Breakpoint/BreakpointLocation.cpp
@@ -72,15 +72,13 @@ bool BreakpointLocation::IsEnabled() const {
return true;
}
-void BreakpointLocation::SetEnabled(bool enabled) {
+bool BreakpointLocation::SetEnabled(bool enabled) {
GetLocationOptions().SetEnabled(enabled);
- if (enabled) {
- ResolveBreakpointSite();
- } else {
- ClearBreakpointSite();
- }
+ const bool success =
+ enabled ? ResolveBreakpointSite() : ClearBreakpointSite();
SendBreakpointLocationChangedEvent(enabled ? eBreakpointEventTypeEnabled
: eBreakpointEventTypeDisabled);
+ return success;
}
bool BreakpointLocation::IsAutoContinue() const {
@@ -436,10 +434,10 @@ bool BreakpointLocation::ResolveBreakpointSite() {
process->CreateBreakpointSite(shared_from_this(), m_owner.IsHardware());
if (new_id == LLDB_INVALID_BREAK_ID) {
- Log *log = GetLog(LLDBLog::Breakpoints);
- if (log)
- log->Warning("Failed to add breakpoint site at 0x%" PRIx64,
- m_address.GetOpcodeLoadAddress(&m_owner.GetTarget()));
+ LLDB_LOGF(GetLog(LLDBLog::Breakpoints),
+ "Failed to add breakpoint site at 0x%" PRIx64 "(resolved=%s)",
+ m_address.GetOpcodeLoadAddress(&m_owner.GetTarget()),
+ IsResolved() ? "yes" : "no");
}
return IsResolved();
diff --git a/lldb/test/API/functionalities/breakpoint/hardware_breakpoints/simple_hw_breakpoints/Makefile b/lldb/test/API/functionalities/breakpoint/hardware_breakpoints/simple_hw_breakpoints/Makefile
new file mode 100644
index 0000000000000..304633c2dca1f
--- /dev/null
+++ b/lldb/test/API/functionalities/breakpoint/hardware_breakpoints/simple_hw_breakpoints/Makefile
@@ -0,0 +1,7 @@
+C_SOURCES := main.c
+
+ifeq ($(CC_TYPE), icc)
+ CFLAGS_EXTRAS := -debug inline-debug-info
+endif
+
+include Makefile.rules
diff --git a/lldb/test/API/functionalities/breakpoint/hardware_breakpoints/simple_hw_breakpoints/TestSimpleHWBreakpoints.py b/lldb/test/API/functionalities/breakpoint/hardware_breakpoints/simple_hw_breakpoints/TestSimpleHWBreakpoints.py
new file mode 100644
index 0000000000000..ccbb23507c1be
--- /dev/null
+++ b/lldb/test/API/functionalities/breakpoint/hardware_breakpoints/simple_hw_breakpoints/TestSimpleHWBreakpoints.py
@@ -0,0 +1,50 @@
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+from functionalities.breakpoint.hardware_breakpoints.base import *
+
+
+class SimpleHWBreakpointTest(HardwareBreakpointTestBase):
+ def does_not_support_hw_breakpoints(self):
+ # FIXME: Use HardwareBreakpointTestBase.supports_hw_breakpoints
+ if super().supports_hw_breakpoints() is None:
+ return "Hardware breakpoints are unsupported"
+ return None
+
+ @skipTestIfFn(does_not_support_hw_breakpoints)
+ def test(self):
+ """Test SBBreakpoint::SetIsHardware"""
+ self.build()
+
+ # Set a breakpoint on main.
+ target, process, _, main_bp = lldbutil.run_to_source_breakpoint(
+ self, "main", lldb.SBFileSpec("main.c")
+ )
+
+ break_on_me_bp = target.BreakpointCreateByLocation("main.c", 1)
+
+ self.assertFalse(main_bp.IsHardware())
+ self.assertFalse(break_on_me_bp.IsHardware())
+ self.assertGreater(break_on_me_bp.GetNumResolvedLocations(), 0)
+
+ error = break_on_me_bp.SetIsHardware(True)
+
+ # Regardless of whether we succeeded in updating all the locations, the
+ # breakpoint will be marked as a hardware breakpoint.
+ self.assertTrue(break_on_me_bp.IsHardware())
+
+ if super().supports_hw_breakpoints():
+ self.assertSuccess(error)
+
+ # Continue to our Hardware breakpoint and verify that's the reason
+ # we're stopped.
+ process.Continue()
+ self.expect(
+ "thread list",
+ STOPPED_DUE_TO_BREAKPOINT,
+ substrs=["stopped", "stop reason = breakpoint"],
+ )
+ else:
+ self.assertFailure(error)
diff --git a/lldb/test/API/functionalities/breakpoint/hardware_breakpoints/simple_hw_breakpoints/main.c b/lldb/test/API/functionalities/breakpoint/hardware_breakpoints/simple_hw_breakpoints/main.c
new file mode 100644
index 0000000000000..4f1c9df56200a
--- /dev/null
+++ b/lldb/test/API/functionalities/breakpoint/hardware_breakpoints/simple_hw_breakpoints/main.c
@@ -0,0 +1,7 @@
+int break_on_me() {
+ int i = 10;
+ i++;
+ return i;
+}
+
+int main() { return break_on_me(); }
More information about the lldb-commits
mailing list