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
LibstdcppMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)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
Update()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("_M_node"));
114 if (!_M_node_sp)
115 return lldb::ChildCacheState::eRefetch;
116
117 m_pair_address = _M_node_sp->GetValueAsUnsigned(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(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>
CalculateNumChildren()136 LibstdcppMapIteratorSyntheticFrontEnd::CalculateNumChildren() {
137 return 2;
138 }
139
140 lldb::ValueObjectSP
GetChildAtIndex(uint32_t idx)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("pair", m_pair_address,
145 m_exe_ctx_ref, m_pair_type);
146 if (m_pair_sp)
147 return m_pair_sp->GetChildAtIndex(idx);
148 }
149 return lldb::ValueObjectSP();
150 }
151
MightHaveChildren()152 bool LibstdcppMapIteratorSyntheticFrontEnd::MightHaveChildren() { return true; }
153
GetIndexOfChildWithName(ConstString name)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 *
LibstdcppMapIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *,lldb::ValueObjectSP valobj_sp)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 *
LibStdcppVectorIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *,lldb::ValueObjectSP valobj_sp)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::
VectorIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp,llvm::ArrayRef<ConstString> item_names)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
Update()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(*valobj_sp, m_item_names);
209 if (!item_ptr)
210 return lldb::ChildCacheState::eRefetch;
211 if (item_ptr->GetValueAsUnsigned(0) == 0)
212 return lldb::ChildCacheState::eRefetch;
213 Status err;
214 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
215 m_item_sp = CreateValueObjectFromAddress(
216 "item", item_ptr->GetValueAsUnsigned(0), m_exe_ctx_ref,
217 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>
CalculateNumChildren()224 VectorIteratorSyntheticFrontEnd::CalculateNumChildren() {
225 return 1;
226 }
227
228 lldb::ValueObjectSP
GetChildAtIndex(uint32_t idx)229 VectorIteratorSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) {
230 if (idx == 0)
231 return m_item_sp;
232 return lldb::ValueObjectSP();
233 }
234
MightHaveChildren()235 bool VectorIteratorSyntheticFrontEnd::MightHaveChildren() { return true; }
236
GetIndexOfChildWithName(ConstString name)237 size_t VectorIteratorSyntheticFrontEnd::GetIndexOfChildWithName(
238 ConstString name) {
239 if (name == "item")
240 return 0;
241 return UINT32_MAX;
242 }
243
LibStdcppStringSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)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_addr, &addr_type);
254 } else
255 addr_of_string =
256 valobj.GetAddressOf(scalar_is_load_addr, &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(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 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("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
LibStdcppWStringSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)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_addr, &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(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(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(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 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("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
LibStdcppSharedPtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)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>
CalculateNumChildren()379 LibStdcppSharedPtrSyntheticFrontEnd::CalculateNumChildren() {
380 return 1;
381 }
382
383 lldb::ValueObjectSP
GetChildAtIndex(uint32_t idx)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(ConstString("object")).get();
393 }
394 if (m_obj_obj)
395 return m_obj_obj->GetSP();
396 }
397 return lldb::ValueObjectSP();
398 }
399
Update()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("_M_ptr");
410 if (!ptr_obj_sp)
411 return lldb::ChildCacheState::eRefetch;
412
413 m_ptr_obj = ptr_obj_sp->Clone(ConstString("pointer")).get();
414 m_obj_obj = nullptr;
415
416 return lldb::ChildCacheState::eRefetch;
417 }
418
MightHaveChildren()419 bool LibStdcppSharedPtrSyntheticFrontEnd::MightHaveChildren() { return true; }
420
GetIndexOfChildWithName(ConstString name)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 *
LibStdcppSharedPtrSyntheticFrontEndCreator(CXXSyntheticChildren *,lldb::ValueObjectSP valobj_sp)431 lldb_private::formatters::LibStdcppSharedPtrSyntheticFrontEndCreator(
432 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
433 return (valobj_sp ? new LibStdcppSharedPtrSyntheticFrontEnd(valobj_sp)
434 : nullptr);
435 }
436
LibStdcppSmartPointerSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)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("_M_ptr"));
444 if (!ptr_sp)
445 return false;
446
447 ValueObjectSP usecount_sp(
448 valobj_sp->GetChildAtNamePath({"_M_refcount", "_M_pi", "_M_use_count"}));
449 if (!usecount_sp)
450 return false;
451
452 if (ptr_sp->GetValueAsUnsigned(0) == 0 ||
453 usecount_sp->GetValueAsUnsigned(0) == 0) {
454 stream.Printf("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 stream, ValueObject::eValueObjectRepresentationStyleSummary,
463 lldb::eFormatInvalid,
464 ValueObject::PrintableRepresentationSpecialCases::eDisable,
465 false)) {
466 return true;
467 }
468 }
469
470 stream.Printf("ptr = 0x%" PRIx64, ptr_sp->GetValueAsUnsigned(0));
471 return true;
472 }
473