1//===-- ThreadPlanShouldStopHere.cpp --------------------------------------===// 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/ThreadPlanShouldStopHere.h" 10#include "lldb/Symbol/Symbol.h" 11#include "lldb/Target/RegisterContext.h" 12#include "lldb/Target/Thread.h" 13#include "lldb/Utility/LLDBLog.h" 14#include "lldb/Utility/Log.h" 15 16using namespace lldb; 17using namespace lldb_private; 18 19// ThreadPlanShouldStopHere constructor 20ThreadPlanShouldStopHere::ThreadPlanShouldStopHere(ThreadPlan *owner) 21 : m_callbacks(), m_baton(nullptr), m_owner(owner), 22 m_flags(ThreadPlanShouldStopHere::eNone) { 23 m_callbacks.should_stop_here_callback = 24 ThreadPlanShouldStopHere::DefaultShouldStopHereCallback; 25 m_callbacks.step_from_here_callback = 26 ThreadPlanShouldStopHere::DefaultStepFromHereCallback; 27} 28 29ThreadPlanShouldStopHere::ThreadPlanShouldStopHere( 30 ThreadPlan *owner, const ThreadPlanShouldStopHereCallbacks *callbacks, 31 void *baton) 32 : m_callbacks(), m_baton(), m_owner(owner), 33 m_flags(ThreadPlanShouldStopHere::eNone) { 34 SetShouldStopHereCallbacks(callbacks, baton); 35} 36 37ThreadPlanShouldStopHere::~ThreadPlanShouldStopHere() = default; 38 39bool ThreadPlanShouldStopHere::InvokeShouldStopHereCallback( 40 FrameComparison operation, Status &status) { 41 bool should_stop_here = true; 42 if (m_callbacks.should_stop_here_callback) { 43 should_stop_here = m_callbacks.should_stop_here_callback( 44 m_owner, m_flags, operation, status, m_baton); 45 Log *log = GetLog(LLDBLog::Step); 46 if (log) { 47 lldb::addr_t current_addr = 48 m_owner->GetThread().GetRegisterContext()->GetPC(0); 49 50 LLDB_LOGF(log, "ShouldStopHere callback returned %u from 0x%" PRIx64 ".", 51 should_stop_here, current_addr); 52 } 53 } 54 55 return should_stop_here; 56} 57 58bool ThreadPlanShouldStopHere::DefaultShouldStopHereCallback( 59 ThreadPlan *current_plan, Flags &flags, FrameComparison operation, 60 Status &status, void *baton) { 61 bool should_stop_here = true; 62 StackFrame *frame = current_plan->GetThread().GetStackFrameAtIndex(0).get(); 63 if (!frame) 64 return true; 65 66 Log *log = GetLog(LLDBLog::Step); 67 68 if ((operation == eFrameCompareOlder && flags.Test(eStepOutAvoidNoDebug)) || 69 (operation == eFrameCompareYounger && flags.Test(eStepInAvoidNoDebug)) || 70 (operation == eFrameCompareSameParent && 71 flags.Test(eStepInAvoidNoDebug))) { 72 if (!frame->HasDebugInformation()) { 73 LLDB_LOGF(log, "Stepping out of frame with no debug info"); 74 75 should_stop_here = false; 76 } 77 } 78 79 // Always avoid code with line number 0. 80 // FIXME: At present the ShouldStop and the StepFromHere calculate this 81 // independently. If this ever 82 // becomes expensive (this one isn't) we can try to have this set a state 83 // that the StepFromHere can use. 84 if (frame) { 85 SymbolContext sc; 86 sc = frame->GetSymbolContext(eSymbolContextLineEntry); 87 if (sc.line_entry.line == 0) 88 should_stop_here = false; 89 } 90 91 return should_stop_here; 92} 93 94ThreadPlanSP ThreadPlanShouldStopHere::DefaultStepFromHereCallback( 95 ThreadPlan *current_plan, Flags &flags, FrameComparison operation, 96 Status &status, void *baton) { 97 const bool stop_others = false; 98 const size_t frame_index = 0; 99 ThreadPlanSP return_plan_sp; 100 // If we are stepping through code at line number 0, then we need to step 101 // over this range. Otherwise we will step out. 102 Log *log = GetLog(LLDBLog::Step); 103 104 StackFrame *frame = current_plan->GetThread().GetStackFrameAtIndex(0).get(); 105 if (!frame) 106 return return_plan_sp; 107 SymbolContext sc; 108 sc = frame->GetSymbolContext(eSymbolContextLineEntry | eSymbolContextSymbol); 109 110 if (sc.line_entry.line == 0) { 111 AddressRange range = sc.line_entry.range; 112 113 // If the whole function is marked line 0 just step out, that's easier & 114 // faster than continuing to step through it. 115 bool just_step_out = false; 116 if (sc.symbol && sc.symbol->ValueIsAddress()) { 117 Address symbol_end = sc.symbol->GetAddress(); 118 symbol_end.Slide(sc.symbol->GetByteSize() - 1); 119 if (range.ContainsFileAddress(sc.symbol->GetAddress()) && 120 range.ContainsFileAddress(symbol_end)) { 121 LLDB_LOGF(log, "Stopped in a function with only line 0 lines, just " 122 "stepping out."); 123 just_step_out = true; 124 } 125 } 126 if (!just_step_out) { 127 LLDB_LOGF(log, "ThreadPlanShouldStopHere::DefaultStepFromHereCallback " 128 "Queueing StepInRange plan to step through line 0 code."); 129 130 return_plan_sp = current_plan->GetThread().QueueThreadPlanForStepInRange( 131 false, range, sc, nullptr, eOnlyDuringStepping, status, 132 eLazyBoolCalculate, eLazyBoolNo); 133 } 134 } 135 136 if (!return_plan_sp) 137 return_plan_sp = 138 current_plan->GetThread().QueueThreadPlanForStepOutNoShouldStop( 139 false, nullptr, true, stop_others, eVoteNo, eVoteNoOpinion, 140 frame_index, status, true); 141 return return_plan_sp; 142} 143 144ThreadPlanSP ThreadPlanShouldStopHere::QueueStepOutFromHerePlan( 145 lldb_private::Flags &flags, lldb::FrameComparison operation, 146 Status &status) { 147 ThreadPlanSP return_plan_sp; 148 if (m_callbacks.step_from_here_callback) { 149 return_plan_sp = m_callbacks.step_from_here_callback( 150 m_owner, flags, operation, status, m_baton); 151 } 152 return return_plan_sp; 153} 154 155lldb::ThreadPlanSP ThreadPlanShouldStopHere::CheckShouldStopHereAndQueueStepOut( 156 lldb::FrameComparison operation, Status &status) { 157 if (!InvokeShouldStopHereCallback(operation, status)) 158 return QueueStepOutFromHerePlan(m_flags, operation, status); 159 else 160 return ThreadPlanSP(); 161} 162