<rdar://problem/14526890>

Fixed a crasher when using memory threads where a thread is sticking around too long and was causing problems when it didn't have a thread plan. 



git-svn-id: https://llvm.org/svn/llvm-project/lldb/trunk@187395 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/lldb/Target/ThreadPlan.h b/include/lldb/Target/ThreadPlan.h
index 3d95a09..3c83fd1 100644
--- a/include/lldb/Target/ThreadPlan.h
+++ b/include/lldb/Target/ThreadPlan.h
@@ -238,6 +238,7 @@
     typedef enum
     {
         eKindGeneric,
+        eKindNull,
         eKindBase,
         eKindCallFunction,
         eKindStepInstruction,
@@ -435,7 +436,7 @@
     virtual void
     WillPop();
 
-    // This pushes \a plan onto the plan stack of the current plan's thread.
+    // This pushes a plan onto the plan stack of the current plan's thread.
     void
     PushPlan (lldb::ThreadPlanSP &thread_plan_sp)
     {
@@ -600,6 +601,57 @@
     DISALLOW_COPY_AND_ASSIGN(ThreadPlan);
 };
 
+//----------------------------------------------------------------------
+// ThreadPlanNull:
+// Threads are assumed to always have at least one plan on the plan stack.
+// This is put on the plan stack when a thread is destroyed so that if you
+// accidentally access a thread after it is destroyed you won't crash.
+// But asking questions of the ThreadPlanNull is definitely an error.
+//----------------------------------------------------------------------
+
+class ThreadPlanNull : public ThreadPlan
+{
+public:
+    ThreadPlanNull (Thread &thread);
+    virtual ~ThreadPlanNull ();
+    
+    virtual void
+    GetDescription (Stream *s,
+                    lldb::DescriptionLevel level);
+
+    virtual bool
+    ValidatePlan (Stream *error);
+
+    virtual bool
+    ShouldStop (Event *event_ptr);
+
+    virtual bool
+    MischiefManaged ();
+
+    virtual bool
+    WillStop ();
+
+    virtual bool
+    IsBasePlan()
+    {
+        return true;
+    }
+    
+    virtual bool
+    OkayToDiscard ()
+    {
+        return false;
+    }
+
+protected:
+    virtual bool
+    DoPlanExplainsStop (Event *event_ptr);
+    
+    virtual lldb::StateType
+    GetPlanRunState ();
+    
+};
+
 
 } // namespace lldb_private
 
diff --git a/source/Target/Thread.cpp b/source/Target/Thread.cpp
index 401eac2..b65434b 100644
--- a/source/Target/Thread.cpp
+++ b/source/Target/Thread.cpp
@@ -292,6 +292,13 @@
     m_plan_stack.clear();
     m_discarded_plan_stack.clear();
     m_completed_plan_stack.clear();
+    
+    // Push a ThreadPlanNull on the plan stack.  That way we can continue assuming that the
+    // plan stack is never empty, but if somebody errantly asks questions of a destroyed thread
+    // without checking first whether it is destroyed, they won't crash.
+    ThreadPlanSP null_plan_sp(new ThreadPlanNull (*this));
+    m_plan_stack.push_back (null_plan_sp);
+    
     m_stop_info_sp.reset();
     m_reg_context_sp.reset();
     m_unwinder_ap.reset();
@@ -362,6 +369,9 @@
 lldb::StopInfoSP
 Thread::GetStopInfo ()
 {
+    if (m_destroy_called)
+        return m_stop_info_sp;
+
     ThreadPlanSP plan_sp (GetCompletedPlan());
     ProcessSP process_sp (GetProcess());
     const uint32_t stop_id = process_sp ? process_sp->GetStopID() : UINT32_MAX;
@@ -387,6 +397,9 @@
 lldb::StopInfoSP
 Thread::GetPrivateStopInfo ()
 {
+    if (m_destroy_called)
+        return m_stop_info_sp;
+
     ProcessSP process_sp (GetProcess());
     if (process_sp)
     {
diff --git a/source/Target/ThreadPlan.cpp b/source/Target/ThreadPlan.cpp
index ba35db1..11240db 100644
--- a/source/Target/ThreadPlan.cpp
+++ b/source/Target/ThreadPlan.cpp
@@ -210,3 +210,144 @@
     else
         return GetPlanRunState();
 }
+
+//----------------------------------------------------------------------
+// ThreadPlanNull
+//----------------------------------------------------------------------
+
+ThreadPlanNull::ThreadPlanNull (Thread &thread) :
+    ThreadPlan (ThreadPlan::eKindNull,
+                "Null Thread Plan",
+                thread,
+                eVoteNoOpinion,
+                eVoteNoOpinion)
+{
+}
+
+ThreadPlanNull::~ThreadPlanNull ()
+{
+}
+
+void
+ThreadPlanNull::GetDescription (Stream *s,
+                                lldb::DescriptionLevel level)
+{
+    s->PutCString("Null thread plan - thread has been destroyed.");
+}
+
+bool
+ThreadPlanNull::ValidatePlan (Stream *error)
+{
+#ifdef LLDB_CONFIGURATION_DEBUG
+    fprintf(stderr, "error: %s called on thread that has been destroyed (tid = 0x%llx, ptid = 0x%llx)",
+            __PRETTY_FUNCTION__,
+            m_thread.GetID(),
+            m_thread.GetProtocolID());
+#else
+    Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD));
+    if (log)
+        log->Error("%s called on thread that has been destroyed (tid = 0x%llx, ptid = 0x%llx)",
+                    __PRETTY_FUNCTION__,
+                    m_thread.GetID(),
+                    m_thread.GetProtocolID());
+#endif
+    return true;
+}
+
+bool
+ThreadPlanNull::ShouldStop (Event *event_ptr)
+{
+#ifdef LLDB_CONFIGURATION_DEBUG
+    fprintf(stderr, "error: %s called on thread that has been destroyed (tid = 0x%llx, ptid = 0x%llx)",
+            __PRETTY_FUNCTION__,
+            m_thread.GetID(),
+            m_thread.GetProtocolID());
+#else
+    Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD));
+    if (log)
+        log->Error("%s called on thread that has been destroyed (tid = 0x%llx, ptid = 0x%llx)",
+                    __PRETTY_FUNCTION__,
+                    m_thread.GetID(),
+                    m_thread.GetProtocolID());
+#endif
+    return true;
+}
+
+bool
+ThreadPlanNull::WillStop ()
+{
+#ifdef LLDB_CONFIGURATION_DEBUG
+    fprintf(stderr, "error: %s called on thread that has been destroyed (tid = 0x%llx, ptid = 0x%llx)",
+            __PRETTY_FUNCTION__,
+            m_thread.GetID(),
+            m_thread.GetProtocolID());
+#else
+    Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD));
+    if (log)
+        log->Error("%s called on thread that has been destroyed (tid = 0x%llx, ptid = 0x%llx)",
+                    __PRETTY_FUNCTION__,
+                    m_thread.GetID(),
+                    m_thread.GetProtocolID());
+#endif
+    return true;
+}
+
+bool
+ThreadPlanNull::DoPlanExplainsStop (Event *event_ptr)
+{
+#ifdef LLDB_CONFIGURATION_DEBUG
+    fprintf(stderr, "error: %s called on thread that has been destroyed (tid = 0x%llx, ptid = 0x%llx)",
+            __PRETTY_FUNCTION__,
+            m_thread.GetID(),
+            m_thread.GetProtocolID());
+#else
+    Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD));
+    if (log)
+        log->Error("%s called on thread that has been destroyed (tid = 0x%llx, ptid = 0x%llx)",
+                   __PRETTY_FUNCTION__,
+                   m_thread.GetID(),
+                   m_thread.GetProtocolID());
+#endif
+    return true;
+}
+
+// The null plan is never done.
+bool
+ThreadPlanNull::MischiefManaged ()
+{
+    // The null plan is never done.
+#ifdef LLDB_CONFIGURATION_DEBUG
+    fprintf(stderr, "error: %s called on thread that has been destroyed (tid = 0x%llx, ptid = 0x%llx)",
+            __PRETTY_FUNCTION__,
+            m_thread.GetID(),
+            m_thread.GetProtocolID());
+#else
+    Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD));
+    if (log)
+        log->Error("%s called on thread that has been destroyed (tid = 0x%llx, ptid = 0x%llx)",
+                   __PRETTY_FUNCTION__,
+                   m_thread.GetID(),
+                   m_thread.GetProtocolID());
+#endif
+    return false;
+}
+
+lldb::StateType
+ThreadPlanNull::GetPlanRunState ()
+{
+    // Not sure what to return here.  This is a dead thread.
+#ifdef LLDB_CONFIGURATION_DEBUG
+    fprintf(stderr, "error: %s called on thread that has been destroyed (tid = 0x%llx, ptid = 0x%llx)",
+            __PRETTY_FUNCTION__,
+            m_thread.GetID(),
+            m_thread.GetProtocolID());
+#else
+    Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD));
+    if (log)
+        log->Error("%s called on thread that has been destroyed (tid = 0x%llx, ptid = 0x%llx)",
+                   __PRETTY_FUNCTION__,
+                   m_thread.GetID(),
+                   m_thread.GetProtocolID());
+#endif
+    return eStateRunning;
+}