1 | //===-- ThreadPlanStepOverRange.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/ThreadPlanStepOverRange.h" |
10 | #include "lldb/Symbol/Block.h" |
11 | #include "lldb/Symbol/CompileUnit.h" |
12 | #include "lldb/Symbol/Function.h" |
13 | #include "lldb/Symbol/LineTable.h" |
14 | #include "lldb/Target/Process.h" |
15 | #include "lldb/Target/RegisterContext.h" |
16 | #include "lldb/Target/Target.h" |
17 | #include "lldb/Target/Thread.h" |
18 | #include "lldb/Target/ThreadPlanStepOut.h" |
19 | #include "lldb/Target/ThreadPlanStepThrough.h" |
20 | #include "lldb/Utility/LLDBLog.h" |
21 | #include "lldb/Utility/Log.h" |
22 | #include "lldb/Utility/Stream.h" |
23 | |
24 | using namespace lldb_private; |
25 | using namespace lldb; |
26 | |
27 | uint32_t ThreadPlanStepOverRange::s_default_flag_values = 0; |
28 | |
29 | // ThreadPlanStepOverRange: Step through a stack range, either stepping over or |
30 | // into based on the value of \a type. |
31 | |
32 | ThreadPlanStepOverRange::ThreadPlanStepOverRange( |
33 | Thread &thread, const AddressRange &range, |
34 | const SymbolContext &addr_context, lldb::RunMode stop_others, |
35 | LazyBool step_out_avoids_code_without_debug_info) |
36 | : ThreadPlanStepRange(ThreadPlan::eKindStepOverRange, |
37 | "Step range stepping over" , thread, range, |
38 | addr_context, stop_others), |
39 | ThreadPlanShouldStopHere(this), m_first_resume(true) { |
40 | SetFlagsToDefault(); |
41 | SetupAvoidNoDebug(step_out_avoids_code_without_debug_info); |
42 | } |
43 | |
44 | ThreadPlanStepOverRange::~ThreadPlanStepOverRange() = default; |
45 | |
46 | void ThreadPlanStepOverRange::GetDescription(Stream *s, |
47 | lldb::DescriptionLevel level) { |
48 | auto PrintFailureIfAny = [&]() { |
49 | if (m_status.Success()) |
50 | return; |
51 | s->Printf(format: " failed (%s)" , m_status.AsCString()); |
52 | }; |
53 | |
54 | if (level == lldb::eDescriptionLevelBrief) { |
55 | s->Printf(format: "step over" ); |
56 | PrintFailureIfAny(); |
57 | return; |
58 | } |
59 | |
60 | s->Printf(format: "Stepping over" ); |
61 | bool printed_line_info = false; |
62 | if (m_addr_context.line_entry.IsValid()) { |
63 | s->Printf(format: " line " ); |
64 | m_addr_context.line_entry.DumpStopContext(s, show_fullpaths: false); |
65 | printed_line_info = true; |
66 | } |
67 | |
68 | if (!printed_line_info || level == eDescriptionLevelVerbose) { |
69 | s->Printf(format: " using ranges: " ); |
70 | DumpRanges(s); |
71 | } |
72 | |
73 | PrintFailureIfAny(); |
74 | |
75 | s->PutChar(ch: '.'); |
76 | } |
77 | |
78 | void ThreadPlanStepOverRange::SetupAvoidNoDebug( |
79 | LazyBool step_out_avoids_code_without_debug_info) { |
80 | bool avoid_nodebug = true; |
81 | switch (step_out_avoids_code_without_debug_info) { |
82 | case eLazyBoolYes: |
83 | avoid_nodebug = true; |
84 | break; |
85 | case eLazyBoolNo: |
86 | avoid_nodebug = false; |
87 | break; |
88 | case eLazyBoolCalculate: |
89 | avoid_nodebug = GetThread().GetStepOutAvoidsNoDebug(); |
90 | break; |
91 | } |
92 | if (avoid_nodebug) |
93 | GetFlags().Set(ThreadPlanShouldStopHere::eStepOutAvoidNoDebug); |
94 | else |
95 | GetFlags().Clear(mask: ThreadPlanShouldStopHere::eStepOutAvoidNoDebug); |
96 | // Step Over plans should always avoid no-debug on step in. Seems like you |
97 | // shouldn't have to say this, but a tail call looks more like a step in that |
98 | // a step out, so we want to catch this case. |
99 | GetFlags().Set(ThreadPlanShouldStopHere::eStepInAvoidNoDebug); |
100 | } |
101 | |
102 | bool ThreadPlanStepOverRange::IsEquivalentContext( |
103 | const SymbolContext &context) { |
104 | // Match as much as is specified in the m_addr_context: This is a fairly |
105 | // loose sanity check. Note, sometimes the target doesn't get filled in so I |
106 | // left out the target check. And sometimes the module comes in as the .o |
107 | // file from the inlined range, so I left that out too... |
108 | if (m_addr_context.comp_unit) { |
109 | if (m_addr_context.comp_unit != context.comp_unit) |
110 | return false; |
111 | if (m_addr_context.function) { |
112 | if (m_addr_context.function != context.function) |
113 | return false; |
114 | // It is okay to return to a different block of a straight function, we |
115 | // only have to be more careful if returning from one inlined block to |
116 | // another. |
117 | if (m_addr_context.block->GetInlinedFunctionInfo() == nullptr && |
118 | context.block->GetInlinedFunctionInfo() == nullptr) |
119 | return true; |
120 | return m_addr_context.block == context.block; |
121 | } |
122 | } |
123 | // Fall back to symbol if we have no decision from comp_unit/function/block. |
124 | return m_addr_context.symbol && m_addr_context.symbol == context.symbol; |
125 | } |
126 | |
127 | bool ThreadPlanStepOverRange::ShouldStop(Event *event_ptr) { |
128 | Log *log = GetLog(mask: LLDBLog::Step); |
129 | Thread &thread = GetThread(); |
130 | |
131 | if (log) { |
132 | StreamString s; |
133 | DumpAddress(s&: s.AsRawOstream(), addr: thread.GetRegisterContext()->GetPC(), |
134 | addr_size: GetTarget().GetArchitecture().GetAddressByteSize()); |
135 | LLDB_LOGF(log, "ThreadPlanStepOverRange reached %s." , s.GetData()); |
136 | } |
137 | |
138 | // If we're out of the range but in the same frame or in our caller's frame |
139 | // then we should stop. When stepping out we only stop others if we are |
140 | // forcing running one thread. |
141 | bool stop_others = (m_stop_others == lldb::eOnlyThisThread); |
142 | ThreadPlanSP new_plan_sp; |
143 | FrameComparison frame_order = CompareCurrentFrameToStartFrame(); |
144 | |
145 | if (frame_order == eFrameCompareOlder) { |
146 | // If we're in an older frame then we should stop. |
147 | // |
148 | // A caveat to this is if we think the frame is older but we're actually in |
149 | // a trampoline. |
150 | // I'm going to make the assumption that you wouldn't RETURN to a |
151 | // trampoline. So if we are in a trampoline we think the frame is older |
152 | // because the trampoline confused the backtracer. As below, we step |
153 | // through first, and then try to figure out how to get back out again. |
154 | |
155 | new_plan_sp = thread.QueueThreadPlanForStepThrough(return_stack_id&: m_stack_id, abort_other_plans: false, |
156 | stop_other_threads: stop_others, status&: m_status); |
157 | |
158 | if (new_plan_sp && log) |
159 | LLDB_LOGF(log, |
160 | "Thought I stepped out, but in fact arrived at a trampoline." ); |
161 | } else if (frame_order == eFrameCompareYounger) { |
162 | // Make sure we really are in a new frame. Do that by unwinding and seeing |
163 | // if the start function really is our start function... |
164 | for (uint32_t i = 1;; ++i) { |
165 | StackFrameSP older_frame_sp = thread.GetStackFrameAtIndex(idx: i); |
166 | if (!older_frame_sp) { |
167 | // We can't unwind the next frame we should just get out of here & |
168 | // stop... |
169 | break; |
170 | } |
171 | |
172 | const SymbolContext &older_context = |
173 | older_frame_sp->GetSymbolContext(resolve_scope: eSymbolContextEverything); |
174 | if (IsEquivalentContext(context: older_context)) { |
175 | // If we have the next-branch-breakpoint in the range, we can just |
176 | // rely on that breakpoint to trigger once we return to the range. |
177 | if (m_next_branch_bp_sp) |
178 | return false; |
179 | new_plan_sp = thread.QueueThreadPlanForStepOutNoShouldStop( |
180 | abort_other_plans: false, addr_context: nullptr, first_insn: true, stop_other_threads: stop_others, report_stop_vote: eVoteNo, report_run_vote: eVoteNoOpinion, frame_idx: 0, |
181 | status&: m_status, continue_to_next_branch: true); |
182 | break; |
183 | } else { |
184 | new_plan_sp = thread.QueueThreadPlanForStepThrough( |
185 | return_stack_id&: m_stack_id, abort_other_plans: false, stop_other_threads: stop_others, status&: m_status); |
186 | // If we found a way through, then we should stop recursing. |
187 | if (new_plan_sp) |
188 | break; |
189 | } |
190 | } |
191 | } else { |
192 | // If we're still in the range, keep going. |
193 | if (InRange()) { |
194 | SetNextBranchBreakpoint(); |
195 | return false; |
196 | } |
197 | |
198 | if (!InSymbol()) { |
199 | // This one is a little tricky. Sometimes we may be in a stub or |
200 | // something similar, in which case we need to get out of there. But if |
201 | // we are in a stub then it's likely going to be hard to get out from |
202 | // here. It is probably easiest to step into the stub, and then it will |
203 | // be straight-forward to step out. |
204 | new_plan_sp = thread.QueueThreadPlanForStepThrough(return_stack_id&: m_stack_id, abort_other_plans: false, |
205 | stop_other_threads: stop_others, status&: m_status); |
206 | } else { |
207 | // The current clang (at least through 424) doesn't always get the |
208 | // address range for the DW_TAG_inlined_subroutines right, so that when |
209 | // you leave the inlined range the line table says you are still in the |
210 | // source file of the inlining function. This is bad, because now you |
211 | // are missing the stack frame for the function containing the inlining, |
212 | // and if you sensibly do "finish" to get out of this function you will |
213 | // instead exit the containing function. To work around this, we check |
214 | // whether we are still in the source file we started in, and if not |
215 | // assume it is an error, and push a plan to get us out of this line and |
216 | // back to the containing file. |
217 | |
218 | if (m_addr_context.line_entry.IsValid()) { |
219 | SymbolContext sc; |
220 | StackFrameSP frame_sp = thread.GetStackFrameAtIndex(idx: 0); |
221 | sc = frame_sp->GetSymbolContext(resolve_scope: eSymbolContextEverything); |
222 | if (sc.line_entry.IsValid()) { |
223 | if (*sc.line_entry.original_file_sp != |
224 | *m_addr_context.line_entry.original_file_sp && |
225 | sc.comp_unit == m_addr_context.comp_unit && |
226 | sc.function == m_addr_context.function) { |
227 | // Okay, find the next occurrence of this file in the line table: |
228 | LineTable *line_table = m_addr_context.comp_unit->GetLineTable(); |
229 | if (line_table) { |
230 | Address cur_address = frame_sp->GetFrameCodeAddress(); |
231 | uint32_t entry_idx; |
232 | LineEntry line_entry; |
233 | if (line_table->FindLineEntryByAddress(so_addr: cur_address, line_entry, |
234 | index_ptr: &entry_idx)) { |
235 | LineEntry next_line_entry; |
236 | bool step_past_remaining_inline = false; |
237 | if (entry_idx > 0) { |
238 | // We require the previous line entry and the current line |
239 | // entry come from the same file. The other requirement is |
240 | // that the previous line table entry be part of an inlined |
241 | // block, we don't want to step past cases where people have |
242 | // inlined some code fragment by using #include <source- |
243 | // fragment.c> directly. |
244 | LineEntry prev_line_entry; |
245 | if (line_table->GetLineEntryAtIndex(idx: entry_idx - 1, |
246 | line_entry&: prev_line_entry) && |
247 | *prev_line_entry.original_file_sp == |
248 | *line_entry.original_file_sp) { |
249 | SymbolContext prev_sc; |
250 | Address prev_address = |
251 | prev_line_entry.range.GetBaseAddress(); |
252 | prev_address.CalculateSymbolContext(sc: &prev_sc); |
253 | if (prev_sc.block) { |
254 | Block *inlined_block = |
255 | prev_sc.block->GetContainingInlinedBlock(); |
256 | if (inlined_block) { |
257 | AddressRange inline_range; |
258 | inlined_block->GetRangeContainingAddress(addr: prev_address, |
259 | range&: inline_range); |
260 | if (!inline_range.ContainsFileAddress(so_addr: cur_address)) { |
261 | |
262 | step_past_remaining_inline = true; |
263 | } |
264 | } |
265 | } |
266 | } |
267 | } |
268 | |
269 | if (step_past_remaining_inline) { |
270 | uint32_t look_ahead_step = 1; |
271 | while (line_table->GetLineEntryAtIndex( |
272 | idx: entry_idx + look_ahead_step, line_entry&: next_line_entry)) { |
273 | // Make sure we haven't wandered out of the function we |
274 | // started from... |
275 | Address next_line_address = |
276 | next_line_entry.range.GetBaseAddress(); |
277 | Function *next_line_function = |
278 | next_line_address.CalculateSymbolContextFunction(); |
279 | if (next_line_function != m_addr_context.function) |
280 | break; |
281 | |
282 | if (*next_line_entry.original_file_sp == |
283 | *m_addr_context.line_entry.original_file_sp) { |
284 | const bool abort_other_plans = false; |
285 | const RunMode stop_other_threads = RunMode::eAllThreads; |
286 | lldb::addr_t cur_pc = thread.GetStackFrameAtIndex(idx: 0) |
287 | ->GetRegisterContext() |
288 | ->GetPC(); |
289 | AddressRange step_range( |
290 | cur_pc, |
291 | next_line_address.GetLoadAddress(target: &GetTarget()) - |
292 | cur_pc); |
293 | |
294 | new_plan_sp = thread.QueueThreadPlanForStepOverRange( |
295 | abort_other_plans, range: step_range, addr_context: sc, stop_other_threads, |
296 | status&: m_status); |
297 | break; |
298 | } |
299 | look_ahead_step++; |
300 | } |
301 | } |
302 | } |
303 | } |
304 | } |
305 | } |
306 | } |
307 | } |
308 | } |
309 | |
310 | // If we get to this point, we're not going to use a previously set "next |
311 | // branch" breakpoint, so delete it: |
312 | ClearNextBranchBreakpoint(); |
313 | |
314 | // If we haven't figured out something to do yet, then ask the ShouldStopHere |
315 | // callback: |
316 | if (!new_plan_sp) { |
317 | new_plan_sp = CheckShouldStopHereAndQueueStepOut(operation: frame_order, status&: m_status); |
318 | } |
319 | |
320 | if (!new_plan_sp) |
321 | m_no_more_plans = true; |
322 | else { |
323 | // Any new plan will be an implementation plan, so mark it private: |
324 | new_plan_sp->SetPrivate(true); |
325 | m_no_more_plans = false; |
326 | } |
327 | |
328 | if (!new_plan_sp) { |
329 | // For efficiencies sake, we know we're done here so we don't have to do |
330 | // this calculation again in MischiefManaged. |
331 | SetPlanComplete(m_status.Success()); |
332 | return true; |
333 | } else |
334 | return false; |
335 | } |
336 | |
337 | bool ThreadPlanStepOverRange::DoPlanExplainsStop(Event *event_ptr) { |
338 | // For crashes, breakpoint hits, signals, etc, let the base plan (or some |
339 | // plan above us) handle the stop. That way the user can see the stop, step |
340 | // around, and then when they are done, continue and have their step |
341 | // complete. The exception is if we've hit our "run to next branch" |
342 | // breakpoint. Note, unlike the step in range plan, we don't mark ourselves |
343 | // complete if we hit an unexplained breakpoint/crash. |
344 | |
345 | Log *log = GetLog(mask: LLDBLog::Step); |
346 | StopInfoSP stop_info_sp = GetPrivateStopInfo(); |
347 | bool return_value; |
348 | |
349 | if (stop_info_sp) { |
350 | StopReason reason = stop_info_sp->GetStopReason(); |
351 | |
352 | if (reason == eStopReasonTrace) { |
353 | return_value = true; |
354 | } else if (reason == eStopReasonBreakpoint) { |
355 | return_value = NextRangeBreakpointExplainsStop(stop_info_sp); |
356 | } else { |
357 | if (log) |
358 | log->PutCString(cstr: "ThreadPlanStepInRange got asked if it explains the " |
359 | "stop for some reason other than step." ); |
360 | return_value = false; |
361 | } |
362 | } else |
363 | return_value = true; |
364 | |
365 | return return_value; |
366 | } |
367 | |
368 | bool ThreadPlanStepOverRange::DoWillResume(lldb::StateType resume_state, |
369 | bool current_plan) { |
370 | if (resume_state != eStateSuspended && m_first_resume) { |
371 | m_first_resume = false; |
372 | if (resume_state == eStateStepping && current_plan) { |
373 | Thread &thread = GetThread(); |
374 | // See if we are about to step over an inlined call in the middle of the |
375 | // inlined stack, if so figure out its extents and reset our range to |
376 | // step over that. |
377 | bool in_inlined_stack = thread.DecrementCurrentInlinedDepth(); |
378 | if (in_inlined_stack) { |
379 | Log *log = GetLog(mask: LLDBLog::Step); |
380 | LLDB_LOGF(log, |
381 | "ThreadPlanStepInRange::DoWillResume: adjusting range to " |
382 | "the frame at inlined depth %d." , |
383 | thread.GetCurrentInlinedDepth()); |
384 | StackFrameSP stack_sp = thread.GetStackFrameAtIndex(idx: 0); |
385 | if (stack_sp) { |
386 | Block *frame_block = stack_sp->GetFrameBlock(); |
387 | lldb::addr_t curr_pc = thread.GetRegisterContext()->GetPC(); |
388 | AddressRange my_range; |
389 | if (frame_block->GetRangeContainingLoadAddress( |
390 | load_addr: curr_pc, target&: m_process.GetTarget(), range&: my_range)) { |
391 | m_address_ranges.clear(); |
392 | m_address_ranges.push_back(x: my_range); |
393 | if (log) { |
394 | StreamString s; |
395 | const InlineFunctionInfo *inline_info = |
396 | frame_block->GetInlinedFunctionInfo(); |
397 | const char *name; |
398 | if (inline_info) |
399 | name = inline_info->GetName().AsCString(); |
400 | else |
401 | name = "<unknown-notinlined>" ; |
402 | |
403 | s.Printf( |
404 | format: "Stepping over inlined function \"%s\" in inlined stack: " , |
405 | name); |
406 | DumpRanges(s: &s); |
407 | log->PutString(str: s.GetString()); |
408 | } |
409 | } |
410 | } |
411 | } |
412 | } |
413 | } |
414 | |
415 | return true; |
416 | } |
417 | |