[Lldb-commits] [lldb] 776ee6f - [lldb] Implement delayed breakpoints (#192971)
via lldb-commits
lldb-commits at lists.llvm.org
Thu Apr 30 23:52:50 PDT 2026
Author: Felipe de Azevedo Piovezan
Date: 2026-05-01T07:52:46+01:00
New Revision: 776ee6f0dd2f4bc87d565d642d95f399224a565f
URL: https://github.com/llvm/llvm-project/commit/776ee6f0dd2f4bc87d565d642d95f399224a565f
DIFF: https://github.com/llvm/llvm-project/commit/776ee6f0dd2f4bc87d565d642d95f399224a565f.diff
LOG: [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:
* https://github.com/llvm/llvm-project/pull/192910
* https://github.com/llvm/llvm-project/pull/192914
* https://github.com/llvm/llvm-project/pull/192915
* https://github.com/llvm/llvm-project/pull/192919
* https://github.com/llvm/llvm-project/pull/192962
* https://github.com/llvm/llvm-project/pull/192964
* https://github.com/llvm/llvm-project/pull/192971
* https://github.com/llvm/llvm-project/pull/192988
Added:
lldb/test/API/functionalities/breakpoint/delayed_breakpoints/Makefile
lldb/test/API/functionalities/breakpoint/delayed_breakpoints/TestDelayedBreakpoint.py
lldb/test/API/functionalities/breakpoint/delayed_breakpoints/main.c
Modified:
lldb/include/lldb/Target/Process.h
lldb/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp
lldb/source/Plugins/Process/Utility/StopInfoMachException.cpp
lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp
lldb/source/Plugins/Process/scripted/ScriptedThread.cpp
lldb/source/Target/Process.cpp
lldb/source/Target/TargetProperties.td
lldb/source/Target/ThreadPlanStepOverBreakpoint.cpp
Removed:
################################################################################
diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h
index fb06850d34a19..045fa9ee2a05a 100644
--- a/lldb/include/lldb/Target/Process.h
+++ b/lldb/include/lldb/Target/Process.h
@@ -114,6 +114,7 @@ class ProcessProperties : public Properties {
Args GetAlwaysRunThreadNames() const;
FollowForkMode GetFollowForkMode() const;
bool TrackMemoryCacheChanges() const;
+ bool GetUseDelayedBreakpoints() const;
protected:
Process *m_process; // Can be nullptr for global ProcessProperties
@@ -2246,6 +2247,9 @@ class Process : public std::enable_shared_from_this<Process>,
// Process Breakpoints
size_t GetSoftwareBreakpointTrapOpcode(BreakpointSite *bp_site);
+ enum class BreakpointAction { Enable, Disable };
+
+protected:
virtual Status EnableBreakpointSite(BreakpointSite *bp_site) {
return Status::FromErrorStringWithFormatv(
"error: {0} does not support enabling breakpoints", GetPluginName());
@@ -2256,6 +2260,24 @@ class Process : public std::enable_shared_from_this<Process>,
"error: {0} does not support disabling breakpoints", GetPluginName());
}
+ /// Compare BreakpointSiteSPs by ID, so that iteration order is independent
+ /// of pointer addresses.
+ struct SiteIDCmp {
+ bool operator()(const lldb::BreakpointSiteSP &lhs,
+ const lldb::BreakpointSiteSP &rhs) const {
+ return lhs->GetID() < rhs->GetID();
+ }
+ };
+ using BreakpointSiteToActionMap =
+ std::map<lldb::BreakpointSiteSP, BreakpointAction, SiteIDCmp>;
+
+ virtual llvm::Error
+ UpdateBreakpointSites(const BreakpointSiteToActionMap &site_to_action);
+
+public:
+ llvm::Error ExecuteBreakpointSiteAction(BreakpointSite &site,
+ Process::BreakpointAction action);
+
// This is implemented completely using the lldb::Process API. Subclasses
// don't need to implement this function unless the standard flow of read
// existing opcode, write breakpoint opcode, verify breakpoint opcode doesn't
@@ -2286,6 +2308,8 @@ class Process : public std::enable_shared_from_this<Process>,
bool IsBreakpointSiteEnabled(const BreakpointSite &site);
+ bool IsBreakpointSitePhysicallyEnabled(const BreakpointSite &site);
+
// BreakpointLocations use RemoveConstituentFromBreakpointSite to remove
// themselves from the constituent's list of this breakpoint sites.
void RemoveConstituentFromBreakpointSite(lldb::user_id_t site_id,
@@ -3540,6 +3564,21 @@ void PruneThreadPlans();
/// GetExtendedCrashInformation.
StructuredData::DictionarySP m_crash_info_dict_sp;
+ struct DelayedBreakpointCache {
+ void Enqueue(lldb::BreakpointSiteSP site, BreakpointAction action);
+ void RemoveSite(lldb::BreakpointSiteSP site) {
+ m_site_to_action.erase(site);
+ }
+ void Clear() { m_site_to_action.clear(); }
+
+ BreakpointSiteToActionMap m_site_to_action;
+ };
+
+ DelayedBreakpointCache m_delayed_breakpoints;
+ std::recursive_mutex m_delayed_breakpoints_mutex;
+
+ llvm::Error FlushDelayedBreakpoints();
+
size_t RemoveBreakpointOpcodesFromBuffer(lldb::addr_t addr, size_t size,
uint8_t *buf) const;
diff --git a/lldb/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp b/lldb/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp
index b4598a422e579..6166096a4e1d3 100644
--- a/lldb/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp
+++ b/lldb/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp
@@ -633,7 +633,7 @@ Status ProcessKDP::EnableBreakpointSite(BreakpointSite *bp_site) {
if (m_comm.LocalBreakpointsAreSupported()) {
Status error;
- if (!IsBreakpointSiteEnabled(*bp_site)) {
+ if (!IsBreakpointSitePhysicallyEnabled(*bp_site)) {
if (m_comm.SendRequestBreakpoint(true, bp_site->GetLoadAddress())) {
SetBreakpointSiteEnabled(*bp_site);
bp_site->SetType(BreakpointSite::eExternal);
@@ -649,7 +649,7 @@ Status ProcessKDP::EnableBreakpointSite(BreakpointSite *bp_site) {
Status ProcessKDP::DisableBreakpointSite(BreakpointSite *bp_site) {
if (m_comm.LocalBreakpointsAreSupported()) {
Status error;
- if (IsBreakpointSiteEnabled(*bp_site)) {
+ if (IsBreakpointSitePhysicallyEnabled(*bp_site)) {
BreakpointSite::Type bp_type = bp_site->GetType();
if (bp_type == BreakpointSite::eExternal) {
if (m_destroy_in_process && m_comm.IsRunning()) {
diff --git a/lldb/source/Plugins/Process/Utility/StopInfoMachException.cpp b/lldb/source/Plugins/Process/Utility/StopInfoMachException.cpp
index deb95d9a03ca3..5fbc1f62a065f 100644
--- a/lldb/source/Plugins/Process/Utility/StopInfoMachException.cpp
+++ b/lldb/source/Plugins/Process/Utility/StopInfoMachException.cpp
@@ -636,7 +636,7 @@ StopInfoSP StopInfoMachException::CreateStopReasonWithMachException(
addr_t pc = reg_ctx_sp->GetPC();
BreakpointSiteSP bp_site_sp =
process_sp->GetBreakpointSiteList().FindByAddress(pc);
- if (bp_site_sp && process_sp->IsBreakpointSiteEnabled(*bp_site_sp))
+ if (bp_site_sp && process_sp->IsBreakpointSitePhysicallyEnabled(*bp_site_sp))
thread.SetThreadStoppedAtUnexecutedBP(pc);
switch (exc_type) {
@@ -771,7 +771,8 @@ StopInfoSP StopInfoMachException::CreateStopReasonWithMachException(
if (!bp_site_sp && reg_ctx_sp) {
bp_site_sp = process_sp->GetBreakpointSiteList().FindByAddress(pc);
}
- if (bp_site_sp && process_sp->IsBreakpointSiteEnabled(*bp_site_sp)) {
+ if (bp_site_sp &&
+ process_sp->IsBreakpointSitePhysicallyEnabled(*bp_site_sp)) {
// We've hit this breakpoint, whether it was intended for this thread
// or not. Clear this in the Tread object so we step past it on resume.
thread.SetThreadHitBreakpointSite();
@@ -865,7 +866,8 @@ bool StopInfoMachException::WasContinueInterrupted(Thread &thread) {
// We have a hardware breakpoint -- this is the kernel bug.
auto &bp_site_list = process_sp->GetBreakpointSiteList();
for (auto &site : bp_site_list.Sites()) {
- if (site->IsHardware() && process_sp->IsBreakpointSiteEnabled(*site)) {
+ if (site->IsHardware() &&
+ process_sp->IsBreakpointSitePhysicallyEnabled(*site)) {
LLDB_LOGF(log,
"Thread stopped with insn-step completed mach exception but "
"thread was not stepping; there is a hardware breakpoint set.");
diff --git a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
index c41547a2c0961..28b5554069c90 100644
--- a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
@@ -415,7 +415,7 @@ void ProcessWindows::RefreshStateAfterStop() {
// If we're at a BreakpointSite, mark this as an Unexecuted Breakpoint.
// We'll clear that state if we've actually executed the breakpoint.
BreakpointSiteSP site(GetBreakpointSiteList().FindByAddress(pc));
- if (site && IsBreakpointSiteEnabled(*site))
+ if (site && IsBreakpointSitePhysicallyEnabled(*site))
stop_thread->SetThreadStoppedAtUnexecutedBP(pc);
switch (active_exception->GetExceptionCode()) {
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index e8f32c564ee03..adf108919b36e 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -1848,7 +1848,7 @@ ThreadSP ProcessGDBRemote::SetThreadStopInfo(
addr_t pc = thread_sp->GetRegisterContext()->GetPC();
BreakpointSiteSP bp_site_sp =
thread_sp->GetProcess()->GetBreakpointSiteList().FindByAddress(pc);
- if (bp_site_sp && IsBreakpointSiteEnabled(*bp_site_sp))
+ if (bp_site_sp && IsBreakpointSitePhysicallyEnabled(*bp_site_sp))
thread_sp->SetThreadStoppedAtUnexecutedBP(pc);
if (exc_type != 0) {
@@ -2032,7 +2032,7 @@ ThreadSP ProcessGDBRemote::SetThreadStopInfo(
// BreakpointSites in any other location, but we can't know for
// sure what happened so it's a reasonable default.
if (bp_site_sp) {
- if (IsBreakpointSiteEnabled(*bp_site_sp))
+ if (IsBreakpointSitePhysicallyEnabled(*bp_site_sp))
thread_sp->SetThreadHitBreakpointSite();
if (bp_site_sp->ValidForThisThread(*thread_sp)) {
@@ -3429,7 +3429,7 @@ Status ProcessGDBRemote::EnableBreakpointSite(BreakpointSite *bp_site) {
site_id, (uint64_t)addr);
// Breakpoint already exists and is enabled
- if (IsBreakpointSiteEnabled(*bp_site)) {
+ if (IsBreakpointSitePhysicallyEnabled(*bp_site)) {
LLDB_LOGF(log,
"ProcessGDBRemote::EnableBreakpointSite (size_id = %" PRIu64
") address = 0x%" PRIx64 " -- SUCCESS (already enabled)",
@@ -3450,7 +3450,7 @@ Status ProcessGDBRemote::DisableBreakpointSite(BreakpointSite *bp_site) {
") addr = 0x%8.8" PRIx64,
site_id, (uint64_t)addr);
- if (!IsBreakpointSiteEnabled(*bp_site)) {
+ if (!IsBreakpointSitePhysicallyEnabled(*bp_site)) {
LLDB_LOGF(log,
"ProcessGDBRemote::DisableBreakpointSite (site_id = %" PRIu64
") addr = 0x%8.8" PRIx64 " -- SUCCESS (already disabled)",
@@ -6112,7 +6112,7 @@ void ProcessGDBRemote::DidForkSwitchSoftwareBreakpoints(
GetBreakpointSiteList().ForEach([this, enable, entry_addr,
log](BreakpointSite *bp_site) {
- if (IsBreakpointSiteEnabled(*bp_site) &&
+ if (IsBreakpointSitePhysicallyEnabled(*bp_site) &&
(bp_site->GetType() == BreakpointSite::eSoftware ||
bp_site->GetType() == BreakpointSite::eExternal)) {
// During expression evaluation, retain the expression-return trap
@@ -6136,7 +6136,7 @@ void ProcessGDBRemote::DidForkSwitchSoftwareBreakpoints(
void ProcessGDBRemote::DidForkSwitchHardwareTraps(bool enable) {
if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointHardware)) {
GetBreakpointSiteList().ForEach([this, enable](BreakpointSite *bp_site) {
- if (IsBreakpointSiteEnabled(*bp_site) &&
+ if (IsBreakpointSitePhysicallyEnabled(*bp_site) &&
bp_site->GetType() == BreakpointSite::eHardware) {
m_gdb_comm.SendGDBStoppointTypePacket(
eBreakpointHardware, enable, bp_site->GetLoadAddress(),
diff --git a/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp b/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp
index ac0f451a3d5fa..c254a6841b707 100644
--- a/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp
+++ b/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp
@@ -265,7 +265,7 @@ size_t ScriptedProcess::DoWriteMemory(lldb::addr_t vm_addr, const void *buf,
Status ScriptedProcess::EnableBreakpointSite(BreakpointSite *bp_site) {
assert(bp_site != nullptr);
- if (IsBreakpointSiteEnabled(*bp_site)) {
+ if (IsBreakpointSitePhysicallyEnabled(*bp_site)) {
return {};
}
diff --git a/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp b/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp
index 35774cfa01742..d11d134295579 100644
--- a/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp
+++ b/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp
@@ -316,7 +316,7 @@ bool ScriptedThread::CalculateStopInfo() {
ProcessSP proc = GetProcess();
if (BreakpointSiteSP bp_site_sp =
proc->GetBreakpointSiteList().FindByAddress(pc))
- if (proc->IsBreakpointSiteEnabled(*bp_site_sp))
+ if (proc->IsBreakpointSitePhysicallyEnabled(*bp_site_sp))
SetThreadStoppedAtUnexecutedBP(pc);
}
diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp
index 58c41c2584676..cb9a13e5bc80c 100644
--- a/lldb/source/Target/Process.cpp
+++ b/lldb/source/Target/Process.cpp
@@ -79,6 +79,17 @@ using namespace lldb;
using namespace lldb_private;
using namespace std::chrono;
+void Process::DelayedBreakpointCache::Enqueue(lldb::BreakpointSiteSP site,
+ BreakpointAction action) {
+ auto [previous, inserted] = m_site_to_action.insert({site, action});
+ // New site or already enqueued for the same action.
+ if (inserted || previous->second == action)
+ return;
+ // Previously enqueued for the opposite action, don't update the site.
+ m_site_to_action.erase(previous);
+ assert(site->m_enabled == (action == BreakpointAction::Enable));
+}
+
class ProcessOptionValueProperties
: public Cloneable<ProcessOptionValueProperties, OptionValueProperties> {
public:
@@ -322,6 +333,12 @@ bool ProcessProperties::GetStopOnExec() const {
idx, g_process_properties[idx].default_uint_value != 0);
}
+bool ProcessProperties::GetUseDelayedBreakpoints() const {
+ const uint32_t idx = ePropertyUseDelayedBreakpoints;
+ return GetPropertyAtIndexAs<bool>(
+ idx, g_process_properties[idx].default_uint_value != 0);
+}
+
std::chrono::seconds ProcessProperties::GetUtilityExpressionTimeout() const {
const uint32_t idx = ePropertyUtilityExpressionTimeout;
uint64_t value = GetPropertyAtIndexAs<uint64_t>(
@@ -1546,7 +1563,8 @@ Process::GetBreakpointSiteList() const {
void Process::DisableAllBreakpointSites() {
m_breakpoint_site_list.ForEach([this](BreakpointSite *bp_site) -> void {
- DisableBreakpointSite(bp_site);
+ llvm::consumeError(
+ ExecuteBreakpointSiteAction(*bp_site, BreakpointAction::Disable));
});
}
@@ -1564,7 +1582,8 @@ Status Process::DisableBreakpointSiteByID(lldb::user_id_t break_id) {
BreakpointSiteSP bp_site_sp = m_breakpoint_site_list.FindByID(break_id);
if (bp_site_sp) {
if (IsBreakpointSiteEnabled(*bp_site_sp))
- error = DisableBreakpointSite(bp_site_sp.get());
+ error = Status::FromError(
+ ExecuteBreakpointSiteAction(*bp_site_sp, BreakpointAction::Disable));
} else {
error = Status::FromErrorStringWithFormat(
"invalid breakpoint site ID: %" PRIu64, break_id);
@@ -1573,12 +1592,40 @@ Status Process::DisableBreakpointSiteByID(lldb::user_id_t break_id) {
return error;
}
+llvm::Error Process::ExecuteBreakpointSiteAction(BreakpointSite &site,
+ BreakpointAction action) {
+ auto site_sp = site.shared_from_this();
+ std::unique_lock<std::recursive_mutex> guard(m_delayed_breakpoints_mutex);
+
+ // Ignore requests that won't change the Site status.
+ if (IsBreakpointSiteEnabled(*site_sp) == (action == BreakpointAction::Enable))
+ return llvm::Error::success();
+
+ if (GetUseDelayedBreakpoints()) {
+ m_delayed_breakpoints.Enqueue(site_sp, action);
+ return llvm::Error::success();
+ }
+
+ m_delayed_breakpoints.RemoveSite(site_sp);
+ guard.unlock();
+
+ switch (action) {
+ case BreakpointAction::Enable:
+ return EnableBreakpointSite(site_sp.get()).takeError();
+ case BreakpointAction::Disable:
+ return DisableBreakpointSite(site_sp.get()).takeError();
+ }
+
+ llvm_unreachable("Unhandled BreakpointAction");
+}
+
Status Process::EnableBreakpointSiteByID(lldb::user_id_t break_id) {
Status error;
BreakpointSiteSP bp_site_sp = m_breakpoint_site_list.FindByID(break_id);
if (bp_site_sp) {
if (!IsBreakpointSiteEnabled(*bp_site_sp))
- error = EnableBreakpointSite(bp_site_sp.get());
+ error = Status::FromError(
+ ExecuteBreakpointSiteAction(*bp_site_sp, BreakpointAction::Enable));
} else {
error = Status::FromErrorStringWithFormat(
"invalid breakpoint site ID: %" PRIu64, break_id);
@@ -1587,6 +1634,20 @@ Status Process::EnableBreakpointSiteByID(lldb::user_id_t break_id) {
}
bool Process::IsBreakpointSiteEnabled(const BreakpointSite &site) {
+ std::lock_guard<std::recursive_mutex> guard(m_delayed_breakpoints_mutex);
+
+ // `site` won't be mutated, but the cache stores mutable pointers.
+ auto it = m_delayed_breakpoints.m_site_to_action.find(
+ const_cast<BreakpointSite &>(site).shared_from_this());
+
+ // If no actions are delayed, use the current state of the site.
+ if (it == m_delayed_breakpoints.m_site_to_action.end())
+ return site.m_enabled;
+
+ return it->second == BreakpointAction::Enable;
+}
+
+bool Process::IsBreakpointSitePhysicallyEnabled(const BreakpointSite &site) {
return site.m_enabled;
}
@@ -1648,6 +1709,35 @@ static addr_t ComputeConstituentLoadAddress(BreakpointLocation &constituent,
return resolved_address.GetOpcodeLoadAddress(&target);
}
+llvm::Error Process::FlushDelayedBreakpoints() {
+ std::unique_lock<std::recursive_mutex> guard(m_delayed_breakpoints_mutex);
+
+ // Clear the cache in m_delayed_breakpoints so it can't affect the actual
+ // enabling of breakpoints. For example, if `EnableSoftwareBreakpoint` is
+ // called outside of FlushDelayedBreakpoints, it needs to check the delayed
+ // breakpoints and possibly early return. However, when called from
+ // FlushDelayedBreakpoints, the queue better be empty so that no early returns
+ // take place.
+ auto site_to_action = std::move(m_delayed_breakpoints.m_site_to_action);
+ m_delayed_breakpoints.m_site_to_action.clear();
+
+ guard.unlock();
+ // Use a copy of the cache so that iteration is safe.
+ return UpdateBreakpointSites(site_to_action);
+}
+
+llvm::Error Process::UpdateBreakpointSites(
+ const BreakpointSiteToActionMap &site_to_action) {
+ llvm::Error error = llvm::Error::success();
+ for (auto [site, action] : site_to_action) {
+ Status new_error = action == BreakpointAction::Enable
+ ? EnableBreakpointSite(site.get())
+ : DisableBreakpointSite(site.get());
+ error = llvm::joinErrors(std::move(error), new_error.takeError());
+ }
+ return error;
+}
+
lldb::break_id_t
Process::CreateBreakpointSite(const BreakpointLocationSP &constituent,
bool use_hardware) {
@@ -1667,7 +1757,15 @@ Process::CreateBreakpointSite(const BreakpointLocationSP &constituent,
BreakpointSiteSP bp_site_sp(
new BreakpointSite(constituent, load_addr, use_hardware));
- Status error = EnableBreakpointSite(bp_site_sp.get());
+
+ bool bp_from_address =
+ constituent->GetBreakpoint().GetResolver()->GetResolverTy() ==
+ BreakpointResolver::ResolverTy::AddressResolver;
+ bool should_be_eager = use_hardware || bp_from_address;
+
+ auto error = should_be_eager ? EnableBreakpointSite(bp_site_sp.get())
+ : Status::FromError(ExecuteBreakpointSiteAction(
+ *bp_site_sp, BreakpointAction::Enable));
if (error.Success()) {
constituent->SetBreakpointSite(bp_site_sp);
return m_breakpoint_site_list.Add(bp_site_sp);
@@ -1692,7 +1790,8 @@ void Process::RemoveConstituentFromBreakpointSite(
if (num_constituents == 0) {
// Don't try to disable the site if we don't have a live process anymore.
if (IsAlive())
- DisableBreakpointSite(bp_site_sp.get());
+ llvm::consumeError(
+ ExecuteBreakpointSiteAction(*bp_site_sp, BreakpointAction::Disable));
m_breakpoint_site_list.RemoveByAddress(bp_site_sp->GetLoadAddress());
}
}
@@ -3435,6 +3534,9 @@ Status Process::PrivateResume() {
"Process::PrivateResume PreResumeActions failed, not resuming.");
} else {
m_mod_id.BumpResumeID();
+ if (auto E = FlushDelayedBreakpoints())
+ LLDB_LOG_ERROR(log, std::move(E),
+ "Failed to update some delayed breakpoints: {0}");
error = DoResume(direction);
if (error.Success()) {
DidResume();
@@ -3641,6 +3743,10 @@ Status Process::Detach(bool keep_stopped) {
m_thread_list.DiscardThreadPlans();
DisableAllBreakpointSites();
+ if (auto error = FlushDelayedBreakpoints())
+ LLDB_LOG_ERROR(
+ GetLog(LLDBLog::Process), std::move(error),
+ "Failed to update some delayed breakpoints during detach: {0}");
error = DoDetach(keep_stopped);
if (error.Success()) {
@@ -3710,6 +3816,10 @@ Status Process::DestroyImpl(bool force_kill) {
// doing this now.
m_thread_list.DiscardThreadPlans();
DisableAllBreakpointSites();
+ if (auto error = FlushDelayedBreakpoints())
+ LLDB_LOG_ERROR(
+ GetLog(LLDBLog::Process), std::move(error),
+ "Failed to update some delayed breakpoints during destroy: {0}");
}
error = DoDestroy();
diff --git a/lldb/source/Target/TargetProperties.td b/lldb/source/Target/TargetProperties.td
index 223a12e059258..23a1bb0b3ce4a 100644
--- a/lldb/source/Target/TargetProperties.td
+++ b/lldb/source/Target/TargetProperties.td
@@ -319,6 +319,11 @@ let Definition = "process", Path = "target.process" in {
Desc<"A list of thread names. Threads with any of these names will "
"always be resumed when the process resumes, even when other "
"threads are suspended during single-stepping operations.">;
+ def UseDelayedBreakpoints
+ : Property<"use-delayed-breakpoints", "Boolean">,
+ DefaultTrue,
+ Desc<"Specify whether updating breakpoints may be delayed until the "
+ "process is about to resume.">;
}
let Definition = "platform", Path = "platform" in {
diff --git a/lldb/source/Target/ThreadPlanStepOverBreakpoint.cpp b/lldb/source/Target/ThreadPlanStepOverBreakpoint.cpp
index 572454f9b897a..878aba2634f49 100644
--- a/lldb/source/Target/ThreadPlanStepOverBreakpoint.cpp
+++ b/lldb/source/Target/ThreadPlanStepOverBreakpoint.cpp
@@ -121,7 +121,8 @@ bool ThreadPlanStepOverBreakpoint::DoWillResume(StateType resume_state,
BreakpointSiteSP bp_site_sp(
m_process.GetBreakpointSiteList().FindByAddress(m_breakpoint_addr));
if (bp_site_sp && m_process.IsBreakpointSiteEnabled(*bp_site_sp)) {
- m_process.DisableBreakpointSite(bp_site_sp.get());
+ llvm::consumeError(m_process.ExecuteBreakpointSiteAction(
+ *bp_site_sp, Process::BreakpointAction::Disable));
m_reenabled_breakpoint_site = false;
}
}
@@ -167,7 +168,8 @@ void ThreadPlanStepOverBreakpoint::ReenableBreakpointSite() {
if (BreakpointSiteSP bp_site_sp =
m_process.GetBreakpointSiteList().FindByAddress(
m_breakpoint_addr))
- m_process.EnableBreakpointSite(bp_site_sp.get());
+ llvm::consumeError(m_process.ExecuteBreakpointSiteAction(
+ *bp_site_sp, Process::BreakpointAction::Enable));
}
}
}
diff --git a/lldb/test/API/functionalities/breakpoint/delayed_breakpoints/Makefile b/lldb/test/API/functionalities/breakpoint/delayed_breakpoints/Makefile
new file mode 100644
index 0000000000000..10495940055b6
--- /dev/null
+++ b/lldb/test/API/functionalities/breakpoint/delayed_breakpoints/Makefile
@@ -0,0 +1,3 @@
+C_SOURCES := main.c
+
+include Makefile.rules
diff --git a/lldb/test/API/functionalities/breakpoint/delayed_breakpoints/TestDelayedBreakpoint.py b/lldb/test/API/functionalities/breakpoint/delayed_breakpoints/TestDelayedBreakpoint.py
new file mode 100644
index 0000000000000..70a28bb935027
--- /dev/null
+++ b/lldb/test/API/functionalities/breakpoint/delayed_breakpoints/TestDelayedBreakpoint.py
@@ -0,0 +1,41 @@
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+import os
+
+
+ at skipIfWindows
+class TestDelayedBreakpoint(TestBase):
+ def test(self):
+ self.build()
+ logfile = os.path.join(self.getBuildDir(), "log.txt")
+ self.runCmd(f"log enable -f {logfile} gdb-remote packets")
+
+ target, process, _, _ = lldbutil.run_to_source_breakpoint(
+ self, "main", lldb.SBFileSpec("main.c")
+ )
+
+ self.runCmd(f"proc plugin packet send BEFORE_BPS", check=False)
+
+ breakpoint = target.BreakpointCreateByLocation("main.c", 1)
+ self.assertGreater(breakpoint.GetNumResolvedLocations(), 0)
+
+ self.runCmd(f"proc plugin packet send AFTER_BPS", check=False)
+
+ lldbutil.continue_to_breakpoint(process, breakpoint)
+ self.runCmd(f"log disable all")
+
+ self.runCmd(f"proc plugin packet send AFTER_CONTINUE", check=False)
+
+ self.assertTrue(os.path.exists(logfile))
+ log_text = open(logfile).read()
+
+ log_before_continue = log_text.split("BEFORE_BPS", 1)[-1].split("AFTER_BPS", 1)[
+ 0
+ ]
+ self.assertNotIn("send packet: $Z", log_before_continue)
+ self.assertNotIn("send packet: $z", log_before_continue)
+
+ log_after = log_text.split("AFTER_BPS", 1)[-1].split("AFTER_CONTINUE", 1)[0]
+ self.assertIn("send packet: $Z", log_after)
diff --git a/lldb/test/API/functionalities/breakpoint/delayed_breakpoints/main.c b/lldb/test/API/functionalities/breakpoint/delayed_breakpoints/main.c
new file mode 100644
index 0000000000000..a9086f89af86a
--- /dev/null
+++ b/lldb/test/API/functionalities/breakpoint/delayed_breakpoints/main.c
@@ -0,0 +1,7 @@
+int break_on_me() {
+ int i = 10; // breakhere
+ i++;
+ return i; // secondbreak
+}
+
+int main() { return break_on_me(); }
More information about the lldb-commits
mailing list