xref: /freebsd/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp (revision e64bea71c21eb42e97aa615188ba91f6cce0d36d)
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/DataFormatters/FormattersHelpers.h"
14 #include "lldb/DataFormatters/StringPrinter.h"
15 #include "lldb/DataFormatters/TypeSummary.h"
16 #include "lldb/DataFormatters/VectorIterator.h"
17 #include "lldb/Target/SectionLoadList.h"
18 #include "lldb/Target/Target.h"
19 #include "lldb/Utility/ConstString.h"
20 #include "lldb/Utility/DataBufferHeap.h"
21 #include "lldb/Utility/Endian.h"
22 #include "lldb/Utility/Status.h"
23 #include "lldb/Utility/Stream.h"
24 #include "lldb/ValueObject/ValueObject.h"
25 #include "lldb/ValueObject/ValueObjectConstResult.h"
26 
27 #include "Plugins/Language/CPlusPlus/CxxStringTypes.h"
28 #include "Plugins/Language/CPlusPlus/Generic.h"
29 #include "Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h"
30 #include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
31 #include "lldb/lldb-enumerations.h"
32 #include "lldb/lldb-forward.h"
33 #include <optional>
34 #include <tuple>
35 
36 using namespace lldb;
37 using namespace lldb_private;
38 using namespace lldb_private::formatters;
39 
consumeInlineNamespace(llvm::StringRef & name)40 static void consumeInlineNamespace(llvm::StringRef &name) {
41   // Delete past an inline namespace, if any: __[a-zA-Z0-9_]+::
42   auto scratch = name;
43   if (scratch.consume_front("__") && std::isalnum(scratch[0])) {
44     scratch = scratch.drop_while([](char c) { return std::isalnum(c); });
45     if (scratch.consume_front("::")) {
46       // Successfully consumed a namespace.
47       name = scratch;
48     }
49   }
50 }
51 
isStdTemplate(ConstString type_name,llvm::StringRef type)52 bool lldb_private::formatters::isStdTemplate(ConstString type_name,
53                                              llvm::StringRef type) {
54   llvm::StringRef name = type_name.GetStringRef();
55   // The type name may be prefixed with `std::__<inline-namespace>::`.
56   if (name.consume_front("std::"))
57     consumeInlineNamespace(name);
58   return name.consume_front(type) && name.starts_with("<");
59 }
60 
GetChildMemberWithName(ValueObject & obj,llvm::ArrayRef<ConstString> alternative_names)61 lldb::ValueObjectSP lldb_private::formatters::GetChildMemberWithName(
62     ValueObject &obj, llvm::ArrayRef<ConstString> alternative_names) {
63   for (ConstString name : alternative_names) {
64     lldb::ValueObjectSP child_sp = obj.GetChildMemberWithName(name);
65 
66     if (child_sp)
67       return child_sp;
68   }
69   return {};
70 }
71 
72 lldb::ValueObjectSP
GetFirstValueOfLibCXXCompressedPair(ValueObject & pair)73 lldb_private::formatters::GetFirstValueOfLibCXXCompressedPair(
74     ValueObject &pair) {
75   ValueObjectSP value;
76   ValueObjectSP first_child = pair.GetChildAtIndex(0);
77   if (first_child)
78     value = first_child->GetChildMemberWithName("__value_");
79   if (!value) {
80     // pre-c88580c member name
81     value = pair.GetChildMemberWithName("__first_");
82   }
83   return value;
84 }
85 
86 lldb::ValueObjectSP
GetSecondValueOfLibCXXCompressedPair(ValueObject & pair)87 lldb_private::formatters::GetSecondValueOfLibCXXCompressedPair(
88     ValueObject &pair) {
89   ValueObjectSP value;
90   if (pair.GetNumChildrenIgnoringErrors() > 1) {
91     ValueObjectSP second_child = pair.GetChildAtIndex(1);
92     if (second_child) {
93       value = second_child->GetChildMemberWithName("__value_");
94     }
95   }
96   if (!value) {
97     // pre-c88580c member name
98     value = pair.GetChildMemberWithName("__second_");
99   }
100   return value;
101 }
102 
103 std::pair<lldb::ValueObjectSP, bool>
GetValueOrOldCompressedPair(ValueObject & obj,size_t anon_struct_idx,llvm::StringRef child_name,llvm::StringRef compressed_pair_name)104 lldb_private::formatters::GetValueOrOldCompressedPair(
105     ValueObject &obj, size_t anon_struct_idx, llvm::StringRef child_name,
106     llvm::StringRef compressed_pair_name) {
107   auto is_old_compressed_pair = [](ValueObject &pair_obj) -> bool {
108     return isStdTemplate(pair_obj.GetTypeName(), "__compressed_pair");
109   };
110 
111   // Try searching the child member in an anonymous structure first.
112   if (auto unwrapped = obj.GetChildAtIndex(anon_struct_idx)) {
113     ValueObjectSP node_sp(obj.GetChildMemberWithName(child_name));
114     if (node_sp)
115       return {node_sp, is_old_compressed_pair(*node_sp)};
116   }
117 
118   // Older versions of libc++ don't wrap the children in anonymous structures.
119   // Try that instead.
120   ValueObjectSP node_sp(obj.GetChildMemberWithName(child_name));
121   if (node_sp)
122     return {node_sp, is_old_compressed_pair(*node_sp)};
123 
124   // Try the even older __compressed_pair layout.
125 
126   assert(!compressed_pair_name.empty());
127 
128   node_sp = obj.GetChildMemberWithName(compressed_pair_name);
129 
130   // Unrecognized layout (possibly older than LLDB supports).
131   if (!node_sp)
132     return {nullptr, false};
133 
134   // Expected old compressed_pair layout, but got something else.
135   if (!is_old_compressed_pair(*node_sp))
136     return {nullptr, false};
137 
138   return {node_sp, true};
139 }
140 
LibcxxFunctionSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)141 bool lldb_private::formatters::LibcxxFunctionSummaryProvider(
142     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
143 
144   ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue());
145 
146   if (!valobj_sp)
147     return false;
148 
149   ExecutionContext exe_ctx(valobj_sp->GetExecutionContextRef());
150   Process *process = exe_ctx.GetProcessPtr();
151 
152   if (process == nullptr)
153     return false;
154 
155   CPPLanguageRuntime *cpp_runtime = CPPLanguageRuntime::Get(*process);
156 
157   if (!cpp_runtime)
158     return false;
159 
160   CPPLanguageRuntime::LibCppStdFunctionCallableInfo callable_info =
161       cpp_runtime->FindLibCppStdFunctionCallableInfo(valobj_sp);
162 
163   switch (callable_info.callable_case) {
164   case CPPLanguageRuntime::LibCppStdFunctionCallableCase::Invalid:
165     stream.Printf(" __f_ = %" PRIu64, callable_info.member_f_pointer_value);
166     return false;
167     break;
168   case CPPLanguageRuntime::LibCppStdFunctionCallableCase::Lambda:
169     stream.Printf(
170         " Lambda in File %s at Line %u",
171         callable_info.callable_line_entry.GetFile().GetFilename().GetCString(),
172         callable_info.callable_line_entry.line);
173     break;
174   case CPPLanguageRuntime::LibCppStdFunctionCallableCase::CallableObject:
175     stream.Printf(
176         " Function in File %s at Line %u",
177         callable_info.callable_line_entry.GetFile().GetFilename().GetCString(),
178         callable_info.callable_line_entry.line);
179     break;
180   case CPPLanguageRuntime::LibCppStdFunctionCallableCase::FreeOrMemberFunction:
181     stream.Printf(" Function = %s ",
182                   callable_info.callable_symbol.GetName().GetCString());
183     break;
184   }
185 
186   return true;
187 }
188 
LibcxxSmartPointerSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)189 bool lldb_private::formatters::LibcxxSmartPointerSummaryProvider(
190     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
191   ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue());
192   if (!valobj_sp)
193     return false;
194 
195   ValueObjectSP ptr_sp(valobj_sp->GetChildMemberWithName("__ptr_"));
196   ValueObjectSP ctrl_sp(valobj_sp->GetChildMemberWithName("__cntrl_"));
197   if (!ctrl_sp || !ptr_sp)
198     return false;
199 
200   DumpCxxSmartPtrPointerSummary(stream, *ptr_sp, options);
201 
202   bool success;
203   uint64_t ctrl_addr = ctrl_sp->GetValueAsUnsigned(0, &success);
204   // Empty control field. We're done.
205   if (!success || ctrl_addr == 0)
206     return true;
207 
208   if (auto count_sp = ctrl_sp->GetChildMemberWithName("__shared_owners_")) {
209     bool success;
210     uint64_t count = count_sp->GetValueAsUnsigned(0, &success);
211     if (!success)
212       return false;
213 
214     // std::shared_ptr releases the underlying resource when the
215     // __shared_owners_ count hits -1. So `__shared_owners_ == 0` indicates 1
216     // owner. Hence add +1 here.
217     stream.Printf(" strong=%" PRIu64, count + 1);
218   }
219 
220   if (auto weak_count_sp =
221           ctrl_sp->GetChildMemberWithName("__shared_weak_owners_")) {
222     bool success;
223     uint64_t count = weak_count_sp->GetValueAsUnsigned(0, &success);
224     if (!success)
225       return false;
226 
227     // Unlike __shared_owners_, __shared_weak_owners_ indicates the exact
228     // std::weak_ptr reference count.
229     stream.Printf(" weak=%" PRIu64, count);
230   }
231 
232   return true;
233 }
234 
LibcxxUniquePointerSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)235 bool lldb_private::formatters::LibcxxUniquePointerSummaryProvider(
236     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
237   ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue());
238   if (!valobj_sp)
239     return false;
240 
241   auto [ptr_sp, is_compressed_pair] = GetValueOrOldCompressedPair(
242       *valobj_sp, /*anon_struct_idx=*/0, "__ptr_", "__ptr_");
243   if (!ptr_sp)
244     return false;
245 
246   if (is_compressed_pair)
247     ptr_sp = GetFirstValueOfLibCXXCompressedPair(*ptr_sp);
248 
249   if (!ptr_sp)
250     return false;
251 
252   DumpCxxSmartPtrPointerSummary(stream, *ptr_sp, options);
253 
254   return true;
255 }
256 
257 /*
258  (lldb) fr var ibeg --raw --ptr-depth 1 -T
259  (std::__1::__wrap_iter<int *>) ibeg = {
260  (std::__1::__wrap_iter<int *>::iterator_type) __i = 0x00000001001037a0 {
261  (int) *__i = 1
262  }
263  }
264 */
265 
266 SyntheticChildrenFrontEnd *
LibCxxVectorIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *,lldb::ValueObjectSP valobj_sp)267 lldb_private::formatters::LibCxxVectorIteratorSyntheticFrontEndCreator(
268     CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
269   return (valobj_sp ? new VectorIteratorSyntheticFrontEnd(
270                           valobj_sp, {ConstString("__i_"), ConstString("__i")})
271                     : nullptr);
272 }
273 
274 lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::
LibcxxSharedPtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)275     LibcxxSharedPtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
276     : SyntheticChildrenFrontEnd(*valobj_sp), m_cntrl(nullptr),
277       m_ptr_obj(nullptr) {
278   if (valobj_sp)
279     Update();
280 }
281 
282 llvm::Expected<uint32_t> lldb_private::formatters::
CalculateNumChildren()283     LibcxxSharedPtrSyntheticFrontEnd::CalculateNumChildren() {
284   return (m_cntrl ? 1 : 0);
285 }
286 
287 lldb::ValueObjectSP
GetChildAtIndex(uint32_t idx)288 lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::GetChildAtIndex(
289     uint32_t idx) {
290   if (!m_cntrl || !m_ptr_obj)
291     return lldb::ValueObjectSP();
292 
293   ValueObjectSP valobj_sp = m_backend.GetSP();
294   if (!valobj_sp)
295     return lldb::ValueObjectSP();
296 
297   if (idx == 0)
298     return m_ptr_obj->GetSP();
299 
300   if (idx == 1) {
301     Status status;
302     ValueObjectSP value_sp = m_ptr_obj->Dereference(status);
303     if (status.Success())
304       return value_sp;
305   }
306 
307   return lldb::ValueObjectSP();
308 }
309 
310 lldb::ChildCacheState
Update()311 lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::Update() {
312   m_cntrl = nullptr;
313   m_ptr_obj = nullptr;
314 
315   ValueObjectSP valobj_sp = m_backend.GetSP();
316   if (!valobj_sp)
317     return lldb::ChildCacheState::eRefetch;
318 
319   TargetSP target_sp(valobj_sp->GetTargetSP());
320   if (!target_sp)
321     return lldb::ChildCacheState::eRefetch;
322 
323   auto ptr_obj_sp = valobj_sp->GetChildMemberWithName("__ptr_");
324   if (!ptr_obj_sp)
325     return lldb::ChildCacheState::eRefetch;
326 
327   auto cast_ptr_sp = GetDesugaredSmartPointerValue(*ptr_obj_sp, *valobj_sp);
328   if (!cast_ptr_sp)
329     return lldb::ChildCacheState::eRefetch;
330 
331   m_ptr_obj = cast_ptr_sp->Clone(ConstString("pointer")).get();
332 
333   lldb::ValueObjectSP cntrl_sp(valobj_sp->GetChildMemberWithName("__cntrl_"));
334 
335   m_cntrl = cntrl_sp.get(); // need to store the raw pointer to avoid a circular
336                             // dependency
337   return lldb::ChildCacheState::eRefetch;
338 }
339 
340 llvm::Expected<size_t>
341 lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::
GetIndexOfChildWithName(ConstString name)342     GetIndexOfChildWithName(ConstString name) {
343   if (name == "pointer")
344     return 0;
345 
346   if (name == "object" || name == "$$dereference$$")
347     return 1;
348 
349   return llvm::createStringError("Type has no child named '%s'",
350                                  name.AsCString());
351 }
352 
353 lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::
354     ~LibcxxSharedPtrSyntheticFrontEnd() = default;
355 
356 SyntheticChildrenFrontEnd *
LibcxxSharedPtrSyntheticFrontEndCreator(CXXSyntheticChildren *,lldb::ValueObjectSP valobj_sp)357 lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEndCreator(
358     CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
359   return (valobj_sp ? new LibcxxSharedPtrSyntheticFrontEnd(valobj_sp)
360                     : nullptr);
361 }
362 
363 lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::
LibcxxUniquePtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)364     LibcxxUniquePtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
365     : SyntheticChildrenFrontEnd(*valobj_sp) {
366   if (valobj_sp)
367     Update();
368 }
369 
370 lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::
371     ~LibcxxUniquePtrSyntheticFrontEnd() = default;
372 
373 SyntheticChildrenFrontEnd *
LibcxxUniquePtrSyntheticFrontEndCreator(CXXSyntheticChildren *,lldb::ValueObjectSP valobj_sp)374 lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEndCreator(
375     CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
376   return (valobj_sp ? new LibcxxUniquePtrSyntheticFrontEnd(valobj_sp)
377                     : nullptr);
378 }
379 
380 llvm::Expected<uint32_t> lldb_private::formatters::
CalculateNumChildren()381     LibcxxUniquePtrSyntheticFrontEnd::CalculateNumChildren() {
382   if (m_value_ptr_sp)
383     return m_deleter_sp ? 2 : 1;
384   return 0;
385 }
386 
387 lldb::ValueObjectSP
GetChildAtIndex(uint32_t idx)388 lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::GetChildAtIndex(
389     uint32_t idx) {
390   if (!m_value_ptr_sp)
391     return lldb::ValueObjectSP();
392 
393   if (idx == 0)
394     return m_value_ptr_sp;
395 
396   if (idx == 1)
397     return m_deleter_sp;
398 
399   if (idx == 2) {
400     Status status;
401     auto value_sp = m_value_ptr_sp->Dereference(status);
402     if (status.Success()) {
403       return value_sp;
404     }
405   }
406 
407   return lldb::ValueObjectSP();
408 }
409 
410 lldb::ChildCacheState
Update()411 lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::Update() {
412   ValueObjectSP valobj_sp = m_backend.GetSP();
413   if (!valobj_sp)
414     return lldb::ChildCacheState::eRefetch;
415 
416   auto [ptr_sp, is_compressed_pair] = GetValueOrOldCompressedPair(
417       *valobj_sp, /*anon_struct_idx=*/0, "__ptr_", "__ptr_");
418   if (!ptr_sp)
419     return lldb::ChildCacheState::eRefetch;
420 
421   // Retrieve the actual pointer and the deleter, and clone them to give them
422   // user-friendly names.
423   if (is_compressed_pair) {
424     if (ValueObjectSP value_pointer_sp =
425             GetFirstValueOfLibCXXCompressedPair(*ptr_sp))
426       m_value_ptr_sp = value_pointer_sp->Clone(ConstString("pointer"));
427 
428     if (ValueObjectSP deleter_sp =
429             GetSecondValueOfLibCXXCompressedPair(*ptr_sp))
430       m_deleter_sp = deleter_sp->Clone(ConstString("deleter"));
431   } else {
432     m_value_ptr_sp = ptr_sp->Clone(ConstString("pointer"));
433 
434     if (ValueObjectSP deleter_sp =
435             valobj_sp->GetChildMemberWithName("__deleter_"))
436       if (deleter_sp->GetNumChildrenIgnoringErrors() > 0)
437         m_deleter_sp = deleter_sp->Clone(ConstString("deleter"));
438   }
439 
440   return lldb::ChildCacheState::eRefetch;
441 }
442 
443 llvm::Expected<size_t>
444 lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::
GetIndexOfChildWithName(ConstString name)445     GetIndexOfChildWithName(ConstString name) {
446   if (name == "pointer")
447     return 0;
448   if (name == "deleter")
449     return 1;
450   if (name == "obj" || name == "object" || name == "$$dereference$$")
451     return 2;
452   return llvm::createStringError("Type has no child named '%s'",
453                                  name.AsCString());
454 }
455 
456 /// The field layout in a libc++ string (cap, side, data or data, size, cap).
457 namespace {
458 enum class StringLayout { CSD, DSC };
459 }
460 
ExtractLibCxxStringData(ValueObject & valobj)461 static ValueObjectSP ExtractLibCxxStringData(ValueObject &valobj) {
462   auto [valobj_r_sp, is_compressed_pair] = GetValueOrOldCompressedPair(
463       valobj, /*anon_struct_idx=*/0, "__rep_", "__r_");
464   if (!valobj_r_sp)
465     return nullptr;
466 
467   if (is_compressed_pair)
468     return GetFirstValueOfLibCXXCompressedPair(*valobj_r_sp);
469 
470   return valobj_r_sp;
471 }
472 
473 /// Determine the size in bytes of \p valobj (a libc++ std::string object) and
474 /// extract its data payload. Return the size + payload pair.
475 // TODO: Support big-endian architectures.
476 static std::optional<std::pair<uint64_t, ValueObjectSP>>
ExtractLibcxxStringInfo(ValueObject & valobj)477 ExtractLibcxxStringInfo(ValueObject &valobj) {
478   ValueObjectSP valobj_rep_sp = ExtractLibCxxStringData(valobj);
479   if (!valobj_rep_sp || !valobj_rep_sp->GetError().Success())
480     return {};
481 
482   ValueObjectSP l = valobj_rep_sp->GetChildMemberWithName("__l");
483   if (!l)
484     return {};
485 
486   auto index_or_err = l->GetIndexOfChildWithName("__data_");
487   if (!index_or_err) {
488     LLDB_LOG_ERROR(GetLog(LLDBLog::DataFormatters), index_or_err.takeError(),
489                    "{0}");
490     return {};
491   }
492 
493   StringLayout layout =
494       *index_or_err == 0 ? StringLayout::DSC : StringLayout::CSD;
495 
496   bool short_mode = false; // this means the string is in short-mode and the
497                            // data is stored inline
498   bool using_bitmasks = true; // Whether the class uses bitmasks for the mode
499                               // flag (pre-D123580).
500   uint64_t size;
501   uint64_t size_mode_value = 0;
502 
503   ValueObjectSP short_sp = valobj_rep_sp->GetChildMemberWithName("__s");
504   if (!short_sp)
505     return {};
506 
507   ValueObjectSP is_long = short_sp->GetChildMemberWithName("__is_long_");
508   ValueObjectSP size_sp = short_sp->GetChildMemberWithName("__size_");
509   if (!size_sp)
510     return {};
511 
512   if (is_long) {
513     using_bitmasks = false;
514     short_mode = !is_long->GetValueAsUnsigned(/*fail_value=*/0);
515     size = size_sp->GetValueAsUnsigned(/*fail_value=*/0);
516   } else {
517     // The string mode is encoded in the size field.
518     size_mode_value = size_sp->GetValueAsUnsigned(0);
519     uint8_t mode_mask = layout == StringLayout::DSC ? 0x80 : 1;
520     short_mode = (size_mode_value & mode_mask) == 0;
521   }
522 
523   if (short_mode) {
524     ValueObjectSP location_sp = short_sp->GetChildMemberWithName("__data_");
525     if (using_bitmasks)
526       size = (layout == StringLayout::DSC) ? size_mode_value
527                                            : ((size_mode_value >> 1) % 256);
528 
529     if (!location_sp)
530       return {};
531 
532     // When the small-string optimization takes place, the data must fit in the
533     // inline string buffer (23 bytes on x86_64/Darwin). If it doesn't, it's
534     // likely that the string isn't initialized and we're reading garbage.
535     ExecutionContext exe_ctx(location_sp->GetExecutionContextRef());
536     const std::optional<uint64_t> max_bytes =
537         llvm::expectedToOptional(location_sp->GetCompilerType().GetByteSize(
538             exe_ctx.GetBestExecutionContextScope()));
539     if (!max_bytes || size > *max_bytes)
540       return {};
541 
542     return std::make_pair(size, location_sp);
543   }
544 
545   // we can use the layout_decider object as the data pointer
546   ValueObjectSP location_sp = l->GetChildMemberWithName("__data_");
547   ValueObjectSP size_vo = l->GetChildMemberWithName("__size_");
548   ValueObjectSP capacity_vo = l->GetChildMemberWithName("__cap_");
549   if (!size_vo || !location_sp || !capacity_vo)
550     return {};
551   size = size_vo->GetValueAsUnsigned(LLDB_INVALID_OFFSET);
552   uint64_t capacity = capacity_vo->GetValueAsUnsigned(LLDB_INVALID_OFFSET);
553   if (!using_bitmasks && layout == StringLayout::CSD)
554     capacity *= 2;
555   if (size == LLDB_INVALID_OFFSET || capacity == LLDB_INVALID_OFFSET ||
556       capacity < size)
557     return {};
558   return std::make_pair(size, location_sp);
559 }
560 
LibcxxWStringSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & summary_options)561 bool lldb_private::formatters::LibcxxWStringSummaryProvider(
562     ValueObject &valobj, Stream &stream,
563     const TypeSummaryOptions &summary_options) {
564   auto string_info = ExtractLibcxxStringInfo(valobj);
565   if (!string_info)
566     return false;
567   uint64_t size;
568   ValueObjectSP location_sp;
569   std::tie(size, location_sp) = *string_info;
570 
571   auto wchar_t_size = GetWCharByteSize(valobj);
572   if (!wchar_t_size)
573     return false;
574 
575   switch (*wchar_t_size) {
576   case 1:
577     return StringBufferSummaryProvider<StringPrinter::StringElementType::UTF8>(
578         stream, summary_options, location_sp, size, "L");
579   case 2:
580     return StringBufferSummaryProvider<StringPrinter::StringElementType::UTF16>(
581         stream, summary_options, location_sp, size, "L");
582   case 4:
583     return StringBufferSummaryProvider<StringPrinter::StringElementType::UTF32>(
584         stream, summary_options, location_sp, size, "L");
585   }
586   return false;
587 }
588 
589 template <StringPrinter::StringElementType element_type>
590 static bool
LibcxxStringSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & summary_options,std::string prefix_token)591 LibcxxStringSummaryProvider(ValueObject &valobj, Stream &stream,
592                             const TypeSummaryOptions &summary_options,
593                             std::string prefix_token) {
594   auto string_info = ExtractLibcxxStringInfo(valobj);
595   if (!string_info)
596     return false;
597   uint64_t size;
598   ValueObjectSP location_sp;
599   std::tie(size, location_sp) = *string_info;
600 
601   return StringBufferSummaryProvider<element_type>(
602       stream, summary_options, location_sp, size, prefix_token);
603 }
604 template <StringPrinter::StringElementType element_type>
formatStringImpl(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & summary_options,std::string prefix_token)605 static bool formatStringImpl(ValueObject &valobj, Stream &stream,
606                              const TypeSummaryOptions &summary_options,
607                              std::string prefix_token) {
608   StreamString scratch_stream;
609   const bool success = LibcxxStringSummaryProvider<element_type>(
610       valobj, scratch_stream, summary_options, prefix_token);
611   if (success)
612     stream << scratch_stream.GetData();
613   else
614     stream << "Summary Unavailable";
615   return true;
616 }
617 
LibcxxStringSummaryProviderASCII(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & summary_options)618 bool lldb_private::formatters::LibcxxStringSummaryProviderASCII(
619     ValueObject &valobj, Stream &stream,
620     const TypeSummaryOptions &summary_options) {
621   return formatStringImpl<StringPrinter::StringElementType::ASCII>(
622       valobj, stream, summary_options, "");
623 }
624 
LibcxxStringSummaryProviderUTF16(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & summary_options)625 bool lldb_private::formatters::LibcxxStringSummaryProviderUTF16(
626     ValueObject &valobj, Stream &stream,
627     const TypeSummaryOptions &summary_options) {
628   return formatStringImpl<StringPrinter::StringElementType::UTF16>(
629       valobj, stream, summary_options, "u");
630 }
631 
LibcxxStringSummaryProviderUTF32(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & summary_options)632 bool lldb_private::formatters::LibcxxStringSummaryProviderUTF32(
633     ValueObject &valobj, Stream &stream,
634     const TypeSummaryOptions &summary_options) {
635   return formatStringImpl<StringPrinter::StringElementType::UTF32>(
636       valobj, stream, summary_options, "U");
637 }
638 
639 static std::tuple<bool, ValueObjectSP, size_t>
LibcxxExtractStringViewData(ValueObject & valobj)640 LibcxxExtractStringViewData(ValueObject& valobj) {
641   auto dataobj = GetChildMemberWithName(
642       valobj, {ConstString("__data_"), ConstString("__data")});
643   auto sizeobj = GetChildMemberWithName(
644       valobj, {ConstString("__size_"), ConstString("__size")});
645   if (!dataobj || !sizeobj)
646     return std::make_tuple<bool,ValueObjectSP,size_t>(false, {}, {});
647 
648   if (!dataobj->GetError().Success() || !sizeobj->GetError().Success())
649     return std::make_tuple<bool,ValueObjectSP,size_t>(false, {}, {});
650 
651   bool success{false};
652   uint64_t size = sizeobj->GetValueAsUnsigned(0, &success);
653   if (!success)
654     return std::make_tuple<bool,ValueObjectSP,size_t>(false, {}, {});
655 
656   return std::make_tuple(true,dataobj,size);
657 }
658 
659 template <StringPrinter::StringElementType element_type>
formatStringViewImpl(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & summary_options,std::string prefix_token)660 static bool formatStringViewImpl(ValueObject &valobj, Stream &stream,
661                                  const TypeSummaryOptions &summary_options,
662                                  std::string prefix_token) {
663 
664   bool success;
665   ValueObjectSP dataobj;
666   size_t size;
667   std::tie(success, dataobj, size) = LibcxxExtractStringViewData(valobj);
668 
669   if (!success) {
670     stream << "Summary Unavailable";
671     return true;
672   }
673 
674   return StringBufferSummaryProvider<element_type>(stream, summary_options,
675                                                    dataobj, size, prefix_token);
676 }
677 
LibcxxStringViewSummaryProviderASCII(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & summary_options)678 bool lldb_private::formatters::LibcxxStringViewSummaryProviderASCII(
679     ValueObject &valobj, Stream &stream,
680     const TypeSummaryOptions &summary_options) {
681   return formatStringViewImpl<StringPrinter::StringElementType::ASCII>(
682       valobj, stream, summary_options, "");
683 }
684 
LibcxxStringViewSummaryProviderUTF16(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & summary_options)685 bool lldb_private::formatters::LibcxxStringViewSummaryProviderUTF16(
686     ValueObject &valobj, Stream &stream,
687     const TypeSummaryOptions &summary_options) {
688   return formatStringViewImpl<StringPrinter::StringElementType::UTF16>(
689       valobj, stream, summary_options, "u");
690 }
691 
LibcxxStringViewSummaryProviderUTF32(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & summary_options)692 bool lldb_private::formatters::LibcxxStringViewSummaryProviderUTF32(
693     ValueObject &valobj, Stream &stream,
694     const TypeSummaryOptions &summary_options) {
695   return formatStringViewImpl<StringPrinter::StringElementType::UTF32>(
696       valobj, stream, summary_options, "U");
697 }
698 
LibcxxWStringViewSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & summary_options)699 bool lldb_private::formatters::LibcxxWStringViewSummaryProvider(
700     ValueObject &valobj, Stream &stream,
701     const TypeSummaryOptions &summary_options) {
702 
703   bool success;
704   ValueObjectSP dataobj;
705   size_t size;
706   std::tie(success, dataobj, size) = LibcxxExtractStringViewData(valobj);
707 
708   if (!success) {
709     stream << "Summary Unavailable";
710     return true;
711   }
712 
713   auto wchar_t_size = GetWCharByteSize(valobj);
714   if (!wchar_t_size)
715     return false;
716 
717   switch (*wchar_t_size) {
718   case 1:
719     return StringBufferSummaryProvider<StringPrinter::StringElementType::UTF8>(
720         stream, summary_options, dataobj, size, "L");
721   case 2:
722     return StringBufferSummaryProvider<StringPrinter::StringElementType::UTF16>(
723         stream, summary_options, dataobj, size, "L");
724   case 4:
725     return StringBufferSummaryProvider<StringPrinter::StringElementType::UTF32>(
726         stream, summary_options, dataobj, size, "L");
727   }
728   return false;
729 }
730 
731 static bool
LibcxxChronoTimePointSecondsSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options,const char * fmt)732 LibcxxChronoTimePointSecondsSummaryProvider(ValueObject &valobj, Stream &stream,
733                                             const TypeSummaryOptions &options,
734                                             const char *fmt) {
735   ValueObjectSP ptr_sp = valobj.GetChildMemberWithName("__d_");
736   if (!ptr_sp)
737     return false;
738   ptr_sp = ptr_sp->GetChildMemberWithName("__rep_");
739   if (!ptr_sp)
740     return false;
741 
742 #ifndef _WIN32
743   // The date time in the chrono library is valid in the range
744   // [-32767-01-01T00:00:00Z, 32767-12-31T23:59:59Z]. A 64-bit time_t has a
745   // larger range, the function strftime is not able to format the entire range
746   // of time_t. The exact point has not been investigated; it's limited to
747   // chrono's range.
748   const std::time_t chrono_timestamp_min =
749       -1'096'193'779'200; // -32767-01-01T00:00:00Z
750   const std::time_t chrono_timestamp_max =
751       971'890'963'199; // 32767-12-31T23:59:59Z
752 #else
753   const std::time_t chrono_timestamp_min = -43'200; // 1969-12-31T12:00:00Z
754   const std::time_t chrono_timestamp_max =
755       32'536'850'399; // 3001-01-19T21:59:59
756 #endif
757 
758   const std::time_t seconds = ptr_sp->GetValueAsSigned(0);
759   if (seconds < chrono_timestamp_min || seconds > chrono_timestamp_max)
760     stream.Printf("timestamp=%" PRId64 " s", static_cast<int64_t>(seconds));
761   else {
762     std::array<char, 128> str;
763     std::size_t size =
764         std::strftime(str.data(), str.size(), fmt, gmtime(&seconds));
765     if (size == 0)
766       return false;
767 
768     stream.Printf("date/time=%s timestamp=%" PRId64 " s", str.data(),
769                   static_cast<int64_t>(seconds));
770   }
771 
772   return true;
773 }
774 
LibcxxChronoSysSecondsSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)775 bool lldb_private::formatters::LibcxxChronoSysSecondsSummaryProvider(
776     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
777   return LibcxxChronoTimePointSecondsSummaryProvider(valobj, stream, options,
778                                                      "%FT%H:%M:%SZ");
779 }
780 
LibcxxChronoLocalSecondsSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)781 bool lldb_private::formatters::LibcxxChronoLocalSecondsSummaryProvider(
782     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
783   return LibcxxChronoTimePointSecondsSummaryProvider(valobj, stream, options,
784                                                      "%FT%H:%M:%S");
785 }
786 
787 static bool
LibcxxChronoTimepointDaysSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options,const char * fmt)788 LibcxxChronoTimepointDaysSummaryProvider(ValueObject &valobj, Stream &stream,
789                                          const TypeSummaryOptions &options,
790                                          const char *fmt) {
791   ValueObjectSP ptr_sp = valobj.GetChildMemberWithName("__d_");
792   if (!ptr_sp)
793     return false;
794   ptr_sp = ptr_sp->GetChildMemberWithName("__rep_");
795   if (!ptr_sp)
796     return false;
797 
798 #ifndef _WIN32
799   // The date time in the chrono library is valid in the range
800   // [-32767-01-01Z, 32767-12-31Z]. A 32-bit time_t has a larger range, the
801   // function strftime is not able to format the entire range of time_t. The
802   // exact point has not been investigated; it's limited to chrono's range.
803   const int chrono_timestamp_min = -12'687'428; // -32767-01-01Z
804   const int chrono_timestamp_max = 11'248'737;  // 32767-12-31Z
805 #else
806   const int chrono_timestamp_min = 0;       // 1970-01-01Z
807   const int chrono_timestamp_max = 376'583; // 3001-01-19Z
808 #endif
809 
810   const int days = ptr_sp->GetValueAsSigned(0);
811   if (days < chrono_timestamp_min || days > chrono_timestamp_max)
812     stream.Printf("timestamp=%d days", days);
813 
814   else {
815     const std::time_t seconds = std::time_t(86400) * days;
816 
817     std::array<char, 128> str;
818     std::size_t size =
819         std::strftime(str.data(), str.size(), fmt, gmtime(&seconds));
820     if (size == 0)
821       return false;
822 
823     stream.Printf("date=%s timestamp=%d days", str.data(), days);
824   }
825 
826   return true;
827 }
828 
LibcxxChronoSysDaysSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)829 bool lldb_private::formatters::LibcxxChronoSysDaysSummaryProvider(
830     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
831   return LibcxxChronoTimepointDaysSummaryProvider(valobj, stream, options,
832                                                   "%FZ");
833 }
834 
LibcxxChronoLocalDaysSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)835 bool lldb_private::formatters::LibcxxChronoLocalDaysSummaryProvider(
836     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
837   return LibcxxChronoTimepointDaysSummaryProvider(valobj, stream, options,
838                                                   "%F");
839 }
840 
LibcxxChronoMonthSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)841 bool lldb_private::formatters::LibcxxChronoMonthSummaryProvider(
842     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
843   // FIXME: These are the names used in the C++20 ostream operator. Since LLVM
844   // uses C++17 it's not possible to use the ostream operator directly.
845   static const std::array<std::string_view, 12> months = {
846       "January", "February", "March",     "April",   "May",      "June",
847       "July",    "August",   "September", "October", "November", "December"};
848 
849   ValueObjectSP ptr_sp = valobj.GetChildMemberWithName("__m_");
850   if (!ptr_sp)
851     return false;
852 
853   const unsigned month = ptr_sp->GetValueAsUnsigned(0);
854   if (month >= 1 && month <= 12)
855     stream << "month=" << months[month - 1];
856   else
857     stream.Printf("month=%u", month);
858 
859   return true;
860 }
861 
LibcxxChronoWeekdaySummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)862 bool lldb_private::formatters::LibcxxChronoWeekdaySummaryProvider(
863     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
864   // FIXME: These are the names used in the C++20 ostream operator. Since LLVM
865   // uses C++17 it's not possible to use the ostream operator directly.
866   static const std::array<std::string_view, 7> weekdays = {
867       "Sunday",   "Monday", "Tuesday", "Wednesday",
868       "Thursday", "Friday", "Saturday"};
869 
870   ValueObjectSP ptr_sp = valobj.GetChildMemberWithName("__wd_");
871   if (!ptr_sp)
872     return false;
873 
874   const unsigned weekday = ptr_sp->GetValueAsUnsigned(0);
875   if (weekday < 7)
876     stream << "weekday=" << weekdays[weekday];
877   else
878     stream.Printf("weekday=%u", weekday);
879 
880   return true;
881 }
882 
LibcxxChronoYearMonthDaySummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)883 bool lldb_private::formatters::LibcxxChronoYearMonthDaySummaryProvider(
884     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
885   ValueObjectSP ptr_sp = valobj.GetChildMemberWithName("__y_");
886   if (!ptr_sp)
887     return false;
888   ptr_sp = ptr_sp->GetChildMemberWithName("__y_");
889   if (!ptr_sp)
890     return false;
891   int year = ptr_sp->GetValueAsSigned(0);
892 
893   ptr_sp = valobj.GetChildMemberWithName("__m_");
894   if (!ptr_sp)
895     return false;
896   ptr_sp = ptr_sp->GetChildMemberWithName("__m_");
897   if (!ptr_sp)
898     return false;
899   const unsigned month = ptr_sp->GetValueAsUnsigned(0);
900 
901   ptr_sp = valobj.GetChildMemberWithName("__d_");
902   if (!ptr_sp)
903     return false;
904   ptr_sp = ptr_sp->GetChildMemberWithName("__d_");
905   if (!ptr_sp)
906     return false;
907   const unsigned day = ptr_sp->GetValueAsUnsigned(0);
908 
909   stream << "date=";
910   if (year < 0) {
911     stream << '-';
912     year = -year;
913   }
914   stream.Printf("%04d-%02u-%02u", year, month, day);
915 
916   return true;
917 }
918