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