Skip to content

[lldb] WatchAddress ignores modify option #124847

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion lldb/source/API/SBTarget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1342,7 +1342,8 @@ lldb::SBWatchpoint SBTarget::WatchAddress(lldb::addr_t addr, size_t size,

SBWatchpointOptions options;
options.SetWatchpointTypeRead(read);
options.SetWatchpointTypeWrite(eWatchpointWriteTypeOnModify);
if (modify)
options.SetWatchpointTypeWrite(eWatchpointWriteTypeOnModify);
return WatchpointCreateByAddress(addr, size, options, error);
}

Expand Down
129 changes: 129 additions & 0 deletions lldb/test/API/python_api/watchpoint/TestWatchpointRead.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
"""
Use lldb Python SBTarget API to set read watchpoints
"""

import lldb
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil


class SetReadOnlyWatchpointTestCase(TestBase):
NO_DEBUG_INFO_TESTCASE = True

def setUp(self):
# Call super's setUp().
TestBase.setUp(self)
# Our simple source filename.
self.source = "main.c"
# Find the line number to break inside main().
self.line = line_number(self.source, "// Set break point at this line.")
self.build()

# Intel hardware does not support read-only watchpoints
@expectedFailureAll(archs=["i386", "x86_64"])
def test_read_watchpoint_watch_address(self):
exe = self.getBuildArtifact("a.out")

target = self.dbg.CreateTarget(exe)
self.assertTrue(target, VALID_TARGET)

# Now create a breakpoint on main.c.
breakpoint = target.BreakpointCreateByLocation(self.source, self.line)
self.assertTrue(
breakpoint and breakpoint.GetNumLocations() == 1, VALID_BREAKPOINT
)

# Now launch the process, and do not stop at the entry point.
process = target.LaunchSimple(None, None, self.get_process_working_directory())

# We should be stopped due to the breakpoint. Get frame #0.
process = target.GetProcess()
self.assertState(process.GetState(), lldb.eStateStopped, PROCESS_STOPPED)
thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint)
frame0 = thread.GetFrameAtIndex(0)

value = frame0.FindValue("global", lldb.eValueTypeVariableGlobal)
local = frame0.FindValue("local", lldb.eValueTypeVariableLocal)
error = lldb.SBError()

watchpoint = target.WatchAddress(value.GetLoadAddress(), 1, True, False, error)
self.assertTrue(
value and local and watchpoint,
"Successfully found the values and set a watchpoint",
)
self.DebugSBValue(value)
self.DebugSBValue(local)

# Hide stdout if not running with '-t' option.
if not self.TraceOn():
self.HideStdout()

print(watchpoint)

# Continue. Expect the program to stop due to the variable being
# read, but *not* written to.
process.Continue()

if self.TraceOn():
lldbutil.print_stacktraces(process)

self.assertTrue(
local.GetValueAsSigned() > 0, "The local variable has been incremented"
)

# Intel hardware does not support read-only watchpoints
@expectedFailureAll(archs=["i386", "x86_64"])
def test_read_watchpoint_watch_create_by_address(self):
exe = self.getBuildArtifact("a.out")

target = self.dbg.CreateTarget(exe)
self.assertTrue(target, VALID_TARGET)

# Now create a breakpoint on main.c.
breakpoint = target.BreakpointCreateByLocation(self.source, self.line)
self.assertTrue(
breakpoint and breakpoint.GetNumLocations() == 1, VALID_BREAKPOINT
)

# Now launch the process, and do not stop at the entry point.
process = target.LaunchSimple(None, None, self.get_process_working_directory())

# We should be stopped due to the breakpoint. Get frame #0.
process = target.GetProcess()
self.assertState(process.GetState(), lldb.eStateStopped, PROCESS_STOPPED)
thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint)
frame0 = thread.GetFrameAtIndex(0)

value = frame0.FindValue("global", lldb.eValueTypeVariableGlobal)
local = frame0.FindValue("local", lldb.eValueTypeVariableLocal)
error = lldb.SBError()

wp_opts = lldb.SBWatchpointOptions()
wp_opts.SetWatchpointTypeRead(True)
watchpoint = target.WatchpointCreateByAddress(
value.GetLoadAddress(), 1, wp_opts, error
)
self.assertTrue(
value and local and watchpoint,
"Successfully found the values and set a watchpoint",
)
self.DebugSBValue(value)
self.DebugSBValue(local)

# Hide stdout if not running with '-t' option.
if not self.TraceOn():
self.HideStdout()

print(watchpoint)

# Continue. Expect the program to stop due to the variable being
# read, but *not* written to.
process.Continue()

if self.TraceOn():
lldbutil.print_stacktraces(process)

self.assertTrue(
local.GetValueAsSigned() > 0, "The local variable has been incremented"
)
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def setUp(self):
# This is for verifying that watch location works.
self.violating_func = "do_bad_thing_with_location"

def test_watch_address(self):
def test_watch_create_by_address(self):
"""Exercise SBTarget.WatchpointCreateByAddress() API to set a watchpoint."""
self.build()
exe = self.getBuildArtifact("a.out")
Expand Down Expand Up @@ -88,6 +88,75 @@ def test_watch_address(self):

# This finishes our test.

def test_watch_address(self):
"""Exercise SBTarget.WatchAddress() API to set a watchpoint.
Same as test_watch_create_by_address, but uses the simpler API.
"""
self.build()
exe = self.getBuildArtifact("a.out")

# Create a target by the debugger.
target = self.dbg.CreateTarget(exe)
self.assertTrue(target, VALID_TARGET)

# Now create a breakpoint on main.c.
breakpoint = target.BreakpointCreateByLocation(self.source, self.line)
self.assertTrue(
breakpoint and breakpoint.GetNumLocations() == 1, VALID_BREAKPOINT
)

# Now launch the process, and do not stop at the entry point.
process = target.LaunchSimple(None, None, self.get_process_working_directory())

# We should be stopped due to the breakpoint. Get frame #0.
process = target.GetProcess()
self.assertState(process.GetState(), lldb.eStateStopped, PROCESS_STOPPED)
thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint)
frame0 = thread.GetFrameAtIndex(0)

value = frame0.FindValue("g_char_ptr", lldb.eValueTypeVariableGlobal)
pointee = value.CreateValueFromAddress(
"pointee", value.GetValueAsUnsigned(0), value.GetType().GetPointeeType()
)
# Watch for write to *g_char_ptr.
error = lldb.SBError()
watch_read = False
watch_write = True
watchpoint = target.WatchAddress(
value.GetValueAsUnsigned(), 1, watch_read, watch_write, error
)
self.assertTrue(
value and watchpoint, "Successfully found the pointer and set a watchpoint"
)
self.DebugSBValue(value)
self.DebugSBValue(pointee)

# Hide stdout if not running with '-t' option.
if not self.TraceOn():
self.HideStdout()

print(watchpoint)

# Continue. Expect the program to stop due to the variable being
# written to.
process.Continue()

if self.TraceOn():
lldbutil.print_stacktraces(process)

thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonWatchpoint)
self.assertTrue(thread, "The thread stopped due to watchpoint")
self.DebugSBValue(value)
self.DebugSBValue(pointee)

self.expect(
lldbutil.print_stacktrace(thread, string_buffer=True),
exe=False,
substrs=[self.violating_func],
)

# This finishes our test.

# No size constraint on MIPS for watches
@skipIf(archs=["mips", "mipsel", "mips64", "mips64el"])
@skipIf(archs=["s390x"]) # Likewise on SystemZ
Expand Down