Move thread plan stacks into the Process, indexed by TID.

Differential Revision: https://reviews.llvm.org/D75880
This commit is contained in:
Jim Ingham 2020-03-10 16:18:11 -07:00
parent 2c1c57a1df
commit 61e8e6882d
10 changed files with 667 additions and 339 deletions

View File

@ -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

View File

@ -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);

View File

@ -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.

View File

@ -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

View File

@ -63,6 +63,7 @@ add_lldb_library(lldbTarget
ThreadPlanStepThrough.cpp
ThreadPlanStepUntil.cpp
ThreadPlanTracer.cpp
ThreadPlanStack.cpp
ThreadSpec.cpp
UnixSignals.cpp
UnwindAssembly.cpp

View File

@ -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

View File

@ -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() {

View File

@ -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());
}
}
}
}

View File

@ -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;

View File

@ -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");
}