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