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
PrintPlanElement(Stream & s,const ThreadPlanSP & plan,lldb::DescriptionLevel desc_level,int32_t elem_idx)19 static 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
ThreadPlanStack(const Thread & thread,bool make_null)30 ThreadPlanStack::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
DumpThreadPlans(Stream & s,lldb::DescriptionLevel desc_level,bool include_internal) const39 void ThreadPlanStack::DumpThreadPlans(Stream &s,
40 lldb::DescriptionLevel desc_level,
41 bool include_internal) const {
42 s.IndentMore();
43 PrintOneStack(s, "Active plan stack", m_plans, desc_level, include_internal);
44 PrintOneStack(s, "Completed plan stack", m_completed_plans, desc_level,
45 include_internal);
46 PrintOneStack(s, "Discarded plan stack", m_discarded_plans, desc_level,
47 include_internal);
48 s.IndentLess();
49 }
50
PrintOneStack(Stream & s,llvm::StringRef stack_name,const PlanStack & stack,lldb::DescriptionLevel desc_level,bool include_internal) const51 void ThreadPlanStack::PrintOneStack(Stream &s, llvm::StringRef stack_name,
52 const PlanStack &stack,
53 lldb::DescriptionLevel desc_level,
54 bool include_internal) const {
55 // If the stack is empty, just exit:
56 if (stack.empty())
57 return;
58
59 // Make sure there are public completed plans:
60 bool any_public = false;
61 if (!include_internal) {
62 for (auto plan : stack) {
63 if (!plan->GetPrivate()) {
64 any_public = true;
65 break;
66 }
67 }
68 }
69
70 if (include_internal || any_public) {
71 int print_idx = 0;
72 s.Indent();
73 s << stack_name << ":\n";
74 for (auto plan : stack) {
75 if (!include_internal && plan->GetPrivate())
76 continue;
77 PrintPlanElement(s, plan, desc_level, print_idx++);
78 }
79 }
80 }
81
CheckpointCompletedPlans()82 size_t ThreadPlanStack::CheckpointCompletedPlans() {
83 m_completed_plan_checkpoint++;
84 m_completed_plan_store.insert(
85 std::make_pair(m_completed_plan_checkpoint, m_completed_plans));
86 return m_completed_plan_checkpoint;
87 }
88
RestoreCompletedPlanCheckpoint(size_t checkpoint)89 void ThreadPlanStack::RestoreCompletedPlanCheckpoint(size_t checkpoint) {
90 auto result = m_completed_plan_store.find(checkpoint);
91 assert(result != m_completed_plan_store.end() &&
92 "Asked for a checkpoint that didn't exist");
93 m_completed_plans.swap((*result).second);
94 m_completed_plan_store.erase(result);
95 }
96
DiscardCompletedPlanCheckpoint(size_t checkpoint)97 void ThreadPlanStack::DiscardCompletedPlanCheckpoint(size_t checkpoint) {
98 m_completed_plan_store.erase(checkpoint);
99 }
100
ThreadDestroyed(Thread * thread)101 void ThreadPlanStack::ThreadDestroyed(Thread *thread) {
102 // Tell the plan stacks that this thread is going away:
103 for (ThreadPlanSP plan : m_plans)
104 plan->ThreadDestroyed();
105
106 for (ThreadPlanSP plan : m_discarded_plans)
107 plan->ThreadDestroyed();
108
109 for (ThreadPlanSP plan : m_completed_plans)
110 plan->ThreadDestroyed();
111
112 // Now clear the current plan stacks:
113 m_plans.clear();
114 m_discarded_plans.clear();
115 m_completed_plans.clear();
116
117 // Push a ThreadPlanNull on the plan stack. That way we can continue
118 // assuming that the plan stack is never empty, but if somebody errantly asks
119 // questions of a destroyed thread without checking first whether it is
120 // destroyed, they won't crash.
121 if (thread != nullptr) {
122 lldb::ThreadPlanSP null_plan_sp(new ThreadPlanNull(*thread));
123 m_plans.push_back(null_plan_sp);
124 }
125 }
126
EnableTracer(bool value,bool single_stepping)127 void ThreadPlanStack::EnableTracer(bool value, bool single_stepping) {
128 for (ThreadPlanSP plan : m_plans) {
129 if (plan->GetThreadPlanTracer()) {
130 plan->GetThreadPlanTracer()->EnableTracing(value);
131 plan->GetThreadPlanTracer()->EnableSingleStep(single_stepping);
132 }
133 }
134 }
135
SetTracer(lldb::ThreadPlanTracerSP & tracer_sp)136 void ThreadPlanStack::SetTracer(lldb::ThreadPlanTracerSP &tracer_sp) {
137 for (ThreadPlanSP plan : m_plans)
138 plan->SetThreadPlanTracer(tracer_sp);
139 }
140
PushPlan(lldb::ThreadPlanSP new_plan_sp)141 void ThreadPlanStack::PushPlan(lldb::ThreadPlanSP new_plan_sp) {
142 // If the thread plan doesn't already have a tracer, give it its parent's
143 // tracer:
144 // The first plan has to be a base plan:
145 assert((m_plans.size() > 0 || new_plan_sp->IsBasePlan()) &&
146 "Zeroth plan must be a base plan");
147
148 if (!new_plan_sp->GetThreadPlanTracer()) {
149 assert(!m_plans.empty());
150 new_plan_sp->SetThreadPlanTracer(m_plans.back()->GetThreadPlanTracer());
151 }
152 m_plans.push_back(new_plan_sp);
153 new_plan_sp->DidPush();
154 }
155
PopPlan()156 lldb::ThreadPlanSP ThreadPlanStack::PopPlan() {
157 assert(m_plans.size() > 1 && "Can't pop the base thread plan");
158
159 lldb::ThreadPlanSP plan_sp = std::move(m_plans.back());
160 m_completed_plans.push_back(plan_sp);
161 plan_sp->WillPop();
162 m_plans.pop_back();
163 return plan_sp;
164 }
165
DiscardPlan()166 lldb::ThreadPlanSP ThreadPlanStack::DiscardPlan() {
167 assert(m_plans.size() > 1 && "Can't discard the base thread plan");
168
169 lldb::ThreadPlanSP plan_sp = std::move(m_plans.back());
170 m_discarded_plans.push_back(plan_sp);
171 plan_sp->WillPop();
172 m_plans.pop_back();
173 return plan_sp;
174 }
175
176 // If the input plan is nullptr, discard all plans. Otherwise make sure this
177 // plan is in the stack, and if so discard up to and including it.
DiscardPlansUpToPlan(ThreadPlan * up_to_plan_ptr)178 void ThreadPlanStack::DiscardPlansUpToPlan(ThreadPlan *up_to_plan_ptr) {
179 int stack_size = m_plans.size();
180
181 if (up_to_plan_ptr == nullptr) {
182 for (int i = stack_size - 1; i > 0; i--)
183 DiscardPlan();
184 return;
185 }
186
187 bool found_it = false;
188 for (int i = stack_size - 1; i > 0; i--) {
189 if (m_plans[i].get() == up_to_plan_ptr) {
190 found_it = true;
191 break;
192 }
193 }
194
195 if (found_it) {
196 bool last_one = false;
197 for (int i = stack_size - 1; i > 0 && !last_one; i--) {
198 if (GetCurrentPlan().get() == up_to_plan_ptr)
199 last_one = true;
200 DiscardPlan();
201 }
202 }
203 }
204
DiscardAllPlans()205 void ThreadPlanStack::DiscardAllPlans() {
206 int stack_size = m_plans.size();
207 for (int i = stack_size - 1; i > 0; i--) {
208 DiscardPlan();
209 }
210 return;
211 }
212
DiscardConsultingMasterPlans()213 void ThreadPlanStack::DiscardConsultingMasterPlans() {
214 while (true) {
215 int master_plan_idx;
216 bool discard = true;
217
218 // Find the first master plan, see if it wants discarding, and if yes
219 // discard up to it.
220 for (master_plan_idx = m_plans.size() - 1; master_plan_idx >= 0;
221 master_plan_idx--) {
222 if (m_plans[master_plan_idx]->IsMasterPlan()) {
223 discard = m_plans[master_plan_idx]->OkayToDiscard();
224 break;
225 }
226 }
227
228 // If the master plan doesn't want to get discarded, then we're done.
229 if (!discard)
230 return;
231
232 // First pop all the dependent plans:
233 for (int i = m_plans.size() - 1; i > master_plan_idx; i--) {
234 DiscardPlan();
235 }
236
237 // Now discard the master plan itself.
238 // The bottom-most plan never gets discarded. "OkayToDiscard" for it
239 // means discard it's dependent plans, but not it...
240 if (master_plan_idx > 0) {
241 DiscardPlan();
242 }
243 }
244 }
245
GetCurrentPlan() const246 lldb::ThreadPlanSP ThreadPlanStack::GetCurrentPlan() const {
247 assert(m_plans.size() != 0 && "There will always be a base plan.");
248 return m_plans.back();
249 }
250
GetCompletedPlan(bool skip_private) const251 lldb::ThreadPlanSP ThreadPlanStack::GetCompletedPlan(bool skip_private) const {
252 if (m_completed_plans.empty())
253 return {};
254
255 if (!skip_private)
256 return m_completed_plans.back();
257
258 for (int i = m_completed_plans.size() - 1; i >= 0; i--) {
259 lldb::ThreadPlanSP completed_plan_sp;
260 completed_plan_sp = m_completed_plans[i];
261 if (!completed_plan_sp->GetPrivate())
262 return completed_plan_sp;
263 }
264 return {};
265 }
266
GetPlanByIndex(uint32_t plan_idx,bool skip_private) const267 lldb::ThreadPlanSP ThreadPlanStack::GetPlanByIndex(uint32_t plan_idx,
268 bool skip_private) const {
269 uint32_t idx = 0;
270
271 for (lldb::ThreadPlanSP plan_sp : m_plans) {
272 if (skip_private && plan_sp->GetPrivate())
273 continue;
274 if (idx == plan_idx)
275 return plan_sp;
276 idx++;
277 }
278 return {};
279 }
280
GetReturnValueObject() const281 lldb::ValueObjectSP ThreadPlanStack::GetReturnValueObject() const {
282 if (m_completed_plans.empty())
283 return {};
284
285 for (int i = m_completed_plans.size() - 1; i >= 0; i--) {
286 lldb::ValueObjectSP return_valobj_sp;
287 return_valobj_sp = m_completed_plans[i]->GetReturnValueObject();
288 if (return_valobj_sp)
289 return return_valobj_sp;
290 }
291 return {};
292 }
293
GetExpressionVariable() const294 lldb::ExpressionVariableSP ThreadPlanStack::GetExpressionVariable() const {
295 if (m_completed_plans.empty())
296 return {};
297
298 for (int i = m_completed_plans.size() - 1; i >= 0; i--) {
299 lldb::ExpressionVariableSP expression_variable_sp;
300 expression_variable_sp = m_completed_plans[i]->GetExpressionVariable();
301 if (expression_variable_sp)
302 return expression_variable_sp;
303 }
304 return {};
305 }
AnyPlans() const306 bool ThreadPlanStack::AnyPlans() const {
307 // There is always a base plan...
308 return m_plans.size() > 1;
309 }
310
AnyCompletedPlans() const311 bool ThreadPlanStack::AnyCompletedPlans() const {
312 return !m_completed_plans.empty();
313 }
314
AnyDiscardedPlans() const315 bool ThreadPlanStack::AnyDiscardedPlans() const {
316 return !m_discarded_plans.empty();
317 }
318
IsPlanDone(ThreadPlan * in_plan) const319 bool ThreadPlanStack::IsPlanDone(ThreadPlan *in_plan) const {
320 for (auto plan : m_completed_plans) {
321 if (plan.get() == in_plan)
322 return true;
323 }
324 return false;
325 }
326
WasPlanDiscarded(ThreadPlan * in_plan) const327 bool ThreadPlanStack::WasPlanDiscarded(ThreadPlan *in_plan) const {
328 for (auto plan : m_discarded_plans) {
329 if (plan.get() == in_plan)
330 return true;
331 }
332 return false;
333 }
334
GetPreviousPlan(ThreadPlan * current_plan) const335 ThreadPlan *ThreadPlanStack::GetPreviousPlan(ThreadPlan *current_plan) const {
336 if (current_plan == nullptr)
337 return nullptr;
338
339 // Look first in the completed plans, if the plan is here and there is
340 // a completed plan above it, return that.
341 int stack_size = m_completed_plans.size();
342 for (int i = stack_size - 1; i > 0; i--) {
343 if (current_plan == m_completed_plans[i].get())
344 return m_completed_plans[i - 1].get();
345 }
346
347 // If this is the first completed plan, the previous one is the
348 // bottom of the regular plan stack.
349 if (stack_size > 0 && m_completed_plans[0].get() == current_plan) {
350 return GetCurrentPlan().get();
351 }
352
353 // Otherwise look for it in the regular plans.
354 stack_size = m_plans.size();
355 for (int i = stack_size - 1; i > 0; i--) {
356 if (current_plan == m_plans[i].get())
357 return m_plans[i - 1].get();
358 }
359 return nullptr;
360 }
361
GetInnermostExpression() const362 ThreadPlan *ThreadPlanStack::GetInnermostExpression() const {
363 int stack_size = m_plans.size();
364
365 for (int i = stack_size - 1; i > 0; i--) {
366 if (m_plans[i]->GetKind() == ThreadPlan::eKindCallFunction)
367 return m_plans[i].get();
368 }
369 return nullptr;
370 }
371
WillResume()372 void ThreadPlanStack::WillResume() {
373 m_completed_plans.clear();
374 m_discarded_plans.clear();
375 }
376
377 const ThreadPlanStack::PlanStack &
GetStackOfKind(ThreadPlanStack::StackKind kind) const378 ThreadPlanStack::GetStackOfKind(ThreadPlanStack::StackKind kind) const {
379 switch (kind) {
380 case ePlans:
381 return m_plans;
382 case eCompletedPlans:
383 return m_completed_plans;
384 case eDiscardedPlans:
385 return m_discarded_plans;
386 }
387 llvm_unreachable("Invalid StackKind value");
388 }
389
Update(ThreadList & current_threads,bool delete_missing,bool check_for_new)390 void ThreadPlanStackMap::Update(ThreadList ¤t_threads,
391 bool delete_missing,
392 bool check_for_new) {
393
394 // Now find all the new threads and add them to the map:
395 if (check_for_new) {
396 for (auto thread : current_threads.Threads()) {
397 lldb::tid_t cur_tid = thread->GetID();
398 if (!Find(cur_tid)) {
399 AddThread(*thread.get());
400 thread->QueueFundamentalPlan(true);
401 }
402 }
403 }
404
405 // If we aren't reaping missing threads at this point,
406 // we are done.
407 if (!delete_missing)
408 return;
409 // Otherwise scan for absent TID's.
410 std::vector<lldb::tid_t> missing_threads;
411 // If we are going to delete plans from the plan stack,
412 // then scan for absent TID's:
413 for (auto thread_plans : m_plans_list) {
414 lldb::tid_t cur_tid = thread_plans.first;
415 ThreadSP thread_sp = current_threads.FindThreadByID(cur_tid);
416 if (!thread_sp)
417 missing_threads.push_back(cur_tid);
418 }
419 for (lldb::tid_t tid : missing_threads) {
420 RemoveTID(tid);
421 }
422 }
423
DumpPlans(Stream & strm,lldb::DescriptionLevel desc_level,bool internal,bool condense_if_trivial,bool skip_unreported)424 void ThreadPlanStackMap::DumpPlans(Stream &strm,
425 lldb::DescriptionLevel desc_level,
426 bool internal, bool condense_if_trivial,
427 bool skip_unreported) {
428 for (auto elem : m_plans_list) {
429 lldb::tid_t tid = elem.first;
430 uint32_t index_id = 0;
431 ThreadSP thread_sp = m_process.GetThreadList().FindThreadByID(tid);
432
433 if (skip_unreported) {
434 if (!thread_sp)
435 continue;
436 }
437 if (thread_sp)
438 index_id = thread_sp->GetIndexID();
439
440 if (condense_if_trivial) {
441 if (!elem.second.AnyPlans() && !elem.second.AnyCompletedPlans() &&
442 !elem.second.AnyDiscardedPlans()) {
443 strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 "\n", index_id, tid);
444 strm.IndentMore();
445 strm.Indent();
446 strm.Printf("No active thread plans\n");
447 strm.IndentLess();
448 return;
449 }
450 }
451
452 strm.Indent();
453 strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 ":\n", index_id, tid);
454
455 elem.second.DumpThreadPlans(strm, desc_level, internal);
456 }
457 }
458
DumpPlansForTID(Stream & strm,lldb::tid_t tid,lldb::DescriptionLevel desc_level,bool internal,bool condense_if_trivial,bool skip_unreported)459 bool ThreadPlanStackMap::DumpPlansForTID(Stream &strm, lldb::tid_t tid,
460 lldb::DescriptionLevel desc_level,
461 bool internal,
462 bool condense_if_trivial,
463 bool skip_unreported) {
464 uint32_t index_id = 0;
465 ThreadSP thread_sp = m_process.GetThreadList().FindThreadByID(tid);
466
467 if (skip_unreported) {
468 if (!thread_sp) {
469 strm.Format("Unknown TID: {0}", tid);
470 return false;
471 }
472 }
473
474 if (thread_sp)
475 index_id = thread_sp->GetIndexID();
476 ThreadPlanStack *stack = Find(tid);
477 if (!stack) {
478 strm.Format("Unknown TID: {0}\n", tid);
479 return false;
480 }
481
482 if (condense_if_trivial) {
483 if (!stack->AnyPlans() && !stack->AnyCompletedPlans() &&
484 !stack->AnyDiscardedPlans()) {
485 strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 "\n", index_id, tid);
486 strm.IndentMore();
487 strm.Indent();
488 strm.Printf("No active thread plans\n");
489 strm.IndentLess();
490 return true;
491 }
492 }
493
494 strm.Indent();
495 strm.Printf("thread #%u: tid = 0x%4.4" PRIx64 ":\n", index_id, tid);
496
497 stack->DumpThreadPlans(strm, desc_level, internal);
498 return true;
499 }
500
PrunePlansForTID(lldb::tid_t tid)501 bool ThreadPlanStackMap::PrunePlansForTID(lldb::tid_t tid) {
502 // We only remove the plans for unreported TID's.
503 ThreadSP thread_sp = m_process.GetThreadList().FindThreadByID(tid);
504 if (thread_sp)
505 return false;
506
507 return RemoveTID(tid);
508 }
509