blob: 0fdacd8c91215ab0c3553ef3102c8e0c32dc2eee [file]
"""
Use lldb Python SBValue API to create a watchpoint for read_write of 'globl' var.
"""
import lldb
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
class SetWatchpointAPITestCase(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()
# Read-write watchpoints not supported on SystemZ
@expectedFailureAll(archs=["s390x"])
def test_watch_val(self):
"""Test watchpoint on a SBValue not backed by a variable yields an expression watchpoint."""
self._test_watch_val(variable_watchpoint=False)
@expectedFailureAll(archs=["s390x"])
def test_watch_variable(self):
"""Test watchpoint on an SBValue backed by a variable yields a variable watchpoint."""
self._test_watch_val(variable_watchpoint=True)
@expectedFailureAll(archs=["s390x"])
def test_local_variable_watchpoint_scoped_to_frame(self):
"""Test watchpoint on a frame local variable only triggers when in the frame scope."""
target, process, thread, _ = lldbutil.run_to_source_breakpoint(
self, "// local_value_breakpoint", lldb.SBFileSpec(self.source, False)
)
frame = thread.selected_frame
value: lldb.SBValue = frame.FindVariable("local_value")
self.assertTrue(value.IsValid(), "Found stack-local 'local_value'")
error = lldb.SBError()
watchpoint = value.Watch(True, True, True, error)
self.assertSuccess(error)
self.assertTrue(watchpoint.IsValid(), "Set watchpoint on 'local_value'")
self.assertEqual(
watchpoint.GetWatchValueKind(), lldb.eWatchPointValueKindVariable
)
# Continue to the read watchpoint.
error = process.Continue()
self.assertSuccess(error)
self.assertState(process.state, lldb.eStateStopped)
thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonWatchpoint)
self.assertTrue(thread, "stopped at watchpoint read")
# Verify the process does not stop again after continuing.
error = process.Continue()
self.assertTrue(error)
self.assertState(process.state, lldb.eStateExited)
def _test_watch_val(self, variable_watchpoint):
target, process, thread, _ = lldbutil.run_to_line_breakpoint(
self, lldb.SBFileSpec(self.source), self.line
)
frame0 = thread.GetFrameAtIndex(0)
# Watch 'global' for read and write.
if variable_watchpoint:
# Variable watchpoint.
# FindValue returns a variable-backed SBValue,
value = frame0.FindValue("global", lldb.eValueTypeVariableGlobal)
error = lldb.SBError()
watchpoint = value.Watch(True, True, True, error)
self.assertSuccess(error)
self.assertTrue(value, VALID_VARIABLE)
self.assertTrue(watchpoint, f"expected a watchpoint from {value=}")
self.DebugSBValue(value)
self.assertEqual(
watchpoint.GetWatchValueKind(), lldb.eWatchPointValueKindVariable
)
self.assertEqual(watchpoint.GetWatchSpec(), value.GetName())
else:
# Expression watchpoint.
# Creates a new SBvalue from an existing SBValue Variable's address and type.
# This new value its not backed by an actual variable in process memory.
var_value = frame0.FindValue("global", lldb.eValueTypeVariableGlobal)
sb_addr = lldb.SBAddress(var_value.GetLoadAddress(), target)
value = target.CreateValueFromAddress("global", sb_addr, var_value.type)
error = lldb.SBError()
watchpoint = value.Watch(True, True, True, error)
self.assertSuccess(error)
self.assertTrue(value, VALID_VARIABLE)
self.assertTrue(watchpoint, f"expected a watchpoint from {value=}")
self.DebugSBValue(value)
self.assertEqual(
watchpoint.GetWatchValueKind(), lldb.eWatchPointValueKindExpression
)
# FIXME: The spec should probably be '&global' given that the kind
# is reported as eWatchPointValueKindExpression. If the kind is
# actually supposed to be eWatchPointValueKindVariable then the spec
# should probably be 'global'.
self.assertEqual(watchpoint.GetWatchSpec(), None)
self.assertEqual(watchpoint.GetType().GetDisplayTypeName(), "int32_t")
self.assertEqual(value.GetName(), "global")
self.assertEqual(value.GetType(), watchpoint.GetType())
self.assertTrue(watchpoint.IsWatchingReads())
self.assertTrue(watchpoint.IsWatchingWrites())
# 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)
# Continue. Expect the program to stop due to the variable being read
# from.
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)
# Continue the process. We don't expect the program to be stopped
# again.
process.Continue()
# At this point, the inferior process should have exited.
self.assertEqual(process.GetState(), lldb.eStateExited, PROCESS_EXITED)
self.dbg.DeleteTarget(target)
self.assertFalse(watchpoint.IsValid())