xref: /llvm-project/lldb/include/lldb/Target/ThreadPlanStack.h (revision b42a81631491571c4b78d095917ebdddee69b04f)
1 //===-- ThreadPlanStack.h ---------------------------------------*- 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 #ifndef LLDB_TARGET_THREADPLANSTACK_H
10 #define LLDB_TARGET_THREADPLANSTACK_H
11 
12 #include <mutex>
13 #include <string>
14 #include <unordered_map>
15 #include <vector>
16 
17 #include "llvm/Support/RWMutex.h"
18 
19 #include "lldb/Target/Target.h"
20 #include "lldb/Target/Thread.h"
21 #include "lldb/lldb-private-forward.h"
22 #include "lldb/lldb-private.h"
23 
24 namespace lldb_private {
25 
26 // The ThreadPlans have a thread for use when they are asked all the ThreadPlan
27 // state machine questions, but they should never cache any pointers from their
28 // owning lldb_private::Thread.  That's because we want to be able to detach
29 // them from an owning thread, then reattach them by TID.
30 // The ThreadPlanStack holds the ThreadPlans for a given TID.  All its methods
31 // are private, and it should only be accessed through the owning thread.  When
32 // it is detached from a thread, all you can do is reattach it or delete it.
33 class ThreadPlanStack {
34   friend class lldb_private::Thread;
35 
36 public:
37   ThreadPlanStack(const Thread &thread, bool make_empty = false);
38   ~ThreadPlanStack() = default;
39 
40   using PlanStack = std::vector<lldb::ThreadPlanSP>;
41 
42   void DumpThreadPlans(Stream &s, lldb::DescriptionLevel desc_level,
43                        bool include_internal) const;
44 
45   size_t CheckpointCompletedPlans();
46 
47   void RestoreCompletedPlanCheckpoint(size_t checkpoint);
48 
49   void DiscardCompletedPlanCheckpoint(size_t checkpoint);
50 
51   void ThreadDestroyed(Thread *thread);
52 
53   void PushPlan(lldb::ThreadPlanSP new_plan_sp);
54 
55   lldb::ThreadPlanSP PopPlan();
56 
57   lldb::ThreadPlanSP DiscardPlan();
58 
59   // If the input plan is nullptr, discard all plans.  Otherwise make sure this
60   // plan is in the stack, and if so discard up to and including it.
61   void DiscardPlansUpToPlan(ThreadPlan *up_to_plan_ptr);
62 
63   void DiscardAllPlans();
64 
65   void DiscardConsultingControllingPlans();
66 
67   lldb::ThreadPlanSP GetCurrentPlan() const;
68 
69   lldb::ThreadPlanSP GetCompletedPlan(bool skip_private = true) const;
70 
71   lldb::ThreadPlanSP GetPlanByIndex(uint32_t plan_idx,
72                                     bool skip_private = true) const;
73 
74   lldb::ValueObjectSP GetReturnValueObject() const;
75 
76   lldb::ExpressionVariableSP GetExpressionVariable() const;
77 
78   bool AnyPlans() const;
79 
80   bool AnyCompletedPlans() const;
81 
82   bool AnyDiscardedPlans() const;
83 
84   bool IsPlanDone(ThreadPlan *plan) const;
85 
86   bool WasPlanDiscarded(ThreadPlan *plan) const;
87 
88   ThreadPlan *GetPreviousPlan(ThreadPlan *current_plan) const;
89 
90   ThreadPlan *GetInnermostExpression() const;
91 
92   void WillResume();
93 
94   /// Clear the Thread* cache that each ThreadPlan contains.
95   ///
96   /// This is useful in situations like when a new Thread list is being
97   /// generated.
98   void ClearThreadCache();
99 
100 private:
101   lldb::ThreadPlanSP DiscardPlanNoLock();
102   lldb::ThreadPlanSP GetCurrentPlanNoLock() const;
103   void PrintOneStackNoLock(Stream &s, llvm::StringRef stack_name,
104                            const PlanStack &stack,
105                            lldb::DescriptionLevel desc_level,
106                            bool include_internal) const;
107 
108   PlanStack m_plans;           ///< The stack of plans this thread is executing.
109   PlanStack m_completed_plans; ///< Plans that have been completed by this
110                                /// stop.  They get deleted when the thread
111                                /// resumes.
112   PlanStack m_discarded_plans; ///< Plans that have been discarded by this
113                                /// stop.  They get deleted when the thread
114                                /// resumes.
115   size_t m_completed_plan_checkpoint = 0; // Monotonically increasing token for
116                                           // completed plan checkpoints.
117   std::unordered_map<size_t, PlanStack> m_completed_plan_store;
118   mutable llvm::sys::RWMutex m_stack_mutex;
119 };
120 
121 class ThreadPlanStackMap {
122 public:
123   ThreadPlanStackMap(Process &process) : m_process(process) {}
124   ~ThreadPlanStackMap() = default;
125 
126   // Prune the map using the current_threads list.
127   void Update(ThreadList &current_threads, bool delete_missing,
128               bool check_for_new = true);
129 
130   void AddThread(Thread &thread) {
131     std::lock_guard<std::recursive_mutex> guard(m_stack_map_mutex);
132     lldb::tid_t tid = thread.GetID();
133     m_plans_list.emplace(tid, thread);
134   }
135 
136   bool RemoveTID(lldb::tid_t tid) {
137     std::lock_guard<std::recursive_mutex> guard(m_stack_map_mutex);
138     auto result = m_plans_list.find(tid);
139     if (result == m_plans_list.end())
140       return false;
141     result->second.ThreadDestroyed(nullptr);
142     m_plans_list.erase(result);
143     return true;
144   }
145 
146   ThreadPlanStack *Find(lldb::tid_t tid) {
147     std::lock_guard<std::recursive_mutex> guard(m_stack_map_mutex);
148     auto result = m_plans_list.find(tid);
149     if (result == m_plans_list.end())
150       return nullptr;
151     else
152       return &result->second;
153   }
154 
155   /// Clear the Thread* cache that each ThreadPlan contains.
156   ///
157   /// This is useful in situations like when a new Thread list is being
158   /// generated.
159   void ClearThreadCache() {
160     for (auto &plan_list : m_plans_list)
161       plan_list.second.ClearThreadCache();
162   }
163 
164   void Clear() {
165     std::lock_guard<std::recursive_mutex> guard(m_stack_map_mutex);
166     for (auto &plan : m_plans_list)
167       plan.second.ThreadDestroyed(nullptr);
168     m_plans_list.clear();
169   }
170 
171   // Implements Process::DumpThreadPlans
172   void DumpPlans(Stream &strm, lldb::DescriptionLevel desc_level, bool internal,
173                  bool ignore_boring, bool skip_unreported);
174 
175   // Implements Process::DumpThreadPlansForTID
176   bool DumpPlansForTID(Stream &strm, lldb::tid_t tid,
177                        lldb::DescriptionLevel desc_level, bool internal,
178                        bool ignore_boring, bool skip_unreported);
179 
180   bool PrunePlansForTID(lldb::tid_t tid);
181 
182 private:
183   Process &m_process;
184   mutable std::recursive_mutex m_stack_map_mutex;
185   using PlansList = std::unordered_map<lldb::tid_t, ThreadPlanStack>;
186   PlansList m_plans_list;
187 
188 };
189 
190 } // namespace lldb_private
191 
192 #endif // LLDB_TARGET_THREADPLANSTACK_H
193