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