161e8e688SJim Ingham //===-- ThreadPlanStack.cpp -------------------------------------*- C++ -*-===// 261e8e688SJim Ingham // 361e8e688SJim Ingham // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 461e8e688SJim Ingham // See https://llvm.org/LICENSE.txt for license information. 561e8e688SJim Ingham // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 661e8e688SJim Ingham // 761e8e688SJim Ingham //===----------------------------------------------------------------------===// 861e8e688SJim Ingham 961e8e688SJim Ingham #include "lldb/Target/ThreadPlanStack.h" 1061e8e688SJim Ingham #include "lldb/Target/Process.h" 1161e8e688SJim Ingham #include "lldb/Target/Target.h" 1261e8e688SJim Ingham #include "lldb/Target/Thread.h" 1361e8e688SJim Ingham #include "lldb/Target/ThreadPlan.h" 1461e8e688SJim Ingham #include "lldb/Utility/Log.h" 1561e8e688SJim Ingham 1661e8e688SJim Ingham using namespace lldb; 1761e8e688SJim Ingham using namespace lldb_private; 1861e8e688SJim Ingham 191893065dSJim Ingham static void PrintPlanElement(Stream &s, const ThreadPlanSP &plan, 2061e8e688SJim Ingham lldb::DescriptionLevel desc_level, 2161e8e688SJim Ingham int32_t elem_idx) { 221893065dSJim Ingham s.IndentMore(); 231893065dSJim Ingham s.Indent(); 241893065dSJim Ingham s.Printf("Element %d: ", elem_idx); 251893065dSJim Ingham plan->GetDescription(&s, desc_level); 261893065dSJim Ingham s.EOL(); 271893065dSJim Ingham s.IndentLess(); 2861e8e688SJim Ingham } 2961e8e688SJim Ingham 301893065dSJim Ingham ThreadPlanStack::ThreadPlanStack(const Thread &thread, bool make_null) { 311893065dSJim Ingham if (make_null) { 321893065dSJim Ingham // The ThreadPlanNull doesn't do anything to the Thread, so this is actually 331893065dSJim Ingham // still a const operation. 341893065dSJim Ingham m_plans.push_back( 351893065dSJim Ingham ThreadPlanSP(new ThreadPlanNull(const_cast<Thread &>(thread)))); 361893065dSJim Ingham } 371893065dSJim Ingham } 381893065dSJim Ingham 391893065dSJim Ingham void ThreadPlanStack::DumpThreadPlans(Stream &s, 4061e8e688SJim Ingham lldb::DescriptionLevel desc_level, 4161e8e688SJim Ingham bool include_internal) const { 426eb576dcSJim Ingham std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 431893065dSJim Ingham s.IndentMore(); 441893065dSJim Ingham PrintOneStack(s, "Active plan stack", m_plans, desc_level, include_internal); 451893065dSJim Ingham PrintOneStack(s, "Completed plan stack", m_completed_plans, desc_level, 461893065dSJim Ingham include_internal); 471893065dSJim Ingham PrintOneStack(s, "Discarded plan stack", m_discarded_plans, desc_level, 481893065dSJim Ingham include_internal); 491893065dSJim Ingham s.IndentLess(); 5061e8e688SJim Ingham } 5161e8e688SJim Ingham 521893065dSJim Ingham void ThreadPlanStack::PrintOneStack(Stream &s, llvm::StringRef stack_name, 531893065dSJim Ingham const PlanStack &stack, 541893065dSJim Ingham lldb::DescriptionLevel desc_level, 551893065dSJim Ingham bool include_internal) const { 566eb576dcSJim Ingham std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 571893065dSJim Ingham // If the stack is empty, just exit: 581893065dSJim Ingham if (stack.empty()) 591893065dSJim Ingham return; 601893065dSJim Ingham 611893065dSJim Ingham // Make sure there are public completed plans: 621893065dSJim Ingham bool any_public = false; 631893065dSJim Ingham if (!include_internal) { 641893065dSJim Ingham for (auto plan : stack) { 651893065dSJim Ingham if (!plan->GetPrivate()) { 661893065dSJim Ingham any_public = true; 671893065dSJim Ingham break; 681893065dSJim Ingham } 691893065dSJim Ingham } 7061e8e688SJim Ingham } 7161e8e688SJim Ingham 721893065dSJim Ingham if (include_internal || any_public) { 731893065dSJim Ingham int print_idx = 0; 741893065dSJim Ingham s.Indent(); 753ccd454cSEric Christopher s << stack_name << ":\n"; 761893065dSJim Ingham for (auto plan : stack) { 771893065dSJim Ingham if (!include_internal && plan->GetPrivate()) 781893065dSJim Ingham continue; 7961e8e688SJim Ingham PrintPlanElement(s, plan, desc_level, print_idx++); 8061e8e688SJim Ingham } 811893065dSJim Ingham } 8261e8e688SJim Ingham } 8361e8e688SJim Ingham 8461e8e688SJim Ingham size_t ThreadPlanStack::CheckpointCompletedPlans() { 856eb576dcSJim Ingham std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 8661e8e688SJim Ingham m_completed_plan_checkpoint++; 8761e8e688SJim Ingham m_completed_plan_store.insert( 8861e8e688SJim Ingham std::make_pair(m_completed_plan_checkpoint, m_completed_plans)); 8961e8e688SJim Ingham return m_completed_plan_checkpoint; 9061e8e688SJim Ingham } 9161e8e688SJim Ingham 9261e8e688SJim Ingham void ThreadPlanStack::RestoreCompletedPlanCheckpoint(size_t checkpoint) { 936eb576dcSJim Ingham std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 9461e8e688SJim Ingham auto result = m_completed_plan_store.find(checkpoint); 9561e8e688SJim Ingham assert(result != m_completed_plan_store.end() && 9661e8e688SJim Ingham "Asked for a checkpoint that didn't exist"); 9761e8e688SJim Ingham m_completed_plans.swap((*result).second); 9861e8e688SJim Ingham m_completed_plan_store.erase(result); 9961e8e688SJim Ingham } 10061e8e688SJim Ingham 10161e8e688SJim Ingham void ThreadPlanStack::DiscardCompletedPlanCheckpoint(size_t checkpoint) { 1026eb576dcSJim Ingham std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 10361e8e688SJim Ingham m_completed_plan_store.erase(checkpoint); 10461e8e688SJim Ingham } 10561e8e688SJim Ingham 10661e8e688SJim Ingham void ThreadPlanStack::ThreadDestroyed(Thread *thread) { 10761e8e688SJim Ingham // Tell the plan stacks that this thread is going away: 1086eb576dcSJim Ingham std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 10961e8e688SJim Ingham for (ThreadPlanSP plan : m_plans) 11061e8e688SJim Ingham plan->ThreadDestroyed(); 11161e8e688SJim Ingham 11261e8e688SJim Ingham for (ThreadPlanSP plan : m_discarded_plans) 11361e8e688SJim Ingham plan->ThreadDestroyed(); 11461e8e688SJim Ingham 11561e8e688SJim Ingham for (ThreadPlanSP plan : m_completed_plans) 11661e8e688SJim Ingham plan->ThreadDestroyed(); 11761e8e688SJim Ingham 11861e8e688SJim Ingham // Now clear the current plan stacks: 11961e8e688SJim Ingham m_plans.clear(); 12061e8e688SJim Ingham m_discarded_plans.clear(); 12161e8e688SJim Ingham m_completed_plans.clear(); 12261e8e688SJim Ingham 12361e8e688SJim Ingham // Push a ThreadPlanNull on the plan stack. That way we can continue 12461e8e688SJim Ingham // assuming that the plan stack is never empty, but if somebody errantly asks 12561e8e688SJim Ingham // questions of a destroyed thread without checking first whether it is 12661e8e688SJim Ingham // destroyed, they won't crash. 12761e8e688SJim Ingham if (thread != nullptr) { 12861e8e688SJim Ingham lldb::ThreadPlanSP null_plan_sp(new ThreadPlanNull(*thread)); 12961e8e688SJim Ingham m_plans.push_back(null_plan_sp); 13061e8e688SJim Ingham } 13161e8e688SJim Ingham } 13261e8e688SJim Ingham 13361e8e688SJim Ingham void ThreadPlanStack::PushPlan(lldb::ThreadPlanSP new_plan_sp) { 13461e8e688SJim Ingham // If the thread plan doesn't already have a tracer, give it its parent's 13561e8e688SJim Ingham // tracer: 13661e8e688SJim Ingham // The first plan has to be a base plan: 1376eb576dcSJim Ingham std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 138ff18a6acSPavel Labath assert((m_plans.size() > 0 || new_plan_sp->IsBasePlan()) && 139ff18a6acSPavel Labath "Zeroth plan must be a base plan"); 14061e8e688SJim Ingham 14161e8e688SJim Ingham if (!new_plan_sp->GetThreadPlanTracer()) { 14261e8e688SJim Ingham assert(!m_plans.empty()); 14361e8e688SJim Ingham new_plan_sp->SetThreadPlanTracer(m_plans.back()->GetThreadPlanTracer()); 14461e8e688SJim Ingham } 14561e8e688SJim Ingham m_plans.push_back(new_plan_sp); 14661e8e688SJim Ingham new_plan_sp->DidPush(); 14761e8e688SJim Ingham } 14861e8e688SJim Ingham 14961e8e688SJim Ingham lldb::ThreadPlanSP ThreadPlanStack::PopPlan() { 1506eb576dcSJim Ingham std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 15161e8e688SJim Ingham assert(m_plans.size() > 1 && "Can't pop the base thread plan"); 15261e8e688SJim Ingham 15341d0b20cSDave Lee // Note that moving the top element of the vector would leave it in an 15441d0b20cSDave Lee // undefined state, and break the guarantee that the stack's thread plans are 15541d0b20cSDave Lee // all valid. 15641d0b20cSDave Lee lldb::ThreadPlanSP plan_sp = m_plans.back(); 15761e8e688SJim Ingham m_plans.pop_back(); 15841d0b20cSDave Lee m_completed_plans.push_back(plan_sp); 15941d0b20cSDave Lee plan_sp->DidPop(); 16061e8e688SJim Ingham return plan_sp; 16161e8e688SJim Ingham } 16261e8e688SJim Ingham 16361e8e688SJim Ingham lldb::ThreadPlanSP ThreadPlanStack::DiscardPlan() { 1646eb576dcSJim Ingham std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 16561e8e688SJim Ingham assert(m_plans.size() > 1 && "Can't discard the base thread plan"); 16661e8e688SJim Ingham 16741d0b20cSDave Lee // Note that moving the top element of the vector would leave it in an 16841d0b20cSDave Lee // undefined state, and break the guarantee that the stack's thread plans are 16941d0b20cSDave Lee // all valid. 17041d0b20cSDave Lee lldb::ThreadPlanSP plan_sp = m_plans.back(); 17161e8e688SJim Ingham m_plans.pop_back(); 17241d0b20cSDave Lee m_discarded_plans.push_back(plan_sp); 17341d0b20cSDave Lee plan_sp->DidPop(); 17461e8e688SJim Ingham return plan_sp; 17561e8e688SJim Ingham } 17661e8e688SJim Ingham 17761e8e688SJim Ingham // If the input plan is nullptr, discard all plans. Otherwise make sure this 17861e8e688SJim Ingham // plan is in the stack, and if so discard up to and including it. 17961e8e688SJim Ingham void ThreadPlanStack::DiscardPlansUpToPlan(ThreadPlan *up_to_plan_ptr) { 1806eb576dcSJim Ingham std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 18161e8e688SJim Ingham int stack_size = m_plans.size(); 18261e8e688SJim Ingham 18361e8e688SJim Ingham if (up_to_plan_ptr == nullptr) { 18461e8e688SJim Ingham for (int i = stack_size - 1; i > 0; i--) 18561e8e688SJim Ingham DiscardPlan(); 18661e8e688SJim Ingham return; 18761e8e688SJim Ingham } 18861e8e688SJim Ingham 18961e8e688SJim Ingham bool found_it = false; 19061e8e688SJim Ingham for (int i = stack_size - 1; i > 0; i--) { 19161e8e688SJim Ingham if (m_plans[i].get() == up_to_plan_ptr) { 19261e8e688SJim Ingham found_it = true; 19361e8e688SJim Ingham break; 19461e8e688SJim Ingham } 19561e8e688SJim Ingham } 19661e8e688SJim Ingham 19761e8e688SJim Ingham if (found_it) { 19861e8e688SJim Ingham bool last_one = false; 19961e8e688SJim Ingham for (int i = stack_size - 1; i > 0 && !last_one; i--) { 20061e8e688SJim Ingham if (GetCurrentPlan().get() == up_to_plan_ptr) 20161e8e688SJim Ingham last_one = true; 20261e8e688SJim Ingham DiscardPlan(); 20361e8e688SJim Ingham } 20461e8e688SJim Ingham } 20561e8e688SJim Ingham } 20661e8e688SJim Ingham 20761e8e688SJim Ingham void ThreadPlanStack::DiscardAllPlans() { 2086eb576dcSJim Ingham std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 20961e8e688SJim Ingham int stack_size = m_plans.size(); 21061e8e688SJim Ingham for (int i = stack_size - 1; i > 0; i--) { 21161e8e688SJim Ingham DiscardPlan(); 21261e8e688SJim Ingham } 21361e8e688SJim Ingham return; 21461e8e688SJim Ingham } 21561e8e688SJim Ingham 216*04cbfa95SQuinn Pham void ThreadPlanStack::DiscardConsultingControllingPlans() { 2176eb576dcSJim Ingham std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 21861e8e688SJim Ingham while (true) { 219*04cbfa95SQuinn Pham int controlling_plan_idx; 22061e8e688SJim Ingham bool discard = true; 22161e8e688SJim Ingham 222*04cbfa95SQuinn Pham // Find the first controlling plan, see if it wants discarding, and if yes 22361e8e688SJim Ingham // discard up to it. 224*04cbfa95SQuinn Pham for (controlling_plan_idx = m_plans.size() - 1; controlling_plan_idx >= 0; 225*04cbfa95SQuinn Pham controlling_plan_idx--) { 226*04cbfa95SQuinn Pham if (m_plans[controlling_plan_idx]->IsControllingPlan()) { 227*04cbfa95SQuinn Pham discard = m_plans[controlling_plan_idx]->OkayToDiscard(); 22861e8e688SJim Ingham break; 22961e8e688SJim Ingham } 23061e8e688SJim Ingham } 23161e8e688SJim Ingham 232*04cbfa95SQuinn Pham // If the controlling plan doesn't want to get discarded, then we're done. 23361e8e688SJim Ingham if (!discard) 23461e8e688SJim Ingham return; 23561e8e688SJim Ingham 23661e8e688SJim Ingham // First pop all the dependent plans: 237*04cbfa95SQuinn Pham for (int i = m_plans.size() - 1; i > controlling_plan_idx; i--) { 23861e8e688SJim Ingham DiscardPlan(); 23961e8e688SJim Ingham } 24061e8e688SJim Ingham 241*04cbfa95SQuinn Pham // Now discard the controlling plan itself. 24261e8e688SJim Ingham // The bottom-most plan never gets discarded. "OkayToDiscard" for it 24361e8e688SJim Ingham // means discard it's dependent plans, but not it... 244*04cbfa95SQuinn Pham if (controlling_plan_idx > 0) { 24561e8e688SJim Ingham DiscardPlan(); 24661e8e688SJim Ingham } 24761e8e688SJim Ingham } 24861e8e688SJim Ingham } 24961e8e688SJim Ingham 25061e8e688SJim Ingham lldb::ThreadPlanSP ThreadPlanStack::GetCurrentPlan() const { 2516eb576dcSJim Ingham std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 25261e8e688SJim Ingham assert(m_plans.size() != 0 && "There will always be a base plan."); 25361e8e688SJim Ingham return m_plans.back(); 25461e8e688SJim Ingham } 25561e8e688SJim Ingham 25661e8e688SJim Ingham lldb::ThreadPlanSP ThreadPlanStack::GetCompletedPlan(bool skip_private) const { 2576eb576dcSJim Ingham std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 25861e8e688SJim Ingham if (m_completed_plans.empty()) 25961e8e688SJim Ingham return {}; 26061e8e688SJim Ingham 26161e8e688SJim Ingham if (!skip_private) 26261e8e688SJim Ingham return m_completed_plans.back(); 26361e8e688SJim Ingham 26461e8e688SJim Ingham for (int i = m_completed_plans.size() - 1; i >= 0; i--) { 26561e8e688SJim Ingham lldb::ThreadPlanSP completed_plan_sp; 26661e8e688SJim Ingham completed_plan_sp = m_completed_plans[i]; 26761e8e688SJim Ingham if (!completed_plan_sp->GetPrivate()) 26861e8e688SJim Ingham return completed_plan_sp; 26961e8e688SJim Ingham } 27061e8e688SJim Ingham return {}; 27161e8e688SJim Ingham } 27261e8e688SJim Ingham 27361e8e688SJim Ingham lldb::ThreadPlanSP ThreadPlanStack::GetPlanByIndex(uint32_t plan_idx, 27461e8e688SJim Ingham bool skip_private) const { 2756eb576dcSJim Ingham std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 27661e8e688SJim Ingham uint32_t idx = 0; 27761e8e688SJim Ingham 27861e8e688SJim Ingham for (lldb::ThreadPlanSP plan_sp : m_plans) { 27961e8e688SJim Ingham if (skip_private && plan_sp->GetPrivate()) 28061e8e688SJim Ingham continue; 28161e8e688SJim Ingham if (idx == plan_idx) 28261e8e688SJim Ingham return plan_sp; 28361e8e688SJim Ingham idx++; 28461e8e688SJim Ingham } 28561e8e688SJim Ingham return {}; 28661e8e688SJim Ingham } 28761e8e688SJim Ingham 28861e8e688SJim Ingham lldb::ValueObjectSP ThreadPlanStack::GetReturnValueObject() const { 2896eb576dcSJim Ingham std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 29061e8e688SJim Ingham if (m_completed_plans.empty()) 29161e8e688SJim Ingham return {}; 29261e8e688SJim Ingham 29361e8e688SJim Ingham for (int i = m_completed_plans.size() - 1; i >= 0; i--) { 29461e8e688SJim Ingham lldb::ValueObjectSP return_valobj_sp; 29561e8e688SJim Ingham return_valobj_sp = m_completed_plans[i]->GetReturnValueObject(); 29661e8e688SJim Ingham if (return_valobj_sp) 29761e8e688SJim Ingham return return_valobj_sp; 29861e8e688SJim Ingham } 29961e8e688SJim Ingham return {}; 30061e8e688SJim Ingham } 30161e8e688SJim Ingham 30261e8e688SJim Ingham lldb::ExpressionVariableSP ThreadPlanStack::GetExpressionVariable() const { 3036eb576dcSJim Ingham std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 30461e8e688SJim Ingham if (m_completed_plans.empty()) 30561e8e688SJim Ingham return {}; 30661e8e688SJim Ingham 30761e8e688SJim Ingham for (int i = m_completed_plans.size() - 1; i >= 0; i--) { 30861e8e688SJim Ingham lldb::ExpressionVariableSP expression_variable_sp; 30961e8e688SJim Ingham expression_variable_sp = m_completed_plans[i]->GetExpressionVariable(); 31061e8e688SJim Ingham if (expression_variable_sp) 31161e8e688SJim Ingham return expression_variable_sp; 31261e8e688SJim Ingham } 31361e8e688SJim Ingham return {}; 31461e8e688SJim Ingham } 31561e8e688SJim Ingham bool ThreadPlanStack::AnyPlans() const { 3166eb576dcSJim Ingham std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 31761e8e688SJim Ingham // There is always a base plan... 31861e8e688SJim Ingham return m_plans.size() > 1; 31961e8e688SJim Ingham } 32061e8e688SJim Ingham 32161e8e688SJim Ingham bool ThreadPlanStack::AnyCompletedPlans() const { 3226eb576dcSJim Ingham std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 32361e8e688SJim Ingham return !m_completed_plans.empty(); 32461e8e688SJim Ingham } 32561e8e688SJim Ingham 32661e8e688SJim Ingham bool ThreadPlanStack::AnyDiscardedPlans() const { 3276eb576dcSJim Ingham std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 32861e8e688SJim Ingham return !m_discarded_plans.empty(); 32961e8e688SJim Ingham } 33061e8e688SJim Ingham 33161e8e688SJim Ingham bool ThreadPlanStack::IsPlanDone(ThreadPlan *in_plan) const { 3326eb576dcSJim Ingham std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 33361e8e688SJim Ingham for (auto plan : m_completed_plans) { 33461e8e688SJim Ingham if (plan.get() == in_plan) 33561e8e688SJim Ingham return true; 33661e8e688SJim Ingham } 33761e8e688SJim Ingham return false; 33861e8e688SJim Ingham } 33961e8e688SJim Ingham 34061e8e688SJim Ingham bool ThreadPlanStack::WasPlanDiscarded(ThreadPlan *in_plan) const { 3416eb576dcSJim Ingham std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 34261e8e688SJim Ingham for (auto plan : m_discarded_plans) { 34361e8e688SJim Ingham if (plan.get() == in_plan) 34461e8e688SJim Ingham return true; 34561e8e688SJim Ingham } 34661e8e688SJim Ingham return false; 34761e8e688SJim Ingham } 34861e8e688SJim Ingham 34961e8e688SJim Ingham ThreadPlan *ThreadPlanStack::GetPreviousPlan(ThreadPlan *current_plan) const { 3506eb576dcSJim Ingham std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 35161e8e688SJim Ingham if (current_plan == nullptr) 35261e8e688SJim Ingham return nullptr; 35361e8e688SJim Ingham 35461e8e688SJim Ingham // Look first in the completed plans, if the plan is here and there is 35561e8e688SJim Ingham // a completed plan above it, return that. 35661e8e688SJim Ingham int stack_size = m_completed_plans.size(); 35761e8e688SJim Ingham for (int i = stack_size - 1; i > 0; i--) { 35861e8e688SJim Ingham if (current_plan == m_completed_plans[i].get()) 35961e8e688SJim Ingham return m_completed_plans[i - 1].get(); 36061e8e688SJim Ingham } 36161e8e688SJim Ingham 36261e8e688SJim Ingham // If this is the first completed plan, the previous one is the 36361e8e688SJim Ingham // bottom of the regular plan stack. 36461e8e688SJim Ingham if (stack_size > 0 && m_completed_plans[0].get() == current_plan) { 36561e8e688SJim Ingham return GetCurrentPlan().get(); 36661e8e688SJim Ingham } 36761e8e688SJim Ingham 36861e8e688SJim Ingham // Otherwise look for it in the regular plans. 36961e8e688SJim Ingham stack_size = m_plans.size(); 37061e8e688SJim Ingham for (int i = stack_size - 1; i > 0; i--) { 37161e8e688SJim Ingham if (current_plan == m_plans[i].get()) 37261e8e688SJim Ingham return m_plans[i - 1].get(); 37361e8e688SJim Ingham } 37461e8e688SJim Ingham return nullptr; 37561e8e688SJim Ingham } 37661e8e688SJim Ingham 37761e8e688SJim Ingham ThreadPlan *ThreadPlanStack::GetInnermostExpression() const { 3786eb576dcSJim Ingham std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 37961e8e688SJim Ingham int stack_size = m_plans.size(); 38061e8e688SJim Ingham 38161e8e688SJim Ingham for (int i = stack_size - 1; i > 0; i--) { 38261e8e688SJim Ingham if (m_plans[i]->GetKind() == ThreadPlan::eKindCallFunction) 38361e8e688SJim Ingham return m_plans[i].get(); 38461e8e688SJim Ingham } 38561e8e688SJim Ingham return nullptr; 38661e8e688SJim Ingham } 38761e8e688SJim Ingham 3884bb62448SWalter Erquinigo void ThreadPlanStack::ClearThreadCache() { 3896eb576dcSJim Ingham std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 3904bb62448SWalter Erquinigo for (lldb::ThreadPlanSP thread_plan_sp : m_plans) 3914bb62448SWalter Erquinigo thread_plan_sp->ClearThreadCache(); 3924bb62448SWalter Erquinigo } 3934bb62448SWalter Erquinigo 39461e8e688SJim Ingham void ThreadPlanStack::WillResume() { 3956eb576dcSJim Ingham std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); 39661e8e688SJim Ingham m_completed_plans.clear(); 39761e8e688SJim Ingham m_discarded_plans.clear(); 39861e8e688SJim Ingham } 39961e8e688SJim Ingham 4001893065dSJim Ingham void ThreadPlanStackMap::Update(ThreadList ¤t_threads, 4011893065dSJim Ingham bool delete_missing, 4021893065dSJim Ingham bool check_for_new) { 4031893065dSJim Ingham 4041893065dSJim Ingham // Now find all the new threads and add them to the map: 4051893065dSJim Ingham if (check_for_new) { 4061893065dSJim Ingham for (auto thread : current_threads.Threads()) { 4071893065dSJim Ingham lldb::tid_t cur_tid = thread->GetID(); 4081893065dSJim Ingham if (!Find(cur_tid)) { 4091893065dSJim Ingham AddThread(*thread.get()); 410e7361c8eSDave Lee thread->QueueBasePlan(true); 4111893065dSJim Ingham } 4121893065dSJim Ingham } 4131893065dSJim Ingham } 4141893065dSJim Ingham 4151893065dSJim Ingham // If we aren't reaping missing threads at this point, 4161893065dSJim Ingham // we are done. 4171893065dSJim Ingham if (!delete_missing) 4181893065dSJim Ingham return; 4191893065dSJim Ingham // Otherwise scan for absent TID's. 4201893065dSJim Ingham std::vector<lldb::tid_t> missing_threads; 4211893065dSJim Ingham // If we are going to delete plans from the plan stack, 4221893065dSJim Ingham // then scan for absent TID's: 4236eb576dcSJim Ingham for (auto &thread_plans : m_plans_list) { 4241893065dSJim Ingham lldb::tid_t cur_tid = thread_plans.first; 4251893065dSJim Ingham ThreadSP thread_sp = current_threads.FindThreadByID(cur_tid); 4261893065dSJim Ingham if (!thread_sp) 4271893065dSJim Ingham missing_threads.push_back(cur_tid); 4281893065dSJim Ingham } 4291893065dSJim Ingham for (lldb::tid_t tid : missing_threads) { 4301893065dSJim Ingham RemoveTID(tid); 4311893065dSJim Ingham } 4321893065dSJim Ingham } 4331893065dSJim Ingham 4341893065dSJim Ingham void ThreadPlanStackMap::DumpPlans(Stream &strm, 4351893065dSJim Ingham lldb::DescriptionLevel desc_level, 4361893065dSJim Ingham bool internal, bool condense_if_trivial, 4371893065dSJim Ingham bool skip_unreported) { 4386eb576dcSJim Ingham for (auto &elem : m_plans_list) { 4391893065dSJim Ingham lldb::tid_t tid = elem.first; 4401893065dSJim Ingham uint32_t index_id = 0; 4411893065dSJim Ingham ThreadSP thread_sp = m_process.GetThreadList().FindThreadByID(tid); 4421893065dSJim Ingham 4431893065dSJim Ingham if (skip_unreported) { 4441893065dSJim Ingham if (!thread_sp) 4451893065dSJim Ingham continue; 4461893065dSJim Ingham } 4471893065dSJim Ingham if (thread_sp) 4481893065dSJim Ingham index_id = thread_sp->GetIndexID(); 4491893065dSJim Ingham 4501893065dSJim Ingham if (condense_if_trivial) { 4511893065dSJim Ingham if (!elem.second.AnyPlans() && !elem.second.AnyCompletedPlans() && 4521893065dSJim Ingham !elem.second.AnyDiscardedPlans()) { 4531893065dSJim Ingham strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 "\n", index_id, tid); 4541893065dSJim Ingham strm.IndentMore(); 4551893065dSJim Ingham strm.Indent(); 4561893065dSJim Ingham strm.Printf("No active thread plans\n"); 4571893065dSJim Ingham strm.IndentLess(); 4581893065dSJim Ingham return; 4591893065dSJim Ingham } 4601893065dSJim Ingham } 4611893065dSJim Ingham 4621893065dSJim Ingham strm.Indent(); 4631893065dSJim Ingham strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 ":\n", index_id, tid); 4641893065dSJim Ingham 4651893065dSJim Ingham elem.second.DumpThreadPlans(strm, desc_level, internal); 4661893065dSJim Ingham } 4671893065dSJim Ingham } 4681893065dSJim Ingham 4691893065dSJim Ingham bool ThreadPlanStackMap::DumpPlansForTID(Stream &strm, lldb::tid_t tid, 4701893065dSJim Ingham lldb::DescriptionLevel desc_level, 4711893065dSJim Ingham bool internal, 4721893065dSJim Ingham bool condense_if_trivial, 4731893065dSJim Ingham bool skip_unreported) { 4741893065dSJim Ingham uint32_t index_id = 0; 4751893065dSJim Ingham ThreadSP thread_sp = m_process.GetThreadList().FindThreadByID(tid); 4761893065dSJim Ingham 4771893065dSJim Ingham if (skip_unreported) { 4781893065dSJim Ingham if (!thread_sp) { 4791893065dSJim Ingham strm.Format("Unknown TID: {0}", tid); 4801893065dSJim Ingham return false; 4811893065dSJim Ingham } 4821893065dSJim Ingham } 4831893065dSJim Ingham 4841893065dSJim Ingham if (thread_sp) 4851893065dSJim Ingham index_id = thread_sp->GetIndexID(); 4861893065dSJim Ingham ThreadPlanStack *stack = Find(tid); 4871893065dSJim Ingham if (!stack) { 4881893065dSJim Ingham strm.Format("Unknown TID: {0}\n", tid); 4891893065dSJim Ingham return false; 4901893065dSJim Ingham } 4911893065dSJim Ingham 4921893065dSJim Ingham if (condense_if_trivial) { 4931893065dSJim Ingham if (!stack->AnyPlans() && !stack->AnyCompletedPlans() && 4941893065dSJim Ingham !stack->AnyDiscardedPlans()) { 4951893065dSJim Ingham strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 "\n", index_id, tid); 4961893065dSJim Ingham strm.IndentMore(); 4971893065dSJim Ingham strm.Indent(); 4981893065dSJim Ingham strm.Printf("No active thread plans\n"); 4991893065dSJim Ingham strm.IndentLess(); 5001893065dSJim Ingham return true; 5011893065dSJim Ingham } 5021893065dSJim Ingham } 5031893065dSJim Ingham 5041893065dSJim Ingham strm.Indent(); 5051893065dSJim Ingham strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 ":\n", index_id, tid); 5061893065dSJim Ingham 5071893065dSJim Ingham stack->DumpThreadPlans(strm, desc_level, internal); 5081893065dSJim Ingham return true; 5091893065dSJim Ingham } 5101893065dSJim Ingham 5111893065dSJim Ingham bool ThreadPlanStackMap::PrunePlansForTID(lldb::tid_t tid) { 5121893065dSJim Ingham // We only remove the plans for unreported TID's. 5131893065dSJim Ingham ThreadSP thread_sp = m_process.GetThreadList().FindThreadByID(tid); 5141893065dSJim Ingham if (thread_sp) 5151893065dSJim Ingham return false; 5161893065dSJim Ingham 5171893065dSJim Ingham return RemoveTID(tid); 5181893065dSJim Ingham } 519