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 { 42*fe6060f1SDimitry 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 { 56*fe6060f1SDimitry 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() { 85*fe6060f1SDimitry 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) { 93*fe6060f1SDimitry 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) { 102*fe6060f1SDimitry 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: 108*fe6060f1SDimitry 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: 137*fe6060f1SDimitry 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() { 150*fe6060f1SDimitry 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 1535ffd83dbSDimitry Andric lldb::ThreadPlanSP plan_sp = std::move(m_plans.back()); 1545ffd83dbSDimitry Andric m_completed_plans.push_back(plan_sp); 1555ffd83dbSDimitry Andric plan_sp->WillPop(); 1565ffd83dbSDimitry Andric m_plans.pop_back(); 1575ffd83dbSDimitry Andric return plan_sp; 1585ffd83dbSDimitry Andric } 1595ffd83dbSDimitry Andric 1605ffd83dbSDimitry Andric lldb::ThreadPlanSP ThreadPlanStack::DiscardPlan() { 161*fe6060f1SDimitry Andric std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 1625ffd83dbSDimitry Andric assert(m_plans.size() > 1 && "Can't discard the base thread plan"); 1635ffd83dbSDimitry Andric 1645ffd83dbSDimitry Andric lldb::ThreadPlanSP plan_sp = std::move(m_plans.back()); 1655ffd83dbSDimitry Andric m_discarded_plans.push_back(plan_sp); 1665ffd83dbSDimitry Andric plan_sp->WillPop(); 1675ffd83dbSDimitry Andric m_plans.pop_back(); 1685ffd83dbSDimitry Andric return plan_sp; 1695ffd83dbSDimitry Andric } 1705ffd83dbSDimitry Andric 1715ffd83dbSDimitry Andric // If the input plan is nullptr, discard all plans. Otherwise make sure this 1725ffd83dbSDimitry Andric // plan is in the stack, and if so discard up to and including it. 1735ffd83dbSDimitry Andric void ThreadPlanStack::DiscardPlansUpToPlan(ThreadPlan *up_to_plan_ptr) { 174*fe6060f1SDimitry Andric std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 1755ffd83dbSDimitry Andric int stack_size = m_plans.size(); 1765ffd83dbSDimitry Andric 1775ffd83dbSDimitry Andric if (up_to_plan_ptr == nullptr) { 1785ffd83dbSDimitry Andric for (int i = stack_size - 1; i > 0; i--) 1795ffd83dbSDimitry Andric DiscardPlan(); 1805ffd83dbSDimitry Andric return; 1815ffd83dbSDimitry Andric } 1825ffd83dbSDimitry Andric 1835ffd83dbSDimitry Andric bool found_it = false; 1845ffd83dbSDimitry Andric for (int i = stack_size - 1; i > 0; i--) { 1855ffd83dbSDimitry Andric if (m_plans[i].get() == up_to_plan_ptr) { 1865ffd83dbSDimitry Andric found_it = true; 1875ffd83dbSDimitry Andric break; 1885ffd83dbSDimitry Andric } 1895ffd83dbSDimitry Andric } 1905ffd83dbSDimitry Andric 1915ffd83dbSDimitry Andric if (found_it) { 1925ffd83dbSDimitry Andric bool last_one = false; 1935ffd83dbSDimitry Andric for (int i = stack_size - 1; i > 0 && !last_one; i--) { 1945ffd83dbSDimitry Andric if (GetCurrentPlan().get() == up_to_plan_ptr) 1955ffd83dbSDimitry Andric last_one = true; 1965ffd83dbSDimitry Andric DiscardPlan(); 1975ffd83dbSDimitry Andric } 1985ffd83dbSDimitry Andric } 1995ffd83dbSDimitry Andric } 2005ffd83dbSDimitry Andric 2015ffd83dbSDimitry Andric void ThreadPlanStack::DiscardAllPlans() { 202*fe6060f1SDimitry Andric std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 2035ffd83dbSDimitry Andric int stack_size = m_plans.size(); 2045ffd83dbSDimitry Andric for (int i = stack_size - 1; i > 0; i--) { 2055ffd83dbSDimitry Andric DiscardPlan(); 2065ffd83dbSDimitry Andric } 2075ffd83dbSDimitry Andric return; 2085ffd83dbSDimitry Andric } 2095ffd83dbSDimitry Andric 2105ffd83dbSDimitry Andric void ThreadPlanStack::DiscardConsultingMasterPlans() { 211*fe6060f1SDimitry Andric std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 2125ffd83dbSDimitry Andric while (true) { 2135ffd83dbSDimitry Andric int master_plan_idx; 2145ffd83dbSDimitry Andric bool discard = true; 2155ffd83dbSDimitry Andric 2165ffd83dbSDimitry Andric // Find the first master plan, see if it wants discarding, and if yes 2175ffd83dbSDimitry Andric // discard up to it. 2185ffd83dbSDimitry Andric for (master_plan_idx = m_plans.size() - 1; master_plan_idx >= 0; 2195ffd83dbSDimitry Andric master_plan_idx--) { 2205ffd83dbSDimitry Andric if (m_plans[master_plan_idx]->IsMasterPlan()) { 2215ffd83dbSDimitry Andric discard = m_plans[master_plan_idx]->OkayToDiscard(); 2225ffd83dbSDimitry Andric break; 2235ffd83dbSDimitry Andric } 2245ffd83dbSDimitry Andric } 2255ffd83dbSDimitry Andric 2265ffd83dbSDimitry Andric // If the master plan doesn't want to get discarded, then we're done. 2275ffd83dbSDimitry Andric if (!discard) 2285ffd83dbSDimitry Andric return; 2295ffd83dbSDimitry Andric 2305ffd83dbSDimitry Andric // First pop all the dependent plans: 2315ffd83dbSDimitry Andric for (int i = m_plans.size() - 1; i > master_plan_idx; i--) { 2325ffd83dbSDimitry Andric DiscardPlan(); 2335ffd83dbSDimitry Andric } 2345ffd83dbSDimitry Andric 2355ffd83dbSDimitry Andric // Now discard the master plan itself. 2365ffd83dbSDimitry Andric // The bottom-most plan never gets discarded. "OkayToDiscard" for it 2375ffd83dbSDimitry Andric // means discard it's dependent plans, but not it... 2385ffd83dbSDimitry Andric if (master_plan_idx > 0) { 2395ffd83dbSDimitry Andric DiscardPlan(); 2405ffd83dbSDimitry Andric } 2415ffd83dbSDimitry Andric } 2425ffd83dbSDimitry Andric } 2435ffd83dbSDimitry Andric 2445ffd83dbSDimitry Andric lldb::ThreadPlanSP ThreadPlanStack::GetCurrentPlan() const { 245*fe6060f1SDimitry Andric std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 2465ffd83dbSDimitry Andric assert(m_plans.size() != 0 && "There will always be a base plan."); 2475ffd83dbSDimitry Andric return m_plans.back(); 2485ffd83dbSDimitry Andric } 2495ffd83dbSDimitry Andric 2505ffd83dbSDimitry Andric lldb::ThreadPlanSP ThreadPlanStack::GetCompletedPlan(bool skip_private) const { 251*fe6060f1SDimitry Andric std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 2525ffd83dbSDimitry Andric if (m_completed_plans.empty()) 2535ffd83dbSDimitry Andric return {}; 2545ffd83dbSDimitry Andric 2555ffd83dbSDimitry Andric if (!skip_private) 2565ffd83dbSDimitry Andric return m_completed_plans.back(); 2575ffd83dbSDimitry Andric 2585ffd83dbSDimitry Andric for (int i = m_completed_plans.size() - 1; i >= 0; i--) { 2595ffd83dbSDimitry Andric lldb::ThreadPlanSP completed_plan_sp; 2605ffd83dbSDimitry Andric completed_plan_sp = m_completed_plans[i]; 2615ffd83dbSDimitry Andric if (!completed_plan_sp->GetPrivate()) 2625ffd83dbSDimitry Andric return completed_plan_sp; 2635ffd83dbSDimitry Andric } 2645ffd83dbSDimitry Andric return {}; 2655ffd83dbSDimitry Andric } 2665ffd83dbSDimitry Andric 2675ffd83dbSDimitry Andric lldb::ThreadPlanSP ThreadPlanStack::GetPlanByIndex(uint32_t plan_idx, 2685ffd83dbSDimitry Andric bool skip_private) const { 269*fe6060f1SDimitry Andric std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 2705ffd83dbSDimitry Andric uint32_t idx = 0; 2715ffd83dbSDimitry Andric 2725ffd83dbSDimitry Andric for (lldb::ThreadPlanSP plan_sp : m_plans) { 2735ffd83dbSDimitry Andric if (skip_private && plan_sp->GetPrivate()) 2745ffd83dbSDimitry Andric continue; 2755ffd83dbSDimitry Andric if (idx == plan_idx) 2765ffd83dbSDimitry Andric return plan_sp; 2775ffd83dbSDimitry Andric idx++; 2785ffd83dbSDimitry Andric } 2795ffd83dbSDimitry Andric return {}; 2805ffd83dbSDimitry Andric } 2815ffd83dbSDimitry Andric 2825ffd83dbSDimitry Andric lldb::ValueObjectSP ThreadPlanStack::GetReturnValueObject() const { 283*fe6060f1SDimitry Andric std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 2845ffd83dbSDimitry Andric if (m_completed_plans.empty()) 2855ffd83dbSDimitry Andric return {}; 2865ffd83dbSDimitry Andric 2875ffd83dbSDimitry Andric for (int i = m_completed_plans.size() - 1; i >= 0; i--) { 2885ffd83dbSDimitry Andric lldb::ValueObjectSP return_valobj_sp; 2895ffd83dbSDimitry Andric return_valobj_sp = m_completed_plans[i]->GetReturnValueObject(); 2905ffd83dbSDimitry Andric if (return_valobj_sp) 2915ffd83dbSDimitry Andric return return_valobj_sp; 2925ffd83dbSDimitry Andric } 2935ffd83dbSDimitry Andric return {}; 2945ffd83dbSDimitry Andric } 2955ffd83dbSDimitry Andric 2965ffd83dbSDimitry Andric lldb::ExpressionVariableSP ThreadPlanStack::GetExpressionVariable() const { 297*fe6060f1SDimitry Andric std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 2985ffd83dbSDimitry Andric if (m_completed_plans.empty()) 2995ffd83dbSDimitry Andric return {}; 3005ffd83dbSDimitry Andric 3015ffd83dbSDimitry Andric for (int i = m_completed_plans.size() - 1; i >= 0; i--) { 3025ffd83dbSDimitry Andric lldb::ExpressionVariableSP expression_variable_sp; 3035ffd83dbSDimitry Andric expression_variable_sp = m_completed_plans[i]->GetExpressionVariable(); 3045ffd83dbSDimitry Andric if (expression_variable_sp) 3055ffd83dbSDimitry Andric return expression_variable_sp; 3065ffd83dbSDimitry Andric } 3075ffd83dbSDimitry Andric return {}; 3085ffd83dbSDimitry Andric } 3095ffd83dbSDimitry Andric bool ThreadPlanStack::AnyPlans() const { 310*fe6060f1SDimitry Andric std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 3115ffd83dbSDimitry Andric // There is always a base plan... 3125ffd83dbSDimitry Andric return m_plans.size() > 1; 3135ffd83dbSDimitry Andric } 3145ffd83dbSDimitry Andric 3155ffd83dbSDimitry Andric bool ThreadPlanStack::AnyCompletedPlans() const { 316*fe6060f1SDimitry Andric std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 3175ffd83dbSDimitry Andric return !m_completed_plans.empty(); 3185ffd83dbSDimitry Andric } 3195ffd83dbSDimitry Andric 3205ffd83dbSDimitry Andric bool ThreadPlanStack::AnyDiscardedPlans() const { 321*fe6060f1SDimitry Andric std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 3225ffd83dbSDimitry Andric return !m_discarded_plans.empty(); 3235ffd83dbSDimitry Andric } 3245ffd83dbSDimitry Andric 3255ffd83dbSDimitry Andric bool ThreadPlanStack::IsPlanDone(ThreadPlan *in_plan) const { 326*fe6060f1SDimitry Andric std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 3275ffd83dbSDimitry Andric for (auto plan : m_completed_plans) { 3285ffd83dbSDimitry Andric if (plan.get() == in_plan) 3295ffd83dbSDimitry Andric return true; 3305ffd83dbSDimitry Andric } 3315ffd83dbSDimitry Andric return false; 3325ffd83dbSDimitry Andric } 3335ffd83dbSDimitry Andric 3345ffd83dbSDimitry Andric bool ThreadPlanStack::WasPlanDiscarded(ThreadPlan *in_plan) const { 335*fe6060f1SDimitry Andric std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 3365ffd83dbSDimitry Andric for (auto plan : m_discarded_plans) { 3375ffd83dbSDimitry Andric if (plan.get() == in_plan) 3385ffd83dbSDimitry Andric return true; 3395ffd83dbSDimitry Andric } 3405ffd83dbSDimitry Andric return false; 3415ffd83dbSDimitry Andric } 3425ffd83dbSDimitry Andric 3435ffd83dbSDimitry Andric ThreadPlan *ThreadPlanStack::GetPreviousPlan(ThreadPlan *current_plan) const { 344*fe6060f1SDimitry Andric std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 3455ffd83dbSDimitry Andric if (current_plan == nullptr) 3465ffd83dbSDimitry Andric return nullptr; 3475ffd83dbSDimitry Andric 3485ffd83dbSDimitry Andric // Look first in the completed plans, if the plan is here and there is 3495ffd83dbSDimitry Andric // a completed plan above it, return that. 3505ffd83dbSDimitry Andric int stack_size = m_completed_plans.size(); 3515ffd83dbSDimitry Andric for (int i = stack_size - 1; i > 0; i--) { 3525ffd83dbSDimitry Andric if (current_plan == m_completed_plans[i].get()) 3535ffd83dbSDimitry Andric return m_completed_plans[i - 1].get(); 3545ffd83dbSDimitry Andric } 3555ffd83dbSDimitry Andric 3565ffd83dbSDimitry Andric // If this is the first completed plan, the previous one is the 3575ffd83dbSDimitry Andric // bottom of the regular plan stack. 3585ffd83dbSDimitry Andric if (stack_size > 0 && m_completed_plans[0].get() == current_plan) { 3595ffd83dbSDimitry Andric return GetCurrentPlan().get(); 3605ffd83dbSDimitry Andric } 3615ffd83dbSDimitry Andric 3625ffd83dbSDimitry Andric // Otherwise look for it in the regular plans. 3635ffd83dbSDimitry Andric stack_size = m_plans.size(); 3645ffd83dbSDimitry Andric for (int i = stack_size - 1; i > 0; i--) { 3655ffd83dbSDimitry Andric if (current_plan == m_plans[i].get()) 3665ffd83dbSDimitry Andric return m_plans[i - 1].get(); 3675ffd83dbSDimitry Andric } 3685ffd83dbSDimitry Andric return nullptr; 3695ffd83dbSDimitry Andric } 3705ffd83dbSDimitry Andric 3715ffd83dbSDimitry Andric ThreadPlan *ThreadPlanStack::GetInnermostExpression() const { 372*fe6060f1SDimitry Andric std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 3735ffd83dbSDimitry Andric int stack_size = m_plans.size(); 3745ffd83dbSDimitry Andric 3755ffd83dbSDimitry Andric for (int i = stack_size - 1; i > 0; i--) { 3765ffd83dbSDimitry Andric if (m_plans[i]->GetKind() == ThreadPlan::eKindCallFunction) 3775ffd83dbSDimitry Andric return m_plans[i].get(); 3785ffd83dbSDimitry Andric } 3795ffd83dbSDimitry Andric return nullptr; 3805ffd83dbSDimitry Andric } 3815ffd83dbSDimitry Andric 382e8d8bef9SDimitry Andric void ThreadPlanStack::ClearThreadCache() { 383*fe6060f1SDimitry Andric std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 384e8d8bef9SDimitry Andric for (lldb::ThreadPlanSP thread_plan_sp : m_plans) 385e8d8bef9SDimitry Andric thread_plan_sp->ClearThreadCache(); 386e8d8bef9SDimitry Andric } 387e8d8bef9SDimitry Andric 3885ffd83dbSDimitry Andric void ThreadPlanStack::WillResume() { 389*fe6060f1SDimitry Andric std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 3905ffd83dbSDimitry Andric m_completed_plans.clear(); 3915ffd83dbSDimitry Andric m_discarded_plans.clear(); 3925ffd83dbSDimitry Andric } 3935ffd83dbSDimitry Andric 3945ffd83dbSDimitry Andric void ThreadPlanStackMap::Update(ThreadList ¤t_threads, 3955ffd83dbSDimitry Andric bool delete_missing, 3965ffd83dbSDimitry Andric bool check_for_new) { 3975ffd83dbSDimitry Andric 3985ffd83dbSDimitry Andric // Now find all the new threads and add them to the map: 3995ffd83dbSDimitry Andric if (check_for_new) { 4005ffd83dbSDimitry Andric for (auto thread : current_threads.Threads()) { 4015ffd83dbSDimitry Andric lldb::tid_t cur_tid = thread->GetID(); 4025ffd83dbSDimitry Andric if (!Find(cur_tid)) { 4035ffd83dbSDimitry Andric AddThread(*thread.get()); 404*fe6060f1SDimitry Andric thread->QueueBasePlan(true); 4055ffd83dbSDimitry Andric } 4065ffd83dbSDimitry Andric } 4075ffd83dbSDimitry Andric } 4085ffd83dbSDimitry Andric 4095ffd83dbSDimitry Andric // If we aren't reaping missing threads at this point, 4105ffd83dbSDimitry Andric // we are done. 4115ffd83dbSDimitry Andric if (!delete_missing) 4125ffd83dbSDimitry Andric return; 4135ffd83dbSDimitry Andric // Otherwise scan for absent TID's. 4145ffd83dbSDimitry Andric std::vector<lldb::tid_t> missing_threads; 4155ffd83dbSDimitry Andric // If we are going to delete plans from the plan stack, 4165ffd83dbSDimitry Andric // then scan for absent TID's: 417*fe6060f1SDimitry Andric for (auto &thread_plans : m_plans_list) { 4185ffd83dbSDimitry Andric lldb::tid_t cur_tid = thread_plans.first; 4195ffd83dbSDimitry Andric ThreadSP thread_sp = current_threads.FindThreadByID(cur_tid); 4205ffd83dbSDimitry Andric if (!thread_sp) 4215ffd83dbSDimitry Andric missing_threads.push_back(cur_tid); 4225ffd83dbSDimitry Andric } 4235ffd83dbSDimitry Andric for (lldb::tid_t tid : missing_threads) { 4245ffd83dbSDimitry Andric RemoveTID(tid); 4255ffd83dbSDimitry Andric } 4265ffd83dbSDimitry Andric } 4275ffd83dbSDimitry Andric 4285ffd83dbSDimitry Andric void ThreadPlanStackMap::DumpPlans(Stream &strm, 4295ffd83dbSDimitry Andric lldb::DescriptionLevel desc_level, 4305ffd83dbSDimitry Andric bool internal, bool condense_if_trivial, 4315ffd83dbSDimitry Andric bool skip_unreported) { 432*fe6060f1SDimitry Andric for (auto &elem : m_plans_list) { 4335ffd83dbSDimitry Andric lldb::tid_t tid = elem.first; 4345ffd83dbSDimitry Andric uint32_t index_id = 0; 4355ffd83dbSDimitry Andric ThreadSP thread_sp = m_process.GetThreadList().FindThreadByID(tid); 4365ffd83dbSDimitry Andric 4375ffd83dbSDimitry Andric if (skip_unreported) { 4385ffd83dbSDimitry Andric if (!thread_sp) 4395ffd83dbSDimitry Andric continue; 4405ffd83dbSDimitry Andric } 4415ffd83dbSDimitry Andric if (thread_sp) 4425ffd83dbSDimitry Andric index_id = thread_sp->GetIndexID(); 4435ffd83dbSDimitry Andric 4445ffd83dbSDimitry Andric if (condense_if_trivial) { 4455ffd83dbSDimitry Andric if (!elem.second.AnyPlans() && !elem.second.AnyCompletedPlans() && 4465ffd83dbSDimitry Andric !elem.second.AnyDiscardedPlans()) { 4475ffd83dbSDimitry Andric strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 "\n", index_id, tid); 4485ffd83dbSDimitry Andric strm.IndentMore(); 4495ffd83dbSDimitry Andric strm.Indent(); 4505ffd83dbSDimitry Andric strm.Printf("No active thread plans\n"); 4515ffd83dbSDimitry Andric strm.IndentLess(); 4525ffd83dbSDimitry Andric return; 4535ffd83dbSDimitry Andric } 4545ffd83dbSDimitry Andric } 4555ffd83dbSDimitry Andric 4565ffd83dbSDimitry Andric strm.Indent(); 4575ffd83dbSDimitry Andric strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 ":\n", index_id, tid); 4585ffd83dbSDimitry Andric 4595ffd83dbSDimitry Andric elem.second.DumpThreadPlans(strm, desc_level, internal); 4605ffd83dbSDimitry Andric } 4615ffd83dbSDimitry Andric } 4625ffd83dbSDimitry Andric 4635ffd83dbSDimitry Andric bool ThreadPlanStackMap::DumpPlansForTID(Stream &strm, lldb::tid_t tid, 4645ffd83dbSDimitry Andric lldb::DescriptionLevel desc_level, 4655ffd83dbSDimitry Andric bool internal, 4665ffd83dbSDimitry Andric bool condense_if_trivial, 4675ffd83dbSDimitry Andric bool skip_unreported) { 4685ffd83dbSDimitry Andric uint32_t index_id = 0; 4695ffd83dbSDimitry Andric ThreadSP thread_sp = m_process.GetThreadList().FindThreadByID(tid); 4705ffd83dbSDimitry Andric 4715ffd83dbSDimitry Andric if (skip_unreported) { 4725ffd83dbSDimitry Andric if (!thread_sp) { 4735ffd83dbSDimitry Andric strm.Format("Unknown TID: {0}", tid); 4745ffd83dbSDimitry Andric return false; 4755ffd83dbSDimitry Andric } 4765ffd83dbSDimitry Andric } 4775ffd83dbSDimitry Andric 4785ffd83dbSDimitry Andric if (thread_sp) 4795ffd83dbSDimitry Andric index_id = thread_sp->GetIndexID(); 4805ffd83dbSDimitry Andric ThreadPlanStack *stack = Find(tid); 4815ffd83dbSDimitry Andric if (!stack) { 4825ffd83dbSDimitry Andric strm.Format("Unknown TID: {0}\n", tid); 4835ffd83dbSDimitry Andric return false; 4845ffd83dbSDimitry Andric } 4855ffd83dbSDimitry Andric 4865ffd83dbSDimitry Andric if (condense_if_trivial) { 4875ffd83dbSDimitry Andric if (!stack->AnyPlans() && !stack->AnyCompletedPlans() && 4885ffd83dbSDimitry Andric !stack->AnyDiscardedPlans()) { 4895ffd83dbSDimitry Andric strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 "\n", index_id, tid); 4905ffd83dbSDimitry Andric strm.IndentMore(); 4915ffd83dbSDimitry Andric strm.Indent(); 4925ffd83dbSDimitry Andric strm.Printf("No active thread plans\n"); 4935ffd83dbSDimitry Andric strm.IndentLess(); 4945ffd83dbSDimitry Andric return true; 4955ffd83dbSDimitry Andric } 4965ffd83dbSDimitry Andric } 4975ffd83dbSDimitry Andric 4985ffd83dbSDimitry Andric strm.Indent(); 4995ffd83dbSDimitry Andric strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 ":\n", index_id, tid); 5005ffd83dbSDimitry Andric 5015ffd83dbSDimitry Andric stack->DumpThreadPlans(strm, desc_level, internal); 5025ffd83dbSDimitry Andric return true; 5035ffd83dbSDimitry Andric } 5045ffd83dbSDimitry Andric 5055ffd83dbSDimitry Andric bool ThreadPlanStackMap::PrunePlansForTID(lldb::tid_t tid) { 5065ffd83dbSDimitry Andric // We only remove the plans for unreported TID's. 5075ffd83dbSDimitry Andric ThreadSP thread_sp = m_process.GetThreadList().FindThreadByID(tid); 5085ffd83dbSDimitry Andric if (thread_sp) 5095ffd83dbSDimitry Andric return false; 5105ffd83dbSDimitry Andric 5115ffd83dbSDimitry Andric return RemoveTID(tid); 5125ffd83dbSDimitry Andric } 513