1*0b57cec5SDimitry Andric //===-- ThreadPlanStepOverBreakpoint.cpp ----------------------------------===//
2*0b57cec5SDimitry Andric //
3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*0b57cec5SDimitry Andric //
7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
8*0b57cec5SDimitry Andric 
9*0b57cec5SDimitry Andric #include "lldb/Target/ThreadPlanStepOverBreakpoint.h"
10*0b57cec5SDimitry Andric 
11*0b57cec5SDimitry Andric #include "lldb/Target/Process.h"
12*0b57cec5SDimitry Andric #include "lldb/Target/RegisterContext.h"
13*0b57cec5SDimitry Andric #include "lldb/Utility/LLDBLog.h"
14*0b57cec5SDimitry Andric #include "lldb/Utility/Log.h"
15*0b57cec5SDimitry Andric #include "lldb/Utility/Stream.h"
16*0b57cec5SDimitry Andric 
17*0b57cec5SDimitry Andric using namespace lldb;
18*0b57cec5SDimitry Andric using namespace lldb_private;
19*0b57cec5SDimitry Andric 
20*0b57cec5SDimitry Andric // ThreadPlanStepOverBreakpoint: Single steps over a breakpoint bp_site_sp at
21*0b57cec5SDimitry Andric // the pc.
22*0b57cec5SDimitry Andric 
ThreadPlanStepOverBreakpoint(Thread & thread)23*0b57cec5SDimitry Andric ThreadPlanStepOverBreakpoint::ThreadPlanStepOverBreakpoint(Thread &thread)
24*0b57cec5SDimitry Andric     : ThreadPlan(
25*0b57cec5SDimitry Andric           ThreadPlan::eKindStepOverBreakpoint, "Step over breakpoint trap",
26*0b57cec5SDimitry Andric           thread, eVoteNo,
27*0b57cec5SDimitry Andric           eVoteNoOpinion), // We need to report the run since this happens
28*0b57cec5SDimitry Andric                            // first in the thread plan stack when stepping over
29*0b57cec5SDimitry Andric                            // a breakpoint
30*0b57cec5SDimitry Andric       m_breakpoint_addr(LLDB_INVALID_ADDRESS),
31*0b57cec5SDimitry Andric       m_auto_continue(false), m_reenabled_breakpoint_site(false)
32*0b57cec5SDimitry Andric 
33*0b57cec5SDimitry Andric {
34*0b57cec5SDimitry Andric   m_breakpoint_addr = thread.GetRegisterContext()->GetPC();
35*0b57cec5SDimitry Andric   m_breakpoint_site_id =
36*0b57cec5SDimitry Andric       thread.GetProcess()->GetBreakpointSiteList().FindIDByAddress(
37*0b57cec5SDimitry Andric           m_breakpoint_addr);
38*0b57cec5SDimitry Andric }
39*0b57cec5SDimitry Andric 
40*0b57cec5SDimitry Andric ThreadPlanStepOverBreakpoint::~ThreadPlanStepOverBreakpoint() = default;
41*0b57cec5SDimitry Andric 
GetDescription(Stream * s,lldb::DescriptionLevel level)42*0b57cec5SDimitry Andric void ThreadPlanStepOverBreakpoint::GetDescription(
43*0b57cec5SDimitry Andric     Stream *s, lldb::DescriptionLevel level) {
44*0b57cec5SDimitry Andric   s->Printf("Single stepping past breakpoint site %" PRIu64 " at 0x%" PRIx64,
45*0b57cec5SDimitry Andric             m_breakpoint_site_id, (uint64_t)m_breakpoint_addr);
46*0b57cec5SDimitry Andric }
47*0b57cec5SDimitry Andric 
ValidatePlan(Stream * error)48*0b57cec5SDimitry Andric bool ThreadPlanStepOverBreakpoint::ValidatePlan(Stream *error) { return true; }
49*0b57cec5SDimitry Andric 
DoPlanExplainsStop(Event * event_ptr)50*0b57cec5SDimitry Andric bool ThreadPlanStepOverBreakpoint::DoPlanExplainsStop(Event *event_ptr) {
51*0b57cec5SDimitry Andric   StopInfoSP stop_info_sp = GetPrivateStopInfo();
52*0b57cec5SDimitry Andric   if (stop_info_sp) {
53*0b57cec5SDimitry Andric     StopReason reason = stop_info_sp->GetStopReason();
54*0b57cec5SDimitry Andric 
55*0b57cec5SDimitry Andric     Log *log = GetLog(LLDBLog::Step);
56*0b57cec5SDimitry Andric     LLDB_LOG(log, "Step over breakpoint stopped for reason: {0}.",
57*0b57cec5SDimitry Andric              Thread::StopReasonAsString(reason));
58*0b57cec5SDimitry Andric 
59*0b57cec5SDimitry Andric     switch (reason) {
60*0b57cec5SDimitry Andric       case eStopReasonTrace:
61*0b57cec5SDimitry Andric       case eStopReasonNone:
62*0b57cec5SDimitry Andric         return true;
63*0b57cec5SDimitry Andric       case eStopReasonBreakpoint:
64*0b57cec5SDimitry Andric       {
65*0b57cec5SDimitry Andric         // It's a little surprising that we stop here for a breakpoint hit.
66*0b57cec5SDimitry Andric         // However, when you single step ONTO a breakpoint we still want to call
67*0b57cec5SDimitry Andric         // that a breakpoint hit, and trigger the actions, etc.  Otherwise you
68*0b57cec5SDimitry Andric         // would see the PC at the breakpoint without having triggered the
69*0b57cec5SDimitry Andric         // actions, then you'd continue, the PC wouldn't change, and you'd see
70*0b57cec5SDimitry Andric         // the breakpoint hit, which would be odd. So the lower levels fake
71*0b57cec5SDimitry Andric         // "step onto breakpoint address" and return that as a breakpoint hit.
72*0b57cec5SDimitry Andric         // So our trace step COULD appear as a breakpoint hit if the next
73*0b57cec5SDimitry Andric         // instruction also contained a breakpoint.  We don't want to handle
74*0b57cec5SDimitry Andric         // that, since we really don't know what to do with breakpoint hits.
75*0b57cec5SDimitry Andric         // But make sure we don't set ourselves to auto-continue or we'll wrench
76*0b57cec5SDimitry Andric         // control away from the plans that can deal with this.
77*0b57cec5SDimitry Andric         // Be careful, however, as we may have "seen a breakpoint under the PC
78*0b57cec5SDimitry Andric         // because we stopped without changing the PC, in which case we do want
79*0b57cec5SDimitry Andric         // to re-claim this stop so we'll try again.
80*0b57cec5SDimitry Andric         lldb::addr_t pc_addr = GetThread().GetRegisterContext()->GetPC();
81*0b57cec5SDimitry Andric 
82*0b57cec5SDimitry Andric         if (pc_addr == m_breakpoint_addr) {
83*0b57cec5SDimitry Andric           LLDB_LOGF(log,
84*0b57cec5SDimitry Andric                     "Got breakpoint stop reason but pc: 0x%" PRIx64
85*0b57cec5SDimitry Andric                     "hasn't changed.",
86*0b57cec5SDimitry Andric                     pc_addr);
87*0b57cec5SDimitry Andric           return true;
88*0b57cec5SDimitry Andric         }
89*0b57cec5SDimitry Andric 
90*0b57cec5SDimitry Andric         SetAutoContinue(false);
91*0b57cec5SDimitry Andric         return false;
92*0b57cec5SDimitry Andric       }
93*0b57cec5SDimitry Andric       default:
94*0b57cec5SDimitry Andric         return false;
95*0b57cec5SDimitry Andric     }
96*0b57cec5SDimitry Andric   }
97*0b57cec5SDimitry Andric   return false;
98*0b57cec5SDimitry Andric }
99*0b57cec5SDimitry Andric 
ShouldStop(Event * event_ptr)100*0b57cec5SDimitry Andric bool ThreadPlanStepOverBreakpoint::ShouldStop(Event *event_ptr) {
101*0b57cec5SDimitry Andric   return !ShouldAutoContinue(event_ptr);
102*0b57cec5SDimitry Andric }
103*0b57cec5SDimitry Andric 
StopOthers()104*0b57cec5SDimitry Andric bool ThreadPlanStepOverBreakpoint::StopOthers() { return true; }
105*0b57cec5SDimitry Andric 
GetPlanRunState()106*0b57cec5SDimitry Andric StateType ThreadPlanStepOverBreakpoint::GetPlanRunState() {
107*0b57cec5SDimitry Andric   return eStateStepping;
108*0b57cec5SDimitry Andric }
109*0b57cec5SDimitry Andric 
DoWillResume(StateType resume_state,bool current_plan)110*0b57cec5SDimitry Andric bool ThreadPlanStepOverBreakpoint::DoWillResume(StateType resume_state,
111*0b57cec5SDimitry Andric                                                 bool current_plan) {
112*0b57cec5SDimitry Andric   if (current_plan) {
113*0b57cec5SDimitry Andric     BreakpointSiteSP bp_site_sp(
114*0b57cec5SDimitry Andric         m_process.GetBreakpointSiteList().FindByAddress(m_breakpoint_addr));
115*0b57cec5SDimitry Andric     if (bp_site_sp && bp_site_sp->IsEnabled()) {
116*0b57cec5SDimitry Andric       m_process.DisableBreakpointSite(bp_site_sp.get());
117*0b57cec5SDimitry Andric       m_reenabled_breakpoint_site = false;
118*0b57cec5SDimitry Andric     }
119*0b57cec5SDimitry Andric   }
120*0b57cec5SDimitry Andric   return true;
121*0b57cec5SDimitry Andric }
122*0b57cec5SDimitry Andric 
WillStop()123*0b57cec5SDimitry Andric bool ThreadPlanStepOverBreakpoint::WillStop() {
124*0b57cec5SDimitry Andric   ReenableBreakpointSite();
125*0b57cec5SDimitry Andric   return true;
126*0b57cec5SDimitry Andric }
127*0b57cec5SDimitry Andric 
DidPop()128*0b57cec5SDimitry Andric void ThreadPlanStepOverBreakpoint::DidPop() { ReenableBreakpointSite(); }
129*0b57cec5SDimitry Andric 
MischiefManaged()130*0b57cec5SDimitry Andric bool ThreadPlanStepOverBreakpoint::MischiefManaged() {
131*0b57cec5SDimitry Andric   lldb::addr_t pc_addr = GetThread().GetRegisterContext()->GetPC();
132*0b57cec5SDimitry Andric 
133*0b57cec5SDimitry Andric   if (pc_addr == m_breakpoint_addr) {
134*0b57cec5SDimitry Andric     // If we are still at the PC of our breakpoint, then for some reason we
135*0b57cec5SDimitry Andric     // didn't get a chance to run.
136*0b57cec5SDimitry Andric     return false;
137*0b57cec5SDimitry Andric   } else {
138*0b57cec5SDimitry Andric     Log *log = GetLog(LLDBLog::Step);
139*0b57cec5SDimitry Andric     LLDB_LOGF(log, "Completed step over breakpoint plan.");
140*0b57cec5SDimitry Andric     // Otherwise, re-enable the breakpoint we were stepping over, and we're
141*0b57cec5SDimitry Andric     // done.
142*0b57cec5SDimitry Andric     ReenableBreakpointSite();
143*0b57cec5SDimitry Andric     ThreadPlan::MischiefManaged();
144*0b57cec5SDimitry Andric     return true;
145*0b57cec5SDimitry Andric   }
146*0b57cec5SDimitry Andric }
147*0b57cec5SDimitry Andric 
ReenableBreakpointSite()148*0b57cec5SDimitry Andric void ThreadPlanStepOverBreakpoint::ReenableBreakpointSite() {
149*0b57cec5SDimitry Andric   if (!m_reenabled_breakpoint_site) {
150*0b57cec5SDimitry Andric     m_reenabled_breakpoint_site = true;
151*0b57cec5SDimitry Andric     BreakpointSiteSP bp_site_sp(
152*0b57cec5SDimitry Andric         m_process.GetBreakpointSiteList().FindByAddress(m_breakpoint_addr));
153*0b57cec5SDimitry Andric     if (bp_site_sp) {
154*0b57cec5SDimitry Andric       m_process.EnableBreakpointSite(bp_site_sp.get());
155*0b57cec5SDimitry Andric     }
156*0b57cec5SDimitry Andric   }
157*0b57cec5SDimitry Andric }
ThreadDestroyed()158*0b57cec5SDimitry Andric void ThreadPlanStepOverBreakpoint::ThreadDestroyed() {
159*0b57cec5SDimitry Andric   ReenableBreakpointSite();
160*0b57cec5SDimitry Andric }
161*0b57cec5SDimitry Andric 
SetAutoContinue(bool do_it)162*0b57cec5SDimitry Andric void ThreadPlanStepOverBreakpoint::SetAutoContinue(bool do_it) {
163*0b57cec5SDimitry Andric   m_auto_continue = do_it;
164*0b57cec5SDimitry Andric }
165*0b57cec5SDimitry Andric 
ShouldAutoContinue(Event * event_ptr)166*0b57cec5SDimitry Andric bool ThreadPlanStepOverBreakpoint::ShouldAutoContinue(Event *event_ptr) {
167*0b57cec5SDimitry Andric   return m_auto_continue;
168*0b57cec5SDimitry Andric }
169*0b57cec5SDimitry Andric 
IsPlanStale()170*0b57cec5SDimitry Andric bool ThreadPlanStepOverBreakpoint::IsPlanStale() {
171*0b57cec5SDimitry Andric   return GetThread().GetRegisterContext()->GetPC() != m_breakpoint_addr;
172*0b57cec5SDimitry Andric }
173*0b57cec5SDimitry Andric