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: |
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 | |
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(args&: tid, args&: thread); |
129 | } |
130 | |
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(x: tid); |
134 | if (result == m_plans_list.end()) |
135 | return false; |
136 | result->second.ThreadDestroyed(thread: nullptr); |
137 | m_plans_list.erase(position: result); |
138 | return true; |
139 | } |
140 | |
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(x: 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. |
154 | void ClearThreadCache() { |
155 | for (auto &plan_list : m_plans_list) |
156 | plan_list.second.ClearThreadCache(); |
157 | } |
158 | |
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(thread: 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 | |