1 | //===-- ThreadPlanStepInstruction.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/ThreadPlanStepInstruction.h" |
10 | #include "lldb/Target/Process.h" |
11 | #include "lldb/Target/RegisterContext.h" |
12 | #include "lldb/Target/StopInfo.h" |
13 | #include "lldb/Target/Target.h" |
14 | #include "lldb/Utility/LLDBLog.h" |
15 | #include "lldb/Utility/Log.h" |
16 | #include "lldb/Utility/Stream.h" |
17 | |
18 | using namespace lldb; |
19 | using namespace lldb_private; |
20 | |
21 | // ThreadPlanStepInstruction: Step over the current instruction |
22 | |
23 | ThreadPlanStepInstruction::ThreadPlanStepInstruction(Thread &thread, |
24 | bool step_over, |
25 | bool stop_other_threads, |
26 | Vote report_stop_vote, |
27 | Vote report_run_vote) |
28 | : ThreadPlan(ThreadPlan::eKindStepInstruction, |
29 | "Step over single instruction" , thread, report_stop_vote, |
30 | report_run_vote), |
31 | m_instruction_addr(0), m_stop_other_threads(stop_other_threads), |
32 | m_step_over(step_over) { |
33 | m_takes_iteration_count = true; |
34 | SetUpState(); |
35 | } |
36 | |
37 | ThreadPlanStepInstruction::~ThreadPlanStepInstruction() = default; |
38 | |
39 | void ThreadPlanStepInstruction::SetUpState() { |
40 | Thread &thread = GetThread(); |
41 | m_instruction_addr = thread.GetRegisterContext()->GetPC(fail_value: 0); |
42 | StackFrameSP start_frame_sp(thread.GetStackFrameAtIndex(idx: 0)); |
43 | m_stack_id = start_frame_sp->GetStackID(); |
44 | |
45 | m_start_has_symbol = |
46 | start_frame_sp->GetSymbolContext(resolve_scope: eSymbolContextSymbol).symbol != nullptr; |
47 | |
48 | StackFrameSP parent_frame_sp = thread.GetStackFrameAtIndex(idx: 1); |
49 | if (parent_frame_sp) |
50 | m_parent_frame_id = parent_frame_sp->GetStackID(); |
51 | } |
52 | |
53 | void ThreadPlanStepInstruction::GetDescription(Stream *s, |
54 | lldb::DescriptionLevel level) { |
55 | auto PrintFailureIfAny = [&]() { |
56 | if (m_status.Success()) |
57 | return; |
58 | s->Printf(format: " failed (%s)" , m_status.AsCString()); |
59 | }; |
60 | |
61 | if (level == lldb::eDescriptionLevelBrief) { |
62 | if (m_step_over) |
63 | s->Printf(format: "instruction step over" ); |
64 | else |
65 | s->Printf(format: "instruction step into" ); |
66 | |
67 | PrintFailureIfAny(); |
68 | } else { |
69 | s->Printf(format: "Stepping one instruction past " ); |
70 | DumpAddress(s&: s->AsRawOstream(), addr: m_instruction_addr, addr_size: sizeof(addr_t)); |
71 | if (!m_start_has_symbol) |
72 | s->Printf(format: " which has no symbol" ); |
73 | |
74 | if (m_step_over) |
75 | s->Printf(format: " stepping over calls" ); |
76 | else |
77 | s->Printf(format: " stepping into calls" ); |
78 | |
79 | PrintFailureIfAny(); |
80 | } |
81 | } |
82 | |
83 | bool ThreadPlanStepInstruction::ValidatePlan(Stream *error) { |
84 | // Since we read the instruction we're stepping over from the thread, this |
85 | // plan will always work. |
86 | return true; |
87 | } |
88 | |
89 | bool ThreadPlanStepInstruction::DoPlanExplainsStop(Event *event_ptr) { |
90 | StopInfoSP stop_info_sp = GetPrivateStopInfo(); |
91 | if (stop_info_sp) { |
92 | StopReason reason = stop_info_sp->GetStopReason(); |
93 | return (reason == eStopReasonTrace || reason == eStopReasonNone); |
94 | } |
95 | return false; |
96 | } |
97 | |
98 | bool ThreadPlanStepInstruction::IsPlanStale() { |
99 | Log *log = GetLog(mask: LLDBLog::Step); |
100 | Thread &thread = GetThread(); |
101 | StackID cur_frame_id = thread.GetStackFrameAtIndex(idx: 0)->GetStackID(); |
102 | if (cur_frame_id == m_stack_id) { |
103 | // Set plan Complete when we reach next instruction |
104 | uint64_t pc = thread.GetRegisterContext()->GetPC(fail_value: 0); |
105 | uint32_t max_opcode_size = |
106 | GetTarget().GetArchitecture().GetMaximumOpcodeByteSize(); |
107 | bool next_instruction_reached = (pc > m_instruction_addr) && |
108 | (pc <= m_instruction_addr + max_opcode_size); |
109 | if (next_instruction_reached) { |
110 | SetPlanComplete(); |
111 | } |
112 | return (thread.GetRegisterContext()->GetPC(fail_value: 0) != m_instruction_addr); |
113 | } else if (cur_frame_id < m_stack_id) { |
114 | // If the current frame is younger than the start frame and we are stepping |
115 | // over, then we need to continue, but if we are doing just one step, we're |
116 | // done. |
117 | return !m_step_over; |
118 | } else { |
119 | if (log) { |
120 | LLDB_LOGF(log, |
121 | "ThreadPlanStepInstruction::IsPlanStale - Current frame is " |
122 | "older than start frame, plan is stale." ); |
123 | } |
124 | return true; |
125 | } |
126 | } |
127 | |
128 | bool ThreadPlanStepInstruction::ShouldStop(Event *event_ptr) { |
129 | Thread &thread = GetThread(); |
130 | if (m_step_over) { |
131 | Log *log = GetLog(mask: LLDBLog::Step); |
132 | StackFrameSP cur_frame_sp = thread.GetStackFrameAtIndex(idx: 0); |
133 | if (!cur_frame_sp) { |
134 | LLDB_LOGF( |
135 | log, |
136 | "ThreadPlanStepInstruction couldn't get the 0th frame, stopping." ); |
137 | SetPlanComplete(); |
138 | return true; |
139 | } |
140 | |
141 | StackID cur_frame_zero_id = cur_frame_sp->GetStackID(); |
142 | |
143 | if (cur_frame_zero_id == m_stack_id || m_stack_id < cur_frame_zero_id) { |
144 | if (thread.GetRegisterContext()->GetPC(fail_value: 0) != m_instruction_addr) { |
145 | if (--m_iteration_count <= 0) { |
146 | SetPlanComplete(); |
147 | return true; |
148 | } else { |
149 | // We are still stepping, reset the start pc, and in case we've |
150 | // stepped out, reset the current stack id. |
151 | SetUpState(); |
152 | return false; |
153 | } |
154 | } else |
155 | return false; |
156 | } else { |
157 | // We've stepped in, step back out again: |
158 | StackFrame *return_frame = thread.GetStackFrameAtIndex(idx: 1).get(); |
159 | if (return_frame) { |
160 | if (return_frame->GetStackID() != m_parent_frame_id || |
161 | m_start_has_symbol) { |
162 | // next-instruction shouldn't step out of inlined functions. But we |
163 | // may have stepped into a real function that starts with an inlined |
164 | // function, and we do want to step out of that... |
165 | |
166 | if (cur_frame_sp->IsInlined()) { |
167 | StackFrameSP parent_frame_sp = |
168 | thread.GetFrameWithStackID(stack_id: m_stack_id); |
169 | |
170 | if (parent_frame_sp && |
171 | parent_frame_sp->GetConcreteFrameIndex() == |
172 | cur_frame_sp->GetConcreteFrameIndex()) { |
173 | SetPlanComplete(); |
174 | if (log) { |
175 | LLDB_LOGF(log, |
176 | "Frame we stepped into is inlined into the frame " |
177 | "we were stepping from, stopping." ); |
178 | } |
179 | return true; |
180 | } |
181 | } |
182 | |
183 | if (log) { |
184 | StreamString s; |
185 | s.PutCString(cstr: "Stepped in to: " ); |
186 | addr_t stop_addr = |
187 | thread.GetStackFrameAtIndex(idx: 0)->GetRegisterContext()->GetPC(); |
188 | DumpAddress(s&: s.AsRawOstream(), addr: stop_addr, |
189 | addr_size: GetTarget().GetArchitecture().GetAddressByteSize()); |
190 | s.PutCString(cstr: " stepping out to: " ); |
191 | addr_t return_addr = return_frame->GetRegisterContext()->GetPC(); |
192 | DumpAddress(s&: s.AsRawOstream(), addr: return_addr, |
193 | addr_size: GetTarget().GetArchitecture().GetAddressByteSize()); |
194 | LLDB_LOGF(log, "%s." , s.GetData()); |
195 | } |
196 | |
197 | // StepInstruction should probably have the tri-state RunMode, but |
198 | // for now it is safer to run others. |
199 | const bool stop_others = false; |
200 | thread.QueueThreadPlanForStepOutNoShouldStop( |
201 | abort_other_plans: false, addr_context: nullptr, first_insn: true, stop_other_threads: stop_others, report_stop_vote: eVoteNo, report_run_vote: eVoteNoOpinion, frame_idx: 0, |
202 | status&: m_status); |
203 | return false; |
204 | } else { |
205 | if (log) { |
206 | log->PutCString( |
207 | cstr: "The stack id we are stepping in changed, but our parent frame " |
208 | "did not when stepping from code with no symbols. " |
209 | "We are probably just confused about where we are, stopping." ); |
210 | } |
211 | SetPlanComplete(); |
212 | return true; |
213 | } |
214 | } else { |
215 | LLDB_LOGF(log, "Could not find previous frame, stopping." ); |
216 | SetPlanComplete(); |
217 | return true; |
218 | } |
219 | } |
220 | } else { |
221 | lldb::addr_t pc_addr = thread.GetRegisterContext()->GetPC(fail_value: 0); |
222 | if (pc_addr != m_instruction_addr) { |
223 | if (--m_iteration_count <= 0) { |
224 | SetPlanComplete(); |
225 | return true; |
226 | } else { |
227 | // We are still stepping, reset the start pc, and in case we've stepped |
228 | // in or out, reset the current stack id. |
229 | SetUpState(); |
230 | return false; |
231 | } |
232 | } else |
233 | return false; |
234 | } |
235 | } |
236 | |
237 | bool ThreadPlanStepInstruction::StopOthers() { return m_stop_other_threads; } |
238 | |
239 | StateType ThreadPlanStepInstruction::GetPlanRunState() { |
240 | return eStateStepping; |
241 | } |
242 | |
243 | bool ThreadPlanStepInstruction::WillStop() { return true; } |
244 | |
245 | bool ThreadPlanStepInstruction::MischiefManaged() { |
246 | if (IsPlanComplete()) { |
247 | Log *log = GetLog(mask: LLDBLog::Step); |
248 | LLDB_LOGF(log, "Completed single instruction step plan." ); |
249 | ThreadPlan::MischiefManaged(); |
250 | return true; |
251 | } else { |
252 | return false; |
253 | } |
254 | } |
255 | |