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