virtual ~StopHook() = default;
enum class StopHookKind : uint32_t { CommandBased = 0, ScriptBased };
+ enum class StopHookResult : uint32_t {
+ KeepStopped = 0,
+ RequestContinue,
+ AlreadyContinued
+ };
lldb::TargetSP &GetTarget() { return m_target_sp; }
// with a reason" thread. It should add to the stream whatever text it
// wants to show the user, and return False to indicate it wants the target
// not to stop.
- virtual bool HandleStop(ExecutionContext &exe_ctx,
- lldb::StreamSP output) = 0;
+ virtual StopHookResult HandleStop(ExecutionContext &exe_ctx,
+ lldb::StreamSP output) = 0;
// Set the Thread Specifier. The stop hook will own the thread specifier,
// and is responsible for deleting it when we're done.
void SetActionFromString(const std::string &strings);
void SetActionFromStrings(const std::vector<std::string> &strings);
- bool HandleStop(ExecutionContext &exc_ctx,
- lldb::StreamSP output_sp) override;
+ StopHookResult HandleStop(ExecutionContext &exc_ctx,
+ lldb::StreamSP output_sp) override;
void GetSubclassDescription(Stream *s,
lldb::DescriptionLevel level) const override;
class StopHookScripted : public StopHook {
public:
virtual ~StopHookScripted() = default;
- bool HandleStop(ExecutionContext &exc_ctx, lldb::StreamSP output) override;
+ StopHookResult HandleStop(ExecutionContext &exc_ctx,
+ lldb::StreamSP output) override;
Status SetScriptCallback(std::string class_name,
StructuredData::ObjectSP extra_args_sp);
/// remove the stop hook, as it will also reset the stop hook counter.
void UndoCreateStopHook(lldb::user_id_t uid);
- void RunStopHooks();
+ // Runs the stop hooks that have been registered for this target.
+ // Returns true if the stop hooks cause the target to resume.
+ bool RunStopHooks();
size_t GetStopHookSize();
}
}
-void Target::RunStopHooks() {
+bool Target::RunStopHooks() {
if (m_suppress_stop_hooks)
- return;
+ return false;
if (!m_process_sp)
- return;
+ return false;
// Somebody might have restarted the process:
+ // Still return false, the return value is about US restarting the target.
if (m_process_sp->GetState() != eStateStopped)
- return;
+ return false;
// <rdar://problem/12027563> make sure we check that we are not stopped
// because of us running a user expression since in that case we do not want
// to run the stop-hooks
if (m_process_sp->GetModIDRef().IsLastResumeForUserExpression())
- return;
+ return false;
if (m_stop_hooks.empty())
- return;
+ return false;
// If there aren't any active stop hooks, don't bother either.
bool any_active_hooks = false;
}
}
if (!any_active_hooks)
- return;
+ return false;
std::vector<ExecutionContext> exc_ctx_with_reasons;
// If no threads stopped for a reason, don't run the stop-hooks.
size_t num_exe_ctx = exc_ctx_with_reasons.size();
if (num_exe_ctx == 0)
- return;
+ return false;
StreamSP output_sp = m_debugger.GetAsyncOutputStream();
output_sp->Printf("-- Thread %d\n",
exc_ctx.GetThreadPtr()->GetIndexID());
- bool this_should_stop = cur_hook_sp->HandleStop(exc_ctx, output_sp);
- // If this hook is set to auto-continue that should override the
- // HandleStop result...
- if (cur_hook_sp->GetAutoContinue())
- this_should_stop = false;
+ StopHook::StopHookResult this_result =
+ cur_hook_sp->HandleStop(exc_ctx, output_sp);
+ bool this_should_stop = true;
- // If anybody wanted to stop, we should all stop.
- if (!should_stop)
- should_stop = this_should_stop;
+ switch (this_result) {
+ case StopHook::StopHookResult::KeepStopped:
+ // If this hook is set to auto-continue that should override the
+ // HandleStop result...
+ if (cur_hook_sp->GetAutoContinue())
+ this_should_stop = false;
+ else
+ this_should_stop = true;
- // We don't have a good way to prohibit people from restarting the target
- // willy nilly in a stop hook. So see if the private state is running
- // here and bag out if it is.
- // FIXME: when we are doing non-stop mode for realz we'll have to instead
- // track each thread, and only bag out if a thread is set running.
- if (m_process_sp->GetPrivateState() != eStateStopped) {
+ break;
+ case StopHook::StopHookResult::RequestContinue:
+ this_should_stop = false;
+ break;
+ case StopHook::StopHookResult::AlreadyContinued:
+ // We don't have a good way to prohibit people from restarting the
+ // target willy nilly in a stop hook. If the hook did so, give a
+ // gentle suggestion here and bag out if the hook processing.
output_sp->Printf("\nAborting stop hooks, hook %" PRIu64
" set the program running.\n"
" Consider using '-G true' to make "
somebody_restarted = true;
break;
}
+ // If we're already restarted, stop processing stop hooks.
+ // FIXME: if we are doing non-stop mode for real, we would have to
+ // check that OUR thread was restarted, otherwise we should keep
+ // processing stop hooks.
+ if (somebody_restarted)
+ break;
+
+ // If anybody wanted to stop, we should all stop.
+ if (!should_stop)
+ should_stop = this_should_stop;
}
}
output_sp->Flush();
+ // If one of the commands in the stop hook already restarted the target,
+ // report that fact.
+ if (somebody_restarted)
+ return true;
+
// Finally, if auto-continue was requested, do it now:
// We only compute should_stop against the hook results if a hook got to run
// which is why we have to do this conjoint test.
- if (!somebody_restarted && ((hooks_ran && !should_stop) || auto_continue))
- m_process_sp->PrivateResume();
+ if ((hooks_ran && !should_stop) || auto_continue) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ Status error = m_process_sp->PrivateResume();
+ if (error.Success()) {
+ LLDB_LOG(log, "Resuming from RunStopHooks");
+ return true;
+ } else {
+ LLDB_LOG(log, "Resuming from RunStopHooks failed: {0}", error);
+ return false;
+ }
+ }
+
+ return false;
}
const TargetPropertiesSP &Target::GetGlobalProperties() {
GetCommands().AppendString(string.c_str());
}
-bool Target::StopHookCommandLine::HandleStop(ExecutionContext &exc_ctx,
- StreamSP output_sp) {
+Target::StopHook::StopHookResult
+Target::StopHookCommandLine::HandleStop(ExecutionContext &exc_ctx,
+ StreamSP output_sp) {
assert(exc_ctx.GetTargetPtr() && "Can't call PerformAction on a context "
"with no target");
if (!m_commands.GetSize())
- return true;
+ return StopHookResult::KeepStopped;
CommandReturnObject result(false);
result.SetImmediateOutputStream(output_sp);
debugger.GetCommandInterpreter().HandleCommands(GetCommands(), &exc_ctx,
options, result);
debugger.SetAsyncExecution(old_async);
-
- return true;
+ lldb::ReturnStatus status = result.GetStatus();
+ if (status == eReturnStatusSuccessContinuingNoResult ||
+ status == eReturnStatusSuccessContinuingResult)
+ return StopHookResult::AlreadyContinued;
+ return StopHookResult::KeepStopped;
}
// Target::StopHookScripted
return error;
}
-bool Target::StopHookScripted::HandleStop(ExecutionContext &exc_ctx,
- StreamSP output_sp) {
+Target::StopHook::StopHookResult
+Target::StopHookScripted::HandleStop(ExecutionContext &exc_ctx,
+ StreamSP output_sp) {
assert(exc_ctx.GetTargetPtr() && "Can't call HandleStop on a context "
"with no target");
ScriptInterpreter *script_interp =
GetTarget()->GetDebugger().GetScriptInterpreter();
if (!script_interp)
- return true;
+ return StopHookResult::KeepStopped;
bool should_stop = script_interp->ScriptedStopHookHandleStop(
m_implementation_sp, exc_ctx, output_sp);
- return should_stop;
+ return should_stop ? StopHookResult::KeepStopped
+ : StopHookResult::RequestContinue;
}
void Target::StopHookScripted::GetSubclassDescription(