1//===-- ThreadPlanStepUntil.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/ThreadPlanStepUntil.h"
10
11#include "lldb/Breakpoint/Breakpoint.h"
12#include "lldb/Symbol/SymbolContextScope.h"
13#include "lldb/Target/Process.h"
14#include "lldb/Target/RegisterContext.h"
15#include "lldb/Target/StopInfo.h"
16#include "lldb/Target/Target.h"
17#include "lldb/Utility/Log.h"
18
19using namespace lldb;
20using namespace lldb_private;
21
22// ThreadPlanStepUntil: Run until we reach a given line number or step out of
23// the current frame
24
25ThreadPlanStepUntil::ThreadPlanStepUntil(Thread &thread,
26                                         lldb::addr_t *address_list,
27                                         size_t num_addresses, bool stop_others,
28                                         uint32_t frame_idx)
29    : ThreadPlan(ThreadPlan::eKindStepUntil, "Step until", thread,
30                 eVoteNoOpinion, eVoteNoOpinion),
31      m_step_from_insn(LLDB_INVALID_ADDRESS),
32      m_return_bp_id(LLDB_INVALID_BREAK_ID),
33      m_return_addr(LLDB_INVALID_ADDRESS), m_stepped_out(false),
34      m_should_stop(false), m_ran_analyze(false), m_explains_stop(false),
35      m_until_points(), m_stop_others(stop_others) {
36  // Stash away our "until" addresses:
37  TargetSP target_sp(m_thread.CalculateTarget());
38
39  StackFrameSP frame_sp(m_thread.GetStackFrameAtIndex(frame_idx));
40  if (frame_sp) {
41    m_step_from_insn = frame_sp->GetStackID().GetPC();
42    lldb::user_id_t thread_id = m_thread.GetID();
43
44    // Find the return address and set a breakpoint there:
45    // FIXME - can we do this more securely if we know first_insn?
46
47    StackFrameSP return_frame_sp(m_thread.GetStackFrameAtIndex(frame_idx + 1));
48    if (return_frame_sp) {
49      // TODO: add inline functionality
50      m_return_addr = return_frame_sp->GetStackID().GetPC();
51      Breakpoint *return_bp =
52          target_sp->CreateBreakpoint(m_return_addr, true, false).get();
53
54      if (return_bp != nullptr) {
55        if (return_bp->IsHardware() && !return_bp->HasResolvedLocations())
56          m_could_not_resolve_hw_bp = true;
57        return_bp->SetThreadID(thread_id);
58        m_return_bp_id = return_bp->GetID();
59        return_bp->SetBreakpointKind("until-return-backstop");
60      }
61    }
62
63    m_stack_id = frame_sp->GetStackID();
64
65    // Now set breakpoints on all our return addresses:
66    for (size_t i = 0; i < num_addresses; i++) {
67      Breakpoint *until_bp =
68          target_sp->CreateBreakpoint(address_list[i], true, false).get();
69      if (until_bp != nullptr) {
70        until_bp->SetThreadID(thread_id);
71        m_until_points[address_list[i]] = until_bp->GetID();
72        until_bp->SetBreakpointKind("until-target");
73      } else {
74        m_until_points[address_list[i]] = LLDB_INVALID_BREAK_ID;
75      }
76    }
77  }
78}
79
80ThreadPlanStepUntil::~ThreadPlanStepUntil() { Clear(); }
81
82void ThreadPlanStepUntil::Clear() {
83  TargetSP target_sp(m_thread.CalculateTarget());
84  if (target_sp) {
85    if (m_return_bp_id != LLDB_INVALID_BREAK_ID) {
86      target_sp->RemoveBreakpointByID(m_return_bp_id);
87      m_return_bp_id = LLDB_INVALID_BREAK_ID;
88    }
89
90    until_collection::iterator pos, end = m_until_points.end();
91    for (pos = m_until_points.begin(); pos != end; pos++) {
92      target_sp->RemoveBreakpointByID((*pos).second);
93    }
94  }
95  m_until_points.clear();
96  m_could_not_resolve_hw_bp = false;
97}
98
99void ThreadPlanStepUntil::GetDescription(Stream *s,
100                                         lldb::DescriptionLevel level) {
101  if (level == lldb::eDescriptionLevelBrief) {
102    s->Printf("step until");
103    if (m_stepped_out)
104      s->Printf(" - stepped out");
105  } else {
106    if (m_until_points.size() == 1)
107      s->Printf("Stepping from address 0x%" PRIx64 " until we reach 0x%" PRIx64
108                " using breakpoint %d",
109                (uint64_t)m_step_from_insn,
110                (uint64_t)(*m_until_points.begin()).first,
111                (*m_until_points.begin()).second);
112    else {
113      until_collection::iterator pos, end = m_until_points.end();
114      s->Printf("Stepping from address 0x%" PRIx64 " until we reach one of:",
115                (uint64_t)m_step_from_insn);
116      for (pos = m_until_points.begin(); pos != end; pos++) {
117        s->Printf("\n\t0x%" PRIx64 " (bp: %d)", (uint64_t)(*pos).first,
118                  (*pos).second);
119      }
120    }
121    s->Printf(" stepped out address is 0x%" PRIx64 ".",
122              (uint64_t)m_return_addr);
123  }
124}
125
126bool ThreadPlanStepUntil::ValidatePlan(Stream *error) {
127  if (m_could_not_resolve_hw_bp) {
128    if (error)
129      error->PutCString(
130          "Could not create hardware breakpoint for thread plan.");
131    return false;
132  } else if (m_return_bp_id == LLDB_INVALID_BREAK_ID) {
133    if (error)
134      error->PutCString("Could not create return breakpoint.");
135    return false;
136  } else {
137    until_collection::iterator pos, end = m_until_points.end();
138    for (pos = m_until_points.begin(); pos != end; pos++) {
139      if (!LLDB_BREAK_ID_IS_VALID((*pos).second))
140        return false;
141    }
142    return true;
143  }
144}
145
146void ThreadPlanStepUntil::AnalyzeStop() {
147  if (m_ran_analyze)
148    return;
149
150  StopInfoSP stop_info_sp = GetPrivateStopInfo();
151  m_should_stop = true;
152  m_explains_stop = false;
153
154  if (stop_info_sp) {
155    StopReason reason = stop_info_sp->GetStopReason();
156
157    if (reason == eStopReasonBreakpoint) {
158      // If this is OUR breakpoint, we're fine, otherwise we don't know why
159      // this happened...
160      BreakpointSiteSP this_site =
161          m_thread.GetProcess()->GetBreakpointSiteList().FindByID(
162              stop_info_sp->GetValue());
163      if (!this_site) {
164        m_explains_stop = false;
165        return;
166      }
167
168      if (this_site->IsBreakpointAtThisSite(m_return_bp_id)) {
169        // If we are at our "step out" breakpoint, and the stack depth has
170        // shrunk, then this is indeed our stop. If the stack depth has grown,
171        // then we've hit our step out breakpoint recursively. If we are the
172        // only breakpoint at that location, then we do explain the stop, and
173        // we'll just continue. If there was another breakpoint here, then we
174        // don't explain the stop, but we won't mark ourselves Completed,
175        // because maybe that breakpoint will continue, and then we'll finish
176        // the "until".
177        bool done;
178        StackID cur_frame_zero_id;
179
180        done = (m_stack_id < cur_frame_zero_id);
181
182        if (done) {
183          m_stepped_out = true;
184          SetPlanComplete();
185        } else
186          m_should_stop = false;
187
188        if (this_site->GetNumberOfOwners() == 1)
189          m_explains_stop = true;
190        else
191          m_explains_stop = false;
192        return;
193      } else {
194        // Check if we've hit one of our "until" breakpoints.
195        until_collection::iterator pos, end = m_until_points.end();
196        for (pos = m_until_points.begin(); pos != end; pos++) {
197          if (this_site->IsBreakpointAtThisSite((*pos).second)) {
198            // If we're at the right stack depth, then we're done.
199
200            bool done;
201            StackID frame_zero_id =
202                m_thread.GetStackFrameAtIndex(0)->GetStackID();
203
204            if (frame_zero_id == m_stack_id)
205              done = true;
206            else if (frame_zero_id < m_stack_id)
207              done = false;
208            else {
209              StackFrameSP older_frame_sp = m_thread.GetStackFrameAtIndex(1);
210
211              // But if we can't even unwind one frame we should just get out
212              // of here & stop...
213              if (older_frame_sp) {
214                const SymbolContext &older_context =
215                    older_frame_sp->GetSymbolContext(eSymbolContextEverything);
216                SymbolContext stack_context;
217                m_stack_id.GetSymbolContextScope()->CalculateSymbolContext(
218                    &stack_context);
219
220                done = (older_context == stack_context);
221              } else
222                done = false;
223            }
224
225            if (done)
226              SetPlanComplete();
227            else
228              m_should_stop = false;
229
230            // Otherwise we've hit this breakpoint recursively.  If we're the
231            // only breakpoint here, then we do explain the stop, and we'll
232            // continue. If not then we should let higher plans handle this
233            // stop.
234            if (this_site->GetNumberOfOwners() == 1)
235              m_explains_stop = true;
236            else {
237              m_should_stop = true;
238              m_explains_stop = false;
239            }
240            return;
241          }
242        }
243      }
244      // If we get here we haven't hit any of our breakpoints, so let the
245      // higher plans take care of the stop.
246      m_explains_stop = false;
247      return;
248    } else if (IsUsuallyUnexplainedStopReason(reason)) {
249      m_explains_stop = false;
250    } else {
251      m_explains_stop = true;
252    }
253  }
254}
255
256bool ThreadPlanStepUntil::DoPlanExplainsStop(Event *event_ptr) {
257  // We don't explain signals or breakpoints (breakpoints that handle stepping
258  // in or out will be handled by a child plan.
259  AnalyzeStop();
260  return m_explains_stop;
261}
262
263bool ThreadPlanStepUntil::ShouldStop(Event *event_ptr) {
264  // If we've told our self in ExplainsStop that we plan to continue, then do
265  // so here.  Otherwise, as long as this thread has stopped for a reason, we
266  // will stop.
267
268  StopInfoSP stop_info_sp = GetPrivateStopInfo();
269  if (!stop_info_sp || stop_info_sp->GetStopReason() == eStopReasonNone)
270    return false;
271
272  AnalyzeStop();
273  return m_should_stop;
274}
275
276bool ThreadPlanStepUntil::StopOthers() { return m_stop_others; }
277
278StateType ThreadPlanStepUntil::GetPlanRunState() { return eStateRunning; }
279
280bool ThreadPlanStepUntil::DoWillResume(StateType resume_state,
281                                       bool current_plan) {
282  if (current_plan) {
283    TargetSP target_sp(m_thread.CalculateTarget());
284    if (target_sp) {
285      Breakpoint *return_bp =
286          target_sp->GetBreakpointByID(m_return_bp_id).get();
287      if (return_bp != nullptr)
288        return_bp->SetEnabled(true);
289
290      until_collection::iterator pos, end = m_until_points.end();
291      for (pos = m_until_points.begin(); pos != end; pos++) {
292        Breakpoint *until_bp =
293            target_sp->GetBreakpointByID((*pos).second).get();
294        if (until_bp != nullptr)
295          until_bp->SetEnabled(true);
296      }
297    }
298  }
299
300  m_should_stop = true;
301  m_ran_analyze = false;
302  m_explains_stop = false;
303  return true;
304}
305
306bool ThreadPlanStepUntil::WillStop() {
307  TargetSP target_sp(m_thread.CalculateTarget());
308  if (target_sp) {
309    Breakpoint *return_bp = target_sp->GetBreakpointByID(m_return_bp_id).get();
310    if (return_bp != nullptr)
311      return_bp->SetEnabled(false);
312
313    until_collection::iterator pos, end = m_until_points.end();
314    for (pos = m_until_points.begin(); pos != end; pos++) {
315      Breakpoint *until_bp = target_sp->GetBreakpointByID((*pos).second).get();
316      if (until_bp != nullptr)
317        until_bp->SetEnabled(false);
318    }
319  }
320  return true;
321}
322
323bool ThreadPlanStepUntil::MischiefManaged() {
324  // I'm letting "PlanExplainsStop" do all the work, and just reporting that
325  // here.
326  bool done = false;
327  if (IsPlanComplete()) {
328    Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
329    LLDB_LOGF(log, "Completed step until plan.");
330
331    Clear();
332    done = true;
333  }
334  if (done)
335    ThreadPlan::MischiefManaged();
336
337  return done;
338}
339