155 lines · python
1import lldb2import time3import unittest4from lldbsuite.test.lldbtest import *5from lldbsuite.test.decorators import *6from lldbsuite.test.gdbclientutils import *7from lldbsuite.test.lldbreverse import ReverseTestBase8from lldbsuite.test import lldbutil9 10 11class TestReverseContinueWatchpoints(ReverseTestBase):12 @skipIfRemote13 # Watchpoints don't work in single-step mode14 @skipIf(macos_version=["<", "15.0"], archs=["arm64"])15 def test_reverse_continue_watchpoint(self):16 self.reverse_continue_watchpoint_internal(async_mode=False)17 18 @skipIfRemote19 # Watchpoints don't work in single-step mode20 @skipIf(macos_version=["<", "15.0"], archs=["arm64"])21 def test_reverse_continue_watchpoint_async(self):22 self.reverse_continue_watchpoint_internal(async_mode=True)23 24 def reverse_continue_watchpoint_internal(self, async_mode):25 target, process, initial_threads, watch_addr = self.setup_recording(async_mode)26 27 error = lldb.SBError()28 wp_opts = lldb.SBWatchpointOptions()29 wp_opts.SetWatchpointTypeWrite(lldb.eWatchpointWriteTypeOnModify)30 watchpoint = target.WatchpointCreateByAddress(watch_addr, 4, wp_opts, error)31 self.assertTrue(watchpoint)32 33 watch_var = target.EvaluateExpression("*g_watched_var_ptr")34 self.assertEqual(watch_var.GetValueAsSigned(-1), 2)35 36 # Reverse-continue to the function "trigger_watchpoint".37 status = process.ContinueInDirection(lldb.eRunReverse)38 self.assertSuccess(status)39 self.expect_async_state_changes(40 async_mode, process, [lldb.eStateRunning, lldb.eStateStopped]41 )42 # We should stop at the point just before the location was modified.43 watch_var = target.EvaluateExpression("*g_watched_var_ptr")44 self.assertEqual(watch_var.GetValueAsSigned(-1), 1)45 self.expect(46 "thread list",47 STOPPED_DUE_TO_WATCHPOINT,48 substrs=["stopped", "trigger_watchpoint", "stop reason = watchpoint 1"],49 )50 51 # Stepping forward one instruction should change the value of the variable.52 initial_threads[0].StepInstruction(False)53 self.expect_async_state_changes(54 async_mode, process, [lldb.eStateRunning, lldb.eStateStopped]55 )56 watch_var = target.EvaluateExpression("*g_watched_var_ptr")57 self.assertEqual(watch_var.GetValueAsSigned(-1), 2)58 self.expect(59 "thread list",60 STOPPED_DUE_TO_WATCHPOINT,61 substrs=["stopped", "trigger_watchpoint", "stop reason = watchpoint 1"],62 )63 64 @skipIfRemote65 # Watchpoints don't work in single-step mode66 @skipIf(macos_version=["<", "15.0"], archs=["arm64"])67 @skipIf(68 oslist=["windows"],69 archs=["x86_64"],70 bugnumber="github.com/llvm/llvm-project/issues/138084",71 )72 def test_reverse_continue_skip_watchpoint(self):73 self.reverse_continue_skip_watchpoint_internal(async_mode=False)74 75 @skipIfRemote76 # Watchpoints don't work in single-step mode77 @skipIf(macos_version=["<", "15.0"], archs=["arm64"])78 @skipIf(79 oslist=["windows"],80 archs=["x86_64"],81 bugnumber="github.com/llvm/llvm-project/issues/138084",82 )83 def test_reverse_continue_skip_watchpoint_async(self):84 self.reverse_continue_skip_watchpoint_internal(async_mode=True)85 86 def reverse_continue_skip_watchpoint_internal(self, async_mode):87 target, process, initial_threads, watch_addr = self.setup_recording(async_mode)88 89 # Reverse-continue over a watchpoint whose condition is false90 # (via function call).91 # This tests that we continue in the correct direction after hitting92 # the watchpoint.93 error = lldb.SBError()94 wp_opts = lldb.SBWatchpointOptions()95 wp_opts.SetWatchpointTypeWrite(lldb.eWatchpointWriteTypeOnModify)96 watchpoint = target.WatchpointCreateByAddress(watch_addr, 4, wp_opts, error)97 self.assertTrue(watchpoint)98 watchpoint.SetCondition("false_condition()")99 status = process.ContinueInDirection(lldb.eRunReverse)100 self.expect_async_state_changes(101 async_mode, process, [lldb.eStateRunning, lldb.eStateStopped]102 )103 self.assertSuccess(status)104 self.expect(105 "thread list",106 STOPPED_DUE_TO_HISTORY_BOUNDARY,107 substrs=["stopped", "stop reason = history boundary"],108 )109 110 def setup_recording(self, async_mode):111 """112 Record execution of code between "start_recording" and "stop_recording" breakpoints.113 114 Returns with the target stopped at "stop_recording", with recording disabled,115 ready to reverse-execute.116 """117 self.build()118 target = self.dbg.CreateTarget(self.getBuildArtifact("a.out"))119 process = self.connect(target)120 121 # Record execution from the start of the function "start_recording"122 # to the start of the function "stop_recording". We want to keep the123 # interval that we record as small as possible to minimize the run-time124 # of our single-stepping recorder.125 start_recording_bkpt = target.BreakpointCreateByName("start_recording", None)126 self.assertTrue(start_recording_bkpt.GetNumLocations() > 0)127 initial_threads = lldbutil.continue_to_breakpoint(process, start_recording_bkpt)128 self.assertEqual(len(initial_threads), 1)129 target.BreakpointDelete(start_recording_bkpt.GetID())130 131 frame0 = initial_threads[0].GetFrameAtIndex(0)132 watched_var_ptr = frame0.FindValue(133 "g_watched_var_ptr", lldb.eValueTypeVariableGlobal134 )135 watch_addr = watched_var_ptr.GetValueAsUnsigned(0)136 self.assertTrue(watch_addr > 0)137 138 self.start_recording()139 stop_recording_bkpt = target.BreakpointCreateByName("stop_recording", None)140 self.assertTrue(stop_recording_bkpt.GetNumLocations() > 0)141 lldbutil.continue_to_breakpoint(process, stop_recording_bkpt)142 target.BreakpointDelete(stop_recording_bkpt.GetID())143 self.stop_recording()144 145 self.dbg.SetAsync(async_mode)146 self.expect_async_state_changes(async_mode, process, [lldb.eStateStopped])147 148 return target, process, initial_threads, watch_addr149 150 def expect_async_state_changes(self, async_mode, process, states):151 if not async_mode:152 return153 listener = self.dbg.GetListener()154 lldbutil.expect_state_changes(self, listener, process, states)155