1 | //===-- ThreadPlanStepInRange.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/ThreadPlanStepInRange.h" |
10 | #include "lldb/Core/Architecture.h" |
11 | #include "lldb/Core/Module.h" |
12 | #include "lldb/Symbol/Function.h" |
13 | #include "lldb/Symbol/Symbol.h" |
14 | #include "lldb/Target/Process.h" |
15 | #include "lldb/Target/RegisterContext.h" |
16 | #include "lldb/Target/SectionLoadList.h" |
17 | #include "lldb/Target/Target.h" |
18 | #include "lldb/Target/Thread.h" |
19 | #include "lldb/Target/ThreadPlanStepOut.h" |
20 | #include "lldb/Target/ThreadPlanStepThrough.h" |
21 | #include "lldb/Utility/LLDBLog.h" |
22 | #include "lldb/Utility/Log.h" |
23 | #include "lldb/Utility/RegularExpression.h" |
24 | #include "lldb/Utility/Stream.h" |
25 | |
26 | using namespace lldb; |
27 | using namespace lldb_private; |
28 | |
29 | uint32_t ThreadPlanStepInRange::s_default_flag_values = |
30 | ThreadPlanShouldStopHere::eStepInAvoidNoDebug; |
31 | |
32 | // ThreadPlanStepInRange: Step through a stack range, either stepping over or |
33 | // into based on the value of \a type. |
34 | |
35 | ThreadPlanStepInRange::ThreadPlanStepInRange( |
36 | Thread &thread, const AddressRange &range, |
37 | const SymbolContext &addr_context, const char *step_into_target, |
38 | lldb::RunMode stop_others, LazyBool step_in_avoids_code_without_debug_info, |
39 | LazyBool step_out_avoids_code_without_debug_info) |
40 | : ThreadPlanStepRange(ThreadPlan::eKindStepInRange, |
41 | "Step Range stepping in" , thread, range, addr_context, |
42 | stop_others), |
43 | ThreadPlanShouldStopHere(this), m_step_past_prologue(true), |
44 | m_virtual_step(false), m_step_into_target(step_into_target) { |
45 | SetCallbacks(); |
46 | SetFlagsToDefault(); |
47 | SetupAvoidNoDebug(step_in_avoids_code_without_debug_info, |
48 | step_out_avoids_code_without_debug_info); |
49 | } |
50 | |
51 | ThreadPlanStepInRange::~ThreadPlanStepInRange() = default; |
52 | |
53 | void ThreadPlanStepInRange::SetupAvoidNoDebug( |
54 | LazyBool step_in_avoids_code_without_debug_info, |
55 | LazyBool step_out_avoids_code_without_debug_info) { |
56 | bool avoid_nodebug = true; |
57 | Thread &thread = GetThread(); |
58 | switch (step_in_avoids_code_without_debug_info) { |
59 | case eLazyBoolYes: |
60 | avoid_nodebug = true; |
61 | break; |
62 | case eLazyBoolNo: |
63 | avoid_nodebug = false; |
64 | break; |
65 | case eLazyBoolCalculate: |
66 | avoid_nodebug = thread.GetStepInAvoidsNoDebug(); |
67 | break; |
68 | } |
69 | if (avoid_nodebug) |
70 | GetFlags().Set(ThreadPlanShouldStopHere::eStepInAvoidNoDebug); |
71 | else |
72 | GetFlags().Clear(mask: ThreadPlanShouldStopHere::eStepInAvoidNoDebug); |
73 | |
74 | switch (step_out_avoids_code_without_debug_info) { |
75 | case eLazyBoolYes: |
76 | avoid_nodebug = true; |
77 | break; |
78 | case eLazyBoolNo: |
79 | avoid_nodebug = false; |
80 | break; |
81 | case eLazyBoolCalculate: |
82 | avoid_nodebug = thread.GetStepOutAvoidsNoDebug(); |
83 | break; |
84 | } |
85 | if (avoid_nodebug) |
86 | GetFlags().Set(ThreadPlanShouldStopHere::eStepOutAvoidNoDebug); |
87 | else |
88 | GetFlags().Clear(mask: ThreadPlanShouldStopHere::eStepOutAvoidNoDebug); |
89 | } |
90 | |
91 | void ThreadPlanStepInRange::GetDescription(Stream *s, |
92 | lldb::DescriptionLevel level) { |
93 | |
94 | auto PrintFailureIfAny = [&]() { |
95 | if (m_status.Success()) |
96 | return; |
97 | s->Printf(format: " failed (%s)" , m_status.AsCString()); |
98 | }; |
99 | |
100 | if (level == lldb::eDescriptionLevelBrief) { |
101 | s->Printf(format: "step in" ); |
102 | PrintFailureIfAny(); |
103 | return; |
104 | } |
105 | |
106 | s->Printf(format: "Stepping in" ); |
107 | bool printed_line_info = false; |
108 | if (m_addr_context.line_entry.IsValid()) { |
109 | s->Printf(format: " through line " ); |
110 | m_addr_context.line_entry.DumpStopContext(s, show_fullpaths: false); |
111 | printed_line_info = true; |
112 | } |
113 | |
114 | const char *step_into_target = m_step_into_target.AsCString(); |
115 | if (step_into_target && step_into_target[0] != '\0') |
116 | s->Printf(format: " targeting %s" , m_step_into_target.AsCString()); |
117 | |
118 | if (!printed_line_info || level == eDescriptionLevelVerbose) { |
119 | s->Printf(format: " using ranges:" ); |
120 | DumpRanges(s); |
121 | } |
122 | |
123 | PrintFailureIfAny(); |
124 | |
125 | s->PutChar(ch: '.'); |
126 | } |
127 | |
128 | bool ThreadPlanStepInRange::ShouldStop(Event *event_ptr) { |
129 | Log *log = GetLog(mask: LLDBLog::Step); |
130 | |
131 | if (log) { |
132 | StreamString s; |
133 | DumpAddress(s&: s.AsRawOstream(), addr: GetThread().GetRegisterContext()->GetPC(), |
134 | addr_size: GetTarget().GetArchitecture().GetAddressByteSize()); |
135 | LLDB_LOGF(log, "ThreadPlanStepInRange reached %s." , s.GetData()); |
136 | } |
137 | |
138 | if (IsPlanComplete()) |
139 | return true; |
140 | |
141 | m_no_more_plans = false; |
142 | if (m_sub_plan_sp && m_sub_plan_sp->IsPlanComplete()) { |
143 | if (!m_sub_plan_sp->PlanSucceeded()) { |
144 | SetPlanComplete(); |
145 | m_no_more_plans = true; |
146 | return true; |
147 | } else |
148 | m_sub_plan_sp.reset(); |
149 | } |
150 | |
151 | if (m_virtual_step) { |
152 | // If we've just completed a virtual step, all we need to do is check for a |
153 | // ShouldStopHere plan, and otherwise we're done. |
154 | // FIXME - This can be both a step in and a step out. Probably should |
155 | // record which in the m_virtual_step. |
156 | m_sub_plan_sp = |
157 | CheckShouldStopHereAndQueueStepOut(operation: eFrameCompareYounger, status&: m_status); |
158 | } else { |
159 | // Stepping through should be done running other threads in general, since |
160 | // we're setting a breakpoint and continuing. So only stop others if we |
161 | // are explicitly told to do so. |
162 | |
163 | bool stop_others = (m_stop_others == lldb::eOnlyThisThread); |
164 | |
165 | FrameComparison frame_order = CompareCurrentFrameToStartFrame(); |
166 | |
167 | Thread &thread = GetThread(); |
168 | if (frame_order == eFrameCompareOlder || |
169 | frame_order == eFrameCompareSameParent) { |
170 | // If we're in an older frame then we should stop. |
171 | // |
172 | // A caveat to this is if we think the frame is older but we're actually |
173 | // in a trampoline. |
174 | // I'm going to make the assumption that you wouldn't RETURN to a |
175 | // trampoline. So if we are in a trampoline we think the frame is older |
176 | // because the trampoline confused the backtracer. |
177 | m_sub_plan_sp = thread.QueueThreadPlanForStepThrough( |
178 | return_stack_id&: m_stack_id, abort_other_plans: false, stop_other_threads: stop_others, status&: m_status); |
179 | if (!m_sub_plan_sp) { |
180 | // Otherwise check the ShouldStopHere for step out: |
181 | m_sub_plan_sp = |
182 | CheckShouldStopHereAndQueueStepOut(operation: frame_order, status&: m_status); |
183 | if (log) { |
184 | if (m_sub_plan_sp) |
185 | LLDB_LOGF(log, |
186 | "ShouldStopHere found plan to step out of this frame." ); |
187 | else |
188 | LLDB_LOGF(log, "ShouldStopHere no plan to step out of this frame." ); |
189 | } |
190 | } else if (log) { |
191 | LLDB_LOGF( |
192 | log, "Thought I stepped out, but in fact arrived at a trampoline." ); |
193 | } |
194 | } else if (frame_order == eFrameCompareEqual && InSymbol()) { |
195 | // If we are not in a place we should step through, we're done. One |
196 | // tricky bit here is that some stubs don't push a frame, so we have to |
197 | // check both the case of a frame that is younger, or the same as this |
198 | // frame. However, if the frame is the same, and we are still in the |
199 | // symbol we started in, the we don't need to do this. This first check |
200 | // isn't strictly necessary, but it is more efficient. |
201 | |
202 | // If we're still in the range, keep going, either by running to the next |
203 | // branch breakpoint, or by stepping. |
204 | if (InRange()) { |
205 | SetNextBranchBreakpoint(); |
206 | return false; |
207 | } |
208 | |
209 | SetPlanComplete(); |
210 | m_no_more_plans = true; |
211 | return true; |
212 | } |
213 | |
214 | // If we get to this point, we're not going to use a previously set "next |
215 | // branch" breakpoint, so delete it: |
216 | ClearNextBranchBreakpoint(); |
217 | |
218 | // We may have set the plan up above in the FrameIsOlder section: |
219 | |
220 | if (!m_sub_plan_sp) |
221 | m_sub_plan_sp = thread.QueueThreadPlanForStepThrough( |
222 | return_stack_id&: m_stack_id, abort_other_plans: false, stop_other_threads: stop_others, status&: m_status); |
223 | |
224 | if (log) { |
225 | if (m_sub_plan_sp) |
226 | LLDB_LOGF(log, "Found a step through plan: %s" , |
227 | m_sub_plan_sp->GetName()); |
228 | else |
229 | LLDB_LOGF(log, "No step through plan found." ); |
230 | } |
231 | |
232 | // If not, give the "should_stop" callback a chance to push a plan to get |
233 | // us out of here. But only do that if we actually have stepped in. |
234 | if (!m_sub_plan_sp && frame_order == eFrameCompareYounger) |
235 | m_sub_plan_sp = CheckShouldStopHereAndQueueStepOut(operation: frame_order, status&: m_status); |
236 | |
237 | // If we've stepped in and we are going to stop here, check to see if we |
238 | // were asked to run past the prologue, and if so do that. |
239 | |
240 | if (!m_sub_plan_sp && frame_order == eFrameCompareYounger && |
241 | m_step_past_prologue) { |
242 | lldb::StackFrameSP curr_frame = thread.GetStackFrameAtIndex(idx: 0); |
243 | if (curr_frame) { |
244 | size_t bytes_to_skip = 0; |
245 | lldb::addr_t curr_addr = thread.GetRegisterContext()->GetPC(); |
246 | Address func_start_address; |
247 | |
248 | SymbolContext sc = curr_frame->GetSymbolContext(resolve_scope: eSymbolContextFunction | |
249 | eSymbolContextSymbol); |
250 | |
251 | if (sc.function) { |
252 | func_start_address = sc.function->GetAddressRange().GetBaseAddress(); |
253 | if (curr_addr == func_start_address.GetLoadAddress(target: &GetTarget())) |
254 | bytes_to_skip = sc.function->GetPrologueByteSize(); |
255 | } else if (sc.symbol) { |
256 | func_start_address = sc.symbol->GetAddress(); |
257 | if (curr_addr == func_start_address.GetLoadAddress(target: &GetTarget())) |
258 | bytes_to_skip = sc.symbol->GetPrologueByteSize(); |
259 | } |
260 | |
261 | if (bytes_to_skip == 0 && sc.symbol) { |
262 | const Architecture *arch = GetTarget().GetArchitecturePlugin(); |
263 | if (arch) { |
264 | Address curr_sec_addr; |
265 | GetTarget().GetSectionLoadList().ResolveLoadAddress(load_addr: curr_addr, |
266 | so_addr&: curr_sec_addr); |
267 | bytes_to_skip = arch->GetBytesToSkip(func&: *sc.symbol, curr_addr: curr_sec_addr); |
268 | } |
269 | } |
270 | |
271 | if (bytes_to_skip != 0) { |
272 | func_start_address.Slide(offset: bytes_to_skip); |
273 | log = GetLog(mask: LLDBLog::Step); |
274 | LLDB_LOGF(log, "Pushing past prologue " ); |
275 | |
276 | m_sub_plan_sp = thread.QueueThreadPlanForRunToAddress( |
277 | abort_other_plans: false, target_addr&: func_start_address, stop_other_threads: true, status&: m_status); |
278 | } |
279 | } |
280 | } |
281 | } |
282 | |
283 | if (!m_sub_plan_sp) { |
284 | m_no_more_plans = true; |
285 | SetPlanComplete(); |
286 | return true; |
287 | } else { |
288 | m_no_more_plans = false; |
289 | m_sub_plan_sp->SetPrivate(true); |
290 | return false; |
291 | } |
292 | } |
293 | |
294 | void ThreadPlanStepInRange::SetAvoidRegexp(const char *name) { |
295 | if (m_avoid_regexp_up) |
296 | *m_avoid_regexp_up = RegularExpression(name); |
297 | else |
298 | m_avoid_regexp_up = std::make_unique<RegularExpression>(args&: name); |
299 | } |
300 | |
301 | void ThreadPlanStepInRange::SetDefaultFlagValue(uint32_t new_value) { |
302 | // TODO: Should we test this for sanity? |
303 | ThreadPlanStepInRange::s_default_flag_values = new_value; |
304 | } |
305 | |
306 | bool ThreadPlanStepInRange::FrameMatchesAvoidCriteria() { |
307 | StackFrame *frame = GetThread().GetStackFrameAtIndex(idx: 0).get(); |
308 | |
309 | // Check the library list first, as that's cheapest: |
310 | bool libraries_say_avoid = false; |
311 | |
312 | FileSpecList libraries_to_avoid(GetThread().GetLibrariesToAvoid()); |
313 | size_t num_libraries = libraries_to_avoid.GetSize(); |
314 | if (num_libraries > 0) { |
315 | SymbolContext sc(frame->GetSymbolContext(resolve_scope: eSymbolContextModule)); |
316 | FileSpec frame_library(sc.module_sp->GetFileSpec()); |
317 | |
318 | if (frame_library) { |
319 | for (size_t i = 0; i < num_libraries; i++) { |
320 | const FileSpec &file_spec(libraries_to_avoid.GetFileSpecAtIndex(idx: i)); |
321 | if (FileSpec::Match(pattern: file_spec, file: frame_library)) { |
322 | libraries_say_avoid = true; |
323 | break; |
324 | } |
325 | } |
326 | } |
327 | } |
328 | if (libraries_say_avoid) |
329 | return true; |
330 | |
331 | const RegularExpression *avoid_regexp_to_use = m_avoid_regexp_up.get(); |
332 | if (avoid_regexp_to_use == nullptr) |
333 | avoid_regexp_to_use = GetThread().GetSymbolsToAvoidRegexp(); |
334 | |
335 | if (avoid_regexp_to_use != nullptr) { |
336 | SymbolContext sc = frame->GetSymbolContext( |
337 | resolve_scope: eSymbolContextFunction | eSymbolContextBlock | eSymbolContextSymbol); |
338 | if (sc.symbol != nullptr) { |
339 | const char *frame_function_name = |
340 | sc.GetFunctionName(preference: Mangled::ePreferDemangledWithoutArguments) |
341 | .GetCString(); |
342 | if (frame_function_name) { |
343 | bool return_value = avoid_regexp_to_use->Execute(string: frame_function_name); |
344 | if (return_value) { |
345 | LLDB_LOGF(GetLog(LLDBLog::Step), |
346 | "Stepping out of function \"%s\" because it matches the " |
347 | "avoid regexp \"%s\"." , |
348 | frame_function_name, |
349 | avoid_regexp_to_use->GetText().str().c_str()); |
350 | } |
351 | return return_value; |
352 | } |
353 | } |
354 | } |
355 | return false; |
356 | } |
357 | |
358 | bool ThreadPlanStepInRange::DefaultShouldStopHereCallback( |
359 | ThreadPlan *current_plan, Flags &flags, FrameComparison operation, |
360 | Status &status, void *baton) { |
361 | bool should_stop_here = true; |
362 | StackFrame *frame = current_plan->GetThread().GetStackFrameAtIndex(idx: 0).get(); |
363 | Log *log = GetLog(mask: LLDBLog::Step); |
364 | |
365 | // First see if the ThreadPlanShouldStopHere default implementation thinks we |
366 | // should get out of here: |
367 | should_stop_here = ThreadPlanShouldStopHere::DefaultShouldStopHereCallback( |
368 | current_plan, flags, operation, status, baton); |
369 | if (!should_stop_here) |
370 | return false; |
371 | |
372 | if (should_stop_here && current_plan->GetKind() == eKindStepInRange && |
373 | operation == eFrameCompareYounger) { |
374 | ThreadPlanStepInRange *step_in_range_plan = |
375 | static_cast<ThreadPlanStepInRange *>(current_plan); |
376 | if (step_in_range_plan->m_step_into_target) { |
377 | SymbolContext sc = frame->GetSymbolContext( |
378 | resolve_scope: eSymbolContextFunction | eSymbolContextBlock | eSymbolContextSymbol); |
379 | if (sc.symbol != nullptr) { |
380 | // First try an exact match, since that's cheap with ConstStrings. |
381 | // Then do a strstr compare. |
382 | if (step_in_range_plan->m_step_into_target == sc.GetFunctionName()) { |
383 | should_stop_here = true; |
384 | } else { |
385 | const char *target_name = |
386 | step_in_range_plan->m_step_into_target.AsCString(); |
387 | const char *function_name = sc.GetFunctionName().AsCString(); |
388 | |
389 | if (function_name == nullptr) |
390 | should_stop_here = false; |
391 | else if (strstr(haystack: function_name, needle: target_name) == nullptr) |
392 | should_stop_here = false; |
393 | } |
394 | if (log && !should_stop_here) |
395 | LLDB_LOGF(log, |
396 | "Stepping out of frame %s which did not match step into " |
397 | "target %s." , |
398 | sc.GetFunctionName().AsCString(), |
399 | step_in_range_plan->m_step_into_target.AsCString()); |
400 | } |
401 | } |
402 | |
403 | if (should_stop_here) { |
404 | ThreadPlanStepInRange *step_in_range_plan = |
405 | static_cast<ThreadPlanStepInRange *>(current_plan); |
406 | // Don't log the should_step_out here, it's easier to do it in |
407 | // FrameMatchesAvoidCriteria. |
408 | should_stop_here = !step_in_range_plan->FrameMatchesAvoidCriteria(); |
409 | } |
410 | } |
411 | |
412 | return should_stop_here; |
413 | } |
414 | |
415 | bool ThreadPlanStepInRange::DoPlanExplainsStop(Event *event_ptr) { |
416 | // We always explain a stop. Either we've just done a single step, in which |
417 | // case we'll do our ordinary processing, or we stopped for some reason that |
418 | // isn't handled by our sub-plans, in which case we want to just stop right |
419 | // away. In general, we don't want to mark the plan as complete for |
420 | // unexplained stops. For instance, if you step in to some code with no debug |
421 | // info, so you step out and in the course of that hit a breakpoint, then you |
422 | // want to stop & show the user the breakpoint, but not unship the step in |
423 | // plan, since you still may want to complete that plan when you continue. |
424 | // This is particularly true when doing "step in to target function." |
425 | // stepping. |
426 | // |
427 | // The only variation is that if we are doing "step by running to next |
428 | // branch" in which case if we hit our branch breakpoint we don't set the |
429 | // plan to complete. |
430 | |
431 | bool return_value = false; |
432 | |
433 | if (m_virtual_step) { |
434 | return_value = true; |
435 | } else { |
436 | StopInfoSP stop_info_sp = GetPrivateStopInfo(); |
437 | if (stop_info_sp) { |
438 | StopReason reason = stop_info_sp->GetStopReason(); |
439 | |
440 | if (reason == eStopReasonBreakpoint) { |
441 | if (NextRangeBreakpointExplainsStop(stop_info_sp)) { |
442 | return_value = true; |
443 | } |
444 | } else if (IsUsuallyUnexplainedStopReason(reason)) { |
445 | Log *log = GetLog(mask: LLDBLog::Step); |
446 | if (log) |
447 | log->PutCString(cstr: "ThreadPlanStepInRange got asked if it explains the " |
448 | "stop for some reason other than step." ); |
449 | return_value = false; |
450 | } else { |
451 | return_value = true; |
452 | } |
453 | } else |
454 | return_value = true; |
455 | } |
456 | |
457 | return return_value; |
458 | } |
459 | |
460 | bool ThreadPlanStepInRange::DoWillResume(lldb::StateType resume_state, |
461 | bool current_plan) { |
462 | m_virtual_step = false; |
463 | if (resume_state == eStateStepping && current_plan) { |
464 | Thread &thread = GetThread(); |
465 | // See if we are about to step over a virtual inlined call. |
466 | bool step_without_resume = thread.DecrementCurrentInlinedDepth(); |
467 | if (step_without_resume) { |
468 | Log *log = GetLog(mask: LLDBLog::Step); |
469 | LLDB_LOGF(log, |
470 | "ThreadPlanStepInRange::DoWillResume: returning false, " |
471 | "inline_depth: %d" , |
472 | thread.GetCurrentInlinedDepth()); |
473 | SetStopInfo(StopInfo::CreateStopReasonToTrace(thread)); |
474 | |
475 | // FIXME: Maybe it would be better to create a InlineStep stop reason, but |
476 | // then |
477 | // the whole rest of the world would have to handle that stop reason. |
478 | m_virtual_step = true; |
479 | } |
480 | return !step_without_resume; |
481 | } |
482 | return true; |
483 | } |
484 | |
485 | bool ThreadPlanStepInRange::IsVirtualStep() { return m_virtual_step; } |
486 | |