1*5ffd83dbSDimitry Andric //===-- ThreadPlanStack.cpp -------------------------------------*- C++ -*-===// 2*5ffd83dbSDimitry Andric // 3*5ffd83dbSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*5ffd83dbSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*5ffd83dbSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*5ffd83dbSDimitry Andric // 7*5ffd83dbSDimitry Andric //===----------------------------------------------------------------------===// 8*5ffd83dbSDimitry Andric 9*5ffd83dbSDimitry Andric #include "lldb/Target/ThreadPlanStack.h" 10*5ffd83dbSDimitry Andric #include "lldb/Target/Process.h" 11*5ffd83dbSDimitry Andric #include "lldb/Target/Target.h" 12*5ffd83dbSDimitry Andric #include "lldb/Target/Thread.h" 13*5ffd83dbSDimitry Andric #include "lldb/Target/ThreadPlan.h" 14*5ffd83dbSDimitry Andric #include "lldb/Utility/Log.h" 15*5ffd83dbSDimitry Andric 16*5ffd83dbSDimitry Andric using namespace lldb; 17*5ffd83dbSDimitry Andric using namespace lldb_private; 18*5ffd83dbSDimitry Andric 19*5ffd83dbSDimitry Andric static void PrintPlanElement(Stream &s, const ThreadPlanSP &plan, 20*5ffd83dbSDimitry Andric lldb::DescriptionLevel desc_level, 21*5ffd83dbSDimitry Andric int32_t elem_idx) { 22*5ffd83dbSDimitry Andric s.IndentMore(); 23*5ffd83dbSDimitry Andric s.Indent(); 24*5ffd83dbSDimitry Andric s.Printf("Element %d: ", elem_idx); 25*5ffd83dbSDimitry Andric plan->GetDescription(&s, desc_level); 26*5ffd83dbSDimitry Andric s.EOL(); 27*5ffd83dbSDimitry Andric s.IndentLess(); 28*5ffd83dbSDimitry Andric } 29*5ffd83dbSDimitry Andric 30*5ffd83dbSDimitry Andric ThreadPlanStack::ThreadPlanStack(const Thread &thread, bool make_null) { 31*5ffd83dbSDimitry Andric if (make_null) { 32*5ffd83dbSDimitry Andric // The ThreadPlanNull doesn't do anything to the Thread, so this is actually 33*5ffd83dbSDimitry Andric // still a const operation. 34*5ffd83dbSDimitry Andric m_plans.push_back( 35*5ffd83dbSDimitry Andric ThreadPlanSP(new ThreadPlanNull(const_cast<Thread &>(thread)))); 36*5ffd83dbSDimitry Andric } 37*5ffd83dbSDimitry Andric } 38*5ffd83dbSDimitry Andric 39*5ffd83dbSDimitry Andric void ThreadPlanStack::DumpThreadPlans(Stream &s, 40*5ffd83dbSDimitry Andric lldb::DescriptionLevel desc_level, 41*5ffd83dbSDimitry Andric bool include_internal) const { 42*5ffd83dbSDimitry Andric s.IndentMore(); 43*5ffd83dbSDimitry Andric PrintOneStack(s, "Active plan stack", m_plans, desc_level, include_internal); 44*5ffd83dbSDimitry Andric PrintOneStack(s, "Completed plan stack", m_completed_plans, desc_level, 45*5ffd83dbSDimitry Andric include_internal); 46*5ffd83dbSDimitry Andric PrintOneStack(s, "Discarded plan stack", m_discarded_plans, desc_level, 47*5ffd83dbSDimitry Andric include_internal); 48*5ffd83dbSDimitry Andric s.IndentLess(); 49*5ffd83dbSDimitry Andric } 50*5ffd83dbSDimitry Andric 51*5ffd83dbSDimitry Andric void ThreadPlanStack::PrintOneStack(Stream &s, llvm::StringRef stack_name, 52*5ffd83dbSDimitry Andric const PlanStack &stack, 53*5ffd83dbSDimitry Andric lldb::DescriptionLevel desc_level, 54*5ffd83dbSDimitry Andric bool include_internal) const { 55*5ffd83dbSDimitry Andric // If the stack is empty, just exit: 56*5ffd83dbSDimitry Andric if (stack.empty()) 57*5ffd83dbSDimitry Andric return; 58*5ffd83dbSDimitry Andric 59*5ffd83dbSDimitry Andric // Make sure there are public completed plans: 60*5ffd83dbSDimitry Andric bool any_public = false; 61*5ffd83dbSDimitry Andric if (!include_internal) { 62*5ffd83dbSDimitry Andric for (auto plan : stack) { 63*5ffd83dbSDimitry Andric if (!plan->GetPrivate()) { 64*5ffd83dbSDimitry Andric any_public = true; 65*5ffd83dbSDimitry Andric break; 66*5ffd83dbSDimitry Andric } 67*5ffd83dbSDimitry Andric } 68*5ffd83dbSDimitry Andric } 69*5ffd83dbSDimitry Andric 70*5ffd83dbSDimitry Andric if (include_internal || any_public) { 71*5ffd83dbSDimitry Andric int print_idx = 0; 72*5ffd83dbSDimitry Andric s.Indent(); 73*5ffd83dbSDimitry Andric s << stack_name << ":\n"; 74*5ffd83dbSDimitry Andric for (auto plan : stack) { 75*5ffd83dbSDimitry Andric if (!include_internal && plan->GetPrivate()) 76*5ffd83dbSDimitry Andric continue; 77*5ffd83dbSDimitry Andric PrintPlanElement(s, plan, desc_level, print_idx++); 78*5ffd83dbSDimitry Andric } 79*5ffd83dbSDimitry Andric } 80*5ffd83dbSDimitry Andric } 81*5ffd83dbSDimitry Andric 82*5ffd83dbSDimitry Andric size_t ThreadPlanStack::CheckpointCompletedPlans() { 83*5ffd83dbSDimitry Andric m_completed_plan_checkpoint++; 84*5ffd83dbSDimitry Andric m_completed_plan_store.insert( 85*5ffd83dbSDimitry Andric std::make_pair(m_completed_plan_checkpoint, m_completed_plans)); 86*5ffd83dbSDimitry Andric return m_completed_plan_checkpoint; 87*5ffd83dbSDimitry Andric } 88*5ffd83dbSDimitry Andric 89*5ffd83dbSDimitry Andric void ThreadPlanStack::RestoreCompletedPlanCheckpoint(size_t checkpoint) { 90*5ffd83dbSDimitry Andric auto result = m_completed_plan_store.find(checkpoint); 91*5ffd83dbSDimitry Andric assert(result != m_completed_plan_store.end() && 92*5ffd83dbSDimitry Andric "Asked for a checkpoint that didn't exist"); 93*5ffd83dbSDimitry Andric m_completed_plans.swap((*result).second); 94*5ffd83dbSDimitry Andric m_completed_plan_store.erase(result); 95*5ffd83dbSDimitry Andric } 96*5ffd83dbSDimitry Andric 97*5ffd83dbSDimitry Andric void ThreadPlanStack::DiscardCompletedPlanCheckpoint(size_t checkpoint) { 98*5ffd83dbSDimitry Andric m_completed_plan_store.erase(checkpoint); 99*5ffd83dbSDimitry Andric } 100*5ffd83dbSDimitry Andric 101*5ffd83dbSDimitry Andric void ThreadPlanStack::ThreadDestroyed(Thread *thread) { 102*5ffd83dbSDimitry Andric // Tell the plan stacks that this thread is going away: 103*5ffd83dbSDimitry Andric for (ThreadPlanSP plan : m_plans) 104*5ffd83dbSDimitry Andric plan->ThreadDestroyed(); 105*5ffd83dbSDimitry Andric 106*5ffd83dbSDimitry Andric for (ThreadPlanSP plan : m_discarded_plans) 107*5ffd83dbSDimitry Andric plan->ThreadDestroyed(); 108*5ffd83dbSDimitry Andric 109*5ffd83dbSDimitry Andric for (ThreadPlanSP plan : m_completed_plans) 110*5ffd83dbSDimitry Andric plan->ThreadDestroyed(); 111*5ffd83dbSDimitry Andric 112*5ffd83dbSDimitry Andric // Now clear the current plan stacks: 113*5ffd83dbSDimitry Andric m_plans.clear(); 114*5ffd83dbSDimitry Andric m_discarded_plans.clear(); 115*5ffd83dbSDimitry Andric m_completed_plans.clear(); 116*5ffd83dbSDimitry Andric 117*5ffd83dbSDimitry Andric // Push a ThreadPlanNull on the plan stack. That way we can continue 118*5ffd83dbSDimitry Andric // assuming that the plan stack is never empty, but if somebody errantly asks 119*5ffd83dbSDimitry Andric // questions of a destroyed thread without checking first whether it is 120*5ffd83dbSDimitry Andric // destroyed, they won't crash. 121*5ffd83dbSDimitry Andric if (thread != nullptr) { 122*5ffd83dbSDimitry Andric lldb::ThreadPlanSP null_plan_sp(new ThreadPlanNull(*thread)); 123*5ffd83dbSDimitry Andric m_plans.push_back(null_plan_sp); 124*5ffd83dbSDimitry Andric } 125*5ffd83dbSDimitry Andric } 126*5ffd83dbSDimitry Andric 127*5ffd83dbSDimitry Andric void ThreadPlanStack::EnableTracer(bool value, bool single_stepping) { 128*5ffd83dbSDimitry Andric for (ThreadPlanSP plan : m_plans) { 129*5ffd83dbSDimitry Andric if (plan->GetThreadPlanTracer()) { 130*5ffd83dbSDimitry Andric plan->GetThreadPlanTracer()->EnableTracing(value); 131*5ffd83dbSDimitry Andric plan->GetThreadPlanTracer()->EnableSingleStep(single_stepping); 132*5ffd83dbSDimitry Andric } 133*5ffd83dbSDimitry Andric } 134*5ffd83dbSDimitry Andric } 135*5ffd83dbSDimitry Andric 136*5ffd83dbSDimitry Andric void ThreadPlanStack::SetTracer(lldb::ThreadPlanTracerSP &tracer_sp) { 137*5ffd83dbSDimitry Andric for (ThreadPlanSP plan : m_plans) 138*5ffd83dbSDimitry Andric plan->SetThreadPlanTracer(tracer_sp); 139*5ffd83dbSDimitry Andric } 140*5ffd83dbSDimitry Andric 141*5ffd83dbSDimitry Andric void ThreadPlanStack::PushPlan(lldb::ThreadPlanSP new_plan_sp) { 142*5ffd83dbSDimitry Andric // If the thread plan doesn't already have a tracer, give it its parent's 143*5ffd83dbSDimitry Andric // tracer: 144*5ffd83dbSDimitry Andric // The first plan has to be a base plan: 145*5ffd83dbSDimitry Andric assert((m_plans.size() > 0 || new_plan_sp->IsBasePlan()) && 146*5ffd83dbSDimitry Andric "Zeroth plan must be a base plan"); 147*5ffd83dbSDimitry Andric 148*5ffd83dbSDimitry Andric if (!new_plan_sp->GetThreadPlanTracer()) { 149*5ffd83dbSDimitry Andric assert(!m_plans.empty()); 150*5ffd83dbSDimitry Andric new_plan_sp->SetThreadPlanTracer(m_plans.back()->GetThreadPlanTracer()); 151*5ffd83dbSDimitry Andric } 152*5ffd83dbSDimitry Andric m_plans.push_back(new_plan_sp); 153*5ffd83dbSDimitry Andric new_plan_sp->DidPush(); 154*5ffd83dbSDimitry Andric } 155*5ffd83dbSDimitry Andric 156*5ffd83dbSDimitry Andric lldb::ThreadPlanSP ThreadPlanStack::PopPlan() { 157*5ffd83dbSDimitry Andric assert(m_plans.size() > 1 && "Can't pop the base thread plan"); 158*5ffd83dbSDimitry Andric 159*5ffd83dbSDimitry Andric lldb::ThreadPlanSP plan_sp = std::move(m_plans.back()); 160*5ffd83dbSDimitry Andric m_completed_plans.push_back(plan_sp); 161*5ffd83dbSDimitry Andric plan_sp->WillPop(); 162*5ffd83dbSDimitry Andric m_plans.pop_back(); 163*5ffd83dbSDimitry Andric return plan_sp; 164*5ffd83dbSDimitry Andric } 165*5ffd83dbSDimitry Andric 166*5ffd83dbSDimitry Andric lldb::ThreadPlanSP ThreadPlanStack::DiscardPlan() { 167*5ffd83dbSDimitry Andric assert(m_plans.size() > 1 && "Can't discard the base thread plan"); 168*5ffd83dbSDimitry Andric 169*5ffd83dbSDimitry Andric lldb::ThreadPlanSP plan_sp = std::move(m_plans.back()); 170*5ffd83dbSDimitry Andric m_discarded_plans.push_back(plan_sp); 171*5ffd83dbSDimitry Andric plan_sp->WillPop(); 172*5ffd83dbSDimitry Andric m_plans.pop_back(); 173*5ffd83dbSDimitry Andric return plan_sp; 174*5ffd83dbSDimitry Andric } 175*5ffd83dbSDimitry Andric 176*5ffd83dbSDimitry Andric // If the input plan is nullptr, discard all plans. Otherwise make sure this 177*5ffd83dbSDimitry Andric // plan is in the stack, and if so discard up to and including it. 178*5ffd83dbSDimitry Andric void ThreadPlanStack::DiscardPlansUpToPlan(ThreadPlan *up_to_plan_ptr) { 179*5ffd83dbSDimitry Andric int stack_size = m_plans.size(); 180*5ffd83dbSDimitry Andric 181*5ffd83dbSDimitry Andric if (up_to_plan_ptr == nullptr) { 182*5ffd83dbSDimitry Andric for (int i = stack_size - 1; i > 0; i--) 183*5ffd83dbSDimitry Andric DiscardPlan(); 184*5ffd83dbSDimitry Andric return; 185*5ffd83dbSDimitry Andric } 186*5ffd83dbSDimitry Andric 187*5ffd83dbSDimitry Andric bool found_it = false; 188*5ffd83dbSDimitry Andric for (int i = stack_size - 1; i > 0; i--) { 189*5ffd83dbSDimitry Andric if (m_plans[i].get() == up_to_plan_ptr) { 190*5ffd83dbSDimitry Andric found_it = true; 191*5ffd83dbSDimitry Andric break; 192*5ffd83dbSDimitry Andric } 193*5ffd83dbSDimitry Andric } 194*5ffd83dbSDimitry Andric 195*5ffd83dbSDimitry Andric if (found_it) { 196*5ffd83dbSDimitry Andric bool last_one = false; 197*5ffd83dbSDimitry Andric for (int i = stack_size - 1; i > 0 && !last_one; i--) { 198*5ffd83dbSDimitry Andric if (GetCurrentPlan().get() == up_to_plan_ptr) 199*5ffd83dbSDimitry Andric last_one = true; 200*5ffd83dbSDimitry Andric DiscardPlan(); 201*5ffd83dbSDimitry Andric } 202*5ffd83dbSDimitry Andric } 203*5ffd83dbSDimitry Andric } 204*5ffd83dbSDimitry Andric 205*5ffd83dbSDimitry Andric void ThreadPlanStack::DiscardAllPlans() { 206*5ffd83dbSDimitry Andric int stack_size = m_plans.size(); 207*5ffd83dbSDimitry Andric for (int i = stack_size - 1; i > 0; i--) { 208*5ffd83dbSDimitry Andric DiscardPlan(); 209*5ffd83dbSDimitry Andric } 210*5ffd83dbSDimitry Andric return; 211*5ffd83dbSDimitry Andric } 212*5ffd83dbSDimitry Andric 213*5ffd83dbSDimitry Andric void ThreadPlanStack::DiscardConsultingMasterPlans() { 214*5ffd83dbSDimitry Andric while (true) { 215*5ffd83dbSDimitry Andric int master_plan_idx; 216*5ffd83dbSDimitry Andric bool discard = true; 217*5ffd83dbSDimitry Andric 218*5ffd83dbSDimitry Andric // Find the first master plan, see if it wants discarding, and if yes 219*5ffd83dbSDimitry Andric // discard up to it. 220*5ffd83dbSDimitry Andric for (master_plan_idx = m_plans.size() - 1; master_plan_idx >= 0; 221*5ffd83dbSDimitry Andric master_plan_idx--) { 222*5ffd83dbSDimitry Andric if (m_plans[master_plan_idx]->IsMasterPlan()) { 223*5ffd83dbSDimitry Andric discard = m_plans[master_plan_idx]->OkayToDiscard(); 224*5ffd83dbSDimitry Andric break; 225*5ffd83dbSDimitry Andric } 226*5ffd83dbSDimitry Andric } 227*5ffd83dbSDimitry Andric 228*5ffd83dbSDimitry Andric // If the master plan doesn't want to get discarded, then we're done. 229*5ffd83dbSDimitry Andric if (!discard) 230*5ffd83dbSDimitry Andric return; 231*5ffd83dbSDimitry Andric 232*5ffd83dbSDimitry Andric // First pop all the dependent plans: 233*5ffd83dbSDimitry Andric for (int i = m_plans.size() - 1; i > master_plan_idx; i--) { 234*5ffd83dbSDimitry Andric DiscardPlan(); 235*5ffd83dbSDimitry Andric } 236*5ffd83dbSDimitry Andric 237*5ffd83dbSDimitry Andric // Now discard the master plan itself. 238*5ffd83dbSDimitry Andric // The bottom-most plan never gets discarded. "OkayToDiscard" for it 239*5ffd83dbSDimitry Andric // means discard it's dependent plans, but not it... 240*5ffd83dbSDimitry Andric if (master_plan_idx > 0) { 241*5ffd83dbSDimitry Andric DiscardPlan(); 242*5ffd83dbSDimitry Andric } 243*5ffd83dbSDimitry Andric } 244*5ffd83dbSDimitry Andric } 245*5ffd83dbSDimitry Andric 246*5ffd83dbSDimitry Andric lldb::ThreadPlanSP ThreadPlanStack::GetCurrentPlan() const { 247*5ffd83dbSDimitry Andric assert(m_plans.size() != 0 && "There will always be a base plan."); 248*5ffd83dbSDimitry Andric return m_plans.back(); 249*5ffd83dbSDimitry Andric } 250*5ffd83dbSDimitry Andric 251*5ffd83dbSDimitry Andric lldb::ThreadPlanSP ThreadPlanStack::GetCompletedPlan(bool skip_private) const { 252*5ffd83dbSDimitry Andric if (m_completed_plans.empty()) 253*5ffd83dbSDimitry Andric return {}; 254*5ffd83dbSDimitry Andric 255*5ffd83dbSDimitry Andric if (!skip_private) 256*5ffd83dbSDimitry Andric return m_completed_plans.back(); 257*5ffd83dbSDimitry Andric 258*5ffd83dbSDimitry Andric for (int i = m_completed_plans.size() - 1; i >= 0; i--) { 259*5ffd83dbSDimitry Andric lldb::ThreadPlanSP completed_plan_sp; 260*5ffd83dbSDimitry Andric completed_plan_sp = m_completed_plans[i]; 261*5ffd83dbSDimitry Andric if (!completed_plan_sp->GetPrivate()) 262*5ffd83dbSDimitry Andric return completed_plan_sp; 263*5ffd83dbSDimitry Andric } 264*5ffd83dbSDimitry Andric return {}; 265*5ffd83dbSDimitry Andric } 266*5ffd83dbSDimitry Andric 267*5ffd83dbSDimitry Andric lldb::ThreadPlanSP ThreadPlanStack::GetPlanByIndex(uint32_t plan_idx, 268*5ffd83dbSDimitry Andric bool skip_private) const { 269*5ffd83dbSDimitry Andric uint32_t idx = 0; 270*5ffd83dbSDimitry Andric 271*5ffd83dbSDimitry Andric for (lldb::ThreadPlanSP plan_sp : m_plans) { 272*5ffd83dbSDimitry Andric if (skip_private && plan_sp->GetPrivate()) 273*5ffd83dbSDimitry Andric continue; 274*5ffd83dbSDimitry Andric if (idx == plan_idx) 275*5ffd83dbSDimitry Andric return plan_sp; 276*5ffd83dbSDimitry Andric idx++; 277*5ffd83dbSDimitry Andric } 278*5ffd83dbSDimitry Andric return {}; 279*5ffd83dbSDimitry Andric } 280*5ffd83dbSDimitry Andric 281*5ffd83dbSDimitry Andric lldb::ValueObjectSP ThreadPlanStack::GetReturnValueObject() const { 282*5ffd83dbSDimitry Andric if (m_completed_plans.empty()) 283*5ffd83dbSDimitry Andric return {}; 284*5ffd83dbSDimitry Andric 285*5ffd83dbSDimitry Andric for (int i = m_completed_plans.size() - 1; i >= 0; i--) { 286*5ffd83dbSDimitry Andric lldb::ValueObjectSP return_valobj_sp; 287*5ffd83dbSDimitry Andric return_valobj_sp = m_completed_plans[i]->GetReturnValueObject(); 288*5ffd83dbSDimitry Andric if (return_valobj_sp) 289*5ffd83dbSDimitry Andric return return_valobj_sp; 290*5ffd83dbSDimitry Andric } 291*5ffd83dbSDimitry Andric return {}; 292*5ffd83dbSDimitry Andric } 293*5ffd83dbSDimitry Andric 294*5ffd83dbSDimitry Andric lldb::ExpressionVariableSP ThreadPlanStack::GetExpressionVariable() const { 295*5ffd83dbSDimitry Andric if (m_completed_plans.empty()) 296*5ffd83dbSDimitry Andric return {}; 297*5ffd83dbSDimitry Andric 298*5ffd83dbSDimitry Andric for (int i = m_completed_plans.size() - 1; i >= 0; i--) { 299*5ffd83dbSDimitry Andric lldb::ExpressionVariableSP expression_variable_sp; 300*5ffd83dbSDimitry Andric expression_variable_sp = m_completed_plans[i]->GetExpressionVariable(); 301*5ffd83dbSDimitry Andric if (expression_variable_sp) 302*5ffd83dbSDimitry Andric return expression_variable_sp; 303*5ffd83dbSDimitry Andric } 304*5ffd83dbSDimitry Andric return {}; 305*5ffd83dbSDimitry Andric } 306*5ffd83dbSDimitry Andric bool ThreadPlanStack::AnyPlans() const { 307*5ffd83dbSDimitry Andric // There is always a base plan... 308*5ffd83dbSDimitry Andric return m_plans.size() > 1; 309*5ffd83dbSDimitry Andric } 310*5ffd83dbSDimitry Andric 311*5ffd83dbSDimitry Andric bool ThreadPlanStack::AnyCompletedPlans() const { 312*5ffd83dbSDimitry Andric return !m_completed_plans.empty(); 313*5ffd83dbSDimitry Andric } 314*5ffd83dbSDimitry Andric 315*5ffd83dbSDimitry Andric bool ThreadPlanStack::AnyDiscardedPlans() const { 316*5ffd83dbSDimitry Andric return !m_discarded_plans.empty(); 317*5ffd83dbSDimitry Andric } 318*5ffd83dbSDimitry Andric 319*5ffd83dbSDimitry Andric bool ThreadPlanStack::IsPlanDone(ThreadPlan *in_plan) const { 320*5ffd83dbSDimitry Andric for (auto plan : m_completed_plans) { 321*5ffd83dbSDimitry Andric if (plan.get() == in_plan) 322*5ffd83dbSDimitry Andric return true; 323*5ffd83dbSDimitry Andric } 324*5ffd83dbSDimitry Andric return false; 325*5ffd83dbSDimitry Andric } 326*5ffd83dbSDimitry Andric 327*5ffd83dbSDimitry Andric bool ThreadPlanStack::WasPlanDiscarded(ThreadPlan *in_plan) const { 328*5ffd83dbSDimitry Andric for (auto plan : m_discarded_plans) { 329*5ffd83dbSDimitry Andric if (plan.get() == in_plan) 330*5ffd83dbSDimitry Andric return true; 331*5ffd83dbSDimitry Andric } 332*5ffd83dbSDimitry Andric return false; 333*5ffd83dbSDimitry Andric } 334*5ffd83dbSDimitry Andric 335*5ffd83dbSDimitry Andric ThreadPlan *ThreadPlanStack::GetPreviousPlan(ThreadPlan *current_plan) const { 336*5ffd83dbSDimitry Andric if (current_plan == nullptr) 337*5ffd83dbSDimitry Andric return nullptr; 338*5ffd83dbSDimitry Andric 339*5ffd83dbSDimitry Andric // Look first in the completed plans, if the plan is here and there is 340*5ffd83dbSDimitry Andric // a completed plan above it, return that. 341*5ffd83dbSDimitry Andric int stack_size = m_completed_plans.size(); 342*5ffd83dbSDimitry Andric for (int i = stack_size - 1; i > 0; i--) { 343*5ffd83dbSDimitry Andric if (current_plan == m_completed_plans[i].get()) 344*5ffd83dbSDimitry Andric return m_completed_plans[i - 1].get(); 345*5ffd83dbSDimitry Andric } 346*5ffd83dbSDimitry Andric 347*5ffd83dbSDimitry Andric // If this is the first completed plan, the previous one is the 348*5ffd83dbSDimitry Andric // bottom of the regular plan stack. 349*5ffd83dbSDimitry Andric if (stack_size > 0 && m_completed_plans[0].get() == current_plan) { 350*5ffd83dbSDimitry Andric return GetCurrentPlan().get(); 351*5ffd83dbSDimitry Andric } 352*5ffd83dbSDimitry Andric 353*5ffd83dbSDimitry Andric // Otherwise look for it in the regular plans. 354*5ffd83dbSDimitry Andric stack_size = m_plans.size(); 355*5ffd83dbSDimitry Andric for (int i = stack_size - 1; i > 0; i--) { 356*5ffd83dbSDimitry Andric if (current_plan == m_plans[i].get()) 357*5ffd83dbSDimitry Andric return m_plans[i - 1].get(); 358*5ffd83dbSDimitry Andric } 359*5ffd83dbSDimitry Andric return nullptr; 360*5ffd83dbSDimitry Andric } 361*5ffd83dbSDimitry Andric 362*5ffd83dbSDimitry Andric ThreadPlan *ThreadPlanStack::GetInnermostExpression() const { 363*5ffd83dbSDimitry Andric int stack_size = m_plans.size(); 364*5ffd83dbSDimitry Andric 365*5ffd83dbSDimitry Andric for (int i = stack_size - 1; i > 0; i--) { 366*5ffd83dbSDimitry Andric if (m_plans[i]->GetKind() == ThreadPlan::eKindCallFunction) 367*5ffd83dbSDimitry Andric return m_plans[i].get(); 368*5ffd83dbSDimitry Andric } 369*5ffd83dbSDimitry Andric return nullptr; 370*5ffd83dbSDimitry Andric } 371*5ffd83dbSDimitry Andric 372*5ffd83dbSDimitry Andric void ThreadPlanStack::WillResume() { 373*5ffd83dbSDimitry Andric m_completed_plans.clear(); 374*5ffd83dbSDimitry Andric m_discarded_plans.clear(); 375*5ffd83dbSDimitry Andric } 376*5ffd83dbSDimitry Andric 377*5ffd83dbSDimitry Andric const ThreadPlanStack::PlanStack & 378*5ffd83dbSDimitry Andric ThreadPlanStack::GetStackOfKind(ThreadPlanStack::StackKind kind) const { 379*5ffd83dbSDimitry Andric switch (kind) { 380*5ffd83dbSDimitry Andric case ePlans: 381*5ffd83dbSDimitry Andric return m_plans; 382*5ffd83dbSDimitry Andric case eCompletedPlans: 383*5ffd83dbSDimitry Andric return m_completed_plans; 384*5ffd83dbSDimitry Andric case eDiscardedPlans: 385*5ffd83dbSDimitry Andric return m_discarded_plans; 386*5ffd83dbSDimitry Andric } 387*5ffd83dbSDimitry Andric llvm_unreachable("Invalid StackKind value"); 388*5ffd83dbSDimitry Andric } 389*5ffd83dbSDimitry Andric 390*5ffd83dbSDimitry Andric void ThreadPlanStackMap::Update(ThreadList ¤t_threads, 391*5ffd83dbSDimitry Andric bool delete_missing, 392*5ffd83dbSDimitry Andric bool check_for_new) { 393*5ffd83dbSDimitry Andric 394*5ffd83dbSDimitry Andric // Now find all the new threads and add them to the map: 395*5ffd83dbSDimitry Andric if (check_for_new) { 396*5ffd83dbSDimitry Andric for (auto thread : current_threads.Threads()) { 397*5ffd83dbSDimitry Andric lldb::tid_t cur_tid = thread->GetID(); 398*5ffd83dbSDimitry Andric if (!Find(cur_tid)) { 399*5ffd83dbSDimitry Andric AddThread(*thread.get()); 400*5ffd83dbSDimitry Andric thread->QueueFundamentalPlan(true); 401*5ffd83dbSDimitry Andric } 402*5ffd83dbSDimitry Andric } 403*5ffd83dbSDimitry Andric } 404*5ffd83dbSDimitry Andric 405*5ffd83dbSDimitry Andric // If we aren't reaping missing threads at this point, 406*5ffd83dbSDimitry Andric // we are done. 407*5ffd83dbSDimitry Andric if (!delete_missing) 408*5ffd83dbSDimitry Andric return; 409*5ffd83dbSDimitry Andric // Otherwise scan for absent TID's. 410*5ffd83dbSDimitry Andric std::vector<lldb::tid_t> missing_threads; 411*5ffd83dbSDimitry Andric // If we are going to delete plans from the plan stack, 412*5ffd83dbSDimitry Andric // then scan for absent TID's: 413*5ffd83dbSDimitry Andric for (auto thread_plans : m_plans_list) { 414*5ffd83dbSDimitry Andric lldb::tid_t cur_tid = thread_plans.first; 415*5ffd83dbSDimitry Andric ThreadSP thread_sp = current_threads.FindThreadByID(cur_tid); 416*5ffd83dbSDimitry Andric if (!thread_sp) 417*5ffd83dbSDimitry Andric missing_threads.push_back(cur_tid); 418*5ffd83dbSDimitry Andric } 419*5ffd83dbSDimitry Andric for (lldb::tid_t tid : missing_threads) { 420*5ffd83dbSDimitry Andric RemoveTID(tid); 421*5ffd83dbSDimitry Andric } 422*5ffd83dbSDimitry Andric } 423*5ffd83dbSDimitry Andric 424*5ffd83dbSDimitry Andric void ThreadPlanStackMap::DumpPlans(Stream &strm, 425*5ffd83dbSDimitry Andric lldb::DescriptionLevel desc_level, 426*5ffd83dbSDimitry Andric bool internal, bool condense_if_trivial, 427*5ffd83dbSDimitry Andric bool skip_unreported) { 428*5ffd83dbSDimitry Andric for (auto elem : m_plans_list) { 429*5ffd83dbSDimitry Andric lldb::tid_t tid = elem.first; 430*5ffd83dbSDimitry Andric uint32_t index_id = 0; 431*5ffd83dbSDimitry Andric ThreadSP thread_sp = m_process.GetThreadList().FindThreadByID(tid); 432*5ffd83dbSDimitry Andric 433*5ffd83dbSDimitry Andric if (skip_unreported) { 434*5ffd83dbSDimitry Andric if (!thread_sp) 435*5ffd83dbSDimitry Andric continue; 436*5ffd83dbSDimitry Andric } 437*5ffd83dbSDimitry Andric if (thread_sp) 438*5ffd83dbSDimitry Andric index_id = thread_sp->GetIndexID(); 439*5ffd83dbSDimitry Andric 440*5ffd83dbSDimitry Andric if (condense_if_trivial) { 441*5ffd83dbSDimitry Andric if (!elem.second.AnyPlans() && !elem.second.AnyCompletedPlans() && 442*5ffd83dbSDimitry Andric !elem.second.AnyDiscardedPlans()) { 443*5ffd83dbSDimitry Andric strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 "\n", index_id, tid); 444*5ffd83dbSDimitry Andric strm.IndentMore(); 445*5ffd83dbSDimitry Andric strm.Indent(); 446*5ffd83dbSDimitry Andric strm.Printf("No active thread plans\n"); 447*5ffd83dbSDimitry Andric strm.IndentLess(); 448*5ffd83dbSDimitry Andric return; 449*5ffd83dbSDimitry Andric } 450*5ffd83dbSDimitry Andric } 451*5ffd83dbSDimitry Andric 452*5ffd83dbSDimitry Andric strm.Indent(); 453*5ffd83dbSDimitry Andric strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 ":\n", index_id, tid); 454*5ffd83dbSDimitry Andric 455*5ffd83dbSDimitry Andric elem.second.DumpThreadPlans(strm, desc_level, internal); 456*5ffd83dbSDimitry Andric } 457*5ffd83dbSDimitry Andric } 458*5ffd83dbSDimitry Andric 459*5ffd83dbSDimitry Andric bool ThreadPlanStackMap::DumpPlansForTID(Stream &strm, lldb::tid_t tid, 460*5ffd83dbSDimitry Andric lldb::DescriptionLevel desc_level, 461*5ffd83dbSDimitry Andric bool internal, 462*5ffd83dbSDimitry Andric bool condense_if_trivial, 463*5ffd83dbSDimitry Andric bool skip_unreported) { 464*5ffd83dbSDimitry Andric uint32_t index_id = 0; 465*5ffd83dbSDimitry Andric ThreadSP thread_sp = m_process.GetThreadList().FindThreadByID(tid); 466*5ffd83dbSDimitry Andric 467*5ffd83dbSDimitry Andric if (skip_unreported) { 468*5ffd83dbSDimitry Andric if (!thread_sp) { 469*5ffd83dbSDimitry Andric strm.Format("Unknown TID: {0}", tid); 470*5ffd83dbSDimitry Andric return false; 471*5ffd83dbSDimitry Andric } 472*5ffd83dbSDimitry Andric } 473*5ffd83dbSDimitry Andric 474*5ffd83dbSDimitry Andric if (thread_sp) 475*5ffd83dbSDimitry Andric index_id = thread_sp->GetIndexID(); 476*5ffd83dbSDimitry Andric ThreadPlanStack *stack = Find(tid); 477*5ffd83dbSDimitry Andric if (!stack) { 478*5ffd83dbSDimitry Andric strm.Format("Unknown TID: {0}\n", tid); 479*5ffd83dbSDimitry Andric return false; 480*5ffd83dbSDimitry Andric } 481*5ffd83dbSDimitry Andric 482*5ffd83dbSDimitry Andric if (condense_if_trivial) { 483*5ffd83dbSDimitry Andric if (!stack->AnyPlans() && !stack->AnyCompletedPlans() && 484*5ffd83dbSDimitry Andric !stack->AnyDiscardedPlans()) { 485*5ffd83dbSDimitry Andric strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 "\n", index_id, tid); 486*5ffd83dbSDimitry Andric strm.IndentMore(); 487*5ffd83dbSDimitry Andric strm.Indent(); 488*5ffd83dbSDimitry Andric strm.Printf("No active thread plans\n"); 489*5ffd83dbSDimitry Andric strm.IndentLess(); 490*5ffd83dbSDimitry Andric return true; 491*5ffd83dbSDimitry Andric } 492*5ffd83dbSDimitry Andric } 493*5ffd83dbSDimitry Andric 494*5ffd83dbSDimitry Andric strm.Indent(); 495*5ffd83dbSDimitry Andric strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 ":\n", index_id, tid); 496*5ffd83dbSDimitry Andric 497*5ffd83dbSDimitry Andric stack->DumpThreadPlans(strm, desc_level, internal); 498*5ffd83dbSDimitry Andric return true; 499*5ffd83dbSDimitry Andric } 500*5ffd83dbSDimitry Andric 501*5ffd83dbSDimitry Andric bool ThreadPlanStackMap::PrunePlansForTID(lldb::tid_t tid) { 502*5ffd83dbSDimitry Andric // We only remove the plans for unreported TID's. 503*5ffd83dbSDimitry Andric ThreadSP thread_sp = m_process.GetThreadList().FindThreadByID(tid); 504*5ffd83dbSDimitry Andric if (thread_sp) 505*5ffd83dbSDimitry Andric return false; 506*5ffd83dbSDimitry Andric 507*5ffd83dbSDimitry Andric return RemoveTID(tid); 508*5ffd83dbSDimitry Andric } 509