[Lldb-commits] [lldb] 19dad06 - [lldb] Fix handling of lldb managed software breakpoints (#222009)
via lldb-commits
lldb-commits at lists.llvm.org
Fri Sep 11 00:55:02 PDT 2026
Author: David Spickett
Date: 2026-09-11T08:54:57+01:00
New Revision: 19dad062e98520c641617fda2cec07592764d5dc
URL: https://github.com/llvm/llvm-project/commit/19dad062e98520c641617fda2cec07592764d5dc
DIFF: https://github.com/llvm/llvm-project/commit/19dad062e98520c641617fda2cec07592764d5dc.diff
LOG: [lldb] Fix handling of lldb managed software breakpoints (#222009)
Fixes #217910.
When the debug server can manage its own breakpoints this is called an
"external" breakpoint. When it cannot, lldb must manage the breakpoint
itself from the client side.
Part of doing that is FindInRange which is used to patch reads and
writes over software breakpoint sites. It had an issue where it would
not account for a breakpoint that started before the range, but extended
into the range. For example:
```
Memory content: ABCDEFGHIJKL
Read range: ----[--]----
Breakpoint location: -BKPT-------
Overlap with read: ---- at -------
Expected read result: ----EFGH----
Would actually get: ----TFGH----
```
This change fixes that mistake:
* Exit early if there are no sites to check.
* Exit early if the lower_bound result is beyond the upper bound of the
range - only if the result is not the end of the list. In the result was
end(), there may be one site before which we must also check.
The existing "Over end of breakpoint" test covers the bug I'm fixing.
The tests have been refactored to run normally and via. a proxy that
refuses breakpoint packets.
We could instead use two different proxies where one supports the
packets and one doesn't. However, that's more overhead per test and you
can't run proxy tests remotely.
Due to https://github.com/llvm/llvm-project/issues/222284, the proxy
tests cannot be run everywhere, but it can run on AArch64 which is
common.
Added:
Modified:
lldb/include/lldb/Breakpoint/StopPointSiteList.h
lldb/test/API/functionalities/breakpoint/write_over_software_breakpoint/TestWriteOverSoftwareBreakpoint.py
Removed:
################################################################################
diff --git a/lldb/include/lldb/Breakpoint/StopPointSiteList.h b/lldb/include/lldb/Breakpoint/StopPointSiteList.h
index 6371c7a7fb877..fd0b17839f3d2 100644
--- a/lldb/include/lldb/Breakpoint/StopPointSiteList.h
+++ b/lldb/include/lldb/Breakpoint/StopPointSiteList.h
@@ -194,10 +194,11 @@ template <typename StopPointSite> class StopPointSiteList {
return false;
std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if (m_site_list.empty())
+ return false;
+
typename collection::const_iterator lower, upper, pos;
lower = m_site_list.lower_bound(lower_bound);
- if (lower == m_site_list.end() || (*lower).first >= upper_bound)
- return false;
// This is one tricky bit. The site might overlap the bottom end of
// the range. So we grab the site prior to the lower bound, and check
@@ -212,6 +213,9 @@ template <typename StopPointSite> class StopPointSiteList {
bp_site_list.Add(prev_site);
}
+ if (lower != m_site_list.end() && lower->first >= upper_bound)
+ return !bp_site_list.IsEmpty();
+
upper = m_site_list.upper_bound(upper_bound);
for (pos = lower; pos != upper; pos++)
diff --git a/lldb/test/API/functionalities/breakpoint/write_over_software_breakpoint/TestWriteOverSoftwareBreakpoint.py b/lldb/test/API/functionalities/breakpoint/write_over_software_breakpoint/TestWriteOverSoftwareBreakpoint.py
index 1821e0fbfe7f3..894b5e4d31782 100644
--- a/lldb/test/API/functionalities/breakpoint/write_over_software_breakpoint/TestWriteOverSoftwareBreakpoint.py
+++ b/lldb/test/API/functionalities/breakpoint/write_over_software_breakpoint/TestWriteOverSoftwareBreakpoint.py
@@ -8,37 +8,42 @@
from lldbsuite.test.lldbtest import *
import lldbsuite.test.lldbutil as lldbutil
from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbgdbproxy import GDBProxyTestBase
-
-class WriteOverSoftwareBreakpoint(TestBase):
+class TestCases(object):
NO_DEBUG_INFO_TESTCASE = True
- # Could not find a way to make place_break_here visible to lldb on Windows.
- @skipIfWindows
- @skipIfOutOfTreeDebugserver
- def test_write_over_breakpoint(self):
- TestBase.setUp(self)
- self.line = line_number("main.c", "// break here")
+ def run_to_start(self):
+ is_proxy = isinstance(self, GDBProxyTestBase)
+
self.build()
exe = self.getBuildArtifact("a.out")
- self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
+ target = self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
+ if is_proxy:
+ self.connect(target)
+ self.line = line_number("main.c", "// break here")
lldbutil.run_break_set_by_file_and_line(
self, "main.c", self.line, num_expected_locations=1, loc_exact=True
)
- self.runCmd("run", RUN_SUCCEEDED)
+
+ self.runCmd("continue" if is_proxy else "run")
self.expect(
"thread list",
STOPPED_DUE_TO_BREAKPOINT,
substrs=["stopped", "stop reason = breakpoint"],
)
- target = self.dbg.GetSelectedTarget()
- process = target.GetProcess()
-
loop_start_breakpoint_addr = (
target.breakpoints[0].GetLocationAtIndex(0).GetLoadAddress()
)
+ return target, target.GetProcess(), loop_start_breakpoint_addr
+
+ # Could not find a way to make place_break_here visible to lldb on Windows.
+ @skipIfWindows
+ @skipIfOutOfTreeDebugserver
+ def test_write_over_breakpoint(self):
+ target, process, loop_start_breakpoint_addr = self.run_to_start()
# Memory operations and breakpoint actions must be sent to the server
# right away instead of waiting for the next continue event.
@@ -158,30 +163,8 @@ def test_write_over_breakpoint(self):
self.assertEqual(loop_start_breakpoint_addr, thread.selected_frame.GetPC())
def test_write_over_uncommitted_breakpoint(self):
- TestBase.setUp(self)
- self.line = line_number("main.c", "// break here")
- self.build()
- exe = self.getBuildArtifact("a.out")
- self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
-
self.runCmd("settings set target.process.use-delayed-breakpoints true")
-
- lldbutil.run_break_set_by_file_and_line(
- self, "main.c", self.line, num_expected_locations=1, loc_exact=True
- )
- self.runCmd("run", RUN_SUCCEEDED)
- self.expect(
- "thread list",
- STOPPED_DUE_TO_BREAKPOINT,
- substrs=["stopped", "stop reason = breakpoint"],
- )
-
- target = self.dbg.GetSelectedTarget()
- process = target.GetProcess()
-
- loop_start_breakpoint_addr = (
- target.breakpoints[0].GetLocationAtIndex(0).GetLoadAddress()
- )
+ target, process, loop_start_breakpoint_addr = self.run_to_start()
bkpt = target.BreakpointCreateByName("foo")
self.assertTrue(bkpt.IsValid())
@@ -244,3 +227,25 @@ def check_memory():
# should be managing the breakpoint, but this checks that the handover
# was done correctly.
check_memory()
+
+
+ at skipIfRemote
+# x86, x86_64, s390x and all versions of Windows report a breakpoint address
+# after the breakpoint and the test fails due to
+# https://github.com/llvm/llvm-project/issues/222284.
+ at skipIf(archs=["i386", "x86_64", "s390x"])
+ at skipIfWindows
+class WriteOverLLDBManagedSoftwareBreakpoint(GDBProxyTestBase, TestCases):
+ def respond(self, packet):
+ # Pretend that the server cannot handle breakpoints, which means lldb
+ # must manage them.
+ if packet.startswith("jMultiBreakpoint"):
+ return ""
+ elif packet.startswith("Z"):
+ return ""
+
+ return super().respond(packet)
+
+
+class WriteOverExternalSoftwareBreakpoint(TestBase, TestCases):
+ pass
More information about the lldb-commits
mailing list