xref: /freebsd/contrib/llvm-project/lldb/source/Target/ThreadPlanShouldStopHere.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
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 
18 using namespace lldb;
19 using namespace lldb_private;
20 
21 // ThreadPlanShouldStopHere constructor
ThreadPlanShouldStopHere(ThreadPlan * owner)22 ThreadPlanShouldStopHere::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 
ThreadPlanShouldStopHere(ThreadPlan * owner,const ThreadPlanShouldStopHereCallbacks * callbacks,void * baton)31 ThreadPlanShouldStopHere::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 
39 ThreadPlanShouldStopHere::~ThreadPlanShouldStopHere() = default;
40 
InvokeShouldStopHereCallback(FrameComparison operation,Status & status)41 bool 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(LLDBLog::Step);
48     if (log) {
49       lldb::addr_t current_addr =
50           m_owner->GetThread().GetRegisterContext()->GetPC(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 
DefaultShouldStopHereCallback(ThreadPlan * current_plan,Flags & flags,FrameComparison operation,Status & status,void * baton)60 bool 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(0).get();
65   if (!frame)
66     return true;
67 
68   Log *log = GetLog(LLDBLog::Step);
69 
70   if ((operation == eFrameCompareOlder && flags.Test(eStepOutAvoidNoDebug)) ||
71       (operation == eFrameCompareYounger && flags.Test(eStepInAvoidNoDebug)) ||
72       (operation == eFrameCompareSameParent &&
73        flags.Test(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(eSymbolContextSymbol).symbol) {
85       ProcessSP process_sp(current_plan->GetThread().GetProcess());
86       for (auto *runtime : process_sp->GetLanguageRuntimes()) {
87         if (runtime->IsSymbolARuntimeThunk(*symbol) &&
88             flags.Test(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(eSymbolContextLineEntry);
107     if (sc.line_entry.line == 0)
108       should_stop_here = false;
109   }
110 
111   return should_stop_here;
112 }
113 
DefaultStepFromHereCallback(ThreadPlan * current_plan,Flags & flags,FrameComparison operation,Status & status,void * baton)114 ThreadPlanSP 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(LLDBLog::Step);
123 
124   StackFrame *frame = current_plan->GetThread().GetStackFrameAtIndex(0).get();
125   if (!frame)
126     return return_plan_sp;
127   SymbolContext sc;
128   sc = frame->GetSymbolContext(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(*sc.symbol) &&
141             flags.Test(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(sc.symbol->GetByteSize() - 1);
158         if (range.ContainsFileAddress(sc.symbol->GetAddress()) &&
159             range.ContainsFileAddress(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           false, range, sc, nullptr, eOnlyDuringStepping, status,
172           eLazyBoolCalculate, eLazyBoolNo);
173     }
174   }
175 
176   if (!return_plan_sp)
177     return_plan_sp =
178         current_plan->GetThread().QueueThreadPlanForStepOutNoShouldStop(
179             false, nullptr, true, stop_others, eVoteNo, eVoteNoOpinion,
180             frame_index, status, true);
181   return return_plan_sp;
182 }
183 
QueueStepOutFromHerePlan(lldb_private::Flags & flags,lldb::FrameComparison operation,Status & status)184 ThreadPlanSP 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 
CheckShouldStopHereAndQueueStepOut(lldb::FrameComparison operation,Status & status)195 lldb::ThreadPlanSP ThreadPlanShouldStopHere::CheckShouldStopHereAndQueueStepOut(
196     lldb::FrameComparison operation, Status &status) {
197   if (!InvokeShouldStopHereCallback(operation, status))
198     return QueueStepOutFromHerePlan(m_flags, operation, status);
199   else
200     return ThreadPlanSP();
201 }
202