• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 &current_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