Skip to content

Commit 5ad26ac

Browse files
author
git apple-llvm automerger
committed
Merge commit '4fdb8cb42f73' from llvm.org/main into next
2 parents d62ab8b + 4fdb8cb commit 5ad26ac

File tree

12 files changed

+1221
-10
lines changed

12 files changed

+1221
-10
lines changed

lldb/include/lldb/Target/Target.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1475,6 +1475,12 @@ class Target : public std::enable_shared_from_this<Target>,
14751475

14761476
bool GetAutoContinue() const { return m_auto_continue; }
14771477

1478+
void SetRunAtInitialStop(bool at_initial_stop) {
1479+
m_at_initial_stop = at_initial_stop;
1480+
}
1481+
1482+
bool GetRunAtInitialStop() const { return m_at_initial_stop; }
1483+
14781484
void GetDescription(Stream &s, lldb::DescriptionLevel level) const;
14791485
virtual void GetSubclassDescription(Stream &s,
14801486
lldb::DescriptionLevel level) const = 0;
@@ -1485,6 +1491,7 @@ class Target : public std::enable_shared_from_this<Target>,
14851491
std::unique_ptr<ThreadSpec> m_thread_spec_up;
14861492
bool m_active = true;
14871493
bool m_auto_continue = false;
1494+
bool m_at_initial_stop = true;
14881495

14891496
StopHook(lldb::TargetSP target_sp, lldb::user_id_t uid);
14901497
};
@@ -1551,7 +1558,9 @@ class Target : public std::enable_shared_from_this<Target>,
15511558

15521559
// Runs the stop hooks that have been registered for this target.
15531560
// Returns true if the stop hooks cause the target to resume.
1554-
bool RunStopHooks();
1561+
// Pass at_initial_stop if this is the stop where lldb gains
1562+
// control over the process for the first time.
1563+
bool RunStopHooks(bool at_initial_stop = false);
15551564

15561565
size_t GetStopHookSize();
15571566

lldb/source/Commands/CommandObjectTarget.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4794,6 +4794,17 @@ class CommandObjectTargetStopHookAdd : public CommandObjectParsed,
47944794
m_one_liner.push_back(std::string(option_arg));
47954795
break;
47964796

4797+
case 'I': {
4798+
bool value, success;
4799+
value = OptionArgParser::ToBoolean(option_arg, false, &success);
4800+
if (success)
4801+
m_at_initial_stop = value;
4802+
else
4803+
error = Status::FromErrorStringWithFormat(
4804+
"invalid boolean value '%s' passed for -F option",
4805+
option_arg.str().c_str());
4806+
} break;
4807+
47974808
default:
47984809
llvm_unreachable("Unimplemented option");
47994810
}
@@ -4820,6 +4831,7 @@ class CommandObjectTargetStopHookAdd : public CommandObjectParsed,
48204831
m_use_one_liner = false;
48214832
m_one_liner.clear();
48224833
m_auto_continue = false;
4834+
m_at_initial_stop = true;
48234835
}
48244836

48254837
std::string m_class_name;
@@ -4840,6 +4852,7 @@ class CommandObjectTargetStopHookAdd : public CommandObjectParsed,
48404852
// Instance variables to hold the values for one_liner options.
48414853
bool m_use_one_liner = false;
48424854
std::vector<std::string> m_one_liner;
4855+
bool m_at_initial_stop;
48434856

48444857
bool m_auto_continue = false;
48454858
};
@@ -5005,6 +5018,9 @@ Filter Options:
50055018
if (specifier_up)
50065019
new_hook_sp->SetSpecifier(specifier_up.release());
50075020

5021+
// Should we run at the initial stop:
5022+
new_hook_sp->SetRunAtInitialStop(m_options.m_at_initial_stop);
5023+
50085024
// Next see if any of the thread options have been entered:
50095025

50105026
if (m_options.m_thread_specified) {

lldb/source/Commands/Options.td

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1064,8 +1064,14 @@ let Command = "target stop hook add" in {
10641064
Arg<"FunctionName">, Desc<"Set the function name within which the stop hook"
10651065
" will be run.">, Completion<"Symbol">;
10661066
def target_stop_hook_add_auto_continue : Option<"auto-continue", "G">,
1067-
Arg<"Boolean">, Desc<"The breakpoint will auto-continue after running its"
1067+
Arg<"Boolean">, Desc<"The stop-hook will auto-continue after running its"
10681068
" commands.">;
1069+
def target_stop_hook_add_at_initial_stop : Option<"at-initial-stop", "I">,
1070+
Arg<"Boolean">, Desc<"Whether the stop-hook will trigger when lldb "
1071+
"initially gains control of the process. For a process launch, this "
1072+
"initial stop may happen very early on - before the loader has run. You "
1073+
"can use this option if you do not want some stop-hooks to run then. "
1074+
"Defaults to true.">;
10691075
}
10701076

10711077
let Command = "thread backtrace" in {

lldb/source/Target/Process.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2981,6 +2981,9 @@ Status Process::LoadCore() {
29812981
"Did not get stopped event after loading the core file.");
29822982
}
29832983
RestoreProcessEvents();
2984+
// Since we hijacked the event stream, we will have we won't have run the
2985+
// stop hooks. Make sure we do that here:
2986+
GetTarget().RunStopHooks(/* at_initial_stop= */ true);
29842987
}
29852988
return error;
29862989
}
@@ -3351,6 +3354,9 @@ void Process::CompleteAttach() {
33513354
: "<none>");
33523355
}
33533356
}
3357+
// Since we hijacked the event stream, we will have we won't have run the
3358+
// stop hooks. Make sure we do that here:
3359+
GetTarget().RunStopHooks(/* at_initial_stop= */ true);
33543360
}
33553361

33563362
Status Process::ConnectRemote(llvm::StringRef remote_url) {

lldb/source/Target/Target.cpp

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3137,7 +3137,7 @@ void Target::SetAllStopHooksActiveState(bool active_state) {
31373137
}
31383138
}
31393139

3140-
bool Target::RunStopHooks() {
3140+
bool Target::RunStopHooks(bool at_initial_stop) {
31413141
if (m_suppress_stop_hooks)
31423142
return false;
31433143

@@ -3146,14 +3146,19 @@ bool Target::RunStopHooks() {
31463146

31473147
// Somebody might have restarted the process:
31483148
// Still return false, the return value is about US restarting the target.
3149-
if (m_process_sp->GetState() != eStateStopped)
3149+
lldb::StateType state = m_process_sp->GetState();
3150+
if (!(state == eStateStopped || state == eStateAttaching))
31503151
return false;
31513152

31523153
if (m_stop_hooks.empty())
31533154
return false;
31543155

31553156
bool no_active_hooks =
3156-
llvm::none_of(m_stop_hooks, [](auto &p) { return p.second->IsActive(); });
3157+
llvm::none_of(m_stop_hooks, [at_initial_stop](auto &p) {
3158+
bool should_run_now =
3159+
!at_initial_stop || p.second->GetRunAtInitialStop();
3160+
return p.second->IsActive() && should_run_now;
3161+
});
31573162
if (no_active_hooks)
31583163
return false;
31593164

@@ -3183,9 +3188,22 @@ bool Target::RunStopHooks() {
31833188
}
31843189

31853190
// If no threads stopped for a reason, don't run the stop-hooks.
3191+
// However, if this is the FIRST stop for this process, then we are in the
3192+
// state where an attach or a core file load was completed without designating
3193+
// a particular thread as responsible for the stop. In that case, we do
3194+
// want to run the stop hooks, but do so just on one thread.
31863195
size_t num_exe_ctx = exc_ctx_with_reasons.size();
3187-
if (num_exe_ctx == 0)
3188-
return false;
3196+
if (num_exe_ctx == 0) {
3197+
if (at_initial_stop && num_threads > 0) {
3198+
lldb::ThreadSP thread_to_use_sp = cur_threadlist.GetThreadAtIndex(0);
3199+
exc_ctx_with_reasons.emplace_back(
3200+
m_process_sp.get(), thread_to_use_sp.get(),
3201+
thread_to_use_sp->GetStackFrameAtIndex(0).get());
3202+
num_exe_ctx = 1;
3203+
} else {
3204+
return false;
3205+
}
3206+
}
31893207

31903208
StreamSP output_sp = m_debugger.GetAsyncOutputStream();
31913209
auto on_exit = llvm::make_scope_exit([output_sp] { output_sp->Flush(); });
@@ -3199,6 +3217,8 @@ bool Target::RunStopHooks() {
31993217
StopHookSP cur_hook_sp = stop_entry.second;
32003218
if (!cur_hook_sp->IsActive())
32013219
continue;
3220+
if (at_initial_stop && !cur_hook_sp->GetRunAtInitialStop())
3221+
continue;
32023222

32033223
bool any_thread_matched = false;
32043224
for (auto exc_ctx : exc_ctx_with_reasons) {
@@ -3525,10 +3545,14 @@ Status Target::Launch(ProcessLaunchInfo &launch_info, Stream *stream) {
35253545
m_process_sp->RestoreProcessEvents();
35263546

35273547
if (rebroadcast_first_stop) {
3548+
// We don't need to run the stop hooks by hand here, they will get
3549+
// triggered when this rebroadcast event gets fetched.
35283550
assert(first_stop_event_sp);
35293551
m_process_sp->BroadcastEvent(first_stop_event_sp);
35303552
return error;
35313553
}
3554+
// Run the stop hooks that want to run at entry.
3555+
RunStopHooks(true /* at entry point */);
35323556

35333557
switch (state) {
35343558
case eStateStopped: {
@@ -3681,6 +3705,10 @@ Status Target::Attach(ProcessAttachInfo &attach_info, Stream *stream) {
36813705
true, SelectMostRelevantFrame);
36823706
process_sp->RestoreProcessEvents();
36833707

3708+
// Run the stop hooks here. Since we were hijacking the events, they
3709+
// wouldn't have gotten run as part of event delivery.
3710+
RunStopHooks(/* at_initial_stop= */ true);
3711+
36843712
if (state != eStateStopped) {
36853713
const char *exit_desc = process_sp->GetExitDescription();
36863714
if (exit_desc)

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,11 @@ def test_bad_handler(self):
5050

5151
def test_stop_hooks_scripted(self):
5252
"""Test that a scripted stop hook works with no specifiers"""
53-
self.stop_hooks_scripted(5)
53+
self.stop_hooks_scripted(5, "-I false")
54+
55+
def test_stop_hooks_scripted_no_entry(self):
56+
"""Test that a scripted stop hook works with no specifiers"""
57+
self.stop_hooks_scripted(10)
5458

5559
def test_stop_hooks_scripted_right_func(self):
5660
"""Test that a scripted stop hook fires when there is a function match"""

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ def step_out_test(self):
5555
def after_expr_test(self):
5656
interp = self.dbg.GetCommandInterpreter()
5757
result = lldb.SBCommandReturnObject()
58-
interp.HandleCommand("target stop-hook add -o 'expr g_var++'", result)
58+
interp.HandleCommand("target stop-hook add -o 'expr g_var++' -I false", result)
5959
self.assertTrue(result.Succeeded(), "Set the target stop hook")
6060

6161
(target, process, thread, first_bkpt) = lldbutil.run_to_source_breakpoint(
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
"""
2+
Test that stop hooks fire on core load (first stop)
3+
"""
4+
5+
6+
import lldb
7+
import os
8+
from lldbsuite.test.decorators import *
9+
from lldbsuite.test.lldbtest import *
10+
from lldbsuite.test import lldbutil
11+
12+
13+
class TestStopOnCoreLoad(TestBase):
14+
NO_DEBUG_INFO_TESTCASE = True
15+
16+
# This was originally marked as expected failure on Windows, but it has
17+
# started timing out instead, so the expectedFailure attribute no longer
18+
# correctly tracks it: llvm.org/pr37371
19+
@skipIfWindows
20+
def test_hook_runs_no_threads(self):
21+
# Create core form YAML.
22+
core_path = self.getBuildArtifact("test.core")
23+
self.yaml2obj("test.core.yaml", core_path)
24+
25+
# Since mach core files don't have stop reasons, we should choose
26+
# the first thread:
27+
self.do_test(core_path, 1)
28+
29+
def test_hook_one_thread(self):
30+
core_path = os.path.join(self.getSourceDir(), "linux-x86_64.core")
31+
self.do_test(core_path, 3)
32+
33+
def do_test(self, core_path, stop_thread):
34+
# Set debugger into synchronous mode
35+
self.dbg.SetAsync(False)
36+
37+
# Create a target by the debugger.
38+
target = self.dbg.CreateTarget("")
39+
40+
# load the stop hook module and add the stop hook:
41+
stop_hook_path = os.path.join(self.getSourceDir(), "stop_hook.py")
42+
self.runCmd(f"command script import {stop_hook_path}")
43+
self.runCmd("target stop-hook add -P stop_hook.stop_handler")
44+
45+
# Load core.
46+
process = target.LoadCore(core_path)
47+
self.assertTrue(process, PROCESS_IS_VALID)
48+
# Now run our report command and make sure we get the right answer.
49+
50+
result = lldb.SBCommandReturnObject()
51+
self.dbg.GetCommandInterpreter().HandleCommand("report_command", result)
52+
print(f"Command Output: '{result.GetOutput}'")
53+
self.assertIn(
54+
f"Stop Threads: {stop_thread}", result.GetOutput(), "Ran the stop hook"
55+
)
Binary file not shown.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import lldb
2+
3+
4+
def report_command(debugger, command, exe_ctx, result, internal_dict):
5+
global stop_thread
6+
print(f"About to report out stop_thread: {stop_thread}")
7+
mssg = f"Stop Threads: {stop_thread}"
8+
result.AppendMessage(mssg)
9+
10+
result.SetStatus(lldb.eReturnStatusSuccessFinishResult)
11+
12+
13+
class stop_handler:
14+
def __init__(self, target, extra_args, dict):
15+
global stop_thread
16+
stop_thead = 0
17+
self.target = target
18+
19+
def handle_stop(self, exe_ctx, stream):
20+
global stop_thread
21+
thread = exe_ctx.thread
22+
stop_thread = thread.idx
23+
24+
25+
def __lldb_init_module(debugger, internal_dict):
26+
global stop_thread
27+
stop_thread = 0
28+
debugger.HandleCommand(
29+
f"command script add -o -f '{__name__}.report_command' report_command"
30+
)

0 commit comments

Comments
 (0)