161e8e688SJim Ingham //===-- ThreadPlanStack.cpp -------------------------------------*- C++ -*-===//
261e8e688SJim Ingham //
361e8e688SJim Ingham // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
461e8e688SJim Ingham // See https://llvm.org/LICENSE.txt for license information.
561e8e688SJim Ingham // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
661e8e688SJim Ingham //
761e8e688SJim Ingham //===----------------------------------------------------------------------===//
861e8e688SJim Ingham 
961e8e688SJim Ingham #include "lldb/Target/ThreadPlanStack.h"
1061e8e688SJim Ingham #include "lldb/Target/Process.h"
1161e8e688SJim Ingham #include "lldb/Target/Target.h"
1261e8e688SJim Ingham #include "lldb/Target/Thread.h"
1361e8e688SJim Ingham #include "lldb/Target/ThreadPlan.h"
1461e8e688SJim Ingham #include "lldb/Utility/Log.h"
1561e8e688SJim Ingham 
1661e8e688SJim Ingham using namespace lldb;
1761e8e688SJim Ingham using namespace lldb_private;
1861e8e688SJim Ingham 
19*1893065dSJim Ingham static void PrintPlanElement(Stream &s, const ThreadPlanSP &plan,
2061e8e688SJim Ingham                              lldb::DescriptionLevel desc_level,
2161e8e688SJim Ingham                              int32_t elem_idx) {
22*1893065dSJim Ingham   s.IndentMore();
23*1893065dSJim Ingham   s.Indent();
24*1893065dSJim Ingham   s.Printf("Element %d: ", elem_idx);
25*1893065dSJim Ingham   plan->GetDescription(&s, desc_level);
26*1893065dSJim Ingham   s.EOL();
27*1893065dSJim Ingham   s.IndentLess();
2861e8e688SJim Ingham }
2961e8e688SJim Ingham 
30*1893065dSJim Ingham ThreadPlanStack::ThreadPlanStack(const Thread &thread, bool make_null) {
31*1893065dSJim Ingham   if (make_null) {
32*1893065dSJim Ingham     // The ThreadPlanNull doesn't do anything to the Thread, so this is actually
33*1893065dSJim Ingham     // still a const operation.
34*1893065dSJim Ingham     m_plans.push_back(
35*1893065dSJim Ingham         ThreadPlanSP(new ThreadPlanNull(const_cast<Thread &>(thread))));
36*1893065dSJim Ingham   }
37*1893065dSJim Ingham }
38*1893065dSJim Ingham 
39*1893065dSJim Ingham void ThreadPlanStack::DumpThreadPlans(Stream &s,
4061e8e688SJim Ingham                                       lldb::DescriptionLevel desc_level,
4161e8e688SJim Ingham                                       bool include_internal) const {
4261e8e688SJim Ingham 
4361e8e688SJim Ingham   uint32_t stack_size;
4461e8e688SJim Ingham 
45*1893065dSJim Ingham   s.IndentMore();
46*1893065dSJim Ingham   PrintOneStack(s, "Active plan stack", m_plans, desc_level, include_internal);
47*1893065dSJim Ingham   PrintOneStack(s, "Completed plan stack", m_completed_plans, desc_level,
48*1893065dSJim Ingham                 include_internal);
49*1893065dSJim Ingham   PrintOneStack(s, "Discarded plan stack", m_discarded_plans, desc_level,
50*1893065dSJim Ingham                 include_internal);
51*1893065dSJim Ingham   s.IndentLess();
5261e8e688SJim Ingham }
5361e8e688SJim Ingham 
54*1893065dSJim Ingham void ThreadPlanStack::PrintOneStack(Stream &s, llvm::StringRef stack_name,
55*1893065dSJim Ingham                                     const PlanStack &stack,
56*1893065dSJim Ingham                                     lldb::DescriptionLevel desc_level,
57*1893065dSJim Ingham                                     bool include_internal) const {
58*1893065dSJim Ingham   // If the stack is empty, just exit:
59*1893065dSJim Ingham   if (stack.empty())
60*1893065dSJim Ingham     return;
61*1893065dSJim Ingham 
62*1893065dSJim Ingham   // Make sure there are public completed plans:
63*1893065dSJim Ingham   bool any_public = false;
64*1893065dSJim Ingham   if (!include_internal) {
65*1893065dSJim Ingham     for (auto plan : stack) {
66*1893065dSJim Ingham       if (!plan->GetPrivate()) {
67*1893065dSJim Ingham         any_public = true;
68*1893065dSJim Ingham         break;
69*1893065dSJim Ingham       }
70*1893065dSJim Ingham     }
7161e8e688SJim Ingham   }
7261e8e688SJim Ingham 
73*1893065dSJim Ingham   if (include_internal || any_public) {
74*1893065dSJim Ingham     int print_idx = 0;
75*1893065dSJim Ingham     s.Indent();
76*1893065dSJim Ingham     s.Printf("%s:\n", stack_name);
77*1893065dSJim Ingham     for (auto plan : stack) {
78*1893065dSJim Ingham       if (!include_internal && plan->GetPrivate())
79*1893065dSJim Ingham         continue;
8061e8e688SJim Ingham       PrintPlanElement(s, plan, desc_level, print_idx++);
8161e8e688SJim Ingham     }
82*1893065dSJim Ingham   }
8361e8e688SJim Ingham }
8461e8e688SJim Ingham 
8561e8e688SJim Ingham size_t ThreadPlanStack::CheckpointCompletedPlans() {
8661e8e688SJim Ingham   m_completed_plan_checkpoint++;
8761e8e688SJim Ingham   m_completed_plan_store.insert(
8861e8e688SJim Ingham       std::make_pair(m_completed_plan_checkpoint, m_completed_plans));
8961e8e688SJim Ingham   return m_completed_plan_checkpoint;
9061e8e688SJim Ingham }
9161e8e688SJim Ingham 
9261e8e688SJim Ingham void ThreadPlanStack::RestoreCompletedPlanCheckpoint(size_t checkpoint) {
9361e8e688SJim Ingham   auto result = m_completed_plan_store.find(checkpoint);
9461e8e688SJim Ingham   assert(result != m_completed_plan_store.end() &&
9561e8e688SJim Ingham          "Asked for a checkpoint that didn't exist");
9661e8e688SJim Ingham   m_completed_plans.swap((*result).second);
9761e8e688SJim Ingham   m_completed_plan_store.erase(result);
9861e8e688SJim Ingham }
9961e8e688SJim Ingham 
10061e8e688SJim Ingham void ThreadPlanStack::DiscardCompletedPlanCheckpoint(size_t checkpoint) {
10161e8e688SJim Ingham   m_completed_plan_store.erase(checkpoint);
10261e8e688SJim Ingham }
10361e8e688SJim Ingham 
10461e8e688SJim Ingham void ThreadPlanStack::ThreadDestroyed(Thread *thread) {
10561e8e688SJim Ingham   // Tell the plan stacks that this thread is going away:
10661e8e688SJim Ingham   for (ThreadPlanSP plan : m_plans)
10761e8e688SJim Ingham     plan->ThreadDestroyed();
10861e8e688SJim Ingham 
10961e8e688SJim Ingham   for (ThreadPlanSP plan : m_discarded_plans)
11061e8e688SJim Ingham     plan->ThreadDestroyed();
11161e8e688SJim Ingham 
11261e8e688SJim Ingham   for (ThreadPlanSP plan : m_completed_plans)
11361e8e688SJim Ingham     plan->ThreadDestroyed();
11461e8e688SJim Ingham 
11561e8e688SJim Ingham   // Now clear the current plan stacks:
11661e8e688SJim Ingham   m_plans.clear();
11761e8e688SJim Ingham   m_discarded_plans.clear();
11861e8e688SJim Ingham   m_completed_plans.clear();
11961e8e688SJim Ingham 
12061e8e688SJim Ingham   // Push a ThreadPlanNull on the plan stack.  That way we can continue
12161e8e688SJim Ingham   // assuming that the plan stack is never empty, but if somebody errantly asks
12261e8e688SJim Ingham   // questions of a destroyed thread without checking first whether it is
12361e8e688SJim Ingham   // destroyed, they won't crash.
12461e8e688SJim Ingham   if (thread != nullptr) {
12561e8e688SJim Ingham     lldb::ThreadPlanSP null_plan_sp(new ThreadPlanNull(*thread));
12661e8e688SJim Ingham     m_plans.push_back(null_plan_sp);
12761e8e688SJim Ingham   }
12861e8e688SJim Ingham }
12961e8e688SJim Ingham 
13061e8e688SJim Ingham void ThreadPlanStack::EnableTracer(bool value, bool single_stepping) {
13161e8e688SJim Ingham   for (ThreadPlanSP plan : m_plans) {
13261e8e688SJim Ingham     if (plan->GetThreadPlanTracer()) {
13361e8e688SJim Ingham       plan->GetThreadPlanTracer()->EnableTracing(value);
13461e8e688SJim Ingham       plan->GetThreadPlanTracer()->EnableSingleStep(single_stepping);
13561e8e688SJim Ingham     }
13661e8e688SJim Ingham   }
13761e8e688SJim Ingham }
13861e8e688SJim Ingham 
13961e8e688SJim Ingham void ThreadPlanStack::SetTracer(lldb::ThreadPlanTracerSP &tracer_sp) {
14061e8e688SJim Ingham   for (ThreadPlanSP plan : m_plans)
14161e8e688SJim Ingham     plan->SetThreadPlanTracer(tracer_sp);
14261e8e688SJim Ingham }
14361e8e688SJim Ingham 
14461e8e688SJim Ingham void ThreadPlanStack::PushPlan(lldb::ThreadPlanSP new_plan_sp) {
14561e8e688SJim Ingham   // If the thread plan doesn't already have a tracer, give it its parent's
14661e8e688SJim Ingham   // tracer:
14761e8e688SJim Ingham   // The first plan has to be a base plan:
14861e8e688SJim Ingham   assert(m_plans.size() > 0 ||
14961e8e688SJim Ingham          new_plan_sp->IsBasePlan() && "Zeroth plan must be a base plan");
15061e8e688SJim Ingham 
15161e8e688SJim Ingham   if (!new_plan_sp->GetThreadPlanTracer()) {
15261e8e688SJim Ingham     assert(!m_plans.empty());
15361e8e688SJim Ingham     new_plan_sp->SetThreadPlanTracer(m_plans.back()->GetThreadPlanTracer());
15461e8e688SJim Ingham   }
15561e8e688SJim Ingham   m_plans.push_back(new_plan_sp);
15661e8e688SJim Ingham   new_plan_sp->DidPush();
15761e8e688SJim Ingham }
15861e8e688SJim Ingham 
15961e8e688SJim Ingham lldb::ThreadPlanSP ThreadPlanStack::PopPlan() {
16061e8e688SJim Ingham   assert(m_plans.size() > 1 && "Can't pop the base thread plan");
16161e8e688SJim Ingham 
16261e8e688SJim Ingham   lldb::ThreadPlanSP &plan_sp = m_plans.back();
16361e8e688SJim Ingham   m_completed_plans.push_back(plan_sp);
16461e8e688SJim Ingham   plan_sp->WillPop();
16561e8e688SJim Ingham   m_plans.pop_back();
16661e8e688SJim Ingham   return plan_sp;
16761e8e688SJim Ingham }
16861e8e688SJim Ingham 
16961e8e688SJim Ingham lldb::ThreadPlanSP ThreadPlanStack::DiscardPlan() {
17061e8e688SJim Ingham   assert(m_plans.size() > 1 && "Can't discard the base thread plan");
17161e8e688SJim Ingham 
17261e8e688SJim Ingham   lldb::ThreadPlanSP &plan_sp = m_plans.back();
17361e8e688SJim Ingham   m_discarded_plans.push_back(plan_sp);
17461e8e688SJim Ingham   plan_sp->WillPop();
17561e8e688SJim Ingham   m_plans.pop_back();
17661e8e688SJim Ingham   return plan_sp;
17761e8e688SJim Ingham }
17861e8e688SJim Ingham 
17961e8e688SJim Ingham // If the input plan is nullptr, discard all plans.  Otherwise make sure this
18061e8e688SJim Ingham // plan is in the stack, and if so discard up to and including it.
18161e8e688SJim Ingham void ThreadPlanStack::DiscardPlansUpToPlan(ThreadPlan *up_to_plan_ptr) {
18261e8e688SJim Ingham   int stack_size = m_plans.size();
18361e8e688SJim Ingham 
18461e8e688SJim Ingham   if (up_to_plan_ptr == nullptr) {
18561e8e688SJim Ingham     for (int i = stack_size - 1; i > 0; i--)
18661e8e688SJim Ingham       DiscardPlan();
18761e8e688SJim Ingham     return;
18861e8e688SJim Ingham   }
18961e8e688SJim Ingham 
19061e8e688SJim Ingham   bool found_it = false;
19161e8e688SJim Ingham   for (int i = stack_size - 1; i > 0; i--) {
19261e8e688SJim Ingham     if (m_plans[i].get() == up_to_plan_ptr) {
19361e8e688SJim Ingham       found_it = true;
19461e8e688SJim Ingham       break;
19561e8e688SJim Ingham     }
19661e8e688SJim Ingham   }
19761e8e688SJim Ingham 
19861e8e688SJim Ingham   if (found_it) {
19961e8e688SJim Ingham     bool last_one = false;
20061e8e688SJim Ingham     for (int i = stack_size - 1; i > 0 && !last_one; i--) {
20161e8e688SJim Ingham       if (GetCurrentPlan().get() == up_to_plan_ptr)
20261e8e688SJim Ingham         last_one = true;
20361e8e688SJim Ingham       DiscardPlan();
20461e8e688SJim Ingham     }
20561e8e688SJim Ingham   }
20661e8e688SJim Ingham }
20761e8e688SJim Ingham 
20861e8e688SJim Ingham void ThreadPlanStack::DiscardAllPlans() {
20961e8e688SJim Ingham   int stack_size = m_plans.size();
21061e8e688SJim Ingham   for (int i = stack_size - 1; i > 0; i--) {
21161e8e688SJim Ingham     DiscardPlan();
21261e8e688SJim Ingham   }
21361e8e688SJim Ingham   return;
21461e8e688SJim Ingham }
21561e8e688SJim Ingham 
21661e8e688SJim Ingham void ThreadPlanStack::DiscardConsultingMasterPlans() {
21761e8e688SJim Ingham   while (true) {
21861e8e688SJim Ingham     int master_plan_idx;
21961e8e688SJim Ingham     bool discard = true;
22061e8e688SJim Ingham 
22161e8e688SJim Ingham     // Find the first master plan, see if it wants discarding, and if yes
22261e8e688SJim Ingham     // discard up to it.
22361e8e688SJim Ingham     for (master_plan_idx = m_plans.size() - 1; master_plan_idx >= 0;
22461e8e688SJim Ingham          master_plan_idx--) {
22561e8e688SJim Ingham       if (m_plans[master_plan_idx]->IsMasterPlan()) {
22661e8e688SJim Ingham         discard = m_plans[master_plan_idx]->OkayToDiscard();
22761e8e688SJim Ingham         break;
22861e8e688SJim Ingham       }
22961e8e688SJim Ingham     }
23061e8e688SJim Ingham 
23161e8e688SJim Ingham     // If the master plan doesn't want to get discarded, then we're done.
23261e8e688SJim Ingham     if (!discard)
23361e8e688SJim Ingham       return;
23461e8e688SJim Ingham 
23561e8e688SJim Ingham     // First pop all the dependent plans:
23661e8e688SJim Ingham     for (int i = m_plans.size() - 1; i > master_plan_idx; i--) {
23761e8e688SJim Ingham       DiscardPlan();
23861e8e688SJim Ingham     }
23961e8e688SJim Ingham 
24061e8e688SJim Ingham     // Now discard the master plan itself.
24161e8e688SJim Ingham     // The bottom-most plan never gets discarded.  "OkayToDiscard" for it
24261e8e688SJim Ingham     // means discard it's dependent plans, but not it...
24361e8e688SJim Ingham     if (master_plan_idx > 0) {
24461e8e688SJim Ingham       DiscardPlan();
24561e8e688SJim Ingham     }
24661e8e688SJim Ingham   }
24761e8e688SJim Ingham }
24861e8e688SJim Ingham 
24961e8e688SJim Ingham lldb::ThreadPlanSP ThreadPlanStack::GetCurrentPlan() const {
25061e8e688SJim Ingham   assert(m_plans.size() != 0 && "There will always be a base plan.");
25161e8e688SJim Ingham   return m_plans.back();
25261e8e688SJim Ingham }
25361e8e688SJim Ingham 
25461e8e688SJim Ingham lldb::ThreadPlanSP ThreadPlanStack::GetCompletedPlan(bool skip_private) const {
25561e8e688SJim Ingham   if (m_completed_plans.empty())
25661e8e688SJim Ingham     return {};
25761e8e688SJim Ingham 
25861e8e688SJim Ingham   if (!skip_private)
25961e8e688SJim Ingham     return m_completed_plans.back();
26061e8e688SJim Ingham 
26161e8e688SJim Ingham   for (int i = m_completed_plans.size() - 1; i >= 0; i--) {
26261e8e688SJim Ingham     lldb::ThreadPlanSP completed_plan_sp;
26361e8e688SJim Ingham     completed_plan_sp = m_completed_plans[i];
26461e8e688SJim Ingham     if (!completed_plan_sp->GetPrivate())
26561e8e688SJim Ingham       return completed_plan_sp;
26661e8e688SJim Ingham   }
26761e8e688SJim Ingham   return {};
26861e8e688SJim Ingham }
26961e8e688SJim Ingham 
27061e8e688SJim Ingham lldb::ThreadPlanSP ThreadPlanStack::GetPlanByIndex(uint32_t plan_idx,
27161e8e688SJim Ingham                                                    bool skip_private) const {
27261e8e688SJim Ingham   uint32_t idx = 0;
27361e8e688SJim Ingham   ThreadPlan *up_to_plan_ptr = nullptr;
27461e8e688SJim Ingham 
27561e8e688SJim Ingham   for (lldb::ThreadPlanSP plan_sp : m_plans) {
27661e8e688SJim Ingham     if (skip_private && plan_sp->GetPrivate())
27761e8e688SJim Ingham       continue;
27861e8e688SJim Ingham     if (idx == plan_idx)
27961e8e688SJim Ingham       return plan_sp;
28061e8e688SJim Ingham     idx++;
28161e8e688SJim Ingham   }
28261e8e688SJim Ingham   return {};
28361e8e688SJim Ingham }
28461e8e688SJim Ingham 
28561e8e688SJim Ingham lldb::ValueObjectSP ThreadPlanStack::GetReturnValueObject() const {
28661e8e688SJim Ingham   if (m_completed_plans.empty())
28761e8e688SJim Ingham     return {};
28861e8e688SJim Ingham 
28961e8e688SJim Ingham   for (int i = m_completed_plans.size() - 1; i >= 0; i--) {
29061e8e688SJim Ingham     lldb::ValueObjectSP return_valobj_sp;
29161e8e688SJim Ingham     return_valobj_sp = m_completed_plans[i]->GetReturnValueObject();
29261e8e688SJim Ingham     if (return_valobj_sp)
29361e8e688SJim Ingham       return return_valobj_sp;
29461e8e688SJim Ingham   }
29561e8e688SJim Ingham   return {};
29661e8e688SJim Ingham }
29761e8e688SJim Ingham 
29861e8e688SJim Ingham lldb::ExpressionVariableSP ThreadPlanStack::GetExpressionVariable() const {
29961e8e688SJim Ingham   if (m_completed_plans.empty())
30061e8e688SJim Ingham     return {};
30161e8e688SJim Ingham 
30261e8e688SJim Ingham   for (int i = m_completed_plans.size() - 1; i >= 0; i--) {
30361e8e688SJim Ingham     lldb::ExpressionVariableSP expression_variable_sp;
30461e8e688SJim Ingham     expression_variable_sp = m_completed_plans[i]->GetExpressionVariable();
30561e8e688SJim Ingham     if (expression_variable_sp)
30661e8e688SJim Ingham       return expression_variable_sp;
30761e8e688SJim Ingham   }
30861e8e688SJim Ingham   return {};
30961e8e688SJim Ingham }
31061e8e688SJim Ingham bool ThreadPlanStack::AnyPlans() const {
31161e8e688SJim Ingham   // There is always a base plan...
31261e8e688SJim Ingham   return m_plans.size() > 1;
31361e8e688SJim Ingham }
31461e8e688SJim Ingham 
31561e8e688SJim Ingham bool ThreadPlanStack::AnyCompletedPlans() const {
31661e8e688SJim Ingham   return !m_completed_plans.empty();
31761e8e688SJim Ingham }
31861e8e688SJim Ingham 
31961e8e688SJim Ingham bool ThreadPlanStack::AnyDiscardedPlans() const {
32061e8e688SJim Ingham   return !m_discarded_plans.empty();
32161e8e688SJim Ingham }
32261e8e688SJim Ingham 
32361e8e688SJim Ingham bool ThreadPlanStack::IsPlanDone(ThreadPlan *in_plan) const {
32461e8e688SJim Ingham   for (auto plan : m_completed_plans) {
32561e8e688SJim Ingham     if (plan.get() == in_plan)
32661e8e688SJim Ingham       return true;
32761e8e688SJim Ingham   }
32861e8e688SJim Ingham   return false;
32961e8e688SJim Ingham }
33061e8e688SJim Ingham 
33161e8e688SJim Ingham bool ThreadPlanStack::WasPlanDiscarded(ThreadPlan *in_plan) const {
33261e8e688SJim Ingham   for (auto plan : m_discarded_plans) {
33361e8e688SJim Ingham     if (plan.get() == in_plan)
33461e8e688SJim Ingham       return true;
33561e8e688SJim Ingham   }
33661e8e688SJim Ingham   return false;
33761e8e688SJim Ingham }
33861e8e688SJim Ingham 
33961e8e688SJim Ingham ThreadPlan *ThreadPlanStack::GetPreviousPlan(ThreadPlan *current_plan) const {
34061e8e688SJim Ingham   if (current_plan == nullptr)
34161e8e688SJim Ingham     return nullptr;
34261e8e688SJim Ingham 
34361e8e688SJim Ingham   // Look first in the completed plans, if the plan is here and there is
34461e8e688SJim Ingham   // a completed plan above it, return that.
34561e8e688SJim Ingham   int stack_size = m_completed_plans.size();
34661e8e688SJim Ingham   for (int i = stack_size - 1; i > 0; i--) {
34761e8e688SJim Ingham     if (current_plan == m_completed_plans[i].get())
34861e8e688SJim Ingham       return m_completed_plans[i - 1].get();
34961e8e688SJim Ingham   }
35061e8e688SJim Ingham 
35161e8e688SJim Ingham   // If this is the first completed plan, the previous one is the
35261e8e688SJim Ingham   // bottom of the regular plan stack.
35361e8e688SJim Ingham   if (stack_size > 0 && m_completed_plans[0].get() == current_plan) {
35461e8e688SJim Ingham     return GetCurrentPlan().get();
35561e8e688SJim Ingham   }
35661e8e688SJim Ingham 
35761e8e688SJim Ingham   // Otherwise look for it in the regular plans.
35861e8e688SJim Ingham   stack_size = m_plans.size();
35961e8e688SJim Ingham   for (int i = stack_size - 1; i > 0; i--) {
36061e8e688SJim Ingham     if (current_plan == m_plans[i].get())
36161e8e688SJim Ingham       return m_plans[i - 1].get();
36261e8e688SJim Ingham   }
36361e8e688SJim Ingham   return nullptr;
36461e8e688SJim Ingham }
36561e8e688SJim Ingham 
36661e8e688SJim Ingham ThreadPlan *ThreadPlanStack::GetInnermostExpression() const {
36761e8e688SJim Ingham   int stack_size = m_plans.size();
36861e8e688SJim Ingham 
36961e8e688SJim Ingham   for (int i = stack_size - 1; i > 0; i--) {
37061e8e688SJim Ingham     if (m_plans[i]->GetKind() == ThreadPlan::eKindCallFunction)
37161e8e688SJim Ingham       return m_plans[i].get();
37261e8e688SJim Ingham   }
37361e8e688SJim Ingham   return nullptr;
37461e8e688SJim Ingham }
37561e8e688SJim Ingham 
37661e8e688SJim Ingham void ThreadPlanStack::WillResume() {
37761e8e688SJim Ingham   m_completed_plans.clear();
37861e8e688SJim Ingham   m_discarded_plans.clear();
37961e8e688SJim Ingham }
38061e8e688SJim Ingham 
38161e8e688SJim Ingham const ThreadPlanStack::PlanStack &
38261e8e688SJim Ingham ThreadPlanStack::GetStackOfKind(ThreadPlanStack::StackKind kind) const {
38361e8e688SJim Ingham   switch (kind) {
38461e8e688SJim Ingham   case ePlans:
38561e8e688SJim Ingham     return m_plans;
38661e8e688SJim Ingham   case eCompletedPlans:
38761e8e688SJim Ingham     return m_completed_plans;
38861e8e688SJim Ingham   case eDiscardedPlans:
38961e8e688SJim Ingham     return m_discarded_plans;
39061e8e688SJim Ingham   }
39161e8e688SJim Ingham   llvm_unreachable("Invalid StackKind value");
39261e8e688SJim Ingham }
393*1893065dSJim Ingham 
394*1893065dSJim Ingham void ThreadPlanStackMap::Update(ThreadList &current_threads,
395*1893065dSJim Ingham                                 bool delete_missing,
396*1893065dSJim Ingham                                 bool check_for_new) {
397*1893065dSJim Ingham 
398*1893065dSJim Ingham   // Now find all the new threads and add them to the map:
399*1893065dSJim Ingham   if (check_for_new) {
400*1893065dSJim Ingham     for (auto thread : current_threads.Threads()) {
401*1893065dSJim Ingham       lldb::tid_t cur_tid = thread->GetID();
402*1893065dSJim Ingham       if (!Find(cur_tid)) {
403*1893065dSJim Ingham         AddThread(*thread.get());
404*1893065dSJim Ingham         thread->QueueFundamentalPlan(true);
405*1893065dSJim Ingham       }
406*1893065dSJim Ingham     }
407*1893065dSJim Ingham   }
408*1893065dSJim Ingham 
409*1893065dSJim Ingham   // If we aren't reaping missing threads at this point,
410*1893065dSJim Ingham   // we are done.
411*1893065dSJim Ingham   if (!delete_missing)
412*1893065dSJim Ingham     return;
413*1893065dSJim Ingham   // Otherwise scan for absent TID's.
414*1893065dSJim Ingham   std::vector<lldb::tid_t> missing_threads;
415*1893065dSJim Ingham   // If we are going to delete plans from the plan stack,
416*1893065dSJim Ingham   // then scan for absent TID's:
417*1893065dSJim Ingham   for (auto thread_plans : m_plans_list) {
418*1893065dSJim Ingham     lldb::tid_t cur_tid = thread_plans.first;
419*1893065dSJim Ingham     ThreadSP thread_sp = current_threads.FindThreadByID(cur_tid);
420*1893065dSJim Ingham     if (!thread_sp)
421*1893065dSJim Ingham       missing_threads.push_back(cur_tid);
422*1893065dSJim Ingham   }
423*1893065dSJim Ingham   for (lldb::tid_t tid : missing_threads) {
424*1893065dSJim Ingham     RemoveTID(tid);
425*1893065dSJim Ingham   }
426*1893065dSJim Ingham }
427*1893065dSJim Ingham 
428*1893065dSJim Ingham void ThreadPlanStackMap::DumpPlans(Stream &strm,
429*1893065dSJim Ingham                                    lldb::DescriptionLevel desc_level,
430*1893065dSJim Ingham                                    bool internal, bool condense_if_trivial,
431*1893065dSJim Ingham                                    bool skip_unreported) {
432*1893065dSJim Ingham   for (auto elem : m_plans_list) {
433*1893065dSJim Ingham     lldb::tid_t tid = elem.first;
434*1893065dSJim Ingham     uint32_t index_id = 0;
435*1893065dSJim Ingham     ThreadSP thread_sp = m_process.GetThreadList().FindThreadByID(tid);
436*1893065dSJim Ingham 
437*1893065dSJim Ingham     if (skip_unreported) {
438*1893065dSJim Ingham       if (!thread_sp)
439*1893065dSJim Ingham         continue;
440*1893065dSJim Ingham     }
441*1893065dSJim Ingham     if (thread_sp)
442*1893065dSJim Ingham       index_id = thread_sp->GetIndexID();
443*1893065dSJim Ingham 
444*1893065dSJim Ingham     if (condense_if_trivial) {
445*1893065dSJim Ingham       if (!elem.second.AnyPlans() && !elem.second.AnyCompletedPlans() &&
446*1893065dSJim Ingham           !elem.second.AnyDiscardedPlans()) {
447*1893065dSJim Ingham         strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 "\n", index_id, tid);
448*1893065dSJim Ingham         strm.IndentMore();
449*1893065dSJim Ingham         strm.Indent();
450*1893065dSJim Ingham         strm.Printf("No active thread plans\n");
451*1893065dSJim Ingham         strm.IndentLess();
452*1893065dSJim Ingham         return;
453*1893065dSJim Ingham       }
454*1893065dSJim Ingham     }
455*1893065dSJim Ingham 
456*1893065dSJim Ingham     strm.Indent();
457*1893065dSJim Ingham     strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 ":\n", index_id, tid);
458*1893065dSJim Ingham 
459*1893065dSJim Ingham     elem.second.DumpThreadPlans(strm, desc_level, internal);
460*1893065dSJim Ingham   }
461*1893065dSJim Ingham }
462*1893065dSJim Ingham 
463*1893065dSJim Ingham bool ThreadPlanStackMap::DumpPlansForTID(Stream &strm, lldb::tid_t tid,
464*1893065dSJim Ingham                                          lldb::DescriptionLevel desc_level,
465*1893065dSJim Ingham                                          bool internal,
466*1893065dSJim Ingham                                          bool condense_if_trivial,
467*1893065dSJim Ingham                                          bool skip_unreported) {
468*1893065dSJim Ingham   uint32_t index_id = 0;
469*1893065dSJim Ingham   ThreadSP thread_sp = m_process.GetThreadList().FindThreadByID(tid);
470*1893065dSJim Ingham 
471*1893065dSJim Ingham   if (skip_unreported) {
472*1893065dSJim Ingham     if (!thread_sp) {
473*1893065dSJim Ingham       strm.Format("Unknown TID: {0}", tid);
474*1893065dSJim Ingham       return false;
475*1893065dSJim Ingham     }
476*1893065dSJim Ingham   }
477*1893065dSJim Ingham 
478*1893065dSJim Ingham   if (thread_sp)
479*1893065dSJim Ingham     index_id = thread_sp->GetIndexID();
480*1893065dSJim Ingham   ThreadPlanStack *stack = Find(tid);
481*1893065dSJim Ingham   if (!stack) {
482*1893065dSJim Ingham     strm.Format("Unknown TID: {0}\n", tid);
483*1893065dSJim Ingham     return false;
484*1893065dSJim Ingham   }
485*1893065dSJim Ingham 
486*1893065dSJim Ingham   if (condense_if_trivial) {
487*1893065dSJim Ingham     if (!stack->AnyPlans() && !stack->AnyCompletedPlans() &&
488*1893065dSJim Ingham         !stack->AnyDiscardedPlans()) {
489*1893065dSJim Ingham       strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 "\n", index_id, tid);
490*1893065dSJim Ingham       strm.IndentMore();
491*1893065dSJim Ingham       strm.Indent();
492*1893065dSJim Ingham       strm.Printf("No active thread plans\n");
493*1893065dSJim Ingham       strm.IndentLess();
494*1893065dSJim Ingham       return true;
495*1893065dSJim Ingham     }
496*1893065dSJim Ingham   }
497*1893065dSJim Ingham 
498*1893065dSJim Ingham   strm.Indent();
499*1893065dSJim Ingham   strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 ":\n", index_id, tid);
500*1893065dSJim Ingham 
501*1893065dSJim Ingham   stack->DumpThreadPlans(strm, desc_level, internal);
502*1893065dSJim Ingham   return true;
503*1893065dSJim Ingham }
504*1893065dSJim Ingham 
505*1893065dSJim Ingham bool ThreadPlanStackMap::PrunePlansForTID(lldb::tid_t tid) {
506*1893065dSJim Ingham   // We only remove the plans for unreported TID's.
507*1893065dSJim Ingham   ThreadSP thread_sp = m_process.GetThreadList().FindThreadByID(tid);
508*1893065dSJim Ingham   if (thread_sp)
509*1893065dSJim Ingham     return false;
510*1893065dSJim Ingham 
511*1893065dSJim Ingham   return RemoveTID(tid);
512*1893065dSJim Ingham }
513