1//===-- LibCxx.cpp --------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "LibCxx.h"
10
11#include "lldb/Core/Debugger.h"
12#include "lldb/Core/FormatEntity.h"
13#include "lldb/Core/ValueObject.h"
14#include "lldb/Core/ValueObjectConstResult.h"
15#include "lldb/DataFormatters/FormattersHelpers.h"
16#include "lldb/DataFormatters/StringPrinter.h"
17#include "lldb/DataFormatters/TypeSummary.h"
18#include "lldb/DataFormatters/VectorIterator.h"
19#include "lldb/Target/SectionLoadList.h"
20#include "lldb/Target/Target.h"
21#include "lldb/Utility/ConstString.h"
22#include "lldb/Utility/DataBufferHeap.h"
23#include "lldb/Utility/Endian.h"
24#include "lldb/Utility/Status.h"
25#include "lldb/Utility/Stream.h"
26
27#include "Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h"
28#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
29#include "lldb/lldb-enumerations.h"
30#include <optional>
31#include <tuple>
32
33using namespace lldb;
34using namespace lldb_private;
35using namespace lldb_private::formatters;
36
37lldb::ValueObjectSP lldb_private::formatters::GetChildMemberWithName(
38 ValueObject &obj, llvm::ArrayRef<ConstString> alternative_names) {
39 for (ConstString name : alternative_names) {
40 lldb::ValueObjectSP child_sp = obj.GetChildMemberWithName(name);
41
42 if (child_sp)
43 return child_sp;
44 }
45 return {};
46}
47
48lldb::ValueObjectSP
49lldb_private::formatters::GetFirstValueOfLibCXXCompressedPair(
50 ValueObject &pair) {
51 ValueObjectSP value;
52 ValueObjectSP first_child = pair.GetChildAtIndex(idx: 0);
53 if (first_child)
54 value = first_child->GetChildMemberWithName(name: "__value_");
55 if (!value) {
56 // pre-r300140 member name
57 value = pair.GetChildMemberWithName(name: "__first_");
58 }
59 return value;
60}
61
62lldb::ValueObjectSP
63lldb_private::formatters::GetSecondValueOfLibCXXCompressedPair(
64 ValueObject &pair) {
65 ValueObjectSP value;
66 if (pair.GetNumChildrenIgnoringErrors() > 1) {
67 ValueObjectSP second_child = pair.GetChildAtIndex(idx: 1);
68 if (second_child) {
69 value = second_child->GetChildMemberWithName(name: "__value_");
70 }
71 }
72 if (!value) {
73 // pre-r300140 member name
74 value = pair.GetChildMemberWithName(name: "__second_");
75 }
76 return value;
77}
78
79bool lldb_private::formatters::LibcxxFunctionSummaryProvider(
80 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
81
82 ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue());
83
84 if (!valobj_sp)
85 return false;
86
87 ExecutionContext exe_ctx(valobj_sp->GetExecutionContextRef());
88 Process *process = exe_ctx.GetProcessPtr();
89
90 if (process == nullptr)
91 return false;
92
93 CPPLanguageRuntime *cpp_runtime = CPPLanguageRuntime::Get(process&: *process);
94
95 if (!cpp_runtime)
96 return false;
97
98 CPPLanguageRuntime::LibCppStdFunctionCallableInfo callable_info =
99 cpp_runtime->FindLibCppStdFunctionCallableInfo(valobj_sp);
100
101 switch (callable_info.callable_case) {
102 case CPPLanguageRuntime::LibCppStdFunctionCallableCase::Invalid:
103 stream.Printf(format: " __f_ = %" PRIu64, callable_info.member_f_pointer_value);
104 return false;
105 break;
106 case CPPLanguageRuntime::LibCppStdFunctionCallableCase::Lambda:
107 stream.Printf(
108 format: " Lambda in File %s at Line %u",
109 callable_info.callable_line_entry.GetFile().GetFilename().GetCString(),
110 callable_info.callable_line_entry.line);
111 break;
112 case CPPLanguageRuntime::LibCppStdFunctionCallableCase::CallableObject:
113 stream.Printf(
114 format: " Function in File %s at Line %u",
115 callable_info.callable_line_entry.GetFile().GetFilename().GetCString(),
116 callable_info.callable_line_entry.line);
117 break;
118 case CPPLanguageRuntime::LibCppStdFunctionCallableCase::FreeOrMemberFunction:
119 stream.Printf(format: " Function = %s ",
120 callable_info.callable_symbol.GetName().GetCString());
121 break;
122 }
123
124 return true;
125}
126
127bool lldb_private::formatters::LibcxxSmartPointerSummaryProvider(
128 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
129 ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue());
130 if (!valobj_sp)
131 return false;
132 ValueObjectSP ptr_sp(valobj_sp->GetChildMemberWithName(name: "__ptr_"));
133 ValueObjectSP count_sp(
134 valobj_sp->GetChildAtNamePath(names: {"__cntrl_", "__shared_owners_"}));
135 ValueObjectSP weakcount_sp(
136 valobj_sp->GetChildAtNamePath(names: {"__cntrl_", "__shared_weak_owners_"}));
137
138 if (!ptr_sp)
139 return false;
140
141 if (ptr_sp->GetValueAsUnsigned(fail_value: 0) == 0) {
142 stream.Printf(format: "nullptr");
143 return true;
144 } else {
145 bool print_pointee = false;
146 Status error;
147 ValueObjectSP pointee_sp = ptr_sp->Dereference(error);
148 if (pointee_sp && error.Success()) {
149 if (pointee_sp->DumpPrintableRepresentation(
150 s&: stream, val_obj_display: ValueObject::eValueObjectRepresentationStyleSummary,
151 custom_format: lldb::eFormatInvalid,
152 special: ValueObject::PrintableRepresentationSpecialCases::eDisable,
153 do_dump_error: false))
154 print_pointee = true;
155 }
156 if (!print_pointee)
157 stream.Printf(format: "ptr = 0x%" PRIx64, ptr_sp->GetValueAsUnsigned(fail_value: 0));
158 }
159
160 if (count_sp)
161 stream.Printf(format: " strong=%" PRIu64, 1 + count_sp->GetValueAsUnsigned(fail_value: 0));
162
163 if (weakcount_sp)
164 stream.Printf(format: " weak=%" PRIu64, 1 + weakcount_sp->GetValueAsUnsigned(fail_value: 0));
165
166 return true;
167}
168
169bool lldb_private::formatters::LibcxxUniquePointerSummaryProvider(
170 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
171 ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue());
172 if (!valobj_sp)
173 return false;
174
175 ValueObjectSP ptr_sp(valobj_sp->GetChildMemberWithName(name: "__ptr_"));
176 if (!ptr_sp)
177 return false;
178
179 ptr_sp = GetFirstValueOfLibCXXCompressedPair(pair&: *ptr_sp);
180 if (!ptr_sp)
181 return false;
182
183 if (ptr_sp->GetValueAsUnsigned(fail_value: 0) == 0) {
184 stream.Printf(format: "nullptr");
185 return true;
186 } else {
187 bool print_pointee = false;
188 Status error;
189 ValueObjectSP pointee_sp = ptr_sp->Dereference(error);
190 if (pointee_sp && error.Success()) {
191 if (pointee_sp->DumpPrintableRepresentation(
192 s&: stream, val_obj_display: ValueObject::eValueObjectRepresentationStyleSummary,
193 custom_format: lldb::eFormatInvalid,
194 special: ValueObject::PrintableRepresentationSpecialCases::eDisable,
195 do_dump_error: false))
196 print_pointee = true;
197 }
198 if (!print_pointee)
199 stream.Printf(format: "ptr = 0x%" PRIx64, ptr_sp->GetValueAsUnsigned(fail_value: 0));
200 }
201
202 return true;
203}
204
205/*
206 (lldb) fr var ibeg --raw --ptr-depth 1
207 (std::__1::__map_iterator<std::__1::__tree_iterator<std::__1::pair<int,
208 std::__1::basic_string<char, std::__1::char_traits<char>,
209 std::__1::allocator<char> > >, std::__1::__tree_node<std::__1::pair<int,
210 std::__1::basic_string<char, std::__1::char_traits<char>,
211 std::__1::allocator<char> > >, void *> *, long> >) ibeg = {
212 __i_ = {
213 __ptr_ = 0x0000000100103870 {
214 std::__1::__tree_node_base<void *> = {
215 std::__1::__tree_end_node<std::__1::__tree_node_base<void *> *> = {
216 __left_ = 0x0000000000000000
217 }
218 __right_ = 0x0000000000000000
219 __parent_ = 0x00000001001038b0
220 __is_black_ = true
221 }
222 __value_ = {
223 first = 0
224 second = { std::string }
225 */
226
227lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::
228 LibCxxMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
229 : SyntheticChildrenFrontEnd(*valobj_sp), m_pair_ptr(), m_pair_sp() {
230 if (valobj_sp)
231 Update();
232}
233
234lldb::ChildCacheState
235lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::Update() {
236 m_pair_sp.reset();
237 m_pair_ptr = nullptr;
238
239 ValueObjectSP valobj_sp = m_backend.GetSP();
240 if (!valobj_sp)
241 return lldb::ChildCacheState::eRefetch;
242
243 TargetSP target_sp(valobj_sp->GetTargetSP());
244
245 if (!target_sp)
246 return lldb::ChildCacheState::eRefetch;
247
248 if (!valobj_sp)
249 return lldb::ChildCacheState::eRefetch;
250
251 // this must be a ValueObject* because it is a child of the ValueObject we
252 // are producing children for it if were a ValueObjectSP, we would end up
253 // with a loop (iterator -> synthetic -> child -> parent == iterator) and
254 // that would in turn leak memory by never allowing the ValueObjects to die
255 // and free their memory
256 m_pair_ptr = valobj_sp
257 ->GetValueForExpressionPath(
258 expression: ".__i_.__ptr_->__value_", reason_to_stop: nullptr, final_value_type: nullptr,
259 options: ValueObject::GetValueForExpressionPathOptions()
260 .DontCheckDotVsArrowSyntax()
261 .SetSyntheticChildrenTraversal(
262 ValueObject::GetValueForExpressionPathOptions::
263 SyntheticChildrenTraversal::None),
264 final_task_on_target: nullptr)
265 .get();
266
267 if (!m_pair_ptr) {
268 m_pair_ptr = valobj_sp
269 ->GetValueForExpressionPath(
270 expression: ".__i_.__ptr_", reason_to_stop: nullptr, final_value_type: nullptr,
271 options: ValueObject::GetValueForExpressionPathOptions()
272 .DontCheckDotVsArrowSyntax()
273 .SetSyntheticChildrenTraversal(
274 ValueObject::GetValueForExpressionPathOptions::
275 SyntheticChildrenTraversal::None),
276 final_task_on_target: nullptr)
277 .get();
278 if (m_pair_ptr) {
279 auto __i_(valobj_sp->GetChildMemberWithName(name: "__i_"));
280 if (!__i_) {
281 m_pair_ptr = nullptr;
282 return lldb::ChildCacheState::eRefetch;
283 }
284 CompilerType pair_type(
285 __i_->GetCompilerType().GetTypeTemplateArgument(idx: 0));
286 std::string name;
287 uint64_t bit_offset_ptr;
288 uint32_t bitfield_bit_size_ptr;
289 bool is_bitfield_ptr;
290 pair_type = pair_type.GetFieldAtIndex(
291 idx: 0, name, bit_offset_ptr: &bit_offset_ptr, bitfield_bit_size_ptr: &bitfield_bit_size_ptr, is_bitfield_ptr: &is_bitfield_ptr);
292 if (!pair_type) {
293 m_pair_ptr = nullptr;
294 return lldb::ChildCacheState::eRefetch;
295 }
296
297 auto addr(m_pair_ptr->GetValueAsUnsigned(LLDB_INVALID_ADDRESS));
298 m_pair_ptr = nullptr;
299 if (addr && addr != LLDB_INVALID_ADDRESS) {
300 auto ts = pair_type.GetTypeSystem();
301 auto ast_ctx = ts.dyn_cast_or_null<TypeSystemClang>();
302 if (!ast_ctx)
303 return lldb::ChildCacheState::eRefetch;
304
305 // Mimick layout of std::__tree_iterator::__ptr_ and read it in
306 // from process memory.
307 //
308 // The following shows the contiguous block of memory:
309 //
310 // +-----------------------------+ class __tree_end_node
311 // __ptr_ | pointer __left_; |
312 // +-----------------------------+ class __tree_node_base
313 // | pointer __right_; |
314 // | __parent_pointer __parent_; |
315 // | bool __is_black_; |
316 // +-----------------------------+ class __tree_node
317 // | __node_value_type __value_; | <<< our key/value pair
318 // +-----------------------------+
319 //
320 CompilerType tree_node_type = ast_ctx->CreateStructForIdentifier(
321 type_name: llvm::StringRef(),
322 type_fields: {{"ptr0",
323 ast_ctx->GetBasicType(type: lldb::eBasicTypeVoid).GetPointerType()},
324 {"ptr1",
325 ast_ctx->GetBasicType(type: lldb::eBasicTypeVoid).GetPointerType()},
326 {"ptr2",
327 ast_ctx->GetBasicType(type: lldb::eBasicTypeVoid).GetPointerType()},
328 {"cw", ast_ctx->GetBasicType(type: lldb::eBasicTypeBool)},
329 {"payload", pair_type}});
330 std::optional<uint64_t> size = tree_node_type.GetByteSize(exe_scope: nullptr);
331 if (!size)
332 return lldb::ChildCacheState::eRefetch;
333 WritableDataBufferSP buffer_sp(new DataBufferHeap(*size, 0));
334 ProcessSP process_sp(target_sp->GetProcessSP());
335 Status error;
336 process_sp->ReadMemory(vm_addr: addr, buf: buffer_sp->GetBytes(),
337 size: buffer_sp->GetByteSize(), error);
338 if (error.Fail())
339 return lldb::ChildCacheState::eRefetch;
340 DataExtractor extractor(buffer_sp, process_sp->GetByteOrder(),
341 process_sp->GetAddressByteSize());
342 auto pair_sp = CreateValueObjectFromData(
343 name: "pair", data: extractor, exe_ctx: valobj_sp->GetExecutionContextRef(),
344 type: tree_node_type);
345 if (pair_sp)
346 m_pair_sp = pair_sp->GetChildAtIndex(idx: 4);
347 }
348 }
349 }
350
351 return lldb::ChildCacheState::eRefetch;
352}
353
354llvm::Expected<uint32_t> lldb_private::formatters::
355 LibCxxMapIteratorSyntheticFrontEnd::CalculateNumChildren() {
356 return 2;
357}
358
359lldb::ValueObjectSP
360lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::GetChildAtIndex(
361 uint32_t idx) {
362 if (m_pair_ptr)
363 return m_pair_ptr->GetChildAtIndex(idx);
364 if (m_pair_sp)
365 return m_pair_sp->GetChildAtIndex(idx);
366 return lldb::ValueObjectSP();
367}
368
369bool lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::
370 MightHaveChildren() {
371 return true;
372}
373
374size_t lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::
375 GetIndexOfChildWithName(ConstString name) {
376 if (name == "first")
377 return 0;
378 if (name == "second")
379 return 1;
380 return UINT32_MAX;
381}
382
383lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::
384 ~LibCxxMapIteratorSyntheticFrontEnd() {
385 // this will be deleted when its parent dies (since it's a child object)
386 // delete m_pair_ptr;
387}
388
389SyntheticChildrenFrontEnd *
390lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEndCreator(
391 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
392 return (valobj_sp ? new LibCxxMapIteratorSyntheticFrontEnd(valobj_sp)
393 : nullptr);
394}
395
396lldb_private::formatters::LibCxxUnorderedMapIteratorSyntheticFrontEnd::
397 LibCxxUnorderedMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
398 : SyntheticChildrenFrontEnd(*valobj_sp) {
399 if (valobj_sp)
400 Update();
401}
402
403lldb::ChildCacheState lldb_private::formatters::
404 LibCxxUnorderedMapIteratorSyntheticFrontEnd::Update() {
405 m_pair_sp.reset();
406 m_iter_ptr = nullptr;
407
408 ValueObjectSP valobj_sp = m_backend.GetSP();
409 if (!valobj_sp)
410 return lldb::ChildCacheState::eRefetch;
411
412 TargetSP target_sp(valobj_sp->GetTargetSP());
413
414 if (!target_sp)
415 return lldb::ChildCacheState::eRefetch;
416
417 if (!valobj_sp)
418 return lldb::ChildCacheState::eRefetch;
419
420 auto exprPathOptions = ValueObject::GetValueForExpressionPathOptions()
421 .DontCheckDotVsArrowSyntax()
422 .SetSyntheticChildrenTraversal(
423 ValueObject::GetValueForExpressionPathOptions::
424 SyntheticChildrenTraversal::None);
425
426 // This must be a ValueObject* because it is a child of the ValueObject we
427 // are producing children for it if were a ValueObjectSP, we would end up
428 // with a loop (iterator -> synthetic -> child -> parent == iterator) and
429 // that would in turn leak memory by never allowing the ValueObjects to die
430 // and free their memory.
431 m_iter_ptr =
432 valobj_sp
433 ->GetValueForExpressionPath(expression: ".__i_.__node_", reason_to_stop: nullptr, final_value_type: nullptr,
434 options: exprPathOptions, final_task_on_target: nullptr)
435 .get();
436
437 if (m_iter_ptr) {
438 auto iter_child(valobj_sp->GetChildMemberWithName(name: "__i_"));
439 if (!iter_child) {
440 m_iter_ptr = nullptr;
441 return lldb::ChildCacheState::eRefetch;
442 }
443
444 CompilerType node_type(iter_child->GetCompilerType()
445 .GetTypeTemplateArgument(idx: 0)
446 .GetPointeeType());
447
448 CompilerType pair_type(node_type.GetTypeTemplateArgument(idx: 0));
449
450 std::string name;
451 uint64_t bit_offset_ptr;
452 uint32_t bitfield_bit_size_ptr;
453 bool is_bitfield_ptr;
454
455 pair_type = pair_type.GetFieldAtIndex(
456 idx: 0, name, bit_offset_ptr: &bit_offset_ptr, bitfield_bit_size_ptr: &bitfield_bit_size_ptr, is_bitfield_ptr: &is_bitfield_ptr);
457 if (!pair_type) {
458 m_iter_ptr = nullptr;
459 return lldb::ChildCacheState::eRefetch;
460 }
461
462 uint64_t addr = m_iter_ptr->GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
463 m_iter_ptr = nullptr;
464
465 if (addr == 0 || addr == LLDB_INVALID_ADDRESS)
466 return lldb::ChildCacheState::eRefetch;
467
468 auto ts = pair_type.GetTypeSystem();
469 auto ast_ctx = ts.dyn_cast_or_null<TypeSystemClang>();
470 if (!ast_ctx)
471 return lldb::ChildCacheState::eRefetch;
472
473 // Mimick layout of std::__hash_iterator::__node_ and read it in
474 // from process memory.
475 //
476 // The following shows the contiguous block of memory:
477 //
478 // +-----------------------------+ class __hash_node_base
479 // __node_ | __next_pointer __next_; |
480 // +-----------------------------+ class __hash_node
481 // | size_t __hash_; |
482 // | __node_value_type __value_; | <<< our key/value pair
483 // +-----------------------------+
484 //
485 CompilerType tree_node_type = ast_ctx->CreateStructForIdentifier(
486 type_name: llvm::StringRef(),
487 type_fields: {{"__next_",
488 ast_ctx->GetBasicType(type: lldb::eBasicTypeVoid).GetPointerType()},
489 {"__hash_", ast_ctx->GetBasicType(type: lldb::eBasicTypeUnsignedLongLong)},
490 {"__value_", pair_type}});
491 std::optional<uint64_t> size = tree_node_type.GetByteSize(exe_scope: nullptr);
492 if (!size)
493 return lldb::ChildCacheState::eRefetch;
494 WritableDataBufferSP buffer_sp(new DataBufferHeap(*size, 0));
495 ProcessSP process_sp(target_sp->GetProcessSP());
496 Status error;
497 process_sp->ReadMemory(vm_addr: addr, buf: buffer_sp->GetBytes(),
498 size: buffer_sp->GetByteSize(), error);
499 if (error.Fail())
500 return lldb::ChildCacheState::eRefetch;
501 DataExtractor extractor(buffer_sp, process_sp->GetByteOrder(),
502 process_sp->GetAddressByteSize());
503 auto pair_sp = CreateValueObjectFromData(
504 name: "pair", data: extractor, exe_ctx: valobj_sp->GetExecutionContextRef(), type: tree_node_type);
505 if (pair_sp)
506 m_pair_sp = pair_sp->GetChildAtIndex(idx: 2);
507 }
508
509 return lldb::ChildCacheState::eRefetch;
510}
511
512llvm::Expected<uint32_t> lldb_private::formatters::
513 LibCxxUnorderedMapIteratorSyntheticFrontEnd::CalculateNumChildren() {
514 return 2;
515}
516
517lldb::ValueObjectSP lldb_private::formatters::
518 LibCxxUnorderedMapIteratorSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) {
519 if (m_pair_sp)
520 return m_pair_sp->GetChildAtIndex(idx);
521 return lldb::ValueObjectSP();
522}
523
524bool lldb_private::formatters::LibCxxUnorderedMapIteratorSyntheticFrontEnd::
525 MightHaveChildren() {
526 return true;
527}
528
529size_t lldb_private::formatters::LibCxxUnorderedMapIteratorSyntheticFrontEnd::
530 GetIndexOfChildWithName(ConstString name) {
531 if (name == "first")
532 return 0;
533 if (name == "second")
534 return 1;
535 return UINT32_MAX;
536}
537
538SyntheticChildrenFrontEnd *
539lldb_private::formatters::LibCxxUnorderedMapIteratorSyntheticFrontEndCreator(
540 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
541 return (valobj_sp ? new LibCxxUnorderedMapIteratorSyntheticFrontEnd(valobj_sp)
542 : nullptr);
543}
544
545/*
546 (lldb) fr var ibeg --raw --ptr-depth 1 -T
547 (std::__1::__wrap_iter<int *>) ibeg = {
548 (std::__1::__wrap_iter<int *>::iterator_type) __i = 0x00000001001037a0 {
549 (int) *__i = 1
550 }
551 }
552*/
553
554SyntheticChildrenFrontEnd *
555lldb_private::formatters::LibCxxVectorIteratorSyntheticFrontEndCreator(
556 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
557 return (valobj_sp ? new VectorIteratorSyntheticFrontEnd(
558 valobj_sp, {ConstString("__i_"), ConstString("__i")})
559 : nullptr);
560}
561
562lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::
563 LibcxxSharedPtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
564 : SyntheticChildrenFrontEnd(*valobj_sp), m_cntrl(nullptr) {
565 if (valobj_sp)
566 Update();
567}
568
569llvm::Expected<uint32_t> lldb_private::formatters::
570 LibcxxSharedPtrSyntheticFrontEnd::CalculateNumChildren() {
571 return (m_cntrl ? 1 : 0);
572}
573
574lldb::ValueObjectSP
575lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::GetChildAtIndex(
576 uint32_t idx) {
577 if (!m_cntrl)
578 return lldb::ValueObjectSP();
579
580 ValueObjectSP valobj_sp = m_backend.GetSP();
581 if (!valobj_sp)
582 return lldb::ValueObjectSP();
583
584 if (idx == 0)
585 return valobj_sp->GetChildMemberWithName(name: "__ptr_");
586
587 if (idx == 1) {
588 if (auto ptr_sp = valobj_sp->GetChildMemberWithName(name: "__ptr_")) {
589 Status status;
590 auto value_type_sp =
591 valobj_sp->GetCompilerType()
592 .GetTypeTemplateArgument(idx: 0).GetPointerType();
593 ValueObjectSP cast_ptr_sp = ptr_sp->Cast(compiler_type: value_type_sp);
594 ValueObjectSP value_sp = cast_ptr_sp->Dereference(error&: status);
595 if (status.Success()) {
596 return value_sp;
597 }
598 }
599 }
600
601 return lldb::ValueObjectSP();
602}
603
604lldb::ChildCacheState
605lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::Update() {
606 m_cntrl = nullptr;
607
608 ValueObjectSP valobj_sp = m_backend.GetSP();
609 if (!valobj_sp)
610 return lldb::ChildCacheState::eRefetch;
611
612 TargetSP target_sp(valobj_sp->GetTargetSP());
613 if (!target_sp)
614 return lldb::ChildCacheState::eRefetch;
615
616 lldb::ValueObjectSP cntrl_sp(valobj_sp->GetChildMemberWithName(name: "__cntrl_"));
617
618 m_cntrl = cntrl_sp.get(); // need to store the raw pointer to avoid a circular
619 // dependency
620 return lldb::ChildCacheState::eRefetch;
621}
622
623bool lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::
624 MightHaveChildren() {
625 return true;
626}
627
628size_t lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::
629 GetIndexOfChildWithName(ConstString name) {
630 if (name == "__ptr_")
631 return 0;
632 if (name == "$$dereference$$")
633 return 1;
634 return UINT32_MAX;
635}
636
637lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::
638 ~LibcxxSharedPtrSyntheticFrontEnd() = default;
639
640SyntheticChildrenFrontEnd *
641lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEndCreator(
642 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
643 return (valobj_sp ? new LibcxxSharedPtrSyntheticFrontEnd(valobj_sp)
644 : nullptr);
645}
646
647lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::
648 LibcxxUniquePtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
649 : SyntheticChildrenFrontEnd(*valobj_sp) {
650 if (valobj_sp)
651 Update();
652}
653
654lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::
655 ~LibcxxUniquePtrSyntheticFrontEnd() = default;
656
657SyntheticChildrenFrontEnd *
658lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEndCreator(
659 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
660 return (valobj_sp ? new LibcxxUniquePtrSyntheticFrontEnd(valobj_sp)
661 : nullptr);
662}
663
664llvm::Expected<uint32_t> lldb_private::formatters::
665 LibcxxUniquePtrSyntheticFrontEnd::CalculateNumChildren() {
666 if (m_value_ptr_sp)
667 return m_deleter_sp ? 2 : 1;
668 return 0;
669}
670
671lldb::ValueObjectSP
672lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::GetChildAtIndex(
673 uint32_t idx) {
674 if (!m_value_ptr_sp)
675 return lldb::ValueObjectSP();
676
677 if (idx == 0)
678 return m_value_ptr_sp;
679
680 if (idx == 1)
681 return m_deleter_sp;
682
683 if (idx == 2) {
684 Status status;
685 auto value_sp = m_value_ptr_sp->Dereference(error&: status);
686 if (status.Success()) {
687 return value_sp;
688 }
689 }
690
691 return lldb::ValueObjectSP();
692}
693
694lldb::ChildCacheState
695lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::Update() {
696 ValueObjectSP valobj_sp = m_backend.GetSP();
697 if (!valobj_sp)
698 return lldb::ChildCacheState::eRefetch;
699
700 ValueObjectSP ptr_sp(valobj_sp->GetChildMemberWithName(name: "__ptr_"));
701 if (!ptr_sp)
702 return lldb::ChildCacheState::eRefetch;
703
704 // Retrieve the actual pointer and the deleter, and clone them to give them
705 // user-friendly names.
706 ValueObjectSP value_pointer_sp = GetFirstValueOfLibCXXCompressedPair(pair&: *ptr_sp);
707 if (value_pointer_sp)
708 m_value_ptr_sp = value_pointer_sp->Clone(new_name: ConstString("pointer"));
709
710 ValueObjectSP deleter_sp = GetSecondValueOfLibCXXCompressedPair(pair&: *ptr_sp);
711 if (deleter_sp)
712 m_deleter_sp = deleter_sp->Clone(new_name: ConstString("deleter"));
713
714 return lldb::ChildCacheState::eRefetch;
715}
716
717bool lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::
718 MightHaveChildren() {
719 return true;
720}
721
722size_t lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::
723 GetIndexOfChildWithName(ConstString name) {
724 if (name == "pointer")
725 return 0;
726 if (name == "deleter")
727 return 1;
728 if (name == "$$dereference$$")
729 return 2;
730 return UINT32_MAX;
731}
732
733bool lldb_private::formatters::LibcxxContainerSummaryProvider(
734 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
735 if (valobj.IsPointerType()) {
736 uint64_t value = valobj.GetValueAsUnsigned(fail_value: 0);
737 if (!value)
738 return false;
739 stream.Printf(format: "0x%016" PRIx64 " ", value);
740 }
741 return FormatEntity::FormatStringRef(format: "size=${svar%#}", s&: stream, sc: nullptr,
742 exe_ctx: nullptr, addr: nullptr, valobj: &valobj, function_changed: false, initial_function: false);
743}
744
745/// The field layout in a libc++ string (cap, side, data or data, size, cap).
746namespace {
747enum class StringLayout { CSD, DSC };
748}
749
750/// Determine the size in bytes of \p valobj (a libc++ std::string object) and
751/// extract its data payload. Return the size + payload pair.
752// TODO: Support big-endian architectures.
753static std::optional<std::pair<uint64_t, ValueObjectSP>>
754ExtractLibcxxStringInfo(ValueObject &valobj) {
755 ValueObjectSP valobj_r_sp = valobj.GetChildMemberWithName(name: "__r_");
756 if (!valobj_r_sp || !valobj_r_sp->GetError().Success())
757 return {};
758
759 // __r_ is a compressed_pair of the actual data and the allocator. The data we
760 // want is in the first base class.
761 ValueObjectSP valobj_r_base_sp = valobj_r_sp->GetChildAtIndex(idx: 0);
762 if (!valobj_r_base_sp)
763 return {};
764
765 ValueObjectSP valobj_rep_sp =
766 valobj_r_base_sp->GetChildMemberWithName(name: "__value_");
767 if (!valobj_rep_sp)
768 return {};
769
770 ValueObjectSP l = valobj_rep_sp->GetChildMemberWithName(name: "__l");
771 if (!l)
772 return {};
773
774 StringLayout layout = l->GetIndexOfChildWithName(name: "__data_") == 0
775 ? StringLayout::DSC
776 : StringLayout::CSD;
777
778 bool short_mode = false; // this means the string is in short-mode and the
779 // data is stored inline
780 bool using_bitmasks = true; // Whether the class uses bitmasks for the mode
781 // flag (pre-D123580).
782 uint64_t size;
783 uint64_t size_mode_value = 0;
784
785 ValueObjectSP short_sp = valobj_rep_sp->GetChildMemberWithName(name: "__s");
786 if (!short_sp)
787 return {};
788
789 ValueObjectSP is_long = short_sp->GetChildMemberWithName(name: "__is_long_");
790 ValueObjectSP size_sp = short_sp->GetChildMemberWithName(name: "__size_");
791 if (!size_sp)
792 return {};
793
794 if (is_long) {
795 using_bitmasks = false;
796 short_mode = !is_long->GetValueAsUnsigned(/*fail_value=*/0);
797 size = size_sp->GetValueAsUnsigned(/*fail_value=*/0);
798 } else {
799 // The string mode is encoded in the size field.
800 size_mode_value = size_sp->GetValueAsUnsigned(fail_value: 0);
801 uint8_t mode_mask = layout == StringLayout::DSC ? 0x80 : 1;
802 short_mode = (size_mode_value & mode_mask) == 0;
803 }
804
805 if (short_mode) {
806 ValueObjectSP location_sp = short_sp->GetChildMemberWithName(name: "__data_");
807 if (using_bitmasks)
808 size = (layout == StringLayout::DSC) ? size_mode_value
809 : ((size_mode_value >> 1) % 256);
810
811 // When the small-string optimization takes place, the data must fit in the
812 // inline string buffer (23 bytes on x86_64/Darwin). If it doesn't, it's
813 // likely that the string isn't initialized and we're reading garbage.
814 ExecutionContext exe_ctx(location_sp->GetExecutionContextRef());
815 const std::optional<uint64_t> max_bytes =
816 location_sp->GetCompilerType().GetByteSize(
817 exe_scope: exe_ctx.GetBestExecutionContextScope());
818 if (!max_bytes || size > *max_bytes || !location_sp)
819 return {};
820
821 return std::make_pair(x&: size, y&: location_sp);
822 }
823
824 // we can use the layout_decider object as the data pointer
825 ValueObjectSP location_sp = l->GetChildMemberWithName(name: "__data_");
826 ValueObjectSP size_vo = l->GetChildMemberWithName(name: "__size_");
827 ValueObjectSP capacity_vo = l->GetChildMemberWithName(name: "__cap_");
828 if (!size_vo || !location_sp || !capacity_vo)
829 return {};
830 size = size_vo->GetValueAsUnsigned(LLDB_INVALID_OFFSET);
831 uint64_t capacity = capacity_vo->GetValueAsUnsigned(LLDB_INVALID_OFFSET);
832 if (!using_bitmasks && layout == StringLayout::CSD)
833 capacity *= 2;
834 if (size == LLDB_INVALID_OFFSET || capacity == LLDB_INVALID_OFFSET ||
835 capacity < size)
836 return {};
837 return std::make_pair(x&: size, y&: location_sp);
838}
839
840static bool
841LibcxxWStringSummaryProvider(ValueObject &valobj, Stream &stream,
842 const TypeSummaryOptions &summary_options,
843 ValueObjectSP location_sp, size_t size) {
844 if (size == 0) {
845 stream.Printf(format: "L\"\"");
846 return true;
847 }
848 if (!location_sp)
849 return false;
850
851 StringPrinter::ReadBufferAndDumpToStreamOptions options(valobj);
852 if (summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryCapped) {
853 const auto max_size = valobj.GetTargetSP()->GetMaximumSizeOfStringSummary();
854 if (size > max_size) {
855 size = max_size;
856 options.SetIsTruncated(true);
857 }
858 }
859
860 DataExtractor extractor;
861 const size_t bytes_read = location_sp->GetPointeeData(data&: extractor, item_idx: 0, item_count: size);
862 if (bytes_read < size)
863 return false;
864
865 // std::wstring::size() is measured in 'characters', not bytes
866 TypeSystemClangSP scratch_ts_sp =
867 ScratchTypeSystemClang::GetForTarget(target&: *valobj.GetTargetSP());
868 if (!scratch_ts_sp)
869 return false;
870
871 auto wchar_t_size =
872 scratch_ts_sp->GetBasicType(type: lldb::eBasicTypeWChar).GetByteSize(exe_scope: nullptr);
873 if (!wchar_t_size)
874 return false;
875
876 options.SetData(std::move(extractor));
877 options.SetStream(&stream);
878 options.SetPrefixToken("L");
879 options.SetQuote('"');
880 options.SetSourceSize(size);
881 options.SetBinaryZeroIsTerminator(false);
882
883 switch (*wchar_t_size) {
884 case 1:
885 return StringPrinter::ReadBufferAndDumpToStream<
886 lldb_private::formatters::StringPrinter::StringElementType::UTF8>(
887 options);
888 break;
889
890 case 2:
891 return StringPrinter::ReadBufferAndDumpToStream<
892 lldb_private::formatters::StringPrinter::StringElementType::UTF16>(
893 options);
894 break;
895
896 case 4:
897 return StringPrinter::ReadBufferAndDumpToStream<
898 lldb_private::formatters::StringPrinter::StringElementType::UTF32>(
899 options);
900 }
901 return false;
902}
903
904bool lldb_private::formatters::LibcxxWStringSummaryProvider(
905 ValueObject &valobj, Stream &stream,
906 const TypeSummaryOptions &summary_options) {
907 auto string_info = ExtractLibcxxStringInfo(valobj);
908 if (!string_info)
909 return false;
910 uint64_t size;
911 ValueObjectSP location_sp;
912 std::tie(args&: size, args&: location_sp) = *string_info;
913
914 return ::LibcxxWStringSummaryProvider(valobj, stream, summary_options,
915 location_sp, size);
916}
917
918template <StringPrinter::StringElementType element_type>
919static bool
920LibcxxStringSummaryProvider(ValueObject &valobj, Stream &stream,
921 const TypeSummaryOptions &summary_options,
922 std::string prefix_token, ValueObjectSP location_sp,
923 uint64_t size) {
924
925 if (size == 0) {
926 stream.Printf(format: "\"\"");
927 return true;
928 }
929
930 if (!location_sp)
931 return false;
932
933 StringPrinter::ReadBufferAndDumpToStreamOptions options(valobj);
934
935 if (summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryCapped) {
936 const auto max_size = valobj.GetTargetSP()->GetMaximumSizeOfStringSummary();
937 if (size > max_size) {
938 size = max_size;
939 options.SetIsTruncated(true);
940 }
941 }
942
943 {
944 DataExtractor extractor;
945 const size_t bytes_read = location_sp->GetPointeeData(data&: extractor, item_idx: 0, item_count: size);
946 if (bytes_read < size)
947 return false;
948
949 options.SetData(std::move(extractor));
950 }
951 options.SetStream(&stream);
952 if (prefix_token.empty())
953 options.SetPrefixToken(nullptr);
954 else
955 options.SetPrefixToken(prefix_token);
956 options.SetQuote('"');
957 options.SetSourceSize(size);
958 options.SetBinaryZeroIsTerminator(false);
959 return StringPrinter::ReadBufferAndDumpToStream<element_type>(options);
960}
961
962template <StringPrinter::StringElementType element_type>
963static bool
964LibcxxStringSummaryProvider(ValueObject &valobj, Stream &stream,
965 const TypeSummaryOptions &summary_options,
966 std::string prefix_token) {
967 auto string_info = ExtractLibcxxStringInfo(valobj);
968 if (!string_info)
969 return false;
970 uint64_t size;
971 ValueObjectSP location_sp;
972 std::tie(args&: size, args&: location_sp) = *string_info;
973
974 return LibcxxStringSummaryProvider<element_type>(
975 valobj, stream, summary_options, prefix_token, location_sp, size);
976}
977template <StringPrinter::StringElementType element_type>
978static bool formatStringImpl(ValueObject &valobj, Stream &stream,
979 const TypeSummaryOptions &summary_options,
980 std::string prefix_token) {
981 StreamString scratch_stream;
982 const bool success = LibcxxStringSummaryProvider<element_type>(
983 valobj, scratch_stream, summary_options, prefix_token);
984 if (success)
985 stream << scratch_stream.GetData();
986 else
987 stream << "Summary Unavailable";
988 return true;
989}
990
991bool lldb_private::formatters::LibcxxStringSummaryProviderASCII(
992 ValueObject &valobj, Stream &stream,
993 const TypeSummaryOptions &summary_options) {
994 return formatStringImpl<StringPrinter::StringElementType::ASCII>(
995 valobj, stream, summary_options, prefix_token: "");
996}
997
998bool lldb_private::formatters::LibcxxStringSummaryProviderUTF16(
999 ValueObject &valobj, Stream &stream,
1000 const TypeSummaryOptions &summary_options) {
1001 return formatStringImpl<StringPrinter::StringElementType::UTF16>(
1002 valobj, stream, summary_options, prefix_token: "u");
1003}
1004
1005bool lldb_private::formatters::LibcxxStringSummaryProviderUTF32(
1006 ValueObject &valobj, Stream &stream,
1007 const TypeSummaryOptions &summary_options) {
1008 return formatStringImpl<StringPrinter::StringElementType::UTF32>(
1009 valobj, stream, summary_options, prefix_token: "U");
1010}
1011
1012static std::tuple<bool, ValueObjectSP, size_t>
1013LibcxxExtractStringViewData(ValueObject& valobj) {
1014 auto dataobj = GetChildMemberWithName(
1015 obj&: valobj, alternative_names: {ConstString("__data_"), ConstString("__data")});
1016 auto sizeobj = GetChildMemberWithName(
1017 obj&: valobj, alternative_names: {ConstString("__size_"), ConstString("__size")});
1018 if (!dataobj || !sizeobj)
1019 return std::make_tuple<bool,ValueObjectSP,size_t>(args: false, args: {}, args: {});
1020
1021 if (!dataobj->GetError().Success() || !sizeobj->GetError().Success())
1022 return std::make_tuple<bool,ValueObjectSP,size_t>(args: false, args: {}, args: {});
1023
1024 bool success{false};
1025 uint64_t size = sizeobj->GetValueAsUnsigned(fail_value: 0, success: &success);
1026 if (!success)
1027 return std::make_tuple<bool,ValueObjectSP,size_t>(args: false, args: {}, args: {});
1028
1029 return std::make_tuple(args: true,args&: dataobj,args&: size);
1030}
1031
1032template <StringPrinter::StringElementType element_type>
1033static bool formatStringViewImpl(ValueObject &valobj, Stream &stream,
1034 const TypeSummaryOptions &summary_options,
1035 std::string prefix_token) {
1036
1037 bool success;
1038 ValueObjectSP dataobj;
1039 size_t size;
1040 std::tie(args&: success, args&: dataobj, args&: size) = LibcxxExtractStringViewData(valobj);
1041
1042 if (!success) {
1043 stream << "Summary Unavailable";
1044 return true;
1045 }
1046
1047 return LibcxxStringSummaryProvider<element_type>(
1048 valobj, stream, summary_options, prefix_token, dataobj, size);
1049}
1050
1051bool lldb_private::formatters::LibcxxStringViewSummaryProviderASCII(
1052 ValueObject &valobj, Stream &stream,
1053 const TypeSummaryOptions &summary_options) {
1054 return formatStringViewImpl<StringPrinter::StringElementType::ASCII>(
1055 valobj, stream, summary_options, prefix_token: "");
1056}
1057
1058bool lldb_private::formatters::LibcxxStringViewSummaryProviderUTF16(
1059 ValueObject &valobj, Stream &stream,
1060 const TypeSummaryOptions &summary_options) {
1061 return formatStringViewImpl<StringPrinter::StringElementType::UTF16>(
1062 valobj, stream, summary_options, prefix_token: "u");
1063}
1064
1065bool lldb_private::formatters::LibcxxStringViewSummaryProviderUTF32(
1066 ValueObject &valobj, Stream &stream,
1067 const TypeSummaryOptions &summary_options) {
1068 return formatStringViewImpl<StringPrinter::StringElementType::UTF32>(
1069 valobj, stream, summary_options, prefix_token: "U");
1070}
1071
1072bool lldb_private::formatters::LibcxxWStringViewSummaryProvider(
1073 ValueObject &valobj, Stream &stream,
1074 const TypeSummaryOptions &summary_options) {
1075
1076 bool success;
1077 ValueObjectSP dataobj;
1078 size_t size;
1079 std::tie(args&: success, args&: dataobj, args&: size) = LibcxxExtractStringViewData(valobj);
1080
1081 if (!success) {
1082 stream << "Summary Unavailable";
1083 return true;
1084 }
1085
1086 return ::LibcxxWStringSummaryProvider(valobj, stream, summary_options,
1087 location_sp: dataobj, size);
1088}
1089
1090static bool
1091LibcxxChronoTimePointSecondsSummaryProvider(ValueObject &valobj, Stream &stream,
1092 const TypeSummaryOptions &options,
1093 const char *fmt) {
1094 ValueObjectSP ptr_sp = valobj.GetChildMemberWithName(name: "__d_");
1095 if (!ptr_sp)
1096 return false;
1097 ptr_sp = ptr_sp->GetChildMemberWithName(name: "__rep_");
1098 if (!ptr_sp)
1099 return false;
1100
1101 // The date time in the chrono library is valid in the range
1102 // [-32767-01-01T00:00:00Z, 32767-12-31T23:59:59Z]. A 64-bit time_t has a
1103 // larger range, the function strftime is not able to format the entire range
1104 // of time_t. The exact point has not been investigated; it's limited to
1105 // chrono's range.
1106 const std::time_t chrono_timestamp_min =
1107 -1'096'193'779'200; // -32767-01-01T00:00:00Z
1108 const std::time_t chrono_timestamp_max =
1109 971'890'963'199; // 32767-12-31T23:59:59Z
1110
1111 const std::time_t seconds = ptr_sp->GetValueAsSigned(fail_value: 0);
1112 if (seconds < chrono_timestamp_min || seconds > chrono_timestamp_max)
1113 stream.Printf(format: "timestamp=%" PRId64 " s", static_cast<int64_t>(seconds));
1114 else {
1115 std::array<char, 128> str;
1116 std::size_t size =
1117 std::strftime(s: str.data(), maxsize: str.size(), format: fmt, tp: gmtime(timer: &seconds));
1118 if (size == 0)
1119 return false;
1120
1121 stream.Printf(format: "date/time=%s timestamp=%" PRId64 " s", str.data(),
1122 static_cast<int64_t>(seconds));
1123 }
1124
1125 return true;
1126}
1127
1128bool lldb_private::formatters::LibcxxChronoSysSecondsSummaryProvider(
1129 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
1130 return LibcxxChronoTimePointSecondsSummaryProvider(valobj, stream, options,
1131 fmt: "%FT%H:%M:%SZ");
1132}
1133
1134bool lldb_private::formatters::LibcxxChronoLocalSecondsSummaryProvider(
1135 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
1136 return LibcxxChronoTimePointSecondsSummaryProvider(valobj, stream, options,
1137 fmt: "%FT%H:%M:%S");
1138}
1139
1140static bool
1141LibcxxChronoTimepointDaysSummaryProvider(ValueObject &valobj, Stream &stream,
1142 const TypeSummaryOptions &options,
1143 const char *fmt) {
1144 ValueObjectSP ptr_sp = valobj.GetChildMemberWithName(name: "__d_");
1145 if (!ptr_sp)
1146 return false;
1147 ptr_sp = ptr_sp->GetChildMemberWithName(name: "__rep_");
1148 if (!ptr_sp)
1149 return false;
1150
1151 // The date time in the chrono library is valid in the range
1152 // [-32767-01-01Z, 32767-12-31Z]. A 32-bit time_t has a larger range, the
1153 // function strftime is not able to format the entire range of time_t. The
1154 // exact point has not been investigated; it's limited to chrono's range.
1155 const int chrono_timestamp_min = -12'687'428; // -32767-01-01Z
1156 const int chrono_timestamp_max = 11'248'737; // 32767-12-31Z
1157
1158 const int days = ptr_sp->GetValueAsSigned(fail_value: 0);
1159 if (days < chrono_timestamp_min || days > chrono_timestamp_max)
1160 stream.Printf(format: "timestamp=%d days", days);
1161
1162 else {
1163 const std::time_t seconds = std::time_t(86400) * days;
1164
1165 std::array<char, 128> str;
1166 std::size_t size =
1167 std::strftime(s: str.data(), maxsize: str.size(), format: fmt, tp: gmtime(timer: &seconds));
1168 if (size == 0)
1169 return false;
1170
1171 stream.Printf(format: "date=%s timestamp=%d days", str.data(), days);
1172 }
1173
1174 return true;
1175}
1176
1177bool lldb_private::formatters::LibcxxChronoSysDaysSummaryProvider(
1178 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
1179 return LibcxxChronoTimepointDaysSummaryProvider(valobj, stream, options,
1180 fmt: "%FZ");
1181}
1182
1183bool lldb_private::formatters::LibcxxChronoLocalDaysSummaryProvider(
1184 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
1185 return LibcxxChronoTimepointDaysSummaryProvider(valobj, stream, options,
1186 fmt: "%F");
1187}
1188
1189bool lldb_private::formatters::LibcxxChronoMonthSummaryProvider(
1190 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
1191 // FIXME: These are the names used in the C++20 ostream operator. Since LLVM
1192 // uses C++17 it's not possible to use the ostream operator directly.
1193 static const std::array<std::string_view, 12> months = {
1194 "January", "February", "March", "April", "May", "June",
1195 "July", "August", "September", "October", "November", "December"};
1196
1197 ValueObjectSP ptr_sp = valobj.GetChildMemberWithName(name: "__m_");
1198 if (!ptr_sp)
1199 return false;
1200
1201 const unsigned month = ptr_sp->GetValueAsUnsigned(fail_value: 0);
1202 if (month >= 1 && month <= 12)
1203 stream << "month=" << months[month - 1];
1204 else
1205 stream.Printf(format: "month=%u", month);
1206
1207 return true;
1208}
1209
1210bool lldb_private::formatters::LibcxxChronoWeekdaySummaryProvider(
1211 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
1212 // FIXME: These are the names used in the C++20 ostream operator. Since LLVM
1213 // uses C++17 it's not possible to use the ostream operator directly.
1214 static const std::array<std::string_view, 7> weekdays = {
1215 "Sunday", "Monday", "Tuesday", "Wednesday",
1216 "Thursday", "Friday", "Saturday"};
1217
1218 ValueObjectSP ptr_sp = valobj.GetChildMemberWithName(name: "__wd_");
1219 if (!ptr_sp)
1220 return false;
1221
1222 const unsigned weekday = ptr_sp->GetValueAsUnsigned(fail_value: 0);
1223 if (weekday < 7)
1224 stream << "weekday=" << weekdays[weekday];
1225 else
1226 stream.Printf(format: "weekday=%u", weekday);
1227
1228 return true;
1229}
1230
1231bool lldb_private::formatters::LibcxxChronoYearMonthDaySummaryProvider(
1232 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
1233 ValueObjectSP ptr_sp = valobj.GetChildMemberWithName(name: "__y_");
1234 if (!ptr_sp)
1235 return false;
1236 ptr_sp = ptr_sp->GetChildMemberWithName(name: "__y_");
1237 if (!ptr_sp)
1238 return false;
1239 int year = ptr_sp->GetValueAsSigned(fail_value: 0);
1240
1241 ptr_sp = valobj.GetChildMemberWithName(name: "__m_");
1242 if (!ptr_sp)
1243 return false;
1244 ptr_sp = ptr_sp->GetChildMemberWithName(name: "__m_");
1245 if (!ptr_sp)
1246 return false;
1247 const unsigned month = ptr_sp->GetValueAsUnsigned(fail_value: 0);
1248
1249 ptr_sp = valobj.GetChildMemberWithName(name: "__d_");
1250 if (!ptr_sp)
1251 return false;
1252 ptr_sp = ptr_sp->GetChildMemberWithName(name: "__d_");
1253 if (!ptr_sp)
1254 return false;
1255 const unsigned day = ptr_sp->GetValueAsUnsigned(fail_value: 0);
1256
1257 stream << "date=";
1258 if (year < 0) {
1259 stream << '-';
1260 year = -year;
1261 }
1262 stream.Printf(format: "%04d-%02u-%02u", year, month, day);
1263
1264 return true;
1265}
1266

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