1 //===-- ThreadPlanStack.h ---------------------------------------*- 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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #ifndef LLDB_TARGET_THREADPLANSTACK_H 10 #define LLDB_TARGET_THREADPLANSTACK_H 11 12 #include <mutex> 13 #include <string> 14 #include <unordered_map> 15 #include <vector> 16 17 #include "lldb/Target/Target.h" 18 #include "lldb/Target/Thread.h" 19 #include "lldb/lldb-private-forward.h" 20 #include "lldb/lldb-private.h" 21 22 namespace lldb_private { 23 24 // The ThreadPlans have a thread for use when they are asked all the ThreadPlan 25 // state machine questions, but they should never cache any pointers from their 26 // owning lldb_private::Thread. That's because we want to be able to detach 27 // them from an owning thread, then reattach them by TID. 28 // The ThreadPlanStack holds the ThreadPlans for a given TID. All its methods 29 // are private, and it should only be accessed through the owning thread. When 30 // it is detached from a thread, all you can do is reattach it or delete it. 31 class ThreadPlanStack { 32 friend class lldb_private::Thread; 33 34 public: 35 ThreadPlanStack(const Thread &thread, bool make_empty = false); 36 ~ThreadPlanStack() = default; 37 38 using PlanStack = std::vector<lldb::ThreadPlanSP>; 39 40 void DumpThreadPlans(Stream &s, lldb::DescriptionLevel desc_level, 41 bool include_internal) const; 42 43 size_t CheckpointCompletedPlans(); 44 45 void RestoreCompletedPlanCheckpoint(size_t checkpoint); 46 47 void DiscardCompletedPlanCheckpoint(size_t checkpoint); 48 49 void ThreadDestroyed(Thread *thread); 50 51 void PushPlan(lldb::ThreadPlanSP new_plan_sp); 52 53 lldb::ThreadPlanSP PopPlan(); 54 55 lldb::ThreadPlanSP DiscardPlan(); 56 57 // If the input plan is nullptr, discard all plans. Otherwise make sure this 58 // plan is in the stack, and if so discard up to and including it. 59 void DiscardPlansUpToPlan(ThreadPlan *up_to_plan_ptr); 60 61 void DiscardAllPlans(); 62 63 void DiscardConsultingMasterPlans(); 64 65 lldb::ThreadPlanSP GetCurrentPlan() const; 66 67 lldb::ThreadPlanSP GetCompletedPlan(bool skip_private = true) const; 68 69 lldb::ThreadPlanSP GetPlanByIndex(uint32_t plan_idx, 70 bool skip_private = true) const; 71 72 lldb::ValueObjectSP GetReturnValueObject() const; 73 74 lldb::ExpressionVariableSP GetExpressionVariable() const; 75 76 bool AnyPlans() const; 77 78 bool AnyCompletedPlans() const; 79 80 bool AnyDiscardedPlans() const; 81 82 bool IsPlanDone(ThreadPlan *plan) const; 83 84 bool WasPlanDiscarded(ThreadPlan *plan) const; 85 86 ThreadPlan *GetPreviousPlan(ThreadPlan *current_plan) const; 87 88 ThreadPlan *GetInnermostExpression() const; 89 90 void WillResume(); 91 92 /// Clear the Thread* cache that each ThreadPlan contains. 93 /// 94 /// This is useful in situations like when a new Thread list is being 95 /// generated. 96 void ClearThreadCache(); 97 98 private: 99 void PrintOneStack(Stream &s, llvm::StringRef stack_name, 100 const PlanStack &stack, lldb::DescriptionLevel desc_level, 101 bool include_internal) const; 102 103 PlanStack m_plans; ///< The stack of plans this thread is executing. 104 PlanStack m_completed_plans; ///< Plans that have been completed by this 105 /// stop. They get deleted when the thread 106 /// resumes. 107 PlanStack m_discarded_plans; ///< Plans that have been discarded by this 108 /// stop. They get deleted when the thread 109 /// resumes. 110 size_t m_completed_plan_checkpoint = 0; // Monotonically increasing token for 111 // completed plan checkpoints. 112 std::unordered_map<size_t, PlanStack> m_completed_plan_store; 113 mutable std::recursive_mutex m_stack_mutex; 114 }; 115 116 class ThreadPlanStackMap { 117 public: ThreadPlanStackMap(Process & process)118 ThreadPlanStackMap(Process &process) : m_process(process) {} 119 ~ThreadPlanStackMap() = default; 120 121 // Prune the map using the current_threads list. 122 void Update(ThreadList ¤t_threads, bool delete_missing, 123 bool check_for_new = true); 124 AddThread(Thread & thread)125 void AddThread(Thread &thread) { 126 lldb::tid_t tid = thread.GetID(); 127 m_plans_list.emplace(tid, thread); 128 } 129 RemoveTID(lldb::tid_t tid)130 bool RemoveTID(lldb::tid_t tid) { 131 auto result = m_plans_list.find(tid); 132 if (result == m_plans_list.end()) 133 return false; 134 result->second.ThreadDestroyed(nullptr); 135 m_plans_list.erase(result); 136 return true; 137 } 138 Find(lldb::tid_t tid)139 ThreadPlanStack *Find(lldb::tid_t tid) { 140 auto result = m_plans_list.find(tid); 141 if (result == m_plans_list.end()) 142 return nullptr; 143 else 144 return &result->second; 145 } 146 147 /// Clear the Thread* cache that each ThreadPlan contains. 148 /// 149 /// This is useful in situations like when a new Thread list is being 150 /// generated. ClearThreadCache()151 void ClearThreadCache() { 152 for (auto &plan_list : m_plans_list) 153 plan_list.second.ClearThreadCache(); 154 } 155 Clear()156 void Clear() { 157 for (auto &plan : m_plans_list) 158 plan.second.ThreadDestroyed(nullptr); 159 m_plans_list.clear(); 160 } 161 162 // Implements Process::DumpThreadPlans 163 void DumpPlans(Stream &strm, lldb::DescriptionLevel desc_level, bool internal, 164 bool ignore_boring, bool skip_unreported); 165 166 // Implements Process::DumpThreadPlansForTID 167 bool DumpPlansForTID(Stream &strm, lldb::tid_t tid, 168 lldb::DescriptionLevel desc_level, bool internal, 169 bool ignore_boring, bool skip_unreported); 170 171 bool PrunePlansForTID(lldb::tid_t tid); 172 173 private: 174 Process &m_process; 175 using PlansList = std::unordered_map<lldb::tid_t, ThreadPlanStack>; 176 PlansList m_plans_list; 177 }; 178 179 } // namespace lldb_private 180 181 #endif // LLDB_TARGET_THREADPLANSTACK_H 182