1254721Semaste//===-- ThreadPlanStepInstruction.cpp ---------------------------*- C++ -*-===//
2254721Semaste//
3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353358Sdim// See https://llvm.org/LICENSE.txt for license information.
5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6254721Semaste//
7254721Semaste//===----------------------------------------------------------------------===//
8254721Semaste
9296417Sdim#include "lldb/Target/ThreadPlanStepInstruction.h"
10254721Semaste#include "lldb/Target/Process.h"
11254721Semaste#include "lldb/Target/RegisterContext.h"
12254721Semaste#include "lldb/Target/RegisterContext.h"
13254721Semaste#include "lldb/Target/StopInfo.h"
14254721Semaste#include "lldb/Target/Target.h"
15321369Sdim#include "lldb/Utility/Log.h"
16321369Sdim#include "lldb/Utility/Stream.h"
17254721Semaste
18254721Semasteusing namespace lldb;
19254721Semasteusing namespace lldb_private;
20254721Semaste
21254721Semaste// ThreadPlanStepInstruction: Step over the current instruction
22254721Semaste
23314564SdimThreadPlanStepInstruction::ThreadPlanStepInstruction(Thread &thread,
24314564Sdim                                                     bool step_over,
25314564Sdim                                                     bool stop_other_threads,
26314564Sdim                                                     Vote stop_vote,
27314564Sdim                                                     Vote run_vote)
28314564Sdim    : ThreadPlan(ThreadPlan::eKindStepInstruction,
29314564Sdim                 "Step over single instruction", thread, stop_vote, run_vote),
30314564Sdim      m_instruction_addr(0), m_stop_other_threads(stop_other_threads),
31314564Sdim      m_step_over(step_over) {
32314564Sdim  m_takes_iteration_count = true;
33314564Sdim  SetUpState();
34276479Sdim}
35276479Sdim
36296417SdimThreadPlanStepInstruction::~ThreadPlanStepInstruction() = default;
37276479Sdim
38314564Sdimvoid ThreadPlanStepInstruction::SetUpState() {
39314564Sdim  m_instruction_addr = m_thread.GetRegisterContext()->GetPC(0);
40314564Sdim  StackFrameSP start_frame_sp(m_thread.GetStackFrameAtIndex(0));
41314564Sdim  m_stack_id = start_frame_sp->GetStackID();
42314564Sdim
43314564Sdim  m_start_has_symbol =
44314564Sdim      start_frame_sp->GetSymbolContext(eSymbolContextSymbol).symbol != nullptr;
45314564Sdim
46314564Sdim  StackFrameSP parent_frame_sp = m_thread.GetStackFrameAtIndex(1);
47314564Sdim  if (parent_frame_sp)
48314564Sdim    m_parent_frame_id = parent_frame_sp->GetStackID();
49254721Semaste}
50254721Semaste
51314564Sdimvoid ThreadPlanStepInstruction::GetDescription(Stream *s,
52314564Sdim                                               lldb::DescriptionLevel level) {
53344779Sdim  auto PrintFailureIfAny = [&]() {
54344779Sdim    if (m_status.Success())
55344779Sdim      return;
56344779Sdim    s->Printf(" failed (%s)", m_status.AsCString());
57344779Sdim  };
58344779Sdim
59314564Sdim  if (level == lldb::eDescriptionLevelBrief) {
60314564Sdim    if (m_step_over)
61314564Sdim      s->Printf("instruction step over");
62254721Semaste    else
63314564Sdim      s->Printf("instruction step into");
64344779Sdim
65344779Sdim    PrintFailureIfAny();
66314564Sdim  } else {
67314564Sdim    s->Printf("Stepping one instruction past ");
68360784Sdim    DumpAddress(s->AsRawOstream(), m_instruction_addr, sizeof(addr_t));
69314564Sdim    if (!m_start_has_symbol)
70314564Sdim      s->Printf(" which has no symbol");
71314564Sdim
72314564Sdim    if (m_step_over)
73314564Sdim      s->Printf(" stepping over calls");
74314564Sdim    else
75314564Sdim      s->Printf(" stepping into calls");
76344779Sdim
77344779Sdim    PrintFailureIfAny();
78314564Sdim  }
79254721Semaste}
80254721Semaste
81314564Sdimbool ThreadPlanStepInstruction::ValidatePlan(Stream *error) {
82341825Sdim  // Since we read the instruction we're stepping over from the thread, this
83341825Sdim  // plan will always work.
84314564Sdim  return true;
85254721Semaste}
86254721Semaste
87314564Sdimbool ThreadPlanStepInstruction::DoPlanExplainsStop(Event *event_ptr) {
88314564Sdim  StopInfoSP stop_info_sp = GetPrivateStopInfo();
89314564Sdim  if (stop_info_sp) {
90314564Sdim    StopReason reason = stop_info_sp->GetStopReason();
91314564Sdim    return (reason == eStopReasonTrace || reason == eStopReasonNone);
92314564Sdim  }
93314564Sdim  return false;
94254721Semaste}
95254721Semaste
96314564Sdimbool ThreadPlanStepInstruction::IsPlanStale() {
97314564Sdim  Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
98314564Sdim  StackID cur_frame_id = m_thread.GetStackFrameAtIndex(0)->GetStackID();
99314564Sdim  if (cur_frame_id == m_stack_id) {
100321369Sdim    // Set plan Complete when we reach next instruction
101321369Sdim    uint64_t pc = m_thread.GetRegisterContext()->GetPC(0);
102321369Sdim    uint32_t max_opcode_size = m_thread.CalculateTarget()
103321369Sdim        ->GetArchitecture().GetMaximumOpcodeByteSize();
104321369Sdim    bool next_instruction_reached = (pc > m_instruction_addr) &&
105321369Sdim        (pc <= m_instruction_addr + max_opcode_size);
106321369Sdim    if (next_instruction_reached) {
107321369Sdim      SetPlanComplete();
108321369Sdim    }
109314564Sdim    return (m_thread.GetRegisterContext()->GetPC(0) != m_instruction_addr);
110314564Sdim  } else if (cur_frame_id < m_stack_id) {
111314564Sdim    // If the current frame is younger than the start frame and we are stepping
112341825Sdim    // over, then we need to continue, but if we are doing just one step, we're
113341825Sdim    // done.
114314564Sdim    return !m_step_over;
115314564Sdim  } else {
116314564Sdim    if (log) {
117360784Sdim      LLDB_LOGF(log,
118360784Sdim                "ThreadPlanStepInstruction::IsPlanStale - Current frame is "
119360784Sdim                "older than start frame, plan is stale.");
120276479Sdim    }
121314564Sdim    return true;
122314564Sdim  }
123276479Sdim}
124276479Sdim
125314564Sdimbool ThreadPlanStepInstruction::ShouldStop(Event *event_ptr) {
126314564Sdim  if (m_step_over) {
127314564Sdim    Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
128314564Sdim
129314564Sdim    StackFrameSP cur_frame_sp = m_thread.GetStackFrameAtIndex(0);
130314564Sdim    if (!cur_frame_sp) {
131360784Sdim      LLDB_LOGF(
132360784Sdim          log,
133360784Sdim          "ThreadPlanStepInstruction couldn't get the 0th frame, stopping.");
134314564Sdim      SetPlanComplete();
135314564Sdim      return true;
136314564Sdim    }
137314564Sdim
138314564Sdim    StackID cur_frame_zero_id = cur_frame_sp->GetStackID();
139314564Sdim
140314564Sdim    if (cur_frame_zero_id == m_stack_id || m_stack_id < cur_frame_zero_id) {
141314564Sdim      if (m_thread.GetRegisterContext()->GetPC(0) != m_instruction_addr) {
142314564Sdim        if (--m_iteration_count <= 0) {
143314564Sdim          SetPlanComplete();
144314564Sdim          return true;
145314564Sdim        } else {
146314564Sdim          // We are still stepping, reset the start pc, and in case we've
147341825Sdim          // stepped out, reset the current stack id.
148314564Sdim          SetUpState();
149314564Sdim          return false;
150280031Sdim        }
151314564Sdim      } else
152314564Sdim        return false;
153314564Sdim    } else {
154314564Sdim      // We've stepped in, step back out again:
155314564Sdim      StackFrame *return_frame = m_thread.GetStackFrameAtIndex(1).get();
156314564Sdim      if (return_frame) {
157314564Sdim        if (return_frame->GetStackID() != m_parent_frame_id ||
158314564Sdim            m_start_has_symbol) {
159314564Sdim          // next-instruction shouldn't step out of inlined functions.  But we
160341825Sdim          // may have stepped into a real function that starts with an inlined
161341825Sdim          // function, and we do want to step out of that...
162280031Sdim
163314564Sdim          if (cur_frame_sp->IsInlined()) {
164314564Sdim            StackFrameSP parent_frame_sp =
165314564Sdim                m_thread.GetFrameWithStackID(m_stack_id);
166314564Sdim
167314564Sdim            if (parent_frame_sp &&
168314564Sdim                parent_frame_sp->GetConcreteFrameIndex() ==
169314564Sdim                    cur_frame_sp->GetConcreteFrameIndex()) {
170314564Sdim              SetPlanComplete();
171314564Sdim              if (log) {
172360784Sdim                LLDB_LOGF(log,
173360784Sdim                          "Frame we stepped into is inlined into the frame "
174360784Sdim                          "we were stepping from, stopping.");
175314564Sdim              }
176314564Sdim              return true;
177254721Semaste            }
178314564Sdim          }
179280031Sdim
180314564Sdim          if (log) {
181314564Sdim            StreamString s;
182314564Sdim            s.PutCString("Stepped in to: ");
183314564Sdim            addr_t stop_addr =
184314564Sdim                m_thread.GetStackFrameAtIndex(0)->GetRegisterContext()->GetPC();
185360784Sdim            DumpAddress(s.AsRawOstream(), stop_addr,
186360784Sdim                        m_thread.CalculateTarget()
187360784Sdim                            ->GetArchitecture()
188360784Sdim                            .GetAddressByteSize());
189314564Sdim            s.PutCString(" stepping out to: ");
190314564Sdim            addr_t return_addr = return_frame->GetRegisterContext()->GetPC();
191360784Sdim            DumpAddress(s.AsRawOstream(), return_addr,
192360784Sdim                        m_thread.CalculateTarget()
193360784Sdim                            ->GetArchitecture()
194360784Sdim                            .GetAddressByteSize());
195360784Sdim            LLDB_LOGF(log, "%s.", s.GetData());
196314564Sdim          }
197280031Sdim
198341825Sdim          // StepInstruction should probably have the tri-state RunMode, but
199341825Sdim          // for now it is safer to run others.
200314564Sdim          const bool stop_others = false;
201314564Sdim          m_thread.QueueThreadPlanForStepOutNoShouldStop(
202344779Sdim              false, nullptr, true, stop_others, eVoteNo, eVoteNoOpinion, 0,
203344779Sdim              m_status);
204314564Sdim          return false;
205314564Sdim        } else {
206314564Sdim          if (log) {
207314564Sdim            log->PutCString(
208314564Sdim                "The stack id we are stepping in changed, but our parent frame "
209314564Sdim                "did not when stepping from code with no symbols.  "
210314564Sdim                "We are probably just confused about where we are, stopping.");
211314564Sdim          }
212314564Sdim          SetPlanComplete();
213314564Sdim          return true;
214254721Semaste        }
215314564Sdim      } else {
216360784Sdim        LLDB_LOGF(log, "Could not find previous frame, stopping.");
217314564Sdim        SetPlanComplete();
218314564Sdim        return true;
219314564Sdim      }
220254721Semaste    }
221314564Sdim  } else {
222314564Sdim    lldb::addr_t pc_addr = m_thread.GetRegisterContext()->GetPC(0);
223314564Sdim    if (pc_addr != m_instruction_addr) {
224314564Sdim      if (--m_iteration_count <= 0) {
225314564Sdim        SetPlanComplete();
226314564Sdim        return true;
227314564Sdim      } else {
228314564Sdim        // We are still stepping, reset the start pc, and in case we've stepped
229341825Sdim        // in or out, reset the current stack id.
230314564Sdim        SetUpState();
231314564Sdim        return false;
232314564Sdim      }
233314564Sdim    } else
234314564Sdim      return false;
235314564Sdim  }
236254721Semaste}
237254721Semaste
238314564Sdimbool ThreadPlanStepInstruction::StopOthers() { return m_stop_other_threads; }
239254721Semaste
240314564SdimStateType ThreadPlanStepInstruction::GetPlanRunState() {
241314564Sdim  return eStateStepping;
242254721Semaste}
243254721Semaste
244314564Sdimbool ThreadPlanStepInstruction::WillStop() { return true; }
245314564Sdim
246314564Sdimbool ThreadPlanStepInstruction::MischiefManaged() {
247314564Sdim  if (IsPlanComplete()) {
248314564Sdim    Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
249360784Sdim    LLDB_LOGF(log, "Completed single instruction step plan.");
250314564Sdim    ThreadPlan::MischiefManaged();
251254721Semaste    return true;
252314564Sdim  } else {
253314564Sdim    return false;
254314564Sdim  }
255254721Semaste}
256