Rationalize how we do Halt-ing before Destroy and Detach.

<rdar://problem/13527167>

llvm-svn: 178325
This commit is contained in:
Jim Ingham 2013-03-29 01:18:12 +00:00
parent dcb1d856d7
commit 8af3b9ca67
5 changed files with 107 additions and 142 deletions

View File

@ -2350,6 +2350,9 @@ public:
//------------------------------------------------------------------
virtual void
DidDetach () {}
virtual bool
DetachRequiresHalt() { return false; }
//------------------------------------------------------------------
/// Called before sending a signal to a process.
@ -2387,6 +2390,9 @@ public:
virtual void
DidDestroy () { }
virtual bool
DestroyRequiresHalt() { return true; }
//------------------------------------------------------------------
@ -3683,6 +3689,8 @@ protected:
const char *bytes,
size_t bytes_len);
Error
HaltForDestroyOrDetach(lldb::EventSP &exit_event_sp);
private:
//------------------------------------------------------------------

View File

@ -1670,99 +1670,6 @@ ProcessGDBRemote::DoHalt (bool &caused_stop)
return error;
}
Error
ProcessGDBRemote::InterruptIfRunning
(
bool discard_thread_plans,
bool catch_stop_event,
EventSP &stop_event_sp
)
{
Error error;
Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
bool paused_private_state_thread = false;
const bool is_running = m_gdb_comm.IsRunning();
if (log)
log->Printf ("ProcessGDBRemote::InterruptIfRunning(discard_thread_plans=%i, catch_stop_event=%i) is_running=%i",
discard_thread_plans,
catch_stop_event,
is_running);
if (discard_thread_plans)
{
if (log)
log->Printf ("ProcessGDBRemote::InterruptIfRunning() discarding all thread plans");
m_thread_list.DiscardThreadPlans();
}
if (is_running)
{
if (catch_stop_event)
{
if (log)
log->Printf ("ProcessGDBRemote::InterruptIfRunning() pausing private state thread");
PausePrivateStateThread();
paused_private_state_thread = true;
}
bool timed_out = false;
Mutex::Locker locker;
if (!m_gdb_comm.SendInterrupt (locker, 1, timed_out))
{
if (timed_out)
error.SetErrorString("timed out sending interrupt packet");
else
error.SetErrorString("unknown error sending interrupt packet");
if (paused_private_state_thread)
ResumePrivateStateThread();
return error;
}
if (catch_stop_event)
{
// LISTEN HERE
TimeValue timeout_time;
timeout_time = TimeValue::Now();
timeout_time.OffsetWithSeconds(5);
StateType state = WaitForStateChangedEventsPrivate (&timeout_time, stop_event_sp);
timed_out = state == eStateInvalid;
if (log)
log->Printf ("ProcessGDBRemote::InterruptIfRunning() catch stop event: state = %s, timed-out=%i", StateAsCString(state), timed_out);
if (timed_out)
error.SetErrorString("unable to verify target stopped");
}
if (paused_private_state_thread)
{
if (log)
log->Printf ("ProcessGDBRemote::InterruptIfRunning() resuming private state thread");
ResumePrivateStateThread();
}
}
return error;
}
Error
ProcessGDBRemote::WillDetach ()
{
Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
if (log)
log->Printf ("ProcessGDBRemote::WillDetach()");
bool discard_thread_plans = true;
bool catch_stop_event = true;
EventSP event_sp;
// FIXME: InterruptIfRunning should be done in the Process base class, or better still make Halt do what is
// needed. This shouldn't be a feature of a particular plugin.
return InterruptIfRunning (discard_thread_plans, catch_stop_event, event_sp);
}
Error
ProcessGDBRemote::DoDetach()
{

View File

@ -140,11 +140,11 @@ public:
virtual lldb_private::Error
DoHalt (bool &caused_stop);
virtual lldb_private::Error
WillDetach ();
virtual lldb_private::Error
DoDetach ();
virtual bool
DetachRequiresHalt() { return true; }
virtual lldb_private::Error
DoSignal (int signal);
@ -383,11 +383,6 @@ protected:
const char *bytes,
size_t bytes_len);
lldb_private::Error
InterruptIfRunning (bool discard_thread_plans,
bool catch_stop_event,
lldb::EventSP &stop_event_sp);
lldb_private::DynamicLoader *
GetDynamicLoader ();

View File

@ -3297,14 +3297,86 @@ Process::Halt ()
return error;
}
Error
Process::HaltForDestroyOrDetach(lldb::EventSP &exit_event_sp)
{
Error error;
if (m_public_state.GetValue() == eStateRunning)
{
Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
if (log)
log->Printf("Process::Destroy() About to halt.");
error = Halt();
if (error.Success())
{
// Consume the halt event.
TimeValue timeout (TimeValue::Now());
timeout.OffsetWithSeconds(1);
StateType state = WaitForProcessToStop (&timeout, &exit_event_sp);
// If the process exited while we were waiting for it to stop, put the exited event into
// the shared pointer passed in and return. Our caller doesn't need to do anything else, since
// they don't have a process anymore...
if (state == eStateExited || m_private_state.GetValue() == eStateExited)
{
if (log)
log->Printf("Process::HaltForDestroyOrDetach() Process exited while waiting to Halt.");
return error;
}
else
exit_event_sp.reset(); // It is ok to consume any non-exit stop events
if (state != eStateStopped)
{
if (log)
log->Printf("Process::HaltForDestroyOrDetach() Halt failed to stop, state is: %s", StateAsCString(state));
// If we really couldn't stop the process then we should just error out here, but if the
// lower levels just bobbled sending the event and we really are stopped, then continue on.
StateType private_state = m_private_state.GetValue();
if (private_state != eStateStopped)
{
return error;
}
}
}
else
{
if (log)
log->Printf("Process::HaltForDestroyOrDetach() Halt got error: %s", error.AsCString());
}
}
return error;
}
Error
Process::Detach ()
{
Error error (WillDetach());
EventSP exit_event_sp;
Error error;
m_destroy_in_process = true;
error = WillDetach();
if (error.Success())
{
DisableAllBreakpointSites();
if (DetachRequiresHalt())
{
error = HaltForDestroyOrDetach (exit_event_sp);
if (!error.Success())
{
m_destroy_in_process = false;
return error;
}
else if (exit_event_sp)
{
// We shouldn't need to do anything else here. There's no process left to detach from...
StopPrivateStateThread();
m_destroy_in_process = false;
return error;
}
}
error = DoDetach();
if (error.Success())
{
@ -3312,6 +3384,22 @@ Process::Detach ()
StopPrivateStateThread();
}
}
m_destroy_in_process = false;
// If we exited when we were waiting for a process to stop, then
// forward the event here so we don't lose the event
if (exit_event_sp)
{
// Directly broadcast our exited event because we shut down our
// private state thread above
BroadcastEvent(exit_event_sp);
}
// If we have been interrupted (to kill us) in the middle of running, we may not end up propagating
// the last events through the event system, in which case we might strand the write lock. Unlock
// it here so when we do to tear down the process we don't get an error destroying the lock.
m_run_lock.WriteUnlock();
return error;
}
@ -3329,46 +3417,11 @@ Process::Destroy ()
if (error.Success())
{
EventSP exit_event_sp;
if (m_public_state.GetValue() == eStateRunning)
if (DestroyRequiresHalt())
{
Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
if (log)
log->Printf("Process::Destroy() About to halt.");
error = Halt();
if (error.Success())
{
// Consume the halt event.
TimeValue timeout (TimeValue::Now());
timeout.OffsetWithSeconds(1);
StateType state = WaitForProcessToStop (&timeout, &exit_event_sp);
if (state != eStateExited)
exit_event_sp.reset(); // It is ok to consume any non-exit stop events
if (state != eStateStopped)
{
if (log)
log->Printf("Process::Destroy() Halt failed to stop, state is: %s", StateAsCString(state));
// If we really couldn't stop the process then we should just error out here, but if the
// lower levels just bobbled sending the event and we really are stopped, then continue on.
StateType private_state = m_private_state.GetValue();
if (private_state != eStateStopped && private_state != eStateExited)
{
// If we exited when we were waiting for a process to stop, then
// forward the event here so we don't lose the event
m_destroy_in_process = false;
return error;
}
}
}
else
{
if (log)
log->Printf("Process::Destroy() Halt got error: %s", error.AsCString());
m_destroy_in_process = false;
return error;
}
error = HaltForDestroyOrDetach(exit_event_sp);
}
if (m_public_state.GetValue() != eStateRunning)
{
// Ditch all thread plans, and remove all our breakpoints: in case we have to restart the target to
@ -3378,7 +3431,7 @@ Process::Destroy ()
m_thread_list.DiscardThreadPlans();
DisableAllBreakpointSites();
}
error = DoDestroy();
if (error.Success())
{

View File

@ -291,6 +291,8 @@ protected:
{
// This shouldn't ever happen, but just in case, don't do more harm.
log->Printf ("PerformAction got called with an invalid thread.");
m_should_stop = true;
m_should_stop_is_valid = true;
return;
}