1"""
2Test stop hook functionality
3"""
4
5
6
7import lldb
8import lldbsuite.test.lldbutil as lldbutil
9from lldbsuite.test.lldbtest import *
10from lldbsuite.test.decorators import *
11
12class TestStopHooks(TestBase):
13
14    mydir = TestBase.compute_mydir(__file__)
15
16    # If your test case doesn't stress debug info, the
17    # set this to true.  That way it won't be run once for
18    # each debug info format.
19    NO_DEBUG_INFO_TESTCASE = True
20
21    def setUp(self):
22        TestBase.setUp(self)
23        self.build()
24        self.main_source_file = lldb.SBFileSpec("main.c")
25        full_path = os.path.join(self.getSourceDir(), "main.c")
26        self.main_start_line = line_number(full_path, "main()")
27
28    def test_bad_handler(self):
29        """Test that we give a good error message when the handler is bad"""
30        self.script_setup()
31        result = lldb.SBCommandReturnObject()
32
33        # First try the wrong number of args handler:
34        command = "target stop-hook add -P stop_hook.bad_handle_stop"
35        self.interp.HandleCommand(command, result)
36        self.assertFalse(result.Succeeded(), "Set the target stop hook")
37        self.assertIn("Wrong number of args", result.GetError(), "Got the wrong number of args error")
38
39        # Next the no handler at all handler:
40        command = "target stop-hook add -P stop_hook.no_handle_stop"
41
42        self.interp.HandleCommand(command, result)
43        self.assertFalse(result.Succeeded(), "Set the target stop hook")
44        self.assertIn('Class "stop_hook.no_handle_stop" is missing the required handle_stop callback', result.GetError(), "Got the right error")
45
46    def test_stop_hooks_scripted(self):
47        """Test that a scripted stop hook works with no specifiers"""
48        self.stop_hooks_scripted(5)
49
50    def test_stop_hooks_scripted_right_func(self):
51        """Test that a scripted stop hook fires when there is a function match"""
52        self.stop_hooks_scripted(5, "-n step_out_of_me")
53
54    def test_stop_hooks_scripted_wrong_func(self):
55        """Test that a scripted stop hook doesn't fire when the function does not match"""
56        self.stop_hooks_scripted(0, "-n main")
57
58    def test_stop_hooks_scripted_right_lines(self):
59        """Test that a scripted stop hook fires when there is a function match"""
60        self.stop_hooks_scripted(5, "-f main.c -l 1 -e %d"%(self.main_start_line))
61
62    def test_stop_hooks_scripted_wrong_lines(self):
63        """Test that a scripted stop hook doesn't fire when the function does not match"""
64        self.stop_hooks_scripted(0, "-f main.c -l %d -e 100"%(self.main_start_line))
65
66    def test_stop_hooks_scripted_auto_continue(self):
67        """Test that the --auto-continue flag works"""
68        self.do_test_auto_continue(False)
69
70    def test_stop_hooks_scripted_return_false(self):
71        """Test that the returning False from a stop hook works"""
72        self.do_test_auto_continue(True)
73
74    # Test is flakey on Linux.
75    @skipIfLinux
76    def do_test_auto_continue(self, return_true):
77        """Test that auto-continue works."""
78        # We set auto-continue to 1 but the stop hook only applies to step_out_of_me,
79        # so we should end up stopped in main, having run the expression only once.
80        self.script_setup()
81
82        result = lldb.SBCommandReturnObject()
83
84        if return_true:
85          command = "target stop-hook add -P stop_hook.stop_handler -k increment -v 5 -k return_false -v 1 -n step_out_of_me"
86        else:
87          command = "target stop-hook add -G 1 -P stop_hook.stop_handler -k increment -v 5 -n step_out_of_me"
88
89        self.interp.HandleCommand(command, result)
90        self.assertTrue(result.Succeeded, "Set the target stop hook")
91
92        # First run to main.  If we go straight to the first stop hook hit,
93        # run_to_source_breakpoint will fail because we aren't at original breakpoint
94
95        (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(self,
96                                   "Stop here first", self.main_source_file)
97
98        # Now set the breakpoint on step_out_of_me, and make sure we run the
99        # expression, then continue back to main.
100        bkpt = target.BreakpointCreateBySourceRegex("Set a breakpoint here and step out", self.main_source_file)
101        self.assertTrue(bkpt.GetNumLocations() > 0, "Got breakpoints in step_out_of_me")
102        process.Continue()
103
104        var = target.FindFirstGlobalVariable("g_var")
105        self.assertTrue(var.IsValid())
106        self.assertEqual(var.GetValueAsUnsigned(), 5, "Updated g_var")
107
108        func_name = process.GetSelectedThread().frames[0].GetFunctionName()
109        self.assertEqual("main", func_name, "Didn't stop at the expected function.")
110
111    def script_setup(self):
112        self.interp = self.dbg.GetCommandInterpreter()
113        result = lldb.SBCommandReturnObject()
114
115        # Bring in our script file:
116        script_name = os.path.join(self.getSourceDir(), "stop_hook.py")
117        command = "command script import " + script_name
118        self.interp.HandleCommand(command, result)
119        self.assertTrue(result.Succeeded(), "com scr imp failed: %s"%(result.GetError()))
120
121        # set a breakpoint at the end of main to catch our auto-continue tests.
122        # Do it in the dummy target so it will get copied to our target even when
123        # we don't have a chance to stop.
124        dummy_target = self.dbg.GetDummyTarget()
125        dummy_target.BreakpointCreateBySourceRegex("return result", self.main_source_file)
126
127
128    def stop_hooks_scripted(self, g_var_value, specifier = None):
129        self.script_setup()
130
131        result = lldb.SBCommandReturnObject()
132
133        command = "target stop-hook add -P stop_hook.stop_handler -k increment -v 5 "
134        if specifier:
135            command += specifier
136
137        self.interp.HandleCommand(command, result)
138        self.assertTrue(result.Succeeded, "Set the target stop hook")
139        (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(self,
140                                   "Set a breakpoint here", self.main_source_file)
141        # At this point we've hit our stop hook so we should have run our expression,
142        # which increments g_var by the amount specified by the increment key's value.
143        while process.GetState() == lldb.eStateRunning:
144            continue
145
146        var = target.FindFirstGlobalVariable("g_var")
147        self.assertTrue(var.IsValid())
148        self.assertEqual(var.GetValueAsUnsigned(), g_var_value, "Updated g_var")
149