#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"
}
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
virtual EventActionResult HandleBeingInterrupted() = 0;
virtual const char *GetExitString() = 0;
void RequestResume() { m_process->m_resume_requested = true; }
-
+
protected:
Process *m_process;
};
///see them. This is usually the same as
///< m_thread_list_real, but might be different 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
namespace lldb_private {
+class ThreadPlanStack;
+
class ThreadProperties : public Properties {
public:
ThreadProperties(bool is_global);
// 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;
///
/// \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.
///
/// \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
///
/// 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.
/// \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.
/// \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.
///
// 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();
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
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);
/// 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.
--- /dev/null
+//===-- 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
ThreadPlanStepThrough.cpp
ThreadPlanStepUntil.cpp
ThreadPlanTracer.cpp
+ ThreadPlanStack.cpp
ThreadSpec.cpp
UnixSignals.cpp
UnwindAssembly.cpp
#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"
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();
}
}
+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 ||
}
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
#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"
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),
static_cast<void *>(this), GetID());
CheckInWithManager();
-
+
+ process.AddThreadPlansForThread(*this);
+
QueueFundamentalPlan(true);
}
}
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();
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;
}
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;
}
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();
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 {
// 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);
// 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);
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();
thread_vote = plan_ptr->ShouldReportStop(event_ptr);
break;
}
- if (PlanIsBasePlan(plan_ptr))
+ if (plan_ptr->IsBasePlan())
break;
else
plan_ptr = GetPreviousPlan(plan_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
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,
}
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;
"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) {
}
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;
}
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();
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() {
break;
}
}
- if (!thread_is_alive)
+ if (!thread_is_alive) {
(*rhs_pos)->DestroyThread();
+ m_process->RemoveThreadPlansForTID((*rhs_pos)->GetID());
+ }
}
}
}
// 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;
--- /dev/null
+//===-- 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");
+}