xref: /freebsd/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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 
GetChildMemberWithName(ValueObject & obj,llvm::ArrayRef<ConstString> alternative_names)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
GetFirstValueOfLibCXXCompressedPair(ValueObject & pair)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
GetSecondValueOfLibCXXCompressedPair(ValueObject & pair)63 lldb_private::formatters::GetSecondValueOfLibCXXCompressedPair(
64     ValueObject &pair) {
65   ValueObjectSP value;
66   if (pair.GetNumChildrenIgnoringErrors() > 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 
LibcxxFunctionSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)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.GetFile().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.GetFile().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 
LibcxxSmartPointerSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)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 
LibcxxUniquePointerSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)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 -T
207  (std::__1::__wrap_iter<int *>) ibeg = {
208  (std::__1::__wrap_iter<int *>::iterator_type) __i = 0x00000001001037a0 {
209  (int) *__i = 1
210  }
211  }
212 */
213 
214 SyntheticChildrenFrontEnd *
LibCxxVectorIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *,lldb::ValueObjectSP valobj_sp)215 lldb_private::formatters::LibCxxVectorIteratorSyntheticFrontEndCreator(
216     CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
217   return (valobj_sp ? new VectorIteratorSyntheticFrontEnd(
218                           valobj_sp, {ConstString("__i_"), ConstString("__i")})
219                     : nullptr);
220 }
221 
222 lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::
LibcxxSharedPtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)223     LibcxxSharedPtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
224     : SyntheticChildrenFrontEnd(*valobj_sp), m_cntrl(nullptr) {
225   if (valobj_sp)
226     Update();
227 }
228 
229 llvm::Expected<uint32_t> lldb_private::formatters::
CalculateNumChildren()230     LibcxxSharedPtrSyntheticFrontEnd::CalculateNumChildren() {
231   return (m_cntrl ? 1 : 0);
232 }
233 
234 lldb::ValueObjectSP
GetChildAtIndex(uint32_t idx)235 lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::GetChildAtIndex(
236     uint32_t idx) {
237   if (!m_cntrl)
238     return lldb::ValueObjectSP();
239 
240   ValueObjectSP valobj_sp = m_backend.GetSP();
241   if (!valobj_sp)
242     return lldb::ValueObjectSP();
243 
244   if (idx == 0)
245     return valobj_sp->GetChildMemberWithName("__ptr_");
246 
247   if (idx == 1) {
248     if (auto ptr_sp = valobj_sp->GetChildMemberWithName("__ptr_")) {
249       Status status;
250       auto value_type_sp =
251             valobj_sp->GetCompilerType()
252               .GetTypeTemplateArgument(0).GetPointerType();
253       ValueObjectSP cast_ptr_sp = ptr_sp->Cast(value_type_sp);
254       ValueObjectSP value_sp = cast_ptr_sp->Dereference(status);
255       if (status.Success()) {
256         return value_sp;
257       }
258     }
259   }
260 
261   return lldb::ValueObjectSP();
262 }
263 
264 lldb::ChildCacheState
Update()265 lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::Update() {
266   m_cntrl = nullptr;
267 
268   ValueObjectSP valobj_sp = m_backend.GetSP();
269   if (!valobj_sp)
270     return lldb::ChildCacheState::eRefetch;
271 
272   TargetSP target_sp(valobj_sp->GetTargetSP());
273   if (!target_sp)
274     return lldb::ChildCacheState::eRefetch;
275 
276   lldb::ValueObjectSP cntrl_sp(valobj_sp->GetChildMemberWithName("__cntrl_"));
277 
278   m_cntrl = cntrl_sp.get(); // need to store the raw pointer to avoid a circular
279                             // dependency
280   return lldb::ChildCacheState::eRefetch;
281 }
282 
283 bool lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::
MightHaveChildren()284     MightHaveChildren() {
285   return true;
286 }
287 
288 size_t lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::
GetIndexOfChildWithName(ConstString name)289     GetIndexOfChildWithName(ConstString name) {
290   if (name == "__ptr_")
291     return 0;
292   if (name == "$$dereference$$")
293     return 1;
294   return UINT32_MAX;
295 }
296 
297 lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::
298     ~LibcxxSharedPtrSyntheticFrontEnd() = default;
299 
300 SyntheticChildrenFrontEnd *
LibcxxSharedPtrSyntheticFrontEndCreator(CXXSyntheticChildren *,lldb::ValueObjectSP valobj_sp)301 lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEndCreator(
302     CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
303   return (valobj_sp ? new LibcxxSharedPtrSyntheticFrontEnd(valobj_sp)
304                     : nullptr);
305 }
306 
307 lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::
LibcxxUniquePtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)308     LibcxxUniquePtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
309     : SyntheticChildrenFrontEnd(*valobj_sp) {
310   if (valobj_sp)
311     Update();
312 }
313 
314 lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::
315     ~LibcxxUniquePtrSyntheticFrontEnd() = default;
316 
317 SyntheticChildrenFrontEnd *
LibcxxUniquePtrSyntheticFrontEndCreator(CXXSyntheticChildren *,lldb::ValueObjectSP valobj_sp)318 lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEndCreator(
319     CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
320   return (valobj_sp ? new LibcxxUniquePtrSyntheticFrontEnd(valobj_sp)
321                     : nullptr);
322 }
323 
324 llvm::Expected<uint32_t> lldb_private::formatters::
CalculateNumChildren()325     LibcxxUniquePtrSyntheticFrontEnd::CalculateNumChildren() {
326   if (m_value_ptr_sp)
327     return m_deleter_sp ? 2 : 1;
328   return 0;
329 }
330 
331 lldb::ValueObjectSP
GetChildAtIndex(uint32_t idx)332 lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::GetChildAtIndex(
333     uint32_t idx) {
334   if (!m_value_ptr_sp)
335     return lldb::ValueObjectSP();
336 
337   if (idx == 0)
338     return m_value_ptr_sp;
339 
340   if (idx == 1)
341     return m_deleter_sp;
342 
343   if (idx == 2) {
344     Status status;
345     auto value_sp = m_value_ptr_sp->Dereference(status);
346     if (status.Success()) {
347       return value_sp;
348     }
349   }
350 
351   return lldb::ValueObjectSP();
352 }
353 
354 lldb::ChildCacheState
Update()355 lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::Update() {
356   ValueObjectSP valobj_sp = m_backend.GetSP();
357   if (!valobj_sp)
358     return lldb::ChildCacheState::eRefetch;
359 
360   ValueObjectSP ptr_sp(valobj_sp->GetChildMemberWithName("__ptr_"));
361   if (!ptr_sp)
362     return lldb::ChildCacheState::eRefetch;
363 
364   // Retrieve the actual pointer and the deleter, and clone them to give them
365   // user-friendly names.
366   ValueObjectSP value_pointer_sp = GetFirstValueOfLibCXXCompressedPair(*ptr_sp);
367   if (value_pointer_sp)
368     m_value_ptr_sp = value_pointer_sp->Clone(ConstString("pointer"));
369 
370   ValueObjectSP deleter_sp = GetSecondValueOfLibCXXCompressedPair(*ptr_sp);
371   if (deleter_sp)
372     m_deleter_sp = deleter_sp->Clone(ConstString("deleter"));
373 
374   return lldb::ChildCacheState::eRefetch;
375 }
376 
377 bool lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::
MightHaveChildren()378     MightHaveChildren() {
379   return true;
380 }
381 
382 size_t lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::
GetIndexOfChildWithName(ConstString name)383     GetIndexOfChildWithName(ConstString name) {
384   if (name == "pointer")
385     return 0;
386   if (name == "deleter")
387     return 1;
388   if (name == "$$dereference$$")
389     return 2;
390   return UINT32_MAX;
391 }
392 
LibcxxContainerSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)393 bool lldb_private::formatters::LibcxxContainerSummaryProvider(
394     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
395   if (valobj.IsPointerType()) {
396     uint64_t value = valobj.GetValueAsUnsigned(0);
397     if (!value)
398       return false;
399     stream.Printf("0x%016" PRIx64 " ", value);
400   }
401   return FormatEntity::FormatStringRef("size=${svar%#}", stream, nullptr,
402                                        nullptr, nullptr, &valobj, false, false);
403 }
404 
405 /// The field layout in a libc++ string (cap, side, data or data, size, cap).
406 namespace {
407 enum class StringLayout { CSD, DSC };
408 }
409 
410 /// Determine the size in bytes of \p valobj (a libc++ std::string object) and
411 /// extract its data payload. Return the size + payload pair.
412 // TODO: Support big-endian architectures.
413 static std::optional<std::pair<uint64_t, ValueObjectSP>>
ExtractLibcxxStringInfo(ValueObject & valobj)414 ExtractLibcxxStringInfo(ValueObject &valobj) {
415   ValueObjectSP valobj_r_sp = valobj.GetChildMemberWithName("__r_");
416   if (!valobj_r_sp || !valobj_r_sp->GetError().Success())
417     return {};
418 
419   // __r_ is a compressed_pair of the actual data and the allocator. The data we
420   // want is in the first base class.
421   ValueObjectSP valobj_r_base_sp = valobj_r_sp->GetChildAtIndex(0);
422   if (!valobj_r_base_sp)
423     return {};
424 
425   ValueObjectSP valobj_rep_sp =
426       valobj_r_base_sp->GetChildMemberWithName("__value_");
427   if (!valobj_rep_sp)
428     return {};
429 
430   ValueObjectSP l = valobj_rep_sp->GetChildMemberWithName("__l");
431   if (!l)
432     return {};
433 
434   StringLayout layout = l->GetIndexOfChildWithName("__data_") == 0
435                             ? StringLayout::DSC
436                             : StringLayout::CSD;
437 
438   bool short_mode = false; // this means the string is in short-mode and the
439                            // data is stored inline
440   bool using_bitmasks = true; // Whether the class uses bitmasks for the mode
441                               // flag (pre-D123580).
442   uint64_t size;
443   uint64_t size_mode_value = 0;
444 
445   ValueObjectSP short_sp = valobj_rep_sp->GetChildMemberWithName("__s");
446   if (!short_sp)
447     return {};
448 
449   ValueObjectSP is_long = short_sp->GetChildMemberWithName("__is_long_");
450   ValueObjectSP size_sp = short_sp->GetChildMemberWithName("__size_");
451   if (!size_sp)
452     return {};
453 
454   if (is_long) {
455     using_bitmasks = false;
456     short_mode = !is_long->GetValueAsUnsigned(/*fail_value=*/0);
457     size = size_sp->GetValueAsUnsigned(/*fail_value=*/0);
458   } else {
459     // The string mode is encoded in the size field.
460     size_mode_value = size_sp->GetValueAsUnsigned(0);
461     uint8_t mode_mask = layout == StringLayout::DSC ? 0x80 : 1;
462     short_mode = (size_mode_value & mode_mask) == 0;
463   }
464 
465   if (short_mode) {
466     ValueObjectSP location_sp = short_sp->GetChildMemberWithName("__data_");
467     if (using_bitmasks)
468       size = (layout == StringLayout::DSC) ? size_mode_value
469                                            : ((size_mode_value >> 1) % 256);
470 
471     if (!location_sp)
472       return {};
473 
474     // When the small-string optimization takes place, the data must fit in the
475     // inline string buffer (23 bytes on x86_64/Darwin). If it doesn't, it's
476     // likely that the string isn't initialized and we're reading garbage.
477     ExecutionContext exe_ctx(location_sp->GetExecutionContextRef());
478     const std::optional<uint64_t> max_bytes =
479         location_sp->GetCompilerType().GetByteSize(
480             exe_ctx.GetBestExecutionContextScope());
481     if (!max_bytes || size > *max_bytes)
482       return {};
483 
484     return std::make_pair(size, location_sp);
485   }
486 
487   // we can use the layout_decider object as the data pointer
488   ValueObjectSP location_sp = l->GetChildMemberWithName("__data_");
489   ValueObjectSP size_vo = l->GetChildMemberWithName("__size_");
490   ValueObjectSP capacity_vo = l->GetChildMemberWithName("__cap_");
491   if (!size_vo || !location_sp || !capacity_vo)
492     return {};
493   size = size_vo->GetValueAsUnsigned(LLDB_INVALID_OFFSET);
494   uint64_t capacity = capacity_vo->GetValueAsUnsigned(LLDB_INVALID_OFFSET);
495   if (!using_bitmasks && layout == StringLayout::CSD)
496     capacity *= 2;
497   if (size == LLDB_INVALID_OFFSET || capacity == LLDB_INVALID_OFFSET ||
498       capacity < size)
499     return {};
500   return std::make_pair(size, location_sp);
501 }
502 
503 static bool
LibcxxWStringSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & summary_options,ValueObjectSP location_sp,size_t size)504 LibcxxWStringSummaryProvider(ValueObject &valobj, Stream &stream,
505                              const TypeSummaryOptions &summary_options,
506                              ValueObjectSP location_sp, size_t size) {
507   if (size == 0) {
508     stream.Printf("L\"\"");
509     return true;
510   }
511   if (!location_sp)
512     return false;
513 
514   StringPrinter::ReadBufferAndDumpToStreamOptions options(valobj);
515   if (summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryCapped) {
516     const auto max_size = valobj.GetTargetSP()->GetMaximumSizeOfStringSummary();
517     if (size > max_size) {
518       size = max_size;
519       options.SetIsTruncated(true);
520     }
521   }
522 
523   DataExtractor extractor;
524   const size_t bytes_read = location_sp->GetPointeeData(extractor, 0, size);
525   if (bytes_read < size)
526     return false;
527 
528   // std::wstring::size() is measured in 'characters', not bytes
529   TypeSystemClangSP scratch_ts_sp =
530       ScratchTypeSystemClang::GetForTarget(*valobj.GetTargetSP());
531   if (!scratch_ts_sp)
532     return false;
533 
534   auto wchar_t_size =
535       scratch_ts_sp->GetBasicType(lldb::eBasicTypeWChar).GetByteSize(nullptr);
536   if (!wchar_t_size)
537     return false;
538 
539   options.SetData(std::move(extractor));
540   options.SetStream(&stream);
541   options.SetPrefixToken("L");
542   options.SetQuote('"');
543   options.SetSourceSize(size);
544   options.SetBinaryZeroIsTerminator(false);
545 
546   switch (*wchar_t_size) {
547   case 1:
548     return StringPrinter::ReadBufferAndDumpToStream<
549         lldb_private::formatters::StringPrinter::StringElementType::UTF8>(
550         options);
551     break;
552 
553   case 2:
554     return StringPrinter::ReadBufferAndDumpToStream<
555         lldb_private::formatters::StringPrinter::StringElementType::UTF16>(
556         options);
557     break;
558 
559   case 4:
560     return StringPrinter::ReadBufferAndDumpToStream<
561         lldb_private::formatters::StringPrinter::StringElementType::UTF32>(
562         options);
563   }
564   return false;
565 }
566 
LibcxxWStringSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & summary_options)567 bool lldb_private::formatters::LibcxxWStringSummaryProvider(
568     ValueObject &valobj, Stream &stream,
569     const TypeSummaryOptions &summary_options) {
570   auto string_info = ExtractLibcxxStringInfo(valobj);
571   if (!string_info)
572     return false;
573   uint64_t size;
574   ValueObjectSP location_sp;
575   std::tie(size, location_sp) = *string_info;
576 
577   return ::LibcxxWStringSummaryProvider(valobj, stream, summary_options,
578                                         location_sp, size);
579 }
580 
581 template <StringPrinter::StringElementType element_type>
582 static bool
LibcxxStringSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & summary_options,std::string prefix_token,ValueObjectSP location_sp,uint64_t size)583 LibcxxStringSummaryProvider(ValueObject &valobj, Stream &stream,
584                             const TypeSummaryOptions &summary_options,
585                             std::string prefix_token, ValueObjectSP location_sp,
586                             uint64_t size) {
587 
588   if (size == 0) {
589     stream.Printf("\"\"");
590     return true;
591   }
592 
593   if (!location_sp)
594     return false;
595 
596   StringPrinter::ReadBufferAndDumpToStreamOptions options(valobj);
597 
598   if (summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryCapped) {
599     const auto max_size = valobj.GetTargetSP()->GetMaximumSizeOfStringSummary();
600     if (size > max_size) {
601       size = max_size;
602       options.SetIsTruncated(true);
603     }
604   }
605 
606   {
607     DataExtractor extractor;
608     const size_t bytes_read = location_sp->GetPointeeData(extractor, 0, size);
609     if (bytes_read < size)
610       return false;
611 
612     options.SetData(std::move(extractor));
613   }
614   options.SetStream(&stream);
615   if (prefix_token.empty())
616     options.SetPrefixToken(nullptr);
617   else
618     options.SetPrefixToken(prefix_token);
619   options.SetQuote('"');
620   options.SetSourceSize(size);
621   options.SetBinaryZeroIsTerminator(false);
622   return StringPrinter::ReadBufferAndDumpToStream<element_type>(options);
623 }
624 
625 template <StringPrinter::StringElementType element_type>
626 static bool
LibcxxStringSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & summary_options,std::string prefix_token)627 LibcxxStringSummaryProvider(ValueObject &valobj, Stream &stream,
628                             const TypeSummaryOptions &summary_options,
629                             std::string prefix_token) {
630   auto string_info = ExtractLibcxxStringInfo(valobj);
631   if (!string_info)
632     return false;
633   uint64_t size;
634   ValueObjectSP location_sp;
635   std::tie(size, location_sp) = *string_info;
636 
637   return LibcxxStringSummaryProvider<element_type>(
638       valobj, stream, summary_options, prefix_token, location_sp, size);
639 }
640 template <StringPrinter::StringElementType element_type>
formatStringImpl(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & summary_options,std::string prefix_token)641 static bool formatStringImpl(ValueObject &valobj, Stream &stream,
642                              const TypeSummaryOptions &summary_options,
643                              std::string prefix_token) {
644   StreamString scratch_stream;
645   const bool success = LibcxxStringSummaryProvider<element_type>(
646       valobj, scratch_stream, summary_options, prefix_token);
647   if (success)
648     stream << scratch_stream.GetData();
649   else
650     stream << "Summary Unavailable";
651   return true;
652 }
653 
LibcxxStringSummaryProviderASCII(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & summary_options)654 bool lldb_private::formatters::LibcxxStringSummaryProviderASCII(
655     ValueObject &valobj, Stream &stream,
656     const TypeSummaryOptions &summary_options) {
657   return formatStringImpl<StringPrinter::StringElementType::ASCII>(
658       valobj, stream, summary_options, "");
659 }
660 
LibcxxStringSummaryProviderUTF16(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & summary_options)661 bool lldb_private::formatters::LibcxxStringSummaryProviderUTF16(
662     ValueObject &valobj, Stream &stream,
663     const TypeSummaryOptions &summary_options) {
664   return formatStringImpl<StringPrinter::StringElementType::UTF16>(
665       valobj, stream, summary_options, "u");
666 }
667 
LibcxxStringSummaryProviderUTF32(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & summary_options)668 bool lldb_private::formatters::LibcxxStringSummaryProviderUTF32(
669     ValueObject &valobj, Stream &stream,
670     const TypeSummaryOptions &summary_options) {
671   return formatStringImpl<StringPrinter::StringElementType::UTF32>(
672       valobj, stream, summary_options, "U");
673 }
674 
675 static std::tuple<bool, ValueObjectSP, size_t>
LibcxxExtractStringViewData(ValueObject & valobj)676 LibcxxExtractStringViewData(ValueObject& valobj) {
677   auto dataobj = GetChildMemberWithName(
678       valobj, {ConstString("__data_"), ConstString("__data")});
679   auto sizeobj = GetChildMemberWithName(
680       valobj, {ConstString("__size_"), ConstString("__size")});
681   if (!dataobj || !sizeobj)
682     return std::make_tuple<bool,ValueObjectSP,size_t>(false, {}, {});
683 
684   if (!dataobj->GetError().Success() || !sizeobj->GetError().Success())
685     return std::make_tuple<bool,ValueObjectSP,size_t>(false, {}, {});
686 
687   bool success{false};
688   uint64_t size = sizeobj->GetValueAsUnsigned(0, &success);
689   if (!success)
690     return std::make_tuple<bool,ValueObjectSP,size_t>(false, {}, {});
691 
692   return std::make_tuple(true,dataobj,size);
693 }
694 
695 template <StringPrinter::StringElementType element_type>
formatStringViewImpl(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & summary_options,std::string prefix_token)696 static bool formatStringViewImpl(ValueObject &valobj, Stream &stream,
697                                  const TypeSummaryOptions &summary_options,
698                                  std::string prefix_token) {
699 
700   bool success;
701   ValueObjectSP dataobj;
702   size_t size;
703   std::tie(success, dataobj, size) = LibcxxExtractStringViewData(valobj);
704 
705   if (!success) {
706     stream << "Summary Unavailable";
707     return true;
708   }
709 
710   return LibcxxStringSummaryProvider<element_type>(
711       valobj, stream, summary_options, prefix_token, dataobj, size);
712 }
713 
LibcxxStringViewSummaryProviderASCII(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & summary_options)714 bool lldb_private::formatters::LibcxxStringViewSummaryProviderASCII(
715     ValueObject &valobj, Stream &stream,
716     const TypeSummaryOptions &summary_options) {
717   return formatStringViewImpl<StringPrinter::StringElementType::ASCII>(
718       valobj, stream, summary_options, "");
719 }
720 
LibcxxStringViewSummaryProviderUTF16(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & summary_options)721 bool lldb_private::formatters::LibcxxStringViewSummaryProviderUTF16(
722     ValueObject &valobj, Stream &stream,
723     const TypeSummaryOptions &summary_options) {
724   return formatStringViewImpl<StringPrinter::StringElementType::UTF16>(
725       valobj, stream, summary_options, "u");
726 }
727 
LibcxxStringViewSummaryProviderUTF32(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & summary_options)728 bool lldb_private::formatters::LibcxxStringViewSummaryProviderUTF32(
729     ValueObject &valobj, Stream &stream,
730     const TypeSummaryOptions &summary_options) {
731   return formatStringViewImpl<StringPrinter::StringElementType::UTF32>(
732       valobj, stream, summary_options, "U");
733 }
734 
LibcxxWStringViewSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & summary_options)735 bool lldb_private::formatters::LibcxxWStringViewSummaryProvider(
736     ValueObject &valobj, Stream &stream,
737     const TypeSummaryOptions &summary_options) {
738 
739   bool success;
740   ValueObjectSP dataobj;
741   size_t size;
742   std::tie(success, dataobj, size) = LibcxxExtractStringViewData(valobj);
743 
744   if (!success) {
745     stream << "Summary Unavailable";
746     return true;
747   }
748 
749   return ::LibcxxWStringSummaryProvider(valobj, stream, summary_options,
750                                         dataobj, size);
751 }
752 
753 static bool
LibcxxChronoTimePointSecondsSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options,const char * fmt)754 LibcxxChronoTimePointSecondsSummaryProvider(ValueObject &valobj, Stream &stream,
755                                             const TypeSummaryOptions &options,
756                                             const char *fmt) {
757   ValueObjectSP ptr_sp = valobj.GetChildMemberWithName("__d_");
758   if (!ptr_sp)
759     return false;
760   ptr_sp = ptr_sp->GetChildMemberWithName("__rep_");
761   if (!ptr_sp)
762     return false;
763 
764 #ifndef _WIN32
765   // The date time in the chrono library is valid in the range
766   // [-32767-01-01T00:00:00Z, 32767-12-31T23:59:59Z]. A 64-bit time_t has a
767   // larger range, the function strftime is not able to format the entire range
768   // of time_t. The exact point has not been investigated; it's limited to
769   // chrono's range.
770   const std::time_t chrono_timestamp_min =
771       -1'096'193'779'200; // -32767-01-01T00:00:00Z
772   const std::time_t chrono_timestamp_max =
773       971'890'963'199; // 32767-12-31T23:59:59Z
774 #else
775   const std::time_t chrono_timestamp_min = -43'200; // 1969-12-31T12:00:00Z
776   const std::time_t chrono_timestamp_max =
777       32'536'850'399; // 3001-01-19T21:59:59
778 #endif
779 
780   const std::time_t seconds = ptr_sp->GetValueAsSigned(0);
781   if (seconds < chrono_timestamp_min || seconds > chrono_timestamp_max)
782     stream.Printf("timestamp=%" PRId64 " s", static_cast<int64_t>(seconds));
783   else {
784     std::array<char, 128> str;
785     std::size_t size =
786         std::strftime(str.data(), str.size(), fmt, gmtime(&seconds));
787     if (size == 0)
788       return false;
789 
790     stream.Printf("date/time=%s timestamp=%" PRId64 " s", str.data(),
791                   static_cast<int64_t>(seconds));
792   }
793 
794   return true;
795 }
796 
LibcxxChronoSysSecondsSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)797 bool lldb_private::formatters::LibcxxChronoSysSecondsSummaryProvider(
798     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
799   return LibcxxChronoTimePointSecondsSummaryProvider(valobj, stream, options,
800                                                      "%FT%H:%M:%SZ");
801 }
802 
LibcxxChronoLocalSecondsSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)803 bool lldb_private::formatters::LibcxxChronoLocalSecondsSummaryProvider(
804     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
805   return LibcxxChronoTimePointSecondsSummaryProvider(valobj, stream, options,
806                                                      "%FT%H:%M:%S");
807 }
808 
809 static bool
LibcxxChronoTimepointDaysSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options,const char * fmt)810 LibcxxChronoTimepointDaysSummaryProvider(ValueObject &valobj, Stream &stream,
811                                          const TypeSummaryOptions &options,
812                                          const char *fmt) {
813   ValueObjectSP ptr_sp = valobj.GetChildMemberWithName("__d_");
814   if (!ptr_sp)
815     return false;
816   ptr_sp = ptr_sp->GetChildMemberWithName("__rep_");
817   if (!ptr_sp)
818     return false;
819 
820 #ifndef _WIN32
821   // The date time in the chrono library is valid in the range
822   // [-32767-01-01Z, 32767-12-31Z]. A 32-bit time_t has a larger range, the
823   // function strftime is not able to format the entire range of time_t. The
824   // exact point has not been investigated; it's limited to chrono's range.
825   const int chrono_timestamp_min = -12'687'428; // -32767-01-01Z
826   const int chrono_timestamp_max = 11'248'737;  // 32767-12-31Z
827 #else
828   const int chrono_timestamp_min = 0;       // 1970-01-01Z
829   const int chrono_timestamp_max = 376'583; // 3001-01-19Z
830 #endif
831 
832   const int days = ptr_sp->GetValueAsSigned(0);
833   if (days < chrono_timestamp_min || days > chrono_timestamp_max)
834     stream.Printf("timestamp=%d days", days);
835 
836   else {
837     const std::time_t seconds = std::time_t(86400) * days;
838 
839     std::array<char, 128> str;
840     std::size_t size =
841         std::strftime(str.data(), str.size(), fmt, gmtime(&seconds));
842     if (size == 0)
843       return false;
844 
845     stream.Printf("date=%s timestamp=%d days", str.data(), days);
846   }
847 
848   return true;
849 }
850 
LibcxxChronoSysDaysSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)851 bool lldb_private::formatters::LibcxxChronoSysDaysSummaryProvider(
852     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
853   return LibcxxChronoTimepointDaysSummaryProvider(valobj, stream, options,
854                                                   "%FZ");
855 }
856 
LibcxxChronoLocalDaysSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)857 bool lldb_private::formatters::LibcxxChronoLocalDaysSummaryProvider(
858     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
859   return LibcxxChronoTimepointDaysSummaryProvider(valobj, stream, options,
860                                                   "%F");
861 }
862 
LibcxxChronoMonthSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)863 bool lldb_private::formatters::LibcxxChronoMonthSummaryProvider(
864     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
865   // FIXME: These are the names used in the C++20 ostream operator. Since LLVM
866   // uses C++17 it's not possible to use the ostream operator directly.
867   static const std::array<std::string_view, 12> months = {
868       "January", "February", "March",     "April",   "May",      "June",
869       "July",    "August",   "September", "October", "November", "December"};
870 
871   ValueObjectSP ptr_sp = valobj.GetChildMemberWithName("__m_");
872   if (!ptr_sp)
873     return false;
874 
875   const unsigned month = ptr_sp->GetValueAsUnsigned(0);
876   if (month >= 1 && month <= 12)
877     stream << "month=" << months[month - 1];
878   else
879     stream.Printf("month=%u", month);
880 
881   return true;
882 }
883 
LibcxxChronoWeekdaySummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)884 bool lldb_private::formatters::LibcxxChronoWeekdaySummaryProvider(
885     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
886   // FIXME: These are the names used in the C++20 ostream operator. Since LLVM
887   // uses C++17 it's not possible to use the ostream operator directly.
888   static const std::array<std::string_view, 7> weekdays = {
889       "Sunday",   "Monday", "Tuesday", "Wednesday",
890       "Thursday", "Friday", "Saturday"};
891 
892   ValueObjectSP ptr_sp = valobj.GetChildMemberWithName("__wd_");
893   if (!ptr_sp)
894     return false;
895 
896   const unsigned weekday = ptr_sp->GetValueAsUnsigned(0);
897   if (weekday < 7)
898     stream << "weekday=" << weekdays[weekday];
899   else
900     stream.Printf("weekday=%u", weekday);
901 
902   return true;
903 }
904 
LibcxxChronoYearMonthDaySummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)905 bool lldb_private::formatters::LibcxxChronoYearMonthDaySummaryProvider(
906     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
907   ValueObjectSP ptr_sp = valobj.GetChildMemberWithName("__y_");
908   if (!ptr_sp)
909     return false;
910   ptr_sp = ptr_sp->GetChildMemberWithName("__y_");
911   if (!ptr_sp)
912     return false;
913   int year = ptr_sp->GetValueAsSigned(0);
914 
915   ptr_sp = valobj.GetChildMemberWithName("__m_");
916   if (!ptr_sp)
917     return false;
918   ptr_sp = ptr_sp->GetChildMemberWithName("__m_");
919   if (!ptr_sp)
920     return false;
921   const unsigned month = ptr_sp->GetValueAsUnsigned(0);
922 
923   ptr_sp = valobj.GetChildMemberWithName("__d_");
924   if (!ptr_sp)
925     return false;
926   ptr_sp = ptr_sp->GetChildMemberWithName("__d_");
927   if (!ptr_sp)
928     return false;
929   const unsigned day = ptr_sp->GetValueAsUnsigned(0);
930 
931   stream << "date=";
932   if (year < 0) {
933     stream << '-';
934     year = -year;
935   }
936   stream.Printf("%04d-%02u-%02u", year, month, day);
937 
938   return true;
939 }
940