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
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 bool MightHaveChildren() override;
53
54 size_t GetIndexOfChildWithName(ConstString name) override;
55
56private:
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
63class LibStdcppSharedPtrSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
64public:
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;
76private:
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
89LibstdcppMapIteratorSyntheticFrontEnd::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
97lldb::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
135llvm::Expected<uint32_t>
136LibstdcppMapIteratorSyntheticFrontEnd::CalculateNumChildren() {
137 return 2;
138}
139
140lldb::ValueObjectSP
141LibstdcppMapIteratorSyntheticFrontEnd::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
152bool LibstdcppMapIteratorSyntheticFrontEnd::MightHaveChildren() { return true; }
153
154size_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
163SyntheticChildrenFrontEnd *
164lldb_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
180SyntheticChildrenFrontEnd *
181lldb_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
188lldb_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
197lldb::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
223llvm::Expected<uint32_t>
224VectorIteratorSyntheticFrontEnd::CalculateNumChildren() {
225 return 1;
226}
227
228lldb::ValueObjectSP
229VectorIteratorSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) {
230 if (idx == 0)
231 return m_item_sp;
232 return lldb::ValueObjectSP();
233}
234
235bool VectorIteratorSyntheticFrontEnd::MightHaveChildren() { return true; }
236
237size_t VectorIteratorSyntheticFrontEnd::GetIndexOfChildWithName(
238 ConstString name) {
239 if (name == "item")
240 return 0;
241 return UINT32_MAX;
242}
243
244bool 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
300bool 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
371LibStdcppSharedPtrSyntheticFrontEnd::LibStdcppSharedPtrSyntheticFrontEnd(
372 lldb::ValueObjectSP valobj_sp)
373 : SyntheticChildrenFrontEnd(*valobj_sp) {
374 if (valobj_sp)
375 Update();
376}
377
378llvm::Expected<uint32_t>
379LibStdcppSharedPtrSyntheticFrontEnd::CalculateNumChildren() {
380 return 1;
381}
382
383lldb::ValueObjectSP
384LibStdcppSharedPtrSyntheticFrontEnd::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
400lldb::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
419bool LibStdcppSharedPtrSyntheticFrontEnd::MightHaveChildren() { return true; }
420
421size_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
430SyntheticChildrenFrontEnd *
431lldb_private::formatters::LibStdcppSharedPtrSyntheticFrontEndCreator(
432 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
433 return (valobj_sp ? new LibStdcppSharedPtrSyntheticFrontEnd(valobj_sp)
434 : nullptr);
435}
436
437bool 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

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