1 //===-- ThreadPlanShouldStopHere.cpp --------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
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"
16 using namespace lldb_private
;
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
;
28 ThreadPlanShouldStopHere::ThreadPlanShouldStopHere(
29 ThreadPlan
*owner
, const ThreadPlanShouldStopHereCallbacks
*callbacks
,
31 : m_callbacks(), m_baton(), m_owner(owner
),
32 m_flags(ThreadPlanShouldStopHere::eNone
) {
33 SetShouldStopHereCallbacks(callbacks
, baton
);
36 ThreadPlanShouldStopHere::~ThreadPlanShouldStopHere() = default;
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
));
46 lldb::addr_t current_addr
=
47 m_owner
->GetThread().GetRegisterContext()->GetPC(0);
49 LLDB_LOGF(log
, "ShouldStopHere callback returned %u from 0x%" PRIx64
".",
50 should_stop_here
, current_addr
);
54 return should_stop_here
;
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();
65 Log
*log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP
));
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");
74 should_stop_here
= false;
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.
85 sc
= frame
->GetSymbolContext(eSymbolContextLineEntry
);
86 if (sc
.line_entry
.line
== 0)
87 should_stop_here
= false;
90 return should_stop_here
;
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
));
103 StackFrame
*frame
= current_plan
->GetThread().GetStackFrameAtIndex(0).get();
105 return return_plan_sp
;
107 sc
= frame
->GetSymbolContext(eSymbolContextLineEntry
| eSymbolContextSymbol
);
109 if (sc
.line_entry
.line
== 0) {
110 AddressRange range
= sc
.line_entry
.range
;
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 "
122 just_step_out
= true;
125 if (!just_step_out
) {
126 LLDB_LOGF(log
, "ThreadPlanShouldStopHere::DefaultStepFromHereCallback "
127 "Queueing StepInRange plan to step through line 0 code.");
129 return_plan_sp
= current_plan
->GetThread().QueueThreadPlanForStepInRange(
130 false, range
, sc
, nullptr, eOnlyDuringStepping
, status
,
131 eLazyBoolCalculate
, eLazyBoolNo
);
137 current_plan
->GetThread().QueueThreadPlanForStepOutNoShouldStop(
138 false, nullptr, true, stop_others
, eVoteNo
, eVoteNoOpinion
,
139 frame_index
, status
, true);
140 return return_plan_sp
;
143 ThreadPlanSP
ThreadPlanShouldStopHere::QueueStepOutFromHerePlan(
144 lldb_private::Flags
&flags
, lldb::FrameComparison operation
,
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
);
151 return return_plan_sp
;
154 lldb::ThreadPlanSP
ThreadPlanShouldStopHere::CheckShouldStopHereAndQueueStepOut(
155 lldb::FrameComparison operation
, Status
&status
) {
156 if (!InvokeShouldStopHereCallback(operation
, status
))
157 return QueueStepOutFromHerePlan(m_flags
, operation
, status
);
159 return ThreadPlanSP();