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
36using namespace lldb;
37using namespace lldb_private;
38using namespace lldb_private::formatters;
39
40static 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(Prefix: "__") && std::isalnum(scratch[0])) {
44 scratch = scratch.drop_while(F: [](char c) { return std::isalnum(c); });
45 if (scratch.consume_front(Prefix: "::")) {
46 // Successfully consumed a namespace.
47 name = scratch;
48 }
49 }
50}
51
52bool 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(Prefix: "std::"))
57 consumeInlineNamespace(name);
58 return name.consume_front(Prefix: type) && name.starts_with(Prefix: "<");
59}
60
61lldb::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
72lldb::ValueObjectSP
73lldb_private::formatters::GetFirstValueOfLibCXXCompressedPair(
74 ValueObject &pair) {
75 ValueObjectSP value;
76 ValueObjectSP first_child = pair.GetChildAtIndex(idx: 0);
77 if (first_child)
78 value = first_child->GetChildMemberWithName(name: "__value_");
79 if (!value) {
80 // pre-c88580c member name
81 value = pair.GetChildMemberWithName(name: "__first_");
82 }
83 return value;
84}
85
86lldb::ValueObjectSP
87lldb_private::formatters::GetSecondValueOfLibCXXCompressedPair(
88 ValueObject &pair) {
89 ValueObjectSP value;
90 if (pair.GetNumChildrenIgnoringErrors() > 1) {
91 ValueObjectSP second_child = pair.GetChildAtIndex(idx: 1);
92 if (second_child) {
93 value = second_child->GetChildMemberWithName(name: "__value_");
94 }
95 }
96 if (!value) {
97 // pre-c88580c member name
98 value = pair.GetChildMemberWithName(name: "__second_");
99 }
100 return value;
101}
102
103std::pair<lldb::ValueObjectSP, bool>
104lldb_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(type_name: pair_obj.GetTypeName(), type: "__compressed_pair");
109 };
110
111 // Try searching the child member in an anonymous structure first.
112 if (auto unwrapped = obj.GetChildAtIndex(idx: anon_struct_idx)) {
113 ValueObjectSP node_sp(obj.GetChildMemberWithName(name: 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(name: 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(name: 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
141bool 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&: *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(format: " __f_ = %" PRIu64, callable_info.member_f_pointer_value);
166 return false;
167 break;
168 case CPPLanguageRuntime::LibCppStdFunctionCallableCase::Lambda:
169 stream.Printf(
170 format: " 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 format: " 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(format: " Function = %s ",
182 callable_info.callable_symbol.GetName().GetCString());
183 break;
184 }
185
186 return true;
187}
188
189bool 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(name: "__ptr_"));
196 ValueObjectSP ctrl_sp(valobj_sp->GetChildMemberWithName(name: "__cntrl_"));
197 if (!ctrl_sp || !ptr_sp)
198 return false;
199
200 DumpCxxSmartPtrPointerSummary(stream, ptr&: *ptr_sp, options);
201
202 bool success;
203 uint64_t ctrl_addr = ctrl_sp->GetValueAsUnsigned(fail_value: 0, success: &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(name: "__shared_owners_")) {
209 bool success;
210 uint64_t count = count_sp->GetValueAsUnsigned(fail_value: 0, success: &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(format: " strong=%" PRIu64, count + 1);
218 }
219
220 if (auto weak_count_sp =
221 ctrl_sp->GetChildMemberWithName(name: "__shared_weak_owners_")) {
222 bool success;
223 uint64_t count = weak_count_sp->GetValueAsUnsigned(fail_value: 0, success: &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(format: " weak=%" PRIu64, count);
230 }
231
232 return true;
233}
234
235bool 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 obj&: *valobj_sp, /*anon_struct_idx=*/0, child_name: "__ptr_", compressed_pair_name: "__ptr_");
243 if (!ptr_sp)
244 return false;
245
246 if (is_compressed_pair)
247 ptr_sp = GetFirstValueOfLibCXXCompressedPair(pair&: *ptr_sp);
248
249 if (!ptr_sp)
250 return false;
251
252 DumpCxxSmartPtrPointerSummary(stream, ptr&: *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
266SyntheticChildrenFrontEnd *
267lldb_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
274lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::
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
282llvm::Expected<uint32_t> lldb_private::formatters::
283 LibcxxSharedPtrSyntheticFrontEnd::CalculateNumChildren() {
284 return (m_cntrl ? 1 : 0);
285}
286
287lldb::ValueObjectSP
288lldb_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(error&: status);
303 if (status.Success())
304 return value_sp;
305 }
306
307 return lldb::ValueObjectSP();
308}
309
310lldb::ChildCacheState
311lldb_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(name: "__ptr_");
324 if (!ptr_obj_sp)
325 return lldb::ChildCacheState::eRefetch;
326
327 auto cast_ptr_sp = GetDesugaredSmartPointerValue(ptr&: *ptr_obj_sp, container&: *valobj_sp);
328 if (!cast_ptr_sp)
329 return lldb::ChildCacheState::eRefetch;
330
331 m_ptr_obj = cast_ptr_sp->Clone(new_name: ConstString("pointer")).get();
332
333 lldb::ValueObjectSP cntrl_sp(valobj_sp->GetChildMemberWithName(name: "__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
340llvm::Expected<size_t>
341lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::
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(Fmt: "Type has no child named '%s'",
350 Vals: name.AsCString());
351}
352
353lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::
354 ~LibcxxSharedPtrSyntheticFrontEnd() = default;
355
356SyntheticChildrenFrontEnd *
357lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEndCreator(
358 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
359 return (valobj_sp ? new LibcxxSharedPtrSyntheticFrontEnd(valobj_sp)
360 : nullptr);
361}
362
363lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::
364 LibcxxUniquePtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
365 : SyntheticChildrenFrontEnd(*valobj_sp) {
366 if (valobj_sp)
367 Update();
368}
369
370lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::
371 ~LibcxxUniquePtrSyntheticFrontEnd() = default;
372
373SyntheticChildrenFrontEnd *
374lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEndCreator(
375 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
376 return (valobj_sp ? new LibcxxUniquePtrSyntheticFrontEnd(valobj_sp)
377 : nullptr);
378}
379
380llvm::Expected<uint32_t> lldb_private::formatters::
381 LibcxxUniquePtrSyntheticFrontEnd::CalculateNumChildren() {
382 if (m_value_ptr_sp)
383 return m_deleter_sp ? 2 : 1;
384 return 0;
385}
386
387lldb::ValueObjectSP
388lldb_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(error&: status);
402 if (status.Success()) {
403 return value_sp;
404 }
405 }
406
407 return lldb::ValueObjectSP();
408}
409
410lldb::ChildCacheState
411lldb_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 obj&: *valobj_sp, /*anon_struct_idx=*/0, child_name: "__ptr_", compressed_pair_name: "__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(pair&: *ptr_sp))
426 m_value_ptr_sp = value_pointer_sp->Clone(new_name: ConstString("pointer"));
427
428 if (ValueObjectSP deleter_sp =
429 GetSecondValueOfLibCXXCompressedPair(pair&: *ptr_sp))
430 m_deleter_sp = deleter_sp->Clone(new_name: ConstString("deleter"));
431 } else {
432 m_value_ptr_sp = ptr_sp->Clone(new_name: ConstString("pointer"));
433
434 if (ValueObjectSP deleter_sp =
435 valobj_sp->GetChildMemberWithName(name: "__deleter_"))
436 if (deleter_sp->GetNumChildrenIgnoringErrors() > 0)
437 m_deleter_sp = deleter_sp->Clone(new_name: ConstString("deleter"));
438 }
439
440 return lldb::ChildCacheState::eRefetch;
441}
442
443llvm::Expected<size_t>
444lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::
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(Fmt: "Type has no child named '%s'",
453 Vals: name.AsCString());
454}
455
456/// The field layout in a libc++ string (cap, side, data or data, size, cap).
457namespace {
458enum class StringLayout { CSD, DSC };
459}
460
461static ValueObjectSP ExtractLibCxxStringData(ValueObject &valobj) {
462 auto [valobj_r_sp, is_compressed_pair] = GetValueOrOldCompressedPair(
463 obj&: valobj, /*anon_struct_idx=*/0, child_name: "__rep_", compressed_pair_name: "__r_");
464 if (!valobj_r_sp)
465 return nullptr;
466
467 if (is_compressed_pair)
468 return GetFirstValueOfLibCXXCompressedPair(pair&: *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.
476static std::optional<std::pair<uint64_t, ValueObjectSP>>
477ExtractLibcxxStringInfo(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(name: "__l");
483 if (!l)
484 return {};
485
486 auto index_or_err = l->GetIndexOfChildWithName(name: "__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(name: "__s");
504 if (!short_sp)
505 return {};
506
507 ValueObjectSP is_long = short_sp->GetChildMemberWithName(name: "__is_long_");
508 ValueObjectSP size_sp = short_sp->GetChildMemberWithName(name: "__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(fail_value: 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(name: "__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(E: location_sp->GetCompilerType().GetByteSize(
538 exe_scope: exe_ctx.GetBestExecutionContextScope()));
539 if (!max_bytes || size > *max_bytes)
540 return {};
541
542 return std::make_pair(x&: size, y&: location_sp);
543 }
544
545 // we can use the layout_decider object as the data pointer
546 ValueObjectSP location_sp = l->GetChildMemberWithName(name: "__data_");
547 ValueObjectSP size_vo = l->GetChildMemberWithName(name: "__size_");
548 ValueObjectSP capacity_vo = l->GetChildMemberWithName(name: "__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(x&: size, y&: location_sp);
559}
560
561bool 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(args&: size, args&: 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, prefix_token: "L");
579 case 2:
580 return StringBufferSummaryProvider<StringPrinter::StringElementType::UTF16>(
581 stream, summary_options, location_sp, size, prefix_token: "L");
582 case 4:
583 return StringBufferSummaryProvider<StringPrinter::StringElementType::UTF32>(
584 stream, summary_options, location_sp, size, prefix_token: "L");
585 }
586 return false;
587}
588
589template <StringPrinter::StringElementType element_type>
590static bool
591LibcxxStringSummaryProvider(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(args&: size, args&: location_sp) = *string_info;
600
601 return StringBufferSummaryProvider<element_type>(
602 stream, summary_options, location_sp, size, prefix_token);
603}
604template <StringPrinter::StringElementType element_type>
605static 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
618bool 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, prefix_token: "");
623}
624
625bool 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, prefix_token: "u");
630}
631
632bool 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, prefix_token: "U");
637}
638
639static std::tuple<bool, ValueObjectSP, size_t>
640LibcxxExtractStringViewData(ValueObject& valobj) {
641 auto dataobj = GetChildMemberWithName(
642 obj&: valobj, alternative_names: {ConstString("__data_"), ConstString("__data")});
643 auto sizeobj = GetChildMemberWithName(
644 obj&: valobj, alternative_names: {ConstString("__size_"), ConstString("__size")});
645 if (!dataobj || !sizeobj)
646 return std::make_tuple<bool,ValueObjectSP,size_t>(args: false, args: {}, args: {});
647
648 if (!dataobj->GetError().Success() || !sizeobj->GetError().Success())
649 return std::make_tuple<bool,ValueObjectSP,size_t>(args: false, args: {}, args: {});
650
651 bool success{false};
652 uint64_t size = sizeobj->GetValueAsUnsigned(fail_value: 0, success: &success);
653 if (!success)
654 return std::make_tuple<bool,ValueObjectSP,size_t>(args: false, args: {}, args: {});
655
656 return std::make_tuple(args: true,args&: dataobj,args&: size);
657}
658
659template <StringPrinter::StringElementType element_type>
660static 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(args&: success, args&: dataobj, args&: 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
678bool 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, prefix_token: "");
683}
684
685bool 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, prefix_token: "u");
690}
691
692bool 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, prefix_token: "U");
697}
698
699bool 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(args&: success, args&: dataobj, args&: 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, location_sp: dataobj, size, prefix_token: "L");
721 case 2:
722 return StringBufferSummaryProvider<StringPrinter::StringElementType::UTF16>(
723 stream, summary_options, location_sp: dataobj, size, prefix_token: "L");
724 case 4:
725 return StringBufferSummaryProvider<StringPrinter::StringElementType::UTF32>(
726 stream, summary_options, location_sp: dataobj, size, prefix_token: "L");
727 }
728 return false;
729}
730
731static bool
732LibcxxChronoTimePointSecondsSummaryProvider(ValueObject &valobj, Stream &stream,
733 const TypeSummaryOptions &options,
734 const char *fmt) {
735 ValueObjectSP ptr_sp = valobj.GetChildMemberWithName(name: "__d_");
736 if (!ptr_sp)
737 return false;
738 ptr_sp = ptr_sp->GetChildMemberWithName(name: "__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(fail_value: 0);
759 if (seconds < chrono_timestamp_min || seconds > chrono_timestamp_max)
760 stream.Printf(format: "timestamp=%" PRId64 " s", static_cast<int64_t>(seconds));
761 else {
762 std::array<char, 128> str;
763 std::size_t size =
764 std::strftime(s: str.data(), maxsize: str.size(), format: fmt, tp: gmtime(timer: &seconds));
765 if (size == 0)
766 return false;
767
768 stream.Printf(format: "date/time=%s timestamp=%" PRId64 " s", str.data(),
769 static_cast<int64_t>(seconds));
770 }
771
772 return true;
773}
774
775bool lldb_private::formatters::LibcxxChronoSysSecondsSummaryProvider(
776 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
777 return LibcxxChronoTimePointSecondsSummaryProvider(valobj, stream, options,
778 fmt: "%FT%H:%M:%SZ");
779}
780
781bool lldb_private::formatters::LibcxxChronoLocalSecondsSummaryProvider(
782 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
783 return LibcxxChronoTimePointSecondsSummaryProvider(valobj, stream, options,
784 fmt: "%FT%H:%M:%S");
785}
786
787static bool
788LibcxxChronoTimepointDaysSummaryProvider(ValueObject &valobj, Stream &stream,
789 const TypeSummaryOptions &options,
790 const char *fmt) {
791 ValueObjectSP ptr_sp = valobj.GetChildMemberWithName(name: "__d_");
792 if (!ptr_sp)
793 return false;
794 ptr_sp = ptr_sp->GetChildMemberWithName(name: "__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(fail_value: 0);
811 if (days < chrono_timestamp_min || days > chrono_timestamp_max)
812 stream.Printf(format: "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(s: str.data(), maxsize: str.size(), format: fmt, tp: gmtime(timer: &seconds));
820 if (size == 0)
821 return false;
822
823 stream.Printf(format: "date=%s timestamp=%d days", str.data(), days);
824 }
825
826 return true;
827}
828
829bool lldb_private::formatters::LibcxxChronoSysDaysSummaryProvider(
830 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
831 return LibcxxChronoTimepointDaysSummaryProvider(valobj, stream, options,
832 fmt: "%FZ");
833}
834
835bool lldb_private::formatters::LibcxxChronoLocalDaysSummaryProvider(
836 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
837 return LibcxxChronoTimepointDaysSummaryProvider(valobj, stream, options,
838 fmt: "%F");
839}
840
841bool 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(name: "__m_");
850 if (!ptr_sp)
851 return false;
852
853 const unsigned month = ptr_sp->GetValueAsUnsigned(fail_value: 0);
854 if (month >= 1 && month <= 12)
855 stream << "month=" << months[month - 1];
856 else
857 stream.Printf(format: "month=%u", month);
858
859 return true;
860}
861
862bool 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(name: "__wd_");
871 if (!ptr_sp)
872 return false;
873
874 const unsigned weekday = ptr_sp->GetValueAsUnsigned(fail_value: 0);
875 if (weekday < 7)
876 stream << "weekday=" << weekdays[weekday];
877 else
878 stream.Printf(format: "weekday=%u", weekday);
879
880 return true;
881}
882
883bool lldb_private::formatters::LibcxxChronoYearMonthDaySummaryProvider(
884 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
885 ValueObjectSP ptr_sp = valobj.GetChildMemberWithName(name: "__y_");
886 if (!ptr_sp)
887 return false;
888 ptr_sp = ptr_sp->GetChildMemberWithName(name: "__y_");
889 if (!ptr_sp)
890 return false;
891 int year = ptr_sp->GetValueAsSigned(fail_value: 0);
892
893 ptr_sp = valobj.GetChildMemberWithName(name: "__m_");
894 if (!ptr_sp)
895 return false;
896 ptr_sp = ptr_sp->GetChildMemberWithName(name: "__m_");
897 if (!ptr_sp)
898 return false;
899 const unsigned month = ptr_sp->GetValueAsUnsigned(fail_value: 0);
900
901 ptr_sp = valobj.GetChildMemberWithName(name: "__d_");
902 if (!ptr_sp)
903 return false;
904 ptr_sp = ptr_sp->GetChildMemberWithName(name: "__d_");
905 if (!ptr_sp)
906 return false;
907 const unsigned day = ptr_sp->GetValueAsUnsigned(fail_value: 0);
908
909 stream << "date=";
910 if (year < 0) {
911 stream << '-';
912 year = -year;
913 }
914 stream.Printf(format: "%04d-%02u-%02u", year, month, day);
915
916 return true;
917}
918

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