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/Language.h"
12#include "lldb/Target/LanguageRuntime.h"
13#include "lldb/Target/RegisterContext.h"
14#include "lldb/Target/Thread.h"
15#include "lldb/Utility/LLDBLog.h"
16#include "lldb/Utility/Log.h"
17
18using namespace lldb;
19using namespace lldb_private;
20
21// ThreadPlanShouldStopHere constructor
22ThreadPlanShouldStopHere::ThreadPlanShouldStopHere(ThreadPlan *owner)
23 : m_callbacks(), m_baton(nullptr), m_owner(owner),
24 m_flags(ThreadPlanShouldStopHere::eNone) {
25 m_callbacks.should_stop_here_callback =
26 ThreadPlanShouldStopHere::DefaultShouldStopHereCallback;
27 m_callbacks.step_from_here_callback =
28 ThreadPlanShouldStopHere::DefaultStepFromHereCallback;
29}
30
31ThreadPlanShouldStopHere::ThreadPlanShouldStopHere(
32 ThreadPlan *owner, const ThreadPlanShouldStopHereCallbacks *callbacks,
33 void *baton)
34 : m_callbacks(), m_baton(), m_owner(owner),
35 m_flags(ThreadPlanShouldStopHere::eNone) {
36 SetShouldStopHereCallbacks(callbacks, baton);
37}
38
39ThreadPlanShouldStopHere::~ThreadPlanShouldStopHere() = default;
40
41bool ThreadPlanShouldStopHere::InvokeShouldStopHereCallback(
42 FrameComparison operation, Status &status) {
43 bool should_stop_here = true;
44 if (m_callbacks.should_stop_here_callback) {
45 should_stop_here = m_callbacks.should_stop_here_callback(
46 m_owner, m_flags, operation, status, m_baton);
47 Log *log = GetLog(mask: LLDBLog::Step);
48 if (log) {
49 lldb::addr_t current_addr =
50 m_owner->GetThread().GetRegisterContext()->GetPC(fail_value: 0);
51
52 LLDB_LOGF(log, "ShouldStopHere callback returned %u from 0x%" PRIx64 ".",
53 should_stop_here, current_addr);
54 }
55 }
56
57 return should_stop_here;
58}
59
60bool ThreadPlanShouldStopHere::DefaultShouldStopHereCallback(
61 ThreadPlan *current_plan, Flags &flags, FrameComparison operation,
62 Status &status, void *baton) {
63 bool should_stop_here = true;
64 StackFrame *frame = current_plan->GetThread().GetStackFrameAtIndex(idx: 0).get();
65 if (!frame)
66 return true;
67
68 Log *log = GetLog(mask: LLDBLog::Step);
69
70 if ((operation == eFrameCompareOlder && flags.Test(bit: eStepOutAvoidNoDebug)) ||
71 (operation == eFrameCompareYounger && flags.Test(bit: eStepInAvoidNoDebug)) ||
72 (operation == eFrameCompareSameParent &&
73 flags.Test(bit: eStepInAvoidNoDebug))) {
74 if (!frame->HasDebugInformation()) {
75 LLDB_LOGF(log, "Stepping out of frame with no debug info");
76
77 should_stop_here = false;
78 }
79 }
80
81 // Check whether the frame we are in is a language runtime thunk, only for
82 // step out:
83 if (operation == eFrameCompareOlder) {
84 if (Symbol *symbol = frame->GetSymbolContext(resolve_scope: eSymbolContextSymbol).symbol) {
85 ProcessSP process_sp(current_plan->GetThread().GetProcess());
86 for (auto *runtime : process_sp->GetLanguageRuntimes()) {
87 if (runtime->IsSymbolARuntimeThunk(symbol: *symbol) &&
88 flags.Test(bit: ThreadPlanShouldStopHere::eStepOutPastThunks)) {
89 LLDB_LOGF(
90 log, "Stepping out past a language thunk %s for: %s",
91 frame->GetFunctionName(),
92 Language::GetNameForLanguageType(runtime->GetLanguageType()));
93 should_stop_here = false;
94 break;
95 }
96 }
97 }
98 }
99 // Always avoid code with line number 0.
100 // FIXME: At present the ShouldStop and the StepFromHere calculate this
101 // independently. If this ever
102 // becomes expensive (this one isn't) we can try to have this set a state
103 // that the StepFromHere can use.
104 if (frame) {
105 SymbolContext sc;
106 sc = frame->GetSymbolContext(resolve_scope: eSymbolContextLineEntry);
107 if (sc.line_entry.line == 0)
108 should_stop_here = false;
109 }
110
111 return should_stop_here;
112}
113
114ThreadPlanSP ThreadPlanShouldStopHere::DefaultStepFromHereCallback(
115 ThreadPlan *current_plan, Flags &flags, FrameComparison operation,
116 Status &status, void *baton) {
117 const bool stop_others = false;
118 const size_t frame_index = 0;
119 ThreadPlanSP return_plan_sp;
120 // If we are stepping through code at line number 0, then we need to step
121 // over this range. Otherwise we will step out.
122 Log *log = GetLog(mask: LLDBLog::Step);
123
124 StackFrame *frame = current_plan->GetThread().GetStackFrameAtIndex(idx: 0).get();
125 if (!frame)
126 return return_plan_sp;
127 SymbolContext sc;
128 sc = frame->GetSymbolContext(resolve_scope: eSymbolContextLineEntry | eSymbolContextSymbol);
129
130 if (sc.line_entry.line == 0) {
131 AddressRange range = sc.line_entry.range;
132 bool just_step_out = false;
133 if (sc.symbol) {
134 ProcessSP process_sp(current_plan->GetThread().GetProcess());
135
136 // If this is a runtime thunk, step through it, rather than stepping out
137 // because it's marked line 0.
138 bool is_thunk = false;
139 for (auto *runtime : process_sp->GetLanguageRuntimes()) {
140 if (runtime->IsSymbolARuntimeThunk(symbol: *sc.symbol) &&
141 flags.Test(bit: ThreadPlanShouldStopHere::eStepOutPastThunks)) {
142 LLDB_LOGF(
143 log, "Stepping out past a language thunk %s for: %s",
144 frame->GetFunctionName(),
145 Language::GetNameForLanguageType(runtime->GetLanguageType()));
146 is_thunk = true;
147 break;
148 }
149 }
150
151 // If the whole function is marked line 0 just step out, that's easier &
152 // faster than continuing to step through it.
153 // FIXME: This assumes that the function is a single line range. It could
154 // be a series of contiguous line 0 ranges. Check for that too.
155 if (!is_thunk && sc.symbol->ValueIsAddress()) {
156 Address symbol_end = sc.symbol->GetAddress();
157 symbol_end.Slide(offset: sc.symbol->GetByteSize() - 1);
158 if (range.ContainsFileAddress(so_addr: sc.symbol->GetAddress()) &&
159 range.ContainsFileAddress(so_addr: symbol_end)) {
160 LLDB_LOGF(log, "Stopped in a function with only line 0 lines, just "
161 "stepping out.");
162 just_step_out = true;
163 }
164 }
165 }
166 if (!just_step_out) {
167 LLDB_LOGF(log, "ThreadPlanShouldStopHere::DefaultStepFromHereCallback "
168 "Queueing StepInRange plan to step through line 0 code.");
169
170 return_plan_sp = current_plan->GetThread().QueueThreadPlanForStepInRange(
171 abort_other_plans: false, range, addr_context: sc, step_in_target: nullptr, stop_other_threads: eOnlyDuringStepping, status,
172 step_in_avoids_code_without_debug_info: eLazyBoolCalculate, step_out_avoids_code_without_debug_info: eLazyBoolNo);
173 }
174 }
175
176 if (!return_plan_sp)
177 return_plan_sp =
178 current_plan->GetThread().QueueThreadPlanForStepOutNoShouldStop(
179 abort_other_plans: false, addr_context: nullptr, first_insn: true, stop_other_threads: stop_others, report_stop_vote: eVoteNo, report_run_vote: eVoteNoOpinion,
180 frame_idx: frame_index, status, continue_to_next_branch: true);
181 return return_plan_sp;
182}
183
184ThreadPlanSP ThreadPlanShouldStopHere::QueueStepOutFromHerePlan(
185 lldb_private::Flags &flags, lldb::FrameComparison operation,
186 Status &status) {
187 ThreadPlanSP return_plan_sp;
188 if (m_callbacks.step_from_here_callback) {
189 return_plan_sp = m_callbacks.step_from_here_callback(
190 m_owner, flags, operation, status, m_baton);
191 }
192 return return_plan_sp;
193}
194
195lldb::ThreadPlanSP ThreadPlanShouldStopHere::CheckShouldStopHereAndQueueStepOut(
196 lldb::FrameComparison operation, Status &status) {
197 if (!InvokeShouldStopHereCallback(operation, status))
198 return QueueStepOutFromHerePlan(flags&: m_flags, operation, status);
199 else
200 return ThreadPlanSP();
201}
202

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

source code of lldb/source/Target/ThreadPlanShouldStopHere.cpp