xref: /freebsd-src/contrib/llvm-project/lldb/source/Target/ThreadPlanStack.cpp (revision bdd1243df58e60e85101c09001d9812a789b6bc4)
15ffd83dbSDimitry Andric //===-- ThreadPlanStack.cpp -------------------------------------*- C++ -*-===//
25ffd83dbSDimitry Andric //
35ffd83dbSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
45ffd83dbSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
55ffd83dbSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
65ffd83dbSDimitry Andric //
75ffd83dbSDimitry Andric //===----------------------------------------------------------------------===//
85ffd83dbSDimitry Andric 
95ffd83dbSDimitry Andric #include "lldb/Target/ThreadPlanStack.h"
105ffd83dbSDimitry Andric #include "lldb/Target/Process.h"
115ffd83dbSDimitry Andric #include "lldb/Target/Target.h"
125ffd83dbSDimitry Andric #include "lldb/Target/Thread.h"
135ffd83dbSDimitry Andric #include "lldb/Target/ThreadPlan.h"
145ffd83dbSDimitry Andric #include "lldb/Utility/Log.h"
155ffd83dbSDimitry Andric 
165ffd83dbSDimitry Andric using namespace lldb;
175ffd83dbSDimitry Andric using namespace lldb_private;
185ffd83dbSDimitry Andric 
PrintPlanElement(Stream & s,const ThreadPlanSP & plan,lldb::DescriptionLevel desc_level,int32_t elem_idx)195ffd83dbSDimitry Andric static void PrintPlanElement(Stream &s, const ThreadPlanSP &plan,
205ffd83dbSDimitry Andric                              lldb::DescriptionLevel desc_level,
215ffd83dbSDimitry Andric                              int32_t elem_idx) {
225ffd83dbSDimitry Andric   s.IndentMore();
235ffd83dbSDimitry Andric   s.Indent();
245ffd83dbSDimitry Andric   s.Printf("Element %d: ", elem_idx);
255ffd83dbSDimitry Andric   plan->GetDescription(&s, desc_level);
265ffd83dbSDimitry Andric   s.EOL();
275ffd83dbSDimitry Andric   s.IndentLess();
285ffd83dbSDimitry Andric }
295ffd83dbSDimitry Andric 
ThreadPlanStack(const Thread & thread,bool make_null)305ffd83dbSDimitry Andric ThreadPlanStack::ThreadPlanStack(const Thread &thread, bool make_null) {
315ffd83dbSDimitry Andric   if (make_null) {
325ffd83dbSDimitry Andric     // The ThreadPlanNull doesn't do anything to the Thread, so this is actually
335ffd83dbSDimitry Andric     // still a const operation.
345ffd83dbSDimitry Andric     m_plans.push_back(
355ffd83dbSDimitry Andric         ThreadPlanSP(new ThreadPlanNull(const_cast<Thread &>(thread))));
365ffd83dbSDimitry Andric   }
375ffd83dbSDimitry Andric }
385ffd83dbSDimitry Andric 
DumpThreadPlans(Stream & s,lldb::DescriptionLevel desc_level,bool include_internal) const395ffd83dbSDimitry Andric void ThreadPlanStack::DumpThreadPlans(Stream &s,
405ffd83dbSDimitry Andric                                       lldb::DescriptionLevel desc_level,
415ffd83dbSDimitry Andric                                       bool include_internal) const {
42fe6060f1SDimitry Andric   std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
435ffd83dbSDimitry Andric   s.IndentMore();
445ffd83dbSDimitry Andric   PrintOneStack(s, "Active plan stack", m_plans, desc_level, include_internal);
455ffd83dbSDimitry Andric   PrintOneStack(s, "Completed plan stack", m_completed_plans, desc_level,
465ffd83dbSDimitry Andric                 include_internal);
475ffd83dbSDimitry Andric   PrintOneStack(s, "Discarded plan stack", m_discarded_plans, desc_level,
485ffd83dbSDimitry Andric                 include_internal);
495ffd83dbSDimitry Andric   s.IndentLess();
505ffd83dbSDimitry Andric }
515ffd83dbSDimitry Andric 
PrintOneStack(Stream & s,llvm::StringRef stack_name,const PlanStack & stack,lldb::DescriptionLevel desc_level,bool include_internal) const525ffd83dbSDimitry Andric void ThreadPlanStack::PrintOneStack(Stream &s, llvm::StringRef stack_name,
535ffd83dbSDimitry Andric                                     const PlanStack &stack,
545ffd83dbSDimitry Andric                                     lldb::DescriptionLevel desc_level,
555ffd83dbSDimitry Andric                                     bool include_internal) const {
56fe6060f1SDimitry Andric   std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
575ffd83dbSDimitry Andric   // If the stack is empty, just exit:
585ffd83dbSDimitry Andric   if (stack.empty())
595ffd83dbSDimitry Andric     return;
605ffd83dbSDimitry Andric 
615ffd83dbSDimitry Andric   // Make sure there are public completed plans:
625ffd83dbSDimitry Andric   bool any_public = false;
635ffd83dbSDimitry Andric   if (!include_internal) {
645ffd83dbSDimitry Andric     for (auto plan : stack) {
655ffd83dbSDimitry Andric       if (!plan->GetPrivate()) {
665ffd83dbSDimitry Andric         any_public = true;
675ffd83dbSDimitry Andric         break;
685ffd83dbSDimitry Andric       }
695ffd83dbSDimitry Andric     }
705ffd83dbSDimitry Andric   }
715ffd83dbSDimitry Andric 
725ffd83dbSDimitry Andric   if (include_internal || any_public) {
735ffd83dbSDimitry Andric     int print_idx = 0;
745ffd83dbSDimitry Andric     s.Indent();
755ffd83dbSDimitry Andric     s << stack_name << ":\n";
765ffd83dbSDimitry Andric     for (auto plan : stack) {
775ffd83dbSDimitry Andric       if (!include_internal && plan->GetPrivate())
785ffd83dbSDimitry Andric         continue;
795ffd83dbSDimitry Andric       PrintPlanElement(s, plan, desc_level, print_idx++);
805ffd83dbSDimitry Andric     }
815ffd83dbSDimitry Andric   }
825ffd83dbSDimitry Andric }
835ffd83dbSDimitry Andric 
CheckpointCompletedPlans()845ffd83dbSDimitry Andric size_t ThreadPlanStack::CheckpointCompletedPlans() {
85fe6060f1SDimitry Andric   std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
865ffd83dbSDimitry Andric   m_completed_plan_checkpoint++;
875ffd83dbSDimitry Andric   m_completed_plan_store.insert(
885ffd83dbSDimitry Andric       std::make_pair(m_completed_plan_checkpoint, m_completed_plans));
895ffd83dbSDimitry Andric   return m_completed_plan_checkpoint;
905ffd83dbSDimitry Andric }
915ffd83dbSDimitry Andric 
RestoreCompletedPlanCheckpoint(size_t checkpoint)925ffd83dbSDimitry Andric void ThreadPlanStack::RestoreCompletedPlanCheckpoint(size_t checkpoint) {
93fe6060f1SDimitry Andric   std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
945ffd83dbSDimitry Andric   auto result = m_completed_plan_store.find(checkpoint);
955ffd83dbSDimitry Andric   assert(result != m_completed_plan_store.end() &&
965ffd83dbSDimitry Andric          "Asked for a checkpoint that didn't exist");
975ffd83dbSDimitry Andric   m_completed_plans.swap((*result).second);
985ffd83dbSDimitry Andric   m_completed_plan_store.erase(result);
995ffd83dbSDimitry Andric }
1005ffd83dbSDimitry Andric 
DiscardCompletedPlanCheckpoint(size_t checkpoint)1015ffd83dbSDimitry Andric void ThreadPlanStack::DiscardCompletedPlanCheckpoint(size_t checkpoint) {
102fe6060f1SDimitry Andric   std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
1035ffd83dbSDimitry Andric   m_completed_plan_store.erase(checkpoint);
1045ffd83dbSDimitry Andric }
1055ffd83dbSDimitry Andric 
ThreadDestroyed(Thread * thread)1065ffd83dbSDimitry Andric void ThreadPlanStack::ThreadDestroyed(Thread *thread) {
1075ffd83dbSDimitry Andric   // Tell the plan stacks that this thread is going away:
108fe6060f1SDimitry Andric   std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
1095ffd83dbSDimitry Andric   for (ThreadPlanSP plan : m_plans)
1105ffd83dbSDimitry Andric     plan->ThreadDestroyed();
1115ffd83dbSDimitry Andric 
1125ffd83dbSDimitry Andric   for (ThreadPlanSP plan : m_discarded_plans)
1135ffd83dbSDimitry Andric     plan->ThreadDestroyed();
1145ffd83dbSDimitry Andric 
1155ffd83dbSDimitry Andric   for (ThreadPlanSP plan : m_completed_plans)
1165ffd83dbSDimitry Andric     plan->ThreadDestroyed();
1175ffd83dbSDimitry Andric 
1185ffd83dbSDimitry Andric   // Now clear the current plan stacks:
1195ffd83dbSDimitry Andric   m_plans.clear();
1205ffd83dbSDimitry Andric   m_discarded_plans.clear();
1215ffd83dbSDimitry Andric   m_completed_plans.clear();
1225ffd83dbSDimitry Andric 
1235ffd83dbSDimitry Andric   // Push a ThreadPlanNull on the plan stack.  That way we can continue
1245ffd83dbSDimitry Andric   // assuming that the plan stack is never empty, but if somebody errantly asks
1255ffd83dbSDimitry Andric   // questions of a destroyed thread without checking first whether it is
1265ffd83dbSDimitry Andric   // destroyed, they won't crash.
1275ffd83dbSDimitry Andric   if (thread != nullptr) {
1285ffd83dbSDimitry Andric     lldb::ThreadPlanSP null_plan_sp(new ThreadPlanNull(*thread));
1295ffd83dbSDimitry Andric     m_plans.push_back(null_plan_sp);
1305ffd83dbSDimitry Andric   }
1315ffd83dbSDimitry Andric }
1325ffd83dbSDimitry Andric 
PushPlan(lldb::ThreadPlanSP new_plan_sp)1335ffd83dbSDimitry Andric void ThreadPlanStack::PushPlan(lldb::ThreadPlanSP new_plan_sp) {
1345ffd83dbSDimitry Andric   // If the thread plan doesn't already have a tracer, give it its parent's
1355ffd83dbSDimitry Andric   // tracer:
1365ffd83dbSDimitry Andric   // The first plan has to be a base plan:
137fe6060f1SDimitry Andric   std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
1385ffd83dbSDimitry Andric   assert((m_plans.size() > 0 || new_plan_sp->IsBasePlan()) &&
1395ffd83dbSDimitry Andric          "Zeroth plan must be a base plan");
1405ffd83dbSDimitry Andric 
1415ffd83dbSDimitry Andric   if (!new_plan_sp->GetThreadPlanTracer()) {
1425ffd83dbSDimitry Andric     assert(!m_plans.empty());
1435ffd83dbSDimitry Andric     new_plan_sp->SetThreadPlanTracer(m_plans.back()->GetThreadPlanTracer());
1445ffd83dbSDimitry Andric   }
1455ffd83dbSDimitry Andric   m_plans.push_back(new_plan_sp);
1465ffd83dbSDimitry Andric   new_plan_sp->DidPush();
1475ffd83dbSDimitry Andric }
1485ffd83dbSDimitry Andric 
PopPlan()1495ffd83dbSDimitry Andric lldb::ThreadPlanSP ThreadPlanStack::PopPlan() {
150fe6060f1SDimitry Andric   std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
1515ffd83dbSDimitry Andric   assert(m_plans.size() > 1 && "Can't pop the base thread plan");
1525ffd83dbSDimitry Andric 
153349cc55cSDimitry Andric   // Note that moving the top element of the vector would leave it in an
154349cc55cSDimitry Andric   // undefined state, and break the guarantee that the stack's thread plans are
155349cc55cSDimitry Andric   // all valid.
156349cc55cSDimitry Andric   lldb::ThreadPlanSP plan_sp = m_plans.back();
1575ffd83dbSDimitry Andric   m_plans.pop_back();
158349cc55cSDimitry Andric   m_completed_plans.push_back(plan_sp);
159349cc55cSDimitry Andric   plan_sp->DidPop();
1605ffd83dbSDimitry Andric   return plan_sp;
1615ffd83dbSDimitry Andric }
1625ffd83dbSDimitry Andric 
DiscardPlan()1635ffd83dbSDimitry Andric lldb::ThreadPlanSP ThreadPlanStack::DiscardPlan() {
164fe6060f1SDimitry Andric   std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
1655ffd83dbSDimitry Andric   assert(m_plans.size() > 1 && "Can't discard the base thread plan");
1665ffd83dbSDimitry Andric 
167349cc55cSDimitry Andric   // Note that moving the top element of the vector would leave it in an
168349cc55cSDimitry Andric   // undefined state, and break the guarantee that the stack's thread plans are
169349cc55cSDimitry Andric   // all valid.
170349cc55cSDimitry Andric   lldb::ThreadPlanSP plan_sp = m_plans.back();
1715ffd83dbSDimitry Andric   m_plans.pop_back();
172349cc55cSDimitry Andric   m_discarded_plans.push_back(plan_sp);
173349cc55cSDimitry Andric   plan_sp->DidPop();
1745ffd83dbSDimitry Andric   return plan_sp;
1755ffd83dbSDimitry Andric }
1765ffd83dbSDimitry Andric 
1775ffd83dbSDimitry Andric // If the input plan is nullptr, discard all plans.  Otherwise make sure this
1785ffd83dbSDimitry Andric // plan is in the stack, and if so discard up to and including it.
DiscardPlansUpToPlan(ThreadPlan * up_to_plan_ptr)1795ffd83dbSDimitry Andric void ThreadPlanStack::DiscardPlansUpToPlan(ThreadPlan *up_to_plan_ptr) {
180fe6060f1SDimitry Andric   std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
1815ffd83dbSDimitry Andric   int stack_size = m_plans.size();
1825ffd83dbSDimitry Andric 
1835ffd83dbSDimitry Andric   if (up_to_plan_ptr == nullptr) {
1845ffd83dbSDimitry Andric     for (int i = stack_size - 1; i > 0; i--)
1855ffd83dbSDimitry Andric       DiscardPlan();
1865ffd83dbSDimitry Andric     return;
1875ffd83dbSDimitry Andric   }
1885ffd83dbSDimitry Andric 
1895ffd83dbSDimitry Andric   bool found_it = false;
1905ffd83dbSDimitry Andric   for (int i = stack_size - 1; i > 0; i--) {
1915ffd83dbSDimitry Andric     if (m_plans[i].get() == up_to_plan_ptr) {
1925ffd83dbSDimitry Andric       found_it = true;
1935ffd83dbSDimitry Andric       break;
1945ffd83dbSDimitry Andric     }
1955ffd83dbSDimitry Andric   }
1965ffd83dbSDimitry Andric 
1975ffd83dbSDimitry Andric   if (found_it) {
1985ffd83dbSDimitry Andric     bool last_one = false;
1995ffd83dbSDimitry Andric     for (int i = stack_size - 1; i > 0 && !last_one; i--) {
2005ffd83dbSDimitry Andric       if (GetCurrentPlan().get() == up_to_plan_ptr)
2015ffd83dbSDimitry Andric         last_one = true;
2025ffd83dbSDimitry Andric       DiscardPlan();
2035ffd83dbSDimitry Andric     }
2045ffd83dbSDimitry Andric   }
2055ffd83dbSDimitry Andric }
2065ffd83dbSDimitry Andric 
DiscardAllPlans()2075ffd83dbSDimitry Andric void ThreadPlanStack::DiscardAllPlans() {
208fe6060f1SDimitry Andric   std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
2095ffd83dbSDimitry Andric   int stack_size = m_plans.size();
2105ffd83dbSDimitry Andric   for (int i = stack_size - 1; i > 0; i--) {
2115ffd83dbSDimitry Andric     DiscardPlan();
2125ffd83dbSDimitry Andric   }
2135ffd83dbSDimitry Andric }
2145ffd83dbSDimitry Andric 
DiscardConsultingControllingPlans()215349cc55cSDimitry Andric void ThreadPlanStack::DiscardConsultingControllingPlans() {
216fe6060f1SDimitry Andric   std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
2175ffd83dbSDimitry Andric   while (true) {
218349cc55cSDimitry Andric     int controlling_plan_idx;
2195ffd83dbSDimitry Andric     bool discard = true;
2205ffd83dbSDimitry Andric 
221349cc55cSDimitry Andric     // Find the first controlling plan, see if it wants discarding, and if yes
2225ffd83dbSDimitry Andric     // discard up to it.
223349cc55cSDimitry Andric     for (controlling_plan_idx = m_plans.size() - 1; controlling_plan_idx >= 0;
224349cc55cSDimitry Andric          controlling_plan_idx--) {
225349cc55cSDimitry Andric       if (m_plans[controlling_plan_idx]->IsControllingPlan()) {
226349cc55cSDimitry Andric         discard = m_plans[controlling_plan_idx]->OkayToDiscard();
2275ffd83dbSDimitry Andric         break;
2285ffd83dbSDimitry Andric       }
2295ffd83dbSDimitry Andric     }
2305ffd83dbSDimitry Andric 
231349cc55cSDimitry Andric     // If the controlling plan doesn't want to get discarded, then we're done.
2325ffd83dbSDimitry Andric     if (!discard)
2335ffd83dbSDimitry Andric       return;
2345ffd83dbSDimitry Andric 
2355ffd83dbSDimitry Andric     // First pop all the dependent plans:
236349cc55cSDimitry Andric     for (int i = m_plans.size() - 1; i > controlling_plan_idx; i--) {
2375ffd83dbSDimitry Andric       DiscardPlan();
2385ffd83dbSDimitry Andric     }
2395ffd83dbSDimitry Andric 
240349cc55cSDimitry Andric     // Now discard the controlling plan itself.
2415ffd83dbSDimitry Andric     // The bottom-most plan never gets discarded.  "OkayToDiscard" for it
2425ffd83dbSDimitry Andric     // means discard it's dependent plans, but not it...
243349cc55cSDimitry Andric     if (controlling_plan_idx > 0) {
2445ffd83dbSDimitry Andric       DiscardPlan();
2455ffd83dbSDimitry Andric     }
2465ffd83dbSDimitry Andric   }
2475ffd83dbSDimitry Andric }
2485ffd83dbSDimitry Andric 
GetCurrentPlan() const2495ffd83dbSDimitry Andric lldb::ThreadPlanSP ThreadPlanStack::GetCurrentPlan() const {
250fe6060f1SDimitry Andric   std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
2515ffd83dbSDimitry Andric   assert(m_plans.size() != 0 && "There will always be a base plan.");
2525ffd83dbSDimitry Andric   return m_plans.back();
2535ffd83dbSDimitry Andric }
2545ffd83dbSDimitry Andric 
GetCompletedPlan(bool skip_private) const2555ffd83dbSDimitry Andric lldb::ThreadPlanSP ThreadPlanStack::GetCompletedPlan(bool skip_private) const {
256fe6060f1SDimitry Andric   std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
2575ffd83dbSDimitry Andric   if (m_completed_plans.empty())
2585ffd83dbSDimitry Andric     return {};
2595ffd83dbSDimitry Andric 
2605ffd83dbSDimitry Andric   if (!skip_private)
2615ffd83dbSDimitry Andric     return m_completed_plans.back();
2625ffd83dbSDimitry Andric 
2635ffd83dbSDimitry Andric   for (int i = m_completed_plans.size() - 1; i >= 0; i--) {
2645ffd83dbSDimitry Andric     lldb::ThreadPlanSP completed_plan_sp;
2655ffd83dbSDimitry Andric     completed_plan_sp = m_completed_plans[i];
2665ffd83dbSDimitry Andric     if (!completed_plan_sp->GetPrivate())
2675ffd83dbSDimitry Andric       return completed_plan_sp;
2685ffd83dbSDimitry Andric   }
2695ffd83dbSDimitry Andric   return {};
2705ffd83dbSDimitry Andric }
2715ffd83dbSDimitry Andric 
GetPlanByIndex(uint32_t plan_idx,bool skip_private) const2725ffd83dbSDimitry Andric lldb::ThreadPlanSP ThreadPlanStack::GetPlanByIndex(uint32_t plan_idx,
2735ffd83dbSDimitry Andric                                                    bool skip_private) const {
274fe6060f1SDimitry Andric   std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
2755ffd83dbSDimitry Andric   uint32_t idx = 0;
2765ffd83dbSDimitry Andric 
2775ffd83dbSDimitry Andric   for (lldb::ThreadPlanSP plan_sp : m_plans) {
2785ffd83dbSDimitry Andric     if (skip_private && plan_sp->GetPrivate())
2795ffd83dbSDimitry Andric       continue;
2805ffd83dbSDimitry Andric     if (idx == plan_idx)
2815ffd83dbSDimitry Andric       return plan_sp;
2825ffd83dbSDimitry Andric     idx++;
2835ffd83dbSDimitry Andric   }
2845ffd83dbSDimitry Andric   return {};
2855ffd83dbSDimitry Andric }
2865ffd83dbSDimitry Andric 
GetReturnValueObject() const2875ffd83dbSDimitry Andric lldb::ValueObjectSP ThreadPlanStack::GetReturnValueObject() const {
288fe6060f1SDimitry Andric   std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
2895ffd83dbSDimitry Andric   if (m_completed_plans.empty())
2905ffd83dbSDimitry Andric     return {};
2915ffd83dbSDimitry Andric 
2925ffd83dbSDimitry Andric   for (int i = m_completed_plans.size() - 1; i >= 0; i--) {
2935ffd83dbSDimitry Andric     lldb::ValueObjectSP return_valobj_sp;
2945ffd83dbSDimitry Andric     return_valobj_sp = m_completed_plans[i]->GetReturnValueObject();
2955ffd83dbSDimitry Andric     if (return_valobj_sp)
2965ffd83dbSDimitry Andric       return return_valobj_sp;
2975ffd83dbSDimitry Andric   }
2985ffd83dbSDimitry Andric   return {};
2995ffd83dbSDimitry Andric }
3005ffd83dbSDimitry Andric 
GetExpressionVariable() const3015ffd83dbSDimitry Andric lldb::ExpressionVariableSP ThreadPlanStack::GetExpressionVariable() const {
302fe6060f1SDimitry Andric   std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
3035ffd83dbSDimitry Andric   if (m_completed_plans.empty())
3045ffd83dbSDimitry Andric     return {};
3055ffd83dbSDimitry Andric 
3065ffd83dbSDimitry Andric   for (int i = m_completed_plans.size() - 1; i >= 0; i--) {
3075ffd83dbSDimitry Andric     lldb::ExpressionVariableSP expression_variable_sp;
3085ffd83dbSDimitry Andric     expression_variable_sp = m_completed_plans[i]->GetExpressionVariable();
3095ffd83dbSDimitry Andric     if (expression_variable_sp)
3105ffd83dbSDimitry Andric       return expression_variable_sp;
3115ffd83dbSDimitry Andric   }
3125ffd83dbSDimitry Andric   return {};
3135ffd83dbSDimitry Andric }
AnyPlans() const3145ffd83dbSDimitry Andric bool ThreadPlanStack::AnyPlans() const {
315fe6060f1SDimitry Andric   std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
3165ffd83dbSDimitry Andric   // There is always a base plan...
3175ffd83dbSDimitry Andric   return m_plans.size() > 1;
3185ffd83dbSDimitry Andric }
3195ffd83dbSDimitry Andric 
AnyCompletedPlans() const3205ffd83dbSDimitry Andric bool ThreadPlanStack::AnyCompletedPlans() const {
321fe6060f1SDimitry Andric   std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
3225ffd83dbSDimitry Andric   return !m_completed_plans.empty();
3235ffd83dbSDimitry Andric }
3245ffd83dbSDimitry Andric 
AnyDiscardedPlans() const3255ffd83dbSDimitry Andric bool ThreadPlanStack::AnyDiscardedPlans() const {
326fe6060f1SDimitry Andric   std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
3275ffd83dbSDimitry Andric   return !m_discarded_plans.empty();
3285ffd83dbSDimitry Andric }
3295ffd83dbSDimitry Andric 
IsPlanDone(ThreadPlan * in_plan) const3305ffd83dbSDimitry Andric bool ThreadPlanStack::IsPlanDone(ThreadPlan *in_plan) const {
331fe6060f1SDimitry Andric   std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
3325ffd83dbSDimitry Andric   for (auto plan : m_completed_plans) {
3335ffd83dbSDimitry Andric     if (plan.get() == in_plan)
3345ffd83dbSDimitry Andric       return true;
3355ffd83dbSDimitry Andric   }
3365ffd83dbSDimitry Andric   return false;
3375ffd83dbSDimitry Andric }
3385ffd83dbSDimitry Andric 
WasPlanDiscarded(ThreadPlan * in_plan) const3395ffd83dbSDimitry Andric bool ThreadPlanStack::WasPlanDiscarded(ThreadPlan *in_plan) const {
340fe6060f1SDimitry Andric   std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
3415ffd83dbSDimitry Andric   for (auto plan : m_discarded_plans) {
3425ffd83dbSDimitry Andric     if (plan.get() == in_plan)
3435ffd83dbSDimitry Andric       return true;
3445ffd83dbSDimitry Andric   }
3455ffd83dbSDimitry Andric   return false;
3465ffd83dbSDimitry Andric }
3475ffd83dbSDimitry Andric 
GetPreviousPlan(ThreadPlan * current_plan) const3485ffd83dbSDimitry Andric ThreadPlan *ThreadPlanStack::GetPreviousPlan(ThreadPlan *current_plan) const {
349fe6060f1SDimitry Andric   std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
3505ffd83dbSDimitry Andric   if (current_plan == nullptr)
3515ffd83dbSDimitry Andric     return nullptr;
3525ffd83dbSDimitry Andric 
3535ffd83dbSDimitry Andric   // Look first in the completed plans, if the plan is here and there is
3545ffd83dbSDimitry Andric   // a completed plan above it, return that.
3555ffd83dbSDimitry Andric   int stack_size = m_completed_plans.size();
3565ffd83dbSDimitry Andric   for (int i = stack_size - 1; i > 0; i--) {
3575ffd83dbSDimitry Andric     if (current_plan == m_completed_plans[i].get())
3585ffd83dbSDimitry Andric       return m_completed_plans[i - 1].get();
3595ffd83dbSDimitry Andric   }
3605ffd83dbSDimitry Andric 
3615ffd83dbSDimitry Andric   // If this is the first completed plan, the previous one is the
3625ffd83dbSDimitry Andric   // bottom of the regular plan stack.
3635ffd83dbSDimitry Andric   if (stack_size > 0 && m_completed_plans[0].get() == current_plan) {
3645ffd83dbSDimitry Andric     return GetCurrentPlan().get();
3655ffd83dbSDimitry Andric   }
3665ffd83dbSDimitry Andric 
3675ffd83dbSDimitry Andric   // Otherwise look for it in the regular plans.
3685ffd83dbSDimitry Andric   stack_size = m_plans.size();
3695ffd83dbSDimitry Andric   for (int i = stack_size - 1; i > 0; i--) {
3705ffd83dbSDimitry Andric     if (current_plan == m_plans[i].get())
3715ffd83dbSDimitry Andric       return m_plans[i - 1].get();
3725ffd83dbSDimitry Andric   }
3735ffd83dbSDimitry Andric   return nullptr;
3745ffd83dbSDimitry Andric }
3755ffd83dbSDimitry Andric 
GetInnermostExpression() const3765ffd83dbSDimitry Andric ThreadPlan *ThreadPlanStack::GetInnermostExpression() const {
377fe6060f1SDimitry Andric   std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
3785ffd83dbSDimitry Andric   int stack_size = m_plans.size();
3795ffd83dbSDimitry Andric 
3805ffd83dbSDimitry Andric   for (int i = stack_size - 1; i > 0; i--) {
3815ffd83dbSDimitry Andric     if (m_plans[i]->GetKind() == ThreadPlan::eKindCallFunction)
3825ffd83dbSDimitry Andric       return m_plans[i].get();
3835ffd83dbSDimitry Andric   }
3845ffd83dbSDimitry Andric   return nullptr;
3855ffd83dbSDimitry Andric }
3865ffd83dbSDimitry Andric 
ClearThreadCache()387e8d8bef9SDimitry Andric void ThreadPlanStack::ClearThreadCache() {
388fe6060f1SDimitry Andric   std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
389e8d8bef9SDimitry Andric   for (lldb::ThreadPlanSP thread_plan_sp : m_plans)
390e8d8bef9SDimitry Andric     thread_plan_sp->ClearThreadCache();
391e8d8bef9SDimitry Andric }
392e8d8bef9SDimitry Andric 
WillResume()3935ffd83dbSDimitry Andric void ThreadPlanStack::WillResume() {
394fe6060f1SDimitry Andric   std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
3955ffd83dbSDimitry Andric   m_completed_plans.clear();
3965ffd83dbSDimitry Andric   m_discarded_plans.clear();
3975ffd83dbSDimitry Andric }
3985ffd83dbSDimitry Andric 
Update(ThreadList & current_threads,bool delete_missing,bool check_for_new)3995ffd83dbSDimitry Andric void ThreadPlanStackMap::Update(ThreadList &current_threads,
4005ffd83dbSDimitry Andric                                 bool delete_missing,
4015ffd83dbSDimitry Andric                                 bool check_for_new) {
4025ffd83dbSDimitry Andric 
40381ad6265SDimitry Andric   std::lock_guard<std::recursive_mutex> guard(m_stack_map_mutex);
4045ffd83dbSDimitry Andric   // Now find all the new threads and add them to the map:
4055ffd83dbSDimitry Andric   if (check_for_new) {
4065ffd83dbSDimitry Andric     for (auto thread : current_threads.Threads()) {
4075ffd83dbSDimitry Andric       lldb::tid_t cur_tid = thread->GetID();
4085ffd83dbSDimitry Andric       if (!Find(cur_tid)) {
409*bdd1243dSDimitry Andric         AddThread(*thread);
410fe6060f1SDimitry Andric         thread->QueueBasePlan(true);
4115ffd83dbSDimitry Andric       }
4125ffd83dbSDimitry Andric     }
4135ffd83dbSDimitry Andric   }
4145ffd83dbSDimitry Andric 
4155ffd83dbSDimitry Andric   // If we aren't reaping missing threads at this point,
4165ffd83dbSDimitry Andric   // we are done.
4175ffd83dbSDimitry Andric   if (!delete_missing)
4185ffd83dbSDimitry Andric     return;
4195ffd83dbSDimitry Andric   // Otherwise scan for absent TID's.
4205ffd83dbSDimitry Andric   std::vector<lldb::tid_t> missing_threads;
4215ffd83dbSDimitry Andric   // If we are going to delete plans from the plan stack,
4225ffd83dbSDimitry Andric   // then scan for absent TID's:
423fe6060f1SDimitry Andric   for (auto &thread_plans : m_plans_list) {
4245ffd83dbSDimitry Andric     lldb::tid_t cur_tid = thread_plans.first;
4255ffd83dbSDimitry Andric     ThreadSP thread_sp = current_threads.FindThreadByID(cur_tid);
4265ffd83dbSDimitry Andric     if (!thread_sp)
4275ffd83dbSDimitry Andric       missing_threads.push_back(cur_tid);
4285ffd83dbSDimitry Andric   }
4295ffd83dbSDimitry Andric   for (lldb::tid_t tid : missing_threads) {
4305ffd83dbSDimitry Andric     RemoveTID(tid);
4315ffd83dbSDimitry Andric   }
4325ffd83dbSDimitry Andric }
4335ffd83dbSDimitry Andric 
DumpPlans(Stream & strm,lldb::DescriptionLevel desc_level,bool internal,bool condense_if_trivial,bool skip_unreported)4345ffd83dbSDimitry Andric void ThreadPlanStackMap::DumpPlans(Stream &strm,
4355ffd83dbSDimitry Andric                                    lldb::DescriptionLevel desc_level,
4365ffd83dbSDimitry Andric                                    bool internal, bool condense_if_trivial,
4375ffd83dbSDimitry Andric                                    bool skip_unreported) {
43881ad6265SDimitry Andric   std::lock_guard<std::recursive_mutex> guard(m_stack_map_mutex);
439fe6060f1SDimitry Andric   for (auto &elem : m_plans_list) {
4405ffd83dbSDimitry Andric     lldb::tid_t tid = elem.first;
4415ffd83dbSDimitry Andric     uint32_t index_id = 0;
4425ffd83dbSDimitry Andric     ThreadSP thread_sp = m_process.GetThreadList().FindThreadByID(tid);
4435ffd83dbSDimitry Andric 
4445ffd83dbSDimitry Andric     if (skip_unreported) {
4455ffd83dbSDimitry Andric       if (!thread_sp)
4465ffd83dbSDimitry Andric         continue;
4475ffd83dbSDimitry Andric     }
4485ffd83dbSDimitry Andric     if (thread_sp)
4495ffd83dbSDimitry Andric       index_id = thread_sp->GetIndexID();
4505ffd83dbSDimitry Andric 
4515ffd83dbSDimitry Andric     if (condense_if_trivial) {
4525ffd83dbSDimitry Andric       if (!elem.second.AnyPlans() && !elem.second.AnyCompletedPlans() &&
4535ffd83dbSDimitry Andric           !elem.second.AnyDiscardedPlans()) {
4545ffd83dbSDimitry Andric         strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 "\n", index_id, tid);
4555ffd83dbSDimitry Andric         strm.IndentMore();
4565ffd83dbSDimitry Andric         strm.Indent();
4575ffd83dbSDimitry Andric         strm.Printf("No active thread plans\n");
4585ffd83dbSDimitry Andric         strm.IndentLess();
4595ffd83dbSDimitry Andric         return;
4605ffd83dbSDimitry Andric       }
4615ffd83dbSDimitry Andric     }
4625ffd83dbSDimitry Andric 
4635ffd83dbSDimitry Andric     strm.Indent();
4645ffd83dbSDimitry Andric     strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 ":\n", index_id, tid);
4655ffd83dbSDimitry Andric 
4665ffd83dbSDimitry Andric     elem.second.DumpThreadPlans(strm, desc_level, internal);
4675ffd83dbSDimitry Andric   }
4685ffd83dbSDimitry Andric }
4695ffd83dbSDimitry Andric 
DumpPlansForTID(Stream & strm,lldb::tid_t tid,lldb::DescriptionLevel desc_level,bool internal,bool condense_if_trivial,bool skip_unreported)4705ffd83dbSDimitry Andric bool ThreadPlanStackMap::DumpPlansForTID(Stream &strm, lldb::tid_t tid,
4715ffd83dbSDimitry Andric                                          lldb::DescriptionLevel desc_level,
4725ffd83dbSDimitry Andric                                          bool internal,
4735ffd83dbSDimitry Andric                                          bool condense_if_trivial,
4745ffd83dbSDimitry Andric                                          bool skip_unreported) {
47581ad6265SDimitry Andric   std::lock_guard<std::recursive_mutex> guard(m_stack_map_mutex);
4765ffd83dbSDimitry Andric   uint32_t index_id = 0;
4775ffd83dbSDimitry Andric   ThreadSP thread_sp = m_process.GetThreadList().FindThreadByID(tid);
4785ffd83dbSDimitry Andric 
4795ffd83dbSDimitry Andric   if (skip_unreported) {
4805ffd83dbSDimitry Andric     if (!thread_sp) {
4815ffd83dbSDimitry Andric       strm.Format("Unknown TID: {0}", tid);
4825ffd83dbSDimitry Andric       return false;
4835ffd83dbSDimitry Andric     }
4845ffd83dbSDimitry Andric   }
4855ffd83dbSDimitry Andric 
4865ffd83dbSDimitry Andric   if (thread_sp)
4875ffd83dbSDimitry Andric     index_id = thread_sp->GetIndexID();
4885ffd83dbSDimitry Andric   ThreadPlanStack *stack = Find(tid);
4895ffd83dbSDimitry Andric   if (!stack) {
4905ffd83dbSDimitry Andric     strm.Format("Unknown TID: {0}\n", tid);
4915ffd83dbSDimitry Andric     return false;
4925ffd83dbSDimitry Andric   }
4935ffd83dbSDimitry Andric 
4945ffd83dbSDimitry Andric   if (condense_if_trivial) {
4955ffd83dbSDimitry Andric     if (!stack->AnyPlans() && !stack->AnyCompletedPlans() &&
4965ffd83dbSDimitry Andric         !stack->AnyDiscardedPlans()) {
4975ffd83dbSDimitry Andric       strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 "\n", index_id, tid);
4985ffd83dbSDimitry Andric       strm.IndentMore();
4995ffd83dbSDimitry Andric       strm.Indent();
5005ffd83dbSDimitry Andric       strm.Printf("No active thread plans\n");
5015ffd83dbSDimitry Andric       strm.IndentLess();
5025ffd83dbSDimitry Andric       return true;
5035ffd83dbSDimitry Andric     }
5045ffd83dbSDimitry Andric   }
5055ffd83dbSDimitry Andric 
5065ffd83dbSDimitry Andric   strm.Indent();
5075ffd83dbSDimitry Andric   strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 ":\n", index_id, tid);
5085ffd83dbSDimitry Andric 
5095ffd83dbSDimitry Andric   stack->DumpThreadPlans(strm, desc_level, internal);
5105ffd83dbSDimitry Andric   return true;
5115ffd83dbSDimitry Andric }
5125ffd83dbSDimitry Andric 
PrunePlansForTID(lldb::tid_t tid)5135ffd83dbSDimitry Andric bool ThreadPlanStackMap::PrunePlansForTID(lldb::tid_t tid) {
5145ffd83dbSDimitry Andric   // We only remove the plans for unreported TID's.
51581ad6265SDimitry Andric   std::lock_guard<std::recursive_mutex> guard(m_stack_map_mutex);
5165ffd83dbSDimitry Andric   ThreadSP thread_sp = m_process.GetThreadList().FindThreadByID(tid);
5175ffd83dbSDimitry Andric   if (thread_sp)
5185ffd83dbSDimitry Andric     return false;
5195ffd83dbSDimitry Andric 
5205ffd83dbSDimitry Andric   return RemoveTID(tid);
5215ffd83dbSDimitry Andric }
522