1 //===-- LibCxx.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 "LibCxx.h"
10
11 #include "lldb/Core/Debugger.h"
12 #include "lldb/Core/FormatEntity.h"
13 #include "lldb/Core/ValueObject.h"
14 #include "lldb/Core/ValueObjectConstResult.h"
15 #include "lldb/DataFormatters/FormattersHelpers.h"
16 #include "lldb/DataFormatters/StringPrinter.h"
17 #include "lldb/DataFormatters/TypeSummary.h"
18 #include "lldb/DataFormatters/VectorIterator.h"
19 #include "lldb/Target/ProcessStructReader.h"
20 #include "lldb/Target/SectionLoadList.h"
21 #include "lldb/Target/Target.h"
22 #include "lldb/Utility/ConstString.h"
23 #include "lldb/Utility/DataBufferHeap.h"
24 #include "lldb/Utility/Endian.h"
25 #include "lldb/Utility/Status.h"
26 #include "lldb/Utility/Stream.h"
27
28 #include "Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h"
29 #include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
30 #include "lldb/lldb-enumerations.h"
31 #include <optional>
32 #include <tuple>
33
34 using namespace lldb;
35 using namespace lldb_private;
36 using namespace lldb_private::formatters;
37
GetChildMemberWithName(ValueObject & obj,llvm::ArrayRef<ConstString> alternative_names)38 lldb::ValueObjectSP lldb_private::formatters::GetChildMemberWithName(
39 ValueObject &obj, llvm::ArrayRef<ConstString> alternative_names) {
40 for (ConstString name : alternative_names) {
41 lldb::ValueObjectSP child_sp = obj.GetChildMemberWithName(name, true);
42
43 if (child_sp)
44 return child_sp;
45 }
46 return {};
47 }
48
LibcxxOptionalSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)49 bool lldb_private::formatters::LibcxxOptionalSummaryProvider(
50 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
51 ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue());
52 if (!valobj_sp)
53 return false;
54
55 // An optional either contains a value or not, the member __engaged_ is
56 // a bool flag, it is true if the optional has a value and false otherwise.
57 ValueObjectSP engaged_sp(
58 valobj_sp->GetChildMemberWithName(ConstString("__engaged_"), true));
59
60 if (!engaged_sp)
61 return false;
62
63 llvm::StringRef engaged_as_cstring(
64 engaged_sp->GetValueAsUnsigned(0) == 1 ? "true" : "false");
65
66 stream.Printf(" Has Value=%s ", engaged_as_cstring.data());
67
68 return true;
69 }
70
LibcxxFunctionSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)71 bool lldb_private::formatters::LibcxxFunctionSummaryProvider(
72 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
73
74 ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue());
75
76 if (!valobj_sp)
77 return false;
78
79 ExecutionContext exe_ctx(valobj_sp->GetExecutionContextRef());
80 Process *process = exe_ctx.GetProcessPtr();
81
82 if (process == nullptr)
83 return false;
84
85 CPPLanguageRuntime *cpp_runtime = CPPLanguageRuntime::Get(*process);
86
87 if (!cpp_runtime)
88 return false;
89
90 CPPLanguageRuntime::LibCppStdFunctionCallableInfo callable_info =
91 cpp_runtime->FindLibCppStdFunctionCallableInfo(valobj_sp);
92
93 switch (callable_info.callable_case) {
94 case CPPLanguageRuntime::LibCppStdFunctionCallableCase::Invalid:
95 stream.Printf(" __f_ = %" PRIu64, callable_info.member_f_pointer_value);
96 return false;
97 break;
98 case CPPLanguageRuntime::LibCppStdFunctionCallableCase::Lambda:
99 stream.Printf(
100 " Lambda in File %s at Line %u",
101 callable_info.callable_line_entry.file.GetFilename().GetCString(),
102 callable_info.callable_line_entry.line);
103 break;
104 case CPPLanguageRuntime::LibCppStdFunctionCallableCase::CallableObject:
105 stream.Printf(
106 " Function in File %s at Line %u",
107 callable_info.callable_line_entry.file.GetFilename().GetCString(),
108 callable_info.callable_line_entry.line);
109 break;
110 case CPPLanguageRuntime::LibCppStdFunctionCallableCase::FreeOrMemberFunction:
111 stream.Printf(" Function = %s ",
112 callable_info.callable_symbol.GetName().GetCString());
113 break;
114 }
115
116 return true;
117 }
118
LibcxxSmartPointerSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)119 bool lldb_private::formatters::LibcxxSmartPointerSummaryProvider(
120 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
121 ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue());
122 if (!valobj_sp)
123 return false;
124 ValueObjectSP ptr_sp(
125 valobj_sp->GetChildMemberWithName(ConstString("__ptr_"), true));
126 ValueObjectSP count_sp(valobj_sp->GetChildAtNamePath(
127 {ConstString("__cntrl_"), ConstString("__shared_owners_")}));
128 ValueObjectSP weakcount_sp(valobj_sp->GetChildAtNamePath(
129 {ConstString("__cntrl_"), ConstString("__shared_weak_owners_")}));
130
131 if (!ptr_sp)
132 return false;
133
134 if (ptr_sp->GetValueAsUnsigned(0) == 0) {
135 stream.Printf("nullptr");
136 return true;
137 } else {
138 bool print_pointee = false;
139 Status error;
140 ValueObjectSP pointee_sp = ptr_sp->Dereference(error);
141 if (pointee_sp && error.Success()) {
142 if (pointee_sp->DumpPrintableRepresentation(
143 stream, ValueObject::eValueObjectRepresentationStyleSummary,
144 lldb::eFormatInvalid,
145 ValueObject::PrintableRepresentationSpecialCases::eDisable,
146 false))
147 print_pointee = true;
148 }
149 if (!print_pointee)
150 stream.Printf("ptr = 0x%" PRIx64, ptr_sp->GetValueAsUnsigned(0));
151 }
152
153 if (count_sp)
154 stream.Printf(" strong=%" PRIu64, 1 + count_sp->GetValueAsUnsigned(0));
155
156 if (weakcount_sp)
157 stream.Printf(" weak=%" PRIu64, 1 + weakcount_sp->GetValueAsUnsigned(0));
158
159 return true;
160 }
161
LibcxxUniquePointerSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)162 bool lldb_private::formatters::LibcxxUniquePointerSummaryProvider(
163 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
164 ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue());
165 if (!valobj_sp)
166 return false;
167
168 ValueObjectSP ptr_sp(
169 valobj_sp->GetChildMemberWithName(ConstString("__ptr_"), true));
170 if (!ptr_sp)
171 return false;
172
173 ptr_sp = GetValueOfLibCXXCompressedPair(*ptr_sp);
174 if (!ptr_sp)
175 return false;
176
177 if (ptr_sp->GetValueAsUnsigned(0) == 0) {
178 stream.Printf("nullptr");
179 return true;
180 } else {
181 bool print_pointee = false;
182 Status error;
183 ValueObjectSP pointee_sp = ptr_sp->Dereference(error);
184 if (pointee_sp && error.Success()) {
185 if (pointee_sp->DumpPrintableRepresentation(
186 stream, ValueObject::eValueObjectRepresentationStyleSummary,
187 lldb::eFormatInvalid,
188 ValueObject::PrintableRepresentationSpecialCases::eDisable,
189 false))
190 print_pointee = true;
191 }
192 if (!print_pointee)
193 stream.Printf("ptr = 0x%" PRIx64, ptr_sp->GetValueAsUnsigned(0));
194 }
195
196 return true;
197 }
198
199 /*
200 (lldb) fr var ibeg --raw --ptr-depth 1
201 (std::__1::__map_iterator<std::__1::__tree_iterator<std::__1::pair<int,
202 std::__1::basic_string<char, std::__1::char_traits<char>,
203 std::__1::allocator<char> > >, std::__1::__tree_node<std::__1::pair<int,
204 std::__1::basic_string<char, std::__1::char_traits<char>,
205 std::__1::allocator<char> > >, void *> *, long> >) ibeg = {
206 __i_ = {
207 __ptr_ = 0x0000000100103870 {
208 std::__1::__tree_node_base<void *> = {
209 std::__1::__tree_end_node<std::__1::__tree_node_base<void *> *> = {
210 __left_ = 0x0000000000000000
211 }
212 __right_ = 0x0000000000000000
213 __parent_ = 0x00000001001038b0
214 __is_black_ = true
215 }
216 __value_ = {
217 first = 0
218 second = { std::string }
219 */
220
221 lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::
LibCxxMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)222 LibCxxMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
223 : SyntheticChildrenFrontEnd(*valobj_sp), m_pair_ptr(), m_pair_sp() {
224 if (valobj_sp)
225 Update();
226 }
227
Update()228 bool lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::Update() {
229 m_pair_sp.reset();
230 m_pair_ptr = nullptr;
231
232 ValueObjectSP valobj_sp = m_backend.GetSP();
233 if (!valobj_sp)
234 return false;
235
236 TargetSP target_sp(valobj_sp->GetTargetSP());
237
238 if (!target_sp)
239 return false;
240
241 if (!valobj_sp)
242 return false;
243
244 static ConstString g_i_("__i_");
245
246 // this must be a ValueObject* because it is a child of the ValueObject we
247 // are producing children for it if were a ValueObjectSP, we would end up
248 // with a loop (iterator -> synthetic -> child -> parent == iterator) and
249 // that would in turn leak memory by never allowing the ValueObjects to die
250 // and free their memory
251 m_pair_ptr = valobj_sp
252 ->GetValueForExpressionPath(
253 ".__i_.__ptr_->__value_", nullptr, nullptr,
254 ValueObject::GetValueForExpressionPathOptions()
255 .DontCheckDotVsArrowSyntax()
256 .SetSyntheticChildrenTraversal(
257 ValueObject::GetValueForExpressionPathOptions::
258 SyntheticChildrenTraversal::None),
259 nullptr)
260 .get();
261
262 if (!m_pair_ptr) {
263 m_pair_ptr = valobj_sp
264 ->GetValueForExpressionPath(
265 ".__i_.__ptr_", nullptr, nullptr,
266 ValueObject::GetValueForExpressionPathOptions()
267 .DontCheckDotVsArrowSyntax()
268 .SetSyntheticChildrenTraversal(
269 ValueObject::GetValueForExpressionPathOptions::
270 SyntheticChildrenTraversal::None),
271 nullptr)
272 .get();
273 if (m_pair_ptr) {
274 auto __i_(valobj_sp->GetChildMemberWithName(g_i_, true));
275 if (!__i_) {
276 m_pair_ptr = nullptr;
277 return false;
278 }
279 CompilerType pair_type(
280 __i_->GetCompilerType().GetTypeTemplateArgument(0));
281 std::string name;
282 uint64_t bit_offset_ptr;
283 uint32_t bitfield_bit_size_ptr;
284 bool is_bitfield_ptr;
285 pair_type = pair_type.GetFieldAtIndex(
286 0, name, &bit_offset_ptr, &bitfield_bit_size_ptr, &is_bitfield_ptr);
287 if (!pair_type) {
288 m_pair_ptr = nullptr;
289 return false;
290 }
291
292 auto addr(m_pair_ptr->GetValueAsUnsigned(LLDB_INVALID_ADDRESS));
293 m_pair_ptr = nullptr;
294 if (addr && addr != LLDB_INVALID_ADDRESS) {
295 auto ts = pair_type.GetTypeSystem();
296 auto ast_ctx = ts.dyn_cast_or_null<TypeSystemClang>();
297 if (!ast_ctx)
298 return false;
299
300 // Mimick layout of std::__tree_iterator::__ptr_ and read it in
301 // from process memory.
302 //
303 // The following shows the contiguous block of memory:
304 //
305 // +-----------------------------+ class __tree_end_node
306 // __ptr_ | pointer __left_; |
307 // +-----------------------------+ class __tree_node_base
308 // | pointer __right_; |
309 // | __parent_pointer __parent_; |
310 // | bool __is_black_; |
311 // +-----------------------------+ class __tree_node
312 // | __node_value_type __value_; | <<< our key/value pair
313 // +-----------------------------+
314 //
315 CompilerType tree_node_type = ast_ctx->CreateStructForIdentifier(
316 ConstString(),
317 {{"ptr0",
318 ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()},
319 {"ptr1",
320 ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()},
321 {"ptr2",
322 ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()},
323 {"cw", ast_ctx->GetBasicType(lldb::eBasicTypeBool)},
324 {"payload", pair_type}});
325 std::optional<uint64_t> size = tree_node_type.GetByteSize(nullptr);
326 if (!size)
327 return false;
328 WritableDataBufferSP buffer_sp(new DataBufferHeap(*size, 0));
329 ProcessSP process_sp(target_sp->GetProcessSP());
330 Status error;
331 process_sp->ReadMemory(addr, buffer_sp->GetBytes(),
332 buffer_sp->GetByteSize(), error);
333 if (error.Fail())
334 return false;
335 DataExtractor extractor(buffer_sp, process_sp->GetByteOrder(),
336 process_sp->GetAddressByteSize());
337 auto pair_sp = CreateValueObjectFromData(
338 "pair", extractor, valobj_sp->GetExecutionContextRef(),
339 tree_node_type);
340 if (pair_sp)
341 m_pair_sp = pair_sp->GetChildAtIndex(4, true);
342 }
343 }
344 }
345
346 return false;
347 }
348
349 size_t lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::
CalculateNumChildren()350 CalculateNumChildren() {
351 return 2;
352 }
353
354 lldb::ValueObjectSP
GetChildAtIndex(size_t idx)355 lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::GetChildAtIndex(
356 size_t idx) {
357 if (m_pair_ptr)
358 return m_pair_ptr->GetChildAtIndex(idx, true);
359 if (m_pair_sp)
360 return m_pair_sp->GetChildAtIndex(idx, true);
361 return lldb::ValueObjectSP();
362 }
363
364 bool lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::
MightHaveChildren()365 MightHaveChildren() {
366 return true;
367 }
368
369 size_t lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::
GetIndexOfChildWithName(ConstString name)370 GetIndexOfChildWithName(ConstString name) {
371 if (name == "first")
372 return 0;
373 if (name == "second")
374 return 1;
375 return UINT32_MAX;
376 }
377
378 lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::
~LibCxxMapIteratorSyntheticFrontEnd()379 ~LibCxxMapIteratorSyntheticFrontEnd() {
380 // this will be deleted when its parent dies (since it's a child object)
381 // delete m_pair_ptr;
382 }
383
384 SyntheticChildrenFrontEnd *
LibCxxMapIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *,lldb::ValueObjectSP valobj_sp)385 lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEndCreator(
386 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
387 return (valobj_sp ? new LibCxxMapIteratorSyntheticFrontEnd(valobj_sp)
388 : nullptr);
389 }
390
391 lldb_private::formatters::LibCxxUnorderedMapIteratorSyntheticFrontEnd::
LibCxxUnorderedMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)392 LibCxxUnorderedMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
393 : SyntheticChildrenFrontEnd(*valobj_sp) {
394 if (valobj_sp)
395 Update();
396 }
397
398 bool lldb_private::formatters::LibCxxUnorderedMapIteratorSyntheticFrontEnd::
Update()399 Update() {
400 m_pair_sp.reset();
401 m_iter_ptr = nullptr;
402
403 ValueObjectSP valobj_sp = m_backend.GetSP();
404 if (!valobj_sp)
405 return false;
406
407 TargetSP target_sp(valobj_sp->GetTargetSP());
408
409 if (!target_sp)
410 return false;
411
412 if (!valobj_sp)
413 return false;
414
415 auto exprPathOptions = ValueObject::GetValueForExpressionPathOptions()
416 .DontCheckDotVsArrowSyntax()
417 .SetSyntheticChildrenTraversal(
418 ValueObject::GetValueForExpressionPathOptions::
419 SyntheticChildrenTraversal::None);
420
421 // This must be a ValueObject* because it is a child of the ValueObject we
422 // are producing children for it if were a ValueObjectSP, we would end up
423 // with a loop (iterator -> synthetic -> child -> parent == iterator) and
424 // that would in turn leak memory by never allowing the ValueObjects to die
425 // and free their memory.
426 m_iter_ptr =
427 valobj_sp
428 ->GetValueForExpressionPath(".__i_.__node_", nullptr, nullptr,
429 exprPathOptions, nullptr)
430 .get();
431
432 if (m_iter_ptr) {
433 auto iter_child(
434 valobj_sp->GetChildMemberWithName(ConstString("__i_"), true));
435 if (!iter_child) {
436 m_iter_ptr = nullptr;
437 return false;
438 }
439
440 CompilerType node_type(iter_child->GetCompilerType()
441 .GetTypeTemplateArgument(0)
442 .GetPointeeType());
443
444 CompilerType pair_type(node_type.GetTypeTemplateArgument(0));
445
446 std::string name;
447 uint64_t bit_offset_ptr;
448 uint32_t bitfield_bit_size_ptr;
449 bool is_bitfield_ptr;
450
451 pair_type = pair_type.GetFieldAtIndex(
452 0, name, &bit_offset_ptr, &bitfield_bit_size_ptr, &is_bitfield_ptr);
453 if (!pair_type) {
454 m_iter_ptr = nullptr;
455 return false;
456 }
457
458 uint64_t addr = m_iter_ptr->GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
459 m_iter_ptr = nullptr;
460
461 if (addr == 0 || addr == LLDB_INVALID_ADDRESS)
462 return false;
463
464 auto ts = pair_type.GetTypeSystem();
465 auto ast_ctx = ts.dyn_cast_or_null<TypeSystemClang>();
466 if (!ast_ctx)
467 return false;
468
469 // Mimick layout of std::__hash_iterator::__node_ and read it in
470 // from process memory.
471 //
472 // The following shows the contiguous block of memory:
473 //
474 // +-----------------------------+ class __hash_node_base
475 // __node_ | __next_pointer __next_; |
476 // +-----------------------------+ class __hash_node
477 // | size_t __hash_; |
478 // | __node_value_type __value_; | <<< our key/value pair
479 // +-----------------------------+
480 //
481 CompilerType tree_node_type = ast_ctx->CreateStructForIdentifier(
482 ConstString(),
483 {{"__next_",
484 ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()},
485 {"__hash_", ast_ctx->GetBasicType(lldb::eBasicTypeUnsignedLongLong)},
486 {"__value_", pair_type}});
487 std::optional<uint64_t> size = tree_node_type.GetByteSize(nullptr);
488 if (!size)
489 return false;
490 WritableDataBufferSP buffer_sp(new DataBufferHeap(*size, 0));
491 ProcessSP process_sp(target_sp->GetProcessSP());
492 Status error;
493 process_sp->ReadMemory(addr, buffer_sp->GetBytes(),
494 buffer_sp->GetByteSize(), error);
495 if (error.Fail())
496 return false;
497 DataExtractor extractor(buffer_sp, process_sp->GetByteOrder(),
498 process_sp->GetAddressByteSize());
499 auto pair_sp = CreateValueObjectFromData(
500 "pair", extractor, valobj_sp->GetExecutionContextRef(), tree_node_type);
501 if (pair_sp)
502 m_pair_sp = pair_sp->GetChildAtIndex(2, true);
503 }
504
505 return false;
506 }
507
508 size_t lldb_private::formatters::LibCxxUnorderedMapIteratorSyntheticFrontEnd::
CalculateNumChildren()509 CalculateNumChildren() {
510 return 2;
511 }
512
513 lldb::ValueObjectSP lldb_private::formatters::
GetChildAtIndex(size_t idx)514 LibCxxUnorderedMapIteratorSyntheticFrontEnd::GetChildAtIndex(size_t idx) {
515 if (m_pair_sp)
516 return m_pair_sp->GetChildAtIndex(idx, true);
517 return lldb::ValueObjectSP();
518 }
519
520 bool lldb_private::formatters::LibCxxUnorderedMapIteratorSyntheticFrontEnd::
MightHaveChildren()521 MightHaveChildren() {
522 return true;
523 }
524
525 size_t lldb_private::formatters::LibCxxUnorderedMapIteratorSyntheticFrontEnd::
GetIndexOfChildWithName(ConstString name)526 GetIndexOfChildWithName(ConstString name) {
527 if (name == "first")
528 return 0;
529 if (name == "second")
530 return 1;
531 return UINT32_MAX;
532 }
533
534 SyntheticChildrenFrontEnd *
LibCxxUnorderedMapIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *,lldb::ValueObjectSP valobj_sp)535 lldb_private::formatters::LibCxxUnorderedMapIteratorSyntheticFrontEndCreator(
536 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
537 return (valobj_sp ? new LibCxxUnorderedMapIteratorSyntheticFrontEnd(valobj_sp)
538 : nullptr);
539 }
540
541 /*
542 (lldb) fr var ibeg --raw --ptr-depth 1 -T
543 (std::__1::__wrap_iter<int *>) ibeg = {
544 (std::__1::__wrap_iter<int *>::iterator_type) __i = 0x00000001001037a0 {
545 (int) *__i = 1
546 }
547 }
548 */
549
550 SyntheticChildrenFrontEnd *
LibCxxVectorIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *,lldb::ValueObjectSP valobj_sp)551 lldb_private::formatters::LibCxxVectorIteratorSyntheticFrontEndCreator(
552 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
553 return (valobj_sp ? new VectorIteratorSyntheticFrontEnd(
554 valobj_sp, {ConstString("__i_"), ConstString("__i")})
555 : nullptr);
556 }
557
558 lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::
LibcxxSharedPtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)559 LibcxxSharedPtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
560 : SyntheticChildrenFrontEnd(*valobj_sp), m_cntrl(nullptr) {
561 if (valobj_sp)
562 Update();
563 }
564
565 size_t lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::
CalculateNumChildren()566 CalculateNumChildren() {
567 return (m_cntrl ? 1 : 0);
568 }
569
570 lldb::ValueObjectSP
GetChildAtIndex(size_t idx)571 lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::GetChildAtIndex(
572 size_t idx) {
573 if (!m_cntrl)
574 return lldb::ValueObjectSP();
575
576 ValueObjectSP valobj_sp = m_backend.GetSP();
577 if (!valobj_sp)
578 return lldb::ValueObjectSP();
579
580 if (idx == 0)
581 return valobj_sp->GetChildMemberWithName(ConstString("__ptr_"), true);
582
583 if (idx == 1) {
584 if (auto ptr_sp =
585 valobj_sp->GetChildMemberWithName(ConstString("__ptr_"), true)) {
586 Status status;
587 auto value_sp = ptr_sp->Dereference(status);
588 if (status.Success()) {
589 auto value_type_sp =
590 valobj_sp->GetCompilerType().GetTypeTemplateArgument(0);
591 return value_sp->Cast(value_type_sp);
592 }
593 }
594 }
595
596 return lldb::ValueObjectSP();
597 }
598
Update()599 bool lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::Update() {
600 m_cntrl = nullptr;
601
602 ValueObjectSP valobj_sp = m_backend.GetSP();
603 if (!valobj_sp)
604 return false;
605
606 TargetSP target_sp(valobj_sp->GetTargetSP());
607 if (!target_sp)
608 return false;
609
610 lldb::ValueObjectSP cntrl_sp(
611 valobj_sp->GetChildMemberWithName(ConstString("__cntrl_"), true));
612
613 m_cntrl = cntrl_sp.get(); // need to store the raw pointer to avoid a circular
614 // dependency
615 return false;
616 }
617
618 bool lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::
MightHaveChildren()619 MightHaveChildren() {
620 return true;
621 }
622
623 size_t lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::
GetIndexOfChildWithName(ConstString name)624 GetIndexOfChildWithName(ConstString name) {
625 if (name == "__ptr_")
626 return 0;
627 if (name == "$$dereference$$")
628 return 1;
629 return UINT32_MAX;
630 }
631
632 lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::
633 ~LibcxxSharedPtrSyntheticFrontEnd() = default;
634
635 SyntheticChildrenFrontEnd *
LibcxxSharedPtrSyntheticFrontEndCreator(CXXSyntheticChildren *,lldb::ValueObjectSP valobj_sp)636 lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEndCreator(
637 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
638 return (valobj_sp ? new LibcxxSharedPtrSyntheticFrontEnd(valobj_sp)
639 : nullptr);
640 }
641
642 lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::
LibcxxUniquePtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)643 LibcxxUniquePtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
644 : SyntheticChildrenFrontEnd(*valobj_sp) {
645 if (valobj_sp)
646 Update();
647 }
648
649 lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::
650 ~LibcxxUniquePtrSyntheticFrontEnd() = default;
651
652 SyntheticChildrenFrontEnd *
LibcxxUniquePtrSyntheticFrontEndCreator(CXXSyntheticChildren *,lldb::ValueObjectSP valobj_sp)653 lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEndCreator(
654 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
655 return (valobj_sp ? new LibcxxUniquePtrSyntheticFrontEnd(valobj_sp)
656 : nullptr);
657 }
658
659 size_t lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::
CalculateNumChildren()660 CalculateNumChildren() {
661 return (m_value_ptr_sp ? 1 : 0);
662 }
663
664 lldb::ValueObjectSP
GetChildAtIndex(size_t idx)665 lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::GetChildAtIndex(
666 size_t idx) {
667 if (!m_value_ptr_sp)
668 return lldb::ValueObjectSP();
669
670 if (idx == 0)
671 return m_value_ptr_sp;
672
673 if (idx == 1) {
674 Status status;
675 auto value_sp = m_value_ptr_sp->Dereference(status);
676 if (status.Success()) {
677 return value_sp;
678 }
679 }
680
681 return lldb::ValueObjectSP();
682 }
683
Update()684 bool lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::Update() {
685 ValueObjectSP valobj_sp = m_backend.GetSP();
686 if (!valobj_sp)
687 return false;
688
689 ValueObjectSP ptr_sp(
690 valobj_sp->GetChildMemberWithName(ConstString("__ptr_"), true));
691 if (!ptr_sp)
692 return false;
693
694 m_value_ptr_sp = GetValueOfLibCXXCompressedPair(*ptr_sp);
695
696 return false;
697 }
698
699 bool lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::
MightHaveChildren()700 MightHaveChildren() {
701 return true;
702 }
703
704 size_t lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::
GetIndexOfChildWithName(ConstString name)705 GetIndexOfChildWithName(ConstString name) {
706 if (name == "__value_")
707 return 0;
708 if (name == "$$dereference$$")
709 return 1;
710 return UINT32_MAX;
711 }
712
LibcxxContainerSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)713 bool lldb_private::formatters::LibcxxContainerSummaryProvider(
714 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
715 if (valobj.IsPointerType()) {
716 uint64_t value = valobj.GetValueAsUnsigned(0);
717 if (!value)
718 return false;
719 stream.Printf("0x%016" PRIx64 " ", value);
720 }
721 return FormatEntity::FormatStringRef("size=${svar%#}", stream, nullptr,
722 nullptr, nullptr, &valobj, false, false);
723 }
724
725 /// The field layout in a libc++ string (cap, side, data or data, size, cap).
726 namespace {
727 enum class StringLayout { CSD, DSC };
728 }
729
730 /// Determine the size in bytes of \p valobj (a libc++ std::string object) and
731 /// extract its data payload. Return the size + payload pair.
732 // TODO: Support big-endian architectures.
733 static std::optional<std::pair<uint64_t, ValueObjectSP>>
ExtractLibcxxStringInfo(ValueObject & valobj)734 ExtractLibcxxStringInfo(ValueObject &valobj) {
735 ValueObjectSP valobj_r_sp =
736 valobj.GetChildMemberWithName(ConstString("__r_"), /*can_create=*/true);
737 if (!valobj_r_sp || !valobj_r_sp->GetError().Success())
738 return {};
739
740 // __r_ is a compressed_pair of the actual data and the allocator. The data we
741 // want is in the first base class.
742 ValueObjectSP valobj_r_base_sp =
743 valobj_r_sp->GetChildAtIndex(0, /*can_create=*/true);
744 if (!valobj_r_base_sp)
745 return {};
746
747 ValueObjectSP valobj_rep_sp = valobj_r_base_sp->GetChildMemberWithName(
748 ConstString("__value_"), /*can_create=*/true);
749 if (!valobj_rep_sp)
750 return {};
751
752 ValueObjectSP l = valobj_rep_sp->GetChildMemberWithName(ConstString("__l"),
753 /*can_create=*/true);
754 if (!l)
755 return {};
756
757 StringLayout layout = l->GetIndexOfChildWithName(ConstString("__data_")) == 0
758 ? StringLayout::DSC
759 : StringLayout::CSD;
760
761 bool short_mode = false; // this means the string is in short-mode and the
762 // data is stored inline
763 bool using_bitmasks = true; // Whether the class uses bitmasks for the mode
764 // flag (pre-D123580).
765 uint64_t size;
766 uint64_t size_mode_value = 0;
767
768 ValueObjectSP short_sp = valobj_rep_sp->GetChildMemberWithName(
769 ConstString("__s"), /*can_create=*/true);
770 if (!short_sp)
771 return {};
772
773 ValueObjectSP is_long =
774 short_sp->GetChildMemberWithName(ConstString("__is_long_"), true);
775 ValueObjectSP size_sp =
776 short_sp->GetChildAtNamePath({ConstString("__size_")});
777 if (!size_sp)
778 return {};
779
780 if (is_long) {
781 using_bitmasks = false;
782 short_mode = !is_long->GetValueAsUnsigned(/*fail_value=*/0);
783 size = size_sp->GetValueAsUnsigned(/*fail_value=*/0);
784 } else {
785 // The string mode is encoded in the size field.
786 size_mode_value = size_sp->GetValueAsUnsigned(0);
787 uint8_t mode_mask = layout == StringLayout::DSC ? 0x80 : 1;
788 short_mode = (size_mode_value & mode_mask) == 0;
789 }
790
791 if (short_mode) {
792 ValueObjectSP location_sp =
793 short_sp->GetChildMemberWithName(ConstString("__data_"), true);
794 if (using_bitmasks)
795 size = (layout == StringLayout::DSC) ? size_mode_value
796 : ((size_mode_value >> 1) % 256);
797
798 // When the small-string optimization takes place, the data must fit in the
799 // inline string buffer (23 bytes on x86_64/Darwin). If it doesn't, it's
800 // likely that the string isn't initialized and we're reading garbage.
801 ExecutionContext exe_ctx(location_sp->GetExecutionContextRef());
802 const std::optional<uint64_t> max_bytes =
803 location_sp->GetCompilerType().GetByteSize(
804 exe_ctx.GetBestExecutionContextScope());
805 if (!max_bytes || size > *max_bytes || !location_sp)
806 return {};
807
808 return std::make_pair(size, location_sp);
809 }
810
811 // we can use the layout_decider object as the data pointer
812 ValueObjectSP location_sp =
813 l->GetChildMemberWithName(ConstString("__data_"), /*can_create=*/true);
814 ValueObjectSP size_vo =
815 l->GetChildMemberWithName(ConstString("__size_"), /*can_create=*/true);
816 ValueObjectSP capacity_vo =
817 l->GetChildMemberWithName(ConstString("__cap_"), /*can_create=*/true);
818 if (!size_vo || !location_sp || !capacity_vo)
819 return {};
820 size = size_vo->GetValueAsUnsigned(LLDB_INVALID_OFFSET);
821 uint64_t capacity = capacity_vo->GetValueAsUnsigned(LLDB_INVALID_OFFSET);
822 if (!using_bitmasks && layout == StringLayout::CSD)
823 capacity *= 2;
824 if (size == LLDB_INVALID_OFFSET || capacity == LLDB_INVALID_OFFSET ||
825 capacity < size)
826 return {};
827 return std::make_pair(size, location_sp);
828 }
829
830 static bool
LibcxxWStringSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & summary_options,ValueObjectSP location_sp,size_t size)831 LibcxxWStringSummaryProvider(ValueObject &valobj, Stream &stream,
832 const TypeSummaryOptions &summary_options,
833 ValueObjectSP location_sp, size_t size) {
834 if (size == 0) {
835 stream.Printf("L\"\"");
836 return true;
837 }
838 if (!location_sp)
839 return false;
840
841 StringPrinter::ReadBufferAndDumpToStreamOptions options(valobj);
842 if (summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryCapped) {
843 const auto max_size = valobj.GetTargetSP()->GetMaximumSizeOfStringSummary();
844 if (size > max_size) {
845 size = max_size;
846 options.SetIsTruncated(true);
847 }
848 }
849
850 DataExtractor extractor;
851 const size_t bytes_read = location_sp->GetPointeeData(extractor, 0, size);
852 if (bytes_read < size)
853 return false;
854
855 // std::wstring::size() is measured in 'characters', not bytes
856 TypeSystemClangSP scratch_ts_sp =
857 ScratchTypeSystemClang::GetForTarget(*valobj.GetTargetSP());
858 if (!scratch_ts_sp)
859 return false;
860
861 auto wchar_t_size =
862 scratch_ts_sp->GetBasicType(lldb::eBasicTypeWChar).GetByteSize(nullptr);
863 if (!wchar_t_size)
864 return false;
865
866 options.SetData(std::move(extractor));
867 options.SetStream(&stream);
868 options.SetPrefixToken("L");
869 options.SetQuote('"');
870 options.SetSourceSize(size);
871 options.SetBinaryZeroIsTerminator(false);
872
873 switch (*wchar_t_size) {
874 case 1:
875 return StringPrinter::ReadBufferAndDumpToStream<
876 lldb_private::formatters::StringPrinter::StringElementType::UTF8>(
877 options);
878 break;
879
880 case 2:
881 return StringPrinter::ReadBufferAndDumpToStream<
882 lldb_private::formatters::StringPrinter::StringElementType::UTF16>(
883 options);
884 break;
885
886 case 4:
887 return StringPrinter::ReadBufferAndDumpToStream<
888 lldb_private::formatters::StringPrinter::StringElementType::UTF32>(
889 options);
890 }
891 return false;
892 }
893
LibcxxWStringSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & summary_options)894 bool lldb_private::formatters::LibcxxWStringSummaryProvider(
895 ValueObject &valobj, Stream &stream,
896 const TypeSummaryOptions &summary_options) {
897 auto string_info = ExtractLibcxxStringInfo(valobj);
898 if (!string_info)
899 return false;
900 uint64_t size;
901 ValueObjectSP location_sp;
902 std::tie(size, location_sp) = *string_info;
903
904 return ::LibcxxWStringSummaryProvider(valobj, stream, summary_options,
905 location_sp, size);
906 }
907
908 template <StringPrinter::StringElementType element_type>
909 static bool
LibcxxStringSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & summary_options,std::string prefix_token,ValueObjectSP location_sp,uint64_t size)910 LibcxxStringSummaryProvider(ValueObject &valobj, Stream &stream,
911 const TypeSummaryOptions &summary_options,
912 std::string prefix_token, ValueObjectSP location_sp,
913 uint64_t size) {
914
915 if (size == 0) {
916 stream.Printf("\"\"");
917 return true;
918 }
919
920 if (!location_sp)
921 return false;
922
923 StringPrinter::ReadBufferAndDumpToStreamOptions options(valobj);
924
925 if (summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryCapped) {
926 const auto max_size = valobj.GetTargetSP()->GetMaximumSizeOfStringSummary();
927 if (size > max_size) {
928 size = max_size;
929 options.SetIsTruncated(true);
930 }
931 }
932
933 {
934 DataExtractor extractor;
935 const size_t bytes_read = location_sp->GetPointeeData(extractor, 0, size);
936 if (bytes_read < size)
937 return false;
938
939 options.SetData(std::move(extractor));
940 }
941 options.SetStream(&stream);
942 if (prefix_token.empty())
943 options.SetPrefixToken(nullptr);
944 else
945 options.SetPrefixToken(prefix_token);
946 options.SetQuote('"');
947 options.SetSourceSize(size);
948 options.SetBinaryZeroIsTerminator(false);
949 return StringPrinter::ReadBufferAndDumpToStream<element_type>(options);
950 }
951
952 template <StringPrinter::StringElementType element_type>
953 static bool
LibcxxStringSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & summary_options,std::string prefix_token)954 LibcxxStringSummaryProvider(ValueObject &valobj, Stream &stream,
955 const TypeSummaryOptions &summary_options,
956 std::string prefix_token) {
957 auto string_info = ExtractLibcxxStringInfo(valobj);
958 if (!string_info)
959 return false;
960 uint64_t size;
961 ValueObjectSP location_sp;
962 std::tie(size, location_sp) = *string_info;
963
964 return LibcxxStringSummaryProvider<element_type>(
965 valobj, stream, summary_options, prefix_token, location_sp, size);
966 }
967 template <StringPrinter::StringElementType element_type>
formatStringImpl(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & summary_options,std::string prefix_token)968 static bool formatStringImpl(ValueObject &valobj, Stream &stream,
969 const TypeSummaryOptions &summary_options,
970 std::string prefix_token) {
971 StreamString scratch_stream;
972 const bool success = LibcxxStringSummaryProvider<element_type>(
973 valobj, scratch_stream, summary_options, prefix_token);
974 if (success)
975 stream << scratch_stream.GetData();
976 else
977 stream << "Summary Unavailable";
978 return true;
979 }
980
LibcxxStringSummaryProviderASCII(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & summary_options)981 bool lldb_private::formatters::LibcxxStringSummaryProviderASCII(
982 ValueObject &valobj, Stream &stream,
983 const TypeSummaryOptions &summary_options) {
984 return formatStringImpl<StringPrinter::StringElementType::ASCII>(
985 valobj, stream, summary_options, "");
986 }
987
LibcxxStringSummaryProviderUTF16(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & summary_options)988 bool lldb_private::formatters::LibcxxStringSummaryProviderUTF16(
989 ValueObject &valobj, Stream &stream,
990 const TypeSummaryOptions &summary_options) {
991 return formatStringImpl<StringPrinter::StringElementType::UTF16>(
992 valobj, stream, summary_options, "u");
993 }
994
LibcxxStringSummaryProviderUTF32(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & summary_options)995 bool lldb_private::formatters::LibcxxStringSummaryProviderUTF32(
996 ValueObject &valobj, Stream &stream,
997 const TypeSummaryOptions &summary_options) {
998 return formatStringImpl<StringPrinter::StringElementType::UTF32>(
999 valobj, stream, summary_options, "U");
1000 }
1001
1002 static std::tuple<bool, ValueObjectSP, size_t>
LibcxxExtractStringViewData(ValueObject & valobj)1003 LibcxxExtractStringViewData(ValueObject& valobj) {
1004 auto dataobj = GetChildMemberWithName(
1005 valobj, {ConstString("__data_"), ConstString("__data")});
1006 auto sizeobj = GetChildMemberWithName(
1007 valobj, {ConstString("__size_"), ConstString("__size")});
1008 if (!dataobj || !sizeobj)
1009 return std::make_tuple<bool,ValueObjectSP,size_t>(false, {}, {});
1010
1011 if (!dataobj->GetError().Success() || !sizeobj->GetError().Success())
1012 return std::make_tuple<bool,ValueObjectSP,size_t>(false, {}, {});
1013
1014 bool success{false};
1015 uint64_t size = sizeobj->GetValueAsUnsigned(0, &success);
1016 if (!success)
1017 return std::make_tuple<bool,ValueObjectSP,size_t>(false, {}, {});
1018
1019 return std::make_tuple(true,dataobj,size);
1020 }
1021
1022 template <StringPrinter::StringElementType element_type>
formatStringViewImpl(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & summary_options,std::string prefix_token)1023 static bool formatStringViewImpl(ValueObject &valobj, Stream &stream,
1024 const TypeSummaryOptions &summary_options,
1025 std::string prefix_token) {
1026
1027 bool success;
1028 ValueObjectSP dataobj;
1029 size_t size;
1030 std::tie(success, dataobj, size) = LibcxxExtractStringViewData(valobj);
1031
1032 if (!success) {
1033 stream << "Summary Unavailable";
1034 return true;
1035 }
1036
1037 return LibcxxStringSummaryProvider<element_type>(
1038 valobj, stream, summary_options, prefix_token, dataobj, size);
1039 }
1040
LibcxxStringViewSummaryProviderASCII(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & summary_options)1041 bool lldb_private::formatters::LibcxxStringViewSummaryProviderASCII(
1042 ValueObject &valobj, Stream &stream,
1043 const TypeSummaryOptions &summary_options) {
1044 return formatStringViewImpl<StringPrinter::StringElementType::ASCII>(
1045 valobj, stream, summary_options, "");
1046 }
1047
LibcxxStringViewSummaryProviderUTF16(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & summary_options)1048 bool lldb_private::formatters::LibcxxStringViewSummaryProviderUTF16(
1049 ValueObject &valobj, Stream &stream,
1050 const TypeSummaryOptions &summary_options) {
1051 return formatStringViewImpl<StringPrinter::StringElementType::UTF16>(
1052 valobj, stream, summary_options, "u");
1053 }
1054
LibcxxStringViewSummaryProviderUTF32(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & summary_options)1055 bool lldb_private::formatters::LibcxxStringViewSummaryProviderUTF32(
1056 ValueObject &valobj, Stream &stream,
1057 const TypeSummaryOptions &summary_options) {
1058 return formatStringViewImpl<StringPrinter::StringElementType::UTF32>(
1059 valobj, stream, summary_options, "U");
1060 }
1061
LibcxxWStringViewSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & summary_options)1062 bool lldb_private::formatters::LibcxxWStringViewSummaryProvider(
1063 ValueObject &valobj, Stream &stream,
1064 const TypeSummaryOptions &summary_options) {
1065
1066 bool success;
1067 ValueObjectSP dataobj;
1068 size_t size;
1069 std::tie( success, dataobj, size ) = LibcxxExtractStringViewData(valobj);
1070
1071 if (!success) {
1072 stream << "Summary Unavailable";
1073 return true;
1074 }
1075
1076
1077 return ::LibcxxWStringSummaryProvider(valobj, stream, summary_options,
1078 dataobj, size);
1079 }
1080