Skip to content

Commit a0237d2

Browse files
authored
Merge pull request #6771 from jasonmolenda/add-mask-and-bas-watchpoint-support-to-debugserver2
Add mask and bas watchpoint support to debugserver2
2 parents 8e6e4c6 + 396f261 commit a0237d2

File tree

19 files changed

+834
-155
lines changed

19 files changed

+834
-155
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
C_SOURCES := main.c
2+
3+
include Makefile.rules
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
"""
2+
Watch one byte in the middle of a doubleword, mutate the
3+
entire doubleword including the watched byte. On AArch64
4+
the trap address is probably the start of the doubleword,
5+
instead of the address of our watched byte. Test that lldb
6+
correctly associates this watchpoint trap with our watchpoint.
7+
"""
8+
9+
10+
11+
import lldb
12+
from lldbsuite.test.decorators import *
13+
from lldbsuite.test.lldbtest import *
14+
from lldbsuite.test import lldbutil
15+
16+
17+
class UnalignedWatchpointTestCase(TestBase):
18+
NO_DEBUG_INFO_TESTCASE = True
19+
@skipIfOutOfTreeDebugserver
20+
def test_unaligned_watchpoint(self):
21+
"""Test an unaligned watchpoint triggered by a larger aligned write."""
22+
self.build()
23+
self.main_source_file = lldb.SBFileSpec("main.c")
24+
(target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(self,
25+
"break here", self.main_source_file)
26+
27+
frame = thread.GetFrameAtIndex(0)
28+
29+
self.expect("watchpoint set variable a.buf[2]")
30+
31+
self.runCmd("process continue")
32+
33+
# We should be stopped again due to the watchpoint (write type), but
34+
# only once. The stop reason of the thread should be watchpoint.
35+
self.expect("thread list", STOPPED_DUE_TO_WATCHPOINT,
36+
substrs=['stopped',
37+
'stop reason = watchpoint'])
38+
39+
# Use the '-v' option to do verbose listing of the watchpoint.
40+
# The hit count should now be 1.
41+
self.expect("watchpoint list -v",
42+
substrs=['hit_count = 1'])
43+
44+
self.runCmd("process continue")
45+
46+
# We should be stopped again due to the watchpoint (write type), but
47+
# only once. The stop reason of the thread should be watchpoint.
48+
self.expect("thread list", STOPPED_DUE_TO_WATCHPOINT,
49+
substrs=['stopped',
50+
'stop reason = watchpoint'])
51+
52+
# Use the '-v' option to do verbose listing of the watchpoint.
53+
# The hit count should now be 1.
54+
self.expect("watchpoint list -v",
55+
substrs=['hit_count = 2'])
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#include <stdint.h>
2+
#include <stdio.h>
3+
int main() {
4+
union {
5+
uint8_t buf[8];
6+
uint64_t val;
7+
} a;
8+
a.val = 0; // break here
9+
for (int i = 0; i < 5; i++) {
10+
a.val = i;
11+
printf("a.val is %lu\n", a.val);
12+
}
13+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
C_SOURCES := main.c
2+
3+
include Makefile.rules
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
"""
2+
Watch larger-than-8-bytes regions of memory, confirm that
3+
writes to those regions are detected.
4+
"""
5+
6+
7+
8+
import lldb
9+
from lldbsuite.test.decorators import *
10+
from lldbsuite.test.lldbtest import *
11+
from lldbsuite.test import lldbutil
12+
13+
class UnalignedWatchpointTestCase(TestBase):
14+
15+
def continue_and_report_stop_reason(self, process, iter_str):
16+
process.Continue()
17+
self.assertIn(process.GetState(), [lldb.eStateStopped, lldb.eStateExited],
18+
iter_str)
19+
thread = process.GetSelectedThread()
20+
return thread.GetStopReason()
21+
22+
23+
NO_DEBUG_INFO_TESTCASE = True
24+
# debugserver on AArch64 has this feature.
25+
@skipIf(archs=no_match(['arm64', 'arm64e', 'aarch64']))
26+
@skipUnlessDarwin
27+
28+
# debugserver only gained the ability to watch larger regions
29+
# with this patch.
30+
@skipIfOutOfTreeDebugserver
31+
32+
def test_large_watchpoint(self):
33+
"""Test watchpoint that covers a large region of memory."""
34+
self.build()
35+
self.main_source_file = lldb.SBFileSpec("main.c")
36+
(target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(self,
37+
"break here", self.main_source_file)
38+
39+
frame = thread.GetFrameAtIndex(0)
40+
41+
array_addr = frame.GetValueForVariablePath("array").GetValueAsUnsigned()
42+
43+
# watch 256 uint32_t elements in the middle of the array,
44+
# don't assume that the heap allocated array is aligned
45+
# to a 1024 byte boundary to begin with, force alignment.
46+
wa_256_addr = ((array_addr + 1024) & ~(1024-1))
47+
err = lldb.SBError()
48+
wp = target.WatchAddress(wa_256_addr, 1024, False, True, err)
49+
self.assertTrue(wp.IsValid())
50+
self.assertSuccess(err)
51+
52+
c_count = 0
53+
reason = self.continue_and_report_stop_reason(process, "continue #%d" % c_count)
54+
while reason == lldb.eStopReasonWatchpoint:
55+
c_count = c_count + 1
56+
reason = self.continue_and_report_stop_reason(process, "continue #%d" % c_count)
57+
self.assertLessEqual(c_count, 16)
58+
59+
self.assertEqual(c_count, 16)
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#include <stdint.h>
2+
#include <stdio.h>
3+
#include <string.h>
4+
#include <stdlib.h>
5+
int main() {
6+
const int count = 65535;
7+
int *array = (int*) malloc(sizeof (int) * count);
8+
memset (array, 0, count * sizeof (int));
9+
10+
puts ("break here");
11+
12+
for (int i = 0; i < count - 16; i += 16)
13+
array[i] += 10;
14+
15+
puts ("done, exiting.");
16+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
C_SOURCES := main.c
2+
3+
include Makefile.rules
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
"""
2+
Watch 4 bytes which spawn two doubleword aligned regions.
3+
On a target that supports 8 byte watchpoints, this will
4+
need to be implemented with a hardware watchpoint on both
5+
doublewords.
6+
"""
7+
8+
9+
10+
import lldb
11+
from lldbsuite.test.decorators import *
12+
from lldbsuite.test.lldbtest import *
13+
from lldbsuite.test import lldbutil
14+
15+
class UnalignedWatchpointTestCase(TestBase):
16+
17+
def hit_watchpoint_and_continue(self, process, iter_str):
18+
process.Continue()
19+
self.assertEqual(process.GetState(), lldb.eStateStopped,
20+
iter_str)
21+
thread = process.GetSelectedThread()
22+
self.assertEqual(thread.GetStopReason(), lldb.eStopReasonWatchpoint, iter_str)
23+
self.assertEqual(thread.GetStopReasonDataCount(), 1, iter_str)
24+
wp_num = thread.GetStopReasonDataAtIndex(0)
25+
self.assertEqual(wp_num, 1, iter_str)
26+
27+
NO_DEBUG_INFO_TESTCASE = True
28+
# debugserver on AArch64 has this feature.
29+
@skipIf(archs=no_match(['x86_64', 'arm64', 'arm64e', 'aarch64']))
30+
@skipUnlessDarwin
31+
# debugserver only started returning an exception address within
32+
# a range lldb expects in https://reviews.llvm.org/D147820 2023-04-12.
33+
# older debugservers will return the base address of the doubleword
34+
# which lldb doesn't understand, and will stop executing without a
35+
# proper stop reason.
36+
@skipIfOutOfTreeDebugserver
37+
38+
def test_unaligned_watchpoint(self):
39+
"""Test a watchpoint that is handled by two hardware watchpoint registers."""
40+
self.build()
41+
self.main_source_file = lldb.SBFileSpec("main.c")
42+
(target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(self,
43+
"break here", self.main_source_file)
44+
45+
frame = thread.GetFrameAtIndex(0)
46+
47+
a_bytebuf_6 = frame.GetValueForVariablePath("a.bytebuf[6]")
48+
a_bytebuf_6_addr = a_bytebuf_6.GetAddress().GetLoadAddress(target)
49+
err = lldb.SBError()
50+
wp = target.WatchAddress(a_bytebuf_6_addr, 4, False, True, err)
51+
self.assertTrue(err.Success())
52+
self.assertTrue(wp.IsEnabled())
53+
self.assertEqual(wp.GetWatchSize(), 4)
54+
self.assertGreater(wp.GetWatchAddress() % 8, 4, "watched region spans two doublewords")
55+
56+
# We will hit our watchpoint 6 times during the execution
57+
# of the inferior. If the remote stub does not actually split
58+
# the watched region into two doubleword watchpoints, we will
59+
# exit before we get to 6 watchpoint hits.
60+
for i in range(1, 7):
61+
self.hit_watchpoint_and_continue(process, "wp hit number %s" % i)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#include <stdint.h>
2+
#include <stdio.h>
3+
int main() {
4+
union {
5+
uint8_t bytebuf[16];
6+
uint16_t shortbuf[8];
7+
uint64_t dwordbuf[2];
8+
} a;
9+
a.dwordbuf[0] = a.dwordbuf[1] = 0;
10+
a.bytebuf[0] = 0; // break here
11+
for (int i = 0; i < 8; i++) {
12+
a.shortbuf[i] += i;
13+
}
14+
for (int i = 0; i < 8; i++) {
15+
a.shortbuf[i] += i;
16+
}
17+
}

lldb/test/API/python_api/watchpoint/watchlocation/TestTargetWatchAddress.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -129,10 +129,15 @@ def test_watch_address_with_invalid_watch_size(self):
129129
"pointee",
130130
value.GetValueAsUnsigned(0),
131131
value.GetType().GetPointeeType())
132-
# Watch for write to *g_char_ptr.
133-
error = lldb.SBError()
134-
watchpoint = target.WatchAddress(
135-
value.GetValueAsUnsigned(), 365, False, True, error)
136-
self.assertFalse(watchpoint)
137-
self.expect(error.GetCString(), exe=False,
138-
substrs=['watch size of %d is not supported' % 365])
132+
133+
# debugserver on Darwin AArch64 systems can watch large regions
134+
# of memory via https://reviews.llvm.org/D149792 , don't run this
135+
# test there.
136+
if self.getArchitecture() not in ['arm64', 'arm64e', 'arm64_32']:
137+
# Watch for write to *g_char_ptr.
138+
error = lldb.SBError()
139+
watchpoint = target.WatchAddress(
140+
value.GetValueAsUnsigned(), 365, False, True, error)
141+
self.assertFalse(watchpoint)
142+
self.expect(error.GetCString(), exe=False,
143+
substrs=['watch size of %d is not supported' % 365])

0 commit comments

Comments
 (0)