1 | //===-- ThreadPlanStepUntil.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/ThreadPlanStepUntil.h" |
10 | |
11 | #include "lldb/Breakpoint/Breakpoint.h" |
12 | #include "lldb/Symbol/SymbolContextScope.h" |
13 | #include "lldb/Target/Process.h" |
14 | #include "lldb/Target/RegisterContext.h" |
15 | #include "lldb/Target/StopInfo.h" |
16 | #include "lldb/Target/Target.h" |
17 | #include "lldb/Utility/LLDBLog.h" |
18 | #include "lldb/Utility/Log.h" |
19 | |
20 | using namespace lldb; |
21 | using namespace lldb_private; |
22 | |
23 | // ThreadPlanStepUntil: Run until we reach a given line number or step out of |
24 | // the current frame |
25 | |
26 | ThreadPlanStepUntil::ThreadPlanStepUntil(Thread &thread, |
27 | lldb::addr_t *address_list, |
28 | size_t num_addresses, bool stop_others, |
29 | uint32_t frame_idx) |
30 | : ThreadPlan(ThreadPlan::eKindStepUntil, "Step until" , thread, |
31 | eVoteNoOpinion, eVoteNoOpinion), |
32 | m_step_from_insn(LLDB_INVALID_ADDRESS), |
33 | m_return_bp_id(LLDB_INVALID_BREAK_ID), |
34 | m_return_addr(LLDB_INVALID_ADDRESS), m_stepped_out(false), |
35 | m_should_stop(false), m_ran_analyze(false), m_explains_stop(false), |
36 | m_until_points(), m_stop_others(stop_others) { |
37 | // Stash away our "until" addresses: |
38 | TargetSP target_sp(thread.CalculateTarget()); |
39 | |
40 | StackFrameSP frame_sp(thread.GetStackFrameAtIndex(idx: frame_idx)); |
41 | if (frame_sp) { |
42 | m_step_from_insn = frame_sp->GetStackID().GetPC(); |
43 | |
44 | // Find the return address and set a breakpoint there: |
45 | // FIXME - can we do this more securely if we know first_insn? |
46 | |
47 | StackFrameSP return_frame_sp(thread.GetStackFrameAtIndex(idx: frame_idx + 1)); |
48 | if (return_frame_sp) { |
49 | // TODO: add inline functionality |
50 | m_return_addr = return_frame_sp->GetStackID().GetPC(); |
51 | Breakpoint *return_bp = |
52 | target_sp->CreateBreakpoint(load_addr: m_return_addr, internal: true, request_hardware: false).get(); |
53 | |
54 | if (return_bp != nullptr) { |
55 | if (return_bp->IsHardware() && !return_bp->HasResolvedLocations()) |
56 | m_could_not_resolve_hw_bp = true; |
57 | return_bp->SetThreadID(m_tid); |
58 | m_return_bp_id = return_bp->GetID(); |
59 | return_bp->SetBreakpointKind("until-return-backstop" ); |
60 | } |
61 | } |
62 | |
63 | m_stack_id = frame_sp->GetStackID(); |
64 | |
65 | // Now set breakpoints on all our return addresses: |
66 | for (size_t i = 0; i < num_addresses; i++) { |
67 | Breakpoint *until_bp = |
68 | target_sp->CreateBreakpoint(load_addr: address_list[i], internal: true, request_hardware: false).get(); |
69 | if (until_bp != nullptr) { |
70 | until_bp->SetThreadID(m_tid); |
71 | m_until_points[address_list[i]] = until_bp->GetID(); |
72 | until_bp->SetBreakpointKind("until-target" ); |
73 | } else { |
74 | m_until_points[address_list[i]] = LLDB_INVALID_BREAK_ID; |
75 | } |
76 | } |
77 | } |
78 | } |
79 | |
80 | ThreadPlanStepUntil::~ThreadPlanStepUntil() { Clear(); } |
81 | |
82 | void ThreadPlanStepUntil::Clear() { |
83 | Target &target = GetTarget(); |
84 | if (m_return_bp_id != LLDB_INVALID_BREAK_ID) { |
85 | target.RemoveBreakpointByID(break_id: m_return_bp_id); |
86 | m_return_bp_id = LLDB_INVALID_BREAK_ID; |
87 | } |
88 | |
89 | until_collection::iterator pos, end = m_until_points.end(); |
90 | for (pos = m_until_points.begin(); pos != end; pos++) { |
91 | target.RemoveBreakpointByID(break_id: (*pos).second); |
92 | } |
93 | m_until_points.clear(); |
94 | m_could_not_resolve_hw_bp = false; |
95 | } |
96 | |
97 | void ThreadPlanStepUntil::GetDescription(Stream *s, |
98 | lldb::DescriptionLevel level) { |
99 | if (level == lldb::eDescriptionLevelBrief) { |
100 | s->Printf(format: "step until" ); |
101 | if (m_stepped_out) |
102 | s->Printf(format: " - stepped out" ); |
103 | } else { |
104 | if (m_until_points.size() == 1) |
105 | s->Printf(format: "Stepping from address 0x%" PRIx64 " until we reach 0x%" PRIx64 |
106 | " using breakpoint %d" , |
107 | (uint64_t)m_step_from_insn, |
108 | (uint64_t)(*m_until_points.begin()).first, |
109 | (*m_until_points.begin()).second); |
110 | else { |
111 | until_collection::iterator pos, end = m_until_points.end(); |
112 | s->Printf(format: "Stepping from address 0x%" PRIx64 " until we reach one of:" , |
113 | (uint64_t)m_step_from_insn); |
114 | for (pos = m_until_points.begin(); pos != end; pos++) { |
115 | s->Printf(format: "\n\t0x%" PRIx64 " (bp: %d)" , (uint64_t)(*pos).first, |
116 | (*pos).second); |
117 | } |
118 | } |
119 | s->Printf(format: " stepped out address is 0x%" PRIx64 "." , |
120 | (uint64_t)m_return_addr); |
121 | } |
122 | } |
123 | |
124 | bool ThreadPlanStepUntil::ValidatePlan(Stream *error) { |
125 | if (m_could_not_resolve_hw_bp) { |
126 | if (error) |
127 | error->PutCString( |
128 | cstr: "Could not create hardware breakpoint for thread plan." ); |
129 | return false; |
130 | } else if (m_return_bp_id == LLDB_INVALID_BREAK_ID) { |
131 | if (error) |
132 | error->PutCString(cstr: "Could not create return breakpoint." ); |
133 | return false; |
134 | } else { |
135 | until_collection::iterator pos, end = m_until_points.end(); |
136 | for (pos = m_until_points.begin(); pos != end; pos++) { |
137 | if (!LLDB_BREAK_ID_IS_VALID((*pos).second)) |
138 | return false; |
139 | } |
140 | return true; |
141 | } |
142 | } |
143 | |
144 | void ThreadPlanStepUntil::AnalyzeStop() { |
145 | if (m_ran_analyze) |
146 | return; |
147 | |
148 | StopInfoSP stop_info_sp = GetPrivateStopInfo(); |
149 | m_should_stop = true; |
150 | m_explains_stop = false; |
151 | |
152 | if (stop_info_sp) { |
153 | StopReason reason = stop_info_sp->GetStopReason(); |
154 | |
155 | if (reason == eStopReasonBreakpoint) { |
156 | // If this is OUR breakpoint, we're fine, otherwise we don't know why |
157 | // this happened... |
158 | BreakpointSiteSP this_site = |
159 | m_process.GetBreakpointSiteList().FindByID(site_id: stop_info_sp->GetValue()); |
160 | if (!this_site) { |
161 | m_explains_stop = false; |
162 | return; |
163 | } |
164 | |
165 | if (this_site->IsBreakpointAtThisSite(bp_id: m_return_bp_id)) { |
166 | // If we are at our "step out" breakpoint, and the stack depth has |
167 | // shrunk, then this is indeed our stop. If the stack depth has grown, |
168 | // then we've hit our step out breakpoint recursively. If we are the |
169 | // only breakpoint at that location, then we do explain the stop, and |
170 | // we'll just continue. If there was another breakpoint here, then we |
171 | // don't explain the stop, but we won't mark ourselves Completed, |
172 | // because maybe that breakpoint will continue, and then we'll finish |
173 | // the "until". |
174 | bool done; |
175 | StackID cur_frame_zero_id; |
176 | |
177 | done = (m_stack_id < cur_frame_zero_id); |
178 | |
179 | if (done) { |
180 | m_stepped_out = true; |
181 | SetPlanComplete(); |
182 | } else |
183 | m_should_stop = false; |
184 | |
185 | if (this_site->GetNumberOfConstituents() == 1) |
186 | m_explains_stop = true; |
187 | else |
188 | m_explains_stop = false; |
189 | return; |
190 | } else { |
191 | // Check if we've hit one of our "until" breakpoints. |
192 | until_collection::iterator pos, end = m_until_points.end(); |
193 | for (pos = m_until_points.begin(); pos != end; pos++) { |
194 | if (this_site->IsBreakpointAtThisSite(bp_id: (*pos).second)) { |
195 | // If we're at the right stack depth, then we're done. |
196 | Thread &thread = GetThread(); |
197 | bool done; |
198 | StackID frame_zero_id = |
199 | thread.GetStackFrameAtIndex(idx: 0)->GetStackID(); |
200 | |
201 | if (frame_zero_id == m_stack_id) |
202 | done = true; |
203 | else if (frame_zero_id < m_stack_id) |
204 | done = false; |
205 | else { |
206 | StackFrameSP older_frame_sp = thread.GetStackFrameAtIndex(idx: 1); |
207 | |
208 | // But if we can't even unwind one frame we should just get out |
209 | // of here & stop... |
210 | if (older_frame_sp) { |
211 | const SymbolContext &older_context = |
212 | older_frame_sp->GetSymbolContext(resolve_scope: eSymbolContextEverything); |
213 | SymbolContext stack_context; |
214 | m_stack_id.GetSymbolContextScope()->CalculateSymbolContext( |
215 | sc: &stack_context); |
216 | |
217 | done = (older_context == stack_context); |
218 | } else |
219 | done = false; |
220 | } |
221 | |
222 | if (done) |
223 | SetPlanComplete(); |
224 | else |
225 | m_should_stop = false; |
226 | |
227 | // Otherwise we've hit this breakpoint recursively. If we're the |
228 | // only breakpoint here, then we do explain the stop, and we'll |
229 | // continue. If not then we should let higher plans handle this |
230 | // stop. |
231 | if (this_site->GetNumberOfConstituents() == 1) |
232 | m_explains_stop = true; |
233 | else { |
234 | m_should_stop = true; |
235 | m_explains_stop = false; |
236 | } |
237 | return; |
238 | } |
239 | } |
240 | } |
241 | // If we get here we haven't hit any of our breakpoints, so let the |
242 | // higher plans take care of the stop. |
243 | m_explains_stop = false; |
244 | return; |
245 | } else if (IsUsuallyUnexplainedStopReason(reason)) { |
246 | m_explains_stop = false; |
247 | } else { |
248 | m_explains_stop = true; |
249 | } |
250 | } |
251 | } |
252 | |
253 | bool ThreadPlanStepUntil::DoPlanExplainsStop(Event *event_ptr) { |
254 | // We don't explain signals or breakpoints (breakpoints that handle stepping |
255 | // in or out will be handled by a child plan. |
256 | AnalyzeStop(); |
257 | return m_explains_stop; |
258 | } |
259 | |
260 | bool ThreadPlanStepUntil::ShouldStop(Event *event_ptr) { |
261 | // If we've told our self in ExplainsStop that we plan to continue, then do |
262 | // so here. Otherwise, as long as this thread has stopped for a reason, we |
263 | // will stop. |
264 | |
265 | StopInfoSP stop_info_sp = GetPrivateStopInfo(); |
266 | if (!stop_info_sp || stop_info_sp->GetStopReason() == eStopReasonNone) |
267 | return false; |
268 | |
269 | AnalyzeStop(); |
270 | return m_should_stop; |
271 | } |
272 | |
273 | bool ThreadPlanStepUntil::StopOthers() { return m_stop_others; } |
274 | |
275 | StateType ThreadPlanStepUntil::GetPlanRunState() { return eStateRunning; } |
276 | |
277 | bool ThreadPlanStepUntil::DoWillResume(StateType resume_state, |
278 | bool current_plan) { |
279 | if (current_plan) { |
280 | Target &target = GetTarget(); |
281 | Breakpoint *return_bp = target.GetBreakpointByID(break_id: m_return_bp_id).get(); |
282 | if (return_bp != nullptr) |
283 | return_bp->SetEnabled(true); |
284 | |
285 | until_collection::iterator pos, end = m_until_points.end(); |
286 | for (pos = m_until_points.begin(); pos != end; pos++) { |
287 | Breakpoint *until_bp = target.GetBreakpointByID(break_id: (*pos).second).get(); |
288 | if (until_bp != nullptr) |
289 | until_bp->SetEnabled(true); |
290 | } |
291 | } |
292 | |
293 | m_should_stop = true; |
294 | m_ran_analyze = false; |
295 | m_explains_stop = false; |
296 | return true; |
297 | } |
298 | |
299 | bool ThreadPlanStepUntil::WillStop() { |
300 | Target &target = GetTarget(); |
301 | Breakpoint *return_bp = target.GetBreakpointByID(break_id: m_return_bp_id).get(); |
302 | if (return_bp != nullptr) |
303 | return_bp->SetEnabled(false); |
304 | |
305 | until_collection::iterator pos, end = m_until_points.end(); |
306 | for (pos = m_until_points.begin(); pos != end; pos++) { |
307 | Breakpoint *until_bp = target.GetBreakpointByID(break_id: (*pos).second).get(); |
308 | if (until_bp != nullptr) |
309 | until_bp->SetEnabled(false); |
310 | } |
311 | return true; |
312 | } |
313 | |
314 | bool ThreadPlanStepUntil::MischiefManaged() { |
315 | // I'm letting "PlanExplainsStop" do all the work, and just reporting that |
316 | // here. |
317 | bool done = false; |
318 | if (IsPlanComplete()) { |
319 | Log *log = GetLog(mask: LLDBLog::Step); |
320 | LLDB_LOGF(log, "Completed step until plan." ); |
321 | |
322 | Clear(); |
323 | done = true; |
324 | } |
325 | if (done) |
326 | ThreadPlan::MischiefManaged(); |
327 | |
328 | return done; |
329 | } |
330 | |