Work around some problems destroying a process with older debugservers.

rdar://problem/11359989

llvm-svn: 159697
This commit is contained in:
Jim Ingham 2012-07-04 00:35:43 +00:00
parent ef285b4c22
commit 43c555dfcd
4 changed files with 121 additions and 7 deletions

View File

@ -775,6 +775,13 @@ public:
{
return m_destroy_called;
}
// When you implement this method, make sure you don't overwrite the m_actual_stop_info if it claims to be
// valid. The stop info may be a "checkpointed and restored" stop info, so if it is still around it is right
// even if you have not calculated this yourself, or if it disagrees with what you might have calculated.
virtual lldb::StopInfoSP
GetPrivateStopReason () = 0;
protected:
friend class ThreadPlan;
@ -796,12 +803,6 @@ protected:
ThreadPlan *GetPreviousPlan (ThreadPlan *plan);
// When you implement this method, make sure you don't overwrite the m_actual_stop_info if it claims to be
// valid. The stop info may be a "checkpointed and restored" stop info, so if it is still around it is right
// even if you have not calculated this yourself, or if it disagrees with what you might have calculated.
virtual lldb::StopInfoSP
GetPrivateStopReason () = 0;
typedef std::vector<lldb::ThreadPlanSP> plan_stack;
void

View File

@ -47,6 +47,7 @@
// Project includes
#include "lldb/Host/Host.h"
#include "Plugins/Process/Utility/InferiorCallPOSIX.h"
#include "Plugins/Platform/MacOSX/PlatformRemoteiOS.h"
#include "Utility/StringExtractorGDBRemote.h"
#include "GDBRemoteRegisterContext.h"
#include "ProcessGDBRemote.h"
@ -175,7 +176,8 @@ ProcessGDBRemote::ProcessGDBRemote(Target& target, Listener &listener) :
m_max_memory_size (512),
m_addr_to_mmap_size (),
m_thread_create_bp_sp (),
m_waiting_for_attach (false)
m_waiting_for_attach (false),
m_destroy_tried_resuming (false)
{
m_async_broadcaster.SetEventName (eBroadcastBitAsyncThreadShouldExit, "async thread should exit");
m_async_broadcaster.SetEventName (eBroadcastBitAsyncContinue, "async thread continue");
@ -1689,6 +1691,7 @@ ProcessGDBRemote::DoDetach()
return error;
}
Error
ProcessGDBRemote::DoDestroy ()
{
@ -1697,6 +1700,110 @@ ProcessGDBRemote::DoDestroy ()
if (log)
log->Printf ("ProcessGDBRemote::DoDestroy()");
// There is a bug in older iOS debugservers where they don't shut down the process
// they are debugging properly. If the process is sitting at a breakpoint or an exception,
// this can cause problems with restarting. So we check to see if any of our threads are stopped
// at a breakpoint, and if so we remove all the breakpoints, resume the process, and THEN
// destroy it again.
//
// Note, we don't have a good way to test the version of debugserver, but I happen to know that
// the set of all the iOS debugservers which don't support GetThreadSuffixSupported() and that of
// the debugservers with this bug are equal. There really should be a better way to test this!
//
// We also use m_destroy_tried_resuming to make sure we only do this once, if we resume and then halt and
// get called here to destroy again and we're still at a breakpoint or exception, then we should
// just do the straight-forward kill.
//
// And of course, if we weren't able to stop the process by the time we get here, it isn't
// necessary (or helpful) to do any of this.
if (!m_gdb_comm.GetThreadSuffixSupported() && m_public_state.GetValue() != eStateRunning)
{
PlatformSP platform_sp = GetTarget().GetPlatform();
// FIXME: These should be ConstStrings so we aren't doing strcmp'ing.
if (platform_sp
&& platform_sp->GetName()
&& strcmp (platform_sp->GetName(), PlatformRemoteiOS::GetShortPluginNameStatic()) == 0)
{
if (m_destroy_tried_resuming)
{
if (log)
log->PutCString ("ProcessGDBRemote::DoDestroy()Tried resuming to destroy once already, not doing it again.");
}
else
{
// At present, the plans are discarded and the breakpoints disabled Process::Destroy,
// but we really need it to happen here and it doesn't matter if we do it twice.
m_thread_list.DiscardThreadPlans();
DisableAllBreakpointSites();
bool stop_looks_like_crash = false;
ThreadList &threads = GetThreadList();
{
Mutex::Locker(threads.GetMutex());
size_t num_threads = threads.GetSize();
for (size_t i = 0; i < num_threads; i++)
{
ThreadSP thread_sp = threads.GetThreadAtIndex(i);
StopInfoSP stop_info_sp = thread_sp->GetPrivateStopReason();
StopReason reason = eStopReasonInvalid;
if (stop_info_sp)
reason = stop_info_sp->GetStopReason();
if (reason == eStopReasonBreakpoint
|| reason == eStopReasonException)
{
if (log)
log->Printf ("ProcessGDBRemote::DoDestroy() - thread: %lld stopped with reason: %s.",
thread_sp->GetID(),
stop_info_sp->GetDescription());
stop_looks_like_crash = true;
break;
}
}
}
if (stop_looks_like_crash)
{
if (log)
log->PutCString ("ProcessGDBRemote::DoDestroy() - Stopped at a breakpoint, continue and then kill.");
m_destroy_tried_resuming = true;
// If we are going to run again before killing, it would be good to suspend all the threads
// before resuming so they won't get into more trouble. Sadly, for the threads stopped with
// the breakpoint or exception, the exception doesn't get cleared if it is suspended, so we do
// have to run the risk of letting those threads proceed a bit.
{
Mutex::Locker(threads.GetMutex());
size_t num_threads = threads.GetSize();
for (size_t i = 0; i < num_threads; i++)
{
ThreadSP thread_sp = threads.GetThreadAtIndex(i);
StopInfoSP stop_info_sp = thread_sp->GetPrivateStopReason();
StopReason reason = eStopReasonInvalid;
if (stop_info_sp)
reason = stop_info_sp->GetStopReason();
if (reason != eStopReasonBreakpoint
&& reason != eStopReasonException)
{
if (log)
log->Printf ("ProcessGDBRemote::DoDestroy() - Suspending thread: %lld before running.",
thread_sp->GetID());
thread_sp->SetResumeState(eStateSuspended);
}
}
}
Resume ();
return Destroy();
}
}
}
}
// Interrupt if our inferior is running...
int exit_status = SIGABRT;
std::string exit_string;

View File

@ -321,6 +321,7 @@ protected:
MMapMap m_addr_to_mmap_size;
lldb::BreakpointSP m_thread_create_bp_sp;
bool m_waiting_for_attach;
bool m_destroy_tried_resuming;
bool
StartAsyncThread ();

View File

@ -1621,6 +1621,11 @@ void
Process::DisableAllBreakpointSites ()
{
m_breakpoint_site_list.SetEnabledForAll (false);
size_t num_sites = m_breakpoint_site_list.GetSize();
for (size_t i = 0; i < num_sites; i++)
{
DisableBreakpoint (m_breakpoint_site_list.GetByIndex(i).get());
}
}
Error