From dc6224e0a3ce2b097f0a631de9ae1960f175dfe6 Mon Sep 17 00:00:00 2001 From: Greg Clayton Date: Tue, 21 Oct 2014 01:00:42 +0000 Subject: [PATCH] Make the "synchronous" mode actually work without race conditions. There were many issues with synchronous mode that we discovered when started to try and add a "batch" mode. There was a race condition where the event handling thread might consume events when in sync mode and other times the Process::WaitForProcessToStop() would consume them. This also led to places where the Process IO handler might or might not get popped when it needed to be. llvm-svn: 220254 --- lldb/include/lldb/Target/Process.h | 35 ++- lldb/include/lldb/Target/Target.h | 3 +- lldb/source/API/SBProcess.cpp | 16 +- lldb/source/API/SBTarget.cpp | 8 +- lldb/source/API/SBThread.cpp | 14 +- lldb/source/Commands/CommandObjectProcess.cpp | 26 ++- lldb/source/Commands/CommandObjectThread.cpp | 52 +++-- lldb/source/Core/Debugger.cpp | 180 +-------------- lldb/source/Interpreter/CommandInterpreter.cpp | 14 -- lldb/source/Target/Process.cpp | 241 ++++++++++++++++++++- lldb/source/Target/StopInfo.cpp | 7 +- lldb/source/Target/Target.cpp | 6 +- .../api/multithreaded/test_breakpoint_callback.cpp | 1 + 13 files changed, 355 insertions(+), 248 deletions(-) diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h index c7b7449..9a959bf 100644 --- a/lldb/include/lldb/Target/Process.h +++ b/lldb/include/lldb/Target/Process.h @@ -1273,7 +1273,9 @@ public: //------------------------------------------------------------------ Error Resume(); - + + Error + ResumeSynchronous (Stream *stream); //------------------------------------------------------------------ /// Halts a running process. /// @@ -2680,7 +2682,8 @@ public: WaitForProcessToStop (const TimeValue *timeout, lldb::EventSP *event_sp_ptr = NULL, bool wait_always = true, - Listener *hijack_listener = NULL); + Listener *hijack_listener = NULL, + Stream *stream = NULL); //-------------------------------------------------------------------------------------- @@ -2705,7 +2708,28 @@ public: WaitForStateChangedEvents (const TimeValue *timeout, lldb::EventSP &event_sp, Listener *hijack_listener); // Pass NULL to use builtin listener - + + //-------------------------------------------------------------------------------------- + /// Centralize the code that handles and prints descriptions for process state changes. + /// + /// @param[in] event_sp + /// The process state changed event + /// + /// @param[in] stream + /// The output stream to get the state change description + /// + /// @param[inout] pop_process_io_handler + /// If this value comes in set to \b true, then pop the Process IOHandler if needed. + /// Else this variable will be set to \b true or \b false to indicate if the process + /// needs to have its process IOHandler popped. + /// + /// @return + /// \b true if the event describes a process state changed event, \b false otherwise. + //-------------------------------------------------------------------------------------- + static bool + HandleProcessStateChangedEvent (const lldb::EventSP &event_sp, + Stream *stream, + bool &pop_process_io_handler); Event * PeekAtStateChangedEvents (); @@ -3183,7 +3207,10 @@ protected: Error HaltForDestroyOrDetach(lldb::EventSP &exit_event_sp); - + + bool + StateChangedIsExternallyHijacked(); + private: //------------------------------------------------------------------ // For Process only diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h index 64f3edf..0ecabb7 100644 --- a/lldb/include/lldb/Target/Target.h +++ b/lldb/include/lldb/Target/Target.h @@ -608,7 +608,8 @@ public: Error Launch (Listener &listener, - ProcessLaunchInfo &launch_info); + ProcessLaunchInfo &launch_info, + Stream *stream); // Optional stream to receive first stop info //------------------------------------------------------------------ // This part handles the breakpoints. diff --git a/lldb/source/API/SBProcess.cpp b/lldb/source/API/SBProcess.cpp index 5231b4b..9a0b23b 100644 --- a/lldb/source/API/SBProcess.cpp +++ b/lldb/source/API/SBProcess.cpp @@ -739,18 +739,10 @@ SBProcess::Continue () { Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); - Error error (process_sp->Resume()); - if (error.Success()) - { - if (process_sp->GetTarget().GetDebugger().GetAsyncExecution () == false) - { - if (log) - log->Printf ("SBProcess(%p)::Continue () waiting for process to stop...", - static_cast(process_sp.get())); - process_sp->WaitForProcessToStop (NULL); - } - } - sb_error.SetError(error); + if (process_sp->GetTarget().GetDebugger().GetAsyncExecution ()) + sb_error.ref() = process_sp->Resume (); + else + sb_error.ref() = process_sp->ResumeSynchronous (NULL); } else sb_error.SetErrorString ("SBProcess is invalid"); diff --git a/lldb/source/API/SBTarget.cpp b/lldb/source/API/SBTarget.cpp index 4080bd5..fb88036 100644 --- a/lldb/source/API/SBTarget.cpp +++ b/lldb/source/API/SBTarget.cpp @@ -738,9 +738,9 @@ SBTarget::Launch launch_info.GetEnvironmentEntries ().SetArguments (envp); if (listener.IsValid()) - error.SetError (target_sp->Launch(listener.ref(), launch_info)); + error.SetError (target_sp->Launch(listener.ref(), launch_info, NULL)); else - error.SetError (target_sp->Launch(target_sp->GetDebugger().GetListener(), launch_info)); + error.SetError (target_sp->Launch(target_sp->GetDebugger().GetListener(), launch_info, NULL)); sb_process.SetSP(target_sp->GetProcessSP()); } @@ -804,7 +804,7 @@ SBTarget::Launch (SBLaunchInfo &sb_launch_info, SBError& error) if (arch_spec.IsValid()) launch_info.GetArchitecture () = arch_spec; - error.SetError (target_sp->Launch (target_sp->GetDebugger().GetListener(), launch_info)); + error.SetError (target_sp->Launch (target_sp->GetDebugger().GetListener(), launch_info, NULL)); sb_process.SetSP(target_sp->GetProcessSP()); } else @@ -1004,7 +1004,7 @@ SBTarget::AttachToProcessWithID // If we are doing synchronous mode, then wait for the // process to stop! if (target_sp->GetDebugger().GetAsyncExecution () == false) - process_sp->WaitForProcessToStop (NULL); + process_sp->WaitForProcessToStop (NULL); } } else diff --git a/lldb/source/API/SBThread.cpp b/lldb/source/API/SBThread.cpp index 036ce10..6524d10 100644 --- a/lldb/source/API/SBThread.cpp +++ b/lldb/source/API/SBThread.cpp @@ -710,15 +710,11 @@ SBThread::ResumeNewPlan (ExecutionContext &exe_ctx, ThreadPlan *new_plan) // Why do we need to set the current thread by ID here??? process->GetThreadList().SetSelectedThreadByID (thread->GetID()); - sb_error.ref() = process->Resume(); - - if (sb_error.Success()) - { - // If we are doing synchronous mode, then wait for the - // process to stop yet again! - if (process->GetTarget().GetDebugger().GetAsyncExecution () == false) - process->WaitForProcessToStop (NULL); - } + + if (process->GetTarget().GetDebugger().GetAsyncExecution ()) + sb_error.ref() = process->Resume (); + else + sb_error.ref() = process->ResumeSynchronous (NULL); return sb_error; } diff --git a/lldb/source/Commands/CommandObjectProcess.cpp b/lldb/source/Commands/CommandObjectProcess.cpp index 6536c6e..b5ca44f 100644 --- a/lldb/source/Commands/CommandObjectProcess.cpp +++ b/lldb/source/Commands/CommandObjectProcess.cpp @@ -258,8 +258,9 @@ protected: // Save the arguments for subsequent runs in the current target. target->SetRunArguments (launch_args); } - - Error error = target->Launch(debugger.GetListener(), m_options.launch_info); + + StreamString stream; + Error error = target->Launch(debugger.GetListener(), m_options.launch_info, &stream); if (error.Success()) { @@ -267,6 +268,8 @@ protected: ProcessSP process_sp (target->GetProcessSP()); if (process_sp) { + if (stream.GetData()) + result.AppendMessage(stream.GetData()); result.AppendMessageWithFormat ("Process %" PRIu64 " launched: '%s' (%s)\n", process_sp->GetID(), exe_module_sp->GetFileSpec().GetPath().c_str(), archname); result.SetStatus (eReturnStatusSuccessFinishResult); result.SetDidChangeProcessState (true); @@ -564,15 +567,18 @@ protected: if (error.Success()) { result.SetStatus (eReturnStatusSuccessContinuingNoResult); - StateType state = process->WaitForProcessToStop (NULL, NULL, false, listener_sp.get()); + StreamString stream; + StateType state = process->WaitForProcessToStop (NULL, NULL, false, listener_sp.get(), &stream); process->RestoreProcessEvents(); result.SetDidChangeProcessState (true); + if (stream.GetData()) + result.AppendMessage(stream.GetData()); + if (state == eStateStopped) { - result.AppendMessageWithFormat ("Process %" PRIu64 " %s\n", process->GetID(), StateAsCString (state)); result.SetStatus (eReturnStatusSuccessFinishNoResult); } else @@ -791,7 +797,12 @@ protected: } } - Error error(process->Resume()); + StreamString stream; + Error error; + if (synchronous_execution) + error = process->ResumeSynchronous (&stream); + else + error = process->Resume (); if (error.Success()) { @@ -803,10 +814,11 @@ protected: result.AppendMessageWithFormat ("Process %" PRIu64 " resuming\n", process->GetID()); if (synchronous_execution) { - state = process->WaitForProcessToStop (NULL); + // If any state changed events had anything to say, add that to the result + if (stream.GetData()) + result.AppendMessage(stream.GetData()); result.SetDidChangeProcessState (true); - result.AppendMessageWithFormat ("Process %" PRIu64 " %s\n", process->GetID(), StateAsCString (state)); result.SetStatus (eReturnStatusSuccessFinishNoResult); } else diff --git a/lldb/source/Commands/CommandObjectThread.cpp b/lldb/source/Commands/CommandObjectThread.cpp index fbb233a..cc97de2 100644 --- a/lldb/source/Commands/CommandObjectThread.cpp +++ b/lldb/source/Commands/CommandObjectThread.cpp @@ -663,8 +663,15 @@ protected: } } + process->GetThreadList().SetSelectedThreadByID (thread->GetID()); - process->Resume (); + + StreamString stream; + Error error; + if (synchronous_execution) + error = process->ResumeSynchronous (&stream); + else + error = process->Resume (); // There is a race condition where this thread will return up the call stack to the main command handler // and show an (lldb) prompt before HandlePrivateEvent (from PrivateStateThread) has @@ -673,17 +680,12 @@ protected: if (synchronous_execution) { - StateType state = process->WaitForProcessToStop (NULL); - - //EventSP event_sp; - //StateType state = process->WaitForStateChangedEvents (NULL, event_sp); - //while (! StateIsStoppedState (state)) - // { - // state = process->WaitForStateChangedEvents (NULL, event_sp); - // } + // If any state changed events had anything to say, add that to the result + if (stream.GetData()) + result.AppendMessage(stream.GetData()); + process->GetThreadList().SetSelectedThreadByID (thread->GetID()); result.SetDidChangeProcessState (true); - result.AppendMessageWithFormat ("Process %" PRIu64 " %s\n", process->GetID(), StateAsCString (state)); result.SetStatus (eReturnStatusSuccessFinishNoResult); } else @@ -902,17 +904,25 @@ public: } } + + StreamString stream; + Error error; + if (synchronous_execution) + error = process->ResumeSynchronous (&stream); + else + error = process->Resume (); + // We should not be holding the thread list lock when we do this. - Error error (process->Resume()); if (error.Success()) { result.AppendMessageWithFormat ("Process %" PRIu64 " resuming\n", process->GetID()); if (synchronous_execution) { - state = process->WaitForProcessToStop (NULL); + // If any state changed events had anything to say, add that to the result + if (stream.GetData()) + result.AppendMessage(stream.GetData()); result.SetDidChangeProcessState (true); - result.AppendMessageWithFormat ("Process %" PRIu64 " %s\n", process->GetID(), StateAsCString (state)); result.SetStatus (eReturnStatusSuccessFinishNoResult); } else @@ -1233,17 +1243,27 @@ protected: } + + process->GetThreadList().SetSelectedThreadByID (m_options.m_thread_idx); - Error error (process->Resume ()); + + StreamString stream; + Error error; + if (synchronous_execution) + error = process->ResumeSynchronous (&stream); + else + error = process->Resume (); + if (error.Success()) { result.AppendMessageWithFormat ("Process %" PRIu64 " resuming\n", process->GetID()); if (synchronous_execution) { - StateType state = process->WaitForProcessToStop (NULL); + // If any state changed events had anything to say, add that to the result + if (stream.GetData()) + result.AppendMessage(stream.GetData()); result.SetDidChangeProcessState (true); - result.AppendMessageWithFormat ("Process %" PRIu64 " %s\n", process->GetID(), StateAsCString (state)); result.SetStatus (eReturnStatusSuccessFinishNoResult); } else diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp index 70038d3..db42772 100644 --- a/lldb/source/Core/Debugger.cpp +++ b/lldb/source/Core/Debugger.cpp @@ -3097,6 +3097,7 @@ Debugger::GetProcessSTDERR (Process *process, Stream *stream) return total_bytes; } + // This function handles events that were broadcast by the process. void Debugger::HandleProcessEvent (const EventSP &event_sp) @@ -3104,7 +3105,7 @@ Debugger::HandleProcessEvent (const EventSP &event_sp) using namespace lldb; const uint32_t event_type = event_sp->GetType(); ProcessSP process_sp = Process::ProcessEventData::GetProcessFromEvent(event_sp.get()); - + StreamString output_stream; StreamString error_stream; const bool gui_enabled = IsForwardingEvents(); @@ -3113,192 +3114,27 @@ Debugger::HandleProcessEvent (const EventSP &event_sp) { bool pop_process_io_handler = false; assert (process_sp); - + if (event_type & Process::eBroadcastBitSTDOUT || event_type & Process::eBroadcastBitStateChanged) { GetProcessSTDOUT (process_sp.get(), &output_stream); } - + if (event_type & Process::eBroadcastBitSTDERR || event_type & Process::eBroadcastBitStateChanged) { GetProcessSTDERR (process_sp.get(), &error_stream); } - + if (event_type & Process::eBroadcastBitStateChanged) { - - // Drain all stout and stderr so we don't see any output come after - // we print our prompts - // Something changed in the process; get the event and report the process's current status and location to - // the user. - StateType event_state = Process::ProcessEventData::GetStateFromEvent (event_sp.get()); - if (event_state == eStateInvalid) - return; - - switch (event_state) - { - case eStateInvalid: - case eStateUnloaded: - case eStateConnected: - case eStateAttaching: - case eStateLaunching: - case eStateStepping: - case eStateDetached: - { - output_stream.Printf("Process %" PRIu64 " %s\n", - process_sp->GetID(), - StateAsCString (event_state)); - - if (event_state == eStateDetached) - pop_process_io_handler = true; - } - break; - - case eStateRunning: - // Don't be chatty when we run... - break; - - case eStateExited: - process_sp->GetStatus(output_stream); - pop_process_io_handler = true; - break; - - case eStateStopped: - case eStateCrashed: - case eStateSuspended: - // Make sure the program hasn't been auto-restarted: - if (Process::ProcessEventData::GetRestartedFromEvent (event_sp.get())) - { - size_t num_reasons = Process::ProcessEventData::GetNumRestartedReasons(event_sp.get()); - if (num_reasons > 0) - { - // FIXME: Do we want to report this, or would that just be annoyingly chatty? - if (num_reasons == 1) - { - const char *reason = Process::ProcessEventData::GetRestartedReasonAtIndex (event_sp.get(), 0); - output_stream.Printf("Process %" PRIu64 " stopped and restarted: %s\n", - process_sp->GetID(), - reason ? reason : ""); - } - else - { - output_stream.Printf("Process %" PRIu64 " stopped and restarted, reasons:\n", - process_sp->GetID()); - - - for (size_t i = 0; i < num_reasons; i++) - { - const char *reason = Process::ProcessEventData::GetRestartedReasonAtIndex (event_sp.get(), i); - output_stream.Printf("\t%s\n", reason ? reason : ""); - } - } - } - } - else - { - // Lock the thread list so it doesn't change on us, this is the scope for the locker: - { - ThreadList &thread_list = process_sp->GetThreadList(); - Mutex::Locker locker (thread_list.GetMutex()); - - ThreadSP curr_thread (thread_list.GetSelectedThread()); - ThreadSP thread; - StopReason curr_thread_stop_reason = eStopReasonInvalid; - if (curr_thread) - curr_thread_stop_reason = curr_thread->GetStopReason(); - if (!curr_thread || - !curr_thread->IsValid() || - curr_thread_stop_reason == eStopReasonInvalid || - curr_thread_stop_reason == eStopReasonNone) - { - // Prefer a thread that has just completed its plan over another thread as current thread. - ThreadSP plan_thread; - ThreadSP other_thread; - const size_t num_threads = thread_list.GetSize(); - size_t i; - for (i = 0; i < num_threads; ++i) - { - thread = thread_list.GetThreadAtIndex(i); - StopReason thread_stop_reason = thread->GetStopReason(); - switch (thread_stop_reason) - { - case eStopReasonInvalid: - case eStopReasonNone: - break; - - case eStopReasonTrace: - case eStopReasonBreakpoint: - case eStopReasonWatchpoint: - case eStopReasonSignal: - case eStopReasonException: - case eStopReasonExec: - case eStopReasonThreadExiting: - case eStopReasonInstrumentation: - if (!other_thread) - other_thread = thread; - break; - case eStopReasonPlanComplete: - if (!plan_thread) - plan_thread = thread; - break; - } - } - if (plan_thread) - thread_list.SetSelectedThreadByID (plan_thread->GetID()); - else if (other_thread) - thread_list.SetSelectedThreadByID (other_thread->GetID()); - else - { - if (curr_thread && curr_thread->IsValid()) - thread = curr_thread; - else - thread = thread_list.GetThreadAtIndex(0); - - if (thread) - thread_list.SetSelectedThreadByID (thread->GetID()); - } - } - } - // Drop the ThreadList mutex by here, since GetThreadStatus below might have to run code, - // e.g. for Data formatters, and if we hold the ThreadList mutex, then the process is going to - // have a hard time restarting the process. - - if (GetTargetList().GetSelectedTarget().get() == &process_sp->GetTarget()) - { - const bool only_threads_with_stop_reason = true; - const uint32_t start_frame = 0; - const uint32_t num_frames = 1; - const uint32_t num_frames_with_source = 1; - process_sp->GetStatus(output_stream); - process_sp->GetThreadStatus (output_stream, - only_threads_with_stop_reason, - start_frame, - num_frames, - num_frames_with_source); - } - else - { - uint32_t target_idx = GetTargetList().GetIndexOfTarget(process_sp->GetTarget().shared_from_this()); - if (target_idx != UINT32_MAX) - output_stream.Printf ("Target %d: (", target_idx); - else - output_stream.Printf ("Target : ("); - process_sp->GetTarget().Dump (&output_stream, eDescriptionLevelBrief); - output_stream.Printf (") stopped.\n"); - } - - // Pop the process IO handler - pop_process_io_handler = true; - } - break; - } + Process::HandleProcessStateChangedEvent (event_sp, &output_stream, pop_process_io_handler); } - + if (output_stream.GetSize() || error_stream.GetSize()) { StreamFileSP error_stream_sp (GetOutputFile()); bool top_io_handler_hid = false; - + if (process_sp->ProcessIOHandlerIsActive() == false) top_io_handler_hid = HideTopIOHandler(); diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp index 9097ab0..8e1d080 100644 --- a/lldb/source/Interpreter/CommandInterpreter.cpp +++ b/lldb/source/Interpreter/CommandInterpreter.cpp @@ -3141,20 +3141,6 @@ CommandInterpreter::IOHandlerInputComplete (IOHandler &io_handler, std::string & StopReason reason = thread_sp->GetStopReason(); if (reason == eStopReasonSignal || reason == eStopReasonException || reason == eStopReasonInstrumentation) { - // If we are printing results, we ought to show the resaon why we are stopping here: - if (io_handler.GetFlags().Test(eHandleCommandFlagPrintResult)) - { - if (!result.GetImmediateOutputStream()) - { - const uint32_t start_frame = 0; - const uint32_t num_frames = 1; - const uint32_t num_frames_with_source = 1; - thread_sp->GetStatus (*io_handler.GetOutputStreamFile().get(), - start_frame, - num_frames, - num_frames_with_source); - } - } should_stop = true; break; } diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp index 9c20973..7bf75a6 100644 --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -951,7 +951,11 @@ Process::SyncIOHandler (uint64_t timeout_msec) } StateType -Process::WaitForProcessToStop (const TimeValue *timeout, lldb::EventSP *event_sp_ptr, bool wait_always, Listener *hijack_listener) +Process::WaitForProcessToStop (const TimeValue *timeout, + EventSP *event_sp_ptr, + bool wait_always, + Listener *hijack_listener, + Stream *stream) { // We can't just wait for a "stopped" event, because the stopped event may have restarted the target. // We have to actually check each event, and in the case of a stopped event check the restarted flag @@ -985,6 +989,9 @@ Process::WaitForProcessToStop (const TimeValue *timeout, lldb::EventSP *event_sp if (event_sp_ptr && event_sp) *event_sp_ptr = event_sp; + bool pop_process_io_handler = hijack_listener != NULL; + Process::HandleProcessStateChangedEvent (event_sp, stream, pop_process_io_handler); + switch (state) { case eStateCrashed: @@ -1014,6 +1021,195 @@ Process::WaitForProcessToStop (const TimeValue *timeout, lldb::EventSP *event_sp return state; } +bool +Process::HandleProcessStateChangedEvent (const EventSP &event_sp, + Stream *stream, + bool &pop_process_io_handler) +{ + const bool handle_pop = pop_process_io_handler == true; + + pop_process_io_handler = false; + ProcessSP process_sp = Process::ProcessEventData::GetProcessFromEvent(event_sp.get()); + + if (!process_sp) + return false; + + StateType event_state = Process::ProcessEventData::GetStateFromEvent (event_sp.get()); + if (event_state == eStateInvalid) + return false; + + switch (event_state) + { + case eStateInvalid: + case eStateUnloaded: + case eStateConnected: + case eStateAttaching: + case eStateLaunching: + case eStateStepping: + case eStateDetached: + { + if (stream) + stream->Printf ("Process %" PRIu64 " %s\n", + process_sp->GetID(), + StateAsCString (event_state)); + + if (event_state == eStateDetached) + pop_process_io_handler = true; + } + break; + + case eStateRunning: + // Don't be chatty when we run... + break; + + case eStateExited: + if (stream) + process_sp->GetStatus(*stream); + pop_process_io_handler = true; + break; + + case eStateStopped: + case eStateCrashed: + case eStateSuspended: + // Make sure the program hasn't been auto-restarted: + if (Process::ProcessEventData::GetRestartedFromEvent (event_sp.get())) + { + if (stream) + { + size_t num_reasons = Process::ProcessEventData::GetNumRestartedReasons(event_sp.get()); + if (num_reasons > 0) + { + // FIXME: Do we want to report this, or would that just be annoyingly chatty? + if (num_reasons == 1) + { + const char *reason = Process::ProcessEventData::GetRestartedReasonAtIndex (event_sp.get(), 0); + stream->Printf ("Process %" PRIu64 " stopped and restarted: %s\n", + process_sp->GetID(), + reason ? reason : ""); + } + else + { + stream->Printf ("Process %" PRIu64 " stopped and restarted, reasons:\n", + process_sp->GetID()); + + + for (size_t i = 0; i < num_reasons; i++) + { + const char *reason = Process::ProcessEventData::GetRestartedReasonAtIndex (event_sp.get(), i); + stream->Printf("\t%s\n", reason ? reason : ""); + } + } + } + } + } + else + { + // Lock the thread list so it doesn't change on us, this is the scope for the locker: + { + ThreadList &thread_list = process_sp->GetThreadList(); + Mutex::Locker locker (thread_list.GetMutex()); + + ThreadSP curr_thread (thread_list.GetSelectedThread()); + ThreadSP thread; + StopReason curr_thread_stop_reason = eStopReasonInvalid; + if (curr_thread) + curr_thread_stop_reason = curr_thread->GetStopReason(); + if (!curr_thread || + !curr_thread->IsValid() || + curr_thread_stop_reason == eStopReasonInvalid || + curr_thread_stop_reason == eStopReasonNone) + { + // Prefer a thread that has just completed its plan over another thread as current thread. + ThreadSP plan_thread; + ThreadSP other_thread; + const size_t num_threads = thread_list.GetSize(); + size_t i; + for (i = 0; i < num_threads; ++i) + { + thread = thread_list.GetThreadAtIndex(i); + StopReason thread_stop_reason = thread->GetStopReason(); + switch (thread_stop_reason) + { + case eStopReasonInvalid: + case eStopReasonNone: + break; + + case eStopReasonTrace: + case eStopReasonBreakpoint: + case eStopReasonWatchpoint: + case eStopReasonSignal: + case eStopReasonException: + case eStopReasonExec: + case eStopReasonThreadExiting: + case eStopReasonInstrumentation: + if (!other_thread) + other_thread = thread; + break; + case eStopReasonPlanComplete: + if (!plan_thread) + plan_thread = thread; + break; + } + } + if (plan_thread) + thread_list.SetSelectedThreadByID (plan_thread->GetID()); + else if (other_thread) + thread_list.SetSelectedThreadByID (other_thread->GetID()); + else + { + if (curr_thread && curr_thread->IsValid()) + thread = curr_thread; + else + thread = thread_list.GetThreadAtIndex(0); + + if (thread) + thread_list.SetSelectedThreadByID (thread->GetID()); + } + } + } + // Drop the ThreadList mutex by here, since GetThreadStatus below might have to run code, + // e.g. for Data formatters, and if we hold the ThreadList mutex, then the process is going to + // have a hard time restarting the process. + if (stream) + { + Debugger &debugger = process_sp->GetTarget().GetDebugger(); + if (debugger.GetTargetList().GetSelectedTarget().get() == &process_sp->GetTarget()) + { + const bool only_threads_with_stop_reason = true; + const uint32_t start_frame = 0; + const uint32_t num_frames = 1; + const uint32_t num_frames_with_source = 1; + process_sp->GetStatus(*stream); + process_sp->GetThreadStatus (*stream, + only_threads_with_stop_reason, + start_frame, + num_frames, + num_frames_with_source); + } + else + { + uint32_t target_idx = debugger.GetTargetList().GetIndexOfTarget(process_sp->GetTarget().shared_from_this()); + if (target_idx != UINT32_MAX) + stream->Printf ("Target %d: (", target_idx); + else + stream->Printf ("Target : ("); + process_sp->GetTarget().Dump (stream, eDescriptionLevelBrief); + stream->Printf (") stopped.\n"); + } + } + + // Pop the process IO handler + pop_process_io_handler = true; + } + break; + } + + if (handle_pop && pop_process_io_handler) + process_sp->PopProcessIOHandler(); + + return true; +} + StateType Process::WaitForState @@ -1420,6 +1616,17 @@ Process::GetState() return m_public_state.GetValue (); } +bool +Process::StateChangedIsExternallyHijacked() +{ + if (IsHijackedForEvent(eBroadcastBitStateChanged)) + { + if (strcmp(m_hijacking_listeners.back()->GetName(), "lldb.Process.ResumeSynchronous.hijack")) + return true; + } + return false; +} + void Process::SetPublicState (StateType new_state, bool restarted) { @@ -1432,7 +1639,7 @@ Process::SetPublicState (StateType new_state, bool restarted) // On the transition from Run to Stopped, we unlock the writer end of the // run lock. The lock gets locked in Resume, which is the public API // to tell the program to run. - if (!IsHijackedForEvent(eBroadcastBitStateChanged)) + if (!StateChangedIsExternallyHijacked()) { if (new_state == eStateDetached) { @@ -1473,6 +1680,36 @@ Process::Resume () return PrivateResume(); } +Error +Process::ResumeSynchronous (Stream *stream) +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_STATE | LIBLLDB_LOG_PROCESS)); + if (log) + log->Printf("Process::ResumeSynchronous -- locking run lock"); + if (!m_public_run_lock.TrySetRunning()) + { + Error error("Resume request failed - process still running."); + if (log) + log->Printf ("Process::Resume: -- TrySetRunning failed, not resuming."); + return error; + } + + ListenerSP listener_sp (new Listener("lldb.Process.ResumeSynchronous.hijack")); + HijackProcessEvents(listener_sp.get()); + + Error error = PrivateResume(); + + StateType state = WaitForProcessToStop (NULL, NULL, true, listener_sp.get(), stream); + + // Undo the hijacking of process events... + RestoreProcessEvents(); + + if (error.Success() && !StateIsStoppedState(state, false)) + error.SetErrorStringWithFormat("process not in stopped state after synchronous resume: %s", StateAsCString(state)); + + return error; +} + StateType Process::GetPrivateState () { diff --git a/lldb/source/Target/StopInfo.cpp b/lldb/source/Target/StopInfo.cpp index b7cf11c..99b1ac1 100644 --- a/lldb/source/Target/StopInfo.cpp +++ b/lldb/source/Target/StopInfo.cpp @@ -690,14 +690,13 @@ protected: assert (stored_stop_info_sp.get() == this); ThreadPlanSP new_plan_sp(thread_sp->QueueThreadPlanForStepSingleInstruction(false, // step-over - false, // abort_other_plans - true)); // stop_other_threads + false, // abort_other_plans + true)); // stop_other_threads new_plan_sp->SetIsMasterPlan (true); new_plan_sp->SetOkayToDiscard (false); new_plan_sp->SetPrivate (true); process->GetThreadList().SetSelectedThreadByID (thread_sp->GetID()); - process->Resume (); - process->WaitForProcessToStop (NULL); + process->ResumeSynchronous(NULL); process->GetThreadList().SetSelectedThreadByID (thread_sp->GetID()); thread_sp->SetStopInfo(stored_stop_info_sp); } diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp index 093e6f2..7bfb49f 100644 --- a/lldb/source/Target/Target.cpp +++ b/lldb/source/Target/Target.cpp @@ -2334,7 +2334,7 @@ Target::ClearAllLoadedSections () Error -Target::Launch (Listener &listener, ProcessLaunchInfo &launch_info) +Target::Launch (Listener &listener, ProcessLaunchInfo &launch_info, Stream *stream) { Error error; Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TARGET)); @@ -2443,7 +2443,7 @@ Target::Launch (Listener &listener, ProcessLaunchInfo &launch_info) { ListenerSP hijack_listener_sp (launch_info.GetHijackListener()); - StateType state = m_process_sp->WaitForProcessToStop (NULL, NULL, false, hijack_listener_sp.get()); + StateType state = m_process_sp->WaitForProcessToStop (NULL, NULL, false, hijack_listener_sp.get(), NULL); if (state == eStateStopped) { @@ -2461,7 +2461,7 @@ Target::Launch (Listener &listener, ProcessLaunchInfo &launch_info) if (synchronous_execution) { - state = m_process_sp->WaitForProcessToStop (NULL, NULL, true, hijack_listener_sp.get()); + state = m_process_sp->WaitForProcessToStop (NULL, NULL, true, hijack_listener_sp.get(), stream); const bool must_be_alive = false; // eStateExited is ok, so this must be false if (!StateIsStoppedState(state, must_be_alive)) { diff --git a/lldb/test/api/multithreaded/test_breakpoint_callback.cpp b/lldb/test/api/multithreaded/test_breakpoint_callback.cpp index 0cc1b6d..def31f8 100644 --- a/lldb/test/api/multithreaded/test_breakpoint_callback.cpp +++ b/lldb/test/api/multithreaded/test_breakpoint_callback.cpp @@ -29,6 +29,7 @@ bool BPCallback (void *baton, } void test(SBDebugger &dbg, vector args) { + dbg.SetAsync(false); SBTarget target = dbg.CreateTarget(args.at(0).c_str()); if (!target.IsValid()) throw Exception("invalid target"); -- 2.7.4