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
ThreadPlanShouldStopHere(ThreadPlan * owner)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
ThreadPlanShouldStopHere(ThreadPlan * owner,const ThreadPlanShouldStopHereCallbacks * callbacks,void * baton)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
InvokeShouldStopHereCallback(FrameComparison operation,Status & status)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
DefaultShouldStopHereCallback(ThreadPlan * current_plan,Flags & flags,FrameComparison operation,Status & status,void * baton)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
DefaultStepFromHereCallback(ThreadPlan * current_plan,Flags & flags,FrameComparison operation,Status & status,void * baton)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
QueueStepOutFromHerePlan(lldb_private::Flags & flags,lldb::FrameComparison operation,Status & status)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
CheckShouldStopHereAndQueueStepOut(lldb::FrameComparison operation,Status & status)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