diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h --- a/lldb/include/lldb/Target/Process.h +++ b/lldb/include/lldb/Target/Process.h @@ -37,6 +37,7 @@ #include "lldb/Target/Memory.h" #include "lldb/Target/QueueList.h" #include "lldb/Target/ThreadList.h" +#include "lldb/Target/ThreadPlanStack.h" #include "lldb/Utility/ArchSpec.h" #include "lldb/Utility/Broadcaster.h" #include "lldb/Utility/Event.h" @@ -2197,6 +2198,19 @@ } 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 @@ 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 @@ ///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 diff --git a/lldb/include/lldb/Target/Thread.h b/lldb/include/lldb/Target/Thread.h --- a/lldb/include/lldb/Target/Thread.h +++ b/lldb/include/lldb/Target/Thread.h @@ -28,6 +28,8 @@ namespace lldb_private { +class ThreadPlanStack; + class ThreadProperties : public Properties { public: ThreadProperties(bool is_global); @@ -119,7 +121,7 @@ // 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 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 @@ /// /// \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 @@ /// /// \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 @@ /// 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 @@ /// \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 @@ /// \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 @@ // 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 plan_stack; + ThreadPlan *GetPreviousPlan(ThreadPlan *plan) const; virtual Unwind &GetUnwinder(); @@ -1238,13 +1240,6 @@ 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 @@ StructuredData::ObjectSP m_extended_info; // The extended info for this thread private: - bool PlanIsBasePlan(ThreadPlan *plan_ptr); - + void BroadcastSelectedFrameChange(StackID &new_frame_id); DISALLOW_COPY_AND_ASSIGN(Thread); diff --git a/lldb/include/lldb/Target/ThreadPlan.h b/lldb/include/lldb/Target/ThreadPlan.h --- a/lldb/include/lldb/Target/ThreadPlan.h +++ b/lldb/include/lldb/Target/ThreadPlan.h @@ -371,9 +371,9 @@ /// A pointer to the thread plan's owning thread. Thread &GetThread(); - Target &GetTarget() { return m_process.GetTarget(); } + Target &GetTarget(); - const Target &GetTarget() const { return m_process.GetTarget(); } + const Target &GetTarget() const; /// Print a description of this thread to the stream \a s. /// \a thread. diff --git a/lldb/include/lldb/Target/ThreadPlanStack.h b/lldb/include/lldb/Target/ThreadPlanStack.h new file mode 100644 --- /dev/null +++ b/lldb/include/lldb/Target/ThreadPlanStack.h @@ -0,0 +1,153 @@ +//===-- ThreadPlanStack.h ---------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_TARGET_THREADPLANSTACK_H +#define LLDB_TARGET_THREADPLANSTACK_H + +#include +#include +#include +#include + +#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; + + 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 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; + PlansList m_plans_list; +}; + +} // namespace lldb_private + +#endif // LLDB_TARGET_THREADPLANSTACK_H diff --git a/lldb/source/Target/CMakeLists.txt b/lldb/source/Target/CMakeLists.txt --- a/lldb/source/Target/CMakeLists.txt +++ b/lldb/source/Target/CMakeLists.txt @@ -63,6 +63,7 @@ ThreadPlanStepThrough.cpp ThreadPlanStepUntil.cpp ThreadPlanTracer.cpp + ThreadPlanStack.cpp ThreadSpec.cpp UnixSignals.cpp UnwindAssembly.cpp diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -60,6 +60,7 @@ #include "lldb/Target/ThreadPlan.h" #include "lldb/Target/ThreadPlanBase.h" #include "lldb/Target/ThreadPlanCallFunction.h" +#include "lldb/Target/ThreadPlanStack.h" #include "lldb/Target/UnixSignals.h" #include "lldb/Utility/Event.h" #include "lldb/Utility/Log.h" @@ -600,6 +601,7 @@ 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 @@ } } +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::Destroy(bool force_kill) { + // If we've already called Process::Finalize then there's nothing useful to + // be done here. Finalize has actually called Destroy already. + if (m_finalize_called) + return {}; // Tell ourselves we are in the process of destroying the process, so that we // don't do any unnecessary work that might hinder the destruction. Remember diff --git a/lldb/source/Target/Thread.cpp b/lldb/source/Target/Thread.cpp --- a/lldb/source/Target/Thread.cpp +++ b/lldb/source/Target/Thread.cpp @@ -33,6 +33,7 @@ #include "lldb/Target/ThreadPlanCallFunction.h" #include "lldb/Target/ThreadPlanPython.h" #include "lldb/Target/ThreadPlanRunToAddress.h" +#include "lldb/Target/ThreadPlanStack.h" #include "lldb/Target/ThreadPlanStepInRange.h" #include "lldb/Target/ThreadPlanStepInstruction.h" #include "lldb/Target/ThreadPlanStepOut.h" @@ -228,8 +229,7 @@ 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 @@ static_cast(this), GetID()); CheckInWithManager(); - + + process.AddThreadPlansForThread(*this); + QueueFundamentalPlan(true); } @@ -254,30 +256,7 @@ } 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ // 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 @@ // 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 @@ 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 @@ 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 @@ } 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(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 @@ 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(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(this), s.GetData(), + thread_plan_sp->GetThread().GetID()); } + + GetPlans().PushPlan(std::move(thread_plan_sp)); } void Thread::PopPlan() { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); - - if (m_plan_stack.size() <= 1) - return; - else { - ThreadPlanSP &plan = m_plan_stack.back(); - if (log) { - LLDB_LOGF(log, "Popping plan: \"%s\", tid = 0x%4.4" PRIx64 ".", - plan->GetName(), plan->GetThread().GetID()); - } - m_completed_plan_stack.push_back(plan); - plan->WillPop(); - m_plan_stack.pop_back(); + ThreadPlanSP popped_plan_sp = GetPlans().PopPlan(); + if (log) { + LLDB_LOGF(log, "Popping plan: \"%s\", tid = 0x%4.4" PRIx64 ".", + popped_plan_sp->GetName(), popped_plan_sp->GetThread().GetID()); } } void Thread::DiscardPlan() { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); - if (m_plan_stack.size() > 1) { - ThreadPlanSP &plan = m_plan_stack.back(); - LLDB_LOGF(log, "Discarding plan: \"%s\", tid = 0x%4.4" PRIx64 ".", - plan->GetName(), plan->GetThread().GetID()); - - m_discarded_plan_stack.push_back(plan); - plan->WillPop(); - m_plan_stack.pop_back(); - } -} + ThreadPlanSP discarded_plan_sp = GetPlans().PopPlan(); -ThreadPlan *Thread::GetCurrentPlan() { - // There will always be at least the base plan. If somebody is mucking with - // a thread with an empty plan stack, we should assert right away. - return m_plan_stack.empty() ? nullptr : m_plan_stack.back().get(); + LLDB_LOGF(log, "Discarding plan: \"%s\", tid = 0x%4.4" PRIx64 ".", + discarded_plan_sp->GetName(), + discarded_plan_sp->GetThread().GetID()); } -ThreadPlanSP Thread::GetCompletedPlan() { - ThreadPlanSP empty_plan_sp; - if (!m_completed_plan_stack.empty()) { - for (int i = m_completed_plan_stack.size() - 1; i >= 0; i--) { - ThreadPlanSP completed_plan_sp; - completed_plan_sp = m_completed_plan_stack[i]; - if (!completed_plan_sp->GetPrivate()) - return completed_plan_sp; - } - } - return empty_plan_sp; +ThreadPlan *Thread::GetCurrentPlan() const { + return GetPlans().GetCurrentPlan().get(); } -ValueObjectSP Thread::GetReturnValueObject() { - if (!m_completed_plan_stack.empty()) { - for (int i = m_completed_plan_stack.size() - 1; i >= 0; i--) { - ValueObjectSP return_valobj_sp; - return_valobj_sp = m_completed_plan_stack[i]->GetReturnValueObject(); - if (return_valobj_sp) - return return_valobj_sp; - } - } - return ValueObjectSP(); +ThreadPlanSP Thread::GetCompletedPlan() const { + return GetPlans().GetCompletedPlan(); } -ExpressionVariableSP Thread::GetExpressionVariable() { - if (!m_completed_plan_stack.empty()) { - for (int i = m_completed_plan_stack.size() - 1; i >= 0; i--) { - ExpressionVariableSP expression_variable_sp; - expression_variable_sp = - m_completed_plan_stack[i]->GetExpressionVariable(); - if (expression_variable_sp) - return expression_variable_sp; - } - } - return ExpressionVariableSP(); +ValueObjectSP Thread::GetReturnValueObject() const { + return GetPlans().GetReturnValueObject(); } -bool Thread::IsThreadPlanDone(ThreadPlan *plan) { - if (!m_completed_plan_stack.empty()) { - for (int i = m_completed_plan_stack.size() - 1; i >= 0; i--) { - if (m_completed_plan_stack[i].get() == plan) - return true; - } - } - return false; +ExpressionVariableSP Thread::GetExpressionVariable() const { + return GetPlans().GetExpressionVariable(); } -bool Thread::WasThreadPlanDiscarded(ThreadPlan *plan) { - if (!m_discarded_plan_stack.empty()) { - for (int i = m_discarded_plan_stack.size() - 1; i >= 0; i--) { - if (m_discarded_plan_stack[i].get() == plan) - return true; - } - } - return false; +bool Thread::IsThreadPlanDone(ThreadPlan *plan) const { + return GetPlans().IsPlanDone(plan); } -bool Thread::CompletedPlanOverridesBreakpoint() { - return (!m_completed_plan_stack.empty()) ; +bool Thread::WasThreadPlanDiscarded(ThreadPlan *plan) const { + return GetPlans().WasPlanDiscarded(plan); } -ThreadPlan *Thread::GetPreviousPlan(ThreadPlan *current_plan) { - if (current_plan == nullptr) - return nullptr; - - int stack_size = m_completed_plan_stack.size(); - for (int i = stack_size - 1; i > 0; i--) { - if (current_plan == m_completed_plan_stack[i].get()) - return m_completed_plan_stack[i - 1].get(); - } - - if (stack_size > 0 && m_completed_plan_stack[0].get() == current_plan) { - return GetCurrentPlan(); - } +bool Thread::CompletedPlanOverridesBreakpoint() const { + return GetPlans().AnyCompletedPlans(); +} - stack_size = m_plan_stack.size(); - for (int i = stack_size - 1; i > 0; i--) { - if (current_plan == m_plan_stack[i].get()) - return m_plan_stack[i - 1].get(); - } - return nullptr; +ThreadPlan *Thread::GetPreviousPlan(ThreadPlan *current_plan) const{ + return GetPlans().GetPreviousPlan(current_plan); } Status Thread::QueueThreadPlan(ThreadPlanSP &thread_plan_sp, @@ -1242,38 +1151,18 @@ } 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 @@ "Discarding thread plans for thread tid = 0x%4.4" PRIx64 ", up to %p", GetID(), static_cast(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 @@ } 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 @@ 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 &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 @@ return; } } - + s->Indent(); s->Printf("thread #%u: tid = 0x%4.4" PRIx64 ":\n", GetIndexID(), GetID()); - s->IndentMore(); - s->Indent(); - s->Printf("Active plan stack:\n"); - PrintPlanStack(s, m_plan_stack, desc_level, include_internal); - - stack_size = m_completed_plan_stack.size(); - if (stack_size > 0) { - s->Indent(); - s->Printf("Completed Plan Stack:\n"); - PrintPlanStack(s, m_completed_plan_stack, desc_level, include_internal); - } - - stack_size = m_discarded_plan_stack.size(); - if (stack_size > 0) { - s->Indent(); - s->Printf("Discarded Plan Stack:\n"); - PrintPlanStack(s, m_discarded_plan_stack, desc_level, include_internal); - } - - s->IndentLess(); + GetPlans().DumpThreadPlans(s, desc_level, include_internal); } TargetSP Thread::CalculateTarget() { diff --git a/lldb/source/Target/ThreadList.cpp b/lldb/source/Target/ThreadList.cpp --- a/lldb/source/Target/ThreadList.cpp +++ b/lldb/source/Target/ThreadList.cpp @@ -726,8 +726,10 @@ break; } } - if (!thread_is_alive) + if (!thread_is_alive) { (*rhs_pos)->DestroyThread(); + m_process->RemoveThreadPlansForTID((*rhs_pos)->GetID()); + } } } } diff --git a/lldb/source/Target/ThreadPlan.cpp b/lldb/source/Target/ThreadPlan.cpp --- a/lldb/source/Target/ThreadPlan.cpp +++ b/lldb/source/Target/ThreadPlan.cpp @@ -34,6 +34,10 @@ // Destructor ThreadPlan::~ThreadPlan() = default; +Target &ThreadPlan::GetTarget() { return m_process.GetTarget(); } + +const Target &ThreadPlan::GetTarget() const { return m_process.GetTarget(); } + Thread &ThreadPlan::GetThread() { if (m_thread) return *m_thread; diff --git a/lldb/source/Target/ThreadPlanStack.cpp b/lldb/source/Target/ThreadPlanStack.cpp new file mode 100644 --- /dev/null +++ b/lldb/source/Target/ThreadPlanStack.cpp @@ -0,0 +1,370 @@ +//===-- ThreadPlanStack.cpp -------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Target/ThreadPlanStack.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlan.h" +#include "lldb/Utility/Log.h" + +using namespace lldb; +using namespace lldb_private; + +static void PrintPlanElement(Stream *s, const ThreadPlanSP &plan, + lldb::DescriptionLevel desc_level, + int32_t elem_idx) { + s->IndentMore(); + s->Indent(); + s->Printf("Element %d: ", elem_idx); + plan->GetDescription(s, desc_level); + s->EOL(); + s->IndentLess(); +} + +void ThreadPlanStack::DumpThreadPlans(Stream *s, + lldb::DescriptionLevel desc_level, + bool include_internal) const { + + uint32_t stack_size; + + s->IndentMore(); + s->Indent(); + s->Printf("Active plan stack:\n"); + int32_t print_idx = 0; + for (auto plan : m_plans) { + PrintPlanElement(s, plan, desc_level, print_idx++); + } + + if (AnyCompletedPlans()) { + print_idx = 0; + s->Indent(); + s->Printf("Completed Plan Stack:\n"); + for (auto plan : m_completed_plans) + PrintPlanElement(s, plan, desc_level, print_idx++); + } + + if (AnyDiscardedPlans()) { + print_idx = 0; + s->Indent(); + s->Printf("Discarded Plan Stack:\n"); + for (auto plan : m_discarded_plans) + PrintPlanElement(s, plan, desc_level, print_idx++); + } + + s->IndentLess(); +} + +size_t ThreadPlanStack::CheckpointCompletedPlans() { + m_completed_plan_checkpoint++; + m_completed_plan_store.insert( + std::make_pair(m_completed_plan_checkpoint, m_completed_plans)); + return m_completed_plan_checkpoint; +} + +void ThreadPlanStack::RestoreCompletedPlanCheckpoint(size_t checkpoint) { + auto result = m_completed_plan_store.find(checkpoint); + assert(result != m_completed_plan_store.end() && + "Asked for a checkpoint that didn't exist"); + m_completed_plans.swap((*result).second); + m_completed_plan_store.erase(result); +} + +void ThreadPlanStack::DiscardCompletedPlanCheckpoint(size_t checkpoint) { + m_completed_plan_store.erase(checkpoint); +} + +void ThreadPlanStack::ThreadDestroyed(Thread *thread) { + // Tell the plan stacks that this thread is going away: + for (ThreadPlanSP plan : m_plans) + plan->ThreadDestroyed(); + + for (ThreadPlanSP plan : m_discarded_plans) + plan->ThreadDestroyed(); + + for (ThreadPlanSP plan : m_completed_plans) + plan->ThreadDestroyed(); + + // Now clear the current plan stacks: + m_plans.clear(); + m_discarded_plans.clear(); + m_completed_plans.clear(); + + // Push a ThreadPlanNull on the plan stack. That way we can continue + // assuming that the plan stack is never empty, but if somebody errantly asks + // questions of a destroyed thread without checking first whether it is + // destroyed, they won't crash. + if (thread != nullptr) { + lldb::ThreadPlanSP null_plan_sp(new ThreadPlanNull(*thread)); + m_plans.push_back(null_plan_sp); + } +} + +void ThreadPlanStack::EnableTracer(bool value, bool single_stepping) { + for (ThreadPlanSP plan : m_plans) { + if (plan->GetThreadPlanTracer()) { + plan->GetThreadPlanTracer()->EnableTracing(value); + plan->GetThreadPlanTracer()->EnableSingleStep(single_stepping); + } + } +} + +void ThreadPlanStack::SetTracer(lldb::ThreadPlanTracerSP &tracer_sp) { + for (ThreadPlanSP plan : m_plans) + plan->SetThreadPlanTracer(tracer_sp); +} + +void ThreadPlanStack::PushPlan(lldb::ThreadPlanSP new_plan_sp) { + // If the thread plan doesn't already have a tracer, give it its parent's + // tracer: + // The first plan has to be a base plan: + assert(m_plans.size() > 0 || + new_plan_sp->IsBasePlan() && "Zeroth plan must be a base plan"); + + if (!new_plan_sp->GetThreadPlanTracer()) { + assert(!m_plans.empty()); + new_plan_sp->SetThreadPlanTracer(m_plans.back()->GetThreadPlanTracer()); + } + m_plans.push_back(new_plan_sp); + new_plan_sp->DidPush(); +} + +lldb::ThreadPlanSP ThreadPlanStack::PopPlan() { + assert(m_plans.size() > 1 && "Can't pop the base thread plan"); + + lldb::ThreadPlanSP &plan_sp = m_plans.back(); + m_completed_plans.push_back(plan_sp); + plan_sp->WillPop(); + m_plans.pop_back(); + return plan_sp; +} + +lldb::ThreadPlanSP ThreadPlanStack::DiscardPlan() { + assert(m_plans.size() > 1 && "Can't discard the base thread plan"); + + lldb::ThreadPlanSP &plan_sp = m_plans.back(); + m_discarded_plans.push_back(plan_sp); + plan_sp->WillPop(); + m_plans.pop_back(); + return plan_sp; +} + +// If the input plan is nullptr, discard all plans. Otherwise make sure this +// plan is in the stack, and if so discard up to and including it. +void ThreadPlanStack::DiscardPlansUpToPlan(ThreadPlan *up_to_plan_ptr) { + int stack_size = m_plans.size(); + + if (up_to_plan_ptr == nullptr) { + for (int i = stack_size - 1; i > 0; i--) + DiscardPlan(); + return; + } + + bool found_it = false; + for (int i = stack_size - 1; i > 0; i--) { + if (m_plans[i].get() == up_to_plan_ptr) { + found_it = true; + break; + } + } + + if (found_it) { + bool last_one = false; + for (int i = stack_size - 1; i > 0 && !last_one; i--) { + if (GetCurrentPlan().get() == up_to_plan_ptr) + last_one = true; + DiscardPlan(); + } + } +} + +void ThreadPlanStack::DiscardAllPlans() { + int stack_size = m_plans.size(); + for (int i = stack_size - 1; i > 0; i--) { + DiscardPlan(); + } + return; +} + +void ThreadPlanStack::DiscardConsultingMasterPlans() { + while (true) { + int master_plan_idx; + bool discard = true; + + // Find the first master plan, see if it wants discarding, and if yes + // discard up to it. + for (master_plan_idx = m_plans.size() - 1; master_plan_idx >= 0; + master_plan_idx--) { + if (m_plans[master_plan_idx]->IsMasterPlan()) { + discard = m_plans[master_plan_idx]->OkayToDiscard(); + break; + } + } + + // If the master plan doesn't want to get discarded, then we're done. + if (!discard) + return; + + // First pop all the dependent plans: + for (int i = m_plans.size() - 1; i > master_plan_idx; i--) { + DiscardPlan(); + } + + // Now discard the master plan itself. + // The bottom-most plan never gets discarded. "OkayToDiscard" for it + // means discard it's dependent plans, but not it... + if (master_plan_idx > 0) { + DiscardPlan(); + } + } +} + +lldb::ThreadPlanSP ThreadPlanStack::GetCurrentPlan() const { + assert(m_plans.size() != 0 && "There will always be a base plan."); + return m_plans.back(); +} + +lldb::ThreadPlanSP ThreadPlanStack::GetCompletedPlan(bool skip_private) const { + if (m_completed_plans.empty()) + return {}; + + if (!skip_private) + return m_completed_plans.back(); + + for (int i = m_completed_plans.size() - 1; i >= 0; i--) { + lldb::ThreadPlanSP completed_plan_sp; + completed_plan_sp = m_completed_plans[i]; + if (!completed_plan_sp->GetPrivate()) + return completed_plan_sp; + } + return {}; +} + +lldb::ThreadPlanSP ThreadPlanStack::GetPlanByIndex(uint32_t plan_idx, + bool skip_private) const { + uint32_t idx = 0; + ThreadPlan *up_to_plan_ptr = nullptr; + + for (lldb::ThreadPlanSP plan_sp : m_plans) { + if (skip_private && plan_sp->GetPrivate()) + continue; + if (idx == plan_idx) + return plan_sp; + idx++; + } + return {}; +} + +lldb::ValueObjectSP ThreadPlanStack::GetReturnValueObject() const { + if (m_completed_plans.empty()) + return {}; + + for (int i = m_completed_plans.size() - 1; i >= 0; i--) { + lldb::ValueObjectSP return_valobj_sp; + return_valobj_sp = m_completed_plans[i]->GetReturnValueObject(); + if (return_valobj_sp) + return return_valobj_sp; + } + return {}; +} + +lldb::ExpressionVariableSP ThreadPlanStack::GetExpressionVariable() const { + if (m_completed_plans.empty()) + return {}; + + for (int i = m_completed_plans.size() - 1; i >= 0; i--) { + lldb::ExpressionVariableSP expression_variable_sp; + expression_variable_sp = m_completed_plans[i]->GetExpressionVariable(); + if (expression_variable_sp) + return expression_variable_sp; + } + return {}; +} +bool ThreadPlanStack::AnyPlans() const { + // There is always a base plan... + return m_plans.size() > 1; +} + +bool ThreadPlanStack::AnyCompletedPlans() const { + return !m_completed_plans.empty(); +} + +bool ThreadPlanStack::AnyDiscardedPlans() const { + return !m_discarded_plans.empty(); +} + +bool ThreadPlanStack::IsPlanDone(ThreadPlan *in_plan) const { + for (auto plan : m_completed_plans) { + if (plan.get() == in_plan) + return true; + } + return false; +} + +bool ThreadPlanStack::WasPlanDiscarded(ThreadPlan *in_plan) const { + for (auto plan : m_discarded_plans) { + if (plan.get() == in_plan) + return true; + } + return false; +} + +ThreadPlan *ThreadPlanStack::GetPreviousPlan(ThreadPlan *current_plan) const { + if (current_plan == nullptr) + return nullptr; + + // Look first in the completed plans, if the plan is here and there is + // a completed plan above it, return that. + int stack_size = m_completed_plans.size(); + for (int i = stack_size - 1; i > 0; i--) { + if (current_plan == m_completed_plans[i].get()) + return m_completed_plans[i - 1].get(); + } + + // If this is the first completed plan, the previous one is the + // bottom of the regular plan stack. + if (stack_size > 0 && m_completed_plans[0].get() == current_plan) { + return GetCurrentPlan().get(); + } + + // Otherwise look for it in the regular plans. + stack_size = m_plans.size(); + for (int i = stack_size - 1; i > 0; i--) { + if (current_plan == m_plans[i].get()) + return m_plans[i - 1].get(); + } + return nullptr; +} + +ThreadPlan *ThreadPlanStack::GetInnermostExpression() const { + int stack_size = m_plans.size(); + + for (int i = stack_size - 1; i > 0; i--) { + if (m_plans[i]->GetKind() == ThreadPlan::eKindCallFunction) + return m_plans[i].get(); + } + return nullptr; +} + +void ThreadPlanStack::WillResume() { + m_completed_plans.clear(); + m_discarded_plans.clear(); +} + +const ThreadPlanStack::PlanStack & +ThreadPlanStack::GetStackOfKind(ThreadPlanStack::StackKind kind) const { + switch (kind) { + case ePlans: + return m_plans; + case eCompletedPlans: + return m_completed_plans; + case eDiscardedPlans: + return m_discarded_plans; + } + llvm_unreachable("Invalid StackKind value"); +}