1 //===-- LibCxx.cpp ----------------------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "LibCxx.h"
11 
12 #include "llvm/ADT/ScopeExit.h"
13 #include "lldb/Core/Debugger.h"
14 #include "lldb/Core/FormatEntity.h"
15 #include "lldb/Core/ValueObject.h"
16 #include "lldb/Core/ValueObjectConstResult.h"
17 #include "lldb/DataFormatters/FormattersHelpers.h"
18 #include "lldb/DataFormatters/StringPrinter.h"
19 #include "lldb/DataFormatters/TypeSummary.h"
20 #include "lldb/DataFormatters/VectorIterator.h"
21 #include "lldb/Symbol/ClangASTContext.h"
22 #include "lldb/Target/CPPLanguageRuntime.h"
23 #include "lldb/Target/ProcessStructReader.h"
24 #include "lldb/Target/SectionLoadList.h"
25 #include "lldb/Target/Target.h"
26 #include "lldb/Utility/DataBufferHeap.h"
27 #include "lldb/Utility/Endian.h"
28 #include "lldb/Utility/Status.h"
29 #include "lldb/Utility/Stream.h"
30 
31 using namespace lldb;
32 using namespace lldb_private;
33 using namespace lldb_private::formatters;
34 
35 bool lldb_private::formatters::LibcxxOptionalSummaryProvider(
36     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
37   ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue());
38   if (!valobj_sp)
39     return false;
40 
41   // An optional either contains a value or not, the member __engaged_ is
42   // a bool flag, it is true if the optional has a value and false otherwise.
43   ValueObjectSP engaged_sp(
44       valobj_sp->GetChildMemberWithName(ConstString("__engaged_"), true));
45 
46   if (!engaged_sp)
47     return false;
48 
49   llvm::StringRef engaged_as_cstring(
50       engaged_sp->GetValueAsUnsigned(0) == 1 ? "true" : "false");
51 
52   stream.Printf(" Has Value=%s ", engaged_as_cstring.data());
53 
54   return true;
55 }
56 
57 bool lldb_private::formatters::LibcxxFunctionSummaryProvider(
58     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
59 
60   ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue());
61 
62   if (!valobj_sp)
63     return false;
64 
65   ExecutionContext exe_ctx(valobj_sp->GetExecutionContextRef());
66   Process *process = exe_ctx.GetProcessPtr();
67 
68   if (process == nullptr)
69     return false;
70 
71   CPPLanguageRuntime *cpp_runtime = process->GetCPPLanguageRuntime();
72 
73   if (!cpp_runtime)
74     return false;
75 
76   CPPLanguageRuntime::LibCppStdFunctionCallableInfo callable_info =
77       cpp_runtime->FindLibCppStdFunctionCallableInfo(valobj_sp);
78 
79   switch (callable_info.callable_case) {
80   case CPPLanguageRuntime::LibCppStdFunctionCallableCase::Invalid:
81     stream.Printf(" __f_ = %" PRIu64, callable_info.member__f_pointer_value);
82     return false;
83     break;
84   case CPPLanguageRuntime::LibCppStdFunctionCallableCase::Lambda:
85     stream.Printf(
86         " Lambda in File %s at Line %u",
87         callable_info.callable_line_entry.file.GetFilename().GetCString(),
88         callable_info.callable_line_entry.line);
89     break;
90   case CPPLanguageRuntime::LibCppStdFunctionCallableCase::CallableObject:
91     stream.Printf(
92         " Function in File %s at Line %u",
93         callable_info.callable_line_entry.file.GetFilename().GetCString(),
94         callable_info.callable_line_entry.line);
95     break;
96   case CPPLanguageRuntime::LibCppStdFunctionCallableCase::FreeOrMemberFunction:
97     stream.Printf(" Function = %s ",
98                   callable_info.callable_symbol.GetName().GetCString());
99     break;
100   }
101 
102   return true;
103 }
104 
105 bool lldb_private::formatters::LibcxxSmartPointerSummaryProvider(
106     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
107   ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue());
108   if (!valobj_sp)
109     return false;
110   ValueObjectSP ptr_sp(
111       valobj_sp->GetChildMemberWithName(ConstString("__ptr_"), true));
112   ValueObjectSP count_sp(valobj_sp->GetChildAtNamePath(
113       {ConstString("__cntrl_"), ConstString("__shared_owners_")}));
114   ValueObjectSP weakcount_sp(valobj_sp->GetChildAtNamePath(
115       {ConstString("__cntrl_"), ConstString("__shared_weak_owners_")}));
116 
117   if (!ptr_sp)
118     return false;
119 
120   if (ptr_sp->GetValueAsUnsigned(0) == 0) {
121     stream.Printf("nullptr");
122     return true;
123   } else {
124     bool print_pointee = false;
125     Status error;
126     ValueObjectSP pointee_sp = ptr_sp->Dereference(error);
127     if (pointee_sp && error.Success()) {
128       if (pointee_sp->DumpPrintableRepresentation(
129               stream, ValueObject::eValueObjectRepresentationStyleSummary,
130               lldb::eFormatInvalid,
131               ValueObject::PrintableRepresentationSpecialCases::eDisable,
132               false))
133         print_pointee = true;
134     }
135     if (!print_pointee)
136       stream.Printf("ptr = 0x%" PRIx64, ptr_sp->GetValueAsUnsigned(0));
137   }
138 
139   if (count_sp)
140     stream.Printf(" strong=%" PRIu64, 1 + count_sp->GetValueAsUnsigned(0));
141 
142   if (weakcount_sp)
143     stream.Printf(" weak=%" PRIu64, 1 + weakcount_sp->GetValueAsUnsigned(0));
144 
145   return true;
146 }
147 
148 /*
149  (lldb) fr var ibeg --raw --ptr-depth 1
150  (std::__1::__map_iterator<std::__1::__tree_iterator<std::__1::pair<int,
151  std::__1::basic_string<char, std::__1::char_traits<char>,
152  std::__1::allocator<char> > >, std::__1::__tree_node<std::__1::pair<int,
153  std::__1::basic_string<char, std::__1::char_traits<char>,
154  std::__1::allocator<char> > >, void *> *, long> >) ibeg = {
155  __i_ = {
156  __ptr_ = 0x0000000100103870 {
157  std::__1::__tree_node_base<void *> = {
158  std::__1::__tree_end_node<std::__1::__tree_node_base<void *> *> = {
159  __left_ = 0x0000000000000000
160  }
161  __right_ = 0x0000000000000000
162  __parent_ = 0x00000001001038b0
163  __is_black_ = true
164  }
165  __value_ = {
166  first = 0
167  second = { std::string }
168  */
169 
170 lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::
171     LibCxxMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
172     : SyntheticChildrenFrontEnd(*valobj_sp), m_pair_ptr(), m_pair_sp() {
173   if (valobj_sp)
174     Update();
175 }
176 
177 bool lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::Update() {
178   m_pair_sp.reset();
179   m_pair_ptr = nullptr;
180 
181   ValueObjectSP valobj_sp = m_backend.GetSP();
182   if (!valobj_sp)
183     return false;
184 
185   TargetSP target_sp(valobj_sp->GetTargetSP());
186 
187   if (!target_sp)
188     return false;
189 
190   if (!valobj_sp)
191     return false;
192 
193   static ConstString g___i_("__i_");
194 
195   // this must be a ValueObject* because it is a child of the ValueObject we
196   // are producing children for it if were a ValueObjectSP, we would end up
197   // with a loop (iterator -> synthetic -> child -> parent == iterator) and
198   // that would in turn leak memory by never allowing the ValueObjects to die
199   // and free their memory
200   m_pair_ptr = valobj_sp
201                    ->GetValueForExpressionPath(
202                        ".__i_.__ptr_->__value_", nullptr, nullptr,
203                        ValueObject::GetValueForExpressionPathOptions()
204                            .DontCheckDotVsArrowSyntax()
205                            .SetSyntheticChildrenTraversal(
206                                ValueObject::GetValueForExpressionPathOptions::
207                                    SyntheticChildrenTraversal::None),
208                        nullptr)
209                    .get();
210 
211   if (!m_pair_ptr) {
212     m_pair_ptr = valobj_sp
213                      ->GetValueForExpressionPath(
214                          ".__i_.__ptr_", nullptr, nullptr,
215                          ValueObject::GetValueForExpressionPathOptions()
216                              .DontCheckDotVsArrowSyntax()
217                              .SetSyntheticChildrenTraversal(
218                                  ValueObject::GetValueForExpressionPathOptions::
219                                      SyntheticChildrenTraversal::None),
220                          nullptr)
221                      .get();
222     if (m_pair_ptr) {
223       auto __i_(valobj_sp->GetChildMemberWithName(g___i_, true));
224       if (!__i_) {
225         m_pair_ptr = nullptr;
226         return false;
227       }
228       CompilerType pair_type(__i_->GetCompilerType().GetTypeTemplateArgument(0));
229       std::string name; uint64_t bit_offset_ptr; uint32_t bitfield_bit_size_ptr; bool is_bitfield_ptr;
230       pair_type = pair_type.GetFieldAtIndex(0, name, &bit_offset_ptr, &bitfield_bit_size_ptr, &is_bitfield_ptr);
231       if (!pair_type) {
232         m_pair_ptr = nullptr;
233         return false;
234       }
235 
236       auto addr(m_pair_ptr->GetValueAsUnsigned(LLDB_INVALID_ADDRESS));
237       m_pair_ptr = nullptr;
238       if (addr && addr!=LLDB_INVALID_ADDRESS) {
239         ClangASTContext *ast_ctx = llvm::dyn_cast_or_null<ClangASTContext>(pair_type.GetTypeSystem());
240         if (!ast_ctx)
241           return false;
242         CompilerType tree_node_type = ast_ctx->CreateStructForIdentifier(ConstString(), {
243           {"ptr0",ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()},
244           {"ptr1",ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()},
245           {"ptr2",ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()},
246           {"cw",ast_ctx->GetBasicType(lldb::eBasicTypeBool)},
247           {"payload",pair_type}
248         });
249         DataBufferSP buffer_sp(new DataBufferHeap(tree_node_type.GetByteSize(nullptr),0));
250         ProcessSP process_sp(target_sp->GetProcessSP());
251         Status error;
252         process_sp->ReadMemory(addr, buffer_sp->GetBytes(), buffer_sp->GetByteSize(), error);
253         if (error.Fail())
254           return false;
255         DataExtractor extractor(buffer_sp, process_sp->GetByteOrder(), process_sp->GetAddressByteSize());
256         auto pair_sp = CreateValueObjectFromData("pair", extractor, valobj_sp->GetExecutionContextRef(), tree_node_type);
257         if (pair_sp)
258           m_pair_sp = pair_sp->GetChildAtIndex(4,true);
259       }
260     }
261   }
262 
263   return false;
264 }
265 
266 size_t lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::
267     CalculateNumChildren() {
268   return 2;
269 }
270 
271 lldb::ValueObjectSP
272 lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::GetChildAtIndex(
273     size_t idx) {
274   if (m_pair_ptr)
275     return m_pair_ptr->GetChildAtIndex(idx, true);
276   if (m_pair_sp)
277     return m_pair_sp->GetChildAtIndex(idx, true);
278   return lldb::ValueObjectSP();
279 }
280 
281 bool lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::
282     MightHaveChildren() {
283   return true;
284 }
285 
286 size_t lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::
287     GetIndexOfChildWithName(const ConstString &name) {
288   if (name == ConstString("first"))
289     return 0;
290   if (name == ConstString("second"))
291     return 1;
292   return UINT32_MAX;
293 }
294 
295 lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::
296     ~LibCxxMapIteratorSyntheticFrontEnd() {
297   // this will be deleted when its parent dies (since it's a child object)
298   // delete m_pair_ptr;
299 }
300 
301 SyntheticChildrenFrontEnd *
302 lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEndCreator(
303     CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
304   return (valobj_sp ? new LibCxxMapIteratorSyntheticFrontEnd(valobj_sp)
305                     : nullptr);
306 }
307 
308 /*
309  (lldb) fr var ibeg --raw --ptr-depth 1 -T
310  (std::__1::__wrap_iter<int *>) ibeg = {
311  (std::__1::__wrap_iter<int *>::iterator_type) __i = 0x00000001001037a0 {
312  (int) *__i = 1
313  }
314  }
315 */
316 
317 SyntheticChildrenFrontEnd *
318 lldb_private::formatters::LibCxxVectorIteratorSyntheticFrontEndCreator(
319     CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
320   static ConstString g_item_name;
321   if (!g_item_name)
322     g_item_name.SetCString("__i");
323   return (valobj_sp
324               ? new VectorIteratorSyntheticFrontEnd(valobj_sp, g_item_name)
325               : nullptr);
326 }
327 
328 lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::
329     LibcxxSharedPtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
330     : SyntheticChildrenFrontEnd(*valobj_sp), m_cntrl(nullptr), m_count_sp(),
331       m_weak_count_sp(), m_ptr_size(0), m_byte_order(lldb::eByteOrderInvalid) {
332   if (valobj_sp)
333     Update();
334 }
335 
336 size_t lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::
337     CalculateNumChildren() {
338   return (m_cntrl ? 1 : 0);
339 }
340 
341 lldb::ValueObjectSP
342 lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::GetChildAtIndex(
343     size_t idx) {
344   if (!m_cntrl)
345     return lldb::ValueObjectSP();
346 
347   ValueObjectSP valobj_sp = m_backend.GetSP();
348   if (!valobj_sp)
349     return lldb::ValueObjectSP();
350 
351   if (idx == 0)
352     return valobj_sp->GetChildMemberWithName(ConstString("__ptr_"), true);
353 
354   if (idx > 2)
355     return lldb::ValueObjectSP();
356 
357   if (idx == 1) {
358     if (!m_count_sp) {
359       ValueObjectSP shared_owners_sp(m_cntrl->GetChildMemberWithName(
360           ConstString("__shared_owners_"), true));
361       if (!shared_owners_sp)
362         return lldb::ValueObjectSP();
363       uint64_t count = 1 + shared_owners_sp->GetValueAsUnsigned(0);
364       DataExtractor data(&count, 8, m_byte_order, m_ptr_size);
365       m_count_sp = CreateValueObjectFromData(
366           "count", data, valobj_sp->GetExecutionContextRef(),
367           shared_owners_sp->GetCompilerType());
368     }
369     return m_count_sp;
370   } else /* if (idx == 2) */
371   {
372     if (!m_weak_count_sp) {
373       ValueObjectSP shared_weak_owners_sp(m_cntrl->GetChildMemberWithName(
374           ConstString("__shared_weak_owners_"), true));
375       if (!shared_weak_owners_sp)
376         return lldb::ValueObjectSP();
377       uint64_t count = 1 + shared_weak_owners_sp->GetValueAsUnsigned(0);
378       DataExtractor data(&count, 8, m_byte_order, m_ptr_size);
379       m_weak_count_sp = CreateValueObjectFromData(
380           "count", data, valobj_sp->GetExecutionContextRef(),
381           shared_weak_owners_sp->GetCompilerType());
382     }
383     return m_weak_count_sp;
384   }
385 }
386 
387 bool lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::Update() {
388   m_count_sp.reset();
389   m_weak_count_sp.reset();
390   m_cntrl = nullptr;
391 
392   ValueObjectSP valobj_sp = m_backend.GetSP();
393   if (!valobj_sp)
394     return false;
395 
396   TargetSP target_sp(valobj_sp->GetTargetSP());
397   if (!target_sp)
398     return false;
399 
400   m_byte_order = target_sp->GetArchitecture().GetByteOrder();
401   m_ptr_size = target_sp->GetArchitecture().GetAddressByteSize();
402 
403   lldb::ValueObjectSP cntrl_sp(
404       valobj_sp->GetChildMemberWithName(ConstString("__cntrl_"), true));
405 
406   m_cntrl = cntrl_sp.get(); // need to store the raw pointer to avoid a circular
407                             // dependency
408   return false;
409 }
410 
411 bool lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::
412     MightHaveChildren() {
413   return true;
414 }
415 
416 size_t lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::
417     GetIndexOfChildWithName(const ConstString &name) {
418   if (name == ConstString("__ptr_"))
419     return 0;
420   if (name == ConstString("count"))
421     return 1;
422   if (name == ConstString("weak_count"))
423     return 2;
424   return UINT32_MAX;
425 }
426 
427 lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::
428     ~LibcxxSharedPtrSyntheticFrontEnd() = default;
429 
430 SyntheticChildrenFrontEnd *
431 lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEndCreator(
432     CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
433   return (valobj_sp ? new LibcxxSharedPtrSyntheticFrontEnd(valobj_sp)
434                     : nullptr);
435 }
436 
437 bool lldb_private::formatters::LibcxxContainerSummaryProvider(
438     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
439   if (valobj.IsPointerType()) {
440     uint64_t value = valobj.GetValueAsUnsigned(0);
441     if (!value)
442       return false;
443     stream.Printf("0x%016" PRIx64 " ", value);
444   }
445   return FormatEntity::FormatStringRef("size=${svar%#}", stream, nullptr,
446                                        nullptr, nullptr, &valobj, false, false);
447 }
448 
449 // the field layout in a libc++ string (cap, side, data or data, size, cap)
450 enum LibcxxStringLayoutMode {
451   eLibcxxStringLayoutModeCSD = 0,
452   eLibcxxStringLayoutModeDSC = 1,
453   eLibcxxStringLayoutModeInvalid = 0xffff
454 };
455 
456 // this function abstracts away the layout and mode details of a libc++ string
457 // and returns the address of the data and the size ready for callers to
458 // consume
459 static bool ExtractLibcxxStringInfo(ValueObject &valobj,
460                                     ValueObjectSP &location_sp,
461                                     uint64_t &size) {
462   ValueObjectSP D(valobj.GetChildAtIndexPath({0, 0, 0, 0}));
463   if (!D)
464     return false;
465 
466   ValueObjectSP layout_decider(
467     D->GetChildAtIndexPath(llvm::ArrayRef<size_t>({0, 0})));
468 
469   // this child should exist
470   if (!layout_decider)
471     return false;
472 
473   ConstString g_data_name("__data_");
474   ConstString g_size_name("__size_");
475   bool short_mode = false; // this means the string is in short-mode and the
476                            // data is stored inline
477   LibcxxStringLayoutMode layout = (layout_decider->GetName() == g_data_name)
478                                       ? eLibcxxStringLayoutModeDSC
479                                       : eLibcxxStringLayoutModeCSD;
480   uint64_t size_mode_value = 0;
481 
482   if (layout == eLibcxxStringLayoutModeDSC) {
483     ValueObjectSP size_mode(D->GetChildAtIndexPath({1, 1, 0}));
484     if (!size_mode)
485       return false;
486 
487     if (size_mode->GetName() != g_size_name) {
488       // we are hitting the padding structure, move along
489       size_mode = D->GetChildAtIndexPath({1, 1, 1});
490       if (!size_mode)
491         return false;
492     }
493 
494     size_mode_value = (size_mode->GetValueAsUnsigned(0));
495     short_mode = ((size_mode_value & 0x80) == 0);
496   } else {
497     ValueObjectSP size_mode(D->GetChildAtIndexPath({1, 0, 0}));
498     if (!size_mode)
499       return false;
500 
501     size_mode_value = (size_mode->GetValueAsUnsigned(0));
502     short_mode = ((size_mode_value & 1) == 0);
503   }
504 
505   if (short_mode) {
506     ValueObjectSP s(D->GetChildAtIndex(1, true));
507     if (!s)
508       return false;
509     location_sp = s->GetChildAtIndex(
510         (layout == eLibcxxStringLayoutModeDSC) ? 0 : 1, true);
511     size = (layout == eLibcxxStringLayoutModeDSC)
512                ? size_mode_value
513                : ((size_mode_value >> 1) % 256);
514     return (location_sp.get() != nullptr);
515   } else {
516     ValueObjectSP l(D->GetChildAtIndex(0, true));
517     if (!l)
518       return false;
519     // we can use the layout_decider object as the data pointer
520     location_sp = (layout == eLibcxxStringLayoutModeDSC)
521                       ? layout_decider
522                       : l->GetChildAtIndex(2, true);
523     ValueObjectSP size_vo(l->GetChildAtIndex(1, true));
524     if (!size_vo || !location_sp)
525       return false;
526     size = size_vo->GetValueAsUnsigned(0);
527     return true;
528   }
529 }
530 
531 bool lldb_private::formatters::LibcxxWStringSummaryProvider(
532     ValueObject &valobj, Stream &stream,
533     const TypeSummaryOptions &summary_options) {
534   uint64_t size = 0;
535   ValueObjectSP location_sp;
536   if (!ExtractLibcxxStringInfo(valobj, location_sp, size))
537     return false;
538   if (size == 0) {
539     stream.Printf("L\"\"");
540     return true;
541   }
542   if (!location_sp)
543     return false;
544 
545   DataExtractor extractor;
546 
547   StringPrinter::ReadBufferAndDumpToStreamOptions options(valobj);
548 
549   if (summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryCapped) {
550     const auto max_size = valobj.GetTargetSP()->GetMaximumSizeOfStringSummary();
551     if (size > max_size) {
552       size = max_size;
553       options.SetIsTruncated(true);
554     }
555   }
556   location_sp->GetPointeeData(extractor, 0, size);
557 
558   // std::wstring::size() is measured in 'characters', not bytes
559   auto wchar_t_size = valobj.GetTargetSP()
560                           ->GetScratchClangASTContext()
561                           ->GetBasicType(lldb::eBasicTypeWChar)
562                           .GetByteSize(nullptr);
563 
564   options.SetData(extractor);
565   options.SetStream(&stream);
566   options.SetPrefixToken("L");
567   options.SetQuote('"');
568   options.SetSourceSize(size);
569   options.SetBinaryZeroIsTerminator(false);
570 
571   switch (wchar_t_size) {
572   case 1:
573     StringPrinter::ReadBufferAndDumpToStream<
574         lldb_private::formatters::StringPrinter::StringElementType::UTF8>(
575         options);
576     break;
577 
578   case 2:
579     lldb_private::formatters::StringPrinter::ReadBufferAndDumpToStream<
580         lldb_private::formatters::StringPrinter::StringElementType::UTF16>(
581         options);
582     break;
583 
584   case 4:
585     lldb_private::formatters::StringPrinter::ReadBufferAndDumpToStream<
586         lldb_private::formatters::StringPrinter::StringElementType::UTF32>(
587         options);
588     break;
589 
590   default:
591     stream.Printf("size for wchar_t is not valid");
592     return true;
593   }
594 
595   return true;
596 }
597 
598 template <StringPrinter::StringElementType element_type>
599 bool LibcxxStringSummaryProvider(ValueObject &valobj, Stream &stream,
600                                  const TypeSummaryOptions &summary_options,
601                                  std::string prefix_token = "") {
602   uint64_t size = 0;
603   ValueObjectSP location_sp;
604 
605   if (!ExtractLibcxxStringInfo(valobj, location_sp, size))
606     return false;
607 
608   if (size == 0) {
609     stream.Printf("\"\"");
610     return true;
611   }
612 
613   if (!location_sp)
614     return false;
615 
616   StringPrinter::ReadBufferAndDumpToStreamOptions options(valobj);
617 
618   DataExtractor extractor;
619   if (summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryCapped) {
620     const auto max_size = valobj.GetTargetSP()->GetMaximumSizeOfStringSummary();
621     if (size > max_size) {
622       size = max_size;
623       options.SetIsTruncated(true);
624     }
625   }
626   location_sp->GetPointeeData(extractor, 0, size);
627 
628   options.SetData(extractor);
629   options.SetStream(&stream);
630 
631   if (prefix_token.empty())
632     options.SetPrefixToken(nullptr);
633   else
634     options.SetPrefixToken(prefix_token);
635 
636   options.SetQuote('"');
637   options.SetSourceSize(size);
638   options.SetBinaryZeroIsTerminator(false);
639   StringPrinter::ReadBufferAndDumpToStream<element_type>(options);
640 
641   return true;
642 }
643 
644 bool lldb_private::formatters::LibcxxStringSummaryProviderASCII(
645     ValueObject &valobj, Stream &stream,
646     const TypeSummaryOptions &summary_options) {
647   return LibcxxStringSummaryProvider<StringPrinter::StringElementType::ASCII>(
648       valobj, stream, summary_options);
649 }
650 
651 bool lldb_private::formatters::LibcxxStringSummaryProviderUTF16(
652     ValueObject &valobj, Stream &stream,
653     const TypeSummaryOptions &summary_options) {
654   return LibcxxStringSummaryProvider<StringPrinter::StringElementType::UTF16>(
655       valobj, stream, summary_options, "u");
656 }
657 
658 bool lldb_private::formatters::LibcxxStringSummaryProviderUTF32(
659     ValueObject &valobj, Stream &stream,
660     const TypeSummaryOptions &summary_options) {
661   return LibcxxStringSummaryProvider<StringPrinter::StringElementType::UTF32>(
662       valobj, stream, summary_options, "U");
663 }
664