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 DiscardConsultingControllingPlans(); 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 std::lock_guard<std::recursive_mutex> guard(m_stack_map_mutex); 127 lldb::tid_t tid = thread.GetID(); 128 m_plans_list.emplace(tid, thread); 129 } 130 RemoveTID(lldb::tid_t tid)131 bool RemoveTID(lldb::tid_t tid) { 132 std::lock_guard<std::recursive_mutex> guard(m_stack_map_mutex); 133 auto result = m_plans_list.find(tid); 134 if (result == m_plans_list.end()) 135 return false; 136 result->second.ThreadDestroyed(nullptr); 137 m_plans_list.erase(result); 138 return true; 139 } 140 Find(lldb::tid_t tid)141 ThreadPlanStack *Find(lldb::tid_t tid) { 142 std::lock_guard<std::recursive_mutex> guard(m_stack_map_mutex); 143 auto result = m_plans_list.find(tid); 144 if (result == m_plans_list.end()) 145 return nullptr; 146 else 147 return &result->second; 148 } 149 150 /// Clear the Thread* cache that each ThreadPlan contains. 151 /// 152 /// This is useful in situations like when a new Thread list is being 153 /// generated. ClearThreadCache()154 void ClearThreadCache() { 155 for (auto &plan_list : m_plans_list) 156 plan_list.second.ClearThreadCache(); 157 } 158 Clear()159 void Clear() { 160 std::lock_guard<std::recursive_mutex> guard(m_stack_map_mutex); 161 for (auto &plan : m_plans_list) 162 plan.second.ThreadDestroyed(nullptr); 163 m_plans_list.clear(); 164 } 165 166 // Implements Process::DumpThreadPlans 167 void DumpPlans(Stream &strm, lldb::DescriptionLevel desc_level, bool internal, 168 bool ignore_boring, bool skip_unreported); 169 170 // Implements Process::DumpThreadPlansForTID 171 bool DumpPlansForTID(Stream &strm, lldb::tid_t tid, 172 lldb::DescriptionLevel desc_level, bool internal, 173 bool ignore_boring, bool skip_unreported); 174 175 bool PrunePlansForTID(lldb::tid_t tid); 176 177 private: 178 Process &m_process; 179 mutable std::recursive_mutex m_stack_map_mutex; 180 using PlansList = std::unordered_map<lldb::tid_t, ThreadPlanStack>; 181 PlansList m_plans_list; 182 183 }; 184 185 } // namespace lldb_private 186 187 #endif // LLDB_TARGET_THREADPLANSTACK_H 188