/Users/buildslave/jenkins/workspace/coverage/llvm-project/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===-- CPPLanguageRuntime.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 <cstring> |
10 | | |
11 | | #include <memory> |
12 | | |
13 | | #include "CPPLanguageRuntime.h" |
14 | | |
15 | | #include "llvm/ADT/StringRef.h" |
16 | | |
17 | | #include "lldb/Symbol/Block.h" |
18 | | #include "lldb/Symbol/Variable.h" |
19 | | #include "lldb/Symbol/VariableList.h" |
20 | | |
21 | | #include "lldb/Core/PluginManager.h" |
22 | | #include "lldb/Core/UniqueCStringMap.h" |
23 | | #include "lldb/Symbol/CompileUnit.h" |
24 | | #include "lldb/Target/ABI.h" |
25 | | #include "lldb/Target/ExecutionContext.h" |
26 | | #include "lldb/Target/RegisterContext.h" |
27 | | #include "lldb/Target/SectionLoadList.h" |
28 | | #include "lldb/Target/StackFrame.h" |
29 | | #include "lldb/Target/ThreadPlanRunToAddress.h" |
30 | | #include "lldb/Target/ThreadPlanStepInRange.h" |
31 | | #include "lldb/Utility/Timer.h" |
32 | | |
33 | | using namespace lldb; |
34 | | using namespace lldb_private; |
35 | | |
36 | | static ConstString g_this = ConstString("this"); |
37 | | |
38 | | char CPPLanguageRuntime::ID = 0; |
39 | | |
40 | | CPPLanguageRuntime::CPPLanguageRuntime(Process *process) |
41 | 2.88k | : LanguageRuntime(process) {} |
42 | | |
43 | 2 | bool CPPLanguageRuntime::IsAllowedRuntimeValue(ConstString name) { |
44 | 2 | return name == g_this; |
45 | 2 | } |
46 | | |
47 | | bool CPPLanguageRuntime::GetObjectDescription(Stream &str, |
48 | 0 | ValueObject &object) { |
49 | | // C++ has no generic way to do this. |
50 | 0 | return false; |
51 | 0 | } |
52 | | |
53 | | bool CPPLanguageRuntime::GetObjectDescription( |
54 | 0 | Stream &str, Value &value, ExecutionContextScope *exe_scope) { |
55 | | // C++ has no generic way to do this. |
56 | 0 | return false; |
57 | 0 | } |
58 | | |
59 | 34 | bool contains_lambda_identifier(llvm::StringRef &str_ref) { |
60 | 34 | return str_ref.contains("$_") || str_ref.contains("'lambda'")20 ; |
61 | 34 | } |
62 | | |
63 | | CPPLanguageRuntime::LibCppStdFunctionCallableInfo |
64 | | line_entry_helper(Target &target, const SymbolContext &sc, Symbol *symbol, |
65 | | llvm::StringRef first_template_param_sref, |
66 | 12 | bool has_invoke) { |
67 | | |
68 | 12 | CPPLanguageRuntime::LibCppStdFunctionCallableInfo optional_info; |
69 | | |
70 | 12 | AddressRange range; |
71 | 12 | sc.GetAddressRange(eSymbolContextEverything, 0, false, range); |
72 | | |
73 | 12 | Address address = range.GetBaseAddress(); |
74 | | |
75 | 12 | Address addr; |
76 | 12 | if (target.ResolveLoadAddress(address.GetCallableLoadAddress(&target), |
77 | 12 | addr)) { |
78 | 12 | LineEntry line_entry; |
79 | 12 | addr.CalculateSymbolContextLineEntry(line_entry); |
80 | | |
81 | 12 | if (contains_lambda_identifier(first_template_param_sref) || has_invoke5 ) { |
82 | | // Case 1 and 2 |
83 | 12 | optional_info.callable_case = lldb_private::CPPLanguageRuntime:: |
84 | 12 | LibCppStdFunctionCallableCase::Lambda; |
85 | 12 | } else { |
86 | | // Case 3 |
87 | 0 | optional_info.callable_case = lldb_private::CPPLanguageRuntime:: |
88 | 0 | LibCppStdFunctionCallableCase::CallableObject; |
89 | 0 | } |
90 | | |
91 | 12 | optional_info.callable_symbol = *symbol; |
92 | 12 | optional_info.callable_line_entry = line_entry; |
93 | 12 | optional_info.callable_address = addr; |
94 | 12 | } |
95 | | |
96 | 12 | return optional_info; |
97 | 12 | } |
98 | | |
99 | | CPPLanguageRuntime::LibCppStdFunctionCallableInfo |
100 | | CPPLanguageRuntime::FindLibCppStdFunctionCallableInfo( |
101 | 30 | lldb::ValueObjectSP &valobj_sp) { |
102 | 30 | LLDB_SCOPED_TIMER(); |
103 | | |
104 | 30 | LibCppStdFunctionCallableInfo optional_info; |
105 | | |
106 | 30 | if (!valobj_sp) |
107 | 0 | return optional_info; |
108 | | |
109 | | // Member __f_ has type __base*, the contents of which will hold: |
110 | | // 1) a vtable entry which may hold type information needed to discover the |
111 | | // lambda being called |
112 | | // 2) possibly hold a pointer to the callable object |
113 | | // e.g. |
114 | | // |
115 | | // (lldb) frame var -R f_display |
116 | | // (std::__1::function<void (int)>) f_display = { |
117 | | // __buf_ = { |
118 | | // … |
119 | | // } |
120 | | // __f_ = 0x00007ffeefbffa00 |
121 | | // } |
122 | | // (lldb) memory read -fA 0x00007ffeefbffa00 |
123 | | // 0x7ffeefbffa00: ... `vtable for std::__1::__function::__func<void (*) ... |
124 | | // 0x7ffeefbffa08: ... `print_num(int) at std_function_cppreference_exam ... |
125 | | // |
126 | | // We will be handling five cases below, std::function is wrapping: |
127 | | // |
128 | | // 1) a lambda we know at compile time. We will obtain the name of the lambda |
129 | | // from the first template pameter from __func's vtable. We will look up |
130 | | // the lambda's operator()() and obtain the line table entry. |
131 | | // 2) a lambda we know at runtime. A pointer to the lambdas __invoke method |
132 | | // will be stored after the vtable. We will obtain the lambdas name from |
133 | | // this entry and lookup operator()() and obtain the line table entry. |
134 | | // 3) a callable object via operator()(). We will obtain the name of the |
135 | | // object from the first template parameter from __func's vtable. We will |
136 | | // look up the objects operator()() and obtain the line table entry. |
137 | | // 4) a member function. A pointer to the function will stored after the |
138 | | // we will obtain the name from this pointer. |
139 | | // 5) a free function. A pointer to the function will stored after the vtable |
140 | | // we will obtain the name from this pointer. |
141 | 30 | ValueObjectSP member_f_(valobj_sp->GetChildMemberWithName("__f_")); |
142 | | |
143 | 30 | if (member_f_) { |
144 | 30 | ValueObjectSP sub_member_f_(member_f_->GetChildMemberWithName("__f_")); |
145 | | |
146 | 30 | if (sub_member_f_) |
147 | 30 | member_f_ = sub_member_f_; |
148 | 30 | } |
149 | | |
150 | 30 | if (!member_f_) |
151 | 0 | return optional_info; |
152 | | |
153 | 30 | lldb::addr_t member_f_pointer_value = member_f_->GetValueAsUnsigned(0); |
154 | | |
155 | 30 | optional_info.member_f_pointer_value = member_f_pointer_value; |
156 | | |
157 | 30 | if (!member_f_pointer_value) |
158 | 5 | return optional_info; |
159 | | |
160 | 25 | ExecutionContext exe_ctx(valobj_sp->GetExecutionContextRef()); |
161 | 25 | Process *process = exe_ctx.GetProcessPtr(); |
162 | | |
163 | 25 | if (process == nullptr) |
164 | 0 | return optional_info; |
165 | | |
166 | 25 | uint32_t address_size = process->GetAddressByteSize(); |
167 | 25 | Status status; |
168 | | |
169 | | // First item pointed to by __f_ should be the pointer to the vtable for |
170 | | // a __base object. |
171 | 25 | lldb::addr_t vtable_address = |
172 | 25 | process->ReadPointerFromMemory(member_f_pointer_value, status); |
173 | | |
174 | 25 | if (status.Fail()) |
175 | 0 | return optional_info; |
176 | | |
177 | 25 | lldb::addr_t vtable_address_first_entry = |
178 | 25 | process->ReadPointerFromMemory(vtable_address + address_size, status); |
179 | | |
180 | 25 | if (status.Fail()) |
181 | 0 | return optional_info; |
182 | | |
183 | 25 | lldb::addr_t address_after_vtable = member_f_pointer_value + address_size; |
184 | | // As commented above we may not have a function pointer but if we do we will |
185 | | // need it. |
186 | 25 | lldb::addr_t possible_function_address = |
187 | 25 | process->ReadPointerFromMemory(address_after_vtable, status); |
188 | | |
189 | 25 | if (status.Fail()) |
190 | 0 | return optional_info; |
191 | | |
192 | 25 | Target &target = process->GetTarget(); |
193 | | |
194 | 25 | if (target.GetSectionLoadList().IsEmpty()) |
195 | 0 | return optional_info; |
196 | | |
197 | 25 | Address vtable_first_entry_resolved; |
198 | | |
199 | 25 | if (!target.GetSectionLoadList().ResolveLoadAddress( |
200 | 25 | vtable_address_first_entry, vtable_first_entry_resolved)) |
201 | 0 | return optional_info; |
202 | | |
203 | 25 | Address vtable_addr_resolved; |
204 | 25 | SymbolContext sc; |
205 | 25 | Symbol *symbol = nullptr; |
206 | | |
207 | 25 | if (!target.GetSectionLoadList().ResolveLoadAddress(vtable_address, |
208 | 25 | vtable_addr_resolved)) |
209 | 0 | return optional_info; |
210 | | |
211 | 25 | target.GetImages().ResolveSymbolContextForAddress( |
212 | 25 | vtable_addr_resolved, eSymbolContextEverything, sc); |
213 | 25 | symbol = sc.symbol; |
214 | | |
215 | 25 | if (symbol == nullptr) |
216 | 0 | return optional_info; |
217 | | |
218 | 25 | llvm::StringRef vtable_name(symbol->GetName().GetStringRef()); |
219 | 25 | bool found_expected_start_string = |
220 | 25 | vtable_name.startswith("vtable for std::__1::__function::__func<"); |
221 | | |
222 | 25 | if (!found_expected_start_string) |
223 | 0 | return optional_info; |
224 | | |
225 | | // Given case 1 or 3 we have a vtable name, we are want to extract the first |
226 | | // template parameter |
227 | | // |
228 | | // ... __func<main::$_0, std::__1::allocator<main::$_0> ... |
229 | | // ^^^^^^^^^ |
230 | | // |
231 | | // We could see names such as: |
232 | | // main::$_0 |
233 | | // Bar::add_num2(int)::'lambda'(int) |
234 | | // Bar |
235 | | // |
236 | | // We do this by find the first < and , and extracting in between. |
237 | | // |
238 | | // This covers the case of the lambda known at compile time. |
239 | 25 | size_t first_open_angle_bracket = vtable_name.find('<') + 1; |
240 | 25 | size_t first_comma = vtable_name.find(','); |
241 | | |
242 | 25 | llvm::StringRef first_template_parameter = |
243 | 25 | vtable_name.slice(first_open_angle_bracket, first_comma); |
244 | | |
245 | 25 | Address function_address_resolved; |
246 | | |
247 | | // Setup for cases 2, 4 and 5 we have a pointer to a function after the |
248 | | // vtable. We will use a process of elimination to drop through each case |
249 | | // and obtain the data we need. |
250 | 25 | if (target.GetSectionLoadList().ResolveLoadAddress( |
251 | 25 | possible_function_address, function_address_resolved)) { |
252 | 19 | target.GetImages().ResolveSymbolContextForAddress( |
253 | 19 | function_address_resolved, eSymbolContextEverything, sc); |
254 | 19 | symbol = sc.symbol; |
255 | 19 | } |
256 | | |
257 | | // These conditions are used several times to simplify statements later on. |
258 | 25 | bool has_invoke = |
259 | 25 | (symbol ? symbol->GetName().GetStringRef().contains("__invoke") : false0 ); |
260 | 25 | auto calculate_symbol_context_helper = [](auto &t, |
261 | 25 | SymbolContextList &sc_list) { |
262 | 12 | SymbolContext sc; |
263 | 12 | t->CalculateSymbolContext(&sc); |
264 | 12 | sc_list.Append(sc); |
265 | 12 | }; CPPLanguageRuntime.cpp:auto lldb_private::CPPLanguageRuntime::FindLibCppStdFunctionCallableInfo(std::__1::shared_ptr<lldb_private::ValueObject>&)::$_0::operator()<lldb_private::Symbol*>(lldb_private::Symbol*&, lldb_private::SymbolContextList&) const Line | Count | Source | 261 | 5 | SymbolContextList &sc_list) { | 262 | 5 | SymbolContext sc; | 263 | 5 | t->CalculateSymbolContext(&sc); | 264 | 5 | sc_list.Append(sc); | 265 | 5 | }; |
CPPLanguageRuntime.cpp:auto lldb_private::CPPLanguageRuntime::FindLibCppStdFunctionCallableInfo(std::__1::shared_ptr<lldb_private::ValueObject>&)::$_0::operator()<std::__1::shared_ptr<lldb_private::Function> >(std::__1::shared_ptr<lldb_private::Function>&, lldb_private::SymbolContextList&) const Line | Count | Source | 261 | 7 | SymbolContextList &sc_list) { | 262 | 7 | SymbolContext sc; | 263 | 7 | t->CalculateSymbolContext(&sc); | 264 | 7 | sc_list.Append(sc); | 265 | 7 | }; |
|
266 | | |
267 | | // Case 2 |
268 | 25 | if (has_invoke) { |
269 | 5 | SymbolContextList scl; |
270 | 5 | calculate_symbol_context_helper(symbol, scl); |
271 | | |
272 | 5 | return line_entry_helper(target, scl[0], symbol, first_template_parameter, |
273 | 5 | has_invoke); |
274 | 5 | } |
275 | | |
276 | | // Case 4 or 5 |
277 | 20 | if (symbol && !symbol->GetName().GetStringRef().startswith("vtable for") && |
278 | 20 | !contains_lambda_identifier(first_template_parameter)14 && !has_invoke6 ) { |
279 | 6 | optional_info.callable_case = |
280 | 6 | LibCppStdFunctionCallableCase::FreeOrMemberFunction; |
281 | 6 | optional_info.callable_address = function_address_resolved; |
282 | 6 | optional_info.callable_symbol = *symbol; |
283 | | |
284 | 6 | return optional_info; |
285 | 6 | } |
286 | | |
287 | 14 | std::string func_to_match = first_template_parameter.str(); |
288 | | |
289 | 14 | auto it = CallableLookupCache.find(func_to_match); |
290 | 14 | if (it != CallableLookupCache.end()) |
291 | 6 | return it->second; |
292 | | |
293 | 8 | SymbolContextList scl; |
294 | | |
295 | 8 | CompileUnit *vtable_cu = |
296 | 8 | vtable_first_entry_resolved.CalculateSymbolContextCompileUnit(); |
297 | 8 | llvm::StringRef name_to_use = func_to_match; |
298 | | |
299 | | // Case 3, we have a callable object instead of a lambda |
300 | | // |
301 | | // TODO |
302 | | // We currently don't support this case a callable object may have multiple |
303 | | // operator()() varying on const/non-const and number of arguments and we |
304 | | // don't have a way to currently distinguish them so we will bail out now. |
305 | 8 | if (!contains_lambda_identifier(name_to_use)) |
306 | 1 | return optional_info; |
307 | | |
308 | 7 | if (vtable_cu && !has_invoke) { |
309 | 7 | lldb::FunctionSP func_sp = |
310 | 1.85k | vtable_cu->FindFunction([name_to_use](const FunctionSP &f) { |
311 | 1.85k | auto name = f->GetName().GetStringRef(); |
312 | 1.85k | if (name.startswith(name_to_use) && name.contains("operator")16 ) |
313 | 7 | return true; |
314 | | |
315 | 1.85k | return false; |
316 | 1.85k | }); |
317 | | |
318 | 7 | if (func_sp) { |
319 | 7 | calculate_symbol_context_helper(func_sp, scl); |
320 | 7 | } |
321 | 7 | } |
322 | | |
323 | 7 | if (symbol == nullptr) |
324 | 0 | return optional_info; |
325 | | |
326 | | // Case 1 or 3 |
327 | 7 | if (scl.GetSize() >= 1) { |
328 | 7 | optional_info = line_entry_helper(target, scl[0], symbol, |
329 | 7 | first_template_parameter, has_invoke); |
330 | 7 | } |
331 | | |
332 | 7 | CallableLookupCache[func_to_match] = optional_info; |
333 | | |
334 | 7 | return optional_info; |
335 | 7 | } |
336 | | |
337 | | lldb::ThreadPlanSP |
338 | | CPPLanguageRuntime::GetStepThroughTrampolinePlan(Thread &thread, |
339 | 398 | bool stop_others) { |
340 | 398 | ThreadPlanSP ret_plan_sp; |
341 | | |
342 | 398 | lldb::addr_t curr_pc = thread.GetRegisterContext()->GetPC(); |
343 | | |
344 | 398 | TargetSP target_sp(thread.CalculateTarget()); |
345 | | |
346 | 398 | if (target_sp->GetSectionLoadList().IsEmpty()) |
347 | 0 | return ret_plan_sp; |
348 | | |
349 | 398 | Address pc_addr_resolved; |
350 | 398 | SymbolContext sc; |
351 | 398 | Symbol *symbol; |
352 | | |
353 | 398 | if (!target_sp->GetSectionLoadList().ResolveLoadAddress(curr_pc, |
354 | 398 | pc_addr_resolved)) |
355 | 0 | return ret_plan_sp; |
356 | | |
357 | 398 | target_sp->GetImages().ResolveSymbolContextForAddress( |
358 | 398 | pc_addr_resolved, eSymbolContextEverything, sc); |
359 | 398 | symbol = sc.symbol; |
360 | | |
361 | 398 | if (symbol == nullptr) |
362 | 0 | return ret_plan_sp; |
363 | | |
364 | 398 | llvm::StringRef function_name(symbol->GetName().GetCString()); |
365 | | |
366 | | // Handling the case where we are attempting to step into std::function. |
367 | | // The behavior will be that we will attempt to obtain the wrapped |
368 | | // callable via FindLibCppStdFunctionCallableInfo() and if we find it we |
369 | | // will return a ThreadPlanRunToAddress to the callable. Therefore we will |
370 | | // step into the wrapped callable. |
371 | | // |
372 | 398 | bool found_expected_start_string = |
373 | 398 | function_name.startswith("std::__1::function<"); |
374 | | |
375 | 398 | if (!found_expected_start_string) |
376 | 388 | return ret_plan_sp; |
377 | | |
378 | 10 | AddressRange range_of_curr_func; |
379 | 10 | sc.GetAddressRange(eSymbolContextEverything, 0, false, range_of_curr_func); |
380 | | |
381 | 10 | StackFrameSP frame = thread.GetStackFrameAtIndex(0); |
382 | | |
383 | 10 | if (frame) { |
384 | 10 | ValueObjectSP value_sp = frame->FindVariable(g_this); |
385 | | |
386 | 10 | CPPLanguageRuntime::LibCppStdFunctionCallableInfo callable_info = |
387 | 10 | FindLibCppStdFunctionCallableInfo(value_sp); |
388 | | |
389 | 10 | if (callable_info.callable_case != LibCppStdFunctionCallableCase::Invalid && |
390 | 10 | value_sp->GetValueIsValid()4 ) { |
391 | | // We found the std::function wrapped callable and we have its address. |
392 | | // We now create a ThreadPlan to run to the callable. |
393 | 4 | ret_plan_sp = std::make_shared<ThreadPlanRunToAddress>( |
394 | 4 | thread, callable_info.callable_address, stop_others); |
395 | 4 | return ret_plan_sp; |
396 | 6 | } else { |
397 | | // We are in std::function but we could not obtain the callable. |
398 | | // We create a ThreadPlan to keep stepping through using the address range |
399 | | // of the current function. |
400 | 6 | ret_plan_sp = std::make_shared<ThreadPlanStepInRange>( |
401 | 6 | thread, range_of_curr_func, sc, nullptr, eOnlyThisThread, |
402 | 6 | eLazyBoolYes, eLazyBoolYes); |
403 | 6 | return ret_plan_sp; |
404 | 6 | } |
405 | 10 | } |
406 | | |
407 | 0 | return ret_plan_sp; |
408 | 10 | } |