aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Molenda <jmolenda@apple.com>2025-04-07 13:48:01 -0700
committerJason Molenda <jmolenda@apple.com>2025-04-07 13:50:31 -0700
commitdf28c81f5a2b61a3b5ad1e6274dd27697a9367ac (patch)
tree2fc9073fb43379fc63a3ac64999109c1f1d5f493
parent3756ba3c87f78a2746f26434209e29ac288a5fc6 (diff)
downloadllvm-df28c81f5a2b61a3b5ad1e6274dd27697a9367ac.zip
llvm-df28c81f5a2b61a3b5ad1e6274dd27697a9367ac.tar.gz
llvm-df28c81f5a2b61a3b5ad1e6274dd27697a9367ac.tar.bz2
[lldb][debugserver] Fix an off-by-one error in watchpoint identification (#134314)
debugserver takes the address of a watchpoint exception and calculates which watchpoint was responsible for it. There was an off-by-one error in the range calculation which causes two watchpoints on consecutive ranges to not correctly identify hits to the second watchpoint. The result is that lldb wouldn't show the second watchpoint as ever being hit. Re-landing this test with a modification to only require two watchpoints in the test, instead of four. If four watchpoints can be set, it will test them. rdar://145107575
-rw-r--r--lldb/test/API/functionalities/watchpoint/consecutive-watchpoints/Makefile3
-rw-r--r--lldb/test/API/functionalities/watchpoint/consecutive-watchpoints/TestConsecutiveWatchpoints.py96
-rw-r--r--lldb/test/API/functionalities/watchpoint/consecutive-watchpoints/main.c22
-rw-r--r--lldb/tools/debugserver/source/DNBBreakpoint.cpp2
4 files changed, 122 insertions, 1 deletions
diff --git a/lldb/test/API/functionalities/watchpoint/consecutive-watchpoints/Makefile b/lldb/test/API/functionalities/watchpoint/consecutive-watchpoints/Makefile
new file mode 100644
index 0000000..1049594
--- /dev/null
+++ b/lldb/test/API/functionalities/watchpoint/consecutive-watchpoints/Makefile
@@ -0,0 +1,3 @@
+C_SOURCES := main.c
+
+include Makefile.rules
diff --git a/lldb/test/API/functionalities/watchpoint/consecutive-watchpoints/TestConsecutiveWatchpoints.py b/lldb/test/API/functionalities/watchpoint/consecutive-watchpoints/TestConsecutiveWatchpoints.py
new file mode 100644
index 0000000..bb73acc
--- /dev/null
+++ b/lldb/test/API/functionalities/watchpoint/consecutive-watchpoints/TestConsecutiveWatchpoints.py
@@ -0,0 +1,96 @@
+"""
+Watch contiguous memory regions with separate watchpoints, check that lldb
+correctly detect which watchpoint was hit for each one.
+"""
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class ConsecutiveWatchpointsTestCase(TestBase):
+ NO_DEBUG_INFO_TESTCASE = True
+
+ def continue_and_report_stop_reason(self, process, iter_str):
+ process.Continue()
+ self.assertIn(
+ process.GetState(), [lldb.eStateStopped, lldb.eStateExited], iter_str
+ )
+ thread = process.GetSelectedThread()
+ return thread.GetStopReason()
+
+ # debugserver only gained the ability to watch larger regions
+ # with this patch.
+ def test_consecutive_watchpoints(self):
+ """Test watchpoint that covers a large region of memory."""
+ self.build()
+ self.main_source_file = lldb.SBFileSpec("main.c")
+ (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
+ self, "break here", self.main_source_file
+ )
+
+ frame = thread.GetFrameAtIndex(0)
+
+ field2_wp = (
+ frame.locals["var"][0]
+ .GetChildMemberWithName("field2")
+ .Watch(True, False, True)
+ )
+ field3_wp = (
+ frame.locals["var"][0]
+ .GetChildMemberWithName("field3")
+ .Watch(True, False, True)
+ )
+ field4_wp = (
+ frame.locals["var"][0]
+ .GetChildMemberWithName("field4")
+ .Watch(True, False, True)
+ )
+ field5_wp = (
+ frame.locals["var"][0]
+ .GetChildMemberWithName("field5")
+ .Watch(True, False, True)
+ )
+
+ # Require that the first two watchpoints
+ # are set -- hopefully every machine running
+ # the testsuite can support two watchpoints.
+ self.assertTrue(field2_wp.IsValid())
+ self.assertTrue(field3_wp.IsValid())
+
+ reason = self.continue_and_report_stop_reason(process, "continue to field2 wp")
+ self.assertEqual(reason, lldb.eStopReasonWatchpoint)
+ stop_reason_watchpoint_id = (
+ process.GetSelectedThread().GetStopReasonDataAtIndex(0)
+ )
+ self.assertEqual(stop_reason_watchpoint_id, field2_wp.GetID())
+
+ reason = self.continue_and_report_stop_reason(process, "continue to field3 wp")
+ self.assertEqual(reason, lldb.eStopReasonWatchpoint)
+ stop_reason_watchpoint_id = (
+ process.GetSelectedThread().GetStopReasonDataAtIndex(0)
+ )
+ self.assertEqual(stop_reason_watchpoint_id, field3_wp.GetID())
+
+ # If we were able to set the second two watchpoints,
+ # check that they are hit. Some CI bots can only
+ # create two watchpoints.
+ if field4_wp.IsValid() and field5_wp.IsValid():
+ reason = self.continue_and_report_stop_reason(
+ process, "continue to field4 wp"
+ )
+ self.assertEqual(reason, lldb.eStopReasonWatchpoint)
+ stop_reason_watchpoint_id = (
+ process.GetSelectedThread().GetStopReasonDataAtIndex(0)
+ )
+ self.assertEqual(stop_reason_watchpoint_id, field4_wp.GetID())
+
+ reason = self.continue_and_report_stop_reason(
+ process, "continue to field5 wp"
+ )
+ self.assertEqual(reason, lldb.eStopReasonWatchpoint)
+ stop_reason_watchpoint_id = (
+ process.GetSelectedThread().GetStopReasonDataAtIndex(0)
+ )
+ self.assertEqual(stop_reason_watchpoint_id, field5_wp.GetID())
diff --git a/lldb/test/API/functionalities/watchpoint/consecutive-watchpoints/main.c b/lldb/test/API/functionalities/watchpoint/consecutive-watchpoints/main.c
new file mode 100644
index 0000000..c0a3530b
--- /dev/null
+++ b/lldb/test/API/functionalities/watchpoint/consecutive-watchpoints/main.c
@@ -0,0 +1,22 @@
+#include <stdint.h>
+struct fields {
+ uint32_t field1;
+ uint32_t field2; // offset +4
+ uint16_t field3; // offset +8
+ uint16_t field4; // offset +10
+ uint16_t field5; // offset +12
+ uint16_t field6; // offset +14
+};
+
+int main() {
+ struct fields var = {0, 0, 0, 0, 0, 0};
+
+ var.field1 = 5; // break here
+ var.field2 = 6;
+ var.field3 = 7;
+ var.field4 = 8;
+ var.field5 = 9;
+ var.field6 = 10;
+
+ return var.field1 + var.field2 + var.field3;
+}
diff --git a/lldb/tools/debugserver/source/DNBBreakpoint.cpp b/lldb/tools/debugserver/source/DNBBreakpoint.cpp
index f63ecf2..e41bf9b 100644
--- a/lldb/tools/debugserver/source/DNBBreakpoint.cpp
+++ b/lldb/tools/debugserver/source/DNBBreakpoint.cpp
@@ -98,7 +98,7 @@ DNBBreakpointList::FindNearestWatchpoint(nub_addr_t addr) const {
if (pos.second.IsEnabled()) {
nub_addr_t start_addr = pos.second.Address();
nub_addr_t end_addr = start_addr + pos.second.ByteSize();
- if (addr >= start_addr && addr <= end_addr)
+ if (addr >= start_addr && addr < end_addr)
return &pos.second;
}
}