15ffd83dbSDimitry Andric //===-- ThreadPlanStepOverBreakpoint.cpp ----------------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric 
90b57cec5SDimitry Andric #include "lldb/Target/ThreadPlanStepOverBreakpoint.h"
100b57cec5SDimitry Andric 
110b57cec5SDimitry Andric #include "lldb/Target/Process.h"
120b57cec5SDimitry Andric #include "lldb/Target/RegisterContext.h"
130b57cec5SDimitry Andric #include "lldb/Utility/Log.h"
140b57cec5SDimitry Andric #include "lldb/Utility/Stream.h"
150b57cec5SDimitry Andric 
160b57cec5SDimitry Andric using namespace lldb;
170b57cec5SDimitry Andric using namespace lldb_private;
180b57cec5SDimitry Andric 
190b57cec5SDimitry Andric // ThreadPlanStepOverBreakpoint: Single steps over a breakpoint bp_site_sp at
200b57cec5SDimitry Andric // the pc.
210b57cec5SDimitry Andric 
ThreadPlanStepOverBreakpoint(Thread & thread)220b57cec5SDimitry Andric ThreadPlanStepOverBreakpoint::ThreadPlanStepOverBreakpoint(Thread &thread)
230b57cec5SDimitry Andric     : ThreadPlan(
240b57cec5SDimitry Andric           ThreadPlan::eKindStepOverBreakpoint, "Step over breakpoint trap",
250b57cec5SDimitry Andric           thread, eVoteNo,
260b57cec5SDimitry Andric           eVoteNoOpinion), // We need to report the run since this happens
270b57cec5SDimitry Andric                            // first in the thread plan stack when stepping over
280b57cec5SDimitry Andric                            // a breakpoint
290b57cec5SDimitry Andric       m_breakpoint_addr(LLDB_INVALID_ADDRESS),
300b57cec5SDimitry Andric       m_auto_continue(false), m_reenabled_breakpoint_site(false)
310b57cec5SDimitry Andric 
320b57cec5SDimitry Andric {
335ffd83dbSDimitry Andric   m_breakpoint_addr = thread.GetRegisterContext()->GetPC();
340b57cec5SDimitry Andric   m_breakpoint_site_id =
355ffd83dbSDimitry Andric       thread.GetProcess()->GetBreakpointSiteList().FindIDByAddress(
360b57cec5SDimitry Andric           m_breakpoint_addr);
370b57cec5SDimitry Andric }
380b57cec5SDimitry Andric 
39*5f7ddb14SDimitry Andric ThreadPlanStepOverBreakpoint::~ThreadPlanStepOverBreakpoint() = default;
400b57cec5SDimitry Andric 
GetDescription(Stream * s,lldb::DescriptionLevel level)410b57cec5SDimitry Andric void ThreadPlanStepOverBreakpoint::GetDescription(
420b57cec5SDimitry Andric     Stream *s, lldb::DescriptionLevel level) {
430b57cec5SDimitry Andric   s->Printf("Single stepping past breakpoint site %" PRIu64 " at 0x%" PRIx64,
440b57cec5SDimitry Andric             m_breakpoint_site_id, (uint64_t)m_breakpoint_addr);
450b57cec5SDimitry Andric }
460b57cec5SDimitry Andric 
ValidatePlan(Stream * error)470b57cec5SDimitry Andric bool ThreadPlanStepOverBreakpoint::ValidatePlan(Stream *error) { return true; }
480b57cec5SDimitry Andric 
DoPlanExplainsStop(Event * event_ptr)490b57cec5SDimitry Andric bool ThreadPlanStepOverBreakpoint::DoPlanExplainsStop(Event *event_ptr) {
500b57cec5SDimitry Andric   StopInfoSP stop_info_sp = GetPrivateStopInfo();
510b57cec5SDimitry Andric   if (stop_info_sp) {
520b57cec5SDimitry Andric     StopReason reason = stop_info_sp->GetStopReason();
530b57cec5SDimitry Andric 
540b57cec5SDimitry Andric     Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
55af732203SDimitry Andric     LLDB_LOG(log, "Step over breakpoint stopped for reason: {0}.",
56af732203SDimitry Andric              Thread::StopReasonAsString(reason));
570b57cec5SDimitry Andric 
580b57cec5SDimitry Andric     switch (reason) {
590b57cec5SDimitry Andric       case eStopReasonTrace:
600b57cec5SDimitry Andric       case eStopReasonNone:
610b57cec5SDimitry Andric         return true;
620b57cec5SDimitry Andric       case eStopReasonBreakpoint:
630b57cec5SDimitry Andric       {
640b57cec5SDimitry Andric         // It's a little surprising that we stop here for a breakpoint hit.
650b57cec5SDimitry Andric         // However, when you single step ONTO a breakpoint we still want to call
660b57cec5SDimitry Andric         // that a breakpoint hit, and trigger the actions, etc.  Otherwise you
670b57cec5SDimitry Andric         // would see the PC at the breakpoint without having triggered the
680b57cec5SDimitry Andric         // actions, then you'd continue, the PC wouldn't change, and you'd see
690b57cec5SDimitry Andric         // the breakpoint hit, which would be odd. So the lower levels fake
700b57cec5SDimitry Andric         // "step onto breakpoint address" and return that as a breakpoint hit.
710b57cec5SDimitry Andric         // So our trace step COULD appear as a breakpoint hit if the next
720b57cec5SDimitry Andric         // instruction also contained a breakpoint.  We don't want to handle
730b57cec5SDimitry Andric         // that, since we really don't know what to do with breakpoint hits.
740b57cec5SDimitry Andric         // But make sure we don't set ourselves to auto-continue or we'll wrench
750b57cec5SDimitry Andric         // control away from the plans that can deal with this.
760b57cec5SDimitry Andric         // Be careful, however, as we may have "seen a breakpoint under the PC
770b57cec5SDimitry Andric         // because we stopped without changing the PC, in which case we do want
780b57cec5SDimitry Andric         // to re-claim this stop so we'll try again.
795ffd83dbSDimitry Andric         lldb::addr_t pc_addr = GetThread().GetRegisterContext()->GetPC();
800b57cec5SDimitry Andric 
810b57cec5SDimitry Andric         if (pc_addr == m_breakpoint_addr) {
829dba64beSDimitry Andric           LLDB_LOGF(log,
839dba64beSDimitry Andric                     "Got breakpoint stop reason but pc: 0x%" PRIx64
849dba64beSDimitry Andric                     "hasn't changed.",
859dba64beSDimitry Andric                     pc_addr);
860b57cec5SDimitry Andric           return true;
870b57cec5SDimitry Andric         }
880b57cec5SDimitry Andric 
890b57cec5SDimitry Andric         SetAutoContinue(false);
900b57cec5SDimitry Andric         return false;
910b57cec5SDimitry Andric       }
920b57cec5SDimitry Andric       default:
930b57cec5SDimitry Andric         return false;
940b57cec5SDimitry Andric     }
950b57cec5SDimitry Andric   }
960b57cec5SDimitry Andric   return false;
970b57cec5SDimitry Andric }
980b57cec5SDimitry Andric 
ShouldStop(Event * event_ptr)990b57cec5SDimitry Andric bool ThreadPlanStepOverBreakpoint::ShouldStop(Event *event_ptr) {
1000b57cec5SDimitry Andric   return !ShouldAutoContinue(event_ptr);
1010b57cec5SDimitry Andric }
1020b57cec5SDimitry Andric 
StopOthers()1030b57cec5SDimitry Andric bool ThreadPlanStepOverBreakpoint::StopOthers() { return true; }
1040b57cec5SDimitry Andric 
GetPlanRunState()1050b57cec5SDimitry Andric StateType ThreadPlanStepOverBreakpoint::GetPlanRunState() {
1060b57cec5SDimitry Andric   return eStateStepping;
1070b57cec5SDimitry Andric }
1080b57cec5SDimitry Andric 
DoWillResume(StateType resume_state,bool current_plan)1090b57cec5SDimitry Andric bool ThreadPlanStepOverBreakpoint::DoWillResume(StateType resume_state,
1100b57cec5SDimitry Andric                                                 bool current_plan) {
1110b57cec5SDimitry Andric   if (current_plan) {
1120b57cec5SDimitry Andric     BreakpointSiteSP bp_site_sp(
1135ffd83dbSDimitry Andric         m_process.GetBreakpointSiteList().FindByAddress(m_breakpoint_addr));
1140b57cec5SDimitry Andric     if (bp_site_sp && bp_site_sp->IsEnabled()) {
1155ffd83dbSDimitry Andric       m_process.DisableBreakpointSite(bp_site_sp.get());
1160b57cec5SDimitry Andric       m_reenabled_breakpoint_site = false;
1170b57cec5SDimitry Andric     }
1180b57cec5SDimitry Andric   }
1190b57cec5SDimitry Andric   return true;
1200b57cec5SDimitry Andric }
1210b57cec5SDimitry Andric 
WillStop()1220b57cec5SDimitry Andric bool ThreadPlanStepOverBreakpoint::WillStop() {
1230b57cec5SDimitry Andric   ReenableBreakpointSite();
1240b57cec5SDimitry Andric   return true;
1250b57cec5SDimitry Andric }
1260b57cec5SDimitry Andric 
WillPop()1270b57cec5SDimitry Andric void ThreadPlanStepOverBreakpoint::WillPop() {
1280b57cec5SDimitry Andric   ReenableBreakpointSite();
1290b57cec5SDimitry Andric }
1300b57cec5SDimitry Andric 
MischiefManaged()1310b57cec5SDimitry Andric bool ThreadPlanStepOverBreakpoint::MischiefManaged() {
1325ffd83dbSDimitry Andric   lldb::addr_t pc_addr = GetThread().GetRegisterContext()->GetPC();
1330b57cec5SDimitry Andric 
1340b57cec5SDimitry Andric   if (pc_addr == m_breakpoint_addr) {
1350b57cec5SDimitry Andric     // If we are still at the PC of our breakpoint, then for some reason we
1360b57cec5SDimitry Andric     // didn't get a chance to run.
1370b57cec5SDimitry Andric     return false;
1380b57cec5SDimitry Andric   } else {
1390b57cec5SDimitry Andric     Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
1409dba64beSDimitry Andric     LLDB_LOGF(log, "Completed step over breakpoint plan.");
1410b57cec5SDimitry Andric     // Otherwise, re-enable the breakpoint we were stepping over, and we're
1420b57cec5SDimitry Andric     // done.
1430b57cec5SDimitry Andric     ReenableBreakpointSite();
1440b57cec5SDimitry Andric     ThreadPlan::MischiefManaged();
1450b57cec5SDimitry Andric     return true;
1460b57cec5SDimitry Andric   }
1470b57cec5SDimitry Andric }
1480b57cec5SDimitry Andric 
ReenableBreakpointSite()1490b57cec5SDimitry Andric void ThreadPlanStepOverBreakpoint::ReenableBreakpointSite() {
1500b57cec5SDimitry Andric   if (!m_reenabled_breakpoint_site) {
1510b57cec5SDimitry Andric     m_reenabled_breakpoint_site = true;
1520b57cec5SDimitry Andric     BreakpointSiteSP bp_site_sp(
1535ffd83dbSDimitry Andric         m_process.GetBreakpointSiteList().FindByAddress(m_breakpoint_addr));
1540b57cec5SDimitry Andric     if (bp_site_sp) {
1555ffd83dbSDimitry Andric       m_process.EnableBreakpointSite(bp_site_sp.get());
1560b57cec5SDimitry Andric     }
1570b57cec5SDimitry Andric   }
1580b57cec5SDimitry Andric }
ThreadDestroyed()1590b57cec5SDimitry Andric void ThreadPlanStepOverBreakpoint::ThreadDestroyed() {
1600b57cec5SDimitry Andric   ReenableBreakpointSite();
1610b57cec5SDimitry Andric }
1620b57cec5SDimitry Andric 
SetAutoContinue(bool do_it)1630b57cec5SDimitry Andric void ThreadPlanStepOverBreakpoint::SetAutoContinue(bool do_it) {
1640b57cec5SDimitry Andric   m_auto_continue = do_it;
1650b57cec5SDimitry Andric }
1660b57cec5SDimitry Andric 
ShouldAutoContinue(Event * event_ptr)1670b57cec5SDimitry Andric bool ThreadPlanStepOverBreakpoint::ShouldAutoContinue(Event *event_ptr) {
1680b57cec5SDimitry Andric   return m_auto_continue;
1690b57cec5SDimitry Andric }
1700b57cec5SDimitry Andric 
IsPlanStale()1710b57cec5SDimitry Andric bool ThreadPlanStepOverBreakpoint::IsPlanStale() {
1725ffd83dbSDimitry Andric   return GetThread().GetRegisterContext()->GetPC() != m_breakpoint_addr;
1730b57cec5SDimitry Andric }
174