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
PrintPlanElement(Stream & s,const ThreadPlanSP & plan,lldb::DescriptionLevel desc_level,int32_t elem_idx)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
ThreadPlanStack(const Thread & thread,bool make_null)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
DumpThreadPlans(Stream & s,lldb::DescriptionLevel desc_level,bool include_internal) const391893065dSJim 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
PrintOneStack(Stream & s,llvm::StringRef stack_name,const PlanStack & stack,lldb::DescriptionLevel desc_level,bool include_internal) const521893065dSJim 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
CheckpointCompletedPlans()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
RestoreCompletedPlanCheckpoint(size_t checkpoint)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
DiscardCompletedPlanCheckpoint(size_t checkpoint)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
ThreadDestroyed(Thread * thread)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
PushPlan(lldb::ThreadPlanSP new_plan_sp)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
PopPlan()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
DiscardPlan()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.
DiscardPlansUpToPlan(ThreadPlan * up_to_plan_ptr)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
DiscardAllPlans()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 }
21461e8e688SJim Ingham
DiscardConsultingControllingPlans()21504cbfa95SQuinn Pham void ThreadPlanStack::DiscardConsultingControllingPlans() {
2166eb576dcSJim Ingham std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
21761e8e688SJim Ingham while (true) {
21804cbfa95SQuinn Pham int controlling_plan_idx;
21961e8e688SJim Ingham bool discard = true;
22061e8e688SJim Ingham
22104cbfa95SQuinn Pham // Find the first controlling plan, see if it wants discarding, and if yes
22261e8e688SJim Ingham // discard up to it.
22304cbfa95SQuinn Pham for (controlling_plan_idx = m_plans.size() - 1; controlling_plan_idx >= 0;
22404cbfa95SQuinn Pham controlling_plan_idx--) {
22504cbfa95SQuinn Pham if (m_plans[controlling_plan_idx]->IsControllingPlan()) {
22604cbfa95SQuinn Pham discard = m_plans[controlling_plan_idx]->OkayToDiscard();
22761e8e688SJim Ingham break;
22861e8e688SJim Ingham }
22961e8e688SJim Ingham }
23061e8e688SJim Ingham
23104cbfa95SQuinn Pham // If the controlling plan doesn't want to get discarded, then we're done.
23261e8e688SJim Ingham if (!discard)
23361e8e688SJim Ingham return;
23461e8e688SJim Ingham
23561e8e688SJim Ingham // First pop all the dependent plans:
23604cbfa95SQuinn Pham for (int i = m_plans.size() - 1; i > controlling_plan_idx; i--) {
23761e8e688SJim Ingham DiscardPlan();
23861e8e688SJim Ingham }
23961e8e688SJim Ingham
24004cbfa95SQuinn Pham // Now discard the controlling plan itself.
24161e8e688SJim Ingham // The bottom-most plan never gets discarded. "OkayToDiscard" for it
24261e8e688SJim Ingham // means discard it's dependent plans, but not it...
24304cbfa95SQuinn Pham if (controlling_plan_idx > 0) {
24461e8e688SJim Ingham DiscardPlan();
24561e8e688SJim Ingham }
24661e8e688SJim Ingham }
24761e8e688SJim Ingham }
24861e8e688SJim Ingham
GetCurrentPlan() const24961e8e688SJim Ingham lldb::ThreadPlanSP ThreadPlanStack::GetCurrentPlan() const {
2506eb576dcSJim Ingham std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
25161e8e688SJim Ingham assert(m_plans.size() != 0 && "There will always be a base plan.");
25261e8e688SJim Ingham return m_plans.back();
25361e8e688SJim Ingham }
25461e8e688SJim Ingham
GetCompletedPlan(bool skip_private) const25561e8e688SJim Ingham lldb::ThreadPlanSP ThreadPlanStack::GetCompletedPlan(bool skip_private) const {
2566eb576dcSJim Ingham std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
25761e8e688SJim Ingham if (m_completed_plans.empty())
25861e8e688SJim Ingham return {};
25961e8e688SJim Ingham
26061e8e688SJim Ingham if (!skip_private)
26161e8e688SJim Ingham return m_completed_plans.back();
26261e8e688SJim Ingham
26361e8e688SJim Ingham for (int i = m_completed_plans.size() - 1; i >= 0; i--) {
26461e8e688SJim Ingham lldb::ThreadPlanSP completed_plan_sp;
26561e8e688SJim Ingham completed_plan_sp = m_completed_plans[i];
26661e8e688SJim Ingham if (!completed_plan_sp->GetPrivate())
26761e8e688SJim Ingham return completed_plan_sp;
26861e8e688SJim Ingham }
26961e8e688SJim Ingham return {};
27061e8e688SJim Ingham }
27161e8e688SJim Ingham
GetPlanByIndex(uint32_t plan_idx,bool skip_private) const27261e8e688SJim Ingham lldb::ThreadPlanSP ThreadPlanStack::GetPlanByIndex(uint32_t plan_idx,
27361e8e688SJim Ingham bool skip_private) const {
2746eb576dcSJim Ingham std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
27561e8e688SJim Ingham uint32_t idx = 0;
27661e8e688SJim Ingham
27761e8e688SJim Ingham for (lldb::ThreadPlanSP plan_sp : m_plans) {
27861e8e688SJim Ingham if (skip_private && plan_sp->GetPrivate())
27961e8e688SJim Ingham continue;
28061e8e688SJim Ingham if (idx == plan_idx)
28161e8e688SJim Ingham return plan_sp;
28261e8e688SJim Ingham idx++;
28361e8e688SJim Ingham }
28461e8e688SJim Ingham return {};
28561e8e688SJim Ingham }
28661e8e688SJim Ingham
GetReturnValueObject() const28761e8e688SJim Ingham lldb::ValueObjectSP ThreadPlanStack::GetReturnValueObject() const {
2886eb576dcSJim Ingham std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
28961e8e688SJim Ingham if (m_completed_plans.empty())
29061e8e688SJim Ingham return {};
29161e8e688SJim Ingham
29261e8e688SJim Ingham for (int i = m_completed_plans.size() - 1; i >= 0; i--) {
29361e8e688SJim Ingham lldb::ValueObjectSP return_valobj_sp;
29461e8e688SJim Ingham return_valobj_sp = m_completed_plans[i]->GetReturnValueObject();
29561e8e688SJim Ingham if (return_valobj_sp)
29661e8e688SJim Ingham return return_valobj_sp;
29761e8e688SJim Ingham }
29861e8e688SJim Ingham return {};
29961e8e688SJim Ingham }
30061e8e688SJim Ingham
GetExpressionVariable() const30161e8e688SJim Ingham lldb::ExpressionVariableSP ThreadPlanStack::GetExpressionVariable() const {
3026eb576dcSJim Ingham std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
30361e8e688SJim Ingham if (m_completed_plans.empty())
30461e8e688SJim Ingham return {};
30561e8e688SJim Ingham
30661e8e688SJim Ingham for (int i = m_completed_plans.size() - 1; i >= 0; i--) {
30761e8e688SJim Ingham lldb::ExpressionVariableSP expression_variable_sp;
30861e8e688SJim Ingham expression_variable_sp = m_completed_plans[i]->GetExpressionVariable();
30961e8e688SJim Ingham if (expression_variable_sp)
31061e8e688SJim Ingham return expression_variable_sp;
31161e8e688SJim Ingham }
31261e8e688SJim Ingham return {};
31361e8e688SJim Ingham }
AnyPlans() const31461e8e688SJim Ingham bool ThreadPlanStack::AnyPlans() const {
3156eb576dcSJim Ingham std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
31661e8e688SJim Ingham // There is always a base plan...
31761e8e688SJim Ingham return m_plans.size() > 1;
31861e8e688SJim Ingham }
31961e8e688SJim Ingham
AnyCompletedPlans() const32061e8e688SJim Ingham bool ThreadPlanStack::AnyCompletedPlans() const {
3216eb576dcSJim Ingham std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
32261e8e688SJim Ingham return !m_completed_plans.empty();
32361e8e688SJim Ingham }
32461e8e688SJim Ingham
AnyDiscardedPlans() const32561e8e688SJim Ingham bool ThreadPlanStack::AnyDiscardedPlans() const {
3266eb576dcSJim Ingham std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
32761e8e688SJim Ingham return !m_discarded_plans.empty();
32861e8e688SJim Ingham }
32961e8e688SJim Ingham
IsPlanDone(ThreadPlan * in_plan) const33061e8e688SJim Ingham bool ThreadPlanStack::IsPlanDone(ThreadPlan *in_plan) const {
3316eb576dcSJim Ingham std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
33261e8e688SJim Ingham for (auto plan : m_completed_plans) {
33361e8e688SJim Ingham if (plan.get() == in_plan)
33461e8e688SJim Ingham return true;
33561e8e688SJim Ingham }
33661e8e688SJim Ingham return false;
33761e8e688SJim Ingham }
33861e8e688SJim Ingham
WasPlanDiscarded(ThreadPlan * in_plan) const33961e8e688SJim Ingham bool ThreadPlanStack::WasPlanDiscarded(ThreadPlan *in_plan) const {
3406eb576dcSJim Ingham std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
34161e8e688SJim Ingham for (auto plan : m_discarded_plans) {
34261e8e688SJim Ingham if (plan.get() == in_plan)
34361e8e688SJim Ingham return true;
34461e8e688SJim Ingham }
34561e8e688SJim Ingham return false;
34661e8e688SJim Ingham }
34761e8e688SJim Ingham
GetPreviousPlan(ThreadPlan * current_plan) const34861e8e688SJim Ingham ThreadPlan *ThreadPlanStack::GetPreviousPlan(ThreadPlan *current_plan) const {
3496eb576dcSJim Ingham std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
35061e8e688SJim Ingham if (current_plan == nullptr)
35161e8e688SJim Ingham return nullptr;
35261e8e688SJim Ingham
35361e8e688SJim Ingham // Look first in the completed plans, if the plan is here and there is
35461e8e688SJim Ingham // a completed plan above it, return that.
35561e8e688SJim Ingham int stack_size = m_completed_plans.size();
35661e8e688SJim Ingham for (int i = stack_size - 1; i > 0; i--) {
35761e8e688SJim Ingham if (current_plan == m_completed_plans[i].get())
35861e8e688SJim Ingham return m_completed_plans[i - 1].get();
35961e8e688SJim Ingham }
36061e8e688SJim Ingham
36161e8e688SJim Ingham // If this is the first completed plan, the previous one is the
36261e8e688SJim Ingham // bottom of the regular plan stack.
36361e8e688SJim Ingham if (stack_size > 0 && m_completed_plans[0].get() == current_plan) {
36461e8e688SJim Ingham return GetCurrentPlan().get();
36561e8e688SJim Ingham }
36661e8e688SJim Ingham
36761e8e688SJim Ingham // Otherwise look for it in the regular plans.
36861e8e688SJim Ingham stack_size = m_plans.size();
36961e8e688SJim Ingham for (int i = stack_size - 1; i > 0; i--) {
37061e8e688SJim Ingham if (current_plan == m_plans[i].get())
37161e8e688SJim Ingham return m_plans[i - 1].get();
37261e8e688SJim Ingham }
37361e8e688SJim Ingham return nullptr;
37461e8e688SJim Ingham }
37561e8e688SJim Ingham
GetInnermostExpression() const37661e8e688SJim Ingham ThreadPlan *ThreadPlanStack::GetInnermostExpression() const {
3776eb576dcSJim Ingham std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
37861e8e688SJim Ingham int stack_size = m_plans.size();
37961e8e688SJim Ingham
38061e8e688SJim Ingham for (int i = stack_size - 1; i > 0; i--) {
38161e8e688SJim Ingham if (m_plans[i]->GetKind() == ThreadPlan::eKindCallFunction)
38261e8e688SJim Ingham return m_plans[i].get();
38361e8e688SJim Ingham }
38461e8e688SJim Ingham return nullptr;
38561e8e688SJim Ingham }
38661e8e688SJim Ingham
ClearThreadCache()3874bb62448SWalter Erquinigo void ThreadPlanStack::ClearThreadCache() {
3886eb576dcSJim Ingham std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
3894bb62448SWalter Erquinigo for (lldb::ThreadPlanSP thread_plan_sp : m_plans)
3904bb62448SWalter Erquinigo thread_plan_sp->ClearThreadCache();
3914bb62448SWalter Erquinigo }
3924bb62448SWalter Erquinigo
WillResume()39361e8e688SJim Ingham void ThreadPlanStack::WillResume() {
3946eb576dcSJim Ingham std::lock_guard<std::recursive_mutex> guard(m_stack_mutex);
39561e8e688SJim Ingham m_completed_plans.clear();
39661e8e688SJim Ingham m_discarded_plans.clear();
39761e8e688SJim Ingham }
39861e8e688SJim Ingham
Update(ThreadList & current_threads,bool delete_missing,bool check_for_new)3991893065dSJim Ingham void ThreadPlanStackMap::Update(ThreadList ¤t_threads,
4001893065dSJim Ingham bool delete_missing,
4011893065dSJim Ingham bool check_for_new) {
4021893065dSJim Ingham
403*dca2bc40SJim Ingham std::lock_guard<std::recursive_mutex> guard(m_stack_map_mutex);
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
DumpPlans(Stream & strm,lldb::DescriptionLevel desc_level,bool internal,bool condense_if_trivial,bool skip_unreported)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) {
438*dca2bc40SJim Ingham std::lock_guard<std::recursive_mutex> guard(m_stack_map_mutex);
4396eb576dcSJim Ingham for (auto &elem : m_plans_list) {
4401893065dSJim Ingham lldb::tid_t tid = elem.first;
4411893065dSJim Ingham uint32_t index_id = 0;
4421893065dSJim Ingham ThreadSP thread_sp = m_process.GetThreadList().FindThreadByID(tid);
4431893065dSJim Ingham
4441893065dSJim Ingham if (skip_unreported) {
4451893065dSJim Ingham if (!thread_sp)
4461893065dSJim Ingham continue;
4471893065dSJim Ingham }
4481893065dSJim Ingham if (thread_sp)
4491893065dSJim Ingham index_id = thread_sp->GetIndexID();
4501893065dSJim Ingham
4511893065dSJim Ingham if (condense_if_trivial) {
4521893065dSJim Ingham if (!elem.second.AnyPlans() && !elem.second.AnyCompletedPlans() &&
4531893065dSJim Ingham !elem.second.AnyDiscardedPlans()) {
4541893065dSJim Ingham strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 "\n", index_id, tid);
4551893065dSJim Ingham strm.IndentMore();
4561893065dSJim Ingham strm.Indent();
4571893065dSJim Ingham strm.Printf("No active thread plans\n");
4581893065dSJim Ingham strm.IndentLess();
4591893065dSJim Ingham return;
4601893065dSJim Ingham }
4611893065dSJim Ingham }
4621893065dSJim Ingham
4631893065dSJim Ingham strm.Indent();
4641893065dSJim Ingham strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 ":\n", index_id, tid);
4651893065dSJim Ingham
4661893065dSJim Ingham elem.second.DumpThreadPlans(strm, desc_level, internal);
4671893065dSJim Ingham }
4681893065dSJim Ingham }
4691893065dSJim Ingham
DumpPlansForTID(Stream & strm,lldb::tid_t tid,lldb::DescriptionLevel desc_level,bool internal,bool condense_if_trivial,bool skip_unreported)4701893065dSJim Ingham bool ThreadPlanStackMap::DumpPlansForTID(Stream &strm, lldb::tid_t tid,
4711893065dSJim Ingham lldb::DescriptionLevel desc_level,
4721893065dSJim Ingham bool internal,
4731893065dSJim Ingham bool condense_if_trivial,
4741893065dSJim Ingham bool skip_unreported) {
475*dca2bc40SJim Ingham std::lock_guard<std::recursive_mutex> guard(m_stack_map_mutex);
4761893065dSJim Ingham uint32_t index_id = 0;
4771893065dSJim Ingham ThreadSP thread_sp = m_process.GetThreadList().FindThreadByID(tid);
4781893065dSJim Ingham
4791893065dSJim Ingham if (skip_unreported) {
4801893065dSJim Ingham if (!thread_sp) {
4811893065dSJim Ingham strm.Format("Unknown TID: {0}", tid);
4821893065dSJim Ingham return false;
4831893065dSJim Ingham }
4841893065dSJim Ingham }
4851893065dSJim Ingham
4861893065dSJim Ingham if (thread_sp)
4871893065dSJim Ingham index_id = thread_sp->GetIndexID();
4881893065dSJim Ingham ThreadPlanStack *stack = Find(tid);
4891893065dSJim Ingham if (!stack) {
4901893065dSJim Ingham strm.Format("Unknown TID: {0}\n", tid);
4911893065dSJim Ingham return false;
4921893065dSJim Ingham }
4931893065dSJim Ingham
4941893065dSJim Ingham if (condense_if_trivial) {
4951893065dSJim Ingham if (!stack->AnyPlans() && !stack->AnyCompletedPlans() &&
4961893065dSJim Ingham !stack->AnyDiscardedPlans()) {
4971893065dSJim Ingham strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 "\n", index_id, tid);
4981893065dSJim Ingham strm.IndentMore();
4991893065dSJim Ingham strm.Indent();
5001893065dSJim Ingham strm.Printf("No active thread plans\n");
5011893065dSJim Ingham strm.IndentLess();
5021893065dSJim Ingham return true;
5031893065dSJim Ingham }
5041893065dSJim Ingham }
5051893065dSJim Ingham
5061893065dSJim Ingham strm.Indent();
5071893065dSJim Ingham strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 ":\n", index_id, tid);
5081893065dSJim Ingham
5091893065dSJim Ingham stack->DumpThreadPlans(strm, desc_level, internal);
5101893065dSJim Ingham return true;
5111893065dSJim Ingham }
5121893065dSJim Ingham
PrunePlansForTID(lldb::tid_t tid)5131893065dSJim Ingham bool ThreadPlanStackMap::PrunePlansForTID(lldb::tid_t tid) {
5141893065dSJim Ingham // We only remove the plans for unreported TID's.
515*dca2bc40SJim Ingham std::lock_guard<std::recursive_mutex> guard(m_stack_map_mutex);
5161893065dSJim Ingham ThreadSP thread_sp = m_process.GetThreadList().FindThreadByID(tid);
5171893065dSJim Ingham if (thread_sp)
5181893065dSJim Ingham return false;
5191893065dSJim Ingham
5201893065dSJim Ingham return RemoveTID(tid);
5211893065dSJim Ingham }
522