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 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 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 395ffd83dbSDimitry 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 525ffd83dbSDimitry 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 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 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 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 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 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 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 153*349cc55cSDimitry Andric // Note that moving the top element of the vector would leave it in an 154*349cc55cSDimitry Andric // undefined state, and break the guarantee that the stack's thread plans are 155*349cc55cSDimitry Andric // all valid. 156*349cc55cSDimitry Andric lldb::ThreadPlanSP plan_sp = m_plans.back(); 1575ffd83dbSDimitry Andric m_plans.pop_back(); 158*349cc55cSDimitry Andric m_completed_plans.push_back(plan_sp); 159*349cc55cSDimitry Andric plan_sp->DidPop(); 1605ffd83dbSDimitry Andric return plan_sp; 1615ffd83dbSDimitry Andric } 1625ffd83dbSDimitry Andric 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 167*349cc55cSDimitry Andric // Note that moving the top element of the vector would leave it in an 168*349cc55cSDimitry Andric // undefined state, and break the guarantee that the stack's thread plans are 169*349cc55cSDimitry Andric // all valid. 170*349cc55cSDimitry Andric lldb::ThreadPlanSP plan_sp = m_plans.back(); 1715ffd83dbSDimitry Andric m_plans.pop_back(); 172*349cc55cSDimitry Andric m_discarded_plans.push_back(plan_sp); 173*349cc55cSDimitry 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. 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 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 return; 2145ffd83dbSDimitry Andric } 2155ffd83dbSDimitry Andric 216*349cc55cSDimitry Andric void ThreadPlanStack::DiscardConsultingControllingPlans() { 217fe6060f1SDimitry Andric std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 2185ffd83dbSDimitry Andric while (true) { 219*349cc55cSDimitry Andric int controlling_plan_idx; 2205ffd83dbSDimitry Andric bool discard = true; 2215ffd83dbSDimitry Andric 222*349cc55cSDimitry Andric // Find the first controlling plan, see if it wants discarding, and if yes 2235ffd83dbSDimitry Andric // discard up to it. 224*349cc55cSDimitry Andric for (controlling_plan_idx = m_plans.size() - 1; controlling_plan_idx >= 0; 225*349cc55cSDimitry Andric controlling_plan_idx--) { 226*349cc55cSDimitry Andric if (m_plans[controlling_plan_idx]->IsControllingPlan()) { 227*349cc55cSDimitry Andric discard = m_plans[controlling_plan_idx]->OkayToDiscard(); 2285ffd83dbSDimitry Andric break; 2295ffd83dbSDimitry Andric } 2305ffd83dbSDimitry Andric } 2315ffd83dbSDimitry Andric 232*349cc55cSDimitry Andric // If the controlling plan doesn't want to get discarded, then we're done. 2335ffd83dbSDimitry Andric if (!discard) 2345ffd83dbSDimitry Andric return; 2355ffd83dbSDimitry Andric 2365ffd83dbSDimitry Andric // First pop all the dependent plans: 237*349cc55cSDimitry Andric for (int i = m_plans.size() - 1; i > controlling_plan_idx; i--) { 2385ffd83dbSDimitry Andric DiscardPlan(); 2395ffd83dbSDimitry Andric } 2405ffd83dbSDimitry Andric 241*349cc55cSDimitry Andric // Now discard the controlling plan itself. 2425ffd83dbSDimitry Andric // The bottom-most plan never gets discarded. "OkayToDiscard" for it 2435ffd83dbSDimitry Andric // means discard it's dependent plans, but not it... 244*349cc55cSDimitry Andric if (controlling_plan_idx > 0) { 2455ffd83dbSDimitry Andric DiscardPlan(); 2465ffd83dbSDimitry Andric } 2475ffd83dbSDimitry Andric } 2485ffd83dbSDimitry Andric } 2495ffd83dbSDimitry Andric 2505ffd83dbSDimitry Andric lldb::ThreadPlanSP ThreadPlanStack::GetCurrentPlan() const { 251fe6060f1SDimitry Andric std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 2525ffd83dbSDimitry Andric assert(m_plans.size() != 0 && "There will always be a base plan."); 2535ffd83dbSDimitry Andric return m_plans.back(); 2545ffd83dbSDimitry Andric } 2555ffd83dbSDimitry Andric 2565ffd83dbSDimitry Andric lldb::ThreadPlanSP ThreadPlanStack::GetCompletedPlan(bool skip_private) const { 257fe6060f1SDimitry Andric std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 2585ffd83dbSDimitry Andric if (m_completed_plans.empty()) 2595ffd83dbSDimitry Andric return {}; 2605ffd83dbSDimitry Andric 2615ffd83dbSDimitry Andric if (!skip_private) 2625ffd83dbSDimitry Andric return m_completed_plans.back(); 2635ffd83dbSDimitry Andric 2645ffd83dbSDimitry Andric for (int i = m_completed_plans.size() - 1; i >= 0; i--) { 2655ffd83dbSDimitry Andric lldb::ThreadPlanSP completed_plan_sp; 2665ffd83dbSDimitry Andric completed_plan_sp = m_completed_plans[i]; 2675ffd83dbSDimitry Andric if (!completed_plan_sp->GetPrivate()) 2685ffd83dbSDimitry Andric return completed_plan_sp; 2695ffd83dbSDimitry Andric } 2705ffd83dbSDimitry Andric return {}; 2715ffd83dbSDimitry Andric } 2725ffd83dbSDimitry Andric 2735ffd83dbSDimitry Andric lldb::ThreadPlanSP ThreadPlanStack::GetPlanByIndex(uint32_t plan_idx, 2745ffd83dbSDimitry Andric bool skip_private) const { 275fe6060f1SDimitry Andric std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 2765ffd83dbSDimitry Andric uint32_t idx = 0; 2775ffd83dbSDimitry Andric 2785ffd83dbSDimitry Andric for (lldb::ThreadPlanSP plan_sp : m_plans) { 2795ffd83dbSDimitry Andric if (skip_private && plan_sp->GetPrivate()) 2805ffd83dbSDimitry Andric continue; 2815ffd83dbSDimitry Andric if (idx == plan_idx) 2825ffd83dbSDimitry Andric return plan_sp; 2835ffd83dbSDimitry Andric idx++; 2845ffd83dbSDimitry Andric } 2855ffd83dbSDimitry Andric return {}; 2865ffd83dbSDimitry Andric } 2875ffd83dbSDimitry Andric 2885ffd83dbSDimitry Andric lldb::ValueObjectSP ThreadPlanStack::GetReturnValueObject() const { 289fe6060f1SDimitry Andric std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 2905ffd83dbSDimitry Andric if (m_completed_plans.empty()) 2915ffd83dbSDimitry Andric return {}; 2925ffd83dbSDimitry Andric 2935ffd83dbSDimitry Andric for (int i = m_completed_plans.size() - 1; i >= 0; i--) { 2945ffd83dbSDimitry Andric lldb::ValueObjectSP return_valobj_sp; 2955ffd83dbSDimitry Andric return_valobj_sp = m_completed_plans[i]->GetReturnValueObject(); 2965ffd83dbSDimitry Andric if (return_valobj_sp) 2975ffd83dbSDimitry Andric return return_valobj_sp; 2985ffd83dbSDimitry Andric } 2995ffd83dbSDimitry Andric return {}; 3005ffd83dbSDimitry Andric } 3015ffd83dbSDimitry Andric 3025ffd83dbSDimitry Andric lldb::ExpressionVariableSP ThreadPlanStack::GetExpressionVariable() const { 303fe6060f1SDimitry Andric std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 3045ffd83dbSDimitry Andric if (m_completed_plans.empty()) 3055ffd83dbSDimitry Andric return {}; 3065ffd83dbSDimitry Andric 3075ffd83dbSDimitry Andric for (int i = m_completed_plans.size() - 1; i >= 0; i--) { 3085ffd83dbSDimitry Andric lldb::ExpressionVariableSP expression_variable_sp; 3095ffd83dbSDimitry Andric expression_variable_sp = m_completed_plans[i]->GetExpressionVariable(); 3105ffd83dbSDimitry Andric if (expression_variable_sp) 3115ffd83dbSDimitry Andric return expression_variable_sp; 3125ffd83dbSDimitry Andric } 3135ffd83dbSDimitry Andric return {}; 3145ffd83dbSDimitry Andric } 3155ffd83dbSDimitry Andric bool ThreadPlanStack::AnyPlans() const { 316fe6060f1SDimitry Andric std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 3175ffd83dbSDimitry Andric // There is always a base plan... 3185ffd83dbSDimitry Andric return m_plans.size() > 1; 3195ffd83dbSDimitry Andric } 3205ffd83dbSDimitry Andric 3215ffd83dbSDimitry Andric bool ThreadPlanStack::AnyCompletedPlans() const { 322fe6060f1SDimitry Andric std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 3235ffd83dbSDimitry Andric return !m_completed_plans.empty(); 3245ffd83dbSDimitry Andric } 3255ffd83dbSDimitry Andric 3265ffd83dbSDimitry Andric bool ThreadPlanStack::AnyDiscardedPlans() const { 327fe6060f1SDimitry Andric std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 3285ffd83dbSDimitry Andric return !m_discarded_plans.empty(); 3295ffd83dbSDimitry Andric } 3305ffd83dbSDimitry Andric 3315ffd83dbSDimitry Andric bool ThreadPlanStack::IsPlanDone(ThreadPlan *in_plan) const { 332fe6060f1SDimitry Andric std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 3335ffd83dbSDimitry Andric for (auto plan : m_completed_plans) { 3345ffd83dbSDimitry Andric if (plan.get() == in_plan) 3355ffd83dbSDimitry Andric return true; 3365ffd83dbSDimitry Andric } 3375ffd83dbSDimitry Andric return false; 3385ffd83dbSDimitry Andric } 3395ffd83dbSDimitry Andric 3405ffd83dbSDimitry Andric bool ThreadPlanStack::WasPlanDiscarded(ThreadPlan *in_plan) const { 341fe6060f1SDimitry Andric std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 3425ffd83dbSDimitry Andric for (auto plan : m_discarded_plans) { 3435ffd83dbSDimitry Andric if (plan.get() == in_plan) 3445ffd83dbSDimitry Andric return true; 3455ffd83dbSDimitry Andric } 3465ffd83dbSDimitry Andric return false; 3475ffd83dbSDimitry Andric } 3485ffd83dbSDimitry Andric 3495ffd83dbSDimitry Andric ThreadPlan *ThreadPlanStack::GetPreviousPlan(ThreadPlan *current_plan) const { 350fe6060f1SDimitry Andric std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 3515ffd83dbSDimitry Andric if (current_plan == nullptr) 3525ffd83dbSDimitry Andric return nullptr; 3535ffd83dbSDimitry Andric 3545ffd83dbSDimitry Andric // Look first in the completed plans, if the plan is here and there is 3555ffd83dbSDimitry Andric // a completed plan above it, return that. 3565ffd83dbSDimitry Andric int stack_size = m_completed_plans.size(); 3575ffd83dbSDimitry Andric for (int i = stack_size - 1; i > 0; i--) { 3585ffd83dbSDimitry Andric if (current_plan == m_completed_plans[i].get()) 3595ffd83dbSDimitry Andric return m_completed_plans[i - 1].get(); 3605ffd83dbSDimitry Andric } 3615ffd83dbSDimitry Andric 3625ffd83dbSDimitry Andric // If this is the first completed plan, the previous one is the 3635ffd83dbSDimitry Andric // bottom of the regular plan stack. 3645ffd83dbSDimitry Andric if (stack_size > 0 && m_completed_plans[0].get() == current_plan) { 3655ffd83dbSDimitry Andric return GetCurrentPlan().get(); 3665ffd83dbSDimitry Andric } 3675ffd83dbSDimitry Andric 3685ffd83dbSDimitry Andric // Otherwise look for it in the regular plans. 3695ffd83dbSDimitry Andric stack_size = m_plans.size(); 3705ffd83dbSDimitry Andric for (int i = stack_size - 1; i > 0; i--) { 3715ffd83dbSDimitry Andric if (current_plan == m_plans[i].get()) 3725ffd83dbSDimitry Andric return m_plans[i - 1].get(); 3735ffd83dbSDimitry Andric } 3745ffd83dbSDimitry Andric return nullptr; 3755ffd83dbSDimitry Andric } 3765ffd83dbSDimitry Andric 3775ffd83dbSDimitry Andric ThreadPlan *ThreadPlanStack::GetInnermostExpression() const { 378fe6060f1SDimitry Andric std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 3795ffd83dbSDimitry Andric int stack_size = m_plans.size(); 3805ffd83dbSDimitry Andric 3815ffd83dbSDimitry Andric for (int i = stack_size - 1; i > 0; i--) { 3825ffd83dbSDimitry Andric if (m_plans[i]->GetKind() == ThreadPlan::eKindCallFunction) 3835ffd83dbSDimitry Andric return m_plans[i].get(); 3845ffd83dbSDimitry Andric } 3855ffd83dbSDimitry Andric return nullptr; 3865ffd83dbSDimitry Andric } 3875ffd83dbSDimitry Andric 388e8d8bef9SDimitry Andric void ThreadPlanStack::ClearThreadCache() { 389fe6060f1SDimitry Andric std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 390e8d8bef9SDimitry Andric for (lldb::ThreadPlanSP thread_plan_sp : m_plans) 391e8d8bef9SDimitry Andric thread_plan_sp->ClearThreadCache(); 392e8d8bef9SDimitry Andric } 393e8d8bef9SDimitry Andric 3945ffd83dbSDimitry Andric void ThreadPlanStack::WillResume() { 395fe6060f1SDimitry Andric std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 3965ffd83dbSDimitry Andric m_completed_plans.clear(); 3975ffd83dbSDimitry Andric m_discarded_plans.clear(); 3985ffd83dbSDimitry Andric } 3995ffd83dbSDimitry Andric 4005ffd83dbSDimitry Andric void ThreadPlanStackMap::Update(ThreadList ¤t_threads, 4015ffd83dbSDimitry Andric bool delete_missing, 4025ffd83dbSDimitry Andric bool check_for_new) { 4035ffd83dbSDimitry Andric 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)) { 4095ffd83dbSDimitry Andric AddThread(*thread.get()); 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 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) { 438fe6060f1SDimitry Andric for (auto &elem : m_plans_list) { 4395ffd83dbSDimitry Andric lldb::tid_t tid = elem.first; 4405ffd83dbSDimitry Andric uint32_t index_id = 0; 4415ffd83dbSDimitry Andric ThreadSP thread_sp = m_process.GetThreadList().FindThreadByID(tid); 4425ffd83dbSDimitry Andric 4435ffd83dbSDimitry Andric if (skip_unreported) { 4445ffd83dbSDimitry Andric if (!thread_sp) 4455ffd83dbSDimitry Andric continue; 4465ffd83dbSDimitry Andric } 4475ffd83dbSDimitry Andric if (thread_sp) 4485ffd83dbSDimitry Andric index_id = thread_sp->GetIndexID(); 4495ffd83dbSDimitry Andric 4505ffd83dbSDimitry Andric if (condense_if_trivial) { 4515ffd83dbSDimitry Andric if (!elem.second.AnyPlans() && !elem.second.AnyCompletedPlans() && 4525ffd83dbSDimitry Andric !elem.second.AnyDiscardedPlans()) { 4535ffd83dbSDimitry Andric strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 "\n", index_id, tid); 4545ffd83dbSDimitry Andric strm.IndentMore(); 4555ffd83dbSDimitry Andric strm.Indent(); 4565ffd83dbSDimitry Andric strm.Printf("No active thread plans\n"); 4575ffd83dbSDimitry Andric strm.IndentLess(); 4585ffd83dbSDimitry Andric return; 4595ffd83dbSDimitry Andric } 4605ffd83dbSDimitry Andric } 4615ffd83dbSDimitry Andric 4625ffd83dbSDimitry Andric strm.Indent(); 4635ffd83dbSDimitry Andric strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 ":\n", index_id, tid); 4645ffd83dbSDimitry Andric 4655ffd83dbSDimitry Andric elem.second.DumpThreadPlans(strm, desc_level, internal); 4665ffd83dbSDimitry Andric } 4675ffd83dbSDimitry Andric } 4685ffd83dbSDimitry Andric 4695ffd83dbSDimitry Andric bool ThreadPlanStackMap::DumpPlansForTID(Stream &strm, lldb::tid_t tid, 4705ffd83dbSDimitry Andric lldb::DescriptionLevel desc_level, 4715ffd83dbSDimitry Andric bool internal, 4725ffd83dbSDimitry Andric bool condense_if_trivial, 4735ffd83dbSDimitry Andric bool skip_unreported) { 4745ffd83dbSDimitry Andric uint32_t index_id = 0; 4755ffd83dbSDimitry Andric ThreadSP thread_sp = m_process.GetThreadList().FindThreadByID(tid); 4765ffd83dbSDimitry Andric 4775ffd83dbSDimitry Andric if (skip_unreported) { 4785ffd83dbSDimitry Andric if (!thread_sp) { 4795ffd83dbSDimitry Andric strm.Format("Unknown TID: {0}", tid); 4805ffd83dbSDimitry Andric return false; 4815ffd83dbSDimitry Andric } 4825ffd83dbSDimitry Andric } 4835ffd83dbSDimitry Andric 4845ffd83dbSDimitry Andric if (thread_sp) 4855ffd83dbSDimitry Andric index_id = thread_sp->GetIndexID(); 4865ffd83dbSDimitry Andric ThreadPlanStack *stack = Find(tid); 4875ffd83dbSDimitry Andric if (!stack) { 4885ffd83dbSDimitry Andric strm.Format("Unknown TID: {0}\n", tid); 4895ffd83dbSDimitry Andric return false; 4905ffd83dbSDimitry Andric } 4915ffd83dbSDimitry Andric 4925ffd83dbSDimitry Andric if (condense_if_trivial) { 4935ffd83dbSDimitry Andric if (!stack->AnyPlans() && !stack->AnyCompletedPlans() && 4945ffd83dbSDimitry Andric !stack->AnyDiscardedPlans()) { 4955ffd83dbSDimitry Andric strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 "\n", index_id, tid); 4965ffd83dbSDimitry Andric strm.IndentMore(); 4975ffd83dbSDimitry Andric strm.Indent(); 4985ffd83dbSDimitry Andric strm.Printf("No active thread plans\n"); 4995ffd83dbSDimitry Andric strm.IndentLess(); 5005ffd83dbSDimitry Andric return true; 5015ffd83dbSDimitry Andric } 5025ffd83dbSDimitry Andric } 5035ffd83dbSDimitry Andric 5045ffd83dbSDimitry Andric strm.Indent(); 5055ffd83dbSDimitry Andric strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 ":\n", index_id, tid); 5065ffd83dbSDimitry Andric 5075ffd83dbSDimitry Andric stack->DumpThreadPlans(strm, desc_level, internal); 5085ffd83dbSDimitry Andric return true; 5095ffd83dbSDimitry Andric } 5105ffd83dbSDimitry Andric 5115ffd83dbSDimitry Andric bool ThreadPlanStackMap::PrunePlansForTID(lldb::tid_t tid) { 5125ffd83dbSDimitry Andric // We only remove the plans for unreported TID's. 5135ffd83dbSDimitry Andric ThreadSP thread_sp = m_process.GetThreadList().FindThreadByID(tid); 5145ffd83dbSDimitry Andric if (thread_sp) 5155ffd83dbSDimitry Andric return false; 5165ffd83dbSDimitry Andric 5175ffd83dbSDimitry Andric return RemoveTID(tid); 5185ffd83dbSDimitry Andric } 519