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