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 == |
124 | *new_context.line_entry.original_file_sp) { |
125 | if (m_addr_context.line_entry.line == new_context.line_entry.line) { |
126 | m_addr_context = new_context; |
127 | const bool include_inlined_functions = |
128 | GetKind() == eKindStepOverRange; |
129 | AddRange(new_range: m_addr_context.line_entry.GetSameLineContiguousAddressRange( |
130 | include_inlined_functions)); |
131 | ret_value = true; |
132 | if (log) { |
133 | StreamString s; |
134 | m_addr_context.line_entry.Dump(s: &s, target: &GetTarget(), show_file: true, |
135 | style: Address::DumpStyleLoadAddress, |
136 | fallback_style: Address::DumpStyleLoadAddress, show_range: true); |
137 | |
138 | LLDB_LOGF( |
139 | log, |
140 | "Step range plan stepped to another range of same line: %s" , |
141 | s.GetData()); |
142 | } |
143 | } else if (new_context.line_entry.line == 0) { |
144 | new_context.line_entry.line = m_addr_context.line_entry.line; |
145 | m_addr_context = new_context; |
146 | const bool include_inlined_functions = |
147 | GetKind() == eKindStepOverRange; |
148 | AddRange(new_range: m_addr_context.line_entry.GetSameLineContiguousAddressRange( |
149 | include_inlined_functions)); |
150 | ret_value = true; |
151 | if (log) { |
152 | StreamString s; |
153 | m_addr_context.line_entry.Dump(s: &s, target: &GetTarget(), show_file: true, |
154 | style: Address::DumpStyleLoadAddress, |
155 | fallback_style: Address::DumpStyleLoadAddress, show_range: true); |
156 | |
157 | LLDB_LOGF(log, |
158 | "Step range plan stepped to a range at linenumber 0 " |
159 | "stepping through that range: %s" , |
160 | s.GetData()); |
161 | } |
162 | } else if (new_context.line_entry.range.GetBaseAddress().GetLoadAddress( |
163 | target: &GetTarget()) != pc_load_addr) { |
164 | // Another thing that sometimes happens here is that we step out of |
165 | // one line into the MIDDLE of another line. So far I mostly see |
166 | // this due to bugs in the debug information. But we probably don't |
167 | // want to be in the middle of a line range, so in that case reset |
168 | // the stepping range to the line we've stepped into the middle of |
169 | // and continue. |
170 | m_addr_context = new_context; |
171 | m_address_ranges.clear(); |
172 | AddRange(new_range: m_addr_context.line_entry.range); |
173 | ret_value = true; |
174 | if (log) { |
175 | StreamString s; |
176 | m_addr_context.line_entry.Dump(s: &s, target: &GetTarget(), show_file: true, |
177 | style: Address::DumpStyleLoadAddress, |
178 | fallback_style: Address::DumpStyleLoadAddress, show_range: true); |
179 | |
180 | LLDB_LOGF(log, |
181 | "Step range plan stepped to the middle of new " |
182 | "line(%d): %s, continuing to clear this line." , |
183 | new_context.line_entry.line, s.GetData()); |
184 | } |
185 | } |
186 | } |
187 | } |
188 | } |
189 | |
190 | if (!ret_value && log) |
191 | LLDB_LOGF(log, "Step range plan out of range to 0x%" PRIx64, pc_load_addr); |
192 | |
193 | return ret_value; |
194 | } |
195 | |
196 | bool ThreadPlanStepRange::InSymbol() { |
197 | lldb::addr_t cur_pc = GetThread().GetRegisterContext()->GetPC(); |
198 | if (m_addr_context.function != nullptr) { |
199 | return m_addr_context.function->GetAddressRange().ContainsLoadAddress( |
200 | load_addr: cur_pc, target: &GetTarget()); |
201 | } else if (m_addr_context.symbol && m_addr_context.symbol->ValueIsAddress()) { |
202 | AddressRange range(m_addr_context.symbol->GetAddressRef(), |
203 | m_addr_context.symbol->GetByteSize()); |
204 | return range.ContainsLoadAddress(load_addr: cur_pc, target: &GetTarget()); |
205 | } |
206 | return false; |
207 | } |
208 | |
209 | // FIXME: This should also handle inlining if we aren't going to do inlining in |
210 | // the |
211 | // main stack. |
212 | // |
213 | // Ideally we should remember the whole stack frame list, and then compare that |
214 | // to the current list. |
215 | |
216 | lldb::FrameComparison ThreadPlanStepRange::CompareCurrentFrameToStartFrame() { |
217 | FrameComparison frame_order; |
218 | Thread &thread = GetThread(); |
219 | StackID cur_frame_id = thread.GetStackFrameAtIndex(idx: 0)->GetStackID(); |
220 | |
221 | if (cur_frame_id == m_stack_id) { |
222 | frame_order = eFrameCompareEqual; |
223 | } else if (cur_frame_id < m_stack_id) { |
224 | frame_order = eFrameCompareYounger; |
225 | } else { |
226 | StackFrameSP cur_parent_frame = thread.GetStackFrameAtIndex(idx: 1); |
227 | StackID cur_parent_id; |
228 | if (cur_parent_frame) |
229 | cur_parent_id = cur_parent_frame->GetStackID(); |
230 | if (m_parent_stack_id.IsValid() && cur_parent_id.IsValid() && |
231 | m_parent_stack_id == cur_parent_id) |
232 | frame_order = eFrameCompareSameParent; |
233 | else |
234 | frame_order = eFrameCompareOlder; |
235 | } |
236 | return frame_order; |
237 | } |
238 | |
239 | bool ThreadPlanStepRange::StopOthers() { |
240 | switch (m_stop_others) { |
241 | case lldb::eOnlyThisThread: |
242 | return true; |
243 | case lldb::eOnlyDuringStepping: |
244 | // If there is a call in the range of the next branch breakpoint, |
245 | // then we should always run all threads, since a call can execute |
246 | // arbitrary code which might for instance take a lock that's held |
247 | // by another thread. |
248 | return !m_found_calls; |
249 | case lldb::eAllThreads: |
250 | return false; |
251 | } |
252 | llvm_unreachable("Unhandled run mode!" ); |
253 | } |
254 | |
255 | InstructionList *ThreadPlanStepRange::GetInstructionsForAddress( |
256 | lldb::addr_t addr, size_t &range_index, size_t &insn_offset) { |
257 | size_t num_ranges = m_address_ranges.size(); |
258 | for (size_t i = 0; i < num_ranges; i++) { |
259 | if (m_address_ranges[i].ContainsLoadAddress(load_addr: addr, target: &GetTarget())) { |
260 | // Some joker added a zero size range to the stepping range... |
261 | if (m_address_ranges[i].GetByteSize() == 0) |
262 | return nullptr; |
263 | |
264 | if (!m_instruction_ranges[i]) { |
265 | // Disassemble the address range given: |
266 | const char *plugin_name = nullptr; |
267 | const char *flavor = nullptr; |
268 | m_instruction_ranges[i] = Disassembler::DisassembleRange( |
269 | arch: GetTarget().GetArchitecture(), plugin_name, flavor, target&: GetTarget(), |
270 | disasm_range: m_address_ranges[i]); |
271 | } |
272 | if (!m_instruction_ranges[i]) |
273 | return nullptr; |
274 | else { |
275 | // Find where we are in the instruction list as well. If we aren't at |
276 | // an instruction, return nullptr. In this case, we're probably lost, |
277 | // and shouldn't try to do anything fancy. |
278 | |
279 | insn_offset = |
280 | m_instruction_ranges[i] |
281 | ->GetInstructionList() |
282 | .GetIndexOfInstructionAtLoadAddress(load_addr: addr, target&: GetTarget()); |
283 | if (insn_offset == UINT32_MAX) |
284 | return nullptr; |
285 | else { |
286 | range_index = i; |
287 | return &m_instruction_ranges[i]->GetInstructionList(); |
288 | } |
289 | } |
290 | } |
291 | } |
292 | return nullptr; |
293 | } |
294 | |
295 | void ThreadPlanStepRange::ClearNextBranchBreakpoint() { |
296 | if (m_next_branch_bp_sp) { |
297 | Log *log = GetLog(mask: LLDBLog::Step); |
298 | LLDB_LOGF(log, "Removing next branch breakpoint: %d." , |
299 | m_next_branch_bp_sp->GetID()); |
300 | GetTarget().RemoveBreakpointByID(break_id: m_next_branch_bp_sp->GetID()); |
301 | m_next_branch_bp_sp.reset(); |
302 | m_could_not_resolve_hw_bp = false; |
303 | m_found_calls = false; |
304 | } |
305 | } |
306 | |
307 | bool ThreadPlanStepRange::SetNextBranchBreakpoint() { |
308 | if (m_next_branch_bp_sp) |
309 | return true; |
310 | |
311 | Log *log = GetLog(mask: LLDBLog::Step); |
312 | // Stepping through ranges using breakpoints doesn't work yet, but with this |
313 | // off we fall back to instruction single stepping. |
314 | if (!m_use_fast_step) |
315 | return false; |
316 | |
317 | // clear the m_found_calls, we'll rediscover it for this range. |
318 | m_found_calls = false; |
319 | |
320 | lldb::addr_t cur_addr = GetThread().GetRegisterContext()->GetPC(); |
321 | // Find the current address in our address ranges, and fetch the disassembly |
322 | // if we haven't already: |
323 | size_t pc_index; |
324 | size_t range_index; |
325 | InstructionList *instructions = |
326 | GetInstructionsForAddress(addr: cur_addr, range_index, insn_offset&: pc_index); |
327 | if (instructions == nullptr) |
328 | return false; |
329 | else { |
330 | const bool ignore_calls = GetKind() == eKindStepOverRange; |
331 | uint32_t branch_index = instructions->GetIndexOfNextBranchInstruction( |
332 | start: pc_index, ignore_calls, found_calls: &m_found_calls); |
333 | Address run_to_address; |
334 | |
335 | // If we didn't find a branch, run to the end of the range. |
336 | if (branch_index == UINT32_MAX) { |
337 | uint32_t last_index = instructions->GetSize() - 1; |
338 | if (last_index - pc_index > 1) { |
339 | InstructionSP last_inst = |
340 | instructions->GetInstructionAtIndex(idx: last_index); |
341 | size_t last_inst_size = last_inst->GetOpcode().GetByteSize(); |
342 | run_to_address = last_inst->GetAddress(); |
343 | run_to_address.Slide(offset: last_inst_size); |
344 | } |
345 | } else if (branch_index - pc_index > 1) { |
346 | run_to_address = |
347 | instructions->GetInstructionAtIndex(idx: branch_index)->GetAddress(); |
348 | } |
349 | |
350 | if (run_to_address.IsValid()) { |
351 | const bool is_internal = true; |
352 | m_next_branch_bp_sp = |
353 | GetTarget().CreateBreakpoint(addr: run_to_address, internal: is_internal, request_hardware: false); |
354 | if (m_next_branch_bp_sp) { |
355 | |
356 | if (m_next_branch_bp_sp->IsHardware() && |
357 | !m_next_branch_bp_sp->HasResolvedLocations()) |
358 | m_could_not_resolve_hw_bp = true; |
359 | |
360 | if (log) { |
361 | lldb::break_id_t bp_site_id = LLDB_INVALID_BREAK_ID; |
362 | BreakpointLocationSP bp_loc = |
363 | m_next_branch_bp_sp->GetLocationAtIndex(index: 0); |
364 | if (bp_loc) { |
365 | BreakpointSiteSP bp_site = bp_loc->GetBreakpointSite(); |
366 | if (bp_site) { |
367 | bp_site_id = bp_site->GetID(); |
368 | } |
369 | } |
370 | LLDB_LOGF(log, |
371 | "ThreadPlanStepRange::SetNextBranchBreakpoint - Setting " |
372 | "breakpoint %d (site %d) to run to address 0x%" PRIx64, |
373 | m_next_branch_bp_sp->GetID(), bp_site_id, |
374 | run_to_address.GetLoadAddress(&m_process.GetTarget())); |
375 | } |
376 | |
377 | m_next_branch_bp_sp->SetThreadID(m_tid); |
378 | m_next_branch_bp_sp->SetBreakpointKind("next-branch-location" ); |
379 | |
380 | return true; |
381 | } else |
382 | return false; |
383 | } |
384 | } |
385 | return false; |
386 | } |
387 | |
388 | bool ThreadPlanStepRange::( |
389 | lldb::StopInfoSP stop_info_sp) { |
390 | Log *log = GetLog(mask: LLDBLog::Step); |
391 | if (!m_next_branch_bp_sp) |
392 | return false; |
393 | |
394 | break_id_t bp_site_id = stop_info_sp->GetValue(); |
395 | BreakpointSiteSP bp_site_sp = |
396 | m_process.GetBreakpointSiteList().FindByID(site_id: bp_site_id); |
397 | if (!bp_site_sp) |
398 | return false; |
399 | else if (!bp_site_sp->IsBreakpointAtThisSite(bp_id: m_next_branch_bp_sp->GetID())) |
400 | return false; |
401 | else { |
402 | // If we've hit the next branch breakpoint, then clear it. |
403 | size_t num_constituents = bp_site_sp->GetNumberOfConstituents(); |
404 | bool explains_stop = true; |
405 | // If all the constituents are internal, then we are probably just stepping |
406 | // over this range from multiple threads, or multiple frames, so we want to |
407 | // continue. If one is not internal, then we should not explain the stop, |
408 | // and let the user breakpoint handle the stop. |
409 | for (size_t i = 0; i < num_constituents; i++) { |
410 | if (!bp_site_sp->GetConstituentAtIndex(idx: i)->GetBreakpoint().IsInternal()) { |
411 | explains_stop = false; |
412 | break; |
413 | } |
414 | } |
415 | LLDB_LOGF(log, |
416 | "ThreadPlanStepRange::NextRangeBreakpointExplainsStop - Hit " |
417 | "next range breakpoint which has %" PRIu64 |
418 | " constituents - explains stop: %u." , |
419 | (uint64_t)num_constituents, explains_stop); |
420 | ClearNextBranchBreakpoint(); |
421 | return explains_stop; |
422 | } |
423 | } |
424 | |
425 | bool ThreadPlanStepRange::WillStop() { return true; } |
426 | |
427 | StateType ThreadPlanStepRange::GetPlanRunState() { |
428 | if (m_next_branch_bp_sp) |
429 | return eStateRunning; |
430 | else |
431 | return eStateStepping; |
432 | } |
433 | |
434 | bool ThreadPlanStepRange::MischiefManaged() { |
435 | // If we have pushed some plans between ShouldStop & MischiefManaged, then |
436 | // we're not done... |
437 | // I do this check first because we might have stepped somewhere that will |
438 | // fool InRange into |
439 | // thinking it needs to step past the end of that line. This happens, for |
440 | // instance, when stepping over inlined code that is in the middle of the |
441 | // current line. |
442 | |
443 | if (!m_no_more_plans) |
444 | return false; |
445 | |
446 | bool done = true; |
447 | if (!IsPlanComplete()) { |
448 | if (InRange()) { |
449 | done = false; |
450 | } else { |
451 | FrameComparison frame_order = CompareCurrentFrameToStartFrame(); |
452 | done = (frame_order != eFrameCompareOlder) ? m_no_more_plans : true; |
453 | } |
454 | } |
455 | |
456 | if (done) { |
457 | Log *log = GetLog(mask: LLDBLog::Step); |
458 | LLDB_LOGF(log, "Completed step through range plan." ); |
459 | ClearNextBranchBreakpoint(); |
460 | ThreadPlan::MischiefManaged(); |
461 | return true; |
462 | } else { |
463 | return false; |
464 | } |
465 | } |
466 | |
467 | bool ThreadPlanStepRange::IsPlanStale() { |
468 | Log *log = GetLog(mask: LLDBLog::Step); |
469 | FrameComparison frame_order = CompareCurrentFrameToStartFrame(); |
470 | |
471 | if (frame_order == eFrameCompareOlder) { |
472 | if (log) { |
473 | LLDB_LOGF(log, "ThreadPlanStepRange::IsPlanStale returning true, we've " |
474 | "stepped out." ); |
475 | } |
476 | return true; |
477 | } else if (frame_order == eFrameCompareEqual && InSymbol()) { |
478 | // If we are not in a place we should step through, we've gotten stale. One |
479 | // tricky bit here is that some stubs don't push a frame, so we should. |
480 | // check that we are in the same symbol. |
481 | if (!InRange()) { |
482 | // Set plan Complete when we reach next instruction just after the range |
483 | lldb::addr_t addr = GetThread().GetRegisterContext()->GetPC() - 1; |
484 | size_t num_ranges = m_address_ranges.size(); |
485 | for (size_t i = 0; i < num_ranges; i++) { |
486 | bool in_range = |
487 | m_address_ranges[i].ContainsLoadAddress(load_addr: addr, target: &GetTarget()); |
488 | if (in_range) { |
489 | SetPlanComplete(); |
490 | } |
491 | } |
492 | return true; |
493 | } |
494 | } |
495 | return false; |
496 | } |
497 | |