532 lines · cpp
1//===-- ThreadPlanStack.cpp -------------------------------------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "lldb/Target/ThreadPlanStack.h"10#include "lldb/Target/Process.h"11#include "lldb/Target/Target.h"12#include "lldb/Target/Thread.h"13#include "lldb/Target/ThreadPlan.h"14#include "lldb/Utility/Log.h"15 16using namespace lldb;17using namespace lldb_private;18 19static void PrintPlanElement(Stream &s, const ThreadPlanSP &plan,20 lldb::DescriptionLevel desc_level,21 int32_t elem_idx) {22 s.IndentMore();23 s.Indent();24 s.Printf("Element %d: ", elem_idx);25 plan->GetDescription(&s, desc_level);26 s.EOL();27 s.IndentLess();28}29 30ThreadPlanStack::ThreadPlanStack(const Thread &thread, bool make_null) {31 if (make_null) {32 // The ThreadPlanNull doesn't do anything to the Thread, so this is actually33 // still a const operation.34 m_plans.push_back(35 ThreadPlanSP(new ThreadPlanNull(const_cast<Thread &>(thread))));36 }37}38 39void ThreadPlanStack::DumpThreadPlans(Stream &s,40 lldb::DescriptionLevel desc_level,41 bool include_internal) const {42 llvm::sys::ScopedReader guard(m_stack_mutex);43 s.IndentMore();44 PrintOneStackNoLock(s, "Active plan stack", m_plans, desc_level,45 include_internal);46 PrintOneStackNoLock(s, "Completed plan stack", m_completed_plans, desc_level,47 include_internal);48 PrintOneStackNoLock(s, "Discarded plan stack", m_discarded_plans, desc_level,49 include_internal);50 s.IndentLess();51}52 53void ThreadPlanStack::PrintOneStackNoLock(Stream &s, llvm::StringRef stack_name,54 const PlanStack &stack,55 lldb::DescriptionLevel desc_level,56 bool include_internal) const {57 // If the stack is empty, just exit:58 if (stack.empty())59 return;60 61 // Make sure there are public completed plans:62 bool any_public = false;63 if (!include_internal) {64 for (auto plan : stack) {65 if (!plan->GetPrivate()) {66 any_public = true;67 break;68 }69 }70 }71 72 if (include_internal || any_public) {73 int print_idx = 0;74 s.Indent();75 s << stack_name << ":\n";76 for (auto plan : stack) {77 if (!include_internal && plan->GetPrivate())78 continue;79 PrintPlanElement(s, plan, desc_level, print_idx++);80 }81 }82}83 84size_t ThreadPlanStack::CheckpointCompletedPlans() {85 llvm::sys::ScopedWriter guard(m_stack_mutex);86 m_completed_plan_checkpoint++;87 m_completed_plan_store.insert(88 std::make_pair(m_completed_plan_checkpoint, m_completed_plans));89 return m_completed_plan_checkpoint;90}91 92void ThreadPlanStack::RestoreCompletedPlanCheckpoint(size_t checkpoint) {93 llvm::sys::ScopedWriter guard(m_stack_mutex);94 auto result = m_completed_plan_store.find(checkpoint);95 assert(result != m_completed_plan_store.end() &&96 "Asked for a checkpoint that didn't exist");97 m_completed_plans.swap((*result).second);98 m_completed_plan_store.erase(result);99}100 101void ThreadPlanStack::DiscardCompletedPlanCheckpoint(size_t checkpoint) {102 llvm::sys::ScopedWriter guard(m_stack_mutex);103 m_completed_plan_store.erase(checkpoint);104}105 106void ThreadPlanStack::ThreadDestroyed(Thread *thread) {107 // Tell the plan stacks that this thread is going away:108 llvm::sys::ScopedWriter guard(m_stack_mutex);109 for (ThreadPlanSP plan : m_plans)110 plan->ThreadDestroyed();111 112 for (ThreadPlanSP plan : m_discarded_plans)113 plan->ThreadDestroyed();114 115 for (ThreadPlanSP plan : m_completed_plans)116 plan->ThreadDestroyed();117 118 // Now clear the current plan stacks:119 m_plans.clear();120 m_discarded_plans.clear();121 m_completed_plans.clear();122 123 // Push a ThreadPlanNull on the plan stack. That way we can continue124 // assuming that the plan stack is never empty, but if somebody errantly asks125 // questions of a destroyed thread without checking first whether it is126 // destroyed, they won't crash.127 if (thread != nullptr) {128 lldb::ThreadPlanSP null_plan_sp(new ThreadPlanNull(*thread));129 m_plans.push_back(null_plan_sp);130 }131}132 133void ThreadPlanStack::PushPlan(lldb::ThreadPlanSP new_plan_sp) {134 // If the thread plan doesn't already have a tracer, give it its parent's135 // tracer:136 // The first plan has to be a base plan:137 { // Scope for Lock - DidPush often adds plans to the stack:138 llvm::sys::ScopedWriter guard(m_stack_mutex);139 assert((m_plans.size() > 0 || new_plan_sp->IsBasePlan()) &&140 "Zeroth plan must be a base plan");141 142 if (!new_plan_sp->GetThreadPlanTracer()) {143 assert(!m_plans.empty());144 new_plan_sp->SetThreadPlanTracer(m_plans.back()->GetThreadPlanTracer());145 }146 m_plans.push_back(new_plan_sp);147 }148 new_plan_sp->DidPush();149}150 151lldb::ThreadPlanSP ThreadPlanStack::PopPlan() {152 llvm::sys::ScopedWriter guard(m_stack_mutex);153 assert(m_plans.size() > 1 && "Can't pop the base thread plan");154 155 // Note that moving the top element of the vector would leave it in an156 // undefined state, and break the guarantee that the stack's thread plans are157 // all valid.158 lldb::ThreadPlanSP plan_sp = m_plans.back();159 m_plans.pop_back();160 m_completed_plans.push_back(plan_sp);161 plan_sp->DidPop();162 return plan_sp;163}164 165lldb::ThreadPlanSP ThreadPlanStack::DiscardPlan() {166 llvm::sys::ScopedWriter guard(m_stack_mutex);167 return DiscardPlanNoLock();168}169 170lldb::ThreadPlanSP ThreadPlanStack::DiscardPlanNoLock() {171 assert(m_plans.size() > 1 && "Can't discard the base thread plan");172 173 // Note that moving the top element of the vector would leave it in an174 // undefined state, and break the guarantee that the stack's thread plans are175 // all valid.176 lldb::ThreadPlanSP plan_sp = m_plans.back();177 m_plans.pop_back();178 m_discarded_plans.push_back(plan_sp);179 plan_sp->DidPop();180 return plan_sp;181}182 183// If the input plan is nullptr, discard all plans. Otherwise make sure this184// plan is in the stack, and if so discard up to and including it.185void ThreadPlanStack::DiscardPlansUpToPlan(ThreadPlan *up_to_plan_ptr) {186 llvm::sys::ScopedWriter guard(m_stack_mutex);187 int stack_size = m_plans.size();188 189 if (up_to_plan_ptr == nullptr) {190 for (int i = stack_size - 1; i > 0; i--)191 DiscardPlanNoLock();192 return;193 }194 195 bool found_it = false;196 for (int i = stack_size - 1; i > 0; i--) {197 if (m_plans[i].get() == up_to_plan_ptr) {198 found_it = true;199 break;200 }201 }202 203 if (found_it) {204 bool last_one = false;205 for (int i = stack_size - 1; i > 0 && !last_one; i--) {206 if (GetCurrentPlanNoLock().get() == up_to_plan_ptr)207 last_one = true;208 DiscardPlanNoLock();209 }210 }211}212 213void ThreadPlanStack::DiscardAllPlans() {214 llvm::sys::ScopedWriter guard(m_stack_mutex);215 int stack_size = m_plans.size();216 for (int i = stack_size - 1; i > 0; i--) {217 DiscardPlanNoLock();218 }219}220 221void ThreadPlanStack::DiscardConsultingControllingPlans() {222 llvm::sys::ScopedWriter guard(m_stack_mutex);223 while (true) {224 int controlling_plan_idx;225 bool discard = true;226 227 // Find the first controlling plan, see if it wants discarding, and if yes228 // discard up to it.229 for (controlling_plan_idx = m_plans.size() - 1; controlling_plan_idx >= 0;230 controlling_plan_idx--) {231 if (m_plans[controlling_plan_idx]->IsControllingPlan()) {232 discard = m_plans[controlling_plan_idx]->OkayToDiscard();233 break;234 }235 }236 237 // If the controlling plan doesn't want to get discarded, then we're done.238 if (!discard)239 return;240 241 // First pop all the dependent plans:242 for (int i = m_plans.size() - 1; i > controlling_plan_idx; i--) {243 DiscardPlanNoLock();244 }245 246 // Now discard the controlling plan itself.247 // The bottom-most plan never gets discarded. "OkayToDiscard" for it248 // means discard it's dependent plans, but not it...249 if (controlling_plan_idx > 0) {250 DiscardPlanNoLock();251 }252 }253}254 255lldb::ThreadPlanSP ThreadPlanStack::GetCurrentPlan() const {256 llvm::sys::ScopedReader guard(m_stack_mutex);257 return GetCurrentPlanNoLock();258}259 260lldb::ThreadPlanSP ThreadPlanStack::GetCurrentPlanNoLock() const {261 assert(m_plans.size() != 0 && "There will always be a base plan.");262 return m_plans.back();263}264 265lldb::ThreadPlanSP ThreadPlanStack::GetCompletedPlan(bool skip_private) const {266 llvm::sys::ScopedReader guard(m_stack_mutex);267 if (m_completed_plans.empty())268 return {};269 270 if (!skip_private)271 return m_completed_plans.back();272 273 for (int i = m_completed_plans.size() - 1; i >= 0; i--) {274 lldb::ThreadPlanSP completed_plan_sp;275 completed_plan_sp = m_completed_plans[i];276 if (!completed_plan_sp->GetPrivate())277 return completed_plan_sp;278 }279 return {};280}281 282lldb::ThreadPlanSP ThreadPlanStack::GetPlanByIndex(uint32_t plan_idx,283 bool skip_private) const {284 llvm::sys::ScopedReader guard(m_stack_mutex);285 uint32_t idx = 0;286 287 for (lldb::ThreadPlanSP plan_sp : m_plans) {288 if (skip_private && plan_sp->GetPrivate())289 continue;290 if (idx == plan_idx)291 return plan_sp;292 idx++;293 }294 return {};295}296 297lldb::ValueObjectSP ThreadPlanStack::GetReturnValueObject() const {298 llvm::sys::ScopedReader guard(m_stack_mutex);299 if (m_completed_plans.empty())300 return {};301 302 for (int i = m_completed_plans.size() - 1; i >= 0; i--) {303 lldb::ValueObjectSP return_valobj_sp;304 return_valobj_sp = m_completed_plans[i]->GetReturnValueObject();305 if (return_valobj_sp)306 return return_valobj_sp;307 }308 return {};309}310 311lldb::ExpressionVariableSP ThreadPlanStack::GetExpressionVariable() const {312 llvm::sys::ScopedReader guard(m_stack_mutex);313 if (m_completed_plans.empty())314 return {};315 316 for (int i = m_completed_plans.size() - 1; i >= 0; i--) {317 lldb::ExpressionVariableSP expression_variable_sp;318 expression_variable_sp = m_completed_plans[i]->GetExpressionVariable();319 if (expression_variable_sp)320 return expression_variable_sp;321 }322 return {};323}324bool ThreadPlanStack::AnyPlans() const {325 llvm::sys::ScopedReader guard(m_stack_mutex);326 // There is always a base plan...327 return m_plans.size() > 1;328}329 330bool ThreadPlanStack::AnyCompletedPlans() const {331 llvm::sys::ScopedReader guard(m_stack_mutex);332 return !m_completed_plans.empty();333}334 335bool ThreadPlanStack::AnyDiscardedPlans() const {336 llvm::sys::ScopedReader guard(m_stack_mutex);337 return !m_discarded_plans.empty();338}339 340bool ThreadPlanStack::IsPlanDone(ThreadPlan *in_plan) const {341 llvm::sys::ScopedReader guard(m_stack_mutex);342 for (auto plan : m_completed_plans) {343 if (plan.get() == in_plan)344 return true;345 }346 return false;347}348 349bool ThreadPlanStack::WasPlanDiscarded(ThreadPlan *in_plan) const {350 llvm::sys::ScopedReader guard(m_stack_mutex);351 for (auto plan : m_discarded_plans) {352 if (plan.get() == in_plan)353 return true;354 }355 return false;356}357 358ThreadPlan *ThreadPlanStack::GetPreviousPlan(ThreadPlan *current_plan) const {359 llvm::sys::ScopedReader guard(m_stack_mutex);360 if (current_plan == nullptr)361 return nullptr;362 363 // Look first in the completed plans, if the plan is here and there is364 // a completed plan above it, return that.365 int stack_size = m_completed_plans.size();366 for (int i = stack_size - 1; i > 0; i--) {367 if (current_plan == m_completed_plans[i].get())368 return m_completed_plans[i - 1].get();369 }370 371 // If this is the first completed plan, the previous one is the372 // bottom of the regular plan stack.373 if (stack_size > 0 && m_completed_plans[0].get() == current_plan) {374 return GetCurrentPlanNoLock().get();375 }376 377 // Otherwise look for it in the regular plans.378 stack_size = m_plans.size();379 for (int i = stack_size - 1; i > 0; i--) {380 if (current_plan == m_plans[i].get())381 return m_plans[i - 1].get();382 }383 return nullptr;384}385 386ThreadPlan *ThreadPlanStack::GetInnermostExpression() const {387 llvm::sys::ScopedReader guard(m_stack_mutex);388 int stack_size = m_plans.size();389 390 for (int i = stack_size - 1; i > 0; i--) {391 if (m_plans[i]->GetKind() == ThreadPlan::eKindCallFunction)392 return m_plans[i].get();393 }394 return nullptr;395}396 397void ThreadPlanStack::ClearThreadCache() {398 llvm::sys::ScopedReader guard(m_stack_mutex);399 for (lldb::ThreadPlanSP thread_plan_sp : m_plans)400 thread_plan_sp->ClearThreadCache();401}402 403void ThreadPlanStack::WillResume() {404 llvm::sys::ScopedWriter guard(m_stack_mutex);405 m_completed_plans.clear();406 m_discarded_plans.clear();407}408 409void ThreadPlanStackMap::Update(ThreadList ¤t_threads,410 bool delete_missing,411 bool check_for_new) {412 413 std::lock_guard<std::recursive_mutex> guard(m_stack_map_mutex);414 // Now find all the new threads and add them to the map:415 if (check_for_new) {416 for (auto thread : current_threads.Threads()) {417 lldb::tid_t cur_tid = thread->GetID();418 if (!Find(cur_tid)) {419 AddThread(*thread);420 thread->QueueBasePlan(true);421 }422 }423 }424 425 // If we aren't reaping missing threads at this point,426 // we are done.427 if (!delete_missing)428 return;429 // Otherwise scan for absent TID's.430 std::vector<lldb::tid_t> missing_threads;431 // If we are going to delete plans from the plan stack,432 // then scan for absent TID's:433 for (auto &thread_plans : m_plans_list) {434 lldb::tid_t cur_tid = thread_plans.first;435 ThreadSP thread_sp = current_threads.FindThreadByID(cur_tid);436 if (!thread_sp)437 missing_threads.push_back(cur_tid);438 }439 for (lldb::tid_t tid : missing_threads) {440 RemoveTID(tid);441 }442}443 444void ThreadPlanStackMap::DumpPlans(Stream &strm,445 lldb::DescriptionLevel desc_level,446 bool internal, bool condense_if_trivial,447 bool skip_unreported) {448 std::lock_guard<std::recursive_mutex> guard(m_stack_map_mutex);449 for (auto &elem : m_plans_list) {450 lldb::tid_t tid = elem.first;451 uint32_t index_id = 0;452 ThreadSP thread_sp = m_process.GetThreadList().FindThreadByID(tid);453 454 if (skip_unreported) {455 if (!thread_sp)456 continue;457 }458 if (thread_sp)459 index_id = thread_sp->GetIndexID();460 461 if (condense_if_trivial) {462 if (!elem.second.AnyPlans() && !elem.second.AnyCompletedPlans() &&463 !elem.second.AnyDiscardedPlans()) {464 strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 "\n", index_id, tid);465 strm.IndentMore();466 strm.Indent();467 strm.Printf("No active thread plans\n");468 strm.IndentLess();469 return;470 }471 }472 473 strm.Indent();474 strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 ":\n", index_id, tid);475 476 elem.second.DumpThreadPlans(strm, desc_level, internal);477 }478}479 480bool ThreadPlanStackMap::DumpPlansForTID(Stream &strm, lldb::tid_t tid,481 lldb::DescriptionLevel desc_level,482 bool internal,483 bool condense_if_trivial,484 bool skip_unreported) {485 std::lock_guard<std::recursive_mutex> guard(m_stack_map_mutex);486 uint32_t index_id = 0;487 ThreadSP thread_sp = m_process.GetThreadList().FindThreadByID(tid);488 489 if (skip_unreported) {490 if (!thread_sp) {491 strm.Format("Unknown TID: {0}", tid);492 return false;493 }494 }495 496 if (thread_sp)497 index_id = thread_sp->GetIndexID();498 ThreadPlanStack *stack = Find(tid);499 if (!stack) {500 strm.Format("Unknown TID: {0}\n", tid);501 return false;502 }503 504 if (condense_if_trivial) {505 if (!stack->AnyPlans() && !stack->AnyCompletedPlans() &&506 !stack->AnyDiscardedPlans()) {507 strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 "\n", index_id, tid);508 strm.IndentMore();509 strm.Indent();510 strm.Printf("No active thread plans\n");511 strm.IndentLess();512 return true;513 }514 }515 516 strm.Indent();517 strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 ":\n", index_id, tid);518 519 stack->DumpThreadPlans(strm, desc_level, internal);520 return true;521}522 523bool ThreadPlanStackMap::PrunePlansForTID(lldb::tid_t tid) {524 // We only remove the plans for unreported TID's.525 std::lock_guard<std::recursive_mutex> guard(m_stack_map_mutex);526 ThreadSP thread_sp = m_process.GetThreadList().FindThreadByID(tid);527 if (thread_sp)528 return false;529 530 return RemoveTID(tid);531}532