Skip to content

Commit 7664494

Browse files
committed
Fix a crash when a stop hook deletes itself in its callback.
1 parent 89d2b7e commit 7664494

File tree

3 files changed

+64
-1
lines changed

3 files changed

+64
-1
lines changed

lldb/source/Target/Target.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3171,7 +3171,13 @@ bool Target::RunStopHooks(bool at_initial_stop) {
31713171
bool should_stop = false;
31723172
bool requested_continue = false;
31733173

3174-
for (auto stop_entry : m_stop_hooks) {
3174+
// A stop hook might get deleted while running stop hooks.
3175+
// We have to decide what that means. We will follow the rule that deleting
3176+
// a stop hook while processing these stop hooks will delete it for FUTURE
3177+
// stops but not this stop. The easiest way to do that is to copy the
3178+
// stop hooks and iterate over the copy.
3179+
StopHookCollection stop_hooks_copy = m_stop_hooks;
3180+
for (auto stop_entry : stop_hooks_copy) {
31753181
StopHookSP cur_hook_sp = stop_entry.second;
31763182
if (!cur_hook_sp->IsActive())
31773183
continue;

lldb/test/API/commands/target/stop-hooks/TestStopHookScripted.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,39 @@ def test_bad_handler(self):
4848
"Got the right error",
4949
)
5050

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 hook
55+
# so we don't have to figure out where it showed up in the new
56+
# target.
57+
(target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
58+
self, "Stop here first", self.main_source_file
59+
)
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+
5184
def test_stop_hooks_scripted(self):
5285
"""Test that a scripted stop hook works with no specifiers"""
5386
self.stop_hooks_scripted(5, "-I false")

lldb/test/API/commands/target/stop-hooks/stop_hook.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,27 @@ def handle_stop(self):
4848
class no_handle_stop:
4949
def __init__(self, target, extra_args, dict):
5050
print("I am okay")
51+
52+
class self_deleting_stop:
53+
def __init__(self, target, extra_args, dict):
54+
self.target = target
55+
56+
def handle_stop(self, exe_ctx, stream):
57+
interp = exe_ctx.target.debugger.GetCommandInterpreter()
58+
result = lldb.SBCommandReturnObject()
59+
interp.HandleCommand("handle_id", result)
60+
id_str = result.GetOutput().rstrip()
61+
62+
command = f"target stop-hook delete {id_str}"
63+
interp.HandleCommand(command, result)
64+
65+
stop_hook_id = 0
66+
def handle_stop_hook_id(debugger, command, exe_ctx, result, extra_args):
67+
global stop_hook_id
68+
if command == "":
69+
result.AppendMessage(str(stop_hook_id))
70+
else:
71+
stop_hook_id = int(command)
72+
73+
74+

0 commit comments

Comments
 (0)