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