jingham created this revision.
jingham added reviewers: friss, clayborg, labath.
Herald added subscribers: lldb-commits, mgorny.
Herald added a project: LLDB.
jingham added parent revisions: D75711: [NFC] Have ThreadPlans hold onto the 
Process & TID, rather than the Thread, D75720: Make ThreadPlanTracer use 
{Process,TID} rather than Thread to find it's underlying thread....

This is the third in the series of patches that move the ThreadPlan stacks out 
of the Threads and lets the process manage them instead.  The first two are:

https://reviews.llvm.org/D75720
https://reviews.llvm.org/D75711

This patch just moves the three thread plan stacks - current, discarded and 
completed - out of ThreadPlan and into the ThreadPlanStack class.  This is a 
nice cleanup on its own as it gets all the thread plan manipulation logic out 
of Thread.  Then the ThreadPlanStacks move out of Thread to Process where they 
are managed in a map ordered by TID.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D75880

Files:
  lldb/include/lldb/Target/Process.h
  lldb/include/lldb/Target/Thread.h
  lldb/include/lldb/Target/ThreadPlan.h
  lldb/include/lldb/Target/ThreadPlanStack.h
  lldb/source/Target/CMakeLists.txt
  lldb/source/Target/Process.cpp
  lldb/source/Target/Thread.cpp
  lldb/source/Target/ThreadList.cpp
  lldb/source/Target/ThreadPlan.cpp
  lldb/source/Target/ThreadPlanStack.cpp

Index: lldb/source/Target/ThreadPlanStack.cpp
===================================================================
--- /dev/null
+++ lldb/source/Target/ThreadPlanStack.cpp
@@ -0,0 +1,379 @@
+//===-- ThreadPlanStack.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/ThreadPlanStack.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadPlan.h"
+#include "lldb/Utility/Log.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+static void PrintPlanElement(Stream *s, const ThreadPlanSP &plan,
+                             lldb::DescriptionLevel desc_level,
+                             int32_t elem_idx) {
+  s->IndentMore();
+  s->Indent();
+  s->Printf("Element %d: ", elem_idx);
+  plan->GetDescription(s, desc_level);
+  s->EOL();
+  s->IndentLess();
+}
+
+void ThreadPlanStack::DumpThreadPlans(Stream *s, 
+                                      lldb::DescriptionLevel desc_level,
+                                      bool include_internal) const {
+
+  uint32_t stack_size;
+
+  s->IndentMore();
+  s->Indent();
+  s->Printf("Active plan stack:\n");
+  int32_t print_idx = 0;
+  for (auto plan : m_plans) {
+    PrintPlanElement(s, plan, desc_level, print_idx++);
+  }
+
+  if (AnyCompletedPlans()) {
+    print_idx = 0;
+    s->Indent();
+    s->Printf("Completed Plan Stack:\n");
+    for (auto plan : m_completed_plans)
+      PrintPlanElement(s, plan, desc_level, print_idx++);
+  }
+
+  if (AnyDiscardedPlans()) {
+    print_idx = 0;
+    s->Indent();
+    s->Printf("Discarded Plan Stack:\n");
+    for (auto plan : m_discarded_plans)
+      PrintPlanElement(s, plan, desc_level, print_idx++);
+  }
+
+  s->IndentLess();
+}
+
+
+size_t ThreadPlanStack::CheckpointCompletedPlans() {
+  m_completed_plan_checkpoint++;
+  m_completed_plan_store
+      .insert(std::make_pair(m_completed_plan_checkpoint, 
+                             m_completed_plans));
+  return m_completed_plan_checkpoint;
+}
+
+void ThreadPlanStack::RestoreCompletedPlanCheckpoint(size_t checkpoint) {
+  auto result = m_completed_plan_store.find(checkpoint);
+  if (result == m_completed_plan_store.end())
+    llvm_unreachable("Asked for a checkpoint that didn't exist");
+  m_completed_plans.swap((*result).second);
+  m_completed_plan_store.erase(result);
+}
+
+void ThreadPlanStack::DiscardCompletedPlanCheckpoint(size_t checkpoint) {
+  m_completed_plan_store.erase(checkpoint);
+}
+
+void ThreadPlanStack::ThreadDestroyed(Thread *thread) {
+  // Tell the plan stacks that this thread is going away:
+  for (auto plan : m_plans)
+    plan->ThreadDestroyed();
+
+  for (auto plan : m_discarded_plans)
+    plan->ThreadDestroyed();
+
+  for (auto plan : m_completed_plans)
+    plan->ThreadDestroyed();
+  
+  // Now clear the current plan stacks:
+  m_plans.clear();
+  m_discarded_plans.clear();
+  m_completed_plans.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.
+  if (thread != nullptr) {
+    lldb::ThreadPlanSP null_plan_sp(new ThreadPlanNull(*thread));
+    m_plans.push_back(null_plan_sp);
+  }
+}
+
+void ThreadPlanStack::EnableTracer(bool value, bool single_stepping) {
+  for (auto plan : m_plans) {
+    if (plan->GetThreadPlanTracer()) {
+      plan->GetThreadPlanTracer()->EnableTracing(value);
+      plan->GetThreadPlanTracer()->EnableSingleStep(single_stepping);
+    }
+  }
+}
+
+void ThreadPlanStack::SetTracer(lldb::ThreadPlanTracerSP &tracer_sp) {
+  for (auto plan : m_plans)
+    plan->SetThreadPlanTracer(tracer_sp);
+}
+
+void ThreadPlanStack::PushPlan(lldb::ThreadPlanSP &new_plan_sp) {
+  // If the thread plan doesn't already have a tracer, give it its parent's
+  // tracer:
+  // The first plan has to be a base plan:
+  if (m_plans.size() == 0 && !new_plan_sp->IsBasePlan())
+    llvm_unreachable("Zeroth plan must be a base plan");
+
+  if (!new_plan_sp->GetThreadPlanTracer()) {
+    assert(!m_plans.empty());
+    new_plan_sp->SetThreadPlanTracer(
+        m_plans.back()->GetThreadPlanTracer());
+  }
+  m_plans.push_back(new_plan_sp);
+  new_plan_sp->DidPush();
+}
+
+lldb::ThreadPlanSP ThreadPlanStack::PopPlan() {
+  if (m_plans.size() <= 1)
+    llvm_unreachable("Can't pop the base thread plan");
+
+  lldb::ThreadPlanSP &plan_sp = m_plans.back();
+  m_completed_plans.push_back(plan_sp);
+  plan_sp->WillPop();
+  m_plans.pop_back();
+  return plan_sp;
+}
+
+lldb::ThreadPlanSP ThreadPlanStack::DiscardPlan() {
+  if (m_plans.size() == 1)
+    llvm_unreachable("Can't discard the base thread plan");
+  
+  lldb::ThreadPlanSP &plan_sp = m_plans.back();
+  m_discarded_plans.push_back(plan_sp);
+  plan_sp->WillPop();
+  m_plans.pop_back();
+  return plan_sp;
+}
+
+// If the input plan is nullptr, discard all plans.  Otherwise make sure this
+// plan is in the stack, and if so discard up to and including it.
+void ThreadPlanStack::DiscardPlansUpToPlan(ThreadPlan *up_to_plan_ptr) {
+  int stack_size = m_plans.size();
+
+  if (up_to_plan_ptr == nullptr) {
+    for (int i = stack_size - 1; i > 0; i--)
+      DiscardPlan();
+    return;
+  }
+  
+  bool found_it = false;
+  for (int i = stack_size - 1; i > 0; i--) {
+    if (m_plans[i].get() == up_to_plan_ptr) {
+      found_it = true;
+      break;
+    }
+  }
+  
+  if (found_it) {
+    bool last_one = false;
+    for (int i = stack_size - 1; i > 0 && !last_one; i--) {
+      if (GetCurrentPlan().get() == up_to_plan_ptr)
+        last_one = true;
+      DiscardPlan();
+    }
+  }
+}
+
+void ThreadPlanStack::DiscardAllPlans() {
+  int stack_size = m_plans.size();
+  for (int i = stack_size - 1; i > 0; i--) {
+    DiscardPlan();
+  }
+  return;
+}
+
+void ThreadPlanStack::DiscardConsultingMasterPlans() {
+  while (true) {
+    int master_plan_idx;
+    bool discard = true;
+
+    // Find the first master plan, see if it wants discarding, and if yes
+    // discard up to it.
+    for (master_plan_idx = m_plans.size() - 1; master_plan_idx >= 0;
+         master_plan_idx--) {
+      if (m_plans[master_plan_idx]->IsMasterPlan()) {
+        discard = m_plans[master_plan_idx]->OkayToDiscard();
+        break;
+      }
+    }
+    
+    // If the master plan doesn't want to get discarded, then we're done.
+    if (!discard)
+      return;
+
+    // First pop all the dependent plans:
+    for (int i = m_plans.size() - 1; i > master_plan_idx; i--) {
+      DiscardPlan();
+    }
+
+    // Now discard the master plan itself.
+    // The bottom-most plan never gets discarded.  "OkayToDiscard" for it
+    // means discard it's dependent plans, but not it...
+    if (master_plan_idx > 0) {
+      DiscardPlan();
+    }
+  }
+}
+
+lldb::ThreadPlanSP ThreadPlanStack::GetCurrentPlan() const {
+  if (m_plans.size() == 0)
+    llvm_unreachable("There will always be a base plan.");
+  return m_plans.back();
+}
+
+lldb::ThreadPlanSP 
+ThreadPlanStack::GetCompletedPlan(bool skip_private) const {
+  if (m_completed_plans.empty())
+    return {};
+    
+  if (!skip_private)
+    return m_completed_plans.back();
+
+  for (int i = m_completed_plans.size() - 1; i >= 0; i--) {
+    lldb::ThreadPlanSP completed_plan_sp;
+    completed_plan_sp = m_completed_plans[i];
+    if (!completed_plan_sp->GetPrivate())
+      return completed_plan_sp;
+  }
+  return {};
+}
+
+lldb::ThreadPlanSP ThreadPlanStack::GetPlanByIndex(uint32_t plan_idx, 
+    bool skip_private) const {
+  uint32_t idx = 0;
+  ThreadPlan *up_to_plan_ptr = nullptr;
+
+  for (lldb::ThreadPlanSP plan_sp : m_plans) {
+    if (skip_private && plan_sp->GetPrivate())
+      continue;
+    if (idx == plan_idx)
+      return plan_sp;
+    idx++;
+  }
+  return {};
+}
+
+lldb::ValueObjectSP ThreadPlanStack::GetReturnValueObject() const {
+  if (m_completed_plans.empty())
+    return {};
+    
+  for (int i = m_completed_plans.size() - 1; i >= 0; i--) {
+    lldb::ValueObjectSP return_valobj_sp;
+    return_valobj_sp = m_completed_plans[i]->GetReturnValueObject();
+    if (return_valobj_sp)
+      return return_valobj_sp;
+  }
+  return {};
+}
+
+lldb::ExpressionVariableSP ThreadPlanStack::GetExpressionVariable() const {
+  if (m_completed_plans.empty())
+    return {};
+
+  for (int i = m_completed_plans.size() - 1; i >= 0; i--) {
+    lldb::ExpressionVariableSP expression_variable_sp;
+    expression_variable_sp =
+        m_completed_plans[i]->GetExpressionVariable();
+    if (expression_variable_sp)
+      return expression_variable_sp;
+  }
+  return {};
+}
+bool ThreadPlanStack::AnyPlans() const {
+  // There is always a base plan...
+  return m_plans.size() > 1;
+}
+
+bool ThreadPlanStack::AnyCompletedPlans() const {
+  return !m_completed_plans.empty();
+}
+
+bool ThreadPlanStack::AnyDiscardedPlans() const {
+  return !m_discarded_plans.empty();
+}
+
+bool ThreadPlanStack::IsPlanDone(ThreadPlan *in_plan) const {
+  for (auto plan : m_completed_plans) {
+    if (plan.get() == in_plan)
+      return true;
+  }
+  return false;
+}
+
+bool ThreadPlanStack::WasPlanDiscarded(ThreadPlan *in_plan) const {
+  for (auto plan : m_discarded_plans) {
+    if (plan.get() == in_plan)
+      return true;
+  }
+  return false;
+}
+
+ThreadPlan *ThreadPlanStack::GetPreviousPlan(ThreadPlan *current_plan) const {
+  if (current_plan == nullptr)
+    return nullptr;
+
+  // Look first in the completed plans, if the plan is here and there is
+  // a completed plan above it, return that.
+  int stack_size = m_completed_plans.size();
+  for (int i = stack_size - 1; i > 0; i--) {
+    if (current_plan == m_completed_plans[i].get())
+      return m_completed_plans[i - 1].get();
+  }
+
+  // If this is the first completed plan, the previous one is the
+  // bottom of the regular plan stack.
+  if (stack_size > 0 && m_completed_plans[0].get() == current_plan) {
+    return GetCurrentPlan().get();
+  }
+
+  // Otherwise look for it in the regular plans.
+  stack_size = m_plans.size();
+  for (int i = stack_size - 1; i > 0; i--) {
+    if (current_plan == m_plans[i].get())
+      return m_plans[i - 1].get();
+  }
+  return nullptr;
+}
+
+ThreadPlan *
+ThreadPlanStack::GetInnermostExpression() const {
+  int stack_size = m_plans.size();
+
+  for (int i = stack_size - 1; i > 0; i--) {
+    if (m_plans[i]->GetKind() == ThreadPlan::eKindCallFunction)
+      return m_plans[i].get();
+  }
+  return nullptr;
+}
+
+void ThreadPlanStack::WillResume() {
+  m_completed_plans.clear();
+  m_discarded_plans.clear();
+}
+
+const ThreadPlanStack::PlanStack &
+ThreadPlanStack::GetStackOfKind(ThreadPlanStack::StackKind kind) const {
+  const PlanStack * stack;
+  switch (kind) {
+  case ePlans:
+    return m_plans;
+  case eCompletedPlans:
+    return m_completed_plans;
+  case eDiscardedPlans:
+    return m_discarded_plans;
+  }
+}
Index: lldb/source/Target/ThreadPlan.cpp
===================================================================
--- lldb/source/Target/ThreadPlan.cpp
+++ lldb/source/Target/ThreadPlan.cpp
@@ -34,6 +34,10 @@
 // Destructor
 ThreadPlan::~ThreadPlan() = default;
 
+Target &ThreadPlan::GetTarget() { return m_process.GetTarget(); }
+
+const Target &ThreadPlan::GetTarget() const { return m_process.GetTarget(); }
+
 Thread &ThreadPlan::GetThread() {
   if (m_thread)
     return *m_thread;
Index: lldb/source/Target/ThreadList.cpp
===================================================================
--- lldb/source/Target/ThreadList.cpp
+++ lldb/source/Target/ThreadList.cpp
@@ -726,8 +726,10 @@
           break;
         }
       }
-      if (!thread_is_alive)
+      if (!thread_is_alive) {
         (*rhs_pos)->DestroyThread();
+        m_process->RemoveThreadPlansForTID((*rhs_pos)->GetID());
+      }
     }
   }
 }
Index: lldb/source/Target/Thread.cpp
===================================================================
--- lldb/source/Target/Thread.cpp
+++ lldb/source/Target/Thread.cpp
@@ -41,6 +41,7 @@
 #include "lldb/Target/ThreadPlanStepOverRange.h"
 #include "lldb/Target/ThreadPlanStepThrough.h"
 #include "lldb/Target/ThreadPlanStepUntil.h"
+#include "lldb/Target/ThreadPlanStack.h"
 #include "lldb/Target/ThreadSpec.h"
 #include "lldb/Target/Unwind.h"
 #include "lldb/Utility/Log.h"
@@ -229,7 +230,7 @@
       m_index_id(use_invalid_index_id ? LLDB_INVALID_INDEX32
                                       : process.GetNextThreadIndexID(tid)),
       m_reg_context_sp(), m_state(eStateUnloaded), m_state_mutex(),
-      m_plan_stack(), m_completed_plan_stack(), m_frame_mutex(),
+      m_frame_mutex(),
       m_curr_frames_sp(), m_prev_frames_sp(),
       m_resume_signal(LLDB_INVALID_SIGNAL_NUMBER),
       m_resume_state(eStateRunning), m_temporary_resume_state(eStateRunning),
@@ -241,7 +242,9 @@
             static_cast<void *>(this), GetID());
 
   CheckInWithManager();
-
+  
+  process.AddThreadPlansForThread(*this);
+  
   QueueFundamentalPlan(true);
 }
 
@@ -255,30 +258,7 @@
 }
 
 void Thread::DestroyThread() {
-  // Tell any plans on the plan stacks that the thread is being destroyed since
-  // any plans that have a thread go away in the middle of might need to do
-  // cleanup, or in some cases NOT do cleanup...
-  for (auto plan : m_plan_stack)
-    plan->ThreadDestroyed();
-
-  for (auto plan : m_discarded_plan_stack)
-    plan->ThreadDestroyed();
-
-  for (auto plan : m_completed_plan_stack)
-    plan->ThreadDestroyed();
-
   m_destroy_called = true;
-  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_up.reset();
@@ -523,7 +503,8 @@
   if (process_sp)
     saved_state.orig_stop_id = process_sp->GetStopID();
   saved_state.current_inlined_depth = GetCurrentInlinedDepth();
-  saved_state.m_completed_plan_stack = m_completed_plan_stack;
+  saved_state.m_completed_plan_checkpoint 
+      = GetPlans().CheckpointCompletedPlans();
 
   return true;
 }
@@ -557,7 +538,8 @@
   SetStopInfo(saved_state.stop_info_sp);
   GetStackFrameList()->SetCurrentInlinedDepth(
       saved_state.current_inlined_depth);
-  m_completed_plan_stack = saved_state.m_completed_plan_stack;
+  GetPlans()
+      .RestoreCompletedPlanCheckpoint(saved_state.m_completed_plan_checkpoint);
   return true;
 }
 
@@ -690,8 +672,7 @@
 
 bool Thread::ShouldResume(StateType resume_state) {
   // At this point clear the completed plan stack.
-  m_completed_plan_stack.clear();
-  m_discarded_plan_stack.clear();
+  GetPlans().WillResume();
   m_override_should_notify = eLazyBoolCalculate;
 
   StateType prev_resume_state = GetTemporaryResumeState();
@@ -885,7 +866,7 @@
               current_plan->GetName(), over_ride_stop);
 
     // We're starting from the base plan, so just let it decide;
-    if (PlanIsBasePlan(current_plan)) {
+    if (current_plan->IsBasePlan()) {
       should_stop = current_plan->ShouldStop(event_ptr);
       LLDB_LOGF(log, "Base plan says should stop: %i.", should_stop);
     } else {
@@ -893,7 +874,7 @@
       // to do, since presumably if there were other plans they would know what
       // to do...
       while (true) {
-        if (PlanIsBasePlan(current_plan))
+        if (current_plan->IsBasePlan())
           break;
 
         should_stop = current_plan->ShouldStop(event_ptr);
@@ -939,7 +920,7 @@
 
     // Discard the stale plans and all plans below them in the stack, plus move
     // the completed plans to the completed plan stack
-    while (!PlanIsBasePlan(plan_ptr)) {
+    while (!plan_ptr->IsBasePlan()) {
       bool stale = plan_ptr->IsPlanStale();
       ThreadPlan *examined_plan = plan_ptr;
       plan_ptr = GetPreviousPlan(examined_plan);
@@ -1005,13 +986,14 @@
     return eVoteNoOpinion;
   }
 
-  if (m_completed_plan_stack.size() > 0) {
-    // Don't use GetCompletedPlan here, since that suppresses private plans.
+  if (GetPlans().AnyCompletedPlans()) {
+    // Pass skip_private = false to GetCompletedPlan, since we want to ask 
+    // the last plan, regardless of whether it is private or not.
     LLDB_LOGF(log,
               "Thread::ShouldReportStop() tid = 0x%4.4" PRIx64
               ": returning vote  for complete stack's back plan",
               GetID());
-    return m_completed_plan_stack.back()->ShouldReportStop(event_ptr);
+    return GetPlans().GetCompletedPlan(false)->ShouldReportStop(event_ptr);
   } else {
     Vote thread_vote = eVoteNoOpinion;
     ThreadPlan *plan_ptr = GetCurrentPlan();
@@ -1020,7 +1002,7 @@
         thread_vote = plan_ptr->ShouldReportStop(event_ptr);
         break;
       }
-      if (PlanIsBasePlan(plan_ptr))
+      if (plan_ptr->IsBasePlan())
         break;
       else
         plan_ptr = GetPreviousPlan(plan_ptr);
@@ -1042,16 +1024,17 @@
   }
 
   Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
-  if (m_completed_plan_stack.size() > 0) {
-    // Don't use GetCompletedPlan here, since that suppresses private plans.
+  if (GetPlans().AnyCompletedPlans()) {
+    // Pass skip_private = false to GetCompletedPlan, since we want to ask 
+    // the last plan, regardless of whether it is private or not.
     LLDB_LOGF(log,
               "Current Plan for thread %d(%p) (0x%4.4" PRIx64
               ", %s): %s being asked whether we should report run.",
               GetIndexID(), static_cast<void *>(this), GetID(),
               StateAsCString(GetTemporaryResumeState()),
-              m_completed_plan_stack.back()->GetName());
+              GetCompletedPlan()->GetName());
 
-    return m_completed_plan_stack.back()->ShouldReportRun(event_ptr);
+    return GetPlans().GetCompletedPlan(false)->ShouldReportRun(event_ptr);
   } else {
     LLDB_LOGF(log,
               "Current Plan for thread %d(%p) (0x%4.4" PRIx64
@@ -1068,148 +1051,78 @@
   return (spec == nullptr) ? true : spec->ThreadPassesBasicTests(*this);
 }
 
+ThreadPlanStack &Thread::GetPlans() const {
+  llvm::Expected<ThreadPlanStack *>plans =
+      GetProcess()->FindThreadPlans(GetID());
+  if (!plans)
+    llvm_unreachable("Thread left with an empty plan stack");
+  return **plans;
+}
+
 void Thread::PushPlan(ThreadPlanSP &thread_plan_sp) {
-  if (thread_plan_sp) {
-    // If the thread plan doesn't already have a tracer, give it its parent's
-    // tracer:
-    if (!thread_plan_sp->GetThreadPlanTracer()) {
-      assert(!m_plan_stack.empty());
-      thread_plan_sp->SetThreadPlanTracer(
-          m_plan_stack.back()->GetThreadPlanTracer());
-    }
-    m_plan_stack.push_back(thread_plan_sp);
+  if (!thread_plan_sp)
+    llvm_unreachable("Don't push an empty thread plan.");
 
-    thread_plan_sp->DidPush();
+  GetPlans().PushPlan(thread_plan_sp);
 
-    Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
-    if (log) {
-      StreamString s;
-      thread_plan_sp->GetDescription(&s, lldb::eDescriptionLevelFull);
-      LLDB_LOGF(log, "Thread::PushPlan(0x%p): \"%s\", tid = 0x%4.4" PRIx64 ".",
-                static_cast<void *>(this), s.GetData(),
-                thread_plan_sp->GetThread().GetID());
-    }
+  Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+  if (log) {
+    StreamString s;
+    thread_plan_sp->GetDescription(&s, lldb::eDescriptionLevelFull);
+    LLDB_LOGF(log, "Thread::PushPlan(0x%p): \"%s\", tid = 0x%4.4" PRIx64 ".",
+              static_cast<void *>(this), s.GetData(),
+              thread_plan_sp->GetThread().GetID());
   }
 }
 
 void Thread::PopPlan() {
   Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
-
-  if (m_plan_stack.size() <= 1)
-    return;
-  else {
-    ThreadPlanSP &plan = m_plan_stack.back();
-    if (log) {
-      LLDB_LOGF(log, "Popping plan: \"%s\", tid = 0x%4.4" PRIx64 ".",
-                plan->GetName(), plan->GetThread().GetID());
-    }
-    m_completed_plan_stack.push_back(plan);
-    plan->WillPop();
-    m_plan_stack.pop_back();
+  ThreadPlanSP popped_plan_sp = GetPlans().PopPlan();
+  if (log) {
+    LLDB_LOGF(log, "Popping plan: \"%s\", tid = 0x%4.4" PRIx64 ".",
+              popped_plan_sp->GetName(), popped_plan_sp->GetThread().GetID());
   }
 }
 
 void Thread::DiscardPlan() {
   Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
-  if (m_plan_stack.size() > 1) {
-    ThreadPlanSP &plan = m_plan_stack.back();
-    LLDB_LOGF(log, "Discarding plan: \"%s\", tid = 0x%4.4" PRIx64 ".",
-              plan->GetName(), plan->GetThread().GetID());
-
-    m_discarded_plan_stack.push_back(plan);
-    plan->WillPop();
-    m_plan_stack.pop_back();
-  }
-}
+  ThreadPlanSP discarded_plan_sp = GetPlans().PopPlan();
 
-ThreadPlan *Thread::GetCurrentPlan() {
-  // There will always be at least the base plan.  If somebody is mucking with
-  // a thread with an empty plan stack, we should assert right away.
-  return m_plan_stack.empty() ? nullptr : m_plan_stack.back().get();
+  LLDB_LOGF(log, "Discarding plan: \"%s\", tid = 0x%4.4" PRIx64 ".",
+            discarded_plan_sp->GetName(), 
+            discarded_plan_sp->GetThread().GetID());
 }
 
-ThreadPlanSP Thread::GetCompletedPlan() {
-  ThreadPlanSP empty_plan_sp;
-  if (!m_completed_plan_stack.empty()) {
-    for (int i = m_completed_plan_stack.size() - 1; i >= 0; i--) {
-      ThreadPlanSP completed_plan_sp;
-      completed_plan_sp = m_completed_plan_stack[i];
-      if (!completed_plan_sp->GetPrivate())
-        return completed_plan_sp;
-    }
-  }
-  return empty_plan_sp;
+ThreadPlan *Thread::GetCurrentPlan() const {
+  return GetPlans().GetCurrentPlan().get();
 }
 
-ValueObjectSP Thread::GetReturnValueObject() {
-  if (!m_completed_plan_stack.empty()) {
-    for (int i = m_completed_plan_stack.size() - 1; i >= 0; i--) {
-      ValueObjectSP return_valobj_sp;
-      return_valobj_sp = m_completed_plan_stack[i]->GetReturnValueObject();
-      if (return_valobj_sp)
-        return return_valobj_sp;
-    }
-  }
-  return ValueObjectSP();
+ThreadPlanSP Thread::GetCompletedPlan() const {
+  return GetPlans().GetCompletedPlan();
 }
 
-ExpressionVariableSP Thread::GetExpressionVariable() {
-  if (!m_completed_plan_stack.empty()) {
-    for (int i = m_completed_plan_stack.size() - 1; i >= 0; i--) {
-      ExpressionVariableSP expression_variable_sp;
-      expression_variable_sp =
-          m_completed_plan_stack[i]->GetExpressionVariable();
-      if (expression_variable_sp)
-        return expression_variable_sp;
-    }
-  }
-  return ExpressionVariableSP();
+ValueObjectSP Thread::GetReturnValueObject() const {
+  return GetPlans().GetReturnValueObject();
 }
 
-bool Thread::IsThreadPlanDone(ThreadPlan *plan) {
-  if (!m_completed_plan_stack.empty()) {
-    for (int i = m_completed_plan_stack.size() - 1; i >= 0; i--) {
-      if (m_completed_plan_stack[i].get() == plan)
-        return true;
-    }
-  }
-  return false;
+ExpressionVariableSP Thread::GetExpressionVariable() const{
+  return GetPlans().GetExpressionVariable();
 }
 
-bool Thread::WasThreadPlanDiscarded(ThreadPlan *plan) {
-  if (!m_discarded_plan_stack.empty()) {
-    for (int i = m_discarded_plan_stack.size() - 1; i >= 0; i--) {
-      if (m_discarded_plan_stack[i].get() == plan)
-        return true;
-    }
-  }
-  return false;
+bool Thread::IsThreadPlanDone(ThreadPlan *plan) const {
+  return GetPlans().IsPlanDone(plan);
 }
 
-bool Thread::CompletedPlanOverridesBreakpoint() {
-  return (!m_completed_plan_stack.empty()) ;
+bool Thread::WasThreadPlanDiscarded(ThreadPlan *plan) const{
+  return GetPlans().WasPlanDiscarded(plan);
 }
 
-ThreadPlan *Thread::GetPreviousPlan(ThreadPlan *current_plan) {
-  if (current_plan == nullptr)
-    return nullptr;
-
-  int stack_size = m_completed_plan_stack.size();
-  for (int i = stack_size - 1; i > 0; i--) {
-    if (current_plan == m_completed_plan_stack[i].get())
-      return m_completed_plan_stack[i - 1].get();
-  }
-
-  if (stack_size > 0 && m_completed_plan_stack[0].get() == current_plan) {
-    return GetCurrentPlan();
-  }
+bool Thread::CompletedPlanOverridesBreakpoint() const {
+  return GetPlans().AnyCompletedPlans();
+}
 
-  stack_size = m_plan_stack.size();
-  for (int i = stack_size - 1; i > 0; i--) {
-    if (current_plan == m_plan_stack[i].get())
-      return m_plan_stack[i - 1].get();
-  }
-  return nullptr;
+ThreadPlan *Thread::GetPreviousPlan(ThreadPlan *current_plan) const{
+  return GetPlans().GetPreviousPlan(current_plan);
 }
 
 Status Thread::QueueThreadPlan(ThreadPlanSP &thread_plan_sp,
@@ -1243,38 +1156,18 @@
 }
 
 void Thread::EnableTracer(bool value, bool single_stepping) {
-  int stack_size = m_plan_stack.size();
-  for (int i = 0; i < stack_size; i++) {
-    if (m_plan_stack[i]->GetThreadPlanTracer()) {
-      m_plan_stack[i]->GetThreadPlanTracer()->EnableTracing(value);
-      m_plan_stack[i]->GetThreadPlanTracer()->EnableSingleStep(single_stepping);
-    }
-  }
+  GetPlans().EnableTracer(value, single_stepping);
 }
 
 void Thread::SetTracer(lldb::ThreadPlanTracerSP &tracer_sp) {
-  int stack_size = m_plan_stack.size();
-  for (int i = 0; i < stack_size; i++)
-    m_plan_stack[i]->SetThreadPlanTracer(tracer_sp);
+  GetPlans().SetTracer(tracer_sp);
 }
 
-bool Thread::DiscardUserThreadPlansUpToIndex(uint32_t thread_index) {
+bool Thread::DiscardUserThreadPlansUpToIndex(uint32_t plan_index) {
   // Count the user thread plans from the back end to get the number of the one
   // we want to discard:
 
-  uint32_t idx = 0;
-  ThreadPlan *up_to_plan_ptr = nullptr;
-
-  for (ThreadPlanSP plan_sp : m_plan_stack) {
-    if (plan_sp->GetPrivate())
-      continue;
-    if (idx == thread_index) {
-      up_to_plan_ptr = plan_sp.get();
-      break;
-    } else
-      idx++;
-  }
-
+  ThreadPlan *up_to_plan_ptr = GetPlans().GetPlanByIndex(plan_index).get();
   if (up_to_plan_ptr == nullptr)
     return false;
 
@@ -1292,30 +1185,7 @@
             "Discarding thread plans for thread tid = 0x%4.4" PRIx64
             ", up to %p",
             GetID(), static_cast<void *>(up_to_plan_ptr));
-
-  int stack_size = m_plan_stack.size();
-
-  // If the input plan is nullptr, discard all plans.  Otherwise make sure this
-  // plan is in the stack, and if so discard up to and including it.
-
-  if (up_to_plan_ptr == nullptr) {
-    for (int i = stack_size - 1; i > 0; i--)
-      DiscardPlan();
-  } else {
-    bool found_it = false;
-    for (int i = stack_size - 1; i > 0; i--) {
-      if (m_plan_stack[i].get() == up_to_plan_ptr)
-        found_it = true;
-    }
-    if (found_it) {
-      bool last_one = false;
-      for (int i = stack_size - 1; i > 0 && !last_one; i--) {
-        if (GetCurrentPlan() == up_to_plan_ptr)
-          last_one = true;
-        DiscardPlan();
-      }
-    }
-  }
+  GetPlans().DiscardPlansUpToPlan(up_to_plan_ptr);
 }
 
 void Thread::DiscardThreadPlans(bool force) {
@@ -1328,73 +1198,21 @@
   }
 
   if (force) {
-    int stack_size = m_plan_stack.size();
-    for (int i = stack_size - 1; i > 0; i--) {
-      DiscardPlan();
-    }
+    GetPlans().DiscardAllPlans();
     return;
   }
-
-  while (true) {
-    int master_plan_idx;
-    bool discard = true;
-
-    // Find the first master plan, see if it wants discarding, and if yes
-    // discard up to it.
-    for (master_plan_idx = m_plan_stack.size() - 1; master_plan_idx >= 0;
-         master_plan_idx--) {
-      if (m_plan_stack[master_plan_idx]->IsMasterPlan()) {
-        discard = m_plan_stack[master_plan_idx]->OkayToDiscard();
-        break;
-      }
-    }
-
-    if (discard) {
-      // First pop all the dependent plans:
-      for (int i = m_plan_stack.size() - 1; i > master_plan_idx; i--) {
-        // FIXME: Do we need a finalize here, or is the rule that
-        // "PrepareForStop"
-        // for the plan leaves it in a state that it is safe to pop the plan
-        // with no more notice?
-        DiscardPlan();
-      }
-
-      // Now discard the master plan itself.
-      // The bottom-most plan never gets discarded.  "OkayToDiscard" for it
-      // means discard it's dependent plans, but not it...
-      if (master_plan_idx > 0) {
-        DiscardPlan();
-      }
-    } else {
-      // If the master plan doesn't want to get discarded, then we're done.
-      break;
-    }
-  }
-}
-
-bool Thread::PlanIsBasePlan(ThreadPlan *plan_ptr) {
-  if (plan_ptr->IsBasePlan())
-    return true;
-  else if (m_plan_stack.size() == 0)
-    return false;
-  else
-    return m_plan_stack[0].get() == plan_ptr;
+  GetPlans().DiscardConsultingMasterPlans();
 }
 
 Status Thread::UnwindInnermostExpression() {
   Status error;
-  int stack_size = m_plan_stack.size();
-
-  // If the input plan is nullptr, discard all plans.  Otherwise make sure this
-  // plan is in the stack, and if so discard up to and including it.
-
-  for (int i = stack_size - 1; i > 0; i--) {
-    if (m_plan_stack[i]->GetKind() == ThreadPlan::eKindCallFunction) {
-      DiscardThreadPlansUpToPlan(m_plan_stack[i].get());
-      return error;
-    }
-  }
-  error.SetErrorString("No expressions currently active on this thread");
+  ThreadPlan *innermost_expr_plan 
+      = GetPlans().GetInnermostExpression();
+  if (!innermost_expr_plan) {
+    error.SetErrorString("No expressions currently active on this thread");
+    return error;
+  }  
+  DiscardThreadPlansUpToPlan(innermost_expr_plan);
   return error;
 }
 
@@ -1560,40 +1378,12 @@
 
 uint32_t Thread::GetIndexID() const { return m_index_id; }
 
-static void PrintPlanElement(Stream *s, const ThreadPlanSP &plan,
-                             lldb::DescriptionLevel desc_level,
-                             int32_t elem_idx) {
-  s->IndentMore();
-  s->Indent();
-  s->Printf("Element %d: ", elem_idx);
-  plan->GetDescription(s, desc_level);
-  s->EOL();
-  s->IndentLess();
-}
-
-static void PrintPlanStack(Stream *s,
-                           const std::vector<lldb::ThreadPlanSP> &plan_stack,
-                           lldb::DescriptionLevel desc_level,
-                           bool include_internal) {
-  int32_t print_idx = 0;
-  for (ThreadPlanSP plan_sp : plan_stack) {
-    if (include_internal || !plan_sp->GetPrivate()) {
-      PrintPlanElement(s, plan_sp, desc_level, print_idx++);
-    }
-  }
-}
-
 void Thread::DumpThreadPlans(Stream *s, lldb::DescriptionLevel desc_level,
                              bool include_internal,
                              bool ignore_boring_threads) const {
-  uint32_t stack_size;
-
   if (ignore_boring_threads) {
-    uint32_t stack_size = m_plan_stack.size();
-    uint32_t completed_stack_size = m_completed_plan_stack.size();
-    uint32_t discarded_stack_size = m_discarded_plan_stack.size();
-    if (stack_size == 1 && completed_stack_size == 0 &&
-        discarded_stack_size == 0) {
+    if (!GetPlans().AnyPlans() && !GetPlans().AnyCompletedPlans()
+        && !GetPlans().AnyDiscardedPlans()) {
       s->Printf("thread #%u: tid = 0x%4.4" PRIx64 "\n", GetIndexID(), GetID());
       s->IndentMore();
       s->Indent();
@@ -1602,31 +1392,11 @@
       return;
     }
   }
-
+  
   s->Indent();
   s->Printf("thread #%u: tid = 0x%4.4" PRIx64 ":\n", GetIndexID(), GetID());
-  s->IndentMore();
-  s->Indent();
-  s->Printf("Active plan stack:\n");
-  PrintPlanStack(s, m_plan_stack, desc_level, include_internal);
-
-  stack_size = m_completed_plan_stack.size();
-  if (stack_size > 0) {
-    s->Indent();
-    s->Printf("Completed Plan Stack:\n");
-    PrintPlanStack(s, m_completed_plan_stack, desc_level, include_internal);
-  }
-
-  stack_size = m_discarded_plan_stack.size();
-  if (stack_size > 0) {
-    s->Indent();
-    s->Printf("Discarded Plan Stack:\n");
-    PrintPlanStack(s, m_discarded_plan_stack, desc_level, include_internal);
-  }
-
-  s->IndentLess();
+  GetPlans().DumpThreadPlans(s, desc_level, include_internal);
 }
-
 TargetSP Thread::CalculateTarget() {
   TargetSP target_sp;
   ProcessSP process_sp(GetProcess());
Index: lldb/source/Target/Process.cpp
===================================================================
--- lldb/source/Target/Process.cpp
+++ lldb/source/Target/Process.cpp
@@ -60,6 +60,7 @@
 #include "lldb/Target/ThreadPlan.h"
 #include "lldb/Target/ThreadPlanBase.h"
 #include "lldb/Target/ThreadPlanCallFunction.h"
+#include "lldb/Target/ThreadPlanStack.h"
 #include "lldb/Target/UnixSignals.h"
 #include "lldb/Utility/Event.h"
 #include "lldb/Utility/Log.h"
@@ -600,6 +601,7 @@
   m_system_runtime_up.reset();
   m_dyld_up.reset();
   m_jit_loaders_up.reset();
+  m_thread_plans.Clear();
   m_thread_list_real.Destroy();
   m_thread_list.Destroy();
   m_extended_thread_list.Destroy();
@@ -1252,6 +1254,25 @@
   }
 }
 
+llvm::Expected<ThreadPlanStack *> Process::FindThreadPlans(lldb::tid_t tid) {
+    ThreadPlanStack *plans = m_thread_plans.Find(tid);
+    if (!plans)
+      return llvm::make_error<llvm::StringError>(
+          llvm::formatv("Invalid option with value '{0}'.", tid),
+          llvm::inconvertibleErrorCode());
+    return plans;
+}
+
+void Process::AddThreadPlansForThread(Thread &thread) {
+  if (m_thread_plans.Find(thread.GetID()))
+    return;
+  m_thread_plans.AddThread(thread);
+}
+
+void Process::RemoveThreadPlansForTID(lldb::tid_t tid) {
+  m_thread_plans.RemoveTID(tid);
+}
+
 void Process::UpdateQueueListIfNeeded() {
   if (m_system_runtime_up) {
     if (m_queue_list.GetSize() == 0 ||
@@ -3231,6 +3252,10 @@
 }
 
 Status Process::Destroy(bool force_kill) {
+  // If we've already called Process::Finalize then there's nothing useful to
+  // be done here.  Finalize has actually called Destroy already.
+  if (m_finalize_called)
+    return {};
 
   // Tell ourselves we are in the process of destroying the process, so that we
   // don't do any unnecessary work that might hinder the destruction.  Remember
Index: lldb/source/Target/CMakeLists.txt
===================================================================
--- lldb/source/Target/CMakeLists.txt
+++ lldb/source/Target/CMakeLists.txt
@@ -62,6 +62,7 @@
   ThreadPlanStepThrough.cpp
   ThreadPlanStepUntil.cpp
   ThreadPlanTracer.cpp
+  ThreadPlanStack.cpp
   ThreadSpec.cpp
   UnixSignals.cpp
   UnwindAssembly.cpp
Index: lldb/include/lldb/Target/ThreadPlanStack.h
===================================================================
--- /dev/null
+++ lldb/include/lldb/Target/ThreadPlanStack.h
@@ -0,0 +1,162 @@
+//===-- ThreadPlanStack.h ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ThreadPlanStack_h_
+#define liblldb_ThreadPlanStack_h_
+
+#include <mutex>
+#include <string>
+#include <vector>
+#include <unordered_map>
+
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/lldb-private.h"
+#include "lldb/lldb-private-forward.h"
+
+namespace lldb_private {
+
+// The ThreadPlans have a thread for use when they are asked all the ThreadPlan
+// state machine questions, but they should never cache any pointers from their
+// owning lldb_private::Thread.  That's because we want to be able to detach
+// them from an owning thread, then reattach them by TID.
+// The ThreadPlanStack holds the ThreadPlans for a given TID.  All its methods
+// are private, and it should only be accessed through the owning thread.  When
+// it is detached from a thread, all you can do is reattach it or delete it.
+class ThreadPlanStack {
+friend class lldb_private::Thread;
+
+public:
+  ThreadPlanStack(Thread &thread) {};
+  ~ThreadPlanStack() {
+  }
+  
+  enum StackKind {
+     ePlans,
+     eCompletedPlans,
+     eDiscardedPlans
+  };
+
+  using PlanStack = std::vector<lldb::ThreadPlanSP>;
+  
+  void DumpThreadPlans(Stream *s, lldb::DescriptionLevel desc_level, 
+                       bool include_internal) const;
+
+  size_t CheckpointCompletedPlans();
+  
+  void RestoreCompletedPlanCheckpoint(size_t checkpoint);
+  
+  void DiscardCompletedPlanCheckpoint(size_t checkpoint);
+  
+  void ThreadDestroyed(Thread *thread);
+  
+  void EnableTracer(bool value, bool single_stepping);
+
+  void SetTracer(lldb::ThreadPlanTracerSP &tracer_sp);
+  
+  void PushPlan(lldb::ThreadPlanSP &new_plan_sp);
+  
+  lldb::ThreadPlanSP PopPlan();
+  
+  lldb::ThreadPlanSP DiscardPlan();
+  
+  // If the input plan is nullptr, discard all plans.  Otherwise make sure this
+  // plan is in the stack, and if so discard up to and including it.
+  void DiscardPlansUpToPlan(ThreadPlan *up_to_plan_ptr);
+  
+  void DiscardAllPlans();
+  
+  void DiscardConsultingMasterPlans();
+  
+  lldb::ThreadPlanSP GetCurrentPlan() const;
+  
+  lldb::ThreadPlanSP GetCompletedPlan(bool skip_private = true) const;
+  
+  lldb::ThreadPlanSP GetPlanByIndex(uint32_t plan_idx, 
+      bool skip_private = true) const;
+
+  lldb::ValueObjectSP GetReturnValueObject() const;
+  
+  lldb::ExpressionVariableSP GetExpressionVariable() const;
+  
+  bool AnyPlans() const;
+  
+  bool AnyCompletedPlans() const;
+  
+  bool AnyDiscardedPlans() const;
+  
+  bool IsPlanDone(ThreadPlan *plan) const;
+  
+  bool WasPlanDiscarded(ThreadPlan *plan) const;
+  
+  ThreadPlan *GetPreviousPlan(ThreadPlan *current_plan) const;
+  
+  ThreadPlan *GetInnermostExpression() const;
+  
+  void WillResume();
+   
+private:
+
+ const PlanStack &GetStackOfKind(ThreadPlanStack::StackKind kind) const;
+ 
+ PlanStack m_plans; ///< The stack of plans this thread is executing.
+ PlanStack m_completed_plans; ///< Plans that have been completed by this
+                              ///stop.  They get deleted when the thread
+                              ///resumes.
+  PlanStack m_discarded_plans; ///< Plans that have been discarded by this
+                               ///stop.  They get deleted when the thread
+                               ///resumes.
+  size_t m_completed_plan_checkpoint = 0; // Monotonically increasing token for
+                                      // completed plan checkpoints.
+  std::unordered_map<size_t, PlanStack> m_completed_plan_store;
+  
+};
+
+class ThreadPlanStackMap {
+public:
+  ThreadPlanStackMap() {}
+  ~ThreadPlanStackMap() {}
+
+  void Update(ThreadList &current_threads);
+  
+  void AddThread(Thread &thread) {
+    lldb::tid_t tid = thread.GetID();
+    auto result = m_plans_list.emplace(tid, thread);
+  }
+  
+  bool RemoveTID(lldb::tid_t tid) {
+    auto result = m_plans_list.find(tid);
+    if (result == m_plans_list.end())
+      return false;
+    result->second.ThreadDestroyed(nullptr);
+    m_plans_list.erase(result);
+    return true;
+  }
+  
+  ThreadPlanStack *Find(lldb::tid_t tid) {
+    auto result = m_plans_list.find(tid);
+    if (result == m_plans_list.end())
+      return nullptr;
+    else
+      return &result->second;
+  }
+  
+  void Clear() {
+    for (auto plan : m_plans_list)
+      plan.second.ThreadDestroyed(nullptr);
+    m_plans_list.clear();
+  }
+
+private:
+    using PlansList = std::unordered_map<lldb::tid_t, ThreadPlanStack>;
+    PlansList m_plans_list;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_ThreadPlan_h_
Index: lldb/include/lldb/Target/ThreadPlan.h
===================================================================
--- lldb/include/lldb/Target/ThreadPlan.h
+++ lldb/include/lldb/Target/ThreadPlan.h
@@ -371,9 +371,9 @@
   ///   A  pointer to the thread plan's owning thread.
   Thread &GetThread();
 
-  Target &GetTarget() { return m_process.GetTarget(); }
+  Target &GetTarget();
 
-  const Target &GetTarget() const { return m_process.GetTarget(); }
+  const Target &GetTarget() const;
 
   /// Print a description of this thread to the stream \a s.
   /// \a thread.
Index: lldb/include/lldb/Target/Thread.h
===================================================================
--- lldb/include/lldb/Target/Thread.h
+++ lldb/include/lldb/Target/Thread.h
@@ -28,6 +28,8 @@
 
 namespace lldb_private {
 
+class ThreadPlanStack;
+
 class ThreadProperties : public Properties {
 public:
   ThreadProperties(bool is_global);
@@ -119,7 +121,7 @@
                            // bit of data.
     lldb::StopInfoSP stop_info_sp; // You have to restore the stop info or you
                                    // might continue with the wrong signals.
-    std::vector<lldb::ThreadPlanSP> m_completed_plan_stack;
+    size_t m_completed_plan_checkpoint;
     lldb::RegisterCheckpointSP
         register_backup_sp; // You need to restore the registers, of course...
     uint32_t current_inlined_depth;
@@ -912,7 +914,7 @@
   ///
   /// \return
   ///     A pointer to the next executed plan.
-  ThreadPlan *GetCurrentPlan();
+  ThreadPlan *GetCurrentPlan() const;
 
   /// Unwinds the thread stack for the innermost expression plan currently
   /// on the thread plan stack.
@@ -927,14 +929,14 @@
   ///
   /// \return
   ///     A pointer to the last completed plan.
-  lldb::ThreadPlanSP GetCompletedPlan();
+  lldb::ThreadPlanSP GetCompletedPlan() const;
 
   /// Gets the outer-most return value from the completed plans
   ///
   /// \return
   ///     A ValueObjectSP, either empty if there is no return value,
   ///     or containing the return value.
-  lldb::ValueObjectSP GetReturnValueObject();
+  lldb::ValueObjectSP GetReturnValueObject() const;
 
   /// Gets the outer-most expression variable from the completed plans
   ///
@@ -942,7 +944,7 @@
   ///     A ExpressionVariableSP, either empty if there is no
   ///     plan completed an expression during the current stop
   ///     or the expression variable that was made for the completed expression.
-  lldb::ExpressionVariableSP GetExpressionVariable();
+  lldb::ExpressionVariableSP GetExpressionVariable() const;
 
   ///  Checks whether the given plan is in the completed plans for this
   ///  stop.
@@ -953,7 +955,7 @@
   /// \return
   ///     Returns true if the input plan is in the completed plan stack,
   ///     false otherwise.
-  bool IsThreadPlanDone(ThreadPlan *plan);
+  bool IsThreadPlanDone(ThreadPlan *plan) const;
 
   ///  Checks whether the given plan is in the discarded plans for this
   ///  stop.
@@ -964,14 +966,14 @@
   /// \return
   ///     Returns true if the input plan is in the discarded plan stack,
   ///     false otherwise.
-  bool WasThreadPlanDiscarded(ThreadPlan *plan);
+  bool WasThreadPlanDiscarded(ThreadPlan *plan) const;
 
   /// Check if we have completed plan to override breakpoint stop reason
   ///
   /// \return
   ///     Returns true if completed plan stack is not empty
   ///     false otherwise.
-  bool CompletedPlanOverridesBreakpoint();
+  bool CompletedPlanOverridesBreakpoint() const;
 
   /// Queues a generic thread plan.
   ///
@@ -1184,6 +1186,8 @@
   // thread is still in good shape to call virtual thread methods.  This must
   // be called by classes that derive from Thread in their destructor.
   virtual void DestroyThread();
+  
+  ThreadPlanStack &GetPlans() const;
 
   void PushPlan(lldb::ThreadPlanSP &plan_sp);
 
@@ -1191,9 +1195,7 @@
 
   void DiscardPlan();
 
-  ThreadPlan *GetPreviousPlan(ThreadPlan *plan);
-
-  typedef std::vector<lldb::ThreadPlanSP> plan_stack;
+  ThreadPlan *GetPreviousPlan(ThreadPlan *plan) const;
 
   virtual Unwind &GetUnwinder();
 
@@ -1238,13 +1240,6 @@
   lldb::StateType m_state;                  ///< The state of our process.
   mutable std::recursive_mutex
       m_state_mutex;       ///< Multithreaded protection for m_state.
-  plan_stack m_plan_stack; ///< The stack of plans this thread is executing.
-  plan_stack m_completed_plan_stack; ///< Plans that have been completed by this
-                                     ///stop.  They get deleted when the thread
-                                     ///resumes.
-  plan_stack m_discarded_plan_stack; ///< Plans that have been discarded by this
-                                     ///stop.  They get deleted when the thread
-                                     ///resumes.
   mutable std::recursive_mutex
       m_frame_mutex; ///< Multithreaded protection for m_state.
   lldb::StackFrameListSP m_curr_frames_sp; ///< The stack frames that get lazily
@@ -1272,8 +1267,7 @@
   StructuredData::ObjectSP m_extended_info; // The extended info for this thread
 
 private:
-  bool PlanIsBasePlan(ThreadPlan *plan_ptr);
-
+  
   void BroadcastSelectedFrameChange(StackID &new_frame_id);
 
   DISALLOW_COPY_AND_ASSIGN(Thread);
Index: lldb/include/lldb/Target/Process.h
===================================================================
--- lldb/include/lldb/Target/Process.h
+++ lldb/include/lldb/Target/Process.h
@@ -37,6 +37,7 @@
 #include "lldb/Target/Memory.h"
 #include "lldb/Target/QueueList.h"
 #include "lldb/Target/ThreadList.h"
+#include "lldb/Target/ThreadPlanStack.h"
 #include "lldb/Utility/ArchSpec.h"
 #include "lldb/Utility/Broadcaster.h"
 #include "lldb/Utility/Event.h"
@@ -2197,6 +2198,19 @@
   }
 
   void SetDynamicCheckers(DynamicCheckerFunctions *dynamic_checkers);
+  
+  /// Find the thread plan stack associated with thread with \a tid.
+  ///
+  /// \param[in] tid
+  ///     The tid whose Plan Stack we are seeking..
+  ///
+  /// \return
+  ///     Returns an Expected with thethread plan found or an error otherwise.
+  llvm::Expected<ThreadPlanStack *> FindThreadPlans(lldb::tid_t tid);
+  
+  void AddThreadPlansForThread(Thread &thread);
+  
+  void RemoveThreadPlansForTID(lldb::tid_t tid);
 
   /// Call this to set the lldb in the mode where it breaks on new thread
   /// creations, and then auto-restarts.  This is useful when you are trying
@@ -2533,7 +2547,7 @@
     virtual EventActionResult HandleBeingInterrupted() = 0;
     virtual const char *GetExitString() = 0;
     void RequestResume() { m_process->m_resume_requested = true; }
-
+    
   protected:
     Process *m_process;
   };
@@ -2667,6 +2681,10 @@
                             ///see them. This is usually the same as
   ///< m_thread_list_real, but might be different if there is an OS plug-in
   ///creating memory threads
+  ThreadPlanStackMap m_thread_plans; ///< This is the list of thread plans for
+                                     /// threads in m_thread_list, as well as
+                                     /// threads we knew existed, but haven't
+                                     /// determined that they have died yet.
   ThreadList m_extended_thread_list; ///< Owner for extended threads that may be
                                      ///generated, cleared on natural stops
   uint32_t m_extended_thread_stop_id; ///< The natural stop id when
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to