Work around some problems destroying a process with older debugservers.

rdar://problem/11359989


git-svn-id: https://llvm.org/svn/llvm-project/lldb/trunk@159697 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index e48c058..7479d1e 100644
--- a/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -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 @@
     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 @@
     return error;
 }
 
+
 Error
 ProcessGDBRemote::DoDestroy ()
 {
@@ -1697,6 +1700,110 @@
     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;