Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions lldb/source/Commands/CommandObjectTarget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5117,6 +5117,15 @@ class CommandObjectTargetStopHookDelete : public CommandObjectParsed {
: CommandObjectParsed(interpreter, "target stop-hook delete",
"Delete a stop-hook.",
"target stop-hook delete [<idx>]") {
SetHelpLong(
R"(
Deletes the stop hook by index.

At any given stop, all enabled stop hooks that pass the stop filter will
get a chance to run. That means if one stop-hook deletes another stop hook
while executing, the deleted stop hook will still fire for the stop at which
it was deleted.
)");
AddSimpleArgumentList(eArgTypeStopHookID, eArgRepeatStar);
}

Expand Down
8 changes: 7 additions & 1 deletion lldb/source/Target/Target.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3171,7 +3171,13 @@ bool Target::RunStopHooks(bool at_initial_stop) {
bool should_stop = false;
bool requested_continue = false;

for (auto stop_entry : m_stop_hooks) {
// A stop hook might get deleted while running stop hooks.
// We have to decide what that means. We will follow the rule that deleting
// a stop hook while processing these stop hooks will delete it for FUTURE
// stops but not this stop. The easiest way to do that is to copy the
// stop hooks and iterate over the copy.
StopHookCollection stop_hooks_copy = m_stop_hooks;
for (auto stop_entry : stop_hooks_copy) {
StopHookSP cur_hook_sp = stop_entry.second;
if (!cur_hook_sp->IsActive())
continue;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,39 @@ def test_bad_handler(self):
"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")
Expand Down
25 changes: 25 additions & 0 deletions lldb/test/API/commands/target/stop-hooks/stop_hook.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,28 @@ def handle_stop(self):
class no_handle_stop:
def __init__(self, target, extra_args, dict):
print("I am okay")


class self_deleting_stop:
def __init__(self, target, extra_args, dict):
self.target = target

def handle_stop(self, exe_ctx, stream):
interp = exe_ctx.target.debugger.GetCommandInterpreter()
result = lldb.SBCommandReturnObject()
interp.HandleCommand("handle_id", result)
id_str = result.GetOutput().rstrip()

command = f"target stop-hook delete {id_str}"
interp.HandleCommand(command, result)


stop_hook_id = 0


def handle_stop_hook_id(debugger, command, exe_ctx, result, extra_args):
global stop_hook_id
if command == "":
result.AppendMessage(str(stop_hook_id))
else:
stop_hook_id = int(command)
Loading