diff options
Diffstat (limited to 'lldb/source/Target/ThreadPlanStack.cpp')
-rw-r--r-- | lldb/source/Target/ThreadPlanStack.cpp | 508 |
1 files changed, 508 insertions, 0 deletions
diff --git a/lldb/source/Target/ThreadPlanStack.cpp b/lldb/source/Target/ThreadPlanStack.cpp new file mode 100644 index 000000000000..1cfc41dcd390 --- /dev/null +++ b/lldb/source/Target/ThreadPlanStack.cpp @@ -0,0 +1,508 @@ +//===-- ThreadPlanStack.cpp -------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Target/ThreadPlanStack.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlan.h" +#include "lldb/Utility/Log.h" + +using namespace lldb; +using namespace lldb_private; + +static void PrintPlanElement(Stream &s, const ThreadPlanSP &plan, + lldb::DescriptionLevel desc_level, + int32_t elem_idx) { + s.IndentMore(); + s.Indent(); + s.Printf("Element %d: ", elem_idx); + plan->GetDescription(&s, desc_level); + s.EOL(); + s.IndentLess(); +} + +ThreadPlanStack::ThreadPlanStack(const Thread &thread, bool make_null) { + if (make_null) { + // The ThreadPlanNull doesn't do anything to the Thread, so this is actually + // still a const operation. + m_plans.push_back( + ThreadPlanSP(new ThreadPlanNull(const_cast<Thread &>(thread)))); + } +} + +void ThreadPlanStack::DumpThreadPlans(Stream &s, + lldb::DescriptionLevel desc_level, + bool include_internal) const { + s.IndentMore(); + PrintOneStack(s, "Active plan stack", m_plans, desc_level, include_internal); + PrintOneStack(s, "Completed plan stack", m_completed_plans, desc_level, + include_internal); + PrintOneStack(s, "Discarded plan stack", m_discarded_plans, desc_level, + include_internal); + s.IndentLess(); +} + +void ThreadPlanStack::PrintOneStack(Stream &s, llvm::StringRef stack_name, + const PlanStack &stack, + lldb::DescriptionLevel desc_level, + bool include_internal) const { + // If the stack is empty, just exit: + if (stack.empty()) + return; + + // Make sure there are public completed plans: + bool any_public = false; + if (!include_internal) { + for (auto plan : stack) { + if (!plan->GetPrivate()) { + any_public = true; + break; + } + } + } + + if (include_internal || any_public) { + int print_idx = 0; + s.Indent(); + s << stack_name << ":\n"; + for (auto plan : stack) { + if (!include_internal && plan->GetPrivate()) + continue; + PrintPlanElement(s, plan, desc_level, print_idx++); + } + } +} + +size_t ThreadPlanStack::CheckpointCompletedPlans() { + m_completed_plan_checkpoint++; + m_completed_plan_store.insert( + std::make_pair(m_completed_plan_checkpoint, m_completed_plans)); + return m_completed_plan_checkpoint; +} + +void ThreadPlanStack::RestoreCompletedPlanCheckpoint(size_t checkpoint) { + auto result = m_completed_plan_store.find(checkpoint); + assert(result != m_completed_plan_store.end() && + "Asked for a checkpoint that didn't exist"); + m_completed_plans.swap((*result).second); + m_completed_plan_store.erase(result); +} + +void ThreadPlanStack::DiscardCompletedPlanCheckpoint(size_t checkpoint) { + m_completed_plan_store.erase(checkpoint); +} + +void ThreadPlanStack::ThreadDestroyed(Thread *thread) { + // Tell the plan stacks that this thread is going away: + for (ThreadPlanSP plan : m_plans) + plan->ThreadDestroyed(); + + for (ThreadPlanSP plan : m_discarded_plans) + plan->ThreadDestroyed(); + + for (ThreadPlanSP plan : m_completed_plans) + plan->ThreadDestroyed(); + + // Now clear the current plan stacks: + m_plans.clear(); + m_discarded_plans.clear(); + m_completed_plans.clear(); + + // Push a ThreadPlanNull on the plan stack. That way we can continue + // assuming that the plan stack is never empty, but if somebody errantly asks + // questions of a destroyed thread without checking first whether it is + // destroyed, they won't crash. + if (thread != nullptr) { + lldb::ThreadPlanSP null_plan_sp(new ThreadPlanNull(*thread)); + m_plans.push_back(null_plan_sp); + } +} + +void ThreadPlanStack::EnableTracer(bool value, bool single_stepping) { + for (ThreadPlanSP plan : m_plans) { + if (plan->GetThreadPlanTracer()) { + plan->GetThreadPlanTracer()->EnableTracing(value); + plan->GetThreadPlanTracer()->EnableSingleStep(single_stepping); + } + } +} + +void ThreadPlanStack::SetTracer(lldb::ThreadPlanTracerSP &tracer_sp) { + for (ThreadPlanSP plan : m_plans) + plan->SetThreadPlanTracer(tracer_sp); +} + +void ThreadPlanStack::PushPlan(lldb::ThreadPlanSP new_plan_sp) { + // If the thread plan doesn't already have a tracer, give it its parent's + // tracer: + // The first plan has to be a base plan: + assert((m_plans.size() > 0 || new_plan_sp->IsBasePlan()) && + "Zeroth plan must be a base plan"); + + if (!new_plan_sp->GetThreadPlanTracer()) { + assert(!m_plans.empty()); + new_plan_sp->SetThreadPlanTracer(m_plans.back()->GetThreadPlanTracer()); + } + m_plans.push_back(new_plan_sp); + new_plan_sp->DidPush(); +} + +lldb::ThreadPlanSP ThreadPlanStack::PopPlan() { + assert(m_plans.size() > 1 && "Can't pop the base thread plan"); + + lldb::ThreadPlanSP plan_sp = std::move(m_plans.back()); + m_completed_plans.push_back(plan_sp); + plan_sp->WillPop(); + m_plans.pop_back(); + return plan_sp; +} + +lldb::ThreadPlanSP ThreadPlanStack::DiscardPlan() { + assert(m_plans.size() > 1 && "Can't discard the base thread plan"); + + lldb::ThreadPlanSP plan_sp = std::move(m_plans.back()); + m_discarded_plans.push_back(plan_sp); + plan_sp->WillPop(); + m_plans.pop_back(); + return plan_sp; +} + +// If the input plan is nullptr, discard all plans. Otherwise make sure this +// plan is in the stack, and if so discard up to and including it. +void ThreadPlanStack::DiscardPlansUpToPlan(ThreadPlan *up_to_plan_ptr) { + int stack_size = m_plans.size(); + + if (up_to_plan_ptr == nullptr) { + for (int i = stack_size - 1; i > 0; i--) + DiscardPlan(); + return; + } + + bool found_it = false; + for (int i = stack_size - 1; i > 0; i--) { + if (m_plans[i].get() == up_to_plan_ptr) { + found_it = true; + break; + } + } + + if (found_it) { + bool last_one = false; + for (int i = stack_size - 1; i > 0 && !last_one; i--) { + if (GetCurrentPlan().get() == up_to_plan_ptr) + last_one = true; + DiscardPlan(); + } + } +} + +void ThreadPlanStack::DiscardAllPlans() { + int stack_size = m_plans.size(); + for (int i = stack_size - 1; i > 0; i--) { + DiscardPlan(); + } + return; +} + +void ThreadPlanStack::DiscardConsultingMasterPlans() { + while (true) { + int master_plan_idx; + bool discard = true; + + // Find the first master plan, see if it wants discarding, and if yes + // discard up to it. + for (master_plan_idx = m_plans.size() - 1; master_plan_idx >= 0; + master_plan_idx--) { + if (m_plans[master_plan_idx]->IsMasterPlan()) { + discard = m_plans[master_plan_idx]->OkayToDiscard(); + break; + } + } + + // If the master plan doesn't want to get discarded, then we're done. + if (!discard) + return; + + // First pop all the dependent plans: + for (int i = m_plans.size() - 1; i > master_plan_idx; i--) { + DiscardPlan(); + } + + // Now discard the master plan itself. + // The bottom-most plan never gets discarded. "OkayToDiscard" for it + // means discard it's dependent plans, but not it... + if (master_plan_idx > 0) { + DiscardPlan(); + } + } +} + +lldb::ThreadPlanSP ThreadPlanStack::GetCurrentPlan() const { + assert(m_plans.size() != 0 && "There will always be a base plan."); + return m_plans.back(); +} + +lldb::ThreadPlanSP ThreadPlanStack::GetCompletedPlan(bool skip_private) const { + if (m_completed_plans.empty()) + return {}; + + if (!skip_private) + return m_completed_plans.back(); + + for (int i = m_completed_plans.size() - 1; i >= 0; i--) { + lldb::ThreadPlanSP completed_plan_sp; + completed_plan_sp = m_completed_plans[i]; + if (!completed_plan_sp->GetPrivate()) + return completed_plan_sp; + } + return {}; +} + +lldb::ThreadPlanSP ThreadPlanStack::GetPlanByIndex(uint32_t plan_idx, + bool skip_private) const { + uint32_t idx = 0; + + for (lldb::ThreadPlanSP plan_sp : m_plans) { + if (skip_private && plan_sp->GetPrivate()) + continue; + if (idx == plan_idx) + return plan_sp; + idx++; + } + return {}; +} + +lldb::ValueObjectSP ThreadPlanStack::GetReturnValueObject() const { + if (m_completed_plans.empty()) + return {}; + + for (int i = m_completed_plans.size() - 1; i >= 0; i--) { + lldb::ValueObjectSP return_valobj_sp; + return_valobj_sp = m_completed_plans[i]->GetReturnValueObject(); + if (return_valobj_sp) + return return_valobj_sp; + } + return {}; +} + +lldb::ExpressionVariableSP ThreadPlanStack::GetExpressionVariable() const { + if (m_completed_plans.empty()) + return {}; + + for (int i = m_completed_plans.size() - 1; i >= 0; i--) { + lldb::ExpressionVariableSP expression_variable_sp; + expression_variable_sp = m_completed_plans[i]->GetExpressionVariable(); + if (expression_variable_sp) + return expression_variable_sp; + } + return {}; +} +bool ThreadPlanStack::AnyPlans() const { + // There is always a base plan... + return m_plans.size() > 1; +} + +bool ThreadPlanStack::AnyCompletedPlans() const { + return !m_completed_plans.empty(); +} + +bool ThreadPlanStack::AnyDiscardedPlans() const { + return !m_discarded_plans.empty(); +} + +bool ThreadPlanStack::IsPlanDone(ThreadPlan *in_plan) const { + for (auto plan : m_completed_plans) { + if (plan.get() == in_plan) + return true; + } + return false; +} + +bool ThreadPlanStack::WasPlanDiscarded(ThreadPlan *in_plan) const { + for (auto plan : m_discarded_plans) { + if (plan.get() == in_plan) + return true; + } + return false; +} + +ThreadPlan *ThreadPlanStack::GetPreviousPlan(ThreadPlan *current_plan) const { + if (current_plan == nullptr) + return nullptr; + + // Look first in the completed plans, if the plan is here and there is + // a completed plan above it, return that. + int stack_size = m_completed_plans.size(); + for (int i = stack_size - 1; i > 0; i--) { + if (current_plan == m_completed_plans[i].get()) + return m_completed_plans[i - 1].get(); + } + + // If this is the first completed plan, the previous one is the + // bottom of the regular plan stack. + if (stack_size > 0 && m_completed_plans[0].get() == current_plan) { + return GetCurrentPlan().get(); + } + + // Otherwise look for it in the regular plans. + stack_size = m_plans.size(); + for (int i = stack_size - 1; i > 0; i--) { + if (current_plan == m_plans[i].get()) + return m_plans[i - 1].get(); + } + return nullptr; +} + +ThreadPlan *ThreadPlanStack::GetInnermostExpression() const { + int stack_size = m_plans.size(); + + for (int i = stack_size - 1; i > 0; i--) { + if (m_plans[i]->GetKind() == ThreadPlan::eKindCallFunction) + return m_plans[i].get(); + } + return nullptr; +} + +void ThreadPlanStack::WillResume() { + m_completed_plans.clear(); + m_discarded_plans.clear(); +} + +const ThreadPlanStack::PlanStack & +ThreadPlanStack::GetStackOfKind(ThreadPlanStack::StackKind kind) const { + switch (kind) { + case ePlans: + return m_plans; + case eCompletedPlans: + return m_completed_plans; + case eDiscardedPlans: + return m_discarded_plans; + } + llvm_unreachable("Invalid StackKind value"); +} + +void ThreadPlanStackMap::Update(ThreadList ¤t_threads, + bool delete_missing, + bool check_for_new) { + + // Now find all the new threads and add them to the map: + if (check_for_new) { + for (auto thread : current_threads.Threads()) { + lldb::tid_t cur_tid = thread->GetID(); + if (!Find(cur_tid)) { + AddThread(*thread.get()); + thread->QueueFundamentalPlan(true); + } + } + } + + // If we aren't reaping missing threads at this point, + // we are done. + if (!delete_missing) + return; + // Otherwise scan for absent TID's. + std::vector<lldb::tid_t> missing_threads; + // If we are going to delete plans from the plan stack, + // then scan for absent TID's: + for (auto thread_plans : m_plans_list) { + lldb::tid_t cur_tid = thread_plans.first; + ThreadSP thread_sp = current_threads.FindThreadByID(cur_tid); + if (!thread_sp) + missing_threads.push_back(cur_tid); + } + for (lldb::tid_t tid : missing_threads) { + RemoveTID(tid); + } +} + +void ThreadPlanStackMap::DumpPlans(Stream &strm, + lldb::DescriptionLevel desc_level, + bool internal, bool condense_if_trivial, + bool skip_unreported) { + for (auto elem : m_plans_list) { + lldb::tid_t tid = elem.first; + uint32_t index_id = 0; + ThreadSP thread_sp = m_process.GetThreadList().FindThreadByID(tid); + + if (skip_unreported) { + if (!thread_sp) + continue; + } + if (thread_sp) + index_id = thread_sp->GetIndexID(); + + if (condense_if_trivial) { + if (!elem.second.AnyPlans() && !elem.second.AnyCompletedPlans() && + !elem.second.AnyDiscardedPlans()) { + strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 "\n", index_id, tid); + strm.IndentMore(); + strm.Indent(); + strm.Printf("No active thread plans\n"); + strm.IndentLess(); + return; + } + } + + strm.Indent(); + strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 ":\n", index_id, tid); + + elem.second.DumpThreadPlans(strm, desc_level, internal); + } +} + +bool ThreadPlanStackMap::DumpPlansForTID(Stream &strm, lldb::tid_t tid, + lldb::DescriptionLevel desc_level, + bool internal, + bool condense_if_trivial, + bool skip_unreported) { + uint32_t index_id = 0; + ThreadSP thread_sp = m_process.GetThreadList().FindThreadByID(tid); + + if (skip_unreported) { + if (!thread_sp) { + strm.Format("Unknown TID: {0}", tid); + return false; + } + } + + if (thread_sp) + index_id = thread_sp->GetIndexID(); + ThreadPlanStack *stack = Find(tid); + if (!stack) { + strm.Format("Unknown TID: {0}\n", tid); + return false; + } + + if (condense_if_trivial) { + if (!stack->AnyPlans() && !stack->AnyCompletedPlans() && + !stack->AnyDiscardedPlans()) { + strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 "\n", index_id, tid); + strm.IndentMore(); + strm.Indent(); + strm.Printf("No active thread plans\n"); + strm.IndentLess(); + return true; + } + } + + strm.Indent(); + strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 ":\n", index_id, tid); + + stack->DumpThreadPlans(strm, desc_level, internal); + return true; +} + +bool ThreadPlanStackMap::PrunePlansForTID(lldb::tid_t tid) { + // We only remove the plans for unreported TID's. + ThreadSP thread_sp = m_process.GetThreadList().FindThreadByID(tid); + if (thread_sp) + return false; + + return RemoveTID(tid); +} |