202 lines · python
1"""2Test stop hook functionality3"""4 5 6import lldb7import lldbsuite.test.lldbutil as lldbutil8from lldbsuite.test.lldbtest import *9from lldbsuite.test.decorators import *10 11 12class TestStopHooks(TestBase):13 # If your test case doesn't stress debug info, then14 # set this to true. That way it won't be run once for15 # each debug info format.16 NO_DEBUG_INFO_TESTCASE = True17 18 def setUp(self):19 TestBase.setUp(self)20 self.build()21 self.main_source_file = lldb.SBFileSpec("main.c")22 full_path = os.path.join(self.getSourceDir(), "main.c")23 self.main_start_line = line_number(full_path, "main()")24 25 def test_bad_handler(self):26 """Test that we give a good error message when the handler is bad"""27 self.script_setup()28 result = lldb.SBCommandReturnObject()29 30 # First try the wrong number of args handler:31 command = "target stop-hook add -P stop_hook.bad_handle_stop"32 self.interp.HandleCommand(command, result)33 self.assertFalse(result.Succeeded(), "Set the target stop hook")34 self.assertIn(35 "has unexpected argument count",36 result.GetError(),37 "Got the wrong number of args error",38 )39 40 # Next the no handler at all handler:41 command = "target stop-hook add -P stop_hook.no_handle_stop"42 43 self.interp.HandleCommand(command, result)44 self.assertFalse(result.Succeeded(), "Set the target stop hook")45 self.assertIn(46 "Abstract method no_handle_stop.handle_stop not implemented",47 result.GetError(),48 "Got the right error",49 )50 51 def test_self_deleting(self):52 """Test that we can handle a stop hook that deletes itself"""53 self.script_setup()54 # Run to the first breakpoint before setting the stop hook55 # so we don't have to figure out where it showed up in the new56 # target.57 (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(58 self, "Stop here first", self.main_source_file59 )60 61 # Now add our stop hook and register it:62 result = lldb.SBCommandReturnObject()63 command = "target stop-hook add -P stop_hook.self_deleting_stop"64 self.interp.HandleCommand(command, result)65 self.assertCommandReturn(result, f"Added my stop hook: {result.GetError()}")66 67 result_str = result.GetOutput()68 p = re.compile("Stop hook #([0-9]+) added.")69 m = p.match(result_str)70 current_stop_hook_id = m.group(1)71 command = "command script add -o -f stop_hook.handle_stop_hook_id handle_id"72 self.interp.HandleCommand(command, result)73 self.assertCommandReturn(result, "Added my command")74 75 command = f"handle_id {current_stop_hook_id}"76 self.interp.HandleCommand(command, result)77 self.assertCommandReturn(result, "Registered my stop ID")78 79 # Now step the process and make sure the stop hook was deleted.80 thread.StepOver()81 self.interp.HandleCommand("target stop-hook list", result)82 self.assertEqual(result.GetOutput().rstrip(), "No stop hooks.", "Deleted hook")83 84 def test_stop_hooks_scripted(self):85 """Test that a scripted stop hook works with no specifiers"""86 self.stop_hooks_scripted(5, "-I false")87 88 def test_stop_hooks_scripted_no_entry(self):89 """Test that a scripted stop hook works with no specifiers"""90 self.stop_hooks_scripted(10)91 92 def test_stop_hooks_scripted_right_func(self):93 """Test that a scripted stop hook fires when there is a function match"""94 self.stop_hooks_scripted(5, "-I 0 -n step_out_of_me")95 96 def test_stop_hooks_scripted_wrong_func(self):97 """Test that a scripted stop hook doesn't fire when the function does not match"""98 self.stop_hooks_scripted(0, "-I 0 -n main")99 100 def test_stop_hooks_scripted_right_lines(self):101 """Test that a scripted stop hook fires when there is a function match"""102 self.stop_hooks_scripted(103 5, "-I 0 -f main.c -l 1 -e %d" % (self.main_start_line)104 )105 106 def test_stop_hooks_scripted_wrong_lines(self):107 """Test that a scripted stop hook doesn't fire when the function does not match"""108 self.stop_hooks_scripted(109 0, "-I 0 -f main.c -l %d -e 100" % (self.main_start_line)110 )111 112 def test_stop_hooks_scripted_auto_continue(self):113 """Test that the --auto-continue flag works"""114 self.do_test_auto_continue(False)115 116 def test_stop_hooks_scripted_return_false(self):117 """Test that the returning False from a stop hook works"""118 self.do_test_auto_continue(True)119 120 def do_test_auto_continue(self, return_true):121 """Test that auto-continue works."""122 # We set auto-continue to 1 but the stop hook only applies to step_out_of_me,123 # so we should end up stopped in main, having run the expression only once.124 self.script_setup()125 126 result = lldb.SBCommandReturnObject()127 128 if return_true:129 command = "target stop-hook add -I 0 -P stop_hook.stop_handler -k increment -v 5 -k return_false -v 1 -n step_out_of_me"130 else:131 command = "target stop-hook add -I 0 -G 1 -P stop_hook.stop_handler -k increment -v 5 -n step_out_of_me"132 133 self.interp.HandleCommand(command, result)134 self.assertTrue(result.Succeeded(), "Set the target stop hook")135 136 # First run to main. If we go straight to the first stop hook hit,137 # run_to_source_breakpoint will fail because we aren't at original breakpoint138 139 (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(140 self, "Stop here first", self.main_source_file141 )142 143 # Now set the breakpoint on step_out_of_me, and make sure we run the144 # expression, then continue back to main.145 bkpt = target.BreakpointCreateBySourceRegex(146 "Set a breakpoint here and step out", self.main_source_file147 )148 self.assertNotEqual(149 bkpt.GetNumLocations(), 0, "Got breakpoints in step_out_of_me"150 )151 process.Continue()152 153 var = target.FindFirstGlobalVariable("g_var")154 self.assertTrue(var.IsValid())155 self.assertEqual(var.GetValueAsUnsigned(), 6, "Updated g_var")156 157 func_name = process.GetSelectedThread().frames[0].GetFunctionName()158 self.assertEqual("main", func_name, "Didn't stop at the expected function.")159 160 def script_setup(self):161 self.interp = self.dbg.GetCommandInterpreter()162 result = lldb.SBCommandReturnObject()163 164 # Bring in our script file:165 script_name = os.path.join(self.getSourceDir(), "stop_hook.py")166 command = "command script import " + script_name167 self.interp.HandleCommand(command, result)168 self.assertTrue(169 result.Succeeded(), "com scr imp failed: %s" % (result.GetError())170 )171 172 # set a breakpoint at the end of main to catch our auto-continue tests.173 # Do it in the dummy target so it will get copied to our target even when174 # we don't have a chance to stop.175 dummy_target = self.dbg.GetDummyTarget()176 dummy_target.BreakpointCreateBySourceRegex(177 "return result", self.main_source_file178 )179 180 def stop_hooks_scripted(self, g_var_value, specifier=None):181 self.script_setup()182 183 result = lldb.SBCommandReturnObject()184 185 command = "target stop-hook add -P stop_hook.stop_handler -k increment -v 5 "186 if specifier:187 command += specifier188 189 self.interp.HandleCommand(command, result)190 self.assertTrue(result.Succeeded(), "Set the target stop hook")191 (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(192 self, "Set a breakpoint here", self.main_source_file193 )194 # At this point we've hit our stop hook so we should have run our expression,195 # which increments g_var by the amount specified by the increment key's value.196 while process.GetState() == lldb.eStateRunning:197 continue198 199 var = target.FindFirstGlobalVariable("g_var")200 self.assertTrue(var.IsValid())201 self.assertEqual(var.GetValueAsUnsigned(), g_var_value, "Updated g_var")202