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