1 | //===-- LibStdcpp.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 "LibStdcpp.h" |
10 | #include "LibCxx.h" |
11 | |
12 | #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" |
13 | #include "lldb/Core/ValueObject.h" |
14 | #include "lldb/Core/ValueObjectConstResult.h" |
15 | #include "lldb/DataFormatters/StringPrinter.h" |
16 | #include "lldb/DataFormatters/VectorIterator.h" |
17 | #include "lldb/Target/Target.h" |
18 | #include "lldb/Utility/DataBufferHeap.h" |
19 | #include "lldb/Utility/Endian.h" |
20 | #include "lldb/Utility/Status.h" |
21 | #include "lldb/Utility/Stream.h" |
22 | #include <optional> |
23 | |
24 | using namespace lldb; |
25 | using namespace lldb_private; |
26 | using namespace lldb_private::formatters; |
27 | |
28 | namespace { |
29 | |
30 | class LibstdcppMapIteratorSyntheticFrontEnd : public SyntheticChildrenFrontEnd { |
31 | /* |
32 | (std::_Rb_tree_iterator<std::pair<const int, std::basic_string<char, |
33 | std::char_traits<char>, std::allocator<char> > > >) ibeg = { |
34 | (_Base_ptr) _M_node = 0x0000000100103910 { |
35 | (std::_Rb_tree_color) _M_color = _S_black |
36 | (std::_Rb_tree_node_base::_Base_ptr) _M_parent = 0x00000001001038c0 |
37 | (std::_Rb_tree_node_base::_Base_ptr) _M_left = 0x0000000000000000 |
38 | (std::_Rb_tree_node_base::_Base_ptr) _M_right = 0x0000000000000000 |
39 | } |
40 | } |
41 | */ |
42 | |
43 | public: |
44 | explicit LibstdcppMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); |
45 | |
46 | llvm::Expected<uint32_t> CalculateNumChildren() override; |
47 | |
48 | lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; |
49 | |
50 | lldb::ChildCacheState Update() override; |
51 | |
52 | bool MightHaveChildren() override; |
53 | |
54 | size_t GetIndexOfChildWithName(ConstString name) override; |
55 | |
56 | private: |
57 | ExecutionContextRef m_exe_ctx_ref; |
58 | lldb::addr_t m_pair_address = 0; |
59 | CompilerType m_pair_type; |
60 | lldb::ValueObjectSP m_pair_sp; |
61 | }; |
62 | |
63 | class LibStdcppSharedPtrSyntheticFrontEnd : public SyntheticChildrenFrontEnd { |
64 | public: |
65 | explicit LibStdcppSharedPtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); |
66 | |
67 | llvm::Expected<uint32_t> CalculateNumChildren() override; |
68 | |
69 | lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; |
70 | |
71 | lldb::ChildCacheState Update() override; |
72 | |
73 | bool MightHaveChildren() override; |
74 | |
75 | size_t GetIndexOfChildWithName(ConstString name) override; |
76 | private: |
77 | |
78 | // The lifetime of a ValueObject and all its derivative ValueObjects |
79 | // (children, clones, etc.) is managed by a ClusterManager. These |
80 | // objects are only destroyed when every shared pointer to any of them |
81 | // is destroyed, so we must not store a shared pointer to any ValueObject |
82 | // derived from our backend ValueObject (since we're in the same cluster). |
83 | ValueObject* m_ptr_obj = nullptr; // Underlying pointer (held, not owned) |
84 | ValueObject* m_obj_obj = nullptr; // Underlying object (held, not owned) |
85 | }; |
86 | |
87 | } // end of anonymous namespace |
88 | |
89 | LibstdcppMapIteratorSyntheticFrontEnd::LibstdcppMapIteratorSyntheticFrontEnd( |
90 | lldb::ValueObjectSP valobj_sp) |
91 | : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_pair_type(), |
92 | m_pair_sp() { |
93 | if (valobj_sp) |
94 | Update(); |
95 | } |
96 | |
97 | lldb::ChildCacheState LibstdcppMapIteratorSyntheticFrontEnd::Update() { |
98 | ValueObjectSP valobj_sp = m_backend.GetSP(); |
99 | if (!valobj_sp) |
100 | return lldb::ChildCacheState::eRefetch; |
101 | |
102 | TargetSP target_sp(valobj_sp->GetTargetSP()); |
103 | |
104 | if (!target_sp) |
105 | return lldb::ChildCacheState::eRefetch; |
106 | |
107 | bool is_64bit = (target_sp->GetArchitecture().GetAddressByteSize() == 8); |
108 | |
109 | if (!valobj_sp) |
110 | return lldb::ChildCacheState::eRefetch; |
111 | m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); |
112 | |
113 | ValueObjectSP _M_node_sp(valobj_sp->GetChildMemberWithName(name: "_M_node" )); |
114 | if (!_M_node_sp) |
115 | return lldb::ChildCacheState::eRefetch; |
116 | |
117 | m_pair_address = _M_node_sp->GetValueAsUnsigned(fail_value: 0); |
118 | if (m_pair_address == 0) |
119 | return lldb::ChildCacheState::eRefetch; |
120 | |
121 | m_pair_address += (is_64bit ? 32 : 16); |
122 | |
123 | CompilerType my_type(valobj_sp->GetCompilerType()); |
124 | if (my_type.GetNumTemplateArguments() >= 1) { |
125 | CompilerType pair_type = my_type.GetTypeTemplateArgument(idx: 0); |
126 | if (!pair_type) |
127 | return lldb::ChildCacheState::eRefetch; |
128 | m_pair_type = pair_type; |
129 | } else |
130 | return lldb::ChildCacheState::eRefetch; |
131 | |
132 | return lldb::ChildCacheState::eReuse; |
133 | } |
134 | |
135 | llvm::Expected<uint32_t> |
136 | LibstdcppMapIteratorSyntheticFrontEnd::CalculateNumChildren() { |
137 | return 2; |
138 | } |
139 | |
140 | lldb::ValueObjectSP |
141 | LibstdcppMapIteratorSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) { |
142 | if (m_pair_address != 0 && m_pair_type) { |
143 | if (!m_pair_sp) |
144 | m_pair_sp = CreateValueObjectFromAddress(name: "pair" , address: m_pair_address, |
145 | exe_ctx: m_exe_ctx_ref, type: m_pair_type); |
146 | if (m_pair_sp) |
147 | return m_pair_sp->GetChildAtIndex(idx); |
148 | } |
149 | return lldb::ValueObjectSP(); |
150 | } |
151 | |
152 | bool LibstdcppMapIteratorSyntheticFrontEnd::MightHaveChildren() { return true; } |
153 | |
154 | size_t LibstdcppMapIteratorSyntheticFrontEnd::GetIndexOfChildWithName( |
155 | ConstString name) { |
156 | if (name == "first" ) |
157 | return 0; |
158 | if (name == "second" ) |
159 | return 1; |
160 | return UINT32_MAX; |
161 | } |
162 | |
163 | SyntheticChildrenFrontEnd * |
164 | lldb_private::formatters::LibstdcppMapIteratorSyntheticFrontEndCreator( |
165 | CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { |
166 | return (valobj_sp ? new LibstdcppMapIteratorSyntheticFrontEnd(valobj_sp) |
167 | : nullptr); |
168 | } |
169 | |
170 | /* |
171 | (lldb) fr var ibeg --ptr-depth 1 |
172 | (__gnu_cxx::__normal_iterator<int *, std::vector<int, std::allocator<int> > >) |
173 | ibeg = { |
174 | _M_current = 0x00000001001037a0 { |
175 | *_M_current = 1 |
176 | } |
177 | } |
178 | */ |
179 | |
180 | SyntheticChildrenFrontEnd * |
181 | lldb_private::formatters::LibStdcppVectorIteratorSyntheticFrontEndCreator( |
182 | CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { |
183 | return (valobj_sp ? new VectorIteratorSyntheticFrontEnd( |
184 | valobj_sp, {ConstString("_M_current" )}) |
185 | : nullptr); |
186 | } |
187 | |
188 | lldb_private::formatters::VectorIteratorSyntheticFrontEnd:: |
189 | VectorIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp, |
190 | llvm::ArrayRef<ConstString> item_names) |
191 | : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), |
192 | m_item_names(item_names), m_item_sp() { |
193 | if (valobj_sp) |
194 | Update(); |
195 | } |
196 | |
197 | lldb::ChildCacheState VectorIteratorSyntheticFrontEnd::Update() { |
198 | m_item_sp.reset(); |
199 | |
200 | ValueObjectSP valobj_sp = m_backend.GetSP(); |
201 | if (!valobj_sp) |
202 | return lldb::ChildCacheState::eRefetch; |
203 | |
204 | if (!valobj_sp) |
205 | return lldb::ChildCacheState::eRefetch; |
206 | |
207 | ValueObjectSP item_ptr = |
208 | formatters::GetChildMemberWithName(obj&: *valobj_sp, alternative_names: m_item_names); |
209 | if (!item_ptr) |
210 | return lldb::ChildCacheState::eRefetch; |
211 | if (item_ptr->GetValueAsUnsigned(fail_value: 0) == 0) |
212 | return lldb::ChildCacheState::eRefetch; |
213 | Status err; |
214 | m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); |
215 | m_item_sp = CreateValueObjectFromAddress( |
216 | name: "item" , address: item_ptr->GetValueAsUnsigned(fail_value: 0), exe_ctx: m_exe_ctx_ref, |
217 | type: item_ptr->GetCompilerType().GetPointeeType()); |
218 | if (err.Fail()) |
219 | m_item_sp.reset(); |
220 | return lldb::ChildCacheState::eRefetch; |
221 | } |
222 | |
223 | llvm::Expected<uint32_t> |
224 | VectorIteratorSyntheticFrontEnd::CalculateNumChildren() { |
225 | return 1; |
226 | } |
227 | |
228 | lldb::ValueObjectSP |
229 | VectorIteratorSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) { |
230 | if (idx == 0) |
231 | return m_item_sp; |
232 | return lldb::ValueObjectSP(); |
233 | } |
234 | |
235 | bool VectorIteratorSyntheticFrontEnd::MightHaveChildren() { return true; } |
236 | |
237 | size_t VectorIteratorSyntheticFrontEnd::GetIndexOfChildWithName( |
238 | ConstString name) { |
239 | if (name == "item" ) |
240 | return 0; |
241 | return UINT32_MAX; |
242 | } |
243 | |
244 | bool lldb_private::formatters::LibStdcppStringSummaryProvider( |
245 | ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { |
246 | const bool scalar_is_load_addr = true; |
247 | AddressType addr_type; |
248 | lldb::addr_t addr_of_string = LLDB_INVALID_ADDRESS; |
249 | if (valobj.IsPointerOrReferenceType()) { |
250 | Status error; |
251 | ValueObjectSP pointee_sp = valobj.Dereference(error); |
252 | if (pointee_sp && error.Success()) |
253 | addr_of_string = pointee_sp->GetAddressOf(scalar_is_load_address: scalar_is_load_addr, address_type: &addr_type); |
254 | } else |
255 | addr_of_string = |
256 | valobj.GetAddressOf(scalar_is_load_address: scalar_is_load_addr, address_type: &addr_type); |
257 | if (addr_of_string != LLDB_INVALID_ADDRESS) { |
258 | switch (addr_type) { |
259 | case eAddressTypeLoad: { |
260 | ProcessSP process_sp(valobj.GetProcessSP()); |
261 | if (!process_sp) |
262 | return false; |
263 | |
264 | StringPrinter::ReadStringAndDumpToStreamOptions options(valobj); |
265 | Status error; |
266 | lldb::addr_t addr_of_data = |
267 | process_sp->ReadPointerFromMemory(vm_addr: addr_of_string, error); |
268 | if (error.Fail() || addr_of_data == 0 || |
269 | addr_of_data == LLDB_INVALID_ADDRESS) |
270 | return false; |
271 | options.SetLocation(addr_of_data); |
272 | options.SetTargetSP(valobj.GetTargetSP()); |
273 | options.SetStream(&stream); |
274 | options.SetNeedsZeroTermination(false); |
275 | options.SetBinaryZeroIsTerminator(true); |
276 | lldb::addr_t size_of_data = process_sp->ReadPointerFromMemory( |
277 | vm_addr: addr_of_string + process_sp->GetAddressByteSize(), error); |
278 | if (error.Fail()) |
279 | return false; |
280 | options.SetSourceSize(size_of_data); |
281 | options.SetHasSourceSize(true); |
282 | |
283 | if (!StringPrinter::ReadStringAndDumpToStream< |
284 | StringPrinter::StringElementType::UTF8>(options)) { |
285 | stream.Printf(format: "Summary Unavailable" ); |
286 | return true; |
287 | } else |
288 | return true; |
289 | } break; |
290 | case eAddressTypeHost: |
291 | break; |
292 | case eAddressTypeInvalid: |
293 | case eAddressTypeFile: |
294 | break; |
295 | } |
296 | } |
297 | return false; |
298 | } |
299 | |
300 | bool lldb_private::formatters::LibStdcppWStringSummaryProvider( |
301 | ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { |
302 | const bool scalar_is_load_addr = true; |
303 | AddressType addr_type; |
304 | lldb::addr_t addr_of_string = |
305 | valobj.GetAddressOf(scalar_is_load_address: scalar_is_load_addr, address_type: &addr_type); |
306 | if (addr_of_string != LLDB_INVALID_ADDRESS) { |
307 | switch (addr_type) { |
308 | case eAddressTypeLoad: { |
309 | ProcessSP process_sp(valobj.GetProcessSP()); |
310 | if (!process_sp) |
311 | return false; |
312 | |
313 | CompilerType wchar_compiler_type = |
314 | valobj.GetCompilerType().GetBasicTypeFromAST(basic_type: lldb::eBasicTypeWChar); |
315 | |
316 | if (!wchar_compiler_type) |
317 | return false; |
318 | |
319 | // Safe to pass nullptr for exe_scope here. |
320 | std::optional<uint64_t> size = wchar_compiler_type.GetBitSize(exe_scope: nullptr); |
321 | if (!size) |
322 | return false; |
323 | const uint32_t wchar_size = *size; |
324 | |
325 | StringPrinter::ReadStringAndDumpToStreamOptions options(valobj); |
326 | Status error; |
327 | lldb::addr_t addr_of_data = |
328 | process_sp->ReadPointerFromMemory(vm_addr: addr_of_string, error); |
329 | if (error.Fail() || addr_of_data == 0 || |
330 | addr_of_data == LLDB_INVALID_ADDRESS) |
331 | return false; |
332 | options.SetLocation(addr_of_data); |
333 | options.SetTargetSP(valobj.GetTargetSP()); |
334 | options.SetStream(&stream); |
335 | options.SetNeedsZeroTermination(false); |
336 | options.SetBinaryZeroIsTerminator(false); |
337 | lldb::addr_t size_of_data = process_sp->ReadPointerFromMemory( |
338 | vm_addr: addr_of_string + process_sp->GetAddressByteSize(), error); |
339 | if (error.Fail()) |
340 | return false; |
341 | options.SetSourceSize(size_of_data); |
342 | options.SetHasSourceSize(true); |
343 | options.SetPrefixToken("L" ); |
344 | |
345 | switch (wchar_size) { |
346 | case 8: |
347 | return StringPrinter::ReadStringAndDumpToStream< |
348 | StringPrinter::StringElementType::UTF8>(options); |
349 | case 16: |
350 | return StringPrinter::ReadStringAndDumpToStream< |
351 | StringPrinter::StringElementType::UTF16>(options); |
352 | case 32: |
353 | return StringPrinter::ReadStringAndDumpToStream< |
354 | StringPrinter::StringElementType::UTF32>(options); |
355 | default: |
356 | stream.Printf(format: "size for wchar_t is not valid" ); |
357 | return true; |
358 | } |
359 | return true; |
360 | } break; |
361 | case eAddressTypeHost: |
362 | break; |
363 | case eAddressTypeInvalid: |
364 | case eAddressTypeFile: |
365 | break; |
366 | } |
367 | } |
368 | return false; |
369 | } |
370 | |
371 | LibStdcppSharedPtrSyntheticFrontEnd::LibStdcppSharedPtrSyntheticFrontEnd( |
372 | lldb::ValueObjectSP valobj_sp) |
373 | : SyntheticChildrenFrontEnd(*valobj_sp) { |
374 | if (valobj_sp) |
375 | Update(); |
376 | } |
377 | |
378 | llvm::Expected<uint32_t> |
379 | LibStdcppSharedPtrSyntheticFrontEnd::CalculateNumChildren() { |
380 | return 1; |
381 | } |
382 | |
383 | lldb::ValueObjectSP |
384 | LibStdcppSharedPtrSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) { |
385 | if (idx == 0) |
386 | return m_ptr_obj->GetSP(); |
387 | if (idx == 1) { |
388 | if (m_ptr_obj && !m_obj_obj) { |
389 | Status error; |
390 | ValueObjectSP obj_obj = m_ptr_obj->Dereference(error); |
391 | if (error.Success()) |
392 | m_obj_obj = obj_obj->Clone(new_name: ConstString("object" )).get(); |
393 | } |
394 | if (m_obj_obj) |
395 | return m_obj_obj->GetSP(); |
396 | } |
397 | return lldb::ValueObjectSP(); |
398 | } |
399 | |
400 | lldb::ChildCacheState LibStdcppSharedPtrSyntheticFrontEnd::Update() { |
401 | auto backend = m_backend.GetSP(); |
402 | if (!backend) |
403 | return lldb::ChildCacheState::eRefetch; |
404 | |
405 | auto valobj_sp = backend->GetNonSyntheticValue(); |
406 | if (!valobj_sp) |
407 | return lldb::ChildCacheState::eRefetch; |
408 | |
409 | auto ptr_obj_sp = valobj_sp->GetChildMemberWithName(name: "_M_ptr" ); |
410 | if (!ptr_obj_sp) |
411 | return lldb::ChildCacheState::eRefetch; |
412 | |
413 | m_ptr_obj = ptr_obj_sp->Clone(new_name: ConstString("pointer" )).get(); |
414 | m_obj_obj = nullptr; |
415 | |
416 | return lldb::ChildCacheState::eRefetch; |
417 | } |
418 | |
419 | bool LibStdcppSharedPtrSyntheticFrontEnd::MightHaveChildren() { return true; } |
420 | |
421 | size_t LibStdcppSharedPtrSyntheticFrontEnd::GetIndexOfChildWithName( |
422 | ConstString name) { |
423 | if (name == "pointer" ) |
424 | return 0; |
425 | if (name == "object" || name == "$$dereference$$" ) |
426 | return 1; |
427 | return UINT32_MAX; |
428 | } |
429 | |
430 | SyntheticChildrenFrontEnd * |
431 | lldb_private::formatters::LibStdcppSharedPtrSyntheticFrontEndCreator( |
432 | CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { |
433 | return (valobj_sp ? new LibStdcppSharedPtrSyntheticFrontEnd(valobj_sp) |
434 | : nullptr); |
435 | } |
436 | |
437 | bool lldb_private::formatters::LibStdcppSmartPointerSummaryProvider( |
438 | ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { |
439 | ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue()); |
440 | if (!valobj_sp) |
441 | return false; |
442 | |
443 | ValueObjectSP ptr_sp(valobj_sp->GetChildMemberWithName(name: "_M_ptr" )); |
444 | if (!ptr_sp) |
445 | return false; |
446 | |
447 | ValueObjectSP usecount_sp( |
448 | valobj_sp->GetChildAtNamePath(names: {"_M_refcount" , "_M_pi" , "_M_use_count" })); |
449 | if (!usecount_sp) |
450 | return false; |
451 | |
452 | if (ptr_sp->GetValueAsUnsigned(fail_value: 0) == 0 || |
453 | usecount_sp->GetValueAsUnsigned(fail_value: 0) == 0) { |
454 | stream.Printf(format: "nullptr" ); |
455 | return true; |
456 | } |
457 | |
458 | Status error; |
459 | ValueObjectSP pointee_sp = ptr_sp->Dereference(error); |
460 | if (pointee_sp && error.Success()) { |
461 | if (pointee_sp->DumpPrintableRepresentation( |
462 | s&: stream, val_obj_display: ValueObject::eValueObjectRepresentationStyleSummary, |
463 | custom_format: lldb::eFormatInvalid, |
464 | special: ValueObject::PrintableRepresentationSpecialCases::eDisable, |
465 | do_dump_error: false)) { |
466 | return true; |
467 | } |
468 | } |
469 | |
470 | stream.Printf(format: "ptr = 0x%" PRIx64, ptr_sp->GetValueAsUnsigned(fail_value: 0)); |
471 | return true; |
472 | } |
473 | |