[Lldb-commits] [lldb] 61e8e68 - Move thread plan stacks into the Process, indexed by TID.
Jim Ingham via lldb-commits
lldb-commits at lists.llvm.org
Fri Apr 3 15:02:28 PDT 2020
Author: Jim Ingham
Date: 2020-04-03T14:56:28-07:00
New Revision: 61e8e6882de7c89933d3cc4c4d44719679e4b4f0
URL: https://github.com/llvm/llvm-project/commit/61e8e6882de7c89933d3cc4c4d44719679e4b4f0
DIFF: https://github.com/llvm/llvm-project/commit/61e8e6882de7c89933d3cc4c4d44719679e4b4f0.diff
LOG: Move thread plan stacks into the Process, indexed by TID.
Differential Revision: https://reviews.llvm.org/D75880
Added:
lldb/include/lldb/Target/ThreadPlanStack.h
lldb/source/Target/ThreadPlanStack.cpp
Modified:
lldb/include/lldb/Target/Process.h
lldb/include/lldb/Target/Thread.h
lldb/include/lldb/Target/ThreadPlan.h
lldb/source/Target/CMakeLists.txt
lldb/source/Target/Process.cpp
lldb/source/Target/Thread.cpp
lldb/source/Target/ThreadList.cpp
lldb/source/Target/ThreadPlan.cpp
Removed:
################################################################################
diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h
index 87f61c66bb41..02987f9b50b7 100644
--- a/lldb/include/lldb/Target/Process.h
+++ b/lldb/include/lldb/Target/Process.h
@@ -37,6 +37,7 @@
#include "lldb/Target/Memory.h"
#include "lldb/Target/QueueList.h"
#include "lldb/Target/ThreadList.h"
+#include "lldb/Target/ThreadPlanStack.h"
#include "lldb/Utility/ArchSpec.h"
#include "lldb/Utility/Broadcaster.h"
#include "lldb/Utility/Event.h"
@@ -2197,6 +2198,19 @@ class Process : public std::enable_shared_from_this<Process>,
}
void SetDynamicCheckers(DynamicCheckerFunctions *dynamic_checkers);
+
+ /// Find the thread plan stack associated with thread with \a tid.
+ ///
+ /// \param[in] tid
+ /// The tid whose Plan Stack we are seeking..
+ ///
+ /// \return
+ /// Returns a ThreadPlan if the TID is found or nullptr if not.
+ ThreadPlanStack *FindThreadPlans(lldb::tid_t tid);
+
+ void AddThreadPlansForThread(Thread &thread);
+
+ void RemoveThreadPlansForTID(lldb::tid_t tid);
/// Call this to set the lldb in the mode where it breaks on new thread
/// creations, and then auto-restarts. This is useful when you are trying
@@ -2533,7 +2547,7 @@ class Process : public std::enable_shared_from_this<Process>,
virtual EventActionResult HandleBeingInterrupted() = 0;
virtual const char *GetExitString() = 0;
void RequestResume() { m_process->m_resume_requested = true; }
-
+
protected:
Process *m_process;
};
@@ -2667,6 +2681,10 @@ class Process : public std::enable_shared_from_this<Process>,
///see them. This is usually the same as
///< m_thread_list_real, but might be
diff erent if there is an OS plug-in
///creating memory threads
+ ThreadPlanStackMap m_thread_plans; ///< This is the list of thread plans for
+ /// threads in m_thread_list, as well as
+ /// threads we knew existed, but haven't
+ /// determined that they have died yet.
ThreadList m_extended_thread_list; ///< Owner for extended threads that may be
///generated, cleared on natural stops
uint32_t m_extended_thread_stop_id; ///< The natural stop id when
diff --git a/lldb/include/lldb/Target/Thread.h b/lldb/include/lldb/Target/Thread.h
index b0bc1b29eb78..dda303ff3c46 100644
--- a/lldb/include/lldb/Target/Thread.h
+++ b/lldb/include/lldb/Target/Thread.h
@@ -28,6 +28,8 @@
namespace lldb_private {
+class ThreadPlanStack;
+
class ThreadProperties : public Properties {
public:
ThreadProperties(bool is_global);
@@ -119,7 +121,7 @@ class Thread : public std::enable_shared_from_this<Thread>,
// bit of data.
lldb::StopInfoSP stop_info_sp; // You have to restore the stop info or you
// might continue with the wrong signals.
- std::vector<lldb::ThreadPlanSP> m_completed_plan_stack;
+ size_t m_completed_plan_checkpoint;
lldb::RegisterCheckpointSP
register_backup_sp; // You need to restore the registers, of course...
uint32_t current_inlined_depth;
@@ -912,7 +914,7 @@ class Thread : public std::enable_shared_from_this<Thread>,
///
/// \return
/// A pointer to the next executed plan.
- ThreadPlan *GetCurrentPlan();
+ ThreadPlan *GetCurrentPlan() const;
/// Unwinds the thread stack for the innermost expression plan currently
/// on the thread plan stack.
@@ -927,14 +929,14 @@ class Thread : public std::enable_shared_from_this<Thread>,
///
/// \return
/// A pointer to the last completed plan.
- lldb::ThreadPlanSP GetCompletedPlan();
+ lldb::ThreadPlanSP GetCompletedPlan() const;
/// Gets the outer-most return value from the completed plans
///
/// \return
/// A ValueObjectSP, either empty if there is no return value,
/// or containing the return value.
- lldb::ValueObjectSP GetReturnValueObject();
+ lldb::ValueObjectSP GetReturnValueObject() const;
/// Gets the outer-most expression variable from the completed plans
///
@@ -942,7 +944,7 @@ class Thread : public std::enable_shared_from_this<Thread>,
/// A ExpressionVariableSP, either empty if there is no
/// plan completed an expression during the current stop
/// or the expression variable that was made for the completed expression.
- lldb::ExpressionVariableSP GetExpressionVariable();
+ lldb::ExpressionVariableSP GetExpressionVariable() const;
/// Checks whether the given plan is in the completed plans for this
/// stop.
@@ -953,7 +955,7 @@ class Thread : public std::enable_shared_from_this<Thread>,
/// \return
/// Returns true if the input plan is in the completed plan stack,
/// false otherwise.
- bool IsThreadPlanDone(ThreadPlan *plan);
+ bool IsThreadPlanDone(ThreadPlan *plan) const;
/// Checks whether the given plan is in the discarded plans for this
/// stop.
@@ -964,14 +966,14 @@ class Thread : public std::enable_shared_from_this<Thread>,
/// \return
/// Returns true if the input plan is in the discarded plan stack,
/// false otherwise.
- bool WasThreadPlanDiscarded(ThreadPlan *plan);
+ bool WasThreadPlanDiscarded(ThreadPlan *plan) const;
/// Check if we have completed plan to override breakpoint stop reason
///
/// \return
/// Returns true if completed plan stack is not empty
/// false otherwise.
- bool CompletedPlanOverridesBreakpoint();
+ bool CompletedPlanOverridesBreakpoint() const;
/// Queues a generic thread plan.
///
@@ -1184,16 +1186,16 @@ class Thread : public std::enable_shared_from_this<Thread>,
// thread is still in good shape to call virtual thread methods. This must
// be called by classes that derive from Thread in their destructor.
virtual void DestroyThread();
+
+ ThreadPlanStack &GetPlans() const;
- void PushPlan(lldb::ThreadPlanSP &plan_sp);
+ void PushPlan(lldb::ThreadPlanSP plan_sp);
void PopPlan();
void DiscardPlan();
- ThreadPlan *GetPreviousPlan(ThreadPlan *plan);
-
- typedef std::vector<lldb::ThreadPlanSP> plan_stack;
+ ThreadPlan *GetPreviousPlan(ThreadPlan *plan) const;
virtual Unwind &GetUnwinder();
@@ -1238,13 +1240,6 @@ class Thread : public std::enable_shared_from_this<Thread>,
lldb::StateType m_state; ///< The state of our process.
mutable std::recursive_mutex
m_state_mutex; ///< Multithreaded protection for m_state.
- plan_stack m_plan_stack; ///< The stack of plans this thread is executing.
- plan_stack m_completed_plan_stack; ///< Plans that have been completed by this
- ///stop. They get deleted when the thread
- ///resumes.
- plan_stack m_discarded_plan_stack; ///< Plans that have been discarded by this
- ///stop. They get deleted when the thread
- ///resumes.
mutable std::recursive_mutex
m_frame_mutex; ///< Multithreaded protection for m_state.
lldb::StackFrameListSP m_curr_frames_sp; ///< The stack frames that get lazily
@@ -1272,8 +1267,7 @@ class Thread : public std::enable_shared_from_this<Thread>,
StructuredData::ObjectSP m_extended_info; // The extended info for this thread
private:
- bool PlanIsBasePlan(ThreadPlan *plan_ptr);
-
+
void BroadcastSelectedFrameChange(StackID &new_frame_id);
DISALLOW_COPY_AND_ASSIGN(Thread);
diff --git a/lldb/include/lldb/Target/ThreadPlan.h b/lldb/include/lldb/Target/ThreadPlan.h
index 40c15e3387d7..ef1e5f212f2d 100644
--- a/lldb/include/lldb/Target/ThreadPlan.h
+++ b/lldb/include/lldb/Target/ThreadPlan.h
@@ -371,9 +371,9 @@ class ThreadPlan : public std::enable_shared_from_this<ThreadPlan>,
/// A pointer to the thread plan's owning thread.
Thread &GetThread();
- Target &GetTarget() { return m_process.GetTarget(); }
+ Target &GetTarget();
- const Target &GetTarget() const { return m_process.GetTarget(); }
+ const Target &GetTarget() const;
/// Print a description of this thread to the stream \a s.
/// \a thread.
diff --git a/lldb/include/lldb/Target/ThreadPlanStack.h b/lldb/include/lldb/Target/ThreadPlanStack.h
new file mode 100644
index 000000000000..be7791abef81
--- /dev/null
+++ b/lldb/include/lldb/Target/ThreadPlanStack.h
@@ -0,0 +1,153 @@
+//===-- ThreadPlanStack.h ---------------------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_TARGET_THREADPLANSTACK_H
+#define LLDB_TARGET_THREADPLANSTACK_H
+
+#include <mutex>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/lldb-private-forward.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+// The ThreadPlans have a thread for use when they are asked all the ThreadPlan
+// state machine questions, but they should never cache any pointers from their
+// owning lldb_private::Thread. That's because we want to be able to detach
+// them from an owning thread, then reattach them by TID.
+// The ThreadPlanStack holds the ThreadPlans for a given TID. All its methods
+// are private, and it should only be accessed through the owning thread. When
+// it is detached from a thread, all you can do is reattach it or delete it.
+class ThreadPlanStack {
+ friend class lldb_private::Thread;
+
+public:
+ ThreadPlanStack(Thread &thread) {}
+ ~ThreadPlanStack() {}
+
+ enum StackKind { ePlans, eCompletedPlans, eDiscardedPlans };
+
+ using PlanStack = std::vector<lldb::ThreadPlanSP>;
+
+ void DumpThreadPlans(Stream *s, lldb::DescriptionLevel desc_level,
+ bool include_internal) const;
+
+ size_t CheckpointCompletedPlans();
+
+ void RestoreCompletedPlanCheckpoint(size_t checkpoint);
+
+ void DiscardCompletedPlanCheckpoint(size_t checkpoint);
+
+ void ThreadDestroyed(Thread *thread);
+
+ void EnableTracer(bool value, bool single_stepping);
+
+ void SetTracer(lldb::ThreadPlanTracerSP &tracer_sp);
+
+ void PushPlan(lldb::ThreadPlanSP new_plan_sp);
+
+ lldb::ThreadPlanSP PopPlan();
+
+ lldb::ThreadPlanSP DiscardPlan();
+
+ // 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 DiscardPlansUpToPlan(ThreadPlan *up_to_plan_ptr);
+
+ void DiscardAllPlans();
+
+ void DiscardConsultingMasterPlans();
+
+ lldb::ThreadPlanSP GetCurrentPlan() const;
+
+ lldb::ThreadPlanSP GetCompletedPlan(bool skip_private = true) const;
+
+ lldb::ThreadPlanSP GetPlanByIndex(uint32_t plan_idx,
+ bool skip_private = true) const;
+
+ lldb::ValueObjectSP GetReturnValueObject() const;
+
+ lldb::ExpressionVariableSP GetExpressionVariable() const;
+
+ bool AnyPlans() const;
+
+ bool AnyCompletedPlans() const;
+
+ bool AnyDiscardedPlans() const;
+
+ bool IsPlanDone(ThreadPlan *plan) const;
+
+ bool WasPlanDiscarded(ThreadPlan *plan) const;
+
+ ThreadPlan *GetPreviousPlan(ThreadPlan *current_plan) const;
+
+ ThreadPlan *GetInnermostExpression() const;
+
+ void WillResume();
+
+private:
+ const PlanStack &GetStackOfKind(ThreadPlanStack::StackKind kind) const;
+
+ PlanStack m_plans; ///< The stack of plans this thread is executing.
+ PlanStack m_completed_plans; ///< Plans that have been completed by this
+ /// stop. They get deleted when the thread
+ /// resumes.
+ PlanStack m_discarded_plans; ///< Plans that have been discarded by this
+ /// stop. They get deleted when the thread
+ /// resumes.
+ size_t m_completed_plan_checkpoint = 0; // Monotonically increasing token for
+ // completed plan checkpoints.
+ std::unordered_map<size_t, PlanStack> m_completed_plan_store;
+};
+
+class ThreadPlanStackMap {
+public:
+ ThreadPlanStackMap() {}
+ ~ThreadPlanStackMap() {}
+
+ void AddThread(Thread &thread) {
+ lldb::tid_t tid = thread.GetID();
+ auto result = m_plans_list.emplace(tid, thread);
+ }
+
+ bool RemoveTID(lldb::tid_t tid) {
+ auto result = m_plans_list.find(tid);
+ if (result == m_plans_list.end())
+ return false;
+ result->second.ThreadDestroyed(nullptr);
+ m_plans_list.erase(result);
+ return true;
+ }
+
+ ThreadPlanStack *Find(lldb::tid_t tid) {
+ auto result = m_plans_list.find(tid);
+ if (result == m_plans_list.end())
+ return nullptr;
+ else
+ return &result->second;
+ }
+
+ void Clear() {
+ for (auto plan : m_plans_list)
+ plan.second.ThreadDestroyed(nullptr);
+ m_plans_list.clear();
+ }
+
+private:
+ using PlansList = std::unordered_map<lldb::tid_t, ThreadPlanStack>;
+ PlansList m_plans_list;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_TARGET_THREADPLANSTACK_H
diff --git a/lldb/source/Target/CMakeLists.txt b/lldb/source/Target/CMakeLists.txt
index 906dcb1f82d7..ca80b5b64778 100644
--- a/lldb/source/Target/CMakeLists.txt
+++ b/lldb/source/Target/CMakeLists.txt
@@ -63,6 +63,7 @@ add_lldb_library(lldbTarget
ThreadPlanStepThrough.cpp
ThreadPlanStepUntil.cpp
ThreadPlanTracer.cpp
+ ThreadPlanStack.cpp
ThreadSpec.cpp
UnixSignals.cpp
UnwindAssembly.cpp
diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp
index 7dac2bedd42b..411160d3f3cd 100644
--- a/lldb/source/Target/Process.cpp
+++ b/lldb/source/Target/Process.cpp
@@ -60,6 +60,7 @@
#include "lldb/Target/ThreadPlan.h"
#include "lldb/Target/ThreadPlanBase.h"
#include "lldb/Target/ThreadPlanCallFunction.h"
+#include "lldb/Target/ThreadPlanStack.h"
#include "lldb/Target/UnixSignals.h"
#include "lldb/Utility/Event.h"
#include "lldb/Utility/Log.h"
@@ -600,6 +601,7 @@ void Process::Finalize() {
m_system_runtime_up.reset();
m_dyld_up.reset();
m_jit_loaders_up.reset();
+ m_thread_plans.Clear();
m_thread_list_real.Destroy();
m_thread_list.Destroy();
m_extended_thread_list.Destroy();
@@ -1252,6 +1254,20 @@ void Process::UpdateThreadListIfNeeded() {
}
}
+ThreadPlanStack *Process::FindThreadPlans(lldb::tid_t tid) {
+ return m_thread_plans.Find(tid);
+}
+
+void Process::AddThreadPlansForThread(Thread &thread) {
+ if (m_thread_plans.Find(thread.GetID()))
+ return;
+ m_thread_plans.AddThread(thread);
+}
+
+void Process::RemoveThreadPlansForTID(lldb::tid_t tid) {
+ m_thread_plans.RemoveTID(tid);
+}
+
void Process::UpdateQueueListIfNeeded() {
if (m_system_runtime_up) {
if (m_queue_list.GetSize() == 0 ||
@@ -3231,6 +3247,10 @@ Status Process::Detach(bool keep_stopped) {
}
Status Process::Destroy(bool force_kill) {
+ // If we've already called Process::Finalize then there's nothing useful to
+ // be done here. Finalize has actually called Destroy already.
+ if (m_finalize_called)
+ return {};
// Tell ourselves we are in the process of destroying the process, so that we
// don't do any unnecessary work that might hinder the destruction. Remember
diff --git a/lldb/source/Target/Thread.cpp b/lldb/source/Target/Thread.cpp
index d00542f77144..6b8edfbbb822 100644
--- a/lldb/source/Target/Thread.cpp
+++ b/lldb/source/Target/Thread.cpp
@@ -33,6 +33,7 @@
#include "lldb/Target/ThreadPlanCallFunction.h"
#include "lldb/Target/ThreadPlanPython.h"
#include "lldb/Target/ThreadPlanRunToAddress.h"
+#include "lldb/Target/ThreadPlanStack.h"
#include "lldb/Target/ThreadPlanStepInRange.h"
#include "lldb/Target/ThreadPlanStepInstruction.h"
#include "lldb/Target/ThreadPlanStepOut.h"
@@ -228,8 +229,7 @@ Thread::Thread(Process &process, lldb::tid_t tid, bool use_invalid_index_id)
m_index_id(use_invalid_index_id ? LLDB_INVALID_INDEX32
: process.GetNextThreadIndexID(tid)),
m_reg_context_sp(), m_state(eStateUnloaded), m_state_mutex(),
- m_plan_stack(), m_completed_plan_stack(), m_frame_mutex(),
- m_curr_frames_sp(), m_prev_frames_sp(),
+ m_frame_mutex(), m_curr_frames_sp(), m_prev_frames_sp(),
m_resume_signal(LLDB_INVALID_SIGNAL_NUMBER),
m_resume_state(eStateRunning), m_temporary_resume_state(eStateRunning),
m_unwinder_up(), m_destroy_called(false),
@@ -240,7 +240,9 @@ Thread::Thread(Process &process, lldb::tid_t tid, bool use_invalid_index_id)
static_cast<void *>(this), GetID());
CheckInWithManager();
-
+
+ process.AddThreadPlansForThread(*this);
+
QueueFundamentalPlan(true);
}
@@ -254,30 +256,7 @@ Thread::~Thread() {
}
void Thread::DestroyThread() {
- // Tell any plans on the plan stacks that the thread is being destroyed since
- // any plans that have a thread go away in the middle of might need to do
- // cleanup, or in some cases NOT do cleanup...
- for (auto plan : m_plan_stack)
- plan->ThreadDestroyed();
-
- for (auto plan : m_discarded_plan_stack)
- plan->ThreadDestroyed();
-
- for (auto plan : m_completed_plan_stack)
- plan->ThreadDestroyed();
-
m_destroy_called = true;
- m_plan_stack.clear();
- m_discarded_plan_stack.clear();
- m_completed_plan_stack.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.
- ThreadPlanSP null_plan_sp(new ThreadPlanNull(*this));
- m_plan_stack.push_back(null_plan_sp);
-
m_stop_info_sp.reset();
m_reg_context_sp.reset();
m_unwinder_up.reset();
@@ -522,7 +501,8 @@ bool Thread::CheckpointThreadState(ThreadStateCheckpoint &saved_state) {
if (process_sp)
saved_state.orig_stop_id = process_sp->GetStopID();
saved_state.current_inlined_depth = GetCurrentInlinedDepth();
- saved_state.m_completed_plan_stack = m_completed_plan_stack;
+ saved_state.m_completed_plan_checkpoint =
+ GetPlans().CheckpointCompletedPlans();
return true;
}
@@ -556,7 +536,8 @@ bool Thread::RestoreThreadStateFromCheckpoint(
SetStopInfo(saved_state.stop_info_sp);
GetStackFrameList()->SetCurrentInlinedDepth(
saved_state.current_inlined_depth);
- m_completed_plan_stack = saved_state.m_completed_plan_stack;
+ GetPlans().RestoreCompletedPlanCheckpoint(
+ saved_state.m_completed_plan_checkpoint);
return true;
}
@@ -689,8 +670,7 @@ void Thread::SetupForResume() {
bool Thread::ShouldResume(StateType resume_state) {
// At this point clear the completed plan stack.
- m_completed_plan_stack.clear();
- m_discarded_plan_stack.clear();
+ GetPlans().WillResume();
m_override_should_notify = eLazyBoolCalculate;
StateType prev_resume_state = GetTemporaryResumeState();
@@ -884,7 +864,7 @@ bool Thread::ShouldStop(Event *event_ptr) {
current_plan->GetName(), over_ride_stop);
// We're starting from the base plan, so just let it decide;
- if (PlanIsBasePlan(current_plan)) {
+ if (current_plan->IsBasePlan()) {
should_stop = current_plan->ShouldStop(event_ptr);
LLDB_LOGF(log, "Base plan says should stop: %i.", should_stop);
} else {
@@ -892,7 +872,7 @@ bool Thread::ShouldStop(Event *event_ptr) {
// to do, since presumably if there were other plans they would know what
// to do...
while (true) {
- if (PlanIsBasePlan(current_plan))
+ if (current_plan->IsBasePlan())
break;
should_stop = current_plan->ShouldStop(event_ptr);
@@ -938,7 +918,7 @@ bool Thread::ShouldStop(Event *event_ptr) {
// Discard the stale plans and all plans below them in the stack, plus move
// the completed plans to the completed plan stack
- while (!PlanIsBasePlan(plan_ptr)) {
+ while (!plan_ptr->IsBasePlan()) {
bool stale = plan_ptr->IsPlanStale();
ThreadPlan *examined_plan = plan_ptr;
plan_ptr = GetPreviousPlan(examined_plan);
@@ -1004,13 +984,14 @@ Vote Thread::ShouldReportStop(Event *event_ptr) {
return eVoteNoOpinion;
}
- if (m_completed_plan_stack.size() > 0) {
- // Don't use GetCompletedPlan here, since that suppresses private plans.
+ if (GetPlans().AnyCompletedPlans()) {
+ // Pass skip_private = false to GetCompletedPlan, since we want to ask
+ // the last plan, regardless of whether it is private or not.
LLDB_LOGF(log,
"Thread::ShouldReportStop() tid = 0x%4.4" PRIx64
": returning vote for complete stack's back plan",
GetID());
- return m_completed_plan_stack.back()->ShouldReportStop(event_ptr);
+ return GetPlans().GetCompletedPlan(false)->ShouldReportStop(event_ptr);
} else {
Vote thread_vote = eVoteNoOpinion;
ThreadPlan *plan_ptr = GetCurrentPlan();
@@ -1019,7 +1000,7 @@ Vote Thread::ShouldReportStop(Event *event_ptr) {
thread_vote = plan_ptr->ShouldReportStop(event_ptr);
break;
}
- if (PlanIsBasePlan(plan_ptr))
+ if (plan_ptr->IsBasePlan())
break;
else
plan_ptr = GetPreviousPlan(plan_ptr);
@@ -1041,16 +1022,17 @@ Vote Thread::ShouldReportRun(Event *event_ptr) {
}
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
- if (m_completed_plan_stack.size() > 0) {
- // Don't use GetCompletedPlan here, since that suppresses private plans.
+ if (GetPlans().AnyCompletedPlans()) {
+ // Pass skip_private = false to GetCompletedPlan, since we want to ask
+ // the last plan, regardless of whether it is private or not.
LLDB_LOGF(log,
"Current Plan for thread %d(%p) (0x%4.4" PRIx64
", %s): %s being asked whether we should report run.",
GetIndexID(), static_cast<void *>(this), GetID(),
StateAsCString(GetTemporaryResumeState()),
- m_completed_plan_stack.back()->GetName());
+ GetCompletedPlan()->GetName());
- return m_completed_plan_stack.back()->ShouldReportRun(event_ptr);
+ return GetPlans().GetCompletedPlan(false)->ShouldReportRun(event_ptr);
} else {
LLDB_LOGF(log,
"Current Plan for thread %d(%p) (0x%4.4" PRIx64
@@ -1067,148 +1049,75 @@ bool Thread::MatchesSpec(const ThreadSpec *spec) {
return (spec == nullptr) ? true : spec->ThreadPassesBasicTests(*this);
}
-void Thread::PushPlan(ThreadPlanSP &thread_plan_sp) {
- if (thread_plan_sp) {
- // If the thread plan doesn't already have a tracer, give it its parent's
- // tracer:
- if (!thread_plan_sp->GetThreadPlanTracer()) {
- assert(!m_plan_stack.empty());
- thread_plan_sp->SetThreadPlanTracer(
- m_plan_stack.back()->GetThreadPlanTracer());
- }
- m_plan_stack.push_back(thread_plan_sp);
+ThreadPlanStack &Thread::GetPlans() const {
+ ThreadPlanStack *plans = GetProcess()->FindThreadPlans(GetID());
+ assert(plans && "Can't have a thread with no plans");
+ return *plans;
+}
- thread_plan_sp->DidPush();
+void Thread::PushPlan(ThreadPlanSP thread_plan_sp) {
+ assert(thread_plan_sp && "Don't push an empty thread plan.");
- Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
- if (log) {
- StreamString s;
- thread_plan_sp->GetDescription(&s, lldb::eDescriptionLevelFull);
- LLDB_LOGF(log, "Thread::PushPlan(0x%p): \"%s\", tid = 0x%4.4" PRIx64 ".",
- static_cast<void *>(this), s.GetData(),
- thread_plan_sp->GetThread().GetID());
- }
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+ if (log) {
+ StreamString s;
+ thread_plan_sp->GetDescription(&s, lldb::eDescriptionLevelFull);
+ LLDB_LOGF(log, "Thread::PushPlan(0x%p): \"%s\", tid = 0x%4.4" PRIx64 ".",
+ static_cast<void *>(this), s.GetData(),
+ thread_plan_sp->GetThread().GetID());
}
+
+ GetPlans().PushPlan(std::move(thread_plan_sp));
}
void Thread::PopPlan() {
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
-
- if (m_plan_stack.size() <= 1)
- return;
- else {
- ThreadPlanSP &plan = m_plan_stack.back();
- if (log) {
- LLDB_LOGF(log, "Popping plan: \"%s\", tid = 0x%4.4" PRIx64 ".",
- plan->GetName(), plan->GetThread().GetID());
- }
- m_completed_plan_stack.push_back(plan);
- plan->WillPop();
- m_plan_stack.pop_back();
+ ThreadPlanSP popped_plan_sp = GetPlans().PopPlan();
+ if (log) {
+ LLDB_LOGF(log, "Popping plan: \"%s\", tid = 0x%4.4" PRIx64 ".",
+ popped_plan_sp->GetName(), popped_plan_sp->GetThread().GetID());
}
}
void Thread::DiscardPlan() {
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
- if (m_plan_stack.size() > 1) {
- ThreadPlanSP &plan = m_plan_stack.back();
- LLDB_LOGF(log, "Discarding plan: \"%s\", tid = 0x%4.4" PRIx64 ".",
- plan->GetName(), plan->GetThread().GetID());
-
- m_discarded_plan_stack.push_back(plan);
- plan->WillPop();
- m_plan_stack.pop_back();
- }
-}
+ ThreadPlanSP discarded_plan_sp = GetPlans().PopPlan();
-ThreadPlan *Thread::GetCurrentPlan() {
- // There will always be at least the base plan. If somebody is mucking with
- // a thread with an empty plan stack, we should assert right away.
- return m_plan_stack.empty() ? nullptr : m_plan_stack.back().get();
+ LLDB_LOGF(log, "Discarding plan: \"%s\", tid = 0x%4.4" PRIx64 ".",
+ discarded_plan_sp->GetName(),
+ discarded_plan_sp->GetThread().GetID());
}
-ThreadPlanSP Thread::GetCompletedPlan() {
- ThreadPlanSP empty_plan_sp;
- if (!m_completed_plan_stack.empty()) {
- for (int i = m_completed_plan_stack.size() - 1; i >= 0; i--) {
- ThreadPlanSP completed_plan_sp;
- completed_plan_sp = m_completed_plan_stack[i];
- if (!completed_plan_sp->GetPrivate())
- return completed_plan_sp;
- }
- }
- return empty_plan_sp;
+ThreadPlan *Thread::GetCurrentPlan() const {
+ return GetPlans().GetCurrentPlan().get();
}
-ValueObjectSP Thread::GetReturnValueObject() {
- if (!m_completed_plan_stack.empty()) {
- for (int i = m_completed_plan_stack.size() - 1; i >= 0; i--) {
- ValueObjectSP return_valobj_sp;
- return_valobj_sp = m_completed_plan_stack[i]->GetReturnValueObject();
- if (return_valobj_sp)
- return return_valobj_sp;
- }
- }
- return ValueObjectSP();
+ThreadPlanSP Thread::GetCompletedPlan() const {
+ return GetPlans().GetCompletedPlan();
}
-ExpressionVariableSP Thread::GetExpressionVariable() {
- if (!m_completed_plan_stack.empty()) {
- for (int i = m_completed_plan_stack.size() - 1; i >= 0; i--) {
- ExpressionVariableSP expression_variable_sp;
- expression_variable_sp =
- m_completed_plan_stack[i]->GetExpressionVariable();
- if (expression_variable_sp)
- return expression_variable_sp;
- }
- }
- return ExpressionVariableSP();
+ValueObjectSP Thread::GetReturnValueObject() const {
+ return GetPlans().GetReturnValueObject();
}
-bool Thread::IsThreadPlanDone(ThreadPlan *plan) {
- if (!m_completed_plan_stack.empty()) {
- for (int i = m_completed_plan_stack.size() - 1; i >= 0; i--) {
- if (m_completed_plan_stack[i].get() == plan)
- return true;
- }
- }
- return false;
+ExpressionVariableSP Thread::GetExpressionVariable() const {
+ return GetPlans().GetExpressionVariable();
}
-bool Thread::WasThreadPlanDiscarded(ThreadPlan *plan) {
- if (!m_discarded_plan_stack.empty()) {
- for (int i = m_discarded_plan_stack.size() - 1; i >= 0; i--) {
- if (m_discarded_plan_stack[i].get() == plan)
- return true;
- }
- }
- return false;
+bool Thread::IsThreadPlanDone(ThreadPlan *plan) const {
+ return GetPlans().IsPlanDone(plan);
}
-bool Thread::CompletedPlanOverridesBreakpoint() {
- return (!m_completed_plan_stack.empty()) ;
+bool Thread::WasThreadPlanDiscarded(ThreadPlan *plan) const {
+ return GetPlans().WasPlanDiscarded(plan);
}
-ThreadPlan *Thread::GetPreviousPlan(ThreadPlan *current_plan) {
- if (current_plan == nullptr)
- return nullptr;
-
- int stack_size = m_completed_plan_stack.size();
- for (int i = stack_size - 1; i > 0; i--) {
- if (current_plan == m_completed_plan_stack[i].get())
- return m_completed_plan_stack[i - 1].get();
- }
-
- if (stack_size > 0 && m_completed_plan_stack[0].get() == current_plan) {
- return GetCurrentPlan();
- }
+bool Thread::CompletedPlanOverridesBreakpoint() const {
+ return GetPlans().AnyCompletedPlans();
+}
- stack_size = m_plan_stack.size();
- for (int i = stack_size - 1; i > 0; i--) {
- if (current_plan == m_plan_stack[i].get())
- return m_plan_stack[i - 1].get();
- }
- return nullptr;
+ThreadPlan *Thread::GetPreviousPlan(ThreadPlan *current_plan) const{
+ return GetPlans().GetPreviousPlan(current_plan);
}
Status Thread::QueueThreadPlan(ThreadPlanSP &thread_plan_sp,
@@ -1242,38 +1151,18 @@ Status Thread::QueueThreadPlan(ThreadPlanSP &thread_plan_sp,
}
void Thread::EnableTracer(bool value, bool single_stepping) {
- int stack_size = m_plan_stack.size();
- for (int i = 0; i < stack_size; i++) {
- if (m_plan_stack[i]->GetThreadPlanTracer()) {
- m_plan_stack[i]->GetThreadPlanTracer()->EnableTracing(value);
- m_plan_stack[i]->GetThreadPlanTracer()->EnableSingleStep(single_stepping);
- }
- }
+ GetPlans().EnableTracer(value, single_stepping);
}
void Thread::SetTracer(lldb::ThreadPlanTracerSP &tracer_sp) {
- int stack_size = m_plan_stack.size();
- for (int i = 0; i < stack_size; i++)
- m_plan_stack[i]->SetThreadPlanTracer(tracer_sp);
+ GetPlans().SetTracer(tracer_sp);
}
-bool Thread::DiscardUserThreadPlansUpToIndex(uint32_t thread_index) {
+bool Thread::DiscardUserThreadPlansUpToIndex(uint32_t plan_index) {
// Count the user thread plans from the back end to get the number of the one
// we want to discard:
- uint32_t idx = 0;
- ThreadPlan *up_to_plan_ptr = nullptr;
-
- for (ThreadPlanSP plan_sp : m_plan_stack) {
- if (plan_sp->GetPrivate())
- continue;
- if (idx == thread_index) {
- up_to_plan_ptr = plan_sp.get();
- break;
- } else
- idx++;
- }
-
+ ThreadPlan *up_to_plan_ptr = GetPlans().GetPlanByIndex(plan_index).get();
if (up_to_plan_ptr == nullptr)
return false;
@@ -1291,30 +1180,7 @@ void Thread::DiscardThreadPlansUpToPlan(ThreadPlan *up_to_plan_ptr) {
"Discarding thread plans for thread tid = 0x%4.4" PRIx64
", up to %p",
GetID(), static_cast<void *>(up_to_plan_ptr));
-
- int stack_size = m_plan_stack.size();
-
- // 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.
-
- if (up_to_plan_ptr == nullptr) {
- for (int i = stack_size - 1; i > 0; i--)
- DiscardPlan();
- } else {
- bool found_it = false;
- for (int i = stack_size - 1; i > 0; i--) {
- if (m_plan_stack[i].get() == up_to_plan_ptr)
- found_it = true;
- }
- if (found_it) {
- bool last_one = false;
- for (int i = stack_size - 1; i > 0 && !last_one; i--) {
- if (GetCurrentPlan() == up_to_plan_ptr)
- last_one = true;
- DiscardPlan();
- }
- }
- }
+ GetPlans().DiscardPlansUpToPlan(up_to_plan_ptr);
}
void Thread::DiscardThreadPlans(bool force) {
@@ -1327,73 +1193,20 @@ void Thread::DiscardThreadPlans(bool force) {
}
if (force) {
- int stack_size = m_plan_stack.size();
- for (int i = stack_size - 1; i > 0; i--) {
- DiscardPlan();
- }
+ GetPlans().DiscardAllPlans();
return;
}
-
- 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_plan_stack.size() - 1; master_plan_idx >= 0;
- master_plan_idx--) {
- if (m_plan_stack[master_plan_idx]->IsMasterPlan()) {
- discard = m_plan_stack[master_plan_idx]->OkayToDiscard();
- break;
- }
- }
-
- if (discard) {
- // First pop all the dependent plans:
- for (int i = m_plan_stack.size() - 1; i > master_plan_idx; i--) {
- // FIXME: Do we need a finalize here, or is the rule that
- // "PrepareForStop"
- // for the plan leaves it in a state that it is safe to pop the plan
- // with no more notice?
- 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();
- }
- } else {
- // If the master plan doesn't want to get discarded, then we're done.
- break;
- }
- }
-}
-
-bool Thread::PlanIsBasePlan(ThreadPlan *plan_ptr) {
- if (plan_ptr->IsBasePlan())
- return true;
- else if (m_plan_stack.size() == 0)
- return false;
- else
- return m_plan_stack[0].get() == plan_ptr;
+ GetPlans().DiscardConsultingMasterPlans();
}
Status Thread::UnwindInnermostExpression() {
Status error;
- int stack_size = m_plan_stack.size();
-
- // 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.
-
- for (int i = stack_size - 1; i > 0; i--) {
- if (m_plan_stack[i]->GetKind() == ThreadPlan::eKindCallFunction) {
- DiscardThreadPlansUpToPlan(m_plan_stack[i].get());
- return error;
- }
- }
- error.SetErrorString("No expressions currently active on this thread");
+ ThreadPlan *innermost_expr_plan = GetPlans().GetInnermostExpression();
+ if (!innermost_expr_plan) {
+ error.SetErrorString("No expressions currently active on this thread");
+ return error;
+ }
+ DiscardThreadPlansUpToPlan(innermost_expr_plan);
return error;
}
@@ -1559,40 +1372,12 @@ lldb::ThreadPlanSP Thread::QueueThreadPlanForStepScripted(
uint32_t Thread::GetIndexID() const { return m_index_id; }
-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();
-}
-
-static void PrintPlanStack(Stream *s,
- const std::vector<lldb::ThreadPlanSP> &plan_stack,
- lldb::DescriptionLevel desc_level,
- bool include_internal) {
- int32_t print_idx = 0;
- for (ThreadPlanSP plan_sp : plan_stack) {
- if (include_internal || !plan_sp->GetPrivate()) {
- PrintPlanElement(s, plan_sp, desc_level, print_idx++);
- }
- }
-}
-
void Thread::DumpThreadPlans(Stream *s, lldb::DescriptionLevel desc_level,
bool include_internal,
bool ignore_boring_threads) const {
- uint32_t stack_size;
-
if (ignore_boring_threads) {
- uint32_t stack_size = m_plan_stack.size();
- uint32_t completed_stack_size = m_completed_plan_stack.size();
- uint32_t discarded_stack_size = m_discarded_plan_stack.size();
- if (stack_size == 1 && completed_stack_size == 0 &&
- discarded_stack_size == 0) {
+ if (!GetPlans().AnyPlans() && !GetPlans().AnyCompletedPlans()
+ && !GetPlans().AnyDiscardedPlans()) {
s->Printf("thread #%u: tid = 0x%4.4" PRIx64 "\n", GetIndexID(), GetID());
s->IndentMore();
s->Indent();
@@ -1601,29 +1386,10 @@ void Thread::DumpThreadPlans(Stream *s, lldb::DescriptionLevel desc_level,
return;
}
}
-
+
s->Indent();
s->Printf("thread #%u: tid = 0x%4.4" PRIx64 ":\n", GetIndexID(), GetID());
- s->IndentMore();
- s->Indent();
- s->Printf("Active plan stack:\n");
- PrintPlanStack(s, m_plan_stack, desc_level, include_internal);
-
- stack_size = m_completed_plan_stack.size();
- if (stack_size > 0) {
- s->Indent();
- s->Printf("Completed Plan Stack:\n");
- PrintPlanStack(s, m_completed_plan_stack, desc_level, include_internal);
- }
-
- stack_size = m_discarded_plan_stack.size();
- if (stack_size > 0) {
- s->Indent();
- s->Printf("Discarded Plan Stack:\n");
- PrintPlanStack(s, m_discarded_plan_stack, desc_level, include_internal);
- }
-
- s->IndentLess();
+ GetPlans().DumpThreadPlans(s, desc_level, include_internal);
}
TargetSP Thread::CalculateTarget() {
diff --git a/lldb/source/Target/ThreadList.cpp b/lldb/source/Target/ThreadList.cpp
index 8d90e48de2f2..327ff9747ce7 100644
--- a/lldb/source/Target/ThreadList.cpp
+++ b/lldb/source/Target/ThreadList.cpp
@@ -726,8 +726,10 @@ void ThreadList::Update(ThreadList &rhs) {
break;
}
}
- if (!thread_is_alive)
+ if (!thread_is_alive) {
(*rhs_pos)->DestroyThread();
+ m_process->RemoveThreadPlansForTID((*rhs_pos)->GetID());
+ }
}
}
}
diff --git a/lldb/source/Target/ThreadPlan.cpp b/lldb/source/Target/ThreadPlan.cpp
index 8253023d4c7c..d0da8e10ca79 100644
--- a/lldb/source/Target/ThreadPlan.cpp
+++ b/lldb/source/Target/ThreadPlan.cpp
@@ -34,6 +34,10 @@ ThreadPlan::ThreadPlan(ThreadPlanKind kind, const char *name, Thread &thread,
// Destructor
ThreadPlan::~ThreadPlan() = default;
+Target &ThreadPlan::GetTarget() { return m_process.GetTarget(); }
+
+const Target &ThreadPlan::GetTarget() const { return m_process.GetTarget(); }
+
Thread &ThreadPlan::GetThread() {
if (m_thread)
return *m_thread;
diff --git a/lldb/source/Target/ThreadPlanStack.cpp b/lldb/source/Target/ThreadPlanStack.cpp
new file mode 100644
index 000000000000..bd11718f01d9
--- /dev/null
+++ b/lldb/source/Target/ThreadPlanStack.cpp
@@ -0,0 +1,370 @@
+//===-- 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();
+}
+
+void ThreadPlanStack::DumpThreadPlans(Stream *s,
+ lldb::DescriptionLevel desc_level,
+ bool include_internal) const {
+
+ uint32_t stack_size;
+
+ s->IndentMore();
+ s->Indent();
+ s->Printf("Active plan stack:\n");
+ int32_t print_idx = 0;
+ for (auto plan : m_plans) {
+ PrintPlanElement(s, plan, desc_level, print_idx++);
+ }
+
+ if (AnyCompletedPlans()) {
+ print_idx = 0;
+ s->Indent();
+ s->Printf("Completed Plan Stack:\n");
+ for (auto plan : m_completed_plans)
+ PrintPlanElement(s, plan, desc_level, print_idx++);
+ }
+
+ if (AnyDiscardedPlans()) {
+ print_idx = 0;
+ s->Indent();
+ s->Printf("Discarded Plan Stack:\n");
+ for (auto plan : m_discarded_plans)
+ PrintPlanElement(s, plan, desc_level, print_idx++);
+ }
+
+ s->IndentLess();
+}
+
+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 = 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 = 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;
+ ThreadPlan *up_to_plan_ptr = nullptr;
+
+ 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");
+}
More information about the lldb-commits
mailing list