Skip to content

Commit e8d437f

Browse files
authored
[lldb] WatchAddress ignores modify option (#124847)
The WatchAddress API includes a flag to indicate if watchpoint should be for read, modify or both. This API uses 2 booleans, but the 'modify' flag was ignored and WatchAddress unconditionally watched write (actually modify). We now only watch for modify when the modify flag is true. --- The included test fails prior to this patch and succeeds after. That is previously specifying `False` for `modify` would still stop on _write_, but after the patch correctly only stops on _read_
1 parent 52b5e36 commit e8d437f

File tree

3 files changed

+201
-2
lines changed

3 files changed

+201
-2
lines changed

lldb/source/API/SBTarget.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1342,7 +1342,8 @@ lldb::SBWatchpoint SBTarget::WatchAddress(lldb::addr_t addr, size_t size,
13421342

13431343
SBWatchpointOptions options;
13441344
options.SetWatchpointTypeRead(read);
1345-
options.SetWatchpointTypeWrite(eWatchpointWriteTypeOnModify);
1345+
if (modify)
1346+
options.SetWatchpointTypeWrite(eWatchpointWriteTypeOnModify);
13461347
return WatchpointCreateByAddress(addr, size, options, error);
13471348
}
13481349

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
"""
2+
Use lldb Python SBTarget API to set read watchpoints
3+
"""
4+
5+
import lldb
6+
from lldbsuite.test.decorators import *
7+
from lldbsuite.test.lldbtest import *
8+
from lldbsuite.test import lldbutil
9+
10+
11+
class SetReadOnlyWatchpointTestCase(TestBase):
12+
NO_DEBUG_INFO_TESTCASE = True
13+
14+
def setUp(self):
15+
# Call super's setUp().
16+
TestBase.setUp(self)
17+
# Our simple source filename.
18+
self.source = "main.c"
19+
# Find the line number to break inside main().
20+
self.line = line_number(self.source, "// Set break point at this line.")
21+
self.build()
22+
23+
# Intel hardware does not support read-only watchpoints
24+
@expectedFailureAll(archs=["i386", "x86_64"])
25+
def test_read_watchpoint_watch_address(self):
26+
exe = self.getBuildArtifact("a.out")
27+
28+
target = self.dbg.CreateTarget(exe)
29+
self.assertTrue(target, VALID_TARGET)
30+
31+
# Now create a breakpoint on main.c.
32+
breakpoint = target.BreakpointCreateByLocation(self.source, self.line)
33+
self.assertTrue(
34+
breakpoint and breakpoint.GetNumLocations() == 1, VALID_BREAKPOINT
35+
)
36+
37+
# Now launch the process, and do not stop at the entry point.
38+
process = target.LaunchSimple(None, None, self.get_process_working_directory())
39+
40+
# We should be stopped due to the breakpoint. Get frame #0.
41+
process = target.GetProcess()
42+
self.assertState(process.GetState(), lldb.eStateStopped, PROCESS_STOPPED)
43+
thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint)
44+
frame0 = thread.GetFrameAtIndex(0)
45+
46+
value = frame0.FindValue("global", lldb.eValueTypeVariableGlobal)
47+
local = frame0.FindValue("local", lldb.eValueTypeVariableLocal)
48+
error = lldb.SBError()
49+
50+
watchpoint = target.WatchAddress(value.GetLoadAddress(), 1, True, False, error)
51+
self.assertTrue(
52+
value and local and watchpoint,
53+
"Successfully found the values and set a watchpoint",
54+
)
55+
self.DebugSBValue(value)
56+
self.DebugSBValue(local)
57+
58+
# Hide stdout if not running with '-t' option.
59+
if not self.TraceOn():
60+
self.HideStdout()
61+
62+
print(watchpoint)
63+
64+
# Continue. Expect the program to stop due to the variable being
65+
# read, but *not* written to.
66+
process.Continue()
67+
68+
if self.TraceOn():
69+
lldbutil.print_stacktraces(process)
70+
71+
self.assertTrue(
72+
local.GetValueAsSigned() > 0, "The local variable has been incremented"
73+
)
74+
75+
# Intel hardware does not support read-only watchpoints
76+
@expectedFailureAll(archs=["i386", "x86_64"])
77+
def test_read_watchpoint_watch_create_by_address(self):
78+
exe = self.getBuildArtifact("a.out")
79+
80+
target = self.dbg.CreateTarget(exe)
81+
self.assertTrue(target, VALID_TARGET)
82+
83+
# Now create a breakpoint on main.c.
84+
breakpoint = target.BreakpointCreateByLocation(self.source, self.line)
85+
self.assertTrue(
86+
breakpoint and breakpoint.GetNumLocations() == 1, VALID_BREAKPOINT
87+
)
88+
89+
# Now launch the process, and do not stop at the entry point.
90+
process = target.LaunchSimple(None, None, self.get_process_working_directory())
91+
92+
# We should be stopped due to the breakpoint. Get frame #0.
93+
process = target.GetProcess()
94+
self.assertState(process.GetState(), lldb.eStateStopped, PROCESS_STOPPED)
95+
thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint)
96+
frame0 = thread.GetFrameAtIndex(0)
97+
98+
value = frame0.FindValue("global", lldb.eValueTypeVariableGlobal)
99+
local = frame0.FindValue("local", lldb.eValueTypeVariableLocal)
100+
error = lldb.SBError()
101+
102+
wp_opts = lldb.SBWatchpointOptions()
103+
wp_opts.SetWatchpointTypeRead(True)
104+
watchpoint = target.WatchpointCreateByAddress(
105+
value.GetLoadAddress(), 1, wp_opts, error
106+
)
107+
self.assertTrue(
108+
value and local and watchpoint,
109+
"Successfully found the values and set a watchpoint",
110+
)
111+
self.DebugSBValue(value)
112+
self.DebugSBValue(local)
113+
114+
# Hide stdout if not running with '-t' option.
115+
if not self.TraceOn():
116+
self.HideStdout()
117+
118+
print(watchpoint)
119+
120+
# Continue. Expect the program to stop due to the variable being
121+
# read, but *not* written to.
122+
process.Continue()
123+
124+
if self.TraceOn():
125+
lldbutil.print_stacktraces(process)
126+
127+
self.assertTrue(
128+
local.GetValueAsSigned() > 0, "The local variable has been incremented"
129+
)

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

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ def setUp(self):
2121
# This is for verifying that watch location works.
2222
self.violating_func = "do_bad_thing_with_location"
2323

24-
def test_watch_address(self):
24+
def test_watch_create_by_address(self):
2525
"""Exercise SBTarget.WatchpointCreateByAddress() API to set a watchpoint."""
2626
self.build()
2727
exe = self.getBuildArtifact("a.out")
@@ -88,6 +88,75 @@ def test_watch_address(self):
8888

8989
# This finishes our test.
9090

91+
def test_watch_address(self):
92+
"""Exercise SBTarget.WatchAddress() API to set a watchpoint.
93+
Same as test_watch_create_by_address, but uses the simpler API.
94+
"""
95+
self.build()
96+
exe = self.getBuildArtifact("a.out")
97+
98+
# Create a target by the debugger.
99+
target = self.dbg.CreateTarget(exe)
100+
self.assertTrue(target, VALID_TARGET)
101+
102+
# Now create a breakpoint on main.c.
103+
breakpoint = target.BreakpointCreateByLocation(self.source, self.line)
104+
self.assertTrue(
105+
breakpoint and breakpoint.GetNumLocations() == 1, VALID_BREAKPOINT
106+
)
107+
108+
# Now launch the process, and do not stop at the entry point.
109+
process = target.LaunchSimple(None, None, self.get_process_working_directory())
110+
111+
# We should be stopped due to the breakpoint. Get frame #0.
112+
process = target.GetProcess()
113+
self.assertState(process.GetState(), lldb.eStateStopped, PROCESS_STOPPED)
114+
thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint)
115+
frame0 = thread.GetFrameAtIndex(0)
116+
117+
value = frame0.FindValue("g_char_ptr", lldb.eValueTypeVariableGlobal)
118+
pointee = value.CreateValueFromAddress(
119+
"pointee", value.GetValueAsUnsigned(0), value.GetType().GetPointeeType()
120+
)
121+
# Watch for write to *g_char_ptr.
122+
error = lldb.SBError()
123+
watch_read = False
124+
watch_write = True
125+
watchpoint = target.WatchAddress(
126+
value.GetValueAsUnsigned(), 1, watch_read, watch_write, error
127+
)
128+
self.assertTrue(
129+
value and watchpoint, "Successfully found the pointer and set a watchpoint"
130+
)
131+
self.DebugSBValue(value)
132+
self.DebugSBValue(pointee)
133+
134+
# Hide stdout if not running with '-t' option.
135+
if not self.TraceOn():
136+
self.HideStdout()
137+
138+
print(watchpoint)
139+
140+
# Continue. Expect the program to stop due to the variable being
141+
# written to.
142+
process.Continue()
143+
144+
if self.TraceOn():
145+
lldbutil.print_stacktraces(process)
146+
147+
thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonWatchpoint)
148+
self.assertTrue(thread, "The thread stopped due to watchpoint")
149+
self.DebugSBValue(value)
150+
self.DebugSBValue(pointee)
151+
152+
self.expect(
153+
lldbutil.print_stacktrace(thread, string_buffer=True),
154+
exe=False,
155+
substrs=[self.violating_func],
156+
)
157+
158+
# This finishes our test.
159+
91160
# No size constraint on MIPS for watches
92161
@skipIf(archs=["mips", "mipsel", "mips64", "mips64el"])
93162
@skipIf(archs=["s390x"]) # Likewise on SystemZ

0 commit comments

Comments
 (0)