LLDB mainline
ThreadPlanStack.cpp
Go to the documentation of this file.
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
10#include "lldb/Target/Process.h"
11#include "lldb/Target/Target.h"
12#include "lldb/Target/Thread.h"
14#include "lldb/Utility/Log.h"
15
16using namespace lldb;
17using namespace lldb_private;
18
19static void PrintPlanElement(Stream &s, const ThreadPlanSP &plan,
20 lldb::DescriptionLevel desc_level,
21 int32_t elem_idx) {
22 s.IndentMore();
23 s.Indent();
24 s.Printf("Element %d: ", elem_idx);
25 plan->GetDescription(&s, desc_level);
26 s.EOL();
27 s.IndentLess();
28}
29
30ThreadPlanStack::ThreadPlanStack(const Thread &thread, bool make_null) {
31 if (make_null) {
32 // The ThreadPlanNull doesn't do anything to the Thread, so this is actually
33 // still a const operation.
34 m_plans.push_back(
35 ThreadPlanSP(new ThreadPlanNull(const_cast<Thread &>(thread))));
36 }
37}
38
40 lldb::DescriptionLevel desc_level,
41 bool include_internal) const {
42 llvm::sys::ScopedReader guard(m_stack_mutex);
43 s.IndentMore();
44 PrintOneStackNoLock(s, "Active plan stack", m_plans, desc_level,
45 include_internal);
46 PrintOneStackNoLock(s, "Completed plan stack", m_completed_plans, desc_level,
47 include_internal);
48 PrintOneStackNoLock(s, "Discarded plan stack", m_discarded_plans, desc_level,
49 include_internal);
50 s.IndentLess();
51}
52
53void ThreadPlanStack::PrintOneStackNoLock(Stream &s, llvm::StringRef stack_name,
54 const PlanStack &stack,
55 lldb::DescriptionLevel desc_level,
56 bool include_internal) const {
57 // If the stack is empty, just exit:
58 if (stack.empty())
59 return;
60
61 // Make sure there are public completed plans:
62 bool any_public = false;
63 if (!include_internal) {
64 for (auto plan : stack) {
65 if (!plan->GetPrivate()) {
66 any_public = true;
67 break;
68 }
69 }
70 }
71
72 if (include_internal || any_public) {
73 int print_idx = 0;
74 s.Indent();
75 s << stack_name << ":\n";
76 for (auto plan : stack) {
77 if (!include_internal && plan->GetPrivate())
78 continue;
79 PrintPlanElement(s, plan, desc_level, print_idx++);
80 }
81 }
82}
83
85 llvm::sys::ScopedWriter guard(m_stack_mutex);
90}
91
93 llvm::sys::ScopedWriter guard(m_stack_mutex);
94 auto result = m_completed_plan_store.find(checkpoint);
95 assert(result != m_completed_plan_store.end() &&
96 "Asked for a checkpoint that didn't exist");
97 m_completed_plans.swap((*result).second);
98 m_completed_plan_store.erase(result);
99}
100
102 llvm::sys::ScopedWriter guard(m_stack_mutex);
103 m_completed_plan_store.erase(checkpoint);
104}
105
107 // Tell the plan stacks that this thread is going away:
108 llvm::sys::ScopedWriter guard(m_stack_mutex);
109 for (ThreadPlanSP plan : m_plans)
110 plan->ThreadDestroyed();
111
113 plan->ThreadDestroyed();
114
116 plan->ThreadDestroyed();
117
118 // Now clear the current plan stacks:
119 m_plans.clear();
120 m_discarded_plans.clear();
121 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 if (thread != nullptr) {
128 lldb::ThreadPlanSP null_plan_sp(new ThreadPlanNull(*thread));
129 m_plans.push_back(null_plan_sp);
130 }
131}
132
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 { // Scope for Lock - DidPush often adds plans to the stack:
138 llvm::sys::ScopedWriter guard(m_stack_mutex);
139 assert((m_plans.size() > 0 || new_plan_sp->IsBasePlan()) &&
140 "Zeroth plan must be a base plan");
141
142 if (!new_plan_sp->GetThreadPlanTracer()) {
143 assert(!m_plans.empty());
144 new_plan_sp->SetThreadPlanTracer(m_plans.back()->GetThreadPlanTracer());
145 }
146 m_plans.push_back(new_plan_sp);
147 }
148 new_plan_sp->DidPush();
149}
150
152 llvm::sys::ScopedWriter guard(m_stack_mutex);
153 assert(m_plans.size() > 1 && "Can't pop the base thread plan");
154
155 // Note that moving the top element of the vector would leave it in an
156 // undefined state, and break the guarantee that the stack's thread plans are
157 // all valid.
158 lldb::ThreadPlanSP plan_sp = m_plans.back();
159 m_plans.pop_back();
160 m_completed_plans.push_back(plan_sp);
161 plan_sp->DidPop();
162 return plan_sp;
163}
164
166 llvm::sys::ScopedWriter guard(m_stack_mutex);
167 return DiscardPlanNoLock();
168}
169
171 assert(m_plans.size() > 1 && "Can't discard the base thread plan");
172
173 // Note that moving the top element of the vector would leave it in an
174 // undefined state, and break the guarantee that the stack's thread plans are
175 // all valid.
176 lldb::ThreadPlanSP plan_sp = m_plans.back();
177 m_plans.pop_back();
178 m_discarded_plans.push_back(plan_sp);
179 plan_sp->DidPop();
180 return plan_sp;
181}
182
183// If the input plan is nullptr, discard all plans. Otherwise make sure this
184// plan is in the stack, and if so discard up to and including it.
186 llvm::sys::ScopedWriter guard(m_stack_mutex);
187 int stack_size = m_plans.size();
188
189 if (up_to_plan_ptr == nullptr) {
190 for (int i = stack_size - 1; i > 0; i--)
192 return;
193 }
194
195 bool found_it = false;
196 for (int i = stack_size - 1; i > 0; i--) {
197 if (m_plans[i].get() == up_to_plan_ptr) {
198 found_it = true;
199 break;
200 }
201 }
202
203 if (found_it) {
204 bool last_one = false;
205 for (int i = stack_size - 1; i > 0 && !last_one; i--) {
206 if (GetCurrentPlanNoLock().get() == up_to_plan_ptr)
207 last_one = true;
209 }
210 }
211}
212
214 llvm::sys::ScopedWriter guard(m_stack_mutex);
215 int stack_size = m_plans.size();
216 for (int i = stack_size - 1; i > 0; i--) {
218 }
219}
220
222 llvm::sys::ScopedWriter guard(m_stack_mutex);
223 while (true) {
224 int controlling_plan_idx;
225 bool discard = true;
226
227 // Find the first controlling plan, see if it wants discarding, and if yes
228 // discard up to it.
229 for (controlling_plan_idx = m_plans.size() - 1; controlling_plan_idx >= 0;
230 controlling_plan_idx--) {
231 if (m_plans[controlling_plan_idx]->IsControllingPlan()) {
232 discard = m_plans[controlling_plan_idx]->OkayToDiscard();
233 break;
234 }
235 }
236
237 // If the controlling plan doesn't want to get discarded, then we're done.
238 if (!discard)
239 return;
240
241 // First pop all the dependent plans:
242 for (int i = m_plans.size() - 1; i > controlling_plan_idx; i--) {
244 }
245
246 // Now discard the controlling plan itself.
247 // The bottom-most plan never gets discarded. "OkayToDiscard" for it
248 // means discard it's dependent plans, but not it...
249 if (controlling_plan_idx > 0) {
251 }
252 }
253}
254
256 llvm::sys::ScopedReader guard(m_stack_mutex);
257 return GetCurrentPlanNoLock();
258}
259
261 assert(m_plans.size() != 0 && "There will always be a base plan.");
262 return m_plans.back();
263}
264
266 llvm::sys::ScopedReader guard(m_stack_mutex);
267 if (m_completed_plans.empty())
268 return {};
269
270 if (!skip_private)
271 return m_completed_plans.back();
272
273 for (int i = m_completed_plans.size() - 1; i >= 0; i--) {
274 lldb::ThreadPlanSP completed_plan_sp;
275 completed_plan_sp = m_completed_plans[i];
276 if (!completed_plan_sp->GetPrivate())
277 return completed_plan_sp;
278 }
279 return {};
280}
281
283 bool skip_private) const {
284 llvm::sys::ScopedReader guard(m_stack_mutex);
285 uint32_t idx = 0;
286
287 for (lldb::ThreadPlanSP plan_sp : m_plans) {
288 if (skip_private && plan_sp->GetPrivate())
289 continue;
290 if (idx == plan_idx)
291 return plan_sp;
292 idx++;
293 }
294 return {};
295}
296
298 llvm::sys::ScopedReader guard(m_stack_mutex);
299 if (m_completed_plans.empty())
300 return {};
301
302 for (int i = m_completed_plans.size() - 1; i >= 0; i--) {
303 lldb::ValueObjectSP return_valobj_sp;
304 return_valobj_sp = m_completed_plans[i]->GetReturnValueObject();
305 if (return_valobj_sp)
306 return return_valobj_sp;
307 }
308 return {};
309}
310
312 llvm::sys::ScopedReader guard(m_stack_mutex);
313 if (m_completed_plans.empty())
314 return {};
315
316 for (int i = m_completed_plans.size() - 1; i >= 0; i--) {
317 lldb::ExpressionVariableSP expression_variable_sp;
318 expression_variable_sp = m_completed_plans[i]->GetExpressionVariable();
319 if (expression_variable_sp)
320 return expression_variable_sp;
321 }
322 return {};
323}
325 llvm::sys::ScopedReader guard(m_stack_mutex);
326 // There is always a base plan...
327 return m_plans.size() > 1;
328}
329
331 llvm::sys::ScopedReader guard(m_stack_mutex);
332 return !m_completed_plans.empty();
333}
334
336 llvm::sys::ScopedReader guard(m_stack_mutex);
337 return !m_discarded_plans.empty();
338}
339
341 llvm::sys::ScopedReader guard(m_stack_mutex);
342 for (auto plan : m_completed_plans) {
343 if (plan.get() == in_plan)
344 return true;
345 }
346 return false;
347}
348
350 llvm::sys::ScopedReader guard(m_stack_mutex);
351 for (auto plan : m_discarded_plans) {
352 if (plan.get() == in_plan)
353 return true;
354 }
355 return false;
356}
357
359 llvm::sys::ScopedReader guard(m_stack_mutex);
360 if (current_plan == nullptr)
361 return nullptr;
362
363 // Look first in the completed plans, if the plan is here and there is
364 // a completed plan above it, return that.
365 int stack_size = m_completed_plans.size();
366 for (int i = stack_size - 1; i > 0; i--) {
367 if (current_plan == m_completed_plans[i].get())
368 return m_completed_plans[i - 1].get();
369 }
370
371 // If this is the first completed plan, the previous one is the
372 // bottom of the regular plan stack.
373 if (stack_size > 0 && m_completed_plans[0].get() == current_plan) {
374 return GetCurrentPlanNoLock().get();
375 }
376
377 // Otherwise look for it in the regular plans.
378 stack_size = m_plans.size();
379 for (int i = stack_size - 1; i > 0; i--) {
380 if (current_plan == m_plans[i].get())
381 return m_plans[i - 1].get();
382 }
383 return nullptr;
384}
385
387 llvm::sys::ScopedReader guard(m_stack_mutex);
388 int stack_size = m_plans.size();
389
390 for (int i = stack_size - 1; i > 0; i--) {
392 return m_plans[i].get();
393 }
394 return nullptr;
395}
396
398 llvm::sys::ScopedReader guard(m_stack_mutex);
399 for (lldb::ThreadPlanSP thread_plan_sp : m_plans)
400 thread_plan_sp->ClearThreadCache();
401}
402
404 llvm::sys::ScopedWriter guard(m_stack_mutex);
405 m_completed_plans.clear();
406 m_discarded_plans.clear();
407}
408
410 bool delete_missing,
411 bool check_for_new) {
412
413 std::lock_guard<std::recursive_mutex> guard(m_stack_map_mutex);
414 // Now find all the new threads and add them to the map:
415 if (check_for_new) {
416 for (auto thread : current_threads.Threads()) {
417 lldb::tid_t cur_tid = thread->GetID();
418 if (!Find(cur_tid)) {
419 AddThread(*thread);
420 thread->QueueBasePlan(true);
421 }
422 }
423 }
424
425 // If we aren't reaping missing threads at this point,
426 // we are done.
427 if (!delete_missing)
428 return;
429 // Otherwise scan for absent TID's.
430 std::vector<lldb::tid_t> missing_threads;
431 // If we are going to delete plans from the plan stack,
432 // then scan for absent TID's:
433 for (auto &thread_plans : m_plans_list) {
434 lldb::tid_t cur_tid = thread_plans.first;
435 ThreadSP thread_sp = current_threads.FindThreadByID(cur_tid);
436 if (!thread_sp)
437 missing_threads.push_back(cur_tid);
438 }
439 for (lldb::tid_t tid : missing_threads) {
440 RemoveTID(tid);
441 }
442}
443
445 lldb::DescriptionLevel desc_level,
446 bool internal, bool condense_if_trivial,
447 bool skip_unreported) {
448 std::lock_guard<std::recursive_mutex> guard(m_stack_map_mutex);
449 for (auto &elem : m_plans_list) {
450 lldb::tid_t tid = elem.first;
451 uint32_t index_id = 0;
453
454 if (skip_unreported) {
455 if (!thread_sp)
456 continue;
457 }
458 if (thread_sp)
459 index_id = thread_sp->GetIndexID();
460
461 if (condense_if_trivial) {
462 if (!elem.second.AnyPlans() && !elem.second.AnyCompletedPlans() &&
463 !elem.second.AnyDiscardedPlans()) {
464 strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 "\n", index_id, tid);
465 strm.IndentMore();
466 strm.Indent();
467 strm.Printf("No active thread plans\n");
468 strm.IndentLess();
469 return;
470 }
471 }
472
473 strm.Indent();
474 strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 ":\n", index_id, tid);
475
476 elem.second.DumpThreadPlans(strm, desc_level, internal);
477 }
478}
479
481 lldb::DescriptionLevel desc_level,
482 bool internal,
483 bool condense_if_trivial,
484 bool skip_unreported) {
485 std::lock_guard<std::recursive_mutex> guard(m_stack_map_mutex);
486 uint32_t index_id = 0;
488
489 if (skip_unreported) {
490 if (!thread_sp) {
491 strm.Format("Unknown TID: {0}", tid);
492 return false;
493 }
494 }
495
496 if (thread_sp)
497 index_id = thread_sp->GetIndexID();
498 ThreadPlanStack *stack = Find(tid);
499 if (!stack) {
500 strm.Format("Unknown TID: {0}\n", tid);
501 return false;
502 }
503
504 if (condense_if_trivial) {
505 if (!stack->AnyPlans() && !stack->AnyCompletedPlans() &&
506 !stack->AnyDiscardedPlans()) {
507 strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 "\n", index_id, tid);
508 strm.IndentMore();
509 strm.Indent();
510 strm.Printf("No active thread plans\n");
511 strm.IndentLess();
512 return true;
513 }
514 }
515
516 strm.Indent();
517 strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 ":\n", index_id, tid);
518
519 stack->DumpThreadPlans(strm, desc_level, internal);
520 return true;
521}
522
524 // We only remove the plans for unreported TID's.
525 std::lock_guard<std::recursive_mutex> guard(m_stack_map_mutex);
527 if (thread_sp)
528 return false;
529
530 return RemoveTID(tid);
531}
uint32_t GetKind(uint32_t data)
Return the type kind encoded in the given data.
static void PrintPlanElement(Stream &s, const ThreadPlanSP &plan, lldb::DescriptionLevel desc_level, int32_t elem_idx)
ThreadList & GetThreadList()
Definition: Process.h:2182
A stream class that can stream formatted output to a file.
Definition: Stream.h:28
void Format(const char *format, Args &&... args)
Definition: Stream.h:353
size_t Indent(llvm::StringRef s="")
Indent the current line in the stream.
Definition: Stream.cpp:157
size_t Printf(const char *format,...) __attribute__((format(printf
Output printf formatted output to the stream.
Definition: Stream.cpp:134
size_t EOL()
Output and End of Line character to the stream.
Definition: Stream.cpp:155
void IndentLess(unsigned amount=2)
Decrement the current indentation level.
Definition: Stream.cpp:198
void IndentMore(unsigned amount=2)
Increment the current indentation level.
Definition: Stream.cpp:195
virtual ThreadIterable Threads()
lldb::ThreadSP FindThreadByID(lldb::tid_t tid, bool can_update=true)
Definition: ThreadList.cpp:102
bool RemoveTID(lldb::tid_t tid)
std::recursive_mutex m_stack_map_mutex
void DumpPlans(Stream &strm, lldb::DescriptionLevel desc_level, bool internal, bool ignore_boring, bool skip_unreported)
bool PrunePlansForTID(lldb::tid_t tid)
ThreadPlanStack * Find(lldb::tid_t tid)
void Update(ThreadList &current_threads, bool delete_missing, bool check_for_new=true)
bool DumpPlansForTID(Stream &strm, lldb::tid_t tid, lldb::DescriptionLevel desc_level, bool internal, bool ignore_boring, bool skip_unreported)
PlanStack m_discarded_plans
Plans that have been discarded by this stop.
void DiscardPlansUpToPlan(ThreadPlan *up_to_plan_ptr)
void DumpThreadPlans(Stream &s, lldb::DescriptionLevel desc_level, bool include_internal) const
lldb::ThreadPlanSP GetCompletedPlan(bool skip_private=true) const
lldb::ExpressionVariableSP GetExpressionVariable() const
PlanStack m_completed_plans
Plans that have been completed by this stop.
void ThreadDestroyed(Thread *thread)
ThreadPlanStack(const Thread &thread, bool make_empty=false)
llvm::sys::RWMutex m_stack_mutex
lldb::ThreadPlanSP GetCurrentPlanNoLock() const
bool IsPlanDone(ThreadPlan *plan) const
void RestoreCompletedPlanCheckpoint(size_t checkpoint)
lldb::ThreadPlanSP GetPlanByIndex(uint32_t plan_idx, bool skip_private=true) const
lldb::ValueObjectSP GetReturnValueObject() const
void PushPlan(lldb::ThreadPlanSP new_plan_sp)
lldb::ThreadPlanSP DiscardPlanNoLock()
lldb::ThreadPlanSP DiscardPlan()
std::unordered_map< size_t, PlanStack > m_completed_plan_store
bool WasPlanDiscarded(ThreadPlan *plan) const
void ClearThreadCache()
Clear the Thread* cache that each ThreadPlan contains.
void DiscardCompletedPlanCheckpoint(size_t checkpoint)
void PrintOneStackNoLock(Stream &s, llvm::StringRef stack_name, const PlanStack &stack, lldb::DescriptionLevel desc_level, bool include_internal) const
ThreadPlan * GetInnermostExpression() const
PlanStack m_plans
The stack of plans this thread is executing.
lldb::ThreadPlanSP GetCurrentPlan() const
ThreadPlan * GetPreviousPlan(ThreadPlan *current_plan) const
std::vector< lldb::ThreadPlanSP > PlanStack
A class that represents a running process on the host machine.
Definition: SBAddress.h:15
std::shared_ptr< lldb_private::ThreadPlan > ThreadPlanSP
Definition: lldb-forward.h:453
DescriptionLevel
Description levels for "void GetDescription(Stream *, DescriptionLevel)" calls.
std::shared_ptr< lldb_private::Thread > ThreadSP
Definition: lldb-forward.h:450
std::shared_ptr< lldb_private::ValueObject > ValueObjectSP
Definition: lldb-forward.h:484
std::shared_ptr< lldb_private::ExpressionVariable > ExpressionVariableSP
Definition: lldb-forward.h:351
uint64_t tid_t
Definition: lldb-types.h:84