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

source code of lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp