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