brintos

brintos / llvm-project-archived public Read only

0
0
Text · 16.3 KiB · d5d600d Raw
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 &current_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