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

source code of lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp