Skip to content

Commit 59bf968

Browse files
author
git apple-llvm automerger
committed
Merge commit '1ae63621c5f3' from swift/release/6.2 into stable/20240723
2 parents 7f9c8ab + 1ae6362 commit 59bf968

File tree

12 files changed

+1223
-18
lines changed

12 files changed

+1223
-18
lines changed

lldb/include/lldb/Target/Target.h

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

14591459
bool GetAutoContinue() const { return m_auto_continue; }
14601460

1461+
void SetRunAtInitialStop(bool at_initial_stop) {
1462+
m_at_initial_stop = at_initial_stop;
1463+
}
1464+
1465+
bool GetRunAtInitialStop() const { return m_at_initial_stop; }
1466+
14611467
void GetDescription(Stream &s, lldb::DescriptionLevel level) const;
14621468
virtual void GetSubclassDescription(Stream &s,
14631469
lldb::DescriptionLevel level) const = 0;
@@ -1468,6 +1474,7 @@ class Target : public std::enable_shared_from_this<Target>,
14681474
std::unique_ptr<ThreadSpec> m_thread_spec_up;
14691475
bool m_active = true;
14701476
bool m_auto_continue = false;
1477+
bool m_at_initial_stop = true;
14711478

14721479
StopHook(lldb::TargetSP target_sp, lldb::user_id_t uid);
14731480
};
@@ -1535,7 +1542,9 @@ class Target : public std::enable_shared_from_this<Target>,
15351542

15361543
// Runs the stop hooks that have been registered for this target.
15371544
// Returns true if the stop hooks cause the target to resume.
1538-
bool RunStopHooks();
1545+
// Pass at_initial_stop if this is the stop where lldb gains
1546+
// control over the process for the first time.
1547+
bool RunStopHooks(bool at_initial_stop = false);
15391548

15401549
size_t GetStopHookSize();
15411550

lldb/source/Commands/CommandObjectTarget.cpp

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

4810+
case 'I': {
4811+
bool value, success;
4812+
value = OptionArgParser::ToBoolean(option_arg, false, &success);
4813+
if (success)
4814+
m_at_initial_stop = value;
4815+
else
4816+
error = Status::FromErrorStringWithFormat(
4817+
"invalid boolean value '%s' passed for -F option",
4818+
option_arg.str().c_str());
4819+
} break;
4820+
48104821
default:
48114822
llvm_unreachable("Unimplemented option");
48124823
}
@@ -4833,6 +4844,7 @@ class CommandObjectTargetStopHookAdd : public CommandObjectParsed,
48334844
m_use_one_liner = false;
48344845
m_one_liner.clear();
48354846
m_auto_continue = false;
4847+
m_at_initial_stop = true;
48364848
}
48374849

48384850
std::string m_class_name;
@@ -4853,6 +4865,7 @@ class CommandObjectTargetStopHookAdd : public CommandObjectParsed,
48534865
// Instance variables to hold the values for one_liner options.
48544866
bool m_use_one_liner = false;
48554867
std::vector<std::string> m_one_liner;
4868+
bool m_at_initial_stop;
48564869

48574870
bool m_auto_continue = false;
48584871
};
@@ -5021,6 +5034,9 @@ Filter Options:
50215034
if (specifier_up)
50225035
new_hook_sp->SetSpecifier(specifier_up.release());
50235036

5037+
// Should we run at the initial stop:
5038+
new_hook_sp->SetRunAtInitialStop(m_options.m_at_initial_stop);
5039+
50245040
// Next see if any of the thread options have been entered:
50255041

50265042
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
@@ -1053,8 +1053,14 @@ let Command = "target stop hook add" in {
10531053
Arg<"FunctionName">, Desc<"Set the function name within which the stop hook"
10541054
" will be run.">, Completion<"Symbol">;
10551055
def target_stop_hook_add_auto_continue : Option<"auto-continue", "G">,
1056-
Arg<"Boolean">, Desc<"The breakpoint will auto-continue after running its"
1056+
Arg<"Boolean">, Desc<"The stop-hook will auto-continue after running its"
10571057
" commands.">;
1058+
def target_stop_hook_add_at_initial_stop : Option<"at-initial-stop", "I">,
1059+
Arg<"Boolean">, Desc<"Whether the stop-hook will trigger when lldb "
1060+
"initially gains control of the process. For a process launch, this "
1061+
"initial stop may happen very early on - before the loader has run. You "
1062+
"can use this option if you do not want some stop-hooks to run then. "
1063+
"Defaults to true.">;
10581064
}
10591065

10601066
let Command = "thread backtrace" in {

lldb/source/Target/Process.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2929,6 +2929,9 @@ Status Process::LoadCore() {
29292929
"Did not get stopped event after loading the core file.");
29302930
}
29312931
RestoreProcessEvents();
2932+
// Since we hijacked the event stream, we will have we won't have run the
2933+
// stop hooks. Make sure we do that here:
2934+
GetTarget().RunStopHooks(/* at_initial_stop= */ true);
29322935
}
29332936
return error;
29342937
}
@@ -3299,6 +3302,9 @@ void Process::CompleteAttach() {
32993302
: "<none>");
33003303
}
33013304
}
3305+
// Since we hijacked the event stream, we will have we won't have run the
3306+
// stop hooks. Make sure we do that here:
3307+
GetTarget().RunStopHooks(/* at_initial_stop= */ true);
33023308
}
33033309

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

lldb/source/Target/Target.cpp

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3027,7 +3027,7 @@ void Target::SetAllStopHooksActiveState(bool active_state) {
30273027
}
30283028
}
30293029

3030-
bool Target::RunStopHooks() {
3030+
bool Target::RunStopHooks(bool at_initial_stop) {
30313031
if (m_suppress_stop_hooks)
30323032
return false;
30333033

@@ -3036,21 +3036,20 @@ bool Target::RunStopHooks() {
30363036

30373037
// Somebody might have restarted the process:
30383038
// Still return false, the return value is about US restarting the target.
3039-
if (m_process_sp->GetState() != eStateStopped)
3039+
lldb::StateType state = m_process_sp->GetState();
3040+
if (!(state == eStateStopped || state == eStateAttaching))
30403041
return false;
30413042

30423043
if (m_stop_hooks.empty())
30433044
return false;
30443045

3045-
// If there aren't any active stop hooks, don't bother either.
3046-
bool any_active_hooks = false;
3047-
for (auto hook : m_stop_hooks) {
3048-
if (hook.second->IsActive()) {
3049-
any_active_hooks = true;
3050-
break;
3051-
}
3052-
}
3053-
if (!any_active_hooks)
3046+
bool no_active_hooks =
3047+
llvm::none_of(m_stop_hooks, [at_initial_stop](auto &p) {
3048+
bool should_run_now =
3049+
!at_initial_stop || p.second->GetRunAtInitialStop();
3050+
return p.second->IsActive() && should_run_now;
3051+
});
3052+
if (no_active_hooks)
30543053
return false;
30553054

30563055
// Make sure we check that we are not stopped because of us running a user
@@ -3079,9 +3078,22 @@ bool Target::RunStopHooks() {
30793078
}
30803079

30813080
// If no threads stopped for a reason, don't run the stop-hooks.
3081+
// However, if this is the FIRST stop for this process, then we are in the
3082+
// state where an attach or a core file load was completed without designating
3083+
// a particular thread as responsible for the stop. In that case, we do
3084+
// want to run the stop hooks, but do so just on one thread.
30823085
size_t num_exe_ctx = exc_ctx_with_reasons.size();
3083-
if (num_exe_ctx == 0)
3084-
return false;
3086+
if (num_exe_ctx == 0) {
3087+
if (at_initial_stop && num_threads > 0) {
3088+
lldb::ThreadSP thread_to_use_sp = cur_threadlist.GetThreadAtIndex(0);
3089+
exc_ctx_with_reasons.emplace_back(
3090+
m_process_sp.get(), thread_to_use_sp.get(),
3091+
thread_to_use_sp->GetStackFrameAtIndex(0).get());
3092+
num_exe_ctx = 1;
3093+
} else {
3094+
return false;
3095+
}
3096+
}
30853097

30863098
StreamSP output_sp = m_debugger.GetAsyncOutputStream();
30873099

@@ -3096,6 +3108,8 @@ bool Target::RunStopHooks() {
30963108
StopHookSP cur_hook_sp = stop_entry.second;
30973109
if (!cur_hook_sp->IsActive())
30983110
continue;
3111+
if (at_initial_stop && !cur_hook_sp->GetRunAtInitialStop())
3112+
continue;
30993113

31003114
bool any_thread_matched = false;
31013115
for (auto exc_ctx : exc_ctx_with_reasons) {
@@ -3472,10 +3486,14 @@ Status Target::Launch(ProcessLaunchInfo &launch_info, Stream *stream) {
34723486
m_process_sp->RestoreProcessEvents();
34733487

34743488
if (rebroadcast_first_stop) {
3489+
// We don't need to run the stop hooks by hand here, they will get
3490+
// triggered when this rebroadcast event gets fetched.
34753491
assert(first_stop_event_sp);
34763492
m_process_sp->BroadcastEvent(first_stop_event_sp);
34773493
return error;
34783494
}
3495+
// Run the stop hooks that want to run at entry.
3496+
RunStopHooks(true /* at entry point */);
34793497

34803498
switch (state) {
34813499
case eStateStopped: {
@@ -3628,6 +3646,10 @@ Status Target::Attach(ProcessAttachInfo &attach_info, Stream *stream) {
36283646
true, SelectMostRelevantFrame);
36293647
process_sp->RestoreProcessEvents();
36303648

3649+
// Run the stop hooks here. Since we were hijacking the events, they
3650+
// wouldn't have gotten run as part of event delivery.
3651+
RunStopHooks(/* at_initial_stop= */ true);
3652+
36313653
if (state != eStateStopped) {
36323654
const char *exit_desc = process_sp->GetExitDescription();
36333655
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
@@ -47,7 +47,7 @@ def step_out_test(self):
4747
def after_expr_test(self):
4848
interp = self.dbg.GetCommandInterpreter()
4949
result = lldb.SBCommandReturnObject()
50-
interp.HandleCommand("target stop-hook add -o 'expr g_var++'", result)
50+
interp.HandleCommand("target stop-hook add -o 'expr g_var++' -I false", result)
5151
self.assertTrue(result.Succeeded(), "Set the target stop hook")
5252

5353
(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)