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