diff --git a/lldb/include/lldb/Target/ThreadPlan.h b/lldb/include/lldb/Target/ThreadPlan.h index b7c63e983d62e..29698e54dac34 100644 --- a/lldb/include/lldb/Target/ThreadPlan.h +++ b/lldb/include/lldb/Target/ThreadPlan.h @@ -81,7 +81,7 @@ namespace lldb_private { // // Cleaning up after your plans: // -// When the plan is moved from the plan stack its WillPop method is always +// When the plan is moved from the plan stack its DidPop method is always // called, no matter why. Once it is moved off the plan stack it is done, and // won't get a chance to run again. So you should undo anything that affects // target state in this method. But be sure to leave the plan able to @@ -414,7 +414,7 @@ class ThreadPlan : public std::enable_shared_from_this, virtual void DidPush(); - virtual void WillPop(); + virtual void DidPop(); // This pushes a plan onto the plan stack of the current plan's thread. // Also sets the plans to private and not master plans. A plan pushed by diff --git a/lldb/include/lldb/Target/ThreadPlanCallFunction.h b/lldb/include/lldb/Target/ThreadPlanCallFunction.h index b1c6740e3204b..eb413edd9c9fb 100644 --- a/lldb/include/lldb/Target/ThreadPlanCallFunction.h +++ b/lldb/include/lldb/Target/ThreadPlanCallFunction.h @@ -69,10 +69,10 @@ class ThreadPlanCallFunction : public ThreadPlan { // been cleaned up. lldb::addr_t GetFunctionStackPointer() { return m_function_sp; } - // Classes that derive from FunctionCaller, and implement their own WillPop + // Classes that derive from FunctionCaller, and implement their own DidPop // methods should call this so that the thread state gets restored if the // plan gets discarded. - void WillPop() override; + void DidPop() override; // If the thread plan stops mid-course, this will be the stop reason that // interrupted us. Once DoTakedown is called, this will be the real stop diff --git a/lldb/include/lldb/Target/ThreadPlanCallUserExpression.h b/lldb/include/lldb/Target/ThreadPlanCallUserExpression.h index adaea6c7056fe..11e126a2da9cb 100644 --- a/lldb/include/lldb/Target/ThreadPlanCallUserExpression.h +++ b/lldb/include/lldb/Target/ThreadPlanCallUserExpression.h @@ -32,7 +32,7 @@ class ThreadPlanCallUserExpression : public ThreadPlanCallFunction { void DidPush() override; - void WillPop() override; + void DidPop() override; lldb::StopInfoSP GetRealStopInfo() override; diff --git a/lldb/include/lldb/Target/ThreadPlanStack.h b/lldb/include/lldb/Target/ThreadPlanStack.h index 15446ae768f7a..964d5dfec4881 100644 --- a/lldb/include/lldb/Target/ThreadPlanStack.h +++ b/lldb/include/lldb/Target/ThreadPlanStack.h @@ -95,7 +95,9 @@ class ThreadPlanStack { void WillResume(); - bool IsTID(lldb::tid_t tid); + bool IsTID(lldb::tid_t tid) { + return GetTID() == tid; + } lldb::tid_t GetTID(); void SetTID(lldb::tid_t tid); @@ -116,6 +118,10 @@ class ThreadPlanStack { size_t m_completed_plan_checkpoint = 0; // Monotonically increasing token for // completed plan checkpoints. std::unordered_map m_completed_plan_store; + mutable std::recursive_mutex m_stack_mutex; + + // ThreadPlanStacks shouldn't be copied. + ThreadPlanStack(ThreadPlanStack &rhs) = delete; }; class ThreadPlanStackMap { @@ -129,15 +135,34 @@ class ThreadPlanStackMap { void AddThread(Thread &thread) { lldb::tid_t tid = thread.GetID(); - m_plans_list.emplace(tid, thread); + std::shared_ptr new_elem_sp(new ThreadPlanStack(thread)); + m_plans_sp_container.push_back(new_elem_sp); + m_plans_list.emplace(tid, new_elem_sp.get()); } 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); + ThreadPlanStack *removed_stack = result->second; m_plans_list.erase(result); + // Also remove this from the stack storage: + PlansStore::iterator end + = m_plans_sp_container.end(); + PlansStore::iterator iter; + for (iter = m_plans_sp_container.begin(); + iter != end; iter++) { + if ((*iter)->IsTID(tid)) { + break; + } + } + if (iter == end) + return false; + + // Finally, tell the stack its thread has been destroyed: + removed_stack->ThreadDestroyed(nullptr); + // And THEN remove it from the container so it goes away. + m_plans_sp_container.erase(iter); return true; } @@ -146,38 +171,49 @@ class ThreadPlanStackMap { if (result == m_plans_list.end()) return nullptr; else - return &result->second; + return result->second; } // rename to Reactivate? - void Activate(ThreadPlanStack &&stack) { + void Activate(ThreadPlanStack &stack) { + // Remove this from the detached plan list: + std::vector::iterator end = m_detached_plans.end(); + + for (std::vector::iterator iter + = m_detached_plans.begin(); iter != end; iter++) { + if (*iter == &stack) { + m_detached_plans.erase(iter); + break; + } + } + if (m_plans_list.find(stack.GetTID()) == m_plans_list.end()) - m_plans_list.emplace(stack.GetTID(), std::move(stack)); + m_plans_list.emplace(stack.GetTID(), &stack); else - m_plans_list.at(stack.GetTID()) = std::move(stack); + m_plans_list.at(stack.GetTID()) = &stack; } - // rename to ...? - std::vector CleanUp() { + void ScanForDetachedPlanStacks() { llvm::SmallVector invalidated_tids; for (auto &pair : m_plans_list) - if (pair.second.GetTID() != pair.first) + if (pair.second->GetTID() != pair.first) invalidated_tids.push_back(pair.first); - std::vector detached_stacks; - detached_stacks.reserve(invalidated_tids.size()); for (auto tid : invalidated_tids) { auto it = m_plans_list.find(tid); - auto stack = std::move(it->second); + ThreadPlanStack *stack = it->second; m_plans_list.erase(it); - detached_stacks.emplace_back(std::move(stack)); + m_detached_plans.push_back(stack); } - return detached_stacks; + } + + std::vector &GetDetachedPlanStacks() { + return m_detached_plans; } void Clear() { for (auto &plan : m_plans_list) - plan.second.ThreadDestroyed(nullptr); + plan.second->ThreadDestroyed(nullptr); m_plans_list.clear(); } @@ -194,7 +230,23 @@ class ThreadPlanStackMap { private: Process &m_process; - using PlansList = std::unordered_map; + // We don't want to make copies of these ThreadPlanStacks, there needs to be + // just one of these tracking each piece of work. But we need to move the + // work from "attached to a TID" state to "detached" state, which is most + // conveniently done by having separate containers for the two states. + // To do that w/o having to spend effort fighting with C++'s desire to invoke + // copy constructors, we keep a store for the real object, and then keep + // pointers to the objects in the two containers. The store actually holds + // shared_ptrs to the objects, so that the pointers in the containers will stay + // valid. + + // Use a shared pointer rather than unique_ptr here to prevent C++ from trying + // to use a copy constructor on the stored type. + using PlansStore = std::vector>; + PlansStore m_plans_sp_container; + std::vector m_detached_plans; + + using PlansList = std::unordered_map; PlansList m_plans_list; }; diff --git a/lldb/include/lldb/Target/ThreadPlanStepOverBreakpoint.h b/lldb/include/lldb/Target/ThreadPlanStepOverBreakpoint.h index 86f7798487c30..1f3aff45c49ab 100644 --- a/lldb/include/lldb/Target/ThreadPlanStepOverBreakpoint.h +++ b/lldb/include/lldb/Target/ThreadPlanStepOverBreakpoint.h @@ -26,7 +26,7 @@ class ThreadPlanStepOverBreakpoint : public ThreadPlan { bool StopOthers() override; lldb::StateType GetPlanRunState() override; bool WillStop() override; - void WillPop() override; + void DidPop() override; bool MischiefManaged() override; void ThreadDestroyed() override; void SetAutoContinue(bool do_it); diff --git a/lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntimeNames.cpp b/lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntimeNames.cpp index e7143e61165de..f048798195afd 100644 --- a/lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntimeNames.cpp +++ b/lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntimeNames.cpp @@ -319,7 +319,7 @@ class ThreadPlanStepInAsync : public ThreadPlan { bool StopOthers() override { return false; } - void WillPop() override { + void DidPop() override { if (m_async_breakpoint_sp) m_async_breakpoint_sp->GetTarget().RemoveBreakpointByID( m_async_breakpoint_sp->GetID()); diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp index 9f2b448e617df..a0e2b66735f00 100644 --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -1453,25 +1453,23 @@ void Process::UpdateThreadListIfNeeded() { } void Process::SynchronizeThreadPlans() { - for (auto &stack : m_thread_plans.CleanUp()) - m_async_thread_plans.emplace_back(std::move(stack)); + m_thread_plans.ScanForDetachedPlanStacks(); } ThreadPlanSP Process::FindDetachedPlanExplainingStop(Thread &thread, Event *event_ptr) { - auto end = m_async_thread_plans.end(); - for (auto it = m_async_thread_plans.begin(); it != end; ++it) { - auto plan_sp = it->GetCurrentPlan(); + std::vector &detached_plans + = m_thread_plans.GetDetachedPlanStacks(); + size_t num_detached_plans = detached_plans.size(); + for (size_t idx = 0; idx < num_detached_plans; idx++) { + ThreadPlanStack *cur_stack = detached_plans[idx]; + ThreadPlanSP plan_sp = cur_stack->GetCurrentPlan(); plan_sp->SetTID(thread.GetID()); if (!plan_sp->DoPlanExplainsStop(event_ptr)) { plan_sp->ClearTID(); continue; } - - auto stack = std::move(*it); - m_async_thread_plans.erase(it); - stack.SetTID(plan_sp->GetTID()); - m_thread_plans.Activate(std::move(stack)); + m_thread_plans.Activate(*cur_stack); return plan_sp; } return {}; diff --git a/lldb/source/Target/ThreadPlan.cpp b/lldb/source/Target/ThreadPlan.cpp index d8e92b8fd0de6..e2efb226c5ead 100644 --- a/lldb/source/Target/ThreadPlan.cpp +++ b/lldb/source/Target/ThreadPlan.cpp @@ -147,7 +147,7 @@ lldb::user_id_t ThreadPlan::GetNextID() { void ThreadPlan::DidPush() {} -void ThreadPlan::WillPop() {} +void ThreadPlan::DidPop() {} bool ThreadPlan::OkayToDiscard() { return IsMasterPlan() ? m_okay_to_discard : true; diff --git a/lldb/source/Target/ThreadPlanCallFunction.cpp b/lldb/source/Target/ThreadPlanCallFunction.cpp index 895bd574f9b47..1ef3e4acd2217 100644 --- a/lldb/source/Target/ThreadPlanCallFunction.cpp +++ b/lldb/source/Target/ThreadPlanCallFunction.cpp @@ -215,7 +215,7 @@ void ThreadPlanCallFunction::DoTakedown(bool success) { } } -void ThreadPlanCallFunction::WillPop() { DoTakedown(PlanSucceeded()); } +void ThreadPlanCallFunction::DidPop() { DoTakedown(PlanSucceeded()); } void ThreadPlanCallFunction::GetDescription(Stream *s, DescriptionLevel level) { if (level == eDescriptionLevelBrief) { diff --git a/lldb/source/Target/ThreadPlanCallUserExpression.cpp b/lldb/source/Target/ThreadPlanCallUserExpression.cpp index 00b01c76d9008..ab75ec765c2fb 100644 --- a/lldb/source/Target/ThreadPlanCallUserExpression.cpp +++ b/lldb/source/Target/ThreadPlanCallUserExpression.cpp @@ -59,8 +59,8 @@ void ThreadPlanCallUserExpression::DidPush() { m_user_expression_sp->WillStartExecuting(); } -void ThreadPlanCallUserExpression::WillPop() { - ThreadPlanCallFunction::WillPop(); +void ThreadPlanCallUserExpression::DidPop() { + ThreadPlanCallFunction::DidPop(); if (m_user_expression_sp) m_user_expression_sp.reset(); } diff --git a/lldb/source/Target/ThreadPlanStack.cpp b/lldb/source/Target/ThreadPlanStack.cpp index 9ecf4be22c524..4631b5033bf36 100644 --- a/lldb/source/Target/ThreadPlanStack.cpp +++ b/lldb/source/Target/ThreadPlanStack.cpp @@ -39,6 +39,7 @@ ThreadPlanStack::ThreadPlanStack(const Thread &thread, bool make_null) { void ThreadPlanStack::DumpThreadPlans(Stream &s, lldb::DescriptionLevel desc_level, bool include_internal) const { + std::lock_guard guard(m_stack_mutex); s.IndentMore(); PrintOneStack(s, "Active plan stack", m_plans, desc_level, include_internal); PrintOneStack(s, "Completed plan stack", m_completed_plans, desc_level, @@ -52,6 +53,7 @@ void ThreadPlanStack::PrintOneStack(Stream &s, llvm::StringRef stack_name, const PlanStack &stack, lldb::DescriptionLevel desc_level, bool include_internal) const { + std::lock_guard guard(m_stack_mutex); // If the stack is empty, just exit: if (stack.empty()) return; @@ -80,6 +82,7 @@ void ThreadPlanStack::PrintOneStack(Stream &s, llvm::StringRef stack_name, } size_t ThreadPlanStack::CheckpointCompletedPlans() { + std::lock_guard guard(m_stack_mutex); m_completed_plan_checkpoint++; m_completed_plan_store.insert( std::make_pair(m_completed_plan_checkpoint, m_completed_plans)); @@ -87,6 +90,7 @@ size_t ThreadPlanStack::CheckpointCompletedPlans() { } void ThreadPlanStack::RestoreCompletedPlanCheckpoint(size_t checkpoint) { + std::lock_guard guard(m_stack_mutex); auto result = m_completed_plan_store.find(checkpoint); assert(result != m_completed_plan_store.end() && "Asked for a checkpoint that didn't exist"); @@ -95,11 +99,13 @@ void ThreadPlanStack::RestoreCompletedPlanCheckpoint(size_t checkpoint) { } void ThreadPlanStack::DiscardCompletedPlanCheckpoint(size_t checkpoint) { + std::lock_guard guard(m_stack_mutex); m_completed_plan_store.erase(checkpoint); } void ThreadPlanStack::ThreadDestroyed(Thread *thread) { // Tell the plan stacks that this thread is going away: + std::lock_guard guard(m_stack_mutex); for (ThreadPlanSP plan : m_plans) plan->ThreadDestroyed(); @@ -142,6 +148,7 @@ 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: + std::lock_guard guard(m_stack_mutex); assert((m_plans.size() > 0 || new_plan_sp->IsBasePlan()) && "Zeroth plan must be a base plan"); @@ -154,28 +161,37 @@ void ThreadPlanStack::PushPlan(lldb::ThreadPlanSP new_plan_sp) { } lldb::ThreadPlanSP ThreadPlanStack::PopPlan() { + std::lock_guard guard(m_stack_mutex); assert(m_plans.size() > 1 && "Can't pop the base thread plan"); - lldb::ThreadPlanSP plan_sp = std::move(m_plans.back()); - m_completed_plans.push_back(plan_sp); - plan_sp->WillPop(); + // Note that moving the top element of the vector would leave it in an + // undefined state, and break the guarantee that the stack's thread plans are + // all valid. + lldb::ThreadPlanSP plan_sp = m_plans.back(); m_plans.pop_back(); + m_completed_plans.push_back(plan_sp); + plan_sp->DidPop(); return plan_sp; } lldb::ThreadPlanSP ThreadPlanStack::DiscardPlan() { + std::lock_guard guard(m_stack_mutex); assert(m_plans.size() > 1 && "Can't discard the base thread plan"); - lldb::ThreadPlanSP plan_sp = std::move(m_plans.back()); - m_discarded_plans.push_back(plan_sp); - plan_sp->WillPop(); + // Note that moving the top element of the vector would leave it in an + // undefined state, and break the guarantee that the stack's thread plans are + // all valid. + lldb::ThreadPlanSP plan_sp = m_plans.back(); m_plans.pop_back(); + m_discarded_plans.push_back(plan_sp); + plan_sp->DidPop(); 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) { + std::lock_guard guard(m_stack_mutex); int stack_size = m_plans.size(); if (up_to_plan_ptr == nullptr) { @@ -203,6 +219,7 @@ void ThreadPlanStack::DiscardPlansUpToPlan(ThreadPlan *up_to_plan_ptr) { } void ThreadPlanStack::DiscardAllPlans() { + std::lock_guard guard(m_stack_mutex); int stack_size = m_plans.size(); for (int i = stack_size - 1; i > 0; i--) { DiscardPlan(); @@ -211,6 +228,7 @@ void ThreadPlanStack::DiscardAllPlans() { } void ThreadPlanStack::DiscardConsultingMasterPlans() { + std::lock_guard guard(m_stack_mutex); while (true) { int master_plan_idx; bool discard = true; @@ -244,11 +262,13 @@ void ThreadPlanStack::DiscardConsultingMasterPlans() { } lldb::ThreadPlanSP ThreadPlanStack::GetCurrentPlan() const { + std::lock_guard guard(m_stack_mutex); assert(m_plans.size() != 0 && "There will always be a base plan."); return m_plans.back(); } lldb::ThreadPlanSP ThreadPlanStack::GetCompletedPlan(bool skip_private) const { + std::lock_guard guard(m_stack_mutex); if (m_completed_plans.empty()) return {}; @@ -266,6 +286,7 @@ lldb::ThreadPlanSP ThreadPlanStack::GetCompletedPlan(bool skip_private) const { lldb::ThreadPlanSP ThreadPlanStack::GetPlanByIndex(uint32_t plan_idx, bool skip_private) const { + std::lock_guard guard(m_stack_mutex); uint32_t idx = 0; for (lldb::ThreadPlanSP plan_sp : m_plans) { @@ -278,8 +299,9 @@ lldb::ThreadPlanSP ThreadPlanStack::GetPlanByIndex(uint32_t plan_idx, return {}; } -lldb::ValueObjectSP +lldb::ValueObjectSP ThreadPlanStack::GetReturnValueObject(bool &is_error) const { + std::lock_guard guard(m_stack_mutex); if (m_completed_plans.empty()) return {}; @@ -295,6 +317,7 @@ ThreadPlanStack::GetReturnValueObject(bool &is_error) const { } lldb::ExpressionVariableSP ThreadPlanStack::GetExpressionVariable() const { + std::lock_guard guard(m_stack_mutex); if (m_completed_plans.empty()) return {}; @@ -307,19 +330,23 @@ lldb::ExpressionVariableSP ThreadPlanStack::GetExpressionVariable() const { return {}; } bool ThreadPlanStack::AnyPlans() const { + std::lock_guard guard(m_stack_mutex); // There is always a base plan... return m_plans.size() > 1; } bool ThreadPlanStack::AnyCompletedPlans() const { + std::lock_guard guard(m_stack_mutex); return !m_completed_plans.empty(); } bool ThreadPlanStack::AnyDiscardedPlans() const { + std::lock_guard guard(m_stack_mutex); return !m_discarded_plans.empty(); } bool ThreadPlanStack::IsPlanDone(ThreadPlan *in_plan) const { + std::lock_guard guard(m_stack_mutex); for (auto plan : m_completed_plans) { if (plan.get() == in_plan) return true; @@ -328,6 +355,7 @@ bool ThreadPlanStack::IsPlanDone(ThreadPlan *in_plan) const { } bool ThreadPlanStack::WasPlanDiscarded(ThreadPlan *in_plan) const { + std::lock_guard guard(m_stack_mutex); for (auto plan : m_discarded_plans) { if (plan.get() == in_plan) return true; @@ -336,6 +364,7 @@ bool ThreadPlanStack::WasPlanDiscarded(ThreadPlan *in_plan) const { } ThreadPlan *ThreadPlanStack::GetPreviousPlan(ThreadPlan *current_plan) const { + std::lock_guard guard(m_stack_mutex); if (current_plan == nullptr) return nullptr; @@ -363,6 +392,7 @@ ThreadPlan *ThreadPlanStack::GetPreviousPlan(ThreadPlan *current_plan) const { } ThreadPlan *ThreadPlanStack::GetInnermostExpression() const { + std::lock_guard guard(m_stack_mutex); int stack_size = m_plans.size(); for (int i = stack_size - 1; i > 0; i--) { @@ -373,6 +403,7 @@ ThreadPlan *ThreadPlanStack::GetInnermostExpression() const { } void ThreadPlanStack::WillResume() { + std::lock_guard guard(m_stack_mutex); m_completed_plans.clear(); m_discarded_plans.clear(); } @@ -420,7 +451,7 @@ void ThreadPlanStackMap::Update(ThreadList ¤t_threads, std::vector missing_threads; // If we are going to delete plans from the plan stack, // then scan for absent TID's: - for (auto thread_plans : m_plans_list) { + for (auto &thread_plans : m_plans_list) { lldb::tid_t cur_tid = thread_plans.first; ThreadSP thread_sp = current_threads.FindThreadByID(cur_tid); if (!thread_sp) @@ -435,7 +466,7 @@ void ThreadPlanStackMap::DumpPlans(Stream &strm, lldb::DescriptionLevel desc_level, bool internal, bool condense_if_trivial, bool skip_unreported) { - for (auto elem : m_plans_list) { + for (auto &elem : m_plans_list) { lldb::tid_t tid = elem.first; uint32_t index_id = 0; ThreadSP thread_sp = m_process.GetThreadList().FindThreadByID(tid); @@ -448,8 +479,8 @@ void ThreadPlanStackMap::DumpPlans(Stream &strm, index_id = thread_sp->GetIndexID(); if (condense_if_trivial) { - if (!elem.second.AnyPlans() && !elem.second.AnyCompletedPlans() && - !elem.second.AnyDiscardedPlans()) { + if (!elem.second->AnyPlans() && !elem.second->AnyCompletedPlans() && + !elem.second->AnyDiscardedPlans()) { strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 "\n", index_id, tid); strm.IndentMore(); strm.Indent(); @@ -462,7 +493,7 @@ void ThreadPlanStackMap::DumpPlans(Stream &strm, strm.Indent(); strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 ":\n", index_id, tid); - elem.second.DumpThreadPlans(strm, desc_level, internal); + elem.second->DumpThreadPlans(strm, desc_level, internal); } } diff --git a/lldb/source/Target/ThreadPlanStepOverBreakpoint.cpp b/lldb/source/Target/ThreadPlanStepOverBreakpoint.cpp index f188d827faae4..2483bac29d7b0 100644 --- a/lldb/source/Target/ThreadPlanStepOverBreakpoint.cpp +++ b/lldb/source/Target/ThreadPlanStepOverBreakpoint.cpp @@ -134,9 +134,7 @@ bool ThreadPlanStepOverBreakpoint::WillStop() { return true; } -void ThreadPlanStepOverBreakpoint::WillPop() { - ReenableBreakpointSite(); -} +void ThreadPlanStepOverBreakpoint::DidPop() { ReenableBreakpointSite(); } bool ThreadPlanStepOverBreakpoint::MischiefManaged() { lldb::addr_t pc_addr = GetThread().GetRegisterContext()->GetPC();