1 | //===-- ThreadPlan.cpp ----------------------------------------------------===// |
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/ThreadPlan.h" |
10 | #include "lldb/Core/Debugger.h" |
11 | #include "lldb/Target/Process.h" |
12 | #include "lldb/Target/RegisterContext.h" |
13 | #include "lldb/Target/Target.h" |
14 | #include "lldb/Target/Thread.h" |
15 | #include "lldb/Utility/LLDBLog.h" |
16 | #include "lldb/Utility/Log.h" |
17 | #include "lldb/Utility/State.h" |
18 | |
19 | using namespace lldb; |
20 | using namespace lldb_private; |
21 | |
22 | // ThreadPlan constructor |
23 | ThreadPlan::ThreadPlan(ThreadPlanKind kind, const char *name, Thread &thread, |
24 | Vote report_stop_vote, Vote report_run_vote) |
25 | : m_process(*thread.GetProcess().get()), m_tid(thread.GetID()), |
26 | m_report_stop_vote(report_stop_vote), m_report_run_vote(report_run_vote), |
27 | m_takes_iteration_count(false), m_could_not_resolve_hw_bp(false), |
28 | m_thread(&thread), m_kind(kind), m_name(name), m_plan_complete_mutex(), |
29 | m_cached_plan_explains_stop(eLazyBoolCalculate), m_plan_complete(false), |
30 | m_plan_private(false), m_okay_to_discard(true), |
31 | m_is_controlling_plan(false), m_plan_succeeded(true) { |
32 | SetID(GetNextID()); |
33 | } |
34 | |
35 | // Destructor |
36 | ThreadPlan::~ThreadPlan() = default; |
37 | |
38 | Target &ThreadPlan::GetTarget() { return m_process.GetTarget(); } |
39 | |
40 | const Target &ThreadPlan::GetTarget() const { return m_process.GetTarget(); } |
41 | |
42 | Thread &ThreadPlan::GetThread() { |
43 | if (m_thread) |
44 | return *m_thread; |
45 | |
46 | ThreadSP thread_sp = m_process.GetThreadList().FindThreadByID(tid: m_tid); |
47 | m_thread = thread_sp.get(); |
48 | return *m_thread; |
49 | } |
50 | |
51 | bool ThreadPlan::PlanExplainsStop(Event *event_ptr) { |
52 | if (m_cached_plan_explains_stop == eLazyBoolCalculate) { |
53 | bool actual_value = DoPlanExplainsStop(event_ptr); |
54 | CachePlanExplainsStop(does_explain: actual_value); |
55 | return actual_value; |
56 | } else { |
57 | return m_cached_plan_explains_stop == eLazyBoolYes; |
58 | } |
59 | } |
60 | |
61 | bool ThreadPlan::IsPlanComplete() { |
62 | std::lock_guard<std::recursive_mutex> guard(m_plan_complete_mutex); |
63 | return m_plan_complete; |
64 | } |
65 | |
66 | void ThreadPlan::SetPlanComplete(bool success) { |
67 | std::lock_guard<std::recursive_mutex> guard(m_plan_complete_mutex); |
68 | m_plan_complete = true; |
69 | m_plan_succeeded = success; |
70 | } |
71 | |
72 | bool ThreadPlan::MischiefManaged() { |
73 | std::lock_guard<std::recursive_mutex> guard(m_plan_complete_mutex); |
74 | // Mark the plan is complete, but don't override the success flag. |
75 | m_plan_complete = true; |
76 | return true; |
77 | } |
78 | |
79 | Vote ThreadPlan::ShouldReportStop(Event *event_ptr) { |
80 | Log *log = GetLog(mask: LLDBLog::Step); |
81 | |
82 | if (m_report_stop_vote == eVoteNoOpinion) { |
83 | ThreadPlan *prev_plan = GetPreviousPlan(); |
84 | if (prev_plan) { |
85 | Vote prev_vote = prev_plan->ShouldReportStop(event_ptr); |
86 | LLDB_LOG(log, "returning previous thread plan vote: {0}" , prev_vote); |
87 | return prev_vote; |
88 | } |
89 | } |
90 | LLDB_LOG(log, "Returning vote: {0}" , m_report_stop_vote); |
91 | return m_report_stop_vote; |
92 | } |
93 | |
94 | Vote ThreadPlan::ShouldReportRun(Event *event_ptr) { |
95 | if (m_report_run_vote == eVoteNoOpinion) { |
96 | ThreadPlan *prev_plan = GetPreviousPlan(); |
97 | if (prev_plan) |
98 | return prev_plan->ShouldReportRun(event_ptr); |
99 | } |
100 | return m_report_run_vote; |
101 | } |
102 | |
103 | void ThreadPlan::ClearThreadCache() { m_thread = nullptr; } |
104 | |
105 | bool ThreadPlan::StopOthers() { |
106 | ThreadPlan *prev_plan; |
107 | prev_plan = GetPreviousPlan(); |
108 | return (prev_plan == nullptr) ? false : prev_plan->StopOthers(); |
109 | } |
110 | |
111 | void ThreadPlan::SetStopOthers(bool new_value) { |
112 | // SetStopOthers doesn't work up the hierarchy. You have to set the explicit |
113 | // ThreadPlan you want to affect. |
114 | } |
115 | |
116 | bool ThreadPlan::WillResume(StateType resume_state, bool current_plan) { |
117 | m_cached_plan_explains_stop = eLazyBoolCalculate; |
118 | |
119 | if (current_plan) { |
120 | Log *log = GetLog(mask: LLDBLog::Step); |
121 | |
122 | if (log) { |
123 | RegisterContext *reg_ctx = GetThread().GetRegisterContext().get(); |
124 | assert(reg_ctx); |
125 | addr_t pc = reg_ctx->GetPC(); |
126 | addr_t sp = reg_ctx->GetSP(); |
127 | addr_t fp = reg_ctx->GetFP(); |
128 | LLDB_LOGF( |
129 | log, |
130 | "%s Thread #%u (0x%p): tid = 0x%4.4" PRIx64 ", pc = 0x%8.8" PRIx64 |
131 | ", sp = 0x%8.8" PRIx64 ", fp = 0x%8.8" PRIx64 ", " |
132 | "plan = '%s', state = %s, stop others = %d" , |
133 | __FUNCTION__, GetThread().GetIndexID(), |
134 | static_cast<void *>(&GetThread()), m_tid, static_cast<uint64_t>(pc), |
135 | static_cast<uint64_t>(sp), static_cast<uint64_t>(fp), m_name.c_str(), |
136 | StateAsCString(resume_state), StopOthers()); |
137 | } |
138 | } |
139 | bool success = DoWillResume(resume_state, current_plan); |
140 | ClearThreadCache(); // We don't cache the thread pointer over resumes. This |
141 | // Thread might go away, and another Thread represent |
142 | // the same underlying object on a later stop. |
143 | return success; |
144 | } |
145 | |
146 | lldb::user_id_t ThreadPlan::GetNextID() { |
147 | static uint32_t g_nextPlanID = 0; |
148 | return ++g_nextPlanID; |
149 | } |
150 | |
151 | void ThreadPlan::DidPush() {} |
152 | |
153 | void ThreadPlan::DidPop() {} |
154 | |
155 | bool ThreadPlan::OkayToDiscard() { |
156 | return IsControllingPlan() ? m_okay_to_discard : true; |
157 | } |
158 | |
159 | lldb::StateType ThreadPlan::RunState() { |
160 | if (m_tracer_sp && m_tracer_sp->TracingEnabled()) |
161 | return eStateStepping; |
162 | else |
163 | return GetPlanRunState(); |
164 | } |
165 | |
166 | bool ThreadPlan::IsUsuallyUnexplainedStopReason(lldb::StopReason reason) { |
167 | switch (reason) { |
168 | case eStopReasonWatchpoint: |
169 | case eStopReasonSignal: |
170 | case eStopReasonException: |
171 | case eStopReasonExec: |
172 | case eStopReasonThreadExiting: |
173 | case eStopReasonInstrumentation: |
174 | case eStopReasonFork: |
175 | case eStopReasonVFork: |
176 | case eStopReasonVForkDone: |
177 | return true; |
178 | default: |
179 | return false; |
180 | } |
181 | } |
182 | |
183 | // ThreadPlanNull |
184 | |
185 | ThreadPlanNull::ThreadPlanNull(Thread &thread) |
186 | : ThreadPlan(ThreadPlan::eKindNull, "Null Thread Plan" , thread, |
187 | eVoteNoOpinion, eVoteNoOpinion) {} |
188 | |
189 | ThreadPlanNull::~ThreadPlanNull() = default; |
190 | |
191 | void ThreadPlanNull::GetDescription(Stream *s, lldb::DescriptionLevel level) { |
192 | s->PutCString(cstr: "Null thread plan - thread has been destroyed." ); |
193 | } |
194 | |
195 | bool ThreadPlanNull::ValidatePlan(Stream *error) { |
196 | #ifdef LLDB_CONFIGURATION_DEBUG |
197 | fprintf(stderr, |
198 | format: "error: %s called on thread that has been destroyed (tid = 0x%" PRIx64 |
199 | ", ptid = 0x%" PRIx64 ")" , |
200 | LLVM_PRETTY_FUNCTION, m_tid, GetThread().GetProtocolID()); |
201 | #else |
202 | Log *log = GetLog(LLDBLog::Thread); |
203 | if (log) |
204 | log->Error("%s called on thread that has been destroyed (tid = 0x%" PRIx64 |
205 | ", ptid = 0x%" PRIx64 ")" , |
206 | LLVM_PRETTY_FUNCTION, m_tid, GetThread().GetProtocolID()); |
207 | #endif |
208 | return true; |
209 | } |
210 | |
211 | bool ThreadPlanNull::ShouldStop(Event *event_ptr) { |
212 | #ifdef LLDB_CONFIGURATION_DEBUG |
213 | fprintf(stderr, |
214 | format: "error: %s called on thread that has been destroyed (tid = 0x%" PRIx64 |
215 | ", ptid = 0x%" PRIx64 ")" , |
216 | LLVM_PRETTY_FUNCTION, m_tid, GetThread().GetProtocolID()); |
217 | #else |
218 | Log *log = GetLog(LLDBLog::Thread); |
219 | if (log) |
220 | log->Error("%s called on thread that has been destroyed (tid = 0x%" PRIx64 |
221 | ", ptid = 0x%" PRIx64 ")" , |
222 | LLVM_PRETTY_FUNCTION, m_tid, GetThread().GetProtocolID()); |
223 | #endif |
224 | return true; |
225 | } |
226 | |
227 | bool ThreadPlanNull::WillStop() { |
228 | #ifdef LLDB_CONFIGURATION_DEBUG |
229 | fprintf(stderr, |
230 | format: "error: %s called on thread that has been destroyed (tid = 0x%" PRIx64 |
231 | ", ptid = 0x%" PRIx64 ")" , |
232 | LLVM_PRETTY_FUNCTION, m_tid, GetThread().GetProtocolID()); |
233 | #else |
234 | Log *log = GetLog(LLDBLog::Thread); |
235 | if (log) |
236 | log->Error("%s called on thread that has been destroyed (tid = 0x%" PRIx64 |
237 | ", ptid = 0x%" PRIx64 ")" , |
238 | LLVM_PRETTY_FUNCTION, m_tid, GetThread().GetProtocolID()); |
239 | #endif |
240 | return true; |
241 | } |
242 | |
243 | bool ThreadPlanNull::DoPlanExplainsStop(Event *event_ptr) { |
244 | #ifdef LLDB_CONFIGURATION_DEBUG |
245 | fprintf(stderr, |
246 | format: "error: %s called on thread that has been destroyed (tid = 0x%" PRIx64 |
247 | ", ptid = 0x%" PRIx64 ")" , |
248 | LLVM_PRETTY_FUNCTION, GetThread().GetID(), GetThread().GetProtocolID()); |
249 | #else |
250 | Log *log = GetLog(LLDBLog::Thread); |
251 | if (log) |
252 | log->Error("%s called on thread that has been destroyed (tid = 0x%" PRIx64 |
253 | ", ptid = 0x%" PRIx64 ")" , |
254 | LLVM_PRETTY_FUNCTION, m_tid, GetThread().GetProtocolID()); |
255 | #endif |
256 | return true; |
257 | } |
258 | |
259 | // The null plan is never done. |
260 | bool ThreadPlanNull::MischiefManaged() { |
261 | // The null plan is never done. |
262 | #ifdef LLDB_CONFIGURATION_DEBUG |
263 | fprintf(stderr, |
264 | format: "error: %s called on thread that has been destroyed (tid = 0x%" PRIx64 |
265 | ", ptid = 0x%" PRIx64 ")" , |
266 | LLVM_PRETTY_FUNCTION, m_tid, GetThread().GetProtocolID()); |
267 | #else |
268 | Log *log = GetLog(LLDBLog::Thread); |
269 | if (log) |
270 | log->Error("%s called on thread that has been destroyed (tid = 0x%" PRIx64 |
271 | ", ptid = 0x%" PRIx64 ")" , |
272 | LLVM_PRETTY_FUNCTION, m_tid, GetThread().GetProtocolID()); |
273 | #endif |
274 | return false; |
275 | } |
276 | |
277 | lldb::StateType ThreadPlanNull::GetPlanRunState() { |
278 | // Not sure what to return here. This is a dead thread. |
279 | #ifdef LLDB_CONFIGURATION_DEBUG |
280 | fprintf(stderr, |
281 | format: "error: %s called on thread that has been destroyed (tid = 0x%" PRIx64 |
282 | ", ptid = 0x%" PRIx64 ")" , |
283 | LLVM_PRETTY_FUNCTION, m_tid, GetThread().GetProtocolID()); |
284 | #else |
285 | Log *log = GetLog(LLDBLog::Thread); |
286 | if (log) |
287 | log->Error("%s called on thread that has been destroyed (tid = 0x%" PRIx64 |
288 | ", ptid = 0x%" PRIx64 ")" , |
289 | LLVM_PRETTY_FUNCTION, m_tid, GetThread().GetProtocolID()); |
290 | #endif |
291 | return eStateRunning; |
292 | } |
293 | |