Index: include/lldb/Target/Process.h =================================================================== --- include/lldb/Target/Process.h +++ include/lldb/Target/Process.h @@ -2519,6 +2519,10 @@ /// //------------------------------------------------------------------ void RestoreProcessEvents(); + + bool StateChangedIsHijackedForSynchronousResume(); + + bool StateChangedIsExternallyHijacked(); const lldb::ABISP &GetABI(); @@ -3211,8 +3215,6 @@ virtual Status UpdateAutomaticSignalFiltering(); - bool StateChangedIsExternallyHijacked(); - void LoadOperatingSystemPlugin(bool flush); private: Index: include/lldb/Target/Target.h =================================================================== --- include/lldb/Target/Target.h +++ include/lldb/Target/Target.h @@ -1153,6 +1153,10 @@ void SetIsActive(bool is_active) { m_active = is_active; } + void SetAutoContinue(bool auto_continue) {m_auto_continue = auto_continue;} + + bool GetAutoContinue() const { return m_auto_continue; } + void GetDescription(Stream *s, lldb::DescriptionLevel level) const; private: @@ -1160,7 +1164,8 @@ StringList m_commands; lldb::SymbolContextSpecifierSP m_specifier_sp; std::unique_ptr m_thread_spec_up; - bool m_active; + bool m_active = true; + bool m_auto_continue = false; // Use CreateStopHook to make a new empty stop hook. The GetCommandPointer // and fill it with commands, and SetSpecifier to set the specifier shared Index: lit/ExecControl/StopHook/Inputs/stop-hook-2.lldbinit =================================================================== --- lit/ExecControl/StopHook/Inputs/stop-hook-2.lldbinit +++ lit/ExecControl/StopHook/Inputs/stop-hook-2.lldbinit @@ -1 +1 @@ -target stop-hook add -f stop-hook.c -l 29 -e 34 -o "expr ptr" \ No newline at end of file +target stop-hook add -f stop-hook.c -l 29 -e 34 -o "expr ptr" Index: lit/ExecControl/StopHook/Inputs/stop-hook-3.lldbinit =================================================================== --- lit/ExecControl/StopHook/Inputs/stop-hook-3.lldbinit +++ lit/ExecControl/StopHook/Inputs/stop-hook-3.lldbinit @@ -1,3 +1,3 @@ target stop-hook add -f stop-hook.c -l 29 -e 34 expr ptr -DONE \ No newline at end of file +DONE Index: lit/ExecControl/StopHook/Inputs/stop-hook-threads-1.lldbinit =================================================================== --- lit/ExecControl/StopHook/Inputs/stop-hook-threads-1.lldbinit +++ lit/ExecControl/StopHook/Inputs/stop-hook-threads-1.lldbinit @@ -1,7 +1,7 @@ +break set -f stop-hook-threads.cpp -p "Break here to set up the stop hook" break set -f stop-hook-threads.cpp -p "Break here to test that the stop-hook" run -target stop-hook add -frame variable --show-globals g_val +target stop-hook add -G true +expr lldb_val += 1 thread list -continue DONE Index: lit/ExecControl/StopHook/Inputs/stop-hook-threads-2.lldbinit =================================================================== --- lit/ExecControl/StopHook/Inputs/stop-hook-threads-2.lldbinit +++ lit/ExecControl/StopHook/Inputs/stop-hook-threads-2.lldbinit @@ -1,4 +1,5 @@ +break set -f stop-hook-threads.cpp -p "Break here to set up the stop hook" break set -f stop-hook-threads.cpp -p "Break here to test that the stop-hook" run -target stop-hook add -x 2 -o "frame variable thread_index" -target stop-hook add -o continue +target stop-hook add -x 2 -o "expr lldb_val += 1" -o "thread list" +target stop-hook add -G true -o "script print('Hit stop hook') Index: lit/ExecControl/StopHook/Inputs/stop-hook-threads.cpp =================================================================== --- lit/ExecControl/StopHook/Inputs/stop-hook-threads.cpp +++ lit/ExecControl/StopHook/Inputs/stop-hook-threads.cpp @@ -16,6 +16,7 @@ std::uniform_int_distribution<> g_distribution{0, 3000}; uint32_t g_val = 0; +uint32_t lldb_val = 0; uint32_t access_pool (bool flag = false) @@ -62,7 +63,8 @@ int main (int argc, char const *argv[]) { std::thread threads[3]; - + // Break here to set up the stop hook + printf("Stop hooks engaged.\n"); // Create 3 threads for (auto &thread : threads) thread = std::thread{thread_func, std::distance(threads, &thread)}; @@ -71,5 +73,7 @@ for (auto &thread : threads) thread.join(); + // print lldb_val so we can check it here. + printf ("lldb_val was set to: %d.\n", lldb_val); return 0; } Index: lit/ExecControl/StopHook/stop-hook-threads.test =================================================================== --- lit/ExecControl/StopHook/stop-hook-threads.test +++ lit/ExecControl/StopHook/stop-hook-threads.test @@ -4,7 +4,6 @@ # RUN: %lldb -b -s %p/Inputs/stop-hook-threads-2.lldbinit -s %s -f %t \ # RUN: | FileCheck --check-prefix=CHECK --check-prefix=CHECK-FILTER %s # XFAIL: system-windows -# UNSUPPORTED: linux thread list break set -f stop-hook-threads.cpp -p "Set break point at this line" @@ -12,23 +11,22 @@ # CHECK: Hook: 1 # CHECK-NEXT: State: enabled +# CHECK-NO-FILTER-NEXT: AutoContinue on # CHECK-FILTER-NEXT: Thread # CHECK-FILTER-NEXT: index: 2 # CHECK-NEXT: Commands: -# CHECK-NEXT: frame variable +# CHECK-NEXT: expr lldb_val += 1 +# CHECK-NEXT: thread list # CHECK-FILTER: Hook: 2 # CHECK-FILTER-NEXT: State: enabled +# CHECK-FILTER-NEXT: AutoContinue on # CHECK-FILTER-NEXT: Commands: -# CHECK-FILTER-NEXT: continue +# CHECK-FILTER-NEXT: script print('Hit stop hook') # Get the threads going continue -# When we filter per thread, we expect exactly 4 identical "frame var" results -# CHECK-FILTER: (uint32_t) thread_index = [[THREAD_INDEX:[0-9]*]] -# CHECK-FILTER-COUNT-3: (uint32_t) thread_index = [[THREAD_INDEX]] -# CHECK-FILTER-NOT: thread_index - -# When we don't filter, we expect to count 12 stopped threads in the thread list output -# CHECK-NO-FILTER-COUNT-12: at stop-hook-threads.cpp{{.*}} stop reason = breakpoint \ No newline at end of file +# Now make sure we hit the command the right number of times: +# CHECK-NO-FILTER: lldb_val was set to: 15. +# CHECK-FILTER: lldb_val was set to: 5. Index: lldb.xcodeproj/xcshareddata/xcschemes/lldb-gtest.xcscheme =================================================================== --- lldb.xcodeproj/xcshareddata/xcschemes/lldb-gtest.xcscheme +++ lldb.xcodeproj/xcshareddata/xcschemes/lldb-gtest.xcscheme @@ -42,7 +42,7 @@ m_one_liner; + bool m_auto_continue; }; CommandObjectTargetStopHookAdd(CommandInterpreter &interpreter) @@ -4833,10 +4847,13 @@ new_hook_sp->SetThreadSpecifier(thread_spec); } + + new_hook_sp->SetAutoContinue(m_options.m_auto_continue); if (m_options.m_use_one_liner) { - // Use one-liner. - new_hook_sp->GetCommandPointer()->AppendString( - m_options.m_one_liner.c_str()); + // Use one-liners. + for (auto cmd : m_options.m_one_liner) + new_hook_sp->GetCommandPointer()->AppendString( + cmd.c_str()); result.AppendMessageWithFormat("Stop hook #%" PRIu64 " added.\n", new_hook_sp->GetID()); } else { Index: source/Target/Process.cpp =================================================================== --- source/Target/Process.cpp +++ source/Target/Process.cpp @@ -1552,16 +1552,6 @@ return m_public_state.GetValue(); } -bool Process::StateChangedIsExternallyHijacked() { - if (IsHijackedForEvent(eBroadcastBitStateChanged)) { - const char *hijacking_name = GetHijackingListenerName(); - if (hijacking_name && - strcmp(hijacking_name, "lldb.Process.ResumeSynchronous.hijack")) - return true; - } - return false; -} - void Process::SetPublicState(StateType new_state, bool restarted) { Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STATE | LIBLLDB_LOG_PROCESS)); @@ -1615,6 +1605,8 @@ return error; } +static const char *g_resume_sync_name = "lldb.Process.ResumeSynchronous.hijack"; + Status Process::ResumeSynchronous(Stream *stream) { Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STATE | LIBLLDB_LOG_PROCESS)); @@ -1628,7 +1620,7 @@ } ListenerSP listener_sp( - Listener::MakeListener("lldb.Process.ResumeSynchronous.hijack")); + Listener::MakeListener(g_resume_sync_name)); HijackProcessEvents(listener_sp); Status error = PrivateResume(); @@ -1652,6 +1644,26 @@ return error; } +bool Process::StateChangedIsExternallyHijacked() { + if (IsHijackedForEvent(eBroadcastBitStateChanged)) { + const char *hijacking_name = GetHijackingListenerName(); + if (hijacking_name && + strcmp(hijacking_name, g_resume_sync_name)) + return true; + } + return false; +} + +bool Process::StateChangedIsHijackedForSynchronousResume() { + if (IsHijackedForEvent(eBroadcastBitStateChanged)) { + const char *hijacking_name = GetHijackingListenerName(); + if (hijacking_name && + strcmp(hijacking_name, g_resume_sync_name) == 0) + return true; + } + return false; +} + StateType Process::GetPrivateState() { return m_private_state.GetValue(); } void Process::SetPrivateState(StateType new_state) { @@ -4260,11 +4272,20 @@ // public resume. process_sp->PrivateResume(); } else { - // If we didn't restart, run the Stop Hooks here: They might also - // restart the target, so watch for that. - process_sp->GetTarget().RunStopHooks(); - if (process_sp->GetPrivateState() == eStateRunning) - SetRestarted(true); + bool hijacked = + process_sp->IsHijackedForEvent(eBroadcastBitStateChanged) + && !process_sp->StateChangedIsHijackedForSynchronousResume(); + + if (!hijacked) { + // If we didn't restart, run the Stop Hooks here. + // Don't do that if state changed events aren't hooked up to the + // public (or SyncResume) broadcasters. StopHooks are just for + // real public stops. They might also restart the target, + // so watch for that. + process_sp->GetTarget().RunStopHooks(); + if (process_sp->GetPrivateState() == eStateRunning) + SetRestarted(true); + } } } } Index: source/Target/Target.cpp =================================================================== --- source/Target/Target.cpp +++ source/Target/Target.cpp @@ -2554,12 +2554,14 @@ StopHookCollection::iterator pos, end = m_stop_hooks.end(); - // If there aren't any active stop hooks, don't bother either: + // If there aren't any active stop hooks, don't bother either. + // Also see if any of the active hooks want to auto-continue. bool any_active_hooks = false; - for (pos = m_stop_hooks.begin(); pos != end; pos++) { - if ((*pos).second->IsActive()) { + bool auto_continue = false; + for (auto hook : m_stop_hooks) { + if (hook.second->IsActive()) { any_active_hooks = true; - break; + auto_continue |= hook.second->GetAutoContinue(); } } if (!any_active_hooks) @@ -2595,6 +2597,7 @@ bool hooks_ran = false; bool print_hook_header = (m_stop_hooks.size() != 1); bool print_thread_header = (num_exe_ctx != 1); + bool did_restart = false; for (pos = m_stop_hooks.begin(); keep_going && pos != end; pos++) { // result.Clear(); @@ -2639,10 +2642,13 @@ options.SetPrintResults(true); options.SetAddToHistory(false); + // Force Async: + bool old_async = GetDebugger().GetAsyncExecution(); + GetDebugger().SetAsyncExecution(true); GetDebugger().GetCommandInterpreter().HandleCommands( cur_hook_sp->GetCommands(), &exc_ctx_with_reasons[i], options, result); - + GetDebugger().SetAsyncExecution(old_async); // If the command started the target going again, we should bag out of // running the stop hooks. if ((result.GetStatus() == eReturnStatusSuccessContinuingNoResult) || @@ -2651,13 +2657,19 @@ StopHookCollection::iterator tmp = pos; if (++tmp != end) result.AppendMessageWithFormat("\nAborting stop hooks, hook %" PRIu64 - " set the program running.\n", + " set the program running.\n" + " Consider using '-G true' to make " + "stop hooks auto-continue.\n", cur_hook_sp->GetID()); keep_going = false; + did_restart = true; } } } } + // Finally, if auto-continue was requested, do it now: + if (!did_restart && auto_continue) + m_process_sp->PrivateResume(); result.GetImmediateOutputStream()->Flush(); result.GetImmediateErrorStream()->Flush(); @@ -2914,17 +2926,11 @@ if (state == eStateStopped) { if (!launch_info.GetFlags().Test(eLaunchFlagStopAtEntry)) { if (synchronous_execution) { - error = m_process_sp->PrivateResume(); - if (error.Success()) { - state = m_process_sp->WaitForProcessToStop( - llvm::None, nullptr, true, hijack_listener_sp, stream); - const bool must_be_alive = - false; // eStateExited is ok, so this must be false - if (!StateIsStoppedState(state, must_be_alive)) { - error.SetErrorStringWithFormat("process isn't stopped: %s", - StateAsCString(state)); - } - } + // Now we have handled the stop-from-attach, and we are just switching + // to a synchronous resume. So we should switch to the SyncResume + // hijacker. + m_process_sp->RestoreProcessEvents(); + m_process_sp->ResumeSynchronous(stream); } else { m_process_sp->RestoreProcessEvents(); error = m_process_sp->PrivateResume(); @@ -3143,12 +3149,13 @@ //-------------------------------------------------------------- Target::StopHook::StopHook(lldb::TargetSP target_sp, lldb::user_id_t uid) : UserID(uid), m_target_sp(target_sp), m_commands(), m_specifier_sp(), - m_thread_spec_up(), m_active(true) {} + m_thread_spec_up() {} Target::StopHook::StopHook(const StopHook &rhs) : UserID(rhs.GetID()), m_target_sp(rhs.m_target_sp), m_commands(rhs.m_commands), m_specifier_sp(rhs.m_specifier_sp), - m_thread_spec_up(), m_active(rhs.m_active) { + m_thread_spec_up(), m_active(rhs.m_active), + m_auto_continue(rhs.m_auto_continue) { if (rhs.m_thread_spec_up) m_thread_spec_up.reset(new ThreadSpec(*rhs.m_thread_spec_up)); } @@ -3175,6 +3182,9 @@ else s->Indent("State: disabled\n"); + if (m_auto_continue) + s->Indent("AutoContinue on\n"); + if (m_specifier_sp) { s->Indent(); s->PutCString("Specifier:\n");