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