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
This commit is contained in:
parent
f16a66973c
commit
dc6224e0a3
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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<void*>(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");
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 : "<UNKNOWN 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 : "<UNKNOWN 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 <unknown index>: (");
|
||||
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();
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 : "<UNKNOWN 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 : "<UNKNOWN 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 <unknown index>: (");
|
||||
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 ()
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
{
|
||||
|
|
|
@ -29,6 +29,7 @@ bool BPCallback (void *baton,
|
|||
}
|
||||
|
||||
void test(SBDebugger &dbg, vector<string> args) {
|
||||
dbg.SetAsync(false);
|
||||
SBTarget target = dbg.CreateTarget(args.at(0).c_str());
|
||||
if (!target.IsValid()) throw Exception("invalid target");
|
||||
|
||||
|
|
Loading…
Reference in New Issue