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 
191893065dSJim Ingham static void PrintPlanElement(Stream &s, const ThreadPlanSP &plan,
2061e8e688SJim Ingham                              lldb::DescriptionLevel desc_level,
2161e8e688SJim Ingham                              int32_t elem_idx) {
221893065dSJim Ingham   s.IndentMore();
231893065dSJim Ingham   s.Indent();
241893065dSJim Ingham   s.Printf("Element %d: ", elem_idx);
251893065dSJim Ingham   plan->GetDescription(&s, desc_level);
261893065dSJim Ingham   s.EOL();
271893065dSJim Ingham   s.IndentLess();
2861e8e688SJim Ingham }
2961e8e688SJim Ingham 
301893065dSJim Ingham ThreadPlanStack::ThreadPlanStack(const Thread &thread, bool make_null) {
311893065dSJim Ingham   if (make_null) {
321893065dSJim Ingham     // The ThreadPlanNull doesn't do anything to the Thread, so this is actually
331893065dSJim Ingham     // still a const operation.
341893065dSJim Ingham     m_plans.push_back(
351893065dSJim Ingham         ThreadPlanSP(new ThreadPlanNull(const_cast<Thread &>(thread))));
361893065dSJim Ingham   }
371893065dSJim Ingham }
381893065dSJim Ingham 
391893065dSJim Ingham void ThreadPlanStack::DumpThreadPlans(Stream &s,
4061e8e688SJim Ingham                                       lldb::DescriptionLevel desc_level,
4161e8e688SJim Ingham                                       bool include_internal) const {
421893065dSJim Ingham   s.IndentMore();
431893065dSJim Ingham   PrintOneStack(s, "Active plan stack", m_plans, desc_level, include_internal);
441893065dSJim Ingham   PrintOneStack(s, "Completed plan stack", m_completed_plans, desc_level,
451893065dSJim Ingham                 include_internal);
461893065dSJim Ingham   PrintOneStack(s, "Discarded plan stack", m_discarded_plans, desc_level,
471893065dSJim Ingham                 include_internal);
481893065dSJim Ingham   s.IndentLess();
4961e8e688SJim Ingham }
5061e8e688SJim Ingham 
511893065dSJim Ingham void ThreadPlanStack::PrintOneStack(Stream &s, llvm::StringRef stack_name,
521893065dSJim Ingham                                     const PlanStack &stack,
531893065dSJim Ingham                                     lldb::DescriptionLevel desc_level,
541893065dSJim Ingham                                     bool include_internal) const {
551893065dSJim Ingham   // If the stack is empty, just exit:
561893065dSJim Ingham   if (stack.empty())
571893065dSJim Ingham     return;
581893065dSJim Ingham 
591893065dSJim Ingham   // Make sure there are public completed plans:
601893065dSJim Ingham   bool any_public = false;
611893065dSJim Ingham   if (!include_internal) {
621893065dSJim Ingham     for (auto plan : stack) {
631893065dSJim Ingham       if (!plan->GetPrivate()) {
641893065dSJim Ingham         any_public = true;
651893065dSJim Ingham         break;
661893065dSJim Ingham       }
671893065dSJim Ingham     }
6861e8e688SJim Ingham   }
6961e8e688SJim Ingham 
701893065dSJim Ingham   if (include_internal || any_public) {
711893065dSJim Ingham     int print_idx = 0;
721893065dSJim Ingham     s.Indent();
733ccd454cSEric Christopher     s << stack_name << ":\n";
741893065dSJim Ingham     for (auto plan : stack) {
751893065dSJim Ingham       if (!include_internal && plan->GetPrivate())
761893065dSJim Ingham         continue;
7761e8e688SJim Ingham       PrintPlanElement(s, plan, desc_level, print_idx++);
7861e8e688SJim Ingham     }
791893065dSJim Ingham   }
8061e8e688SJim Ingham }
8161e8e688SJim Ingham 
8261e8e688SJim Ingham size_t ThreadPlanStack::CheckpointCompletedPlans() {
8361e8e688SJim Ingham   m_completed_plan_checkpoint++;
8461e8e688SJim Ingham   m_completed_plan_store.insert(
8561e8e688SJim Ingham       std::make_pair(m_completed_plan_checkpoint, m_completed_plans));
8661e8e688SJim Ingham   return m_completed_plan_checkpoint;
8761e8e688SJim Ingham }
8861e8e688SJim Ingham 
8961e8e688SJim Ingham void ThreadPlanStack::RestoreCompletedPlanCheckpoint(size_t checkpoint) {
9061e8e688SJim Ingham   auto result = m_completed_plan_store.find(checkpoint);
9161e8e688SJim Ingham   assert(result != m_completed_plan_store.end() &&
9261e8e688SJim Ingham          "Asked for a checkpoint that didn't exist");
9361e8e688SJim Ingham   m_completed_plans.swap((*result).second);
9461e8e688SJim Ingham   m_completed_plan_store.erase(result);
9561e8e688SJim Ingham }
9661e8e688SJim Ingham 
9761e8e688SJim Ingham void ThreadPlanStack::DiscardCompletedPlanCheckpoint(size_t checkpoint) {
9861e8e688SJim Ingham   m_completed_plan_store.erase(checkpoint);
9961e8e688SJim Ingham }
10061e8e688SJim Ingham 
10161e8e688SJim Ingham void ThreadPlanStack::ThreadDestroyed(Thread *thread) {
10261e8e688SJim Ingham   // Tell the plan stacks that this thread is going away:
10361e8e688SJim Ingham   for (ThreadPlanSP plan : m_plans)
10461e8e688SJim Ingham     plan->ThreadDestroyed();
10561e8e688SJim Ingham 
10661e8e688SJim Ingham   for (ThreadPlanSP plan : m_discarded_plans)
10761e8e688SJim Ingham     plan->ThreadDestroyed();
10861e8e688SJim Ingham 
10961e8e688SJim Ingham   for (ThreadPlanSP plan : m_completed_plans)
11061e8e688SJim Ingham     plan->ThreadDestroyed();
11161e8e688SJim Ingham 
11261e8e688SJim Ingham   // Now clear the current plan stacks:
11361e8e688SJim Ingham   m_plans.clear();
11461e8e688SJim Ingham   m_discarded_plans.clear();
11561e8e688SJim Ingham   m_completed_plans.clear();
11661e8e688SJim Ingham 
11761e8e688SJim Ingham   // Push a ThreadPlanNull on the plan stack.  That way we can continue
11861e8e688SJim Ingham   // assuming that the plan stack is never empty, but if somebody errantly asks
11961e8e688SJim Ingham   // questions of a destroyed thread without checking first whether it is
12061e8e688SJim Ingham   // destroyed, they won't crash.
12161e8e688SJim Ingham   if (thread != nullptr) {
12261e8e688SJim Ingham     lldb::ThreadPlanSP null_plan_sp(new ThreadPlanNull(*thread));
12361e8e688SJim Ingham     m_plans.push_back(null_plan_sp);
12461e8e688SJim Ingham   }
12561e8e688SJim Ingham }
12661e8e688SJim Ingham 
12761e8e688SJim Ingham void ThreadPlanStack::PushPlan(lldb::ThreadPlanSP new_plan_sp) {
12861e8e688SJim Ingham   // If the thread plan doesn't already have a tracer, give it its parent's
12961e8e688SJim Ingham   // tracer:
13061e8e688SJim Ingham   // The first plan has to be a base plan:
131ff18a6acSPavel Labath   assert((m_plans.size() > 0 || new_plan_sp->IsBasePlan()) &&
132ff18a6acSPavel Labath          "Zeroth plan must be a base plan");
13361e8e688SJim Ingham 
13461e8e688SJim Ingham   if (!new_plan_sp->GetThreadPlanTracer()) {
13561e8e688SJim Ingham     assert(!m_plans.empty());
13661e8e688SJim Ingham     new_plan_sp->SetThreadPlanTracer(m_plans.back()->GetThreadPlanTracer());
13761e8e688SJim Ingham   }
13861e8e688SJim Ingham   m_plans.push_back(new_plan_sp);
13961e8e688SJim Ingham   new_plan_sp->DidPush();
14061e8e688SJim Ingham }
14161e8e688SJim Ingham 
14261e8e688SJim Ingham lldb::ThreadPlanSP ThreadPlanStack::PopPlan() {
14361e8e688SJim Ingham   assert(m_plans.size() > 1 && "Can't pop the base thread plan");
14461e8e688SJim Ingham 
145873b79b8SBenjamin Kramer   lldb::ThreadPlanSP plan_sp = std::move(m_plans.back());
14661e8e688SJim Ingham   m_completed_plans.push_back(plan_sp);
14761e8e688SJim Ingham   plan_sp->WillPop();
14861e8e688SJim Ingham   m_plans.pop_back();
14961e8e688SJim Ingham   return plan_sp;
15061e8e688SJim Ingham }
15161e8e688SJim Ingham 
15261e8e688SJim Ingham lldb::ThreadPlanSP ThreadPlanStack::DiscardPlan() {
15361e8e688SJim Ingham   assert(m_plans.size() > 1 && "Can't discard the base thread plan");
15461e8e688SJim Ingham 
155873b79b8SBenjamin Kramer   lldb::ThreadPlanSP plan_sp = std::move(m_plans.back());
15661e8e688SJim Ingham   m_discarded_plans.push_back(plan_sp);
15761e8e688SJim Ingham   plan_sp->WillPop();
15861e8e688SJim Ingham   m_plans.pop_back();
15961e8e688SJim Ingham   return plan_sp;
16061e8e688SJim Ingham }
16161e8e688SJim Ingham 
16261e8e688SJim Ingham // If the input plan is nullptr, discard all plans.  Otherwise make sure this
16361e8e688SJim Ingham // plan is in the stack, and if so discard up to and including it.
16461e8e688SJim Ingham void ThreadPlanStack::DiscardPlansUpToPlan(ThreadPlan *up_to_plan_ptr) {
16561e8e688SJim Ingham   int stack_size = m_plans.size();
16661e8e688SJim Ingham 
16761e8e688SJim Ingham   if (up_to_plan_ptr == nullptr) {
16861e8e688SJim Ingham     for (int i = stack_size - 1; i > 0; i--)
16961e8e688SJim Ingham       DiscardPlan();
17061e8e688SJim Ingham     return;
17161e8e688SJim Ingham   }
17261e8e688SJim Ingham 
17361e8e688SJim Ingham   bool found_it = false;
17461e8e688SJim Ingham   for (int i = stack_size - 1; i > 0; i--) {
17561e8e688SJim Ingham     if (m_plans[i].get() == up_to_plan_ptr) {
17661e8e688SJim Ingham       found_it = true;
17761e8e688SJim Ingham       break;
17861e8e688SJim Ingham     }
17961e8e688SJim Ingham   }
18061e8e688SJim Ingham 
18161e8e688SJim Ingham   if (found_it) {
18261e8e688SJim Ingham     bool last_one = false;
18361e8e688SJim Ingham     for (int i = stack_size - 1; i > 0 && !last_one; i--) {
18461e8e688SJim Ingham       if (GetCurrentPlan().get() == up_to_plan_ptr)
18561e8e688SJim Ingham         last_one = true;
18661e8e688SJim Ingham       DiscardPlan();
18761e8e688SJim Ingham     }
18861e8e688SJim Ingham   }
18961e8e688SJim Ingham }
19061e8e688SJim Ingham 
19161e8e688SJim Ingham void ThreadPlanStack::DiscardAllPlans() {
19261e8e688SJim Ingham   int stack_size = m_plans.size();
19361e8e688SJim Ingham   for (int i = stack_size - 1; i > 0; i--) {
19461e8e688SJim Ingham     DiscardPlan();
19561e8e688SJim Ingham   }
19661e8e688SJim Ingham   return;
19761e8e688SJim Ingham }
19861e8e688SJim Ingham 
19961e8e688SJim Ingham void ThreadPlanStack::DiscardConsultingMasterPlans() {
20061e8e688SJim Ingham   while (true) {
20161e8e688SJim Ingham     int master_plan_idx;
20261e8e688SJim Ingham     bool discard = true;
20361e8e688SJim Ingham 
20461e8e688SJim Ingham     // Find the first master plan, see if it wants discarding, and if yes
20561e8e688SJim Ingham     // discard up to it.
20661e8e688SJim Ingham     for (master_plan_idx = m_plans.size() - 1; master_plan_idx >= 0;
20761e8e688SJim Ingham          master_plan_idx--) {
20861e8e688SJim Ingham       if (m_plans[master_plan_idx]->IsMasterPlan()) {
20961e8e688SJim Ingham         discard = m_plans[master_plan_idx]->OkayToDiscard();
21061e8e688SJim Ingham         break;
21161e8e688SJim Ingham       }
21261e8e688SJim Ingham     }
21361e8e688SJim Ingham 
21461e8e688SJim Ingham     // If the master plan doesn't want to get discarded, then we're done.
21561e8e688SJim Ingham     if (!discard)
21661e8e688SJim Ingham       return;
21761e8e688SJim Ingham 
21861e8e688SJim Ingham     // First pop all the dependent plans:
21961e8e688SJim Ingham     for (int i = m_plans.size() - 1; i > master_plan_idx; i--) {
22061e8e688SJim Ingham       DiscardPlan();
22161e8e688SJim Ingham     }
22261e8e688SJim Ingham 
22361e8e688SJim Ingham     // Now discard the master plan itself.
22461e8e688SJim Ingham     // The bottom-most plan never gets discarded.  "OkayToDiscard" for it
22561e8e688SJim Ingham     // means discard it's dependent plans, but not it...
22661e8e688SJim Ingham     if (master_plan_idx > 0) {
22761e8e688SJim Ingham       DiscardPlan();
22861e8e688SJim Ingham     }
22961e8e688SJim Ingham   }
23061e8e688SJim Ingham }
23161e8e688SJim Ingham 
23261e8e688SJim Ingham lldb::ThreadPlanSP ThreadPlanStack::GetCurrentPlan() const {
23361e8e688SJim Ingham   assert(m_plans.size() != 0 && "There will always be a base plan.");
23461e8e688SJim Ingham   return m_plans.back();
23561e8e688SJim Ingham }
23661e8e688SJim Ingham 
23761e8e688SJim Ingham lldb::ThreadPlanSP ThreadPlanStack::GetCompletedPlan(bool skip_private) const {
23861e8e688SJim Ingham   if (m_completed_plans.empty())
23961e8e688SJim Ingham     return {};
24061e8e688SJim Ingham 
24161e8e688SJim Ingham   if (!skip_private)
24261e8e688SJim Ingham     return m_completed_plans.back();
24361e8e688SJim Ingham 
24461e8e688SJim Ingham   for (int i = m_completed_plans.size() - 1; i >= 0; i--) {
24561e8e688SJim Ingham     lldb::ThreadPlanSP completed_plan_sp;
24661e8e688SJim Ingham     completed_plan_sp = m_completed_plans[i];
24761e8e688SJim Ingham     if (!completed_plan_sp->GetPrivate())
24861e8e688SJim Ingham       return completed_plan_sp;
24961e8e688SJim Ingham   }
25061e8e688SJim Ingham   return {};
25161e8e688SJim Ingham }
25261e8e688SJim Ingham 
25361e8e688SJim Ingham lldb::ThreadPlanSP ThreadPlanStack::GetPlanByIndex(uint32_t plan_idx,
25461e8e688SJim Ingham                                                    bool skip_private) const {
25561e8e688SJim Ingham   uint32_t idx = 0;
25661e8e688SJim Ingham 
25761e8e688SJim Ingham   for (lldb::ThreadPlanSP plan_sp : m_plans) {
25861e8e688SJim Ingham     if (skip_private && plan_sp->GetPrivate())
25961e8e688SJim Ingham       continue;
26061e8e688SJim Ingham     if (idx == plan_idx)
26161e8e688SJim Ingham       return plan_sp;
26261e8e688SJim Ingham     idx++;
26361e8e688SJim Ingham   }
26461e8e688SJim Ingham   return {};
26561e8e688SJim Ingham }
26661e8e688SJim Ingham 
26761e8e688SJim Ingham lldb::ValueObjectSP ThreadPlanStack::GetReturnValueObject() const {
26861e8e688SJim Ingham   if (m_completed_plans.empty())
26961e8e688SJim Ingham     return {};
27061e8e688SJim Ingham 
27161e8e688SJim Ingham   for (int i = m_completed_plans.size() - 1; i >= 0; i--) {
27261e8e688SJim Ingham     lldb::ValueObjectSP return_valobj_sp;
27361e8e688SJim Ingham     return_valobj_sp = m_completed_plans[i]->GetReturnValueObject();
27461e8e688SJim Ingham     if (return_valobj_sp)
27561e8e688SJim Ingham       return return_valobj_sp;
27661e8e688SJim Ingham   }
27761e8e688SJim Ingham   return {};
27861e8e688SJim Ingham }
27961e8e688SJim Ingham 
28061e8e688SJim Ingham lldb::ExpressionVariableSP ThreadPlanStack::GetExpressionVariable() const {
28161e8e688SJim Ingham   if (m_completed_plans.empty())
28261e8e688SJim Ingham     return {};
28361e8e688SJim Ingham 
28461e8e688SJim Ingham   for (int i = m_completed_plans.size() - 1; i >= 0; i--) {
28561e8e688SJim Ingham     lldb::ExpressionVariableSP expression_variable_sp;
28661e8e688SJim Ingham     expression_variable_sp = m_completed_plans[i]->GetExpressionVariable();
28761e8e688SJim Ingham     if (expression_variable_sp)
28861e8e688SJim Ingham       return expression_variable_sp;
28961e8e688SJim Ingham   }
29061e8e688SJim Ingham   return {};
29161e8e688SJim Ingham }
29261e8e688SJim Ingham bool ThreadPlanStack::AnyPlans() const {
29361e8e688SJim Ingham   // There is always a base plan...
29461e8e688SJim Ingham   return m_plans.size() > 1;
29561e8e688SJim Ingham }
29661e8e688SJim Ingham 
29761e8e688SJim Ingham bool ThreadPlanStack::AnyCompletedPlans() const {
29861e8e688SJim Ingham   return !m_completed_plans.empty();
29961e8e688SJim Ingham }
30061e8e688SJim Ingham 
30161e8e688SJim Ingham bool ThreadPlanStack::AnyDiscardedPlans() const {
30261e8e688SJim Ingham   return !m_discarded_plans.empty();
30361e8e688SJim Ingham }
30461e8e688SJim Ingham 
30561e8e688SJim Ingham bool ThreadPlanStack::IsPlanDone(ThreadPlan *in_plan) const {
30661e8e688SJim Ingham   for (auto plan : m_completed_plans) {
30761e8e688SJim Ingham     if (plan.get() == in_plan)
30861e8e688SJim Ingham       return true;
30961e8e688SJim Ingham   }
31061e8e688SJim Ingham   return false;
31161e8e688SJim Ingham }
31261e8e688SJim Ingham 
31361e8e688SJim Ingham bool ThreadPlanStack::WasPlanDiscarded(ThreadPlan *in_plan) const {
31461e8e688SJim Ingham   for (auto plan : m_discarded_plans) {
31561e8e688SJim Ingham     if (plan.get() == in_plan)
31661e8e688SJim Ingham       return true;
31761e8e688SJim Ingham   }
31861e8e688SJim Ingham   return false;
31961e8e688SJim Ingham }
32061e8e688SJim Ingham 
32161e8e688SJim Ingham ThreadPlan *ThreadPlanStack::GetPreviousPlan(ThreadPlan *current_plan) const {
32261e8e688SJim Ingham   if (current_plan == nullptr)
32361e8e688SJim Ingham     return nullptr;
32461e8e688SJim Ingham 
32561e8e688SJim Ingham   // Look first in the completed plans, if the plan is here and there is
32661e8e688SJim Ingham   // a completed plan above it, return that.
32761e8e688SJim Ingham   int stack_size = m_completed_plans.size();
32861e8e688SJim Ingham   for (int i = stack_size - 1; i > 0; i--) {
32961e8e688SJim Ingham     if (current_plan == m_completed_plans[i].get())
33061e8e688SJim Ingham       return m_completed_plans[i - 1].get();
33161e8e688SJim Ingham   }
33261e8e688SJim Ingham 
33361e8e688SJim Ingham   // If this is the first completed plan, the previous one is the
33461e8e688SJim Ingham   // bottom of the regular plan stack.
33561e8e688SJim Ingham   if (stack_size > 0 && m_completed_plans[0].get() == current_plan) {
33661e8e688SJim Ingham     return GetCurrentPlan().get();
33761e8e688SJim Ingham   }
33861e8e688SJim Ingham 
33961e8e688SJim Ingham   // Otherwise look for it in the regular plans.
34061e8e688SJim Ingham   stack_size = m_plans.size();
34161e8e688SJim Ingham   for (int i = stack_size - 1; i > 0; i--) {
34261e8e688SJim Ingham     if (current_plan == m_plans[i].get())
34361e8e688SJim Ingham       return m_plans[i - 1].get();
34461e8e688SJim Ingham   }
34561e8e688SJim Ingham   return nullptr;
34661e8e688SJim Ingham }
34761e8e688SJim Ingham 
34861e8e688SJim Ingham ThreadPlan *ThreadPlanStack::GetInnermostExpression() const {
34961e8e688SJim Ingham   int stack_size = m_plans.size();
35061e8e688SJim Ingham 
35161e8e688SJim Ingham   for (int i = stack_size - 1; i > 0; i--) {
35261e8e688SJim Ingham     if (m_plans[i]->GetKind() == ThreadPlan::eKindCallFunction)
35361e8e688SJim Ingham       return m_plans[i].get();
35461e8e688SJim Ingham   }
35561e8e688SJim Ingham   return nullptr;
35661e8e688SJim Ingham }
35761e8e688SJim Ingham 
3584bb62448SWalter Erquinigo void ThreadPlanStack::ClearThreadCache() {
3594bb62448SWalter Erquinigo   for (lldb::ThreadPlanSP thread_plan_sp : m_plans)
3604bb62448SWalter Erquinigo     thread_plan_sp->ClearThreadCache();
3614bb62448SWalter Erquinigo }
3624bb62448SWalter Erquinigo 
36361e8e688SJim Ingham void ThreadPlanStack::WillResume() {
36461e8e688SJim Ingham   m_completed_plans.clear();
36561e8e688SJim Ingham   m_discarded_plans.clear();
36661e8e688SJim Ingham }
36761e8e688SJim Ingham 
3681893065dSJim Ingham void ThreadPlanStackMap::Update(ThreadList &current_threads,
3691893065dSJim Ingham                                 bool delete_missing,
3701893065dSJim Ingham                                 bool check_for_new) {
3711893065dSJim Ingham 
3721893065dSJim Ingham   // Now find all the new threads and add them to the map:
3731893065dSJim Ingham   if (check_for_new) {
3741893065dSJim Ingham     for (auto thread : current_threads.Threads()) {
3751893065dSJim Ingham       lldb::tid_t cur_tid = thread->GetID();
3761893065dSJim Ingham       if (!Find(cur_tid)) {
3771893065dSJim Ingham         AddThread(*thread.get());
378*e7361c8eSDave Lee         thread->QueueBasePlan(true);
3791893065dSJim Ingham       }
3801893065dSJim Ingham     }
3811893065dSJim Ingham   }
3821893065dSJim Ingham 
3831893065dSJim Ingham   // If we aren't reaping missing threads at this point,
3841893065dSJim Ingham   // we are done.
3851893065dSJim Ingham   if (!delete_missing)
3861893065dSJim Ingham     return;
3871893065dSJim Ingham   // Otherwise scan for absent TID's.
3881893065dSJim Ingham   std::vector<lldb::tid_t> missing_threads;
3891893065dSJim Ingham   // If we are going to delete plans from the plan stack,
3901893065dSJim Ingham   // then scan for absent TID's:
3911893065dSJim Ingham   for (auto thread_plans : m_plans_list) {
3921893065dSJim Ingham     lldb::tid_t cur_tid = thread_plans.first;
3931893065dSJim Ingham     ThreadSP thread_sp = current_threads.FindThreadByID(cur_tid);
3941893065dSJim Ingham     if (!thread_sp)
3951893065dSJim Ingham       missing_threads.push_back(cur_tid);
3961893065dSJim Ingham   }
3971893065dSJim Ingham   for (lldb::tid_t tid : missing_threads) {
3981893065dSJim Ingham     RemoveTID(tid);
3991893065dSJim Ingham   }
4001893065dSJim Ingham }
4011893065dSJim Ingham 
4021893065dSJim Ingham void ThreadPlanStackMap::DumpPlans(Stream &strm,
4031893065dSJim Ingham                                    lldb::DescriptionLevel desc_level,
4041893065dSJim Ingham                                    bool internal, bool condense_if_trivial,
4051893065dSJim Ingham                                    bool skip_unreported) {
4061893065dSJim Ingham   for (auto elem : m_plans_list) {
4071893065dSJim Ingham     lldb::tid_t tid = elem.first;
4081893065dSJim Ingham     uint32_t index_id = 0;
4091893065dSJim Ingham     ThreadSP thread_sp = m_process.GetThreadList().FindThreadByID(tid);
4101893065dSJim Ingham 
4111893065dSJim Ingham     if (skip_unreported) {
4121893065dSJim Ingham       if (!thread_sp)
4131893065dSJim Ingham         continue;
4141893065dSJim Ingham     }
4151893065dSJim Ingham     if (thread_sp)
4161893065dSJim Ingham       index_id = thread_sp->GetIndexID();
4171893065dSJim Ingham 
4181893065dSJim Ingham     if (condense_if_trivial) {
4191893065dSJim Ingham       if (!elem.second.AnyPlans() && !elem.second.AnyCompletedPlans() &&
4201893065dSJim Ingham           !elem.second.AnyDiscardedPlans()) {
4211893065dSJim Ingham         strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 "\n", index_id, tid);
4221893065dSJim Ingham         strm.IndentMore();
4231893065dSJim Ingham         strm.Indent();
4241893065dSJim Ingham         strm.Printf("No active thread plans\n");
4251893065dSJim Ingham         strm.IndentLess();
4261893065dSJim Ingham         return;
4271893065dSJim Ingham       }
4281893065dSJim Ingham     }
4291893065dSJim Ingham 
4301893065dSJim Ingham     strm.Indent();
4311893065dSJim Ingham     strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 ":\n", index_id, tid);
4321893065dSJim Ingham 
4331893065dSJim Ingham     elem.second.DumpThreadPlans(strm, desc_level, internal);
4341893065dSJim Ingham   }
4351893065dSJim Ingham }
4361893065dSJim Ingham 
4371893065dSJim Ingham bool ThreadPlanStackMap::DumpPlansForTID(Stream &strm, lldb::tid_t tid,
4381893065dSJim Ingham                                          lldb::DescriptionLevel desc_level,
4391893065dSJim Ingham                                          bool internal,
4401893065dSJim Ingham                                          bool condense_if_trivial,
4411893065dSJim Ingham                                          bool skip_unreported) {
4421893065dSJim Ingham   uint32_t index_id = 0;
4431893065dSJim Ingham   ThreadSP thread_sp = m_process.GetThreadList().FindThreadByID(tid);
4441893065dSJim Ingham 
4451893065dSJim Ingham   if (skip_unreported) {
4461893065dSJim Ingham     if (!thread_sp) {
4471893065dSJim Ingham       strm.Format("Unknown TID: {0}", tid);
4481893065dSJim Ingham       return false;
4491893065dSJim Ingham     }
4501893065dSJim Ingham   }
4511893065dSJim Ingham 
4521893065dSJim Ingham   if (thread_sp)
4531893065dSJim Ingham     index_id = thread_sp->GetIndexID();
4541893065dSJim Ingham   ThreadPlanStack *stack = Find(tid);
4551893065dSJim Ingham   if (!stack) {
4561893065dSJim Ingham     strm.Format("Unknown TID: {0}\n", tid);
4571893065dSJim Ingham     return false;
4581893065dSJim Ingham   }
4591893065dSJim Ingham 
4601893065dSJim Ingham   if (condense_if_trivial) {
4611893065dSJim Ingham     if (!stack->AnyPlans() && !stack->AnyCompletedPlans() &&
4621893065dSJim Ingham         !stack->AnyDiscardedPlans()) {
4631893065dSJim Ingham       strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 "\n", index_id, tid);
4641893065dSJim Ingham       strm.IndentMore();
4651893065dSJim Ingham       strm.Indent();
4661893065dSJim Ingham       strm.Printf("No active thread plans\n");
4671893065dSJim Ingham       strm.IndentLess();
4681893065dSJim Ingham       return true;
4691893065dSJim Ingham     }
4701893065dSJim Ingham   }
4711893065dSJim Ingham 
4721893065dSJim Ingham   strm.Indent();
4731893065dSJim Ingham   strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 ":\n", index_id, tid);
4741893065dSJim Ingham 
4751893065dSJim Ingham   stack->DumpThreadPlans(strm, desc_level, internal);
4761893065dSJim Ingham   return true;
4771893065dSJim Ingham }
4781893065dSJim Ingham 
4791893065dSJim Ingham bool ThreadPlanStackMap::PrunePlansForTID(lldb::tid_t tid) {
4801893065dSJim Ingham   // We only remove the plans for unreported TID's.
4811893065dSJim Ingham   ThreadSP thread_sp = m_process.GetThreadList().FindThreadByID(tid);
4821893065dSJim Ingham   if (thread_sp)
4831893065dSJim Ingham     return false;
4841893065dSJim Ingham 
4851893065dSJim Ingham   return RemoveTID(tid);
4861893065dSJim Ingham }
487