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
16#include <unordered_set>
17
18using namespace lldb;
19using namespace lldb_private;
20
21static void PrintPlanElement(Stream &s, const ThreadPlanSP &plan,
22 lldb::DescriptionLevel desc_level,
23 int32_t elem_idx) {
24 s.IndentMore();
25 s.Indent();
26 s.Printf("Element %d: ", elem_idx);
27 plan->GetDescription(&s, desc_level);
28 s.EOL();
29 s.IndentLess();
30}
31
32ThreadPlanStack::ThreadPlanStack(const Thread &thread, bool make_null) {
33 if (make_null) {
34 // The ThreadPlanNull doesn't do anything to the Thread, so this is actually
35 // still a const operation.
36 m_plans.push_back(
37 ThreadPlanSP(new ThreadPlanNull(const_cast<Thread &>(thread))));
38 }
39}
40
42 lldb::DescriptionLevel desc_level,
43 bool include_internal) const {
44 llvm::sys::ScopedReader guard(m_stack_mutex);
45 s.IndentMore();
46 PrintOneStackNoLock(s, "Active plan stack", m_plans, desc_level,
47 include_internal);
48 PrintOneStackNoLock(s, "Completed plan stack", m_completed_plans, desc_level,
49 include_internal);
50 PrintOneStackNoLock(s, "Discarded plan stack", m_discarded_plans, desc_level,
51 include_internal);
52 s.IndentLess();
53}
54
55void ThreadPlanStack::PrintOneStackNoLock(Stream &s, llvm::StringRef stack_name,
56 const PlanStack &stack,
57 lldb::DescriptionLevel desc_level,
58 bool include_internal) const {
59 // If the stack is empty, just exit:
60 if (stack.empty())
61 return;
62
63 // Make sure there are public completed plans:
64 bool any_public = false;
65 if (!include_internal) {
66 for (auto plan : stack) {
67 if (!plan->GetPrivate()) {
68 any_public = true;
69 break;
70 }
71 }
72 }
73
74 if (include_internal || any_public) {
75 int print_idx = 0;
76 s.Indent();
77 s << stack_name << ":\n";
78 for (auto plan : stack) {
79 if (!include_internal && plan->GetPrivate())
80 continue;
81 PrintPlanElement(s, plan, desc_level, print_idx++);
82 }
83 }
84}
85
93
95 llvm::sys::ScopedWriter guard(m_stack_mutex);
96 auto result = m_completed_plan_store.find(checkpoint);
97 assert(result != m_completed_plan_store.end() &&
98 "Asked for a checkpoint that didn't exist");
99 m_completed_plans.swap((*result).second);
100 m_completed_plan_store.erase(result);
101}
102
104 llvm::sys::ScopedWriter guard(m_stack_mutex);
105 m_completed_plan_store.erase(checkpoint);
106}
107
109 // Tell the plan stacks that this thread is going away:
110 llvm::sys::ScopedWriter guard(m_stack_mutex);
111 for (ThreadPlanSP plan : m_plans)
112 plan->ThreadDestroyed();
113
115 plan->ThreadDestroyed();
116
118 plan->ThreadDestroyed();
119
120 // Now clear the current plan stacks:
121 m_plans.clear();
122 m_discarded_plans.clear();
123 m_completed_plans.clear();
124
125 // Push a ThreadPlanNull on the plan stack. That way we can continue
126 // assuming that the plan stack is never empty, but if somebody errantly asks
127 // questions of a destroyed thread without checking first whether it is
128 // destroyed, they won't crash.
129 if (thread != nullptr) {
130 lldb::ThreadPlanSP null_plan_sp(new ThreadPlanNull(*thread));
131 m_plans.push_back(null_plan_sp);
132 }
133}
134
136 // If the thread plan doesn't already have a tracer, give it its parent's
137 // tracer:
138 // The first plan has to be a base plan:
139 { // Scope for Lock - DidPush often adds plans to the stack:
140 llvm::sys::ScopedWriter guard(m_stack_mutex);
141 assert((m_plans.size() > 0 || new_plan_sp->IsBasePlan()) &&
142 "Zeroth plan must be a base plan");
143
144 if (!new_plan_sp->GetThreadPlanTracer()) {
145 assert(!m_plans.empty());
146 new_plan_sp->SetThreadPlanTracer(m_plans.back()->GetThreadPlanTracer());
147 }
148 m_plans.push_back(new_plan_sp);
149 }
150 new_plan_sp->DidPush();
151}
152
154 llvm::sys::ScopedWriter guard(m_stack_mutex);
155 assert(m_plans.size() > 1 && "Can't pop the base thread plan");
156
157 // Note that moving the top element of the vector would leave it in an
158 // undefined state, and break the guarantee that the stack's thread plans are
159 // all valid.
160 lldb::ThreadPlanSP plan_sp = m_plans.back();
161 m_plans.pop_back();
162 m_completed_plans.push_back(plan_sp);
163 plan_sp->DidPop();
164 return plan_sp;
165}
166
168 llvm::sys::ScopedWriter guard(m_stack_mutex);
169 return DiscardPlanNoLock();
170}
171
173 assert(m_plans.size() > 1 && "Can't discard the base thread plan");
174
175 // Note that moving the top element of the vector would leave it in an
176 // undefined state, and break the guarantee that the stack's thread plans are
177 // all valid.
178 lldb::ThreadPlanSP plan_sp = m_plans.back();
179 m_plans.pop_back();
180 m_discarded_plans.push_back(plan_sp);
181 plan_sp->DidPop();
182 return plan_sp;
183}
184
185// If the input plan is nullptr, discard all plans. Otherwise make sure this
186// plan is in the stack, and if so discard up to and including it.
188 llvm::sys::ScopedWriter guard(m_stack_mutex);
189 int stack_size = m_plans.size();
190
191 if (up_to_plan_ptr == nullptr) {
192 for (int i = stack_size - 1; i > 0; i--)
194 return;
195 }
196
197 bool found_it = false;
198 for (int i = stack_size - 1; i > 0; i--) {
199 if (m_plans[i].get() == up_to_plan_ptr) {
200 found_it = true;
201 break;
202 }
203 }
204
205 if (found_it) {
206 bool last_one = false;
207 for (int i = stack_size - 1; i > 0 && !last_one; i--) {
208 if (GetCurrentPlanNoLock().get() == up_to_plan_ptr)
209 last_one = true;
211 }
212 }
213}
214
216 llvm::sys::ScopedWriter guard(m_stack_mutex);
217 int stack_size = m_plans.size();
218 for (int i = stack_size - 1; i > 0; i--) {
220 }
221}
222
224 llvm::sys::ScopedWriter guard(m_stack_mutex);
225 while (true) {
226 int controlling_plan_idx;
227 bool discard = true;
228
229 // Find the first controlling plan, see if it wants discarding, and if yes
230 // discard up to it.
231 for (controlling_plan_idx = m_plans.size() - 1; controlling_plan_idx >= 0;
232 controlling_plan_idx--) {
233 if (m_plans[controlling_plan_idx]->IsControllingPlan()) {
234 discard = m_plans[controlling_plan_idx]->OkayToDiscard();
235 break;
236 }
237 }
238
239 // If the controlling plan doesn't want to get discarded, then we're done.
240 if (!discard)
241 return;
242
243 // First pop all the dependent plans:
244 for (int i = m_plans.size() - 1; i > controlling_plan_idx; i--) {
246 }
247
248 // Now discard the controlling plan itself.
249 // The bottom-most plan never gets discarded. "OkayToDiscard" for it
250 // means discard it's dependent plans, but not it...
251 if (controlling_plan_idx > 0) {
253 }
254 }
255}
256
258 llvm::sys::ScopedReader guard(m_stack_mutex);
259 return GetCurrentPlanNoLock();
260}
261
263 assert(m_plans.size() != 0 && "There will always be a base plan.");
264 return m_plans.back();
265}
266
268 llvm::sys::ScopedReader guard(m_stack_mutex);
269 if (m_completed_plans.empty())
270 return {};
271
272 if (!skip_private)
273 return m_completed_plans.back();
274
275 for (int i = m_completed_plans.size() - 1; i >= 0; i--) {
276 lldb::ThreadPlanSP completed_plan_sp;
277 completed_plan_sp = m_completed_plans[i];
278 if (!completed_plan_sp->GetPrivate())
279 return completed_plan_sp;
280 }
281 return {};
282}
283
285 bool skip_private) const {
286 llvm::sys::ScopedReader guard(m_stack_mutex);
287 uint32_t idx = 0;
288
289 for (lldb::ThreadPlanSP plan_sp : m_plans) {
290 if (skip_private && plan_sp->GetPrivate())
291 continue;
292 if (idx == plan_idx)
293 return plan_sp;
294 idx++;
295 }
296 return {};
297}
298
300 llvm::sys::ScopedReader guard(m_stack_mutex);
301 if (m_completed_plans.empty())
302 return {};
303
304 for (int i = m_completed_plans.size() - 1; i >= 0; i--) {
305 lldb::ValueObjectSP return_valobj_sp;
306 return_valobj_sp = m_completed_plans[i]->GetReturnValueObject();
307 if (return_valobj_sp)
308 return return_valobj_sp;
309 }
310 return {};
311}
312
314 llvm::sys::ScopedReader guard(m_stack_mutex);
315 if (m_completed_plans.empty())
316 return {};
317
318 for (int i = m_completed_plans.size() - 1; i >= 0; i--) {
319 lldb::ExpressionVariableSP expression_variable_sp;
320 expression_variable_sp = m_completed_plans[i]->GetExpressionVariable();
321 if (expression_variable_sp)
322 return expression_variable_sp;
323 }
324 return {};
325}
327 llvm::sys::ScopedReader guard(m_stack_mutex);
328 // There is always a base plan...
329 return m_plans.size() > 1;
330}
331
333 llvm::sys::ScopedReader guard(m_stack_mutex);
334 return !m_completed_plans.empty();
335}
336
338 llvm::sys::ScopedReader guard(m_stack_mutex);
339 return !m_discarded_plans.empty();
340}
341
343 llvm::sys::ScopedReader guard(m_stack_mutex);
344 for (auto plan : m_completed_plans) {
345 if (plan.get() == in_plan)
346 return true;
347 }
348 return false;
349}
350
352 llvm::sys::ScopedReader guard(m_stack_mutex);
353 for (auto plan : m_discarded_plans) {
354 if (plan.get() == in_plan)
355 return true;
356 }
357 return false;
358}
359
361 llvm::sys::ScopedReader guard(m_stack_mutex);
362 if (current_plan == nullptr)
363 return nullptr;
364
365 // Look first in the completed plans, if the plan is here and there is
366 // a completed plan above it, return that.
367 int stack_size = m_completed_plans.size();
368 for (int i = stack_size - 1; i > 0; i--) {
369 if (current_plan == m_completed_plans[i].get())
370 return m_completed_plans[i - 1].get();
371 }
372
373 // If this is the first completed plan, the previous one is the
374 // bottom of the regular plan stack.
375 if (stack_size > 0 && m_completed_plans[0].get() == current_plan) {
376 return GetCurrentPlanNoLock().get();
377 }
378
379 // Otherwise look for it in the regular plans.
380 stack_size = m_plans.size();
381 for (int i = stack_size - 1; i > 0; i--) {
382 if (current_plan == m_plans[i].get())
383 return m_plans[i - 1].get();
384 }
385 return nullptr;
386}
387
389 llvm::sys::ScopedReader guard(m_stack_mutex);
390 int stack_size = m_plans.size();
391
392 for (int i = stack_size - 1; i > 0; i--) {
394 return m_plans[i].get();
395 }
396 return nullptr;
397}
398
400 llvm::sys::ScopedReader guard(m_stack_mutex);
401 for (lldb::ThreadPlanSP thread_plan_sp : m_plans)
402 thread_plan_sp->ClearThreadCache();
403}
404
406 llvm::sys::ScopedWriter guard(m_stack_mutex);
407 m_completed_plans.clear();
408 m_discarded_plans.clear();
409}
410
412 bool delete_missing,
413 bool check_for_new) {
414
415 std::lock_guard<std::recursive_mutex> guard(m_stack_map_mutex);
416 assert(current_threads.GetStopID() == m_process.GetStopID() &&
417 "thread list is stale");
418 // Now find all the new threads and add them to the map:
419 if (check_for_new) {
420 for (auto thread : current_threads.Threads()) {
421 lldb::tid_t cur_tid = thread->GetID();
422 if (!Find(cur_tid)) {
423 AddThread(*thread);
424 thread->QueueBasePlan(true);
425 }
426 }
427 }
428
429 // If we aren't reaping missing threads or have no plans, we are done.
430 if (!delete_missing || m_plans_list.empty())
431 return;
432
433 // Index the already-current thread list rather than searching it separately
434 // for every plan. Repeated linear searches become prohibitively expensive
435 // for targets with large thread counts.
436 std::unordered_set<lldb::tid_t> live_tids;
437 live_tids.reserve(current_threads.GetSize(/*can_update=*/false));
438 for (const auto &thread : current_threads.Threads())
439 live_tids.insert(thread->GetID());
440
441 std::vector<lldb::tid_t> missing_threads;
442 // If we are going to delete plans from the plan stack,
443 // then scan for absent TID's:
444 for (auto &thread_plans : m_plans_list) {
445 lldb::tid_t cur_tid = thread_plans.first;
446 if (live_tids.find(cur_tid) == live_tids.end())
447 missing_threads.push_back(cur_tid);
448 }
449 for (lldb::tid_t tid : missing_threads) {
450 RemoveTID(tid);
451 }
452}
453
455 lldb::DescriptionLevel desc_level,
456 bool internal, bool condense_if_trivial,
457 bool skip_unreported) {
458 std::lock_guard<std::recursive_mutex> guard(m_stack_map_mutex);
459 for (auto &elem : m_plans_list) {
460 lldb::tid_t tid = elem.first;
461 uint32_t index_id = 0;
462 ThreadSP thread_sp = m_process.GetThreadList().FindThreadByID(tid);
463
464 if (skip_unreported) {
465 if (!thread_sp)
466 continue;
467 }
468 if (thread_sp)
469 index_id = thread_sp->GetIndexID();
470
471 if (condense_if_trivial) {
472 if (!elem.second.AnyPlans() && !elem.second.AnyCompletedPlans() &&
473 !elem.second.AnyDiscardedPlans()) {
474 strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 "\n", index_id, tid);
475 strm.IndentMore();
476 strm.Indent();
477 strm.PutCString("No active thread plans\n");
478 strm.IndentLess();
479 return;
480 }
481 }
482
483 strm.Indent();
484 strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 ":\n", index_id, tid);
485
486 elem.second.DumpThreadPlans(strm, desc_level, internal);
487 }
488}
489
491 lldb::DescriptionLevel desc_level,
492 bool internal,
493 bool condense_if_trivial,
494 bool skip_unreported) {
495 std::lock_guard<std::recursive_mutex> guard(m_stack_map_mutex);
496 uint32_t index_id = 0;
497 ThreadSP thread_sp = m_process.GetThreadList().FindThreadByID(tid);
498
499 if (skip_unreported) {
500 if (!thread_sp) {
501 strm.Format("Unknown TID: {0}", tid);
502 return false;
503 }
504 }
505
506 if (thread_sp)
507 index_id = thread_sp->GetIndexID();
508 ThreadPlanStack *stack = Find(tid);
509 if (!stack) {
510 strm.Format("Unknown TID: {0}\n", tid);
511 return false;
512 }
513
514 if (condense_if_trivial) {
515 if (!stack->AnyPlans() && !stack->AnyCompletedPlans() &&
516 !stack->AnyDiscardedPlans()) {
517 strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 "\n", index_id, tid);
518 strm.IndentMore();
519 strm.Indent();
520 strm.PutCString("No active thread plans\n");
521 strm.IndentLess();
522 return true;
523 }
524 }
525
526 strm.Indent();
527 strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 ":\n", index_id, tid);
528
529 stack->DumpThreadPlans(strm, desc_level, internal);
530 return true;
531}
532
534 // We only remove the plans for unreported TID's.
535 std::lock_guard<std::recursive_mutex> guard(m_stack_map_mutex);
536 ThreadSP thread_sp = m_process.GetThreadList().FindThreadByID(tid);
537 if (thread_sp)
538 return false;
539
540 return RemoveTID(tid);
541}
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)
A stream class that can stream formatted output to a file.
Definition Stream.h:28
void Format(const char *format, Args &&... args)
Forwards the arguments to llvm::formatv and writes to the stream.
Definition Stream.h:370
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 PutCString(llvm::StringRef cstr)
Output a C string to the stream.
Definition Stream.cpp:63
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:204
void IndentMore(unsigned amount=2)
Increment the current indentation level.
Definition Stream.cpp:201
virtual ThreadIterable Threads()
uint32_t GetStopID() const
uint32_t GetSize(bool can_update=true)
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
friend class lldb_private::Thread
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)
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()
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.
std::shared_ptr< lldb_private::ThreadPlan > ThreadPlanSP
DescriptionLevel
Description levels for "void GetDescription(Stream *, DescriptionLevel)" calls.
std::shared_ptr< lldb_private::Thread > ThreadSP
std::shared_ptr< lldb_private::ValueObject > ValueObjectSP
std::shared_ptr< lldb_private::ExpressionVariable > ExpressionVariableSP
uint64_t tid_t
Definition lldb-types.h:85