Skip to content

Commit 776ee6f

Browse files
[lldb] Implement delayed breakpoints (#192971)
This patch changes the Process class so that it delays *physically* enabling/disabling breakpoints until the process is about to resume/detach/be destroyed, potentially reducing the packets transmitted by batching all breakpoints together. Most classes only need to know whether a breakpoint is "logically" enabled, as opposed to "physically" enabled (i.e. the remote server has actually enabled the breakpoint). However, lower level classes like derived Process classes, or StopInfo may actually need to know whether the breakpoint was physically enabled. As such, this commit also adds a "IsPhysicallyEnabled" API. The following PRs are related to the MultiBreakpoint feature: * #192910 * #192914 * #192915 * #192919 * #192962 * #192964 * #192971 * #192988
1 parent f973fa0 commit 776ee6f

13 files changed

Lines changed: 230 additions & 21 deletions

File tree

lldb/include/lldb/Target/Process.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ class ProcessProperties : public Properties {
114114
Args GetAlwaysRunThreadNames() const;
115115
FollowForkMode GetFollowForkMode() const;
116116
bool TrackMemoryCacheChanges() const;
117+
bool GetUseDelayedBreakpoints() const;
117118

118119
protected:
119120
Process *m_process; // Can be nullptr for global ProcessProperties
@@ -2246,6 +2247,9 @@ class Process : public std::enable_shared_from_this<Process>,
22462247
// Process Breakpoints
22472248
size_t GetSoftwareBreakpointTrapOpcode(BreakpointSite *bp_site);
22482249

2250+
enum class BreakpointAction { Enable, Disable };
2251+
2252+
protected:
22492253
virtual Status EnableBreakpointSite(BreakpointSite *bp_site) {
22502254
return Status::FromErrorStringWithFormatv(
22512255
"error: {0} does not support enabling breakpoints", GetPluginName());
@@ -2256,6 +2260,24 @@ class Process : public std::enable_shared_from_this<Process>,
22562260
"error: {0} does not support disabling breakpoints", GetPluginName());
22572261
}
22582262

2263+
/// Compare BreakpointSiteSPs by ID, so that iteration order is independent
2264+
/// of pointer addresses.
2265+
struct SiteIDCmp {
2266+
bool operator()(const lldb::BreakpointSiteSP &lhs,
2267+
const lldb::BreakpointSiteSP &rhs) const {
2268+
return lhs->GetID() < rhs->GetID();
2269+
}
2270+
};
2271+
using BreakpointSiteToActionMap =
2272+
std::map<lldb::BreakpointSiteSP, BreakpointAction, SiteIDCmp>;
2273+
2274+
virtual llvm::Error
2275+
UpdateBreakpointSites(const BreakpointSiteToActionMap &site_to_action);
2276+
2277+
public:
2278+
llvm::Error ExecuteBreakpointSiteAction(BreakpointSite &site,
2279+
Process::BreakpointAction action);
2280+
22592281
// This is implemented completely using the lldb::Process API. Subclasses
22602282
// don't need to implement this function unless the standard flow of read
22612283
// existing opcode, write breakpoint opcode, verify breakpoint opcode doesn't
@@ -2286,6 +2308,8 @@ class Process : public std::enable_shared_from_this<Process>,
22862308

22872309
bool IsBreakpointSiteEnabled(const BreakpointSite &site);
22882310

2311+
bool IsBreakpointSitePhysicallyEnabled(const BreakpointSite &site);
2312+
22892313
// BreakpointLocations use RemoveConstituentFromBreakpointSite to remove
22902314
// themselves from the constituent's list of this breakpoint sites.
22912315
void RemoveConstituentFromBreakpointSite(lldb::user_id_t site_id,
@@ -3540,6 +3564,21 @@ void PruneThreadPlans();
35403564
/// GetExtendedCrashInformation.
35413565
StructuredData::DictionarySP m_crash_info_dict_sp;
35423566

3567+
struct DelayedBreakpointCache {
3568+
void Enqueue(lldb::BreakpointSiteSP site, BreakpointAction action);
3569+
void RemoveSite(lldb::BreakpointSiteSP site) {
3570+
m_site_to_action.erase(site);
3571+
}
3572+
void Clear() { m_site_to_action.clear(); }
3573+
3574+
BreakpointSiteToActionMap m_site_to_action;
3575+
};
3576+
3577+
DelayedBreakpointCache m_delayed_breakpoints;
3578+
std::recursive_mutex m_delayed_breakpoints_mutex;
3579+
3580+
llvm::Error FlushDelayedBreakpoints();
3581+
35433582
size_t RemoveBreakpointOpcodesFromBuffer(lldb::addr_t addr, size_t size,
35443583
uint8_t *buf) const;
35453584

lldb/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -633,7 +633,7 @@ Status ProcessKDP::EnableBreakpointSite(BreakpointSite *bp_site) {
633633

634634
if (m_comm.LocalBreakpointsAreSupported()) {
635635
Status error;
636-
if (!IsBreakpointSiteEnabled(*bp_site)) {
636+
if (!IsBreakpointSitePhysicallyEnabled(*bp_site)) {
637637
if (m_comm.SendRequestBreakpoint(true, bp_site->GetLoadAddress())) {
638638
SetBreakpointSiteEnabled(*bp_site);
639639
bp_site->SetType(BreakpointSite::eExternal);
@@ -649,7 +649,7 @@ Status ProcessKDP::EnableBreakpointSite(BreakpointSite *bp_site) {
649649
Status ProcessKDP::DisableBreakpointSite(BreakpointSite *bp_site) {
650650
if (m_comm.LocalBreakpointsAreSupported()) {
651651
Status error;
652-
if (IsBreakpointSiteEnabled(*bp_site)) {
652+
if (IsBreakpointSitePhysicallyEnabled(*bp_site)) {
653653
BreakpointSite::Type bp_type = bp_site->GetType();
654654
if (bp_type == BreakpointSite::eExternal) {
655655
if (m_destroy_in_process && m_comm.IsRunning()) {

lldb/source/Plugins/Process/Utility/StopInfoMachException.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -636,7 +636,7 @@ StopInfoSP StopInfoMachException::CreateStopReasonWithMachException(
636636
addr_t pc = reg_ctx_sp->GetPC();
637637
BreakpointSiteSP bp_site_sp =
638638
process_sp->GetBreakpointSiteList().FindByAddress(pc);
639-
if (bp_site_sp && process_sp->IsBreakpointSiteEnabled(*bp_site_sp))
639+
if (bp_site_sp && process_sp->IsBreakpointSitePhysicallyEnabled(*bp_site_sp))
640640
thread.SetThreadStoppedAtUnexecutedBP(pc);
641641

642642
switch (exc_type) {
@@ -771,7 +771,8 @@ StopInfoSP StopInfoMachException::CreateStopReasonWithMachException(
771771
if (!bp_site_sp && reg_ctx_sp) {
772772
bp_site_sp = process_sp->GetBreakpointSiteList().FindByAddress(pc);
773773
}
774-
if (bp_site_sp && process_sp->IsBreakpointSiteEnabled(*bp_site_sp)) {
774+
if (bp_site_sp &&
775+
process_sp->IsBreakpointSitePhysicallyEnabled(*bp_site_sp)) {
775776
// We've hit this breakpoint, whether it was intended for this thread
776777
// or not. Clear this in the Tread object so we step past it on resume.
777778
thread.SetThreadHitBreakpointSite();
@@ -865,7 +866,8 @@ bool StopInfoMachException::WasContinueInterrupted(Thread &thread) {
865866
// We have a hardware breakpoint -- this is the kernel bug.
866867
auto &bp_site_list = process_sp->GetBreakpointSiteList();
867868
for (auto &site : bp_site_list.Sites()) {
868-
if (site->IsHardware() && process_sp->IsBreakpointSiteEnabled(*site)) {
869+
if (site->IsHardware() &&
870+
process_sp->IsBreakpointSitePhysicallyEnabled(*site)) {
869871
LLDB_LOGF(log,
870872
"Thread stopped with insn-step completed mach exception but "
871873
"thread was not stepping; there is a hardware breakpoint set.");

lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,7 @@ void ProcessWindows::RefreshStateAfterStop() {
415415
// If we're at a BreakpointSite, mark this as an Unexecuted Breakpoint.
416416
// We'll clear that state if we've actually executed the breakpoint.
417417
BreakpointSiteSP site(GetBreakpointSiteList().FindByAddress(pc));
418-
if (site && IsBreakpointSiteEnabled(*site))
418+
if (site && IsBreakpointSitePhysicallyEnabled(*site))
419419
stop_thread->SetThreadStoppedAtUnexecutedBP(pc);
420420

421421
switch (active_exception->GetExceptionCode()) {

lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1848,7 +1848,7 @@ ThreadSP ProcessGDBRemote::SetThreadStopInfo(
18481848
addr_t pc = thread_sp->GetRegisterContext()->GetPC();
18491849
BreakpointSiteSP bp_site_sp =
18501850
thread_sp->GetProcess()->GetBreakpointSiteList().FindByAddress(pc);
1851-
if (bp_site_sp && IsBreakpointSiteEnabled(*bp_site_sp))
1851+
if (bp_site_sp && IsBreakpointSitePhysicallyEnabled(*bp_site_sp))
18521852
thread_sp->SetThreadStoppedAtUnexecutedBP(pc);
18531853

18541854
if (exc_type != 0) {
@@ -2032,7 +2032,7 @@ ThreadSP ProcessGDBRemote::SetThreadStopInfo(
20322032
// BreakpointSites in any other location, but we can't know for
20332033
// sure what happened so it's a reasonable default.
20342034
if (bp_site_sp) {
2035-
if (IsBreakpointSiteEnabled(*bp_site_sp))
2035+
if (IsBreakpointSitePhysicallyEnabled(*bp_site_sp))
20362036
thread_sp->SetThreadHitBreakpointSite();
20372037

20382038
if (bp_site_sp->ValidForThisThread(*thread_sp)) {
@@ -3429,7 +3429,7 @@ Status ProcessGDBRemote::EnableBreakpointSite(BreakpointSite *bp_site) {
34293429
site_id, (uint64_t)addr);
34303430

34313431
// Breakpoint already exists and is enabled
3432-
if (IsBreakpointSiteEnabled(*bp_site)) {
3432+
if (IsBreakpointSitePhysicallyEnabled(*bp_site)) {
34333433
LLDB_LOGF(log,
34343434
"ProcessGDBRemote::EnableBreakpointSite (size_id = %" PRIu64
34353435
") address = 0x%" PRIx64 " -- SUCCESS (already enabled)",
@@ -3450,7 +3450,7 @@ Status ProcessGDBRemote::DisableBreakpointSite(BreakpointSite *bp_site) {
34503450
") addr = 0x%8.8" PRIx64,
34513451
site_id, (uint64_t)addr);
34523452

3453-
if (!IsBreakpointSiteEnabled(*bp_site)) {
3453+
if (!IsBreakpointSitePhysicallyEnabled(*bp_site)) {
34543454
LLDB_LOGF(log,
34553455
"ProcessGDBRemote::DisableBreakpointSite (site_id = %" PRIu64
34563456
") addr = 0x%8.8" PRIx64 " -- SUCCESS (already disabled)",
@@ -6112,7 +6112,7 @@ void ProcessGDBRemote::DidForkSwitchSoftwareBreakpoints(
61126112

61136113
GetBreakpointSiteList().ForEach([this, enable, entry_addr,
61146114
log](BreakpointSite *bp_site) {
6115-
if (IsBreakpointSiteEnabled(*bp_site) &&
6115+
if (IsBreakpointSitePhysicallyEnabled(*bp_site) &&
61166116
(bp_site->GetType() == BreakpointSite::eSoftware ||
61176117
bp_site->GetType() == BreakpointSite::eExternal)) {
61186118
// During expression evaluation, retain the expression-return trap
@@ -6136,7 +6136,7 @@ void ProcessGDBRemote::DidForkSwitchSoftwareBreakpoints(
61366136
void ProcessGDBRemote::DidForkSwitchHardwareTraps(bool enable) {
61376137
if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointHardware)) {
61386138
GetBreakpointSiteList().ForEach([this, enable](BreakpointSite *bp_site) {
6139-
if (IsBreakpointSiteEnabled(*bp_site) &&
6139+
if (IsBreakpointSitePhysicallyEnabled(*bp_site) &&
61406140
bp_site->GetType() == BreakpointSite::eHardware) {
61416141
m_gdb_comm.SendGDBStoppointTypePacket(
61426142
eBreakpointHardware, enable, bp_site->GetLoadAddress(),

lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ size_t ScriptedProcess::DoWriteMemory(lldb::addr_t vm_addr, const void *buf,
265265
Status ScriptedProcess::EnableBreakpointSite(BreakpointSite *bp_site) {
266266
assert(bp_site != nullptr);
267267

268-
if (IsBreakpointSiteEnabled(*bp_site)) {
268+
if (IsBreakpointSitePhysicallyEnabled(*bp_site)) {
269269
return {};
270270
}
271271

lldb/source/Plugins/Process/scripted/ScriptedThread.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ bool ScriptedThread::CalculateStopInfo() {
316316
ProcessSP proc = GetProcess();
317317
if (BreakpointSiteSP bp_site_sp =
318318
proc->GetBreakpointSiteList().FindByAddress(pc))
319-
if (proc->IsBreakpointSiteEnabled(*bp_site_sp))
319+
if (proc->IsBreakpointSitePhysicallyEnabled(*bp_site_sp))
320320
SetThreadStoppedAtUnexecutedBP(pc);
321321
}
322322

0 commit comments

Comments
 (0)