1 | //===-- ThreadPlanShouldStopHere.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/ThreadPlanShouldStopHere.h" |
10 | #include "lldb/Symbol/Symbol.h" |
11 | #include "lldb/Target/RegisterContext.h" |
12 | #include "lldb/Target/Thread.h" |
13 | #include "lldb/Utility/LLDBLog.h" |
14 | #include "lldb/Utility/Log.h" |
15 | |
16 | using namespace lldb; |
17 | using namespace lldb_private; |
18 | |
19 | // ThreadPlanShouldStopHere constructor |
20 | ThreadPlanShouldStopHere::ThreadPlanShouldStopHere(ThreadPlan *owner) |
21 | : m_callbacks(), m_baton(nullptr), m_owner(owner), |
22 | m_flags(ThreadPlanShouldStopHere::eNone) { |
23 | m_callbacks.should_stop_here_callback = |
24 | ThreadPlanShouldStopHere::DefaultShouldStopHereCallback; |
25 | m_callbacks.step_from_here_callback = |
26 | ThreadPlanShouldStopHere::DefaultStepFromHereCallback; |
27 | } |
28 | |
29 | ThreadPlanShouldStopHere::ThreadPlanShouldStopHere( |
30 | ThreadPlan *owner, const ThreadPlanShouldStopHereCallbacks *callbacks, |
31 | void *baton) |
32 | : m_callbacks(), m_baton(), m_owner(owner), |
33 | m_flags(ThreadPlanShouldStopHere::eNone) { |
34 | SetShouldStopHereCallbacks(callbacks, baton); |
35 | } |
36 | |
37 | ThreadPlanShouldStopHere::~ThreadPlanShouldStopHere() = default; |
38 | |
39 | bool ThreadPlanShouldStopHere::InvokeShouldStopHereCallback( |
40 | FrameComparison operation, Status &status) { |
41 | bool should_stop_here = true; |
42 | if (m_callbacks.should_stop_here_callback) { |
43 | should_stop_here = m_callbacks.should_stop_here_callback( |
44 | m_owner, m_flags, operation, status, m_baton); |
45 | Log *log = GetLog(mask: LLDBLog::Step); |
46 | if (log) { |
47 | lldb::addr_t current_addr = |
48 | m_owner->GetThread().GetRegisterContext()->GetPC(fail_value: 0); |
49 | |
50 | LLDB_LOGF(log, "ShouldStopHere callback returned %u from 0x%" PRIx64 "." , |
51 | should_stop_here, current_addr); |
52 | } |
53 | } |
54 | |
55 | return should_stop_here; |
56 | } |
57 | |
58 | bool ThreadPlanShouldStopHere::DefaultShouldStopHereCallback( |
59 | ThreadPlan *current_plan, Flags &flags, FrameComparison operation, |
60 | Status &status, void *baton) { |
61 | bool should_stop_here = true; |
62 | StackFrame *frame = current_plan->GetThread().GetStackFrameAtIndex(idx: 0).get(); |
63 | if (!frame) |
64 | return true; |
65 | |
66 | Log *log = GetLog(mask: LLDBLog::Step); |
67 | |
68 | if ((operation == eFrameCompareOlder && flags.Test(bit: eStepOutAvoidNoDebug)) || |
69 | (operation == eFrameCompareYounger && flags.Test(bit: eStepInAvoidNoDebug)) || |
70 | (operation == eFrameCompareSameParent && |
71 | flags.Test(bit: eStepInAvoidNoDebug))) { |
72 | if (!frame->HasDebugInformation()) { |
73 | LLDB_LOGF(log, "Stepping out of frame with no debug info" ); |
74 | |
75 | should_stop_here = false; |
76 | } |
77 | } |
78 | |
79 | // Always avoid code with line number 0. |
80 | // FIXME: At present the ShouldStop and the StepFromHere calculate this |
81 | // independently. If this ever |
82 | // becomes expensive (this one isn't) we can try to have this set a state |
83 | // that the StepFromHere can use. |
84 | if (frame) { |
85 | SymbolContext sc; |
86 | sc = frame->GetSymbolContext(resolve_scope: eSymbolContextLineEntry); |
87 | if (sc.line_entry.line == 0) |
88 | should_stop_here = false; |
89 | } |
90 | |
91 | return should_stop_here; |
92 | } |
93 | |
94 | ThreadPlanSP ThreadPlanShouldStopHere::DefaultStepFromHereCallback( |
95 | ThreadPlan *current_plan, Flags &flags, FrameComparison operation, |
96 | Status &status, void *baton) { |
97 | const bool stop_others = false; |
98 | const size_t frame_index = 0; |
99 | ThreadPlanSP return_plan_sp; |
100 | // If we are stepping through code at line number 0, then we need to step |
101 | // over this range. Otherwise we will step out. |
102 | Log *log = GetLog(mask: LLDBLog::Step); |
103 | |
104 | StackFrame *frame = current_plan->GetThread().GetStackFrameAtIndex(idx: 0).get(); |
105 | if (!frame) |
106 | return return_plan_sp; |
107 | SymbolContext sc; |
108 | sc = frame->GetSymbolContext(resolve_scope: eSymbolContextLineEntry | eSymbolContextSymbol); |
109 | |
110 | if (sc.line_entry.line == 0) { |
111 | AddressRange range = sc.line_entry.range; |
112 | |
113 | // If the whole function is marked line 0 just step out, that's easier & |
114 | // faster than continuing to step through it. |
115 | bool just_step_out = false; |
116 | if (sc.symbol && sc.symbol->ValueIsAddress()) { |
117 | Address symbol_end = sc.symbol->GetAddress(); |
118 | symbol_end.Slide(offset: sc.symbol->GetByteSize() - 1); |
119 | if (range.ContainsFileAddress(so_addr: sc.symbol->GetAddress()) && |
120 | range.ContainsFileAddress(so_addr: symbol_end)) { |
121 | LLDB_LOGF(log, "Stopped in a function with only line 0 lines, just " |
122 | "stepping out." ); |
123 | just_step_out = true; |
124 | } |
125 | } |
126 | if (!just_step_out) { |
127 | LLDB_LOGF(log, "ThreadPlanShouldStopHere::DefaultStepFromHereCallback " |
128 | "Queueing StepInRange plan to step through line 0 code." ); |
129 | |
130 | return_plan_sp = current_plan->GetThread().QueueThreadPlanForStepInRange( |
131 | abort_other_plans: false, range, addr_context: sc, step_in_target: nullptr, stop_other_threads: eOnlyDuringStepping, status, |
132 | step_in_avoids_code_without_debug_info: eLazyBoolCalculate, step_out_avoids_code_without_debug_info: eLazyBoolNo); |
133 | } |
134 | } |
135 | |
136 | if (!return_plan_sp) |
137 | return_plan_sp = |
138 | current_plan->GetThread().QueueThreadPlanForStepOutNoShouldStop( |
139 | abort_other_plans: false, addr_context: nullptr, first_insn: true, stop_other_threads: stop_others, report_stop_vote: eVoteNo, report_run_vote: eVoteNoOpinion, |
140 | frame_idx: frame_index, status, continue_to_next_branch: true); |
141 | return return_plan_sp; |
142 | } |
143 | |
144 | ThreadPlanSP ThreadPlanShouldStopHere::QueueStepOutFromHerePlan( |
145 | lldb_private::Flags &flags, lldb::FrameComparison operation, |
146 | Status &status) { |
147 | ThreadPlanSP return_plan_sp; |
148 | if (m_callbacks.step_from_here_callback) { |
149 | return_plan_sp = m_callbacks.step_from_here_callback( |
150 | m_owner, flags, operation, status, m_baton); |
151 | } |
152 | return return_plan_sp; |
153 | } |
154 | |
155 | lldb::ThreadPlanSP ThreadPlanShouldStopHere::CheckShouldStopHereAndQueueStepOut( |
156 | lldb::FrameComparison operation, Status &status) { |
157 | if (!InvokeShouldStopHereCallback(operation, status)) |
158 | return QueueStepOutFromHerePlan(flags&: m_flags, operation, status); |
159 | else |
160 | return ThreadPlanSP(); |
161 | } |
162 | |