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 {
42*6eb576dcSJim Ingham   std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
431893065dSJim Ingham   s.IndentMore();
441893065dSJim Ingham   PrintOneStack(s, "Active plan stack", m_plans, desc_level, include_internal);
451893065dSJim Ingham   PrintOneStack(s, "Completed plan stack", m_completed_plans, desc_level,
461893065dSJim Ingham                 include_internal);
471893065dSJim Ingham   PrintOneStack(s, "Discarded plan stack", m_discarded_plans, desc_level,
481893065dSJim Ingham                 include_internal);
491893065dSJim Ingham   s.IndentLess();
5061e8e688SJim Ingham }
5161e8e688SJim Ingham 
521893065dSJim Ingham void ThreadPlanStack::PrintOneStack(Stream &s, llvm::StringRef stack_name,
531893065dSJim Ingham                                     const PlanStack &stack,
541893065dSJim Ingham                                     lldb::DescriptionLevel desc_level,
551893065dSJim Ingham                                     bool include_internal) const {
56*6eb576dcSJim Ingham   std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
571893065dSJim Ingham   // If the stack is empty, just exit:
581893065dSJim Ingham   if (stack.empty())
591893065dSJim Ingham     return;
601893065dSJim Ingham 
611893065dSJim Ingham   // Make sure there are public completed plans:
621893065dSJim Ingham   bool any_public = false;
631893065dSJim Ingham   if (!include_internal) {
641893065dSJim Ingham     for (auto plan : stack) {
651893065dSJim Ingham       if (!plan->GetPrivate()) {
661893065dSJim Ingham         any_public = true;
671893065dSJim Ingham         break;
681893065dSJim Ingham       }
691893065dSJim Ingham     }
7061e8e688SJim Ingham   }
7161e8e688SJim Ingham 
721893065dSJim Ingham   if (include_internal || any_public) {
731893065dSJim Ingham     int print_idx = 0;
741893065dSJim Ingham     s.Indent();
753ccd454cSEric Christopher     s << stack_name << ":\n";
761893065dSJim Ingham     for (auto plan : stack) {
771893065dSJim Ingham       if (!include_internal && plan->GetPrivate())
781893065dSJim Ingham         continue;
7961e8e688SJim Ingham       PrintPlanElement(s, plan, desc_level, print_idx++);
8061e8e688SJim Ingham     }
811893065dSJim Ingham   }
8261e8e688SJim Ingham }
8361e8e688SJim Ingham 
8461e8e688SJim Ingham size_t ThreadPlanStack::CheckpointCompletedPlans() {
85*6eb576dcSJim Ingham   std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
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) {
93*6eb576dcSJim Ingham   std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
9461e8e688SJim Ingham   auto result = m_completed_plan_store.find(checkpoint);
9561e8e688SJim Ingham   assert(result != m_completed_plan_store.end() &&
9661e8e688SJim Ingham          "Asked for a checkpoint that didn't exist");
9761e8e688SJim Ingham   m_completed_plans.swap((*result).second);
9861e8e688SJim Ingham   m_completed_plan_store.erase(result);
9961e8e688SJim Ingham }
10061e8e688SJim Ingham 
10161e8e688SJim Ingham void ThreadPlanStack::DiscardCompletedPlanCheckpoint(size_t checkpoint) {
102*6eb576dcSJim Ingham   std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
10361e8e688SJim Ingham   m_completed_plan_store.erase(checkpoint);
10461e8e688SJim Ingham }
10561e8e688SJim Ingham 
10661e8e688SJim Ingham void ThreadPlanStack::ThreadDestroyed(Thread *thread) {
10761e8e688SJim Ingham   // Tell the plan stacks that this thread is going away:
108*6eb576dcSJim Ingham   std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
10961e8e688SJim Ingham   for (ThreadPlanSP plan : m_plans)
11061e8e688SJim Ingham     plan->ThreadDestroyed();
11161e8e688SJim Ingham 
11261e8e688SJim Ingham   for (ThreadPlanSP plan : m_discarded_plans)
11361e8e688SJim Ingham     plan->ThreadDestroyed();
11461e8e688SJim Ingham 
11561e8e688SJim Ingham   for (ThreadPlanSP plan : m_completed_plans)
11661e8e688SJim Ingham     plan->ThreadDestroyed();
11761e8e688SJim Ingham 
11861e8e688SJim Ingham   // Now clear the current plan stacks:
11961e8e688SJim Ingham   m_plans.clear();
12061e8e688SJim Ingham   m_discarded_plans.clear();
12161e8e688SJim Ingham   m_completed_plans.clear();
12261e8e688SJim Ingham 
12361e8e688SJim Ingham   // Push a ThreadPlanNull on the plan stack.  That way we can continue
12461e8e688SJim Ingham   // assuming that the plan stack is never empty, but if somebody errantly asks
12561e8e688SJim Ingham   // questions of a destroyed thread without checking first whether it is
12661e8e688SJim Ingham   // destroyed, they won't crash.
12761e8e688SJim Ingham   if (thread != nullptr) {
12861e8e688SJim Ingham     lldb::ThreadPlanSP null_plan_sp(new ThreadPlanNull(*thread));
12961e8e688SJim Ingham     m_plans.push_back(null_plan_sp);
13061e8e688SJim Ingham   }
13161e8e688SJim Ingham }
13261e8e688SJim Ingham 
13361e8e688SJim Ingham void ThreadPlanStack::PushPlan(lldb::ThreadPlanSP new_plan_sp) {
13461e8e688SJim Ingham   // If the thread plan doesn't already have a tracer, give it its parent's
13561e8e688SJim Ingham   // tracer:
13661e8e688SJim Ingham   // The first plan has to be a base plan:
137*6eb576dcSJim Ingham   std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
138ff18a6acSPavel Labath   assert((m_plans.size() > 0 || new_plan_sp->IsBasePlan()) &&
139ff18a6acSPavel Labath          "Zeroth plan must be a base plan");
14061e8e688SJim Ingham 
14161e8e688SJim Ingham   if (!new_plan_sp->GetThreadPlanTracer()) {
14261e8e688SJim Ingham     assert(!m_plans.empty());
14361e8e688SJim Ingham     new_plan_sp->SetThreadPlanTracer(m_plans.back()->GetThreadPlanTracer());
14461e8e688SJim Ingham   }
14561e8e688SJim Ingham   m_plans.push_back(new_plan_sp);
14661e8e688SJim Ingham   new_plan_sp->DidPush();
14761e8e688SJim Ingham }
14861e8e688SJim Ingham 
14961e8e688SJim Ingham lldb::ThreadPlanSP ThreadPlanStack::PopPlan() {
150*6eb576dcSJim Ingham   std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
15161e8e688SJim Ingham   assert(m_plans.size() > 1 && "Can't pop the base thread plan");
15261e8e688SJim Ingham 
153873b79b8SBenjamin Kramer   lldb::ThreadPlanSP plan_sp = std::move(m_plans.back());
15461e8e688SJim Ingham   m_completed_plans.push_back(plan_sp);
15561e8e688SJim Ingham   plan_sp->WillPop();
15661e8e688SJim Ingham   m_plans.pop_back();
15761e8e688SJim Ingham   return plan_sp;
15861e8e688SJim Ingham }
15961e8e688SJim Ingham 
16061e8e688SJim Ingham lldb::ThreadPlanSP ThreadPlanStack::DiscardPlan() {
161*6eb576dcSJim Ingham   std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
16261e8e688SJim Ingham   assert(m_plans.size() > 1 && "Can't discard the base thread plan");
16361e8e688SJim Ingham 
164873b79b8SBenjamin Kramer   lldb::ThreadPlanSP plan_sp = std::move(m_plans.back());
16561e8e688SJim Ingham   m_discarded_plans.push_back(plan_sp);
16661e8e688SJim Ingham   plan_sp->WillPop();
16761e8e688SJim Ingham   m_plans.pop_back();
16861e8e688SJim Ingham   return plan_sp;
16961e8e688SJim Ingham }
17061e8e688SJim Ingham 
17161e8e688SJim Ingham // If the input plan is nullptr, discard all plans.  Otherwise make sure this
17261e8e688SJim Ingham // plan is in the stack, and if so discard up to and including it.
17361e8e688SJim Ingham void ThreadPlanStack::DiscardPlansUpToPlan(ThreadPlan *up_to_plan_ptr) {
174*6eb576dcSJim Ingham   std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
17561e8e688SJim Ingham   int stack_size = m_plans.size();
17661e8e688SJim Ingham 
17761e8e688SJim Ingham   if (up_to_plan_ptr == nullptr) {
17861e8e688SJim Ingham     for (int i = stack_size - 1; i > 0; i--)
17961e8e688SJim Ingham       DiscardPlan();
18061e8e688SJim Ingham     return;
18161e8e688SJim Ingham   }
18261e8e688SJim Ingham 
18361e8e688SJim Ingham   bool found_it = false;
18461e8e688SJim Ingham   for (int i = stack_size - 1; i > 0; i--) {
18561e8e688SJim Ingham     if (m_plans[i].get() == up_to_plan_ptr) {
18661e8e688SJim Ingham       found_it = true;
18761e8e688SJim Ingham       break;
18861e8e688SJim Ingham     }
18961e8e688SJim Ingham   }
19061e8e688SJim Ingham 
19161e8e688SJim Ingham   if (found_it) {
19261e8e688SJim Ingham     bool last_one = false;
19361e8e688SJim Ingham     for (int i = stack_size - 1; i > 0 && !last_one; i--) {
19461e8e688SJim Ingham       if (GetCurrentPlan().get() == up_to_plan_ptr)
19561e8e688SJim Ingham         last_one = true;
19661e8e688SJim Ingham       DiscardPlan();
19761e8e688SJim Ingham     }
19861e8e688SJim Ingham   }
19961e8e688SJim Ingham }
20061e8e688SJim Ingham 
20161e8e688SJim Ingham void ThreadPlanStack::DiscardAllPlans() {
202*6eb576dcSJim Ingham   std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
20361e8e688SJim Ingham   int stack_size = m_plans.size();
20461e8e688SJim Ingham   for (int i = stack_size - 1; i > 0; i--) {
20561e8e688SJim Ingham     DiscardPlan();
20661e8e688SJim Ingham   }
20761e8e688SJim Ingham   return;
20861e8e688SJim Ingham }
20961e8e688SJim Ingham 
21061e8e688SJim Ingham void ThreadPlanStack::DiscardConsultingMasterPlans() {
211*6eb576dcSJim Ingham   std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
21261e8e688SJim Ingham   while (true) {
21361e8e688SJim Ingham     int master_plan_idx;
21461e8e688SJim Ingham     bool discard = true;
21561e8e688SJim Ingham 
21661e8e688SJim Ingham     // Find the first master plan, see if it wants discarding, and if yes
21761e8e688SJim Ingham     // discard up to it.
21861e8e688SJim Ingham     for (master_plan_idx = m_plans.size() - 1; master_plan_idx >= 0;
21961e8e688SJim Ingham          master_plan_idx--) {
22061e8e688SJim Ingham       if (m_plans[master_plan_idx]->IsMasterPlan()) {
22161e8e688SJim Ingham         discard = m_plans[master_plan_idx]->OkayToDiscard();
22261e8e688SJim Ingham         break;
22361e8e688SJim Ingham       }
22461e8e688SJim Ingham     }
22561e8e688SJim Ingham 
22661e8e688SJim Ingham     // If the master plan doesn't want to get discarded, then we're done.
22761e8e688SJim Ingham     if (!discard)
22861e8e688SJim Ingham       return;
22961e8e688SJim Ingham 
23061e8e688SJim Ingham     // First pop all the dependent plans:
23161e8e688SJim Ingham     for (int i = m_plans.size() - 1; i > master_plan_idx; i--) {
23261e8e688SJim Ingham       DiscardPlan();
23361e8e688SJim Ingham     }
23461e8e688SJim Ingham 
23561e8e688SJim Ingham     // Now discard the master plan itself.
23661e8e688SJim Ingham     // The bottom-most plan never gets discarded.  "OkayToDiscard" for it
23761e8e688SJim Ingham     // means discard it's dependent plans, but not it...
23861e8e688SJim Ingham     if (master_plan_idx > 0) {
23961e8e688SJim Ingham       DiscardPlan();
24061e8e688SJim Ingham     }
24161e8e688SJim Ingham   }
24261e8e688SJim Ingham }
24361e8e688SJim Ingham 
24461e8e688SJim Ingham lldb::ThreadPlanSP ThreadPlanStack::GetCurrentPlan() const {
245*6eb576dcSJim Ingham   std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
24661e8e688SJim Ingham   assert(m_plans.size() != 0 && "There will always be a base plan.");
24761e8e688SJim Ingham   return m_plans.back();
24861e8e688SJim Ingham }
24961e8e688SJim Ingham 
25061e8e688SJim Ingham lldb::ThreadPlanSP ThreadPlanStack::GetCompletedPlan(bool skip_private) const {
251*6eb576dcSJim Ingham   std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
25261e8e688SJim Ingham   if (m_completed_plans.empty())
25361e8e688SJim Ingham     return {};
25461e8e688SJim Ingham 
25561e8e688SJim Ingham   if (!skip_private)
25661e8e688SJim Ingham     return m_completed_plans.back();
25761e8e688SJim Ingham 
25861e8e688SJim Ingham   for (int i = m_completed_plans.size() - 1; i >= 0; i--) {
25961e8e688SJim Ingham     lldb::ThreadPlanSP completed_plan_sp;
26061e8e688SJim Ingham     completed_plan_sp = m_completed_plans[i];
26161e8e688SJim Ingham     if (!completed_plan_sp->GetPrivate())
26261e8e688SJim Ingham       return completed_plan_sp;
26361e8e688SJim Ingham   }
26461e8e688SJim Ingham   return {};
26561e8e688SJim Ingham }
26661e8e688SJim Ingham 
26761e8e688SJim Ingham lldb::ThreadPlanSP ThreadPlanStack::GetPlanByIndex(uint32_t plan_idx,
26861e8e688SJim Ingham                                                    bool skip_private) const {
269*6eb576dcSJim Ingham   std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
27061e8e688SJim Ingham   uint32_t idx = 0;
27161e8e688SJim Ingham 
27261e8e688SJim Ingham   for (lldb::ThreadPlanSP plan_sp : m_plans) {
27361e8e688SJim Ingham     if (skip_private && plan_sp->GetPrivate())
27461e8e688SJim Ingham       continue;
27561e8e688SJim Ingham     if (idx == plan_idx)
27661e8e688SJim Ingham       return plan_sp;
27761e8e688SJim Ingham     idx++;
27861e8e688SJim Ingham   }
27961e8e688SJim Ingham   return {};
28061e8e688SJim Ingham }
28161e8e688SJim Ingham 
28261e8e688SJim Ingham lldb::ValueObjectSP ThreadPlanStack::GetReturnValueObject() const {
283*6eb576dcSJim Ingham   std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
28461e8e688SJim Ingham   if (m_completed_plans.empty())
28561e8e688SJim Ingham     return {};
28661e8e688SJim Ingham 
28761e8e688SJim Ingham   for (int i = m_completed_plans.size() - 1; i >= 0; i--) {
28861e8e688SJim Ingham     lldb::ValueObjectSP return_valobj_sp;
28961e8e688SJim Ingham     return_valobj_sp = m_completed_plans[i]->GetReturnValueObject();
29061e8e688SJim Ingham     if (return_valobj_sp)
29161e8e688SJim Ingham       return return_valobj_sp;
29261e8e688SJim Ingham   }
29361e8e688SJim Ingham   return {};
29461e8e688SJim Ingham }
29561e8e688SJim Ingham 
29661e8e688SJim Ingham lldb::ExpressionVariableSP ThreadPlanStack::GetExpressionVariable() const {
297*6eb576dcSJim Ingham   std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
29861e8e688SJim Ingham   if (m_completed_plans.empty())
29961e8e688SJim Ingham     return {};
30061e8e688SJim Ingham 
30161e8e688SJim Ingham   for (int i = m_completed_plans.size() - 1; i >= 0; i--) {
30261e8e688SJim Ingham     lldb::ExpressionVariableSP expression_variable_sp;
30361e8e688SJim Ingham     expression_variable_sp = m_completed_plans[i]->GetExpressionVariable();
30461e8e688SJim Ingham     if (expression_variable_sp)
30561e8e688SJim Ingham       return expression_variable_sp;
30661e8e688SJim Ingham   }
30761e8e688SJim Ingham   return {};
30861e8e688SJim Ingham }
30961e8e688SJim Ingham bool ThreadPlanStack::AnyPlans() const {
310*6eb576dcSJim Ingham   std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
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 {
316*6eb576dcSJim Ingham   std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
31761e8e688SJim Ingham   return !m_completed_plans.empty();
31861e8e688SJim Ingham }
31961e8e688SJim Ingham 
32061e8e688SJim Ingham bool ThreadPlanStack::AnyDiscardedPlans() const {
321*6eb576dcSJim Ingham   std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
32261e8e688SJim Ingham   return !m_discarded_plans.empty();
32361e8e688SJim Ingham }
32461e8e688SJim Ingham 
32561e8e688SJim Ingham bool ThreadPlanStack::IsPlanDone(ThreadPlan *in_plan) const {
326*6eb576dcSJim Ingham   std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
32761e8e688SJim Ingham   for (auto plan : m_completed_plans) {
32861e8e688SJim Ingham     if (plan.get() == in_plan)
32961e8e688SJim Ingham       return true;
33061e8e688SJim Ingham   }
33161e8e688SJim Ingham   return false;
33261e8e688SJim Ingham }
33361e8e688SJim Ingham 
33461e8e688SJim Ingham bool ThreadPlanStack::WasPlanDiscarded(ThreadPlan *in_plan) const {
335*6eb576dcSJim Ingham   std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
33661e8e688SJim Ingham   for (auto plan : m_discarded_plans) {
33761e8e688SJim Ingham     if (plan.get() == in_plan)
33861e8e688SJim Ingham       return true;
33961e8e688SJim Ingham   }
34061e8e688SJim Ingham   return false;
34161e8e688SJim Ingham }
34261e8e688SJim Ingham 
34361e8e688SJim Ingham ThreadPlan *ThreadPlanStack::GetPreviousPlan(ThreadPlan *current_plan) const {
344*6eb576dcSJim Ingham   std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
34561e8e688SJim Ingham   if (current_plan == nullptr)
34661e8e688SJim Ingham     return nullptr;
34761e8e688SJim Ingham 
34861e8e688SJim Ingham   // Look first in the completed plans, if the plan is here and there is
34961e8e688SJim Ingham   // a completed plan above it, return that.
35061e8e688SJim Ingham   int stack_size = m_completed_plans.size();
35161e8e688SJim Ingham   for (int i = stack_size - 1; i > 0; i--) {
35261e8e688SJim Ingham     if (current_plan == m_completed_plans[i].get())
35361e8e688SJim Ingham       return m_completed_plans[i - 1].get();
35461e8e688SJim Ingham   }
35561e8e688SJim Ingham 
35661e8e688SJim Ingham   // If this is the first completed plan, the previous one is the
35761e8e688SJim Ingham   // bottom of the regular plan stack.
35861e8e688SJim Ingham   if (stack_size > 0 && m_completed_plans[0].get() == current_plan) {
35961e8e688SJim Ingham     return GetCurrentPlan().get();
36061e8e688SJim Ingham   }
36161e8e688SJim Ingham 
36261e8e688SJim Ingham   // Otherwise look for it in the regular plans.
36361e8e688SJim Ingham   stack_size = m_plans.size();
36461e8e688SJim Ingham   for (int i = stack_size - 1; i > 0; i--) {
36561e8e688SJim Ingham     if (current_plan == m_plans[i].get())
36661e8e688SJim Ingham       return m_plans[i - 1].get();
36761e8e688SJim Ingham   }
36861e8e688SJim Ingham   return nullptr;
36961e8e688SJim Ingham }
37061e8e688SJim Ingham 
37161e8e688SJim Ingham ThreadPlan *ThreadPlanStack::GetInnermostExpression() const {
372*6eb576dcSJim Ingham   std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
37361e8e688SJim Ingham   int stack_size = m_plans.size();
37461e8e688SJim Ingham 
37561e8e688SJim Ingham   for (int i = stack_size - 1; i > 0; i--) {
37661e8e688SJim Ingham     if (m_plans[i]->GetKind() == ThreadPlan::eKindCallFunction)
37761e8e688SJim Ingham       return m_plans[i].get();
37861e8e688SJim Ingham   }
37961e8e688SJim Ingham   return nullptr;
38061e8e688SJim Ingham }
38161e8e688SJim Ingham 
3824bb62448SWalter Erquinigo void ThreadPlanStack::ClearThreadCache() {
383*6eb576dcSJim Ingham   std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
3844bb62448SWalter Erquinigo   for (lldb::ThreadPlanSP thread_plan_sp : m_plans)
3854bb62448SWalter Erquinigo     thread_plan_sp->ClearThreadCache();
3864bb62448SWalter Erquinigo }
3874bb62448SWalter Erquinigo 
38861e8e688SJim Ingham void ThreadPlanStack::WillResume() {
389*6eb576dcSJim Ingham   std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
39061e8e688SJim Ingham   m_completed_plans.clear();
39161e8e688SJim Ingham   m_discarded_plans.clear();
39261e8e688SJim Ingham }
39361e8e688SJim Ingham 
3941893065dSJim Ingham void ThreadPlanStackMap::Update(ThreadList &current_threads,
3951893065dSJim Ingham                                 bool delete_missing,
3961893065dSJim Ingham                                 bool check_for_new) {
3971893065dSJim Ingham 
3981893065dSJim Ingham   // Now find all the new threads and add them to the map:
3991893065dSJim Ingham   if (check_for_new) {
4001893065dSJim Ingham     for (auto thread : current_threads.Threads()) {
4011893065dSJim Ingham       lldb::tid_t cur_tid = thread->GetID();
4021893065dSJim Ingham       if (!Find(cur_tid)) {
4031893065dSJim Ingham         AddThread(*thread.get());
404e7361c8eSDave Lee         thread->QueueBasePlan(true);
4051893065dSJim Ingham       }
4061893065dSJim Ingham     }
4071893065dSJim Ingham   }
4081893065dSJim Ingham 
4091893065dSJim Ingham   // If we aren't reaping missing threads at this point,
4101893065dSJim Ingham   // we are done.
4111893065dSJim Ingham   if (!delete_missing)
4121893065dSJim Ingham     return;
4131893065dSJim Ingham   // Otherwise scan for absent TID's.
4141893065dSJim Ingham   std::vector<lldb::tid_t> missing_threads;
4151893065dSJim Ingham   // If we are going to delete plans from the plan stack,
4161893065dSJim Ingham   // then scan for absent TID's:
417*6eb576dcSJim Ingham   for (auto &thread_plans : m_plans_list) {
4181893065dSJim Ingham     lldb::tid_t cur_tid = thread_plans.first;
4191893065dSJim Ingham     ThreadSP thread_sp = current_threads.FindThreadByID(cur_tid);
4201893065dSJim Ingham     if (!thread_sp)
4211893065dSJim Ingham       missing_threads.push_back(cur_tid);
4221893065dSJim Ingham   }
4231893065dSJim Ingham   for (lldb::tid_t tid : missing_threads) {
4241893065dSJim Ingham     RemoveTID(tid);
4251893065dSJim Ingham   }
4261893065dSJim Ingham }
4271893065dSJim Ingham 
4281893065dSJim Ingham void ThreadPlanStackMap::DumpPlans(Stream &strm,
4291893065dSJim Ingham                                    lldb::DescriptionLevel desc_level,
4301893065dSJim Ingham                                    bool internal, bool condense_if_trivial,
4311893065dSJim Ingham                                    bool skip_unreported) {
432*6eb576dcSJim Ingham   for (auto &elem : m_plans_list) {
4331893065dSJim Ingham     lldb::tid_t tid = elem.first;
4341893065dSJim Ingham     uint32_t index_id = 0;
4351893065dSJim Ingham     ThreadSP thread_sp = m_process.GetThreadList().FindThreadByID(tid);
4361893065dSJim Ingham 
4371893065dSJim Ingham     if (skip_unreported) {
4381893065dSJim Ingham       if (!thread_sp)
4391893065dSJim Ingham         continue;
4401893065dSJim Ingham     }
4411893065dSJim Ingham     if (thread_sp)
4421893065dSJim Ingham       index_id = thread_sp->GetIndexID();
4431893065dSJim Ingham 
4441893065dSJim Ingham     if (condense_if_trivial) {
4451893065dSJim Ingham       if (!elem.second.AnyPlans() && !elem.second.AnyCompletedPlans() &&
4461893065dSJim Ingham           !elem.second.AnyDiscardedPlans()) {
4471893065dSJim Ingham         strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 "\n", index_id, tid);
4481893065dSJim Ingham         strm.IndentMore();
4491893065dSJim Ingham         strm.Indent();
4501893065dSJim Ingham         strm.Printf("No active thread plans\n");
4511893065dSJim Ingham         strm.IndentLess();
4521893065dSJim Ingham         return;
4531893065dSJim Ingham       }
4541893065dSJim Ingham     }
4551893065dSJim Ingham 
4561893065dSJim Ingham     strm.Indent();
4571893065dSJim Ingham     strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 ":\n", index_id, tid);
4581893065dSJim Ingham 
4591893065dSJim Ingham     elem.second.DumpThreadPlans(strm, desc_level, internal);
4601893065dSJim Ingham   }
4611893065dSJim Ingham }
4621893065dSJim Ingham 
4631893065dSJim Ingham bool ThreadPlanStackMap::DumpPlansForTID(Stream &strm, lldb::tid_t tid,
4641893065dSJim Ingham                                          lldb::DescriptionLevel desc_level,
4651893065dSJim Ingham                                          bool internal,
4661893065dSJim Ingham                                          bool condense_if_trivial,
4671893065dSJim Ingham                                          bool skip_unreported) {
4681893065dSJim Ingham   uint32_t index_id = 0;
4691893065dSJim Ingham   ThreadSP thread_sp = m_process.GetThreadList().FindThreadByID(tid);
4701893065dSJim Ingham 
4711893065dSJim Ingham   if (skip_unreported) {
4721893065dSJim Ingham     if (!thread_sp) {
4731893065dSJim Ingham       strm.Format("Unknown TID: {0}", tid);
4741893065dSJim Ingham       return false;
4751893065dSJim Ingham     }
4761893065dSJim Ingham   }
4771893065dSJim Ingham 
4781893065dSJim Ingham   if (thread_sp)
4791893065dSJim Ingham     index_id = thread_sp->GetIndexID();
4801893065dSJim Ingham   ThreadPlanStack *stack = Find(tid);
4811893065dSJim Ingham   if (!stack) {
4821893065dSJim Ingham     strm.Format("Unknown TID: {0}\n", tid);
4831893065dSJim Ingham     return false;
4841893065dSJim Ingham   }
4851893065dSJim Ingham 
4861893065dSJim Ingham   if (condense_if_trivial) {
4871893065dSJim Ingham     if (!stack->AnyPlans() && !stack->AnyCompletedPlans() &&
4881893065dSJim Ingham         !stack->AnyDiscardedPlans()) {
4891893065dSJim Ingham       strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 "\n", index_id, tid);
4901893065dSJim Ingham       strm.IndentMore();
4911893065dSJim Ingham       strm.Indent();
4921893065dSJim Ingham       strm.Printf("No active thread plans\n");
4931893065dSJim Ingham       strm.IndentLess();
4941893065dSJim Ingham       return true;
4951893065dSJim Ingham     }
4961893065dSJim Ingham   }
4971893065dSJim Ingham 
4981893065dSJim Ingham   strm.Indent();
4991893065dSJim Ingham   strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 ":\n", index_id, tid);
5001893065dSJim Ingham 
5011893065dSJim Ingham   stack->DumpThreadPlans(strm, desc_level, internal);
5021893065dSJim Ingham   return true;
5031893065dSJim Ingham }
5041893065dSJim Ingham 
5051893065dSJim Ingham bool ThreadPlanStackMap::PrunePlansForTID(lldb::tid_t tid) {
5061893065dSJim Ingham   // We only remove the plans for unreported TID's.
5071893065dSJim Ingham   ThreadSP thread_sp = m_process.GetThreadList().FindThreadByID(tid);
5081893065dSJim Ingham   if (thread_sp)
5091893065dSJim Ingham     return false;
5101893065dSJim Ingham 
5111893065dSJim Ingham   return RemoveTID(tid);
5121893065dSJim Ingham }
513