Skip to content

Commit be66987

Browse files
committed
Fix raciness in the StopHook check for "has the target run".
This was looking at the privateState, but it's possible that the actual process has started up and then stopped again by the time we get to the check, which would lead us to get out of running the stop hooks too early. Instead we need to track the intention of the stop hooks directly. Differential Revision: https://reviews.llvm.org/D88753
1 parent a4b842e commit be66987

File tree

4 files changed

+86
-43
lines changed

4 files changed

+86
-43
lines changed

lldb/include/lldb/Target/Target.h

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1145,6 +1145,11 @@ class Target : public std::enable_shared_from_this<Target>,
11451145
virtual ~StopHook() = default;
11461146

11471147
enum class StopHookKind : uint32_t { CommandBased = 0, ScriptBased };
1148+
enum class StopHookResult : uint32_t {
1149+
KeepStopped = 0,
1150+
RequestContinue,
1151+
AlreadyContinued
1152+
};
11481153

11491154
lldb::TargetSP &GetTarget() { return m_target_sp; }
11501155

@@ -1160,8 +1165,8 @@ class Target : public std::enable_shared_from_this<Target>,
11601165
// with a reason" thread. It should add to the stream whatever text it
11611166
// wants to show the user, and return False to indicate it wants the target
11621167
// not to stop.
1163-
virtual bool HandleStop(ExecutionContext &exe_ctx,
1164-
lldb::StreamSP output) = 0;
1168+
virtual StopHookResult HandleStop(ExecutionContext &exe_ctx,
1169+
lldb::StreamSP output) = 0;
11651170

11661171
// Set the Thread Specifier. The stop hook will own the thread specifier,
11671172
// and is responsible for deleting it when we're done.
@@ -1201,8 +1206,8 @@ class Target : public std::enable_shared_from_this<Target>,
12011206
void SetActionFromString(const std::string &strings);
12021207
void SetActionFromStrings(const std::vector<std::string> &strings);
12031208

1204-
bool HandleStop(ExecutionContext &exc_ctx,
1205-
lldb::StreamSP output_sp) override;
1209+
StopHookResult HandleStop(ExecutionContext &exc_ctx,
1210+
lldb::StreamSP output_sp) override;
12061211
void GetSubclassDescription(Stream *s,
12071212
lldb::DescriptionLevel level) const override;
12081213

@@ -1219,7 +1224,8 @@ class Target : public std::enable_shared_from_this<Target>,
12191224
class StopHookScripted : public StopHook {
12201225
public:
12211226
virtual ~StopHookScripted() = default;
1222-
bool HandleStop(ExecutionContext &exc_ctx, lldb::StreamSP output) override;
1227+
StopHookResult HandleStop(ExecutionContext &exc_ctx,
1228+
lldb::StreamSP output) override;
12231229

12241230
Status SetScriptCallback(std::string class_name,
12251231
StructuredData::ObjectSP extra_args_sp);
@@ -1254,7 +1260,9 @@ class Target : public std::enable_shared_from_this<Target>,
12541260
/// remove the stop hook, as it will also reset the stop hook counter.
12551261
void UndoCreateStopHook(lldb::user_id_t uid);
12561262

1257-
void RunStopHooks();
1263+
// Runs the stop hooks that have been registered for this target.
1264+
// Returns true if the stop hooks cause the target to resume.
1265+
bool RunStopHooks();
12581266

12591267
size_t GetStopHookSize();
12601268

lldb/source/Target/Process.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4178,8 +4178,7 @@ void Process::ProcessEventData::DoOnRemoval(Event *event_ptr) {
41784178
// public (or SyncResume) broadcasters. StopHooks are just for
41794179
// real public stops. They might also restart the target,
41804180
// so watch for that.
4181-
process_sp->GetTarget().RunStopHooks();
4182-
if (process_sp->GetPrivateState() == eStateRunning)
4181+
if (process_sp->GetTarget().RunStopHooks())
41834182
SetRestarted(true);
41844183
}
41854184
}

lldb/source/Target/Target.cpp

Lines changed: 71 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2541,25 +2541,26 @@ void Target::SetAllStopHooksActiveState(bool active_state) {
25412541
}
25422542
}
25432543

2544-
void Target::RunStopHooks() {
2544+
bool Target::RunStopHooks() {
25452545
if (m_suppress_stop_hooks)
2546-
return;
2546+
return false;
25472547

25482548
if (!m_process_sp)
2549-
return;
2549+
return false;
25502550

25512551
// Somebody might have restarted the process:
2552+
// Still return false, the return value is about US restarting the target.
25522553
if (m_process_sp->GetState() != eStateStopped)
2553-
return;
2554+
return false;
25542555

25552556
// <rdar://problem/12027563> make sure we check that we are not stopped
25562557
// because of us running a user expression since in that case we do not want
25572558
// to run the stop-hooks
25582559
if (m_process_sp->GetModIDRef().IsLastResumeForUserExpression())
2559-
return;
2560+
return false;
25602561

25612562
if (m_stop_hooks.empty())
2562-
return;
2563+
return false;
25632564

25642565
// If there aren't any active stop hooks, don't bother either.
25652566
bool any_active_hooks = false;
@@ -2570,7 +2571,7 @@ void Target::RunStopHooks() {
25702571
}
25712572
}
25722573
if (!any_active_hooks)
2573-
return;
2574+
return false;
25742575

25752576
std::vector<ExecutionContext> exc_ctx_with_reasons;
25762577

@@ -2588,7 +2589,7 @@ void Target::RunStopHooks() {
25882589
// If no threads stopped for a reason, don't run the stop-hooks.
25892590
size_t num_exe_ctx = exc_ctx_with_reasons.size();
25902591
if (num_exe_ctx == 0)
2591-
return;
2592+
return false;
25922593

25932594
StreamSP output_sp = m_debugger.GetAsyncOutputStream();
25942595

@@ -2636,22 +2637,27 @@ void Target::RunStopHooks() {
26362637
output_sp->Printf("-- Thread %d\n",
26372638
exc_ctx.GetThreadPtr()->GetIndexID());
26382639

2639-
bool this_should_stop = cur_hook_sp->HandleStop(exc_ctx, output_sp);
2640-
// If this hook is set to auto-continue that should override the
2641-
// HandleStop result...
2642-
if (cur_hook_sp->GetAutoContinue())
2643-
this_should_stop = false;
2640+
StopHook::StopHookResult this_result =
2641+
cur_hook_sp->HandleStop(exc_ctx, output_sp);
2642+
bool this_should_stop = true;
26442643

2645-
// If anybody wanted to stop, we should all stop.
2646-
if (!should_stop)
2647-
should_stop = this_should_stop;
2644+
switch (this_result) {
2645+
case StopHook::StopHookResult::KeepStopped:
2646+
// If this hook is set to auto-continue that should override the
2647+
// HandleStop result...
2648+
if (cur_hook_sp->GetAutoContinue())
2649+
this_should_stop = false;
2650+
else
2651+
this_should_stop = true;
26482652

2649-
// We don't have a good way to prohibit people from restarting the target
2650-
// willy nilly in a stop hook. So see if the private state is running
2651-
// here and bag out if it is.
2652-
// FIXME: when we are doing non-stop mode for realz we'll have to instead
2653-
// track each thread, and only bag out if a thread is set running.
2654-
if (m_process_sp->GetPrivateState() != eStateStopped) {
2653+
break;
2654+
case StopHook::StopHookResult::RequestContinue:
2655+
this_should_stop = false;
2656+
break;
2657+
case StopHook::StopHookResult::AlreadyContinued:
2658+
// We don't have a good way to prohibit people from restarting the
2659+
// target willy nilly in a stop hook. If the hook did so, give a
2660+
// gentle suggestion here and bag out if the hook processing.
26552661
output_sp->Printf("\nAborting stop hooks, hook %" PRIu64
26562662
" set the program running.\n"
26572663
" Consider using '-G true' to make "
@@ -2660,16 +2666,42 @@ void Target::RunStopHooks() {
26602666
somebody_restarted = true;
26612667
break;
26622668
}
2669+
// If we're already restarted, stop processing stop hooks.
2670+
// FIXME: if we are doing non-stop mode for real, we would have to
2671+
// check that OUR thread was restarted, otherwise we should keep
2672+
// processing stop hooks.
2673+
if (somebody_restarted)
2674+
break;
2675+
2676+
// If anybody wanted to stop, we should all stop.
2677+
if (!should_stop)
2678+
should_stop = this_should_stop;
26632679
}
26642680
}
26652681

26662682
output_sp->Flush();
26672683

2684+
// If one of the commands in the stop hook already restarted the target,
2685+
// report that fact.
2686+
if (somebody_restarted)
2687+
return true;
2688+
26682689
// Finally, if auto-continue was requested, do it now:
26692690
// We only compute should_stop against the hook results if a hook got to run
26702691
// which is why we have to do this conjoint test.
2671-
if (!somebody_restarted && ((hooks_ran && !should_stop) || auto_continue))
2672-
m_process_sp->PrivateResume();
2692+
if ((hooks_ran && !should_stop) || auto_continue) {
2693+
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
2694+
Status error = m_process_sp->PrivateResume();
2695+
if (error.Success()) {
2696+
LLDB_LOG(log, "Resuming from RunStopHooks");
2697+
return true;
2698+
} else {
2699+
LLDB_LOG(log, "Resuming from RunStopHooks failed: {0}", error);
2700+
return false;
2701+
}
2702+
}
2703+
2704+
return false;
26732705
}
26742706

26752707
const TargetPropertiesSP &Target::GetGlobalProperties() {
@@ -3235,13 +3267,14 @@ void Target::StopHookCommandLine::SetActionFromStrings(
32353267
GetCommands().AppendString(string.c_str());
32363268
}
32373269

3238-
bool Target::StopHookCommandLine::HandleStop(ExecutionContext &exc_ctx,
3239-
StreamSP output_sp) {
3270+
Target::StopHook::StopHookResult
3271+
Target::StopHookCommandLine::HandleStop(ExecutionContext &exc_ctx,
3272+
StreamSP output_sp) {
32403273
assert(exc_ctx.GetTargetPtr() && "Can't call PerformAction on a context "
32413274
"with no target");
32423275

32433276
if (!m_commands.GetSize())
3244-
return true;
3277+
return StopHookResult::KeepStopped;
32453278

32463279
CommandReturnObject result(false);
32473280
result.SetImmediateOutputStream(output_sp);
@@ -3260,8 +3293,11 @@ bool Target::StopHookCommandLine::HandleStop(ExecutionContext &exc_ctx,
32603293
debugger.GetCommandInterpreter().HandleCommands(GetCommands(), &exc_ctx,
32613294
options, result);
32623295
debugger.SetAsyncExecution(old_async);
3263-
3264-
return true;
3296+
lldb::ReturnStatus status = result.GetStatus();
3297+
if (status == eReturnStatusSuccessContinuingNoResult ||
3298+
status == eReturnStatusSuccessContinuingResult)
3299+
return StopHookResult::AlreadyContinued;
3300+
return StopHookResult::KeepStopped;
32653301
}
32663302

32673303
// Target::StopHookScripted
@@ -3289,20 +3325,22 @@ Status Target::StopHookScripted::SetScriptCallback(
32893325
return error;
32903326
}
32913327

3292-
bool Target::StopHookScripted::HandleStop(ExecutionContext &exc_ctx,
3293-
StreamSP output_sp) {
3328+
Target::StopHook::StopHookResult
3329+
Target::StopHookScripted::HandleStop(ExecutionContext &exc_ctx,
3330+
StreamSP output_sp) {
32943331
assert(exc_ctx.GetTargetPtr() && "Can't call HandleStop on a context "
32953332
"with no target");
32963333

32973334
ScriptInterpreter *script_interp =
32983335
GetTarget()->GetDebugger().GetScriptInterpreter();
32993336
if (!script_interp)
3300-
return true;
3337+
return StopHookResult::KeepStopped;
33013338

33023339
bool should_stop = script_interp->ScriptedStopHookHandleStop(
33033340
m_implementation_sp, exc_ctx, output_sp);
33043341

3305-
return should_stop;
3342+
return should_stop ? StopHookResult::KeepStopped
3343+
: StopHookResult::RequestContinue;
33063344
}
33073345

33083346
void Target::StopHookScripted::GetSubclassDescription(

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,6 @@ def test_stop_hooks_scripted_return_false(self):
7171
"""Test that the returning False from a stop hook works"""
7272
self.do_test_auto_continue(True)
7373

74-
# Test is flakey on Linux.
75-
@skipIfLinux
7674
def do_test_auto_continue(self, return_true):
7775
"""Test that auto-continue works."""
7876
# We set auto-continue to 1 but the stop hook only applies to step_out_of_me,

0 commit comments

Comments
 (0)