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