Skip to content

Commit 6b5d907

Browse files
committed
[lldb] Add SB API to make a breakpoint a hardware breakpoint
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
1 parent ab0fa6c commit 6b5d907

File tree

9 files changed

+164
-32
lines changed

9 files changed

+164
-32
lines changed

lldb/include/lldb/API/SBBreakpoint.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,11 @@ class LLDB_API SBBreakpoint {
148148

149149
bool IsHardware() const;
150150

151+
/// Make this breakpoint a hardware breakpoint. This will replace all existing
152+
/// breakpoint locations with hardware breakpoints. Returns an error if this
153+
/// fails, e.g. when there aren't enough hardware resources available.
154+
lldb::SBError SetIsHardware(bool is_hardware);
155+
151156
// Can only be called from a ScriptedBreakpointResolver...
152157
SBError
153158
AddLocation(SBAddress &address);

lldb/include/lldb/Breakpoint/Breakpoint.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,8 @@ class Breakpoint : public std::enable_shared_from_this<Breakpoint>,
519519

520520
bool IsHardware() const { return m_hardware; }
521521

522+
llvm::Error SetIsHardware(bool is_hardware);
523+
522524
lldb::BreakpointResolverSP GetResolver() { return m_resolver_sp; }
523525

524526
lldb::SearchFilterSP GetSearchFilter() { return m_filter_sp; }

lldb/include/lldb/Breakpoint/BreakpointLocation.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ class BreakpointLocation
6969
// The next section deals with various breakpoint options.
7070

7171
/// If \a enabled is \b true, enable the breakpoint, if \b false disable it.
72-
void SetEnabled(bool enabled);
72+
bool SetEnabled(bool enabled);
7373

7474
/// Check the Enable/Disable state.
7575
///

lldb/source/API/SBBreakpoint.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -781,6 +781,18 @@ bool SBBreakpoint::IsHardware() const {
781781
return false;
782782
}
783783

784+
lldb::SBError SBBreakpoint::SetIsHardware(bool is_hardware) {
785+
LLDB_INSTRUMENT_VA(this, is_hardware);
786+
787+
BreakpointSP bkpt_sp = GetSP();
788+
if (bkpt_sp) {
789+
std::lock_guard<std::recursive_mutex> guard(
790+
bkpt_sp->GetTarget().GetAPIMutex());
791+
return SBError(Status::FromError(bkpt_sp->SetIsHardware(is_hardware)));
792+
}
793+
return SBError();
794+
}
795+
784796
BreakpointSP SBBreakpoint::GetSP() const { return m_opaque_wp.lock(); }
785797

786798
// This is simple collection of breakpoint id's and their target.

lldb/source/Breakpoint/Breakpoint.cpp

Lines changed: 72 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ Breakpoint::Breakpoint(Target &new_target, const Breakpoint &source_bp)
6161
Breakpoint::~Breakpoint() = default;
6262

6363
BreakpointSP Breakpoint::CopyFromBreakpoint(TargetSP new_target,
64-
const Breakpoint& bp_to_copy_from) {
64+
const Breakpoint &bp_to_copy_from) {
6565
if (!new_target)
6666
return BreakpointSP();
6767

@@ -163,7 +163,7 @@ lldb::BreakpointSP Breakpoint::CreateFromStructuredData(
163163
std::make_shared<SearchFilterForUnconstrainedSearches>(target_sp);
164164
else {
165165
filter_sp = SearchFilter::CreateFromStructuredData(target_sp, *filter_dict,
166-
create_error);
166+
create_error);
167167
if (create_error.Fail()) {
168168
error = Status::FromErrorStringWithFormat(
169169
"Error creating breakpoint filter from data: %s.",
@@ -174,7 +174,7 @@ lldb::BreakpointSP Breakpoint::CreateFromStructuredData(
174174

175175
std::unique_ptr<BreakpointOptions> options_up;
176176
StructuredData::Dictionary *options_dict;
177-
Target& target = *target_sp;
177+
Target &target = *target_sp;
178178
success = breakpoint_dict->GetValueForKeyAsDictionary(
179179
BreakpointOptions::GetSerializationKey(), options_dict);
180180
if (success) {
@@ -192,8 +192,8 @@ lldb::BreakpointSP Breakpoint::CreateFromStructuredData(
192192
success = breakpoint_dict->GetValueForKeyAsBoolean(
193193
Breakpoint::GetKey(OptionNames::Hardware), hardware);
194194

195-
result_sp = target.CreateBreakpoint(filter_sp, resolver_sp, false,
196-
hardware, true);
195+
result_sp =
196+
target.CreateBreakpoint(filter_sp, resolver_sp, false, hardware, true);
197197

198198
if (result_sp && options_up) {
199199
result_sp->m_options = *options_up;
@@ -251,6 +251,45 @@ const lldb::TargetSP Breakpoint::GetTargetSP() {
251251

252252
bool Breakpoint::IsInternal() const { return LLDB_BREAK_ID_IS_INTERNAL(m_bid); }
253253

254+
llvm::Error Breakpoint::SetIsHardware(bool is_hardware) {
255+
if (is_hardware == m_hardware)
256+
return llvm::Error::success();
257+
258+
// Disable all non-hardware breakpoint locations.
259+
std::vector<BreakpointLocationSP> locations;
260+
for (BreakpointLocationSP location_sp : m_locations.BreakpointLocations()) {
261+
if (!location_sp || !location_sp->IsEnabled())
262+
continue;
263+
264+
lldb::BreakpointSiteSP breakpoint_site_sp =
265+
location_sp->GetBreakpointSite();
266+
if (!breakpoint_site_sp ||
267+
breakpoint_site_sp->GetType() == BreakpointSite::eHardware)
268+
continue;
269+
270+
locations.push_back(location_sp);
271+
location_sp->SetEnabled(false);
272+
}
273+
274+
// Toggle the hardware mode.
275+
m_hardware = is_hardware;
276+
277+
// Re-enable all breakpoint locations.
278+
size_t num_failures = 0;
279+
for (BreakpointLocationSP location_sp : locations) {
280+
if (!location_sp->SetEnabled(true))
281+
num_failures++;
282+
}
283+
284+
if (num_failures != 0)
285+
return llvm::createStringError(
286+
"%ull out of %ull breakpoint locations left disabled because they "
287+
"couldn't be converted to hardware",
288+
num_failures, locations.size());
289+
290+
return llvm::Error::success();
291+
}
292+
254293
BreakpointLocationSP Breakpoint::AddLocation(const Address &addr,
255294
bool *new_location) {
256295
return m_locations.AddLocation(addr, m_resolve_indirect_symbols,
@@ -952,8 +991,7 @@ void Breakpoint::GetResolverDescription(Stream *s) {
952991
m_resolver_sp->GetDescription(s);
953992
}
954993

955-
bool Breakpoint::GetMatchingFileLine(ConstString filename,
956-
uint32_t line_number,
994+
bool Breakpoint::GetMatchingFileLine(ConstString filename, uint32_t line_number,
957995
BreakpointLocationCollection &loc_coll) {
958996
// TODO: To be correct, this method needs to fill the breakpoint location
959997
// collection
@@ -1010,19 +1048,32 @@ void Breakpoint::SendBreakpointChangedEvent(
10101048

10111049
const char *Breakpoint::BreakpointEventTypeAsCString(BreakpointEventType type) {
10121050
switch (type) {
1013-
case eBreakpointEventTypeInvalidType: return "invalid";
1014-
case eBreakpointEventTypeAdded: return "breakpoint added";
1015-
case eBreakpointEventTypeRemoved: return "breakpoint removed";
1016-
case eBreakpointEventTypeLocationsAdded: return "locations added";
1017-
case eBreakpointEventTypeLocationsRemoved: return "locations removed";
1018-
case eBreakpointEventTypeLocationsResolved: return "locations resolved";
1019-
case eBreakpointEventTypeEnabled: return "breakpoint enabled";
1020-
case eBreakpointEventTypeDisabled: return "breakpoint disabled";
1021-
case eBreakpointEventTypeCommandChanged: return "command changed";
1022-
case eBreakpointEventTypeConditionChanged: return "condition changed";
1023-
case eBreakpointEventTypeIgnoreChanged: return "ignore count changed";
1024-
case eBreakpointEventTypeThreadChanged: return "thread changed";
1025-
case eBreakpointEventTypeAutoContinueChanged: return "autocontinue changed";
1051+
case eBreakpointEventTypeInvalidType:
1052+
return "invalid";
1053+
case eBreakpointEventTypeAdded:
1054+
return "breakpoint added";
1055+
case eBreakpointEventTypeRemoved:
1056+
return "breakpoint removed";
1057+
case eBreakpointEventTypeLocationsAdded:
1058+
return "locations added";
1059+
case eBreakpointEventTypeLocationsRemoved:
1060+
return "locations removed";
1061+
case eBreakpointEventTypeLocationsResolved:
1062+
return "locations resolved";
1063+
case eBreakpointEventTypeEnabled:
1064+
return "breakpoint enabled";
1065+
case eBreakpointEventTypeDisabled:
1066+
return "breakpoint disabled";
1067+
case eBreakpointEventTypeCommandChanged:
1068+
return "command changed";
1069+
case eBreakpointEventTypeConditionChanged:
1070+
return "condition changed";
1071+
case eBreakpointEventTypeIgnoreChanged:
1072+
return "ignore count changed";
1073+
case eBreakpointEventTypeThreadChanged:
1074+
return "thread changed";
1075+
case eBreakpointEventTypeAutoContinueChanged:
1076+
return "autocontinue changed";
10261077
};
10271078
llvm_unreachable("Fully covered switch above!");
10281079
}
@@ -1060,7 +1111,7 @@ void Breakpoint::BreakpointEventData::Dump(Stream *s) const {
10601111
BreakpointEventType event_type = GetBreakpointEventType();
10611112
break_id_t bkpt_id = GetBreakpoint()->GetID();
10621113
s->Format("bkpt: {0} type: {1}", bkpt_id,
1063-
BreakpointEventTypeAsCString(event_type));
1114+
BreakpointEventTypeAsCString(event_type));
10641115
}
10651116

10661117
const Breakpoint::BreakpointEventData *

lldb/source/Breakpoint/BreakpointLocation.cpp

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -72,15 +72,13 @@ bool BreakpointLocation::IsEnabled() const {
7272
return true;
7373
}
7474

75-
void BreakpointLocation::SetEnabled(bool enabled) {
75+
bool BreakpointLocation::SetEnabled(bool enabled) {
7676
GetLocationOptions().SetEnabled(enabled);
77-
if (enabled) {
78-
ResolveBreakpointSite();
79-
} else {
80-
ClearBreakpointSite();
81-
}
77+
const bool success =
78+
enabled ? ResolveBreakpointSite() : ClearBreakpointSite();
8279
SendBreakpointLocationChangedEvent(enabled ? eBreakpointEventTypeEnabled
8380
: eBreakpointEventTypeDisabled);
81+
return success;
8482
}
8583

8684
bool BreakpointLocation::IsAutoContinue() const {
@@ -436,10 +434,10 @@ bool BreakpointLocation::ResolveBreakpointSite() {
436434
process->CreateBreakpointSite(shared_from_this(), m_owner.IsHardware());
437435

438436
if (new_id == LLDB_INVALID_BREAK_ID) {
439-
Log *log = GetLog(LLDBLog::Breakpoints);
440-
if (log)
441-
log->Warning("Failed to add breakpoint site at 0x%" PRIx64,
442-
m_address.GetOpcodeLoadAddress(&m_owner.GetTarget()));
437+
LLDB_LOGF(GetLog(LLDBLog::Breakpoints),
438+
"Failed to add breakpoint site at 0x%" PRIx64 "(resolved=%s)",
439+
m_address.GetOpcodeLoadAddress(&m_owner.GetTarget()),
440+
IsResolved() ? "yes" : "no");
443441
}
444442

445443
return IsResolved();
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
C_SOURCES := main.c
2+
3+
ifeq ($(CC_TYPE), icc)
4+
CFLAGS_EXTRAS := -debug inline-debug-info
5+
endif
6+
7+
include Makefile.rules
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import lldb
2+
from lldbsuite.test.decorators import *
3+
from lldbsuite.test.lldbtest import *
4+
from lldbsuite.test import lldbutil
5+
6+
from functionalities.breakpoint.hardware_breakpoints.base import *
7+
8+
9+
class SimpleHWBreakpointTest(HardwareBreakpointTestBase):
10+
def does_not_support_hw_breakpoints(self):
11+
# FIXME: Use HardwareBreakpointTestBase.supports_hw_breakpoints
12+
if super().supports_hw_breakpoints() is None:
13+
return "Hardware breakpoints are unsupported"
14+
return None
15+
16+
@skipTestIfFn(does_not_support_hw_breakpoints)
17+
def test(self):
18+
"""Test SBBreakpoint::SetIsHardware"""
19+
self.build()
20+
21+
# Set a breakpoint on main.
22+
target, process, _, main_bp = lldbutil.run_to_source_breakpoint(
23+
self, "main", lldb.SBFileSpec("main.c")
24+
)
25+
26+
break_on_me_bp = target.BreakpointCreateByLocation("main.c", 1)
27+
28+
self.assertFalse(main_bp.IsHardware())
29+
self.assertFalse(break_on_me_bp.IsHardware())
30+
self.assertGreater(break_on_me_bp.GetNumResolvedLocations(), 0)
31+
32+
error = break_on_me_bp.SetIsHardware(True)
33+
34+
# Regardless of whether we succeeded in updating all the locations, the
35+
# breakpoint will be marked as a hardware breakpoint.
36+
self.assertTrue(break_on_me_bp.IsHardware())
37+
38+
if super().supports_hw_breakpoints():
39+
self.assertSuccess(error)
40+
41+
# Continue to our Hardware breakpoint and verify that's the reason
42+
# we're stopped.
43+
process.Continue()
44+
self.expect(
45+
"thread list",
46+
STOPPED_DUE_TO_BREAKPOINT,
47+
substrs=["stopped", "stop reason = breakpoint"],
48+
)
49+
else:
50+
self.assertFailure(error)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
int break_on_me() {
2+
int i = 10;
3+
i++;
4+
return i;
5+
}
6+
7+
int main() { return break_on_me(); }

0 commit comments

Comments
 (0)