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