/Users/buildslave/jenkins/workspace/coverage/llvm-project/lldb/source/Target/ThreadPlanStack.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===-- ThreadPlanStack.cpp -------------------------------------*- 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 | | #include "lldb/Target/ThreadPlanStack.h" |
10 | | #include "lldb/Target/Process.h" |
11 | | #include "lldb/Target/Target.h" |
12 | | #include "lldb/Target/Thread.h" |
13 | | #include "lldb/Target/ThreadPlan.h" |
14 | | #include "lldb/Utility/Log.h" |
15 | | |
16 | | using namespace lldb; |
17 | | using namespace lldb_private; |
18 | | |
19 | | static void PrintPlanElement(Stream &s, const ThreadPlanSP &plan, |
20 | | lldb::DescriptionLevel desc_level, |
21 | 18 | int32_t elem_idx) { |
22 | 18 | s.IndentMore(); |
23 | 18 | s.Indent(); |
24 | 18 | s.Printf("Element %d: ", elem_idx); |
25 | 18 | plan->GetDescription(&s, desc_level); |
26 | 18 | s.EOL(); |
27 | 18 | s.IndentLess(); |
28 | 18 | } |
29 | | |
30 | 2.84k | ThreadPlanStack::ThreadPlanStack(const Thread &thread, bool make_null) { |
31 | 2.84k | if (make_null) { |
32 | | // The ThreadPlanNull doesn't do anything to the Thread, so this is actually |
33 | | // still a const operation. |
34 | 10 | m_plans.push_back( |
35 | 10 | ThreadPlanSP(new ThreadPlanNull(const_cast<Thread &>(thread)))); |
36 | 10 | } |
37 | 2.84k | } |
38 | | |
39 | | void ThreadPlanStack::DumpThreadPlans(Stream &s, |
40 | | lldb::DescriptionLevel desc_level, |
41 | 8 | bool include_internal) const { |
42 | 8 | std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); |
43 | 8 | s.IndentMore(); |
44 | 8 | PrintOneStack(s, "Active plan stack", m_plans, desc_level, include_internal); |
45 | 8 | PrintOneStack(s, "Completed plan stack", m_completed_plans, desc_level, |
46 | 8 | include_internal); |
47 | 8 | PrintOneStack(s, "Discarded plan stack", m_discarded_plans, desc_level, |
48 | 8 | include_internal); |
49 | 8 | s.IndentLess(); |
50 | 8 | } |
51 | | |
52 | | void ThreadPlanStack::PrintOneStack(Stream &s, llvm::StringRef stack_name, |
53 | | const PlanStack &stack, |
54 | | lldb::DescriptionLevel desc_level, |
55 | 24 | bool include_internal) const { |
56 | 24 | std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); |
57 | | // If the stack is empty, just exit: |
58 | 24 | if (stack.empty()) |
59 | 14 | return; |
60 | | |
61 | | // Make sure there are public completed plans: |
62 | 10 | bool any_public = false; |
63 | 10 | if (!include_internal) { |
64 | 12 | for (auto plan : stack) { |
65 | 12 | if (!plan->GetPrivate()) { |
66 | 9 | any_public = true; |
67 | 9 | break; |
68 | 9 | } |
69 | 12 | } |
70 | 9 | } |
71 | | |
72 | 10 | if (include_internal || any_public9 ) { |
73 | 10 | int print_idx = 0; |
74 | 10 | s.Indent(); |
75 | 10 | s << stack_name << ":\n"; |
76 | 27 | for (auto plan : stack) { |
77 | 27 | if (!include_internal && plan->GetPrivate()23 ) |
78 | 9 | continue; |
79 | 18 | PrintPlanElement(s, plan, desc_level, print_idx++); |
80 | 18 | } |
81 | 10 | } |
82 | 10 | } |
83 | | |
84 | 3.36k | size_t ThreadPlanStack::CheckpointCompletedPlans() { |
85 | 3.36k | std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); |
86 | 3.36k | m_completed_plan_checkpoint++; |
87 | 3.36k | m_completed_plan_store.insert( |
88 | 3.36k | std::make_pair(m_completed_plan_checkpoint, m_completed_plans)); |
89 | 3.36k | return m_completed_plan_checkpoint; |
90 | 3.36k | } |
91 | | |
92 | 3.32k | void ThreadPlanStack::RestoreCompletedPlanCheckpoint(size_t checkpoint) { |
93 | 3.32k | std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); |
94 | 3.32k | auto result = m_completed_plan_store.find(checkpoint); |
95 | 3.32k | assert(result != m_completed_plan_store.end() && |
96 | 3.32k | "Asked for a checkpoint that didn't exist"); |
97 | 3.32k | m_completed_plans.swap((*result).second); |
98 | 3.32k | m_completed_plan_store.erase(result); |
99 | 3.32k | } |
100 | | |
101 | 0 | void ThreadPlanStack::DiscardCompletedPlanCheckpoint(size_t checkpoint) { |
102 | 0 | std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); |
103 | 0 | m_completed_plan_store.erase(checkpoint); |
104 | 0 | } |
105 | | |
106 | 2.83k | void ThreadPlanStack::ThreadDestroyed(Thread *thread) { |
107 | | // Tell the plan stacks that this thread is going away: |
108 | 2.83k | std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); |
109 | 2.83k | for (ThreadPlanSP plan : m_plans) |
110 | 2.84k | plan->ThreadDestroyed(); |
111 | | |
112 | 2.83k | for (ThreadPlanSP plan : m_discarded_plans) |
113 | 71 | plan->ThreadDestroyed(); |
114 | | |
115 | 2.83k | for (ThreadPlanSP plan : m_completed_plans) |
116 | 191 | plan->ThreadDestroyed(); |
117 | | |
118 | | // Now clear the current plan stacks: |
119 | 2.83k | m_plans.clear(); |
120 | 2.83k | m_discarded_plans.clear(); |
121 | 2.83k | m_completed_plans.clear(); |
122 | | |
123 | | // Push a ThreadPlanNull on the plan stack. That way we can continue |
124 | | // assuming that the plan stack is never empty, but if somebody errantly asks |
125 | | // questions of a destroyed thread without checking first whether it is |
126 | | // destroyed, they won't crash. |
127 | 2.83k | if (thread != nullptr) { |
128 | 0 | lldb::ThreadPlanSP null_plan_sp(new ThreadPlanNull(*thread)); |
129 | 0 | m_plans.push_back(null_plan_sp); |
130 | 0 | } |
131 | 2.83k | } |
132 | | |
133 | 14.2k | void ThreadPlanStack::PushPlan(lldb::ThreadPlanSP new_plan_sp) { |
134 | | // If the thread plan doesn't already have a tracer, give it its parent's |
135 | | // tracer: |
136 | | // The first plan has to be a base plan: |
137 | 14.2k | std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); |
138 | 14.2k | assert((m_plans.size() > 0 || new_plan_sp->IsBasePlan()) && |
139 | 14.2k | "Zeroth plan must be a base plan"); |
140 | | |
141 | 14.2k | if (!new_plan_sp->GetThreadPlanTracer()) { |
142 | 11.4k | assert(!m_plans.empty()); |
143 | 11.4k | new_plan_sp->SetThreadPlanTracer(m_plans.back()->GetThreadPlanTracer()); |
144 | 11.4k | } |
145 | 14.2k | m_plans.push_back(new_plan_sp); |
146 | 14.2k | new_plan_sp->DidPush(); |
147 | 14.2k | } |
148 | | |
149 | 11.3k | lldb::ThreadPlanSP ThreadPlanStack::PopPlan() { |
150 | 11.3k | std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); |
151 | 11.3k | assert(m_plans.size() > 1 && "Can't pop the base thread plan"); |
152 | | |
153 | | // Note that moving the top element of the vector would leave it in an |
154 | | // undefined state, and break the guarantee that the stack's thread plans are |
155 | | // all valid. |
156 | 11.3k | lldb::ThreadPlanSP plan_sp = m_plans.back(); |
157 | 11.3k | m_plans.pop_back(); |
158 | 11.3k | m_completed_plans.push_back(plan_sp); |
159 | 11.3k | plan_sp->DidPop(); |
160 | 11.3k | return plan_sp; |
161 | 11.3k | } |
162 | | |
163 | 105 | lldb::ThreadPlanSP ThreadPlanStack::DiscardPlan() { |
164 | 105 | std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); |
165 | 105 | assert(m_plans.size() > 1 && "Can't discard the base thread plan"); |
166 | | |
167 | | // Note that moving the top element of the vector would leave it in an |
168 | | // undefined state, and break the guarantee that the stack's thread plans are |
169 | | // all valid. |
170 | 105 | lldb::ThreadPlanSP plan_sp = m_plans.back(); |
171 | 105 | m_plans.pop_back(); |
172 | 105 | m_discarded_plans.push_back(plan_sp); |
173 | 105 | plan_sp->DidPop(); |
174 | 105 | return plan_sp; |
175 | 105 | } |
176 | | |
177 | | // If the input plan is nullptr, discard all plans. Otherwise make sure this |
178 | | // plan is in the stack, and if so discard up to and including it. |
179 | 3.34k | void ThreadPlanStack::DiscardPlansUpToPlan(ThreadPlan *up_to_plan_ptr) { |
180 | 3.34k | std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); |
181 | 3.34k | int stack_size = m_plans.size(); |
182 | | |
183 | 3.34k | if (up_to_plan_ptr == nullptr) { |
184 | 0 | for (int i = stack_size - 1; i > 0; i--) |
185 | 0 | DiscardPlan(); |
186 | 0 | return; |
187 | 0 | } |
188 | | |
189 | 3.34k | bool found_it = false; |
190 | 3.37k | for (int i = stack_size - 1; i > 0; i--35 ) { |
191 | 48 | if (m_plans[i].get() == up_to_plan_ptr) { |
192 | 13 | found_it = true; |
193 | 13 | break; |
194 | 13 | } |
195 | 48 | } |
196 | | |
197 | 3.34k | if (found_it) { |
198 | 13 | bool last_one = false; |
199 | 35 | for (int i = stack_size - 1; i > 0 && !last_one24 ; i--22 ) { |
200 | 22 | if (GetCurrentPlan().get() == up_to_plan_ptr) |
201 | 13 | last_one = true; |
202 | 22 | DiscardPlan(); |
203 | 22 | } |
204 | 13 | } |
205 | 3.34k | } |
206 | | |
207 | 7.31k | void ThreadPlanStack::DiscardAllPlans() { |
208 | 7.31k | std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); |
209 | 7.31k | int stack_size = m_plans.size(); |
210 | 7.37k | for (int i = stack_size - 1; i > 0; i--61 ) { |
211 | 61 | DiscardPlan(); |
212 | 61 | } |
213 | 7.31k | } |
214 | | |
215 | 5.57k | void ThreadPlanStack::DiscardConsultingControllingPlans() { |
216 | 5.57k | std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); |
217 | 5.57k | while (true) { |
218 | 5.57k | int controlling_plan_idx; |
219 | 5.57k | bool discard = true; |
220 | | |
221 | | // Find the first controlling plan, see if it wants discarding, and if yes |
222 | | // discard up to it. |
223 | 5.61k | for (controlling_plan_idx = m_plans.size() - 1; controlling_plan_idx >= 0; |
224 | 5.61k | controlling_plan_idx--46 ) { |
225 | 5.61k | if (m_plans[controlling_plan_idx]->IsControllingPlan()) { |
226 | 5.57k | discard = m_plans[controlling_plan_idx]->OkayToDiscard(); |
227 | 5.57k | break; |
228 | 5.57k | } |
229 | 5.61k | } |
230 | | |
231 | | // If the controlling plan doesn't want to get discarded, then we're done. |
232 | 5.57k | if (!discard) |
233 | 5.57k | return; |
234 | | |
235 | | // First pop all the dependent plans: |
236 | 0 | for (int i = m_plans.size() - 1; i > controlling_plan_idx; i--) { |
237 | 0 | DiscardPlan(); |
238 | 0 | } |
239 | | |
240 | | // Now discard the controlling plan itself. |
241 | | // The bottom-most plan never gets discarded. "OkayToDiscard" for it |
242 | | // means discard it's dependent plans, but not it... |
243 | 0 | if (controlling_plan_idx > 0) { |
244 | 0 | DiscardPlan(); |
245 | 0 | } |
246 | 0 | } |
247 | 5.57k | } |
248 | | |
249 | 145k | lldb::ThreadPlanSP ThreadPlanStack::GetCurrentPlan() const { |
250 | 145k | std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); |
251 | 145k | assert(m_plans.size() != 0 && "There will always be a base plan."); |
252 | 145k | return m_plans.back(); |
253 | 145k | } |
254 | | |
255 | 74.3k | lldb::ThreadPlanSP ThreadPlanStack::GetCompletedPlan(bool skip_private) const { |
256 | 74.3k | std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); |
257 | 74.3k | if (m_completed_plans.empty()) |
258 | 56.5k | return {}; |
259 | | |
260 | 17.7k | if (!skip_private) |
261 | 3.71k | return m_completed_plans.back(); |
262 | | |
263 | 14.1k | for (int i = m_completed_plans.size() - 1; 13.9k i >= 0; i--109 ) { |
264 | 14.0k | lldb::ThreadPlanSP completed_plan_sp; |
265 | 14.0k | completed_plan_sp = m_completed_plans[i]; |
266 | 14.0k | if (!completed_plan_sp->GetPrivate()) |
267 | 13.9k | return completed_plan_sp; |
268 | 14.0k | } |
269 | 81 | return {}; |
270 | 13.9k | } |
271 | | |
272 | | lldb::ThreadPlanSP ThreadPlanStack::GetPlanByIndex(uint32_t plan_idx, |
273 | 4 | bool skip_private) const { |
274 | 4 | std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); |
275 | 4 | uint32_t idx = 0; |
276 | | |
277 | 8 | for (lldb::ThreadPlanSP plan_sp : m_plans) { |
278 | 8 | if (skip_private && plan_sp->GetPrivate()4 ) |
279 | 0 | continue; |
280 | 8 | if (idx == plan_idx) |
281 | 3 | return plan_sp; |
282 | 5 | idx++; |
283 | 5 | } |
284 | 1 | return {}; |
285 | 4 | } |
286 | | |
287 | 5.39k | lldb::ValueObjectSP ThreadPlanStack::GetReturnValueObject() const { |
288 | 5.39k | std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); |
289 | 5.39k | if (m_completed_plans.empty()) |
290 | 0 | return {}; |
291 | | |
292 | 14.3k | for (int i = m_completed_plans.size() - 1; 5.39k i >= 0; i--8.95k ) { |
293 | 9.08k | lldb::ValueObjectSP return_valobj_sp; |
294 | 9.08k | return_valobj_sp = m_completed_plans[i]->GetReturnValueObject(); |
295 | 9.08k | if (return_valobj_sp) |
296 | 130 | return return_valobj_sp; |
297 | 9.08k | } |
298 | 5.26k | return {}; |
299 | 5.39k | } |
300 | | |
301 | 5.39k | lldb::ExpressionVariableSP ThreadPlanStack::GetExpressionVariable() const { |
302 | 5.39k | std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); |
303 | 5.39k | if (m_completed_plans.empty()) |
304 | 0 | return {}; |
305 | | |
306 | 14.4k | for (int i = m_completed_plans.size() - 1; 5.39k i >= 0; i--9.10k ) { |
307 | 9.10k | lldb::ExpressionVariableSP expression_variable_sp; |
308 | 9.10k | expression_variable_sp = m_completed_plans[i]->GetExpressionVariable(); |
309 | 9.10k | if (expression_variable_sp) |
310 | 4 | return expression_variable_sp; |
311 | 9.10k | } |
312 | 5.38k | return {}; |
313 | 5.39k | } |
314 | 12 | bool ThreadPlanStack::AnyPlans() const { |
315 | 12 | std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); |
316 | | // There is always a base plan... |
317 | 12 | return m_plans.size() > 1; |
318 | 12 | } |
319 | | |
320 | 14.9k | bool ThreadPlanStack::AnyCompletedPlans() const { |
321 | 14.9k | std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); |
322 | 14.9k | return !m_completed_plans.empty(); |
323 | 14.9k | } |
324 | | |
325 | 3 | bool ThreadPlanStack::AnyDiscardedPlans() const { |
326 | 3 | std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); |
327 | 3 | return !m_discarded_plans.empty(); |
328 | 3 | } |
329 | | |
330 | 3.32k | bool ThreadPlanStack::IsPlanDone(ThreadPlan *in_plan) const { |
331 | 3.32k | std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); |
332 | 3.32k | for (auto plan : m_completed_plans) { |
333 | 72 | if (plan.get() == in_plan) |
334 | 27 | return true; |
335 | 72 | } |
336 | 3.30k | return false; |
337 | 3.32k | } |
338 | | |
339 | 3.30k | bool ThreadPlanStack::WasPlanDiscarded(ThreadPlan *in_plan) const { |
340 | 3.30k | std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); |
341 | 3.30k | for (auto plan : m_discarded_plans) { |
342 | 2 | if (plan.get() == in_plan) |
343 | 0 | return true; |
344 | 2 | } |
345 | 3.30k | return false; |
346 | 3.30k | } |
347 | | |
348 | 35.8k | ThreadPlan *ThreadPlanStack::GetPreviousPlan(ThreadPlan *current_plan) const { |
349 | 35.8k | std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); |
350 | 35.8k | if (current_plan == nullptr) |
351 | 0 | return nullptr; |
352 | | |
353 | | // Look first in the completed plans, if the plan is here and there is |
354 | | // a completed plan above it, return that. |
355 | 35.8k | int stack_size = m_completed_plans.size(); |
356 | 36.0k | for (int i = stack_size - 1; i > 0; i--254 ) { |
357 | 353 | if (current_plan == m_completed_plans[i].get()) |
358 | 99 | return m_completed_plans[i - 1].get(); |
359 | 353 | } |
360 | | |
361 | | // If this is the first completed plan, the previous one is the |
362 | | // bottom of the regular plan stack. |
363 | 35.7k | if (stack_size > 0 && m_completed_plans[0].get() == current_plan197 ) { |
364 | 75 | return GetCurrentPlan().get(); |
365 | 75 | } |
366 | | |
367 | | // Otherwise look for it in the regular plans. |
368 | 35.6k | stack_size = m_plans.size(); |
369 | 55.9k | for (int i = stack_size - 1; i > 0; i--20.3k ) { |
370 | 35.9k | if (current_plan == m_plans[i].get()) |
371 | 15.5k | return m_plans[i - 1].get(); |
372 | 35.9k | } |
373 | 20.0k | return nullptr; |
374 | 35.6k | } |
375 | | |
376 | 4 | ThreadPlan *ThreadPlanStack::GetInnermostExpression() const { |
377 | 4 | std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); |
378 | 4 | int stack_size = m_plans.size(); |
379 | | |
380 | 8 | for (int i = stack_size - 1; i > 0; i--4 ) { |
381 | 8 | if (m_plans[i]->GetKind() == ThreadPlan::eKindCallFunction) |
382 | 4 | return m_plans[i].get(); |
383 | 8 | } |
384 | 0 | return nullptr; |
385 | 4 | } |
386 | | |
387 | 14.7k | void ThreadPlanStack::ClearThreadCache() { |
388 | 14.7k | std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); |
389 | 14.7k | for (lldb::ThreadPlanSP thread_plan_sp : m_plans) |
390 | 27.3k | thread_plan_sp->ClearThreadCache(); |
391 | 14.7k | } |
392 | | |
393 | 15.2k | void ThreadPlanStack::WillResume() { |
394 | 15.2k | std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); |
395 | 15.2k | m_completed_plans.clear(); |
396 | 15.2k | m_discarded_plans.clear(); |
397 | 15.2k | } |
398 | | |
399 | | void ThreadPlanStackMap::Update(ThreadList ¤t_threads, |
400 | | bool delete_missing, |
401 | 16.8k | bool check_for_new) { |
402 | | |
403 | 16.8k | std::lock_guard<std::recursive_mutex> guard(m_stack_map_mutex); |
404 | | // Now find all the new threads and add them to the map: |
405 | 16.8k | if (check_for_new) { |
406 | 17.4k | for (auto thread : current_threads.Threads()) { |
407 | 17.4k | lldb::tid_t cur_tid = thread->GetID(); |
408 | 17.4k | if (!Find(cur_tid)) { |
409 | 2.83k | AddThread(*thread); |
410 | 2.83k | thread->QueueBasePlan(true); |
411 | 2.83k | } |
412 | 17.4k | } |
413 | 16.8k | } |
414 | | |
415 | | // If we aren't reaping missing threads at this point, |
416 | | // we are done. |
417 | 16.8k | if (!delete_missing) |
418 | 16 | return; |
419 | | // Otherwise scan for absent TID's. |
420 | 16.7k | std::vector<lldb::tid_t> missing_threads; |
421 | | // If we are going to delete plans from the plan stack, |
422 | | // then scan for absent TID's: |
423 | 17.5k | for (auto &thread_plans : m_plans_list) { |
424 | 17.5k | lldb::tid_t cur_tid = thread_plans.first; |
425 | 17.5k | ThreadSP thread_sp = current_threads.FindThreadByID(cur_tid); |
426 | 17.5k | if (!thread_sp) |
427 | 124 | missing_threads.push_back(cur_tid); |
428 | 17.5k | } |
429 | 16.7k | for (lldb::tid_t tid : missing_threads) { |
430 | 124 | RemoveTID(tid); |
431 | 124 | } |
432 | 16.7k | } |
433 | | |
434 | | void ThreadPlanStackMap::DumpPlans(Stream &strm, |
435 | | lldb::DescriptionLevel desc_level, |
436 | | bool internal, bool condense_if_trivial, |
437 | 0 | bool skip_unreported) { |
438 | 0 | std::lock_guard<std::recursive_mutex> guard(m_stack_map_mutex); |
439 | 0 | for (auto &elem : m_plans_list) { |
440 | 0 | lldb::tid_t tid = elem.first; |
441 | 0 | uint32_t index_id = 0; |
442 | 0 | ThreadSP thread_sp = m_process.GetThreadList().FindThreadByID(tid); |
443 | |
|
444 | 0 | if (skip_unreported) { |
445 | 0 | if (!thread_sp) |
446 | 0 | continue; |
447 | 0 | } |
448 | 0 | if (thread_sp) |
449 | 0 | index_id = thread_sp->GetIndexID(); |
450 | |
|
451 | 0 | if (condense_if_trivial) { |
452 | 0 | if (!elem.second.AnyPlans() && !elem.second.AnyCompletedPlans() && |
453 | 0 | !elem.second.AnyDiscardedPlans()) { |
454 | 0 | strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 "\n", index_id, tid); |
455 | 0 | strm.IndentMore(); |
456 | 0 | strm.Indent(); |
457 | 0 | strm.Printf("No active thread plans\n"); |
458 | 0 | strm.IndentLess(); |
459 | 0 | return; |
460 | 0 | } |
461 | 0 | } |
462 | | |
463 | 0 | strm.Indent(); |
464 | 0 | strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 ":\n", index_id, tid); |
465 | |
|
466 | 0 | elem.second.DumpThreadPlans(strm, desc_level, internal); |
467 | 0 | } |
468 | 0 | } |
469 | | |
470 | | bool ThreadPlanStackMap::DumpPlansForTID(Stream &strm, lldb::tid_t tid, |
471 | | lldb::DescriptionLevel desc_level, |
472 | | bool internal, |
473 | | bool condense_if_trivial, |
474 | 14 | bool skip_unreported) { |
475 | 14 | std::lock_guard<std::recursive_mutex> guard(m_stack_map_mutex); |
476 | 14 | uint32_t index_id = 0; |
477 | 14 | ThreadSP thread_sp = m_process.GetThreadList().FindThreadByID(tid); |
478 | | |
479 | 14 | if (skip_unreported) { |
480 | 9 | if (!thread_sp) { |
481 | 3 | strm.Format("Unknown TID: {0}", tid); |
482 | 3 | return false; |
483 | 3 | } |
484 | 9 | } |
485 | | |
486 | 11 | if (thread_sp) |
487 | 8 | index_id = thread_sp->GetIndexID(); |
488 | 11 | ThreadPlanStack *stack = Find(tid); |
489 | 11 | if (!stack) { |
490 | 1 | strm.Format("Unknown TID: {0}\n", tid); |
491 | 1 | return false; |
492 | 1 | } |
493 | | |
494 | 10 | if (condense_if_trivial) { |
495 | 10 | if (!stack->AnyPlans() && !stack->AnyCompletedPlans()4 && |
496 | 10 | !stack->AnyDiscardedPlans()3 ) { |
497 | 2 | strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 "\n", index_id, tid); |
498 | 2 | strm.IndentMore(); |
499 | 2 | strm.Indent(); |
500 | 2 | strm.Printf("No active thread plans\n"); |
501 | 2 | strm.IndentLess(); |
502 | 2 | return true; |
503 | 2 | } |
504 | 10 | } |
505 | | |
506 | 8 | strm.Indent(); |
507 | 8 | strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 ":\n", index_id, tid); |
508 | | |
509 | 8 | stack->DumpThreadPlans(strm, desc_level, internal); |
510 | 8 | return true; |
511 | 10 | } |
512 | | |
513 | 1 | bool ThreadPlanStackMap::PrunePlansForTID(lldb::tid_t tid) { |
514 | | // We only remove the plans for unreported TID's. |
515 | 1 | std::lock_guard<std::recursive_mutex> guard(m_stack_map_mutex); |
516 | 1 | ThreadSP thread_sp = m_process.GetThreadList().FindThreadByID(tid); |
517 | 1 | if (thread_sp) |
518 | 0 | return false; |
519 | | |
520 | 1 | return RemoveTID(tid); |
521 | 1 | } |