/Users/buildslave/jenkins/workspace/coverage/llvm-project/lldb/include/lldb/Target/ThreadPlanStack.h
Line | Count | Source (jump to first uncovered line) |
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 | 2.83k | ~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 | 2.67k | ThreadPlanStackMap(Process &process) : m_process(process) {} |
119 | 2.56k | ~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 | 2.82k | void AddThread(Thread &thread) { |
126 | 2.82k | std::lock_guard<std::recursive_mutex> guard(m_stack_map_mutex); |
127 | 2.82k | lldb::tid_t tid = thread.GetID(); |
128 | 2.82k | m_plans_list.emplace(tid, thread); |
129 | 2.82k | } |
130 | | |
131 | 125 | bool RemoveTID(lldb::tid_t tid) { |
132 | 125 | std::lock_guard<std::recursive_mutex> guard(m_stack_map_mutex); |
133 | 125 | auto result = m_plans_list.find(tid); |
134 | 125 | if (result == m_plans_list.end()) |
135 | 0 | return false; |
136 | 125 | result->second.ThreadDestroyed(nullptr); |
137 | 125 | m_plans_list.erase(result); |
138 | 125 | return true; |
139 | 125 | } |
140 | | |
141 | 368k | ThreadPlanStack *Find(lldb::tid_t tid) { |
142 | 368k | std::lock_guard<std::recursive_mutex> guard(m_stack_map_mutex); |
143 | 368k | auto result = m_plans_list.find(tid); |
144 | 368k | if (result == m_plans_list.end()) |
145 | 2.84k | return nullptr; |
146 | 365k | else |
147 | 365k | return &result->second; |
148 | 368k | } |
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 | 16.7k | void ClearThreadCache() { |
155 | 16.7k | for (auto &plan_list : m_plans_list) |
156 | 14.6k | plan_list.second.ClearThreadCache(); |
157 | 16.7k | } |
158 | | |
159 | 2.66k | void Clear() { |
160 | 2.66k | std::lock_guard<std::recursive_mutex> guard(m_stack_map_mutex); |
161 | 2.66k | for (auto &plan : m_plans_list) |
162 | 2.69k | plan.second.ThreadDestroyed(nullptr); |
163 | 2.66k | m_plans_list.clear(); |
164 | 2.66k | } |
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 |