1 | //===-- ThreadPlanStepRange.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/ThreadPlanStepRange.h" |
10 | #include "lldb/Breakpoint/BreakpointLocation.h" |
11 | #include "lldb/Breakpoint/BreakpointSite.h" |
12 | #include "lldb/Core/Disassembler.h" |
13 | #include "lldb/Symbol/Function.h" |
14 | #include "lldb/Symbol/Symbol.h" |
15 | #include "lldb/Target/ExecutionContext.h" |
16 | #include "lldb/Target/Process.h" |
17 | #include "lldb/Target/RegisterContext.h" |
18 | #include "lldb/Target/StopInfo.h" |
19 | #include "lldb/Target/Target.h" |
20 | #include "lldb/Target/Thread.h" |
21 | #include "lldb/Target/ThreadPlanRunToAddress.h" |
22 | #include "lldb/Utility/LLDBLog.h" |
23 | #include "lldb/Utility/Log.h" |
24 | #include "lldb/Utility/Stream.h" |
25 | |
26 | using namespace lldb; |
27 | using namespace lldb_private; |
28 | |
29 | // ThreadPlanStepRange: Step through a stack range, either stepping over or |
30 | // into based on the value of \a type. |
31 | |
32 | ThreadPlanStepRange::ThreadPlanStepRange(ThreadPlanKind kind, const char *name, |
33 | Thread &thread, |
34 | const AddressRange &range, |
35 | const SymbolContext &addr_context, |
36 | lldb::RunMode stop_others, |
37 | bool given_ranges_only) |
38 | : ThreadPlan(kind, name, thread, eVoteNoOpinion, eVoteNoOpinion), |
39 | m_addr_context(addr_context), m_address_ranges(), |
40 | m_stop_others(stop_others), m_stack_id(), m_parent_stack_id(), |
41 | m_no_more_plans(false), m_first_run_event(true), m_use_fast_step(false), |
42 | m_given_ranges_only(given_ranges_only) { |
43 | m_use_fast_step = GetTarget().GetUseFastStepping(); |
44 | AddRange(new_range: range); |
45 | m_stack_id = thread.GetStackFrameAtIndex(idx: 0)->GetStackID(); |
46 | StackFrameSP parent_stack = thread.GetStackFrameAtIndex(idx: 1); |
47 | if (parent_stack) |
48 | m_parent_stack_id = parent_stack->GetStackID(); |
49 | } |
50 | |
51 | ThreadPlanStepRange::~ThreadPlanStepRange() { ClearNextBranchBreakpoint(); } |
52 | |
53 | void ThreadPlanStepRange::DidPush() { |
54 | // See if we can find a "next range" breakpoint: |
55 | SetNextBranchBreakpoint(); |
56 | } |
57 | |
58 | bool ThreadPlanStepRange::ValidatePlan(Stream *error) { |
59 | if (m_could_not_resolve_hw_bp) { |
60 | if (error) |
61 | error->PutCString( |
62 | cstr: "Could not create hardware breakpoint for thread plan."); |
63 | return false; |
64 | } |
65 | return true; |
66 | } |
67 | |
68 | Vote ThreadPlanStepRange::ShouldReportStop(Event *event_ptr) { |
69 | Log *log = GetLog(mask: LLDBLog::Step); |
70 | |
71 | const Vote vote = IsPlanComplete() ? eVoteYes : eVoteNo; |
72 | LLDB_LOGF(log, "ThreadPlanStepRange::ShouldReportStop() returning vote %i\n", |
73 | vote); |
74 | return vote; |
75 | } |
76 | |
77 | void ThreadPlanStepRange::AddRange(const AddressRange &new_range) { |
78 | // For now I'm just adding the ranges. At some point we may want to condense |
79 | // the ranges if they overlap, though I don't think it is likely to be very |
80 | // important. |
81 | m_address_ranges.push_back(x: new_range); |
82 | |
83 | // Fill the slot for this address range with an empty DisassemblerSP in the |
84 | // instruction ranges. I want the indices to match, but I don't want to do |
85 | // the work to disassemble this range if I don't step into it. |
86 | m_instruction_ranges.push_back(x: DisassemblerSP()); |
87 | } |
88 | |
89 | void ThreadPlanStepRange::DumpRanges(Stream *s) { |
90 | size_t num_ranges = m_address_ranges.size(); |
91 | if (num_ranges == 1) { |
92 | m_address_ranges[0].Dump(s, target: &GetTarget(), style: Address::DumpStyleLoadAddress); |
93 | } else { |
94 | for (size_t i = 0; i < num_ranges; i++) { |
95 | s->Printf(format: " %"PRIu64 ": ", uint64_t(i)); |
96 | m_address_ranges[i].Dump(s, target: &GetTarget(), style: Address::DumpStyleLoadAddress); |
97 | } |
98 | } |
99 | } |
100 | |
101 | bool ThreadPlanStepRange::InRange() { |
102 | Log *log = GetLog(mask: LLDBLog::Step); |
103 | bool ret_value = false; |
104 | Thread &thread = GetThread(); |
105 | lldb::addr_t pc_load_addr = thread.GetRegisterContext()->GetPC(); |
106 | |
107 | size_t num_ranges = m_address_ranges.size(); |
108 | for (size_t i = 0; i < num_ranges; i++) { |
109 | ret_value = |
110 | m_address_ranges[i].ContainsLoadAddress(load_addr: pc_load_addr, target: &GetTarget()); |
111 | if (ret_value) |
112 | break; |
113 | } |
114 | |
115 | if (!ret_value && !m_given_ranges_only) { |
116 | // See if we've just stepped to another part of the same line number... |
117 | StackFrame *frame = thread.GetStackFrameAtIndex(idx: 0).get(); |
118 | |
119 | SymbolContext new_context( |
120 | frame->GetSymbolContext(resolve_scope: eSymbolContextEverything)); |
121 | if (m_addr_context.line_entry.IsValid() && |
122 | new_context.line_entry.IsValid()) { |
123 | if (m_addr_context.line_entry.original_file_sp->Equal( |
124 | other: *new_context.line_entry.original_file_sp, |
125 | equality: SupportFile::eEqualFileSpecAndChecksumIfSet)) { |
126 | if (m_addr_context.line_entry.line == new_context.line_entry.line) { |
127 | m_addr_context = new_context; |
128 | const bool include_inlined_functions = |
129 | GetKind() == eKindStepOverRange; |
130 | AddRange(new_range: m_addr_context.line_entry.GetSameLineContiguousAddressRange( |
131 | include_inlined_functions)); |
132 | ret_value = true; |
133 | if (log) { |
134 | StreamString s; |
135 | m_addr_context.line_entry.Dump(s: &s, target: &GetTarget(), show_file: true, |
136 | style: Address::DumpStyleLoadAddress, |
137 | fallback_style: Address::DumpStyleLoadAddress, show_range: true); |
138 | |
139 | LLDB_LOGF( |
140 | log, |
141 | "Step range plan stepped to another range of same line: %s", |
142 | s.GetData()); |
143 | } |
144 | } else if (new_context.line_entry.line == 0) { |
145 | new_context.line_entry.line = m_addr_context.line_entry.line; |
146 | m_addr_context = new_context; |
147 | const bool include_inlined_functions = |
148 | GetKind() == eKindStepOverRange; |
149 | AddRange(new_range: m_addr_context.line_entry.GetSameLineContiguousAddressRange( |
150 | include_inlined_functions)); |
151 | ret_value = true; |
152 | if (log) { |
153 | StreamString s; |
154 | m_addr_context.line_entry.Dump(s: &s, target: &GetTarget(), show_file: true, |
155 | style: Address::DumpStyleLoadAddress, |
156 | fallback_style: Address::DumpStyleLoadAddress, show_range: true); |
157 | |
158 | LLDB_LOGF(log, |
159 | "Step range plan stepped to a range at linenumber 0 " |
160 | "stepping through that range: %s", |
161 | s.GetData()); |
162 | } |
163 | } else if (new_context.line_entry.range.GetBaseAddress().GetLoadAddress( |
164 | target: &GetTarget()) != pc_load_addr) { |
165 | // Another thing that sometimes happens here is that we step out of |
166 | // one line into the MIDDLE of another line. So far I mostly see |
167 | // this due to bugs in the debug information. But we probably don't |
168 | // want to be in the middle of a line range, so in that case reset |
169 | // the stepping range to the line we've stepped into the middle of |
170 | // and continue. |
171 | m_addr_context = new_context; |
172 | m_address_ranges.clear(); |
173 | AddRange(new_range: m_addr_context.line_entry.range); |
174 | ret_value = true; |
175 | if (log) { |
176 | StreamString s; |
177 | m_addr_context.line_entry.Dump(s: &s, target: &GetTarget(), show_file: true, |
178 | style: Address::DumpStyleLoadAddress, |
179 | fallback_style: Address::DumpStyleLoadAddress, show_range: true); |
180 | |
181 | LLDB_LOGF(log, |
182 | "Step range plan stepped to the middle of new " |
183 | "line(%d): %s, continuing to clear this line.", |
184 | new_context.line_entry.line, s.GetData()); |
185 | } |
186 | } |
187 | } |
188 | } |
189 | } |
190 | |
191 | if (!ret_value && log) |
192 | LLDB_LOGF(log, "Step range plan out of range to 0x%"PRIx64, pc_load_addr); |
193 | |
194 | return ret_value; |
195 | } |
196 | |
197 | bool ThreadPlanStepRange::InSymbol() { |
198 | lldb::addr_t cur_pc = GetThread().GetRegisterContext()->GetPC(); |
199 | if (m_addr_context.function != nullptr) { |
200 | AddressRange unused_range; |
201 | return m_addr_context.function->GetRangeContainingLoadAddress( |
202 | load_addr: cur_pc, target&: GetTarget(), range&: unused_range); |
203 | } |
204 | if (m_addr_context.symbol && m_addr_context.symbol->ValueIsAddress()) { |
205 | AddressRange range(m_addr_context.symbol->GetAddressRef(), |
206 | m_addr_context.symbol->GetByteSize()); |
207 | return range.ContainsLoadAddress(load_addr: cur_pc, target: &GetTarget()); |
208 | } |
209 | return false; |
210 | } |
211 | |
212 | // FIXME: This should also handle inlining if we aren't going to do inlining in |
213 | // the |
214 | // main stack. |
215 | // |
216 | // Ideally we should remember the whole stack frame list, and then compare that |
217 | // to the current list. |
218 | |
219 | lldb::FrameComparison ThreadPlanStepRange::CompareCurrentFrameToStartFrame() { |
220 | FrameComparison frame_order; |
221 | Thread &thread = GetThread(); |
222 | StackID cur_frame_id = thread.GetStackFrameAtIndex(idx: 0)->GetStackID(); |
223 | |
224 | if (cur_frame_id == m_stack_id) { |
225 | frame_order = eFrameCompareEqual; |
226 | } else if (cur_frame_id < m_stack_id) { |
227 | frame_order = eFrameCompareYounger; |
228 | } else { |
229 | StackFrameSP cur_parent_frame = thread.GetStackFrameAtIndex(idx: 1); |
230 | StackID cur_parent_id; |
231 | if (cur_parent_frame) |
232 | cur_parent_id = cur_parent_frame->GetStackID(); |
233 | if (m_parent_stack_id.IsValid() && cur_parent_id.IsValid() && |
234 | m_parent_stack_id == cur_parent_id) |
235 | frame_order = eFrameCompareSameParent; |
236 | else |
237 | frame_order = eFrameCompareOlder; |
238 | } |
239 | return frame_order; |
240 | } |
241 | |
242 | bool ThreadPlanStepRange::StopOthers() { |
243 | switch (m_stop_others) { |
244 | case lldb::eOnlyThisThread: |
245 | return true; |
246 | case lldb::eOnlyDuringStepping: |
247 | // If there is a call in the range of the next branch breakpoint, |
248 | // then we should always run all threads, since a call can execute |
249 | // arbitrary code which might for instance take a lock that's held |
250 | // by another thread. |
251 | return !m_found_calls; |
252 | case lldb::eAllThreads: |
253 | return false; |
254 | } |
255 | llvm_unreachable("Unhandled run mode!"); |
256 | } |
257 | |
258 | InstructionList *ThreadPlanStepRange::GetInstructionsForAddress( |
259 | lldb::addr_t addr, size_t &range_index, size_t &insn_offset) { |
260 | size_t num_ranges = m_address_ranges.size(); |
261 | for (size_t i = 0; i < num_ranges; i++) { |
262 | if (m_address_ranges[i].ContainsLoadAddress(load_addr: addr, target: &GetTarget())) { |
263 | // Some joker added a zero size range to the stepping range... |
264 | if (m_address_ranges[i].GetByteSize() == 0) |
265 | return nullptr; |
266 | |
267 | if (!m_instruction_ranges[i]) { |
268 | // Disassemble the address range given: |
269 | const char *plugin_name = nullptr; |
270 | const char *flavor = nullptr; |
271 | const char *cpu = nullptr; |
272 | const char *features = nullptr; |
273 | m_instruction_ranges[i] = Disassembler::DisassembleRange( |
274 | arch: GetTarget().GetArchitecture(), plugin_name, flavor, cpu, features, |
275 | target&: GetTarget(), disasm_ranges: m_address_ranges[i]); |
276 | } |
277 | if (!m_instruction_ranges[i]) |
278 | return nullptr; |
279 | else { |
280 | // Find where we are in the instruction list as well. If we aren't at |
281 | // an instruction, return nullptr. In this case, we're probably lost, |
282 | // and shouldn't try to do anything fancy. |
283 | |
284 | insn_offset = |
285 | m_instruction_ranges[i] |
286 | ->GetInstructionList() |
287 | .GetIndexOfInstructionAtLoadAddress(load_addr: addr, target&: GetTarget()); |
288 | if (insn_offset == UINT32_MAX) |
289 | return nullptr; |
290 | else { |
291 | range_index = i; |
292 | return &m_instruction_ranges[i]->GetInstructionList(); |
293 | } |
294 | } |
295 | } |
296 | } |
297 | return nullptr; |
298 | } |
299 | |
300 | bool ThreadPlanStepRange::IsNextBranchBreakpointStop(StopInfoSP stop_info_sp) { |
301 | if (!m_next_branch_bp_sp) |
302 | return false; |
303 | |
304 | break_id_t bp_site_id = stop_info_sp->GetValue(); |
305 | BreakpointSiteSP bp_site_sp = |
306 | m_process.GetBreakpointSiteList().FindByID(site_id: bp_site_id); |
307 | if (!bp_site_sp) |
308 | return false; |
309 | else if (!bp_site_sp->IsBreakpointAtThisSite(bp_id: m_next_branch_bp_sp->GetID())) |
310 | return false; |
311 | return true; |
312 | } |
313 | |
314 | void ThreadPlanStepRange::ClearNextBranchBreakpoint() { |
315 | if (m_next_branch_bp_sp) { |
316 | Log *log = GetLog(mask: LLDBLog::Step); |
317 | LLDB_LOGF(log, "Removing next branch breakpoint: %d.", |
318 | m_next_branch_bp_sp->GetID()); |
319 | GetTarget().RemoveBreakpointByID(break_id: m_next_branch_bp_sp->GetID()); |
320 | m_next_branch_bp_sp.reset(); |
321 | m_could_not_resolve_hw_bp = false; |
322 | m_found_calls = false; |
323 | } |
324 | } |
325 | |
326 | void ThreadPlanStepRange::ClearNextBranchBreakpointExplainedStop() { |
327 | if (IsNextBranchBreakpointStop(stop_info_sp: GetPrivateStopInfo())) |
328 | ClearNextBranchBreakpoint(); |
329 | } |
330 | |
331 | bool ThreadPlanStepRange::SetNextBranchBreakpoint() { |
332 | if (m_next_branch_bp_sp) |
333 | return true; |
334 | |
335 | Log *log = GetLog(mask: LLDBLog::Step); |
336 | // Stepping through ranges using breakpoints doesn't work yet, but with this |
337 | // off we fall back to instruction single stepping. |
338 | if (!m_use_fast_step) |
339 | return false; |
340 | |
341 | // clear the m_found_calls, we'll rediscover it for this range. |
342 | m_found_calls = false; |
343 | |
344 | lldb::addr_t cur_addr = GetThread().GetRegisterContext()->GetPC(); |
345 | // Find the current address in our address ranges, and fetch the disassembly |
346 | // if we haven't already: |
347 | size_t pc_index; |
348 | size_t range_index; |
349 | InstructionList *instructions = |
350 | GetInstructionsForAddress(addr: cur_addr, range_index, insn_offset&: pc_index); |
351 | if (instructions == nullptr) |
352 | return false; |
353 | else { |
354 | const bool ignore_calls = GetKind() == eKindStepOverRange; |
355 | uint32_t branch_index = instructions->GetIndexOfNextBranchInstruction( |
356 | start: pc_index, ignore_calls, found_calls: &m_found_calls); |
357 | Address run_to_address; |
358 | |
359 | // If we didn't find a branch, run to the end of the range. |
360 | if (branch_index == UINT32_MAX) { |
361 | uint32_t last_index = instructions->GetSize() - 1; |
362 | if (last_index - pc_index > 1) { |
363 | InstructionSP last_inst = |
364 | instructions->GetInstructionAtIndex(idx: last_index); |
365 | size_t last_inst_size = last_inst->GetOpcode().GetByteSize(); |
366 | run_to_address = last_inst->GetAddress(); |
367 | run_to_address.Slide(offset: last_inst_size); |
368 | } |
369 | } else if (branch_index - pc_index > 1) { |
370 | run_to_address = |
371 | instructions->GetInstructionAtIndex(idx: branch_index)->GetAddress(); |
372 | } |
373 | if (branch_index == pc_index) |
374 | LLDB_LOGF(log, "ThreadPlanStepRange::SetNextBranchBreakpoint - skipping " |
375 | "because current is branch instruction"); |
376 | if (run_to_address.IsValid()) { |
377 | const bool is_internal = true; |
378 | m_next_branch_bp_sp = |
379 | GetTarget().CreateBreakpoint(addr: run_to_address, internal: is_internal, request_hardware: false); |
380 | if (m_next_branch_bp_sp) { |
381 | |
382 | if (m_next_branch_bp_sp->IsHardware() && |
383 | !m_next_branch_bp_sp->HasResolvedLocations()) |
384 | m_could_not_resolve_hw_bp = true; |
385 | |
386 | BreakpointLocationSP bp_loc = |
387 | m_next_branch_bp_sp->GetLocationAtIndex(index: 0); |
388 | if (log) { |
389 | lldb::break_id_t bp_site_id = LLDB_INVALID_BREAK_ID; |
390 | if (bp_loc) { |
391 | BreakpointSiteSP bp_site = bp_loc->GetBreakpointSite(); |
392 | if (bp_site) { |
393 | bp_site_id = bp_site->GetID(); |
394 | } |
395 | } |
396 | LLDB_LOGF(log, |
397 | "ThreadPlanStepRange::SetNextBranchBreakpoint - Setting " |
398 | "breakpoint %d (site %d) to run to address 0x%"PRIx64, |
399 | m_next_branch_bp_sp->GetID(), bp_site_id, |
400 | run_to_address.GetLoadAddress(&m_process.GetTarget())); |
401 | } |
402 | // The "next branch breakpoint might land on a virtual inlined call |
403 | // stack. If that's true, we should always stop at the top of the |
404 | // inlined call stack. Only virtual steps should walk deeper into the |
405 | // inlined call stack. |
406 | Block *block = run_to_address.CalculateSymbolContextBlock(); |
407 | if (bp_loc && block) { |
408 | LineEntry top_most_line_entry; |
409 | lldb::addr_t run_to_addr = run_to_address.GetFileAddress(); |
410 | for (Block *inlined_parent = block->GetContainingInlinedBlock(); |
411 | inlined_parent; |
412 | inlined_parent = inlined_parent->GetInlinedParent()) { |
413 | AddressRange range; |
414 | if (!inlined_parent->GetRangeContainingAddress(addr: run_to_address, |
415 | range)) |
416 | break; |
417 | Address range_start_address = range.GetBaseAddress(); |
418 | // Only compare addresses here, we may have different symbol |
419 | // contexts (for virtual inlined stacks), but we just want to know |
420 | // that they are all at the same address. |
421 | if (range_start_address.GetFileAddress() != run_to_addr) |
422 | break; |
423 | const InlineFunctionInfo *inline_info = |
424 | inlined_parent->GetInlinedFunctionInfo(); |
425 | if (!inline_info) |
426 | break; |
427 | const Declaration &call_site = inline_info->GetCallSite(); |
428 | top_most_line_entry.line = call_site.GetLine(); |
429 | top_most_line_entry.column = call_site.GetColumn(); |
430 | FileSpec call_site_file_spec = call_site.GetFile(); |
431 | top_most_line_entry.original_file_sp.reset( |
432 | p: new SupportFile(call_site_file_spec)); |
433 | top_most_line_entry.range = range; |
434 | top_most_line_entry.file_sp.reset(); |
435 | top_most_line_entry.ApplyFileMappings( |
436 | target_sp: GetThread().CalculateTarget()); |
437 | if (!top_most_line_entry.file_sp) |
438 | top_most_line_entry.file_sp = |
439 | top_most_line_entry.original_file_sp; |
440 | } |
441 | if (top_most_line_entry.IsValid()) { |
442 | LLDB_LOG(log, "Setting preferred line entry: {0}:{1}", |
443 | top_most_line_entry.GetFile(), top_most_line_entry.line); |
444 | bp_loc->SetPreferredLineEntry(top_most_line_entry); |
445 | } |
446 | } |
447 | m_next_branch_bp_sp->SetThreadID(m_tid); |
448 | m_next_branch_bp_sp->SetBreakpointKind("next-branch-location"); |
449 | |
450 | return true; |
451 | } else |
452 | return false; |
453 | } else |
454 | LLDB_LOGF(log, "ThreadPlanStepRange::SetNextBranchBreakpoint - skipping " |
455 | "invalid run_to_address"); |
456 | } |
457 | return false; |
458 | } |
459 | |
460 | bool ThreadPlanStepRange::NextRangeBreakpointExplainsStop( |
461 | lldb::StopInfoSP stop_info_sp) { |
462 | if (!IsNextBranchBreakpointStop(stop_info_sp)) |
463 | return false; |
464 | |
465 | break_id_t bp_site_id = stop_info_sp->GetValue(); |
466 | BreakpointSiteSP bp_site_sp = |
467 | m_process.GetBreakpointSiteList().FindByID(site_id: bp_site_id); |
468 | if (!bp_site_sp) |
469 | return false; |
470 | |
471 | // If we've hit the next branch breakpoint, then clear it. |
472 | size_t num_constituents = bp_site_sp->GetNumberOfConstituents(); |
473 | bool explains_stop = true; |
474 | // If all the constituents are internal, then we are probably just stepping |
475 | // over this range from multiple threads, or multiple frames, so we want to |
476 | // continue. If one is not internal, then we should not explain the stop, |
477 | // and let the user breakpoint handle the stop. |
478 | for (size_t i = 0; i < num_constituents; i++) { |
479 | if (!bp_site_sp->GetConstituentAtIndex(idx: i)->GetBreakpoint().IsInternal()) { |
480 | explains_stop = false; |
481 | break; |
482 | } |
483 | } |
484 | Log *log = GetLog(mask: LLDBLog::Step); |
485 | LLDB_LOGF(log, |
486 | "ThreadPlanStepRange::NextRangeBreakpointExplainsStop - Hit " |
487 | "next range breakpoint which has %"PRIu64 |
488 | " constituents - explains stop: %u.", |
489 | (uint64_t)num_constituents, explains_stop); |
490 | return explains_stop; |
491 | } |
492 | |
493 | bool ThreadPlanStepRange::WillStop() { return true; } |
494 | |
495 | StateType ThreadPlanStepRange::GetPlanRunState() { |
496 | if (m_next_branch_bp_sp) |
497 | return eStateRunning; |
498 | else |
499 | return eStateStepping; |
500 | } |
501 | |
502 | bool ThreadPlanStepRange::MischiefManaged() { |
503 | // If we have pushed some plans between ShouldStop & MischiefManaged, then |
504 | // we're not done... |
505 | // I do this check first because we might have stepped somewhere that will |
506 | // fool InRange into |
507 | // thinking it needs to step past the end of that line. This happens, for |
508 | // instance, when stepping over inlined code that is in the middle of the |
509 | // current line. |
510 | |
511 | if (!m_no_more_plans) |
512 | return false; |
513 | |
514 | bool done = true; |
515 | if (!IsPlanComplete()) { |
516 | if (InRange()) { |
517 | done = false; |
518 | } else { |
519 | FrameComparison frame_order = CompareCurrentFrameToStartFrame(); |
520 | done = (frame_order != eFrameCompareOlder) ? m_no_more_plans : true; |
521 | } |
522 | } |
523 | |
524 | if (done) { |
525 | Log *log = GetLog(mask: LLDBLog::Step); |
526 | LLDB_LOGF(log, "Completed step through range plan."); |
527 | ClearNextBranchBreakpoint(); |
528 | ThreadPlan::MischiefManaged(); |
529 | return true; |
530 | } else { |
531 | return false; |
532 | } |
533 | } |
534 | |
535 | bool ThreadPlanStepRange::IsPlanStale() { |
536 | Log *log = GetLog(mask: LLDBLog::Step); |
537 | FrameComparison frame_order = CompareCurrentFrameToStartFrame(); |
538 | |
539 | if (frame_order == eFrameCompareOlder) { |
540 | if (log) { |
541 | LLDB_LOGF(log, "ThreadPlanStepRange::IsPlanStale returning true, we've " |
542 | "stepped out."); |
543 | } |
544 | return true; |
545 | } else if (frame_order == eFrameCompareEqual && InSymbol()) { |
546 | // If we are not in a place we should step through, we've gotten stale. One |
547 | // tricky bit here is that some stubs don't push a frame, so we should. |
548 | // check that we are in the same symbol. |
549 | if (!InRange()) { |
550 | // Set plan Complete when we reach next instruction just after the range |
551 | lldb::addr_t addr = GetThread().GetRegisterContext()->GetPC() - 1; |
552 | size_t num_ranges = m_address_ranges.size(); |
553 | for (size_t i = 0; i < num_ranges; i++) { |
554 | bool in_range = |
555 | m_address_ranges[i].ContainsLoadAddress(load_addr: addr, target: &GetTarget()); |
556 | if (in_range) { |
557 | SetPlanComplete(); |
558 | } |
559 | } |
560 | return true; |
561 | } |
562 | } |
563 | return false; |
564 | } |
565 |
Definitions
- ThreadPlanStepRange
- ~ThreadPlanStepRange
- DidPush
- ValidatePlan
- ShouldReportStop
- AddRange
- DumpRanges
- InRange
- InSymbol
- CompareCurrentFrameToStartFrame
- StopOthers
- GetInstructionsForAddress
- IsNextBranchBreakpointStop
- ClearNextBranchBreakpoint
- ClearNextBranchBreakpointExplainedStop
- SetNextBranchBreakpoint
- NextRangeBreakpointExplainsStop
- WillStop
- GetPlanRunState
- MischiefManaged
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more