forked from OSchip/llvm-project
Move thread plan stacks into the Process, indexed by TID.
Differential Revision: https://reviews.llvm.org/D75880
This commit is contained in:
parent
2c1c57a1df
commit
61e8e6882d
|
@ -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 @@ public:
|
|||
}
|
||||
|
||||
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 @@ protected:
|
|||
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 @@ protected:
|
|||
///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
|
||||
|
|
|
@ -28,6 +28,8 @@
|
|||
|
||||
namespace lldb_private {
|
||||
|
||||
class ThreadPlanStack;
|
||||
|
||||
class ThreadProperties : public Properties {
|
||||
public:
|
||||
ThreadProperties(bool is_global);
|
||||
|
@ -119,7 +121,7 @@ public:
|
|||
// 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 @@ public:
|
|||
///
|
||||
/// \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 @@ public:
|
|||
///
|
||||
/// \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 @@ public:
|
|||
/// 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 @@ public:
|
|||
/// \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 @@ public:
|
|||
/// \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 @@ protected:
|
|||
// 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 @@ protected:
|
|||
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 @@ private:
|
|||
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);
|
||||
|
|
|
@ -371,9 +371,9 @@ public:
|
|||
/// 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.
|
||||
|
|
|
@ -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
|
|
@ -63,6 +63,7 @@ add_lldb_library(lldbTarget
|
|||
ThreadPlanStepThrough.cpp
|
||||
ThreadPlanStepUntil.cpp
|
||||
ThreadPlanTracer.cpp
|
||||
ThreadPlanStack.cpp
|
||||
ThreadSpec.cpp
|
||||
UnixSignals.cpp
|
||||
UnwindAssembly.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
|
||||
|
|
|
@ -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());
|
||||
ThreadPlanSP discarded_plan_sp = GetPlans().PopPlan();
|
||||
|
||||
m_discarded_plan_stack.push_back(plan);
|
||||
plan->WillPop();
|
||||
m_plan_stack.pop_back();
|
||||
}
|
||||
LLDB_LOGF(log, "Discarding plan: \"%s\", tid = 0x%4.4" PRIx64 ".",
|
||||
discarded_plan_sp->GetName(),
|
||||
discarded_plan_sp->GetThread().GetID());
|
||||
}
|
||||
|
||||
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();
|
||||
ThreadPlan *Thread::GetCurrentPlan() const {
|
||||
return GetPlans().GetCurrentPlan().get();
|
||||
}
|
||||
|
||||
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;
|
||||
ThreadPlanSP Thread::GetCompletedPlan() const {
|
||||
return GetPlans().GetCompletedPlan();
|
||||
}
|
||||
|
||||
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();
|
||||
ValueObjectSP Thread::GetReturnValueObject() const {
|
||||
return GetPlans().GetReturnValueObject();
|
||||
}
|
||||
|
||||
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();
|
||||
ExpressionVariableSP Thread::GetExpressionVariable() const {
|
||||
return GetPlans().GetExpressionVariable();
|
||||
}
|
||||
|
||||
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;
|
||||
bool Thread::IsThreadPlanDone(ThreadPlan *plan) const {
|
||||
return GetPlans().IsPlanDone(plan);
|
||||
}
|
||||
|
||||
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::WasThreadPlanDiscarded(ThreadPlan *plan) const {
|
||||
return GetPlans().WasPlanDiscarded(plan);
|
||||
}
|
||||
|
||||
bool Thread::CompletedPlanOverridesBreakpoint() {
|
||||
return (!m_completed_plan_stack.empty()) ;
|
||||
bool Thread::CompletedPlanOverridesBreakpoint() const {
|
||||
return GetPlans().AnyCompletedPlans();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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() {
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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");
|
||||
}
|
Loading…
Reference in New Issue