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