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.GetNumChildrenIgnoringErrors() > 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.GetFile().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.GetFile().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 -T
207 (std::__1::__wrap_iter<int *>) ibeg = {
208 (std::__1::__wrap_iter<int *>::iterator_type) __i = 0x00000001001037a0 {
209 (int) *__i = 1
210 }
211 }
212 */
213
214 SyntheticChildrenFrontEnd *
LibCxxVectorIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *,lldb::ValueObjectSP valobj_sp)215 lldb_private::formatters::LibCxxVectorIteratorSyntheticFrontEndCreator(
216 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
217 return (valobj_sp ? new VectorIteratorSyntheticFrontEnd(
218 valobj_sp, {ConstString("__i_"), ConstString("__i")})
219 : nullptr);
220 }
221
222 lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::
LibcxxSharedPtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)223 LibcxxSharedPtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
224 : SyntheticChildrenFrontEnd(*valobj_sp), m_cntrl(nullptr) {
225 if (valobj_sp)
226 Update();
227 }
228
229 llvm::Expected<uint32_t> lldb_private::formatters::
CalculateNumChildren()230 LibcxxSharedPtrSyntheticFrontEnd::CalculateNumChildren() {
231 return (m_cntrl ? 1 : 0);
232 }
233
234 lldb::ValueObjectSP
GetChildAtIndex(uint32_t idx)235 lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::GetChildAtIndex(
236 uint32_t idx) {
237 if (!m_cntrl)
238 return lldb::ValueObjectSP();
239
240 ValueObjectSP valobj_sp = m_backend.GetSP();
241 if (!valobj_sp)
242 return lldb::ValueObjectSP();
243
244 if (idx == 0)
245 return valobj_sp->GetChildMemberWithName("__ptr_");
246
247 if (idx == 1) {
248 if (auto ptr_sp = valobj_sp->GetChildMemberWithName("__ptr_")) {
249 Status status;
250 auto value_type_sp =
251 valobj_sp->GetCompilerType()
252 .GetTypeTemplateArgument(0).GetPointerType();
253 ValueObjectSP cast_ptr_sp = ptr_sp->Cast(value_type_sp);
254 ValueObjectSP value_sp = cast_ptr_sp->Dereference(status);
255 if (status.Success()) {
256 return value_sp;
257 }
258 }
259 }
260
261 return lldb::ValueObjectSP();
262 }
263
264 lldb::ChildCacheState
Update()265 lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::Update() {
266 m_cntrl = nullptr;
267
268 ValueObjectSP valobj_sp = m_backend.GetSP();
269 if (!valobj_sp)
270 return lldb::ChildCacheState::eRefetch;
271
272 TargetSP target_sp(valobj_sp->GetTargetSP());
273 if (!target_sp)
274 return lldb::ChildCacheState::eRefetch;
275
276 lldb::ValueObjectSP cntrl_sp(valobj_sp->GetChildMemberWithName("__cntrl_"));
277
278 m_cntrl = cntrl_sp.get(); // need to store the raw pointer to avoid a circular
279 // dependency
280 return lldb::ChildCacheState::eRefetch;
281 }
282
283 bool lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::
MightHaveChildren()284 MightHaveChildren() {
285 return true;
286 }
287
288 size_t lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::
GetIndexOfChildWithName(ConstString name)289 GetIndexOfChildWithName(ConstString name) {
290 if (name == "__ptr_")
291 return 0;
292 if (name == "$$dereference$$")
293 return 1;
294 return UINT32_MAX;
295 }
296
297 lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::
298 ~LibcxxSharedPtrSyntheticFrontEnd() = default;
299
300 SyntheticChildrenFrontEnd *
LibcxxSharedPtrSyntheticFrontEndCreator(CXXSyntheticChildren *,lldb::ValueObjectSP valobj_sp)301 lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEndCreator(
302 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
303 return (valobj_sp ? new LibcxxSharedPtrSyntheticFrontEnd(valobj_sp)
304 : nullptr);
305 }
306
307 lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::
LibcxxUniquePtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)308 LibcxxUniquePtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
309 : SyntheticChildrenFrontEnd(*valobj_sp) {
310 if (valobj_sp)
311 Update();
312 }
313
314 lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::
315 ~LibcxxUniquePtrSyntheticFrontEnd() = default;
316
317 SyntheticChildrenFrontEnd *
LibcxxUniquePtrSyntheticFrontEndCreator(CXXSyntheticChildren *,lldb::ValueObjectSP valobj_sp)318 lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEndCreator(
319 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
320 return (valobj_sp ? new LibcxxUniquePtrSyntheticFrontEnd(valobj_sp)
321 : nullptr);
322 }
323
324 llvm::Expected<uint32_t> lldb_private::formatters::
CalculateNumChildren()325 LibcxxUniquePtrSyntheticFrontEnd::CalculateNumChildren() {
326 if (m_value_ptr_sp)
327 return m_deleter_sp ? 2 : 1;
328 return 0;
329 }
330
331 lldb::ValueObjectSP
GetChildAtIndex(uint32_t idx)332 lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::GetChildAtIndex(
333 uint32_t idx) {
334 if (!m_value_ptr_sp)
335 return lldb::ValueObjectSP();
336
337 if (idx == 0)
338 return m_value_ptr_sp;
339
340 if (idx == 1)
341 return m_deleter_sp;
342
343 if (idx == 2) {
344 Status status;
345 auto value_sp = m_value_ptr_sp->Dereference(status);
346 if (status.Success()) {
347 return value_sp;
348 }
349 }
350
351 return lldb::ValueObjectSP();
352 }
353
354 lldb::ChildCacheState
Update()355 lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::Update() {
356 ValueObjectSP valobj_sp = m_backend.GetSP();
357 if (!valobj_sp)
358 return lldb::ChildCacheState::eRefetch;
359
360 ValueObjectSP ptr_sp(valobj_sp->GetChildMemberWithName("__ptr_"));
361 if (!ptr_sp)
362 return lldb::ChildCacheState::eRefetch;
363
364 // Retrieve the actual pointer and the deleter, and clone them to give them
365 // user-friendly names.
366 ValueObjectSP value_pointer_sp = GetFirstValueOfLibCXXCompressedPair(*ptr_sp);
367 if (value_pointer_sp)
368 m_value_ptr_sp = value_pointer_sp->Clone(ConstString("pointer"));
369
370 ValueObjectSP deleter_sp = GetSecondValueOfLibCXXCompressedPair(*ptr_sp);
371 if (deleter_sp)
372 m_deleter_sp = deleter_sp->Clone(ConstString("deleter"));
373
374 return lldb::ChildCacheState::eRefetch;
375 }
376
377 bool lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::
MightHaveChildren()378 MightHaveChildren() {
379 return true;
380 }
381
382 size_t lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::
GetIndexOfChildWithName(ConstString name)383 GetIndexOfChildWithName(ConstString name) {
384 if (name == "pointer")
385 return 0;
386 if (name == "deleter")
387 return 1;
388 if (name == "$$dereference$$")
389 return 2;
390 return UINT32_MAX;
391 }
392
LibcxxContainerSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)393 bool lldb_private::formatters::LibcxxContainerSummaryProvider(
394 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
395 if (valobj.IsPointerType()) {
396 uint64_t value = valobj.GetValueAsUnsigned(0);
397 if (!value)
398 return false;
399 stream.Printf("0x%016" PRIx64 " ", value);
400 }
401 return FormatEntity::FormatStringRef("size=${svar%#}", stream, nullptr,
402 nullptr, nullptr, &valobj, false, false);
403 }
404
405 /// The field layout in a libc++ string (cap, side, data or data, size, cap).
406 namespace {
407 enum class StringLayout { CSD, DSC };
408 }
409
410 /// Determine the size in bytes of \p valobj (a libc++ std::string object) and
411 /// extract its data payload. Return the size + payload pair.
412 // TODO: Support big-endian architectures.
413 static std::optional<std::pair<uint64_t, ValueObjectSP>>
ExtractLibcxxStringInfo(ValueObject & valobj)414 ExtractLibcxxStringInfo(ValueObject &valobj) {
415 ValueObjectSP valobj_r_sp = valobj.GetChildMemberWithName("__r_");
416 if (!valobj_r_sp || !valobj_r_sp->GetError().Success())
417 return {};
418
419 // __r_ is a compressed_pair of the actual data and the allocator. The data we
420 // want is in the first base class.
421 ValueObjectSP valobj_r_base_sp = valobj_r_sp->GetChildAtIndex(0);
422 if (!valobj_r_base_sp)
423 return {};
424
425 ValueObjectSP valobj_rep_sp =
426 valobj_r_base_sp->GetChildMemberWithName("__value_");
427 if (!valobj_rep_sp)
428 return {};
429
430 ValueObjectSP l = valobj_rep_sp->GetChildMemberWithName("__l");
431 if (!l)
432 return {};
433
434 StringLayout layout = l->GetIndexOfChildWithName("__data_") == 0
435 ? StringLayout::DSC
436 : StringLayout::CSD;
437
438 bool short_mode = false; // this means the string is in short-mode and the
439 // data is stored inline
440 bool using_bitmasks = true; // Whether the class uses bitmasks for the mode
441 // flag (pre-D123580).
442 uint64_t size;
443 uint64_t size_mode_value = 0;
444
445 ValueObjectSP short_sp = valobj_rep_sp->GetChildMemberWithName("__s");
446 if (!short_sp)
447 return {};
448
449 ValueObjectSP is_long = short_sp->GetChildMemberWithName("__is_long_");
450 ValueObjectSP size_sp = short_sp->GetChildMemberWithName("__size_");
451 if (!size_sp)
452 return {};
453
454 if (is_long) {
455 using_bitmasks = false;
456 short_mode = !is_long->GetValueAsUnsigned(/*fail_value=*/0);
457 size = size_sp->GetValueAsUnsigned(/*fail_value=*/0);
458 } else {
459 // The string mode is encoded in the size field.
460 size_mode_value = size_sp->GetValueAsUnsigned(0);
461 uint8_t mode_mask = layout == StringLayout::DSC ? 0x80 : 1;
462 short_mode = (size_mode_value & mode_mask) == 0;
463 }
464
465 if (short_mode) {
466 ValueObjectSP location_sp = short_sp->GetChildMemberWithName("__data_");
467 if (using_bitmasks)
468 size = (layout == StringLayout::DSC) ? size_mode_value
469 : ((size_mode_value >> 1) % 256);
470
471 if (!location_sp)
472 return {};
473
474 // When the small-string optimization takes place, the data must fit in the
475 // inline string buffer (23 bytes on x86_64/Darwin). If it doesn't, it's
476 // likely that the string isn't initialized and we're reading garbage.
477 ExecutionContext exe_ctx(location_sp->GetExecutionContextRef());
478 const std::optional<uint64_t> max_bytes =
479 location_sp->GetCompilerType().GetByteSize(
480 exe_ctx.GetBestExecutionContextScope());
481 if (!max_bytes || size > *max_bytes)
482 return {};
483
484 return std::make_pair(size, location_sp);
485 }
486
487 // we can use the layout_decider object as the data pointer
488 ValueObjectSP location_sp = l->GetChildMemberWithName("__data_");
489 ValueObjectSP size_vo = l->GetChildMemberWithName("__size_");
490 ValueObjectSP capacity_vo = l->GetChildMemberWithName("__cap_");
491 if (!size_vo || !location_sp || !capacity_vo)
492 return {};
493 size = size_vo->GetValueAsUnsigned(LLDB_INVALID_OFFSET);
494 uint64_t capacity = capacity_vo->GetValueAsUnsigned(LLDB_INVALID_OFFSET);
495 if (!using_bitmasks && layout == StringLayout::CSD)
496 capacity *= 2;
497 if (size == LLDB_INVALID_OFFSET || capacity == LLDB_INVALID_OFFSET ||
498 capacity < size)
499 return {};
500 return std::make_pair(size, location_sp);
501 }
502
503 static bool
LibcxxWStringSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & summary_options,ValueObjectSP location_sp,size_t size)504 LibcxxWStringSummaryProvider(ValueObject &valobj, Stream &stream,
505 const TypeSummaryOptions &summary_options,
506 ValueObjectSP location_sp, size_t size) {
507 if (size == 0) {
508 stream.Printf("L\"\"");
509 return true;
510 }
511 if (!location_sp)
512 return false;
513
514 StringPrinter::ReadBufferAndDumpToStreamOptions options(valobj);
515 if (summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryCapped) {
516 const auto max_size = valobj.GetTargetSP()->GetMaximumSizeOfStringSummary();
517 if (size > max_size) {
518 size = max_size;
519 options.SetIsTruncated(true);
520 }
521 }
522
523 DataExtractor extractor;
524 const size_t bytes_read = location_sp->GetPointeeData(extractor, 0, size);
525 if (bytes_read < size)
526 return false;
527
528 // std::wstring::size() is measured in 'characters', not bytes
529 TypeSystemClangSP scratch_ts_sp =
530 ScratchTypeSystemClang::GetForTarget(*valobj.GetTargetSP());
531 if (!scratch_ts_sp)
532 return false;
533
534 auto wchar_t_size =
535 scratch_ts_sp->GetBasicType(lldb::eBasicTypeWChar).GetByteSize(nullptr);
536 if (!wchar_t_size)
537 return false;
538
539 options.SetData(std::move(extractor));
540 options.SetStream(&stream);
541 options.SetPrefixToken("L");
542 options.SetQuote('"');
543 options.SetSourceSize(size);
544 options.SetBinaryZeroIsTerminator(false);
545
546 switch (*wchar_t_size) {
547 case 1:
548 return StringPrinter::ReadBufferAndDumpToStream<
549 lldb_private::formatters::StringPrinter::StringElementType::UTF8>(
550 options);
551 break;
552
553 case 2:
554 return StringPrinter::ReadBufferAndDumpToStream<
555 lldb_private::formatters::StringPrinter::StringElementType::UTF16>(
556 options);
557 break;
558
559 case 4:
560 return StringPrinter::ReadBufferAndDumpToStream<
561 lldb_private::formatters::StringPrinter::StringElementType::UTF32>(
562 options);
563 }
564 return false;
565 }
566
LibcxxWStringSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & summary_options)567 bool lldb_private::formatters::LibcxxWStringSummaryProvider(
568 ValueObject &valobj, Stream &stream,
569 const TypeSummaryOptions &summary_options) {
570 auto string_info = ExtractLibcxxStringInfo(valobj);
571 if (!string_info)
572 return false;
573 uint64_t size;
574 ValueObjectSP location_sp;
575 std::tie(size, location_sp) = *string_info;
576
577 return ::LibcxxWStringSummaryProvider(valobj, stream, summary_options,
578 location_sp, size);
579 }
580
581 template <StringPrinter::StringElementType element_type>
582 static bool
LibcxxStringSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & summary_options,std::string prefix_token,ValueObjectSP location_sp,uint64_t size)583 LibcxxStringSummaryProvider(ValueObject &valobj, Stream &stream,
584 const TypeSummaryOptions &summary_options,
585 std::string prefix_token, ValueObjectSP location_sp,
586 uint64_t size) {
587
588 if (size == 0) {
589 stream.Printf("\"\"");
590 return true;
591 }
592
593 if (!location_sp)
594 return false;
595
596 StringPrinter::ReadBufferAndDumpToStreamOptions options(valobj);
597
598 if (summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryCapped) {
599 const auto max_size = valobj.GetTargetSP()->GetMaximumSizeOfStringSummary();
600 if (size > max_size) {
601 size = max_size;
602 options.SetIsTruncated(true);
603 }
604 }
605
606 {
607 DataExtractor extractor;
608 const size_t bytes_read = location_sp->GetPointeeData(extractor, 0, size);
609 if (bytes_read < size)
610 return false;
611
612 options.SetData(std::move(extractor));
613 }
614 options.SetStream(&stream);
615 if (prefix_token.empty())
616 options.SetPrefixToken(nullptr);
617 else
618 options.SetPrefixToken(prefix_token);
619 options.SetQuote('"');
620 options.SetSourceSize(size);
621 options.SetBinaryZeroIsTerminator(false);
622 return StringPrinter::ReadBufferAndDumpToStream<element_type>(options);
623 }
624
625 template <StringPrinter::StringElementType element_type>
626 static bool
LibcxxStringSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & summary_options,std::string prefix_token)627 LibcxxStringSummaryProvider(ValueObject &valobj, Stream &stream,
628 const TypeSummaryOptions &summary_options,
629 std::string prefix_token) {
630 auto string_info = ExtractLibcxxStringInfo(valobj);
631 if (!string_info)
632 return false;
633 uint64_t size;
634 ValueObjectSP location_sp;
635 std::tie(size, location_sp) = *string_info;
636
637 return LibcxxStringSummaryProvider<element_type>(
638 valobj, stream, summary_options, prefix_token, location_sp, size);
639 }
640 template <StringPrinter::StringElementType element_type>
formatStringImpl(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & summary_options,std::string prefix_token)641 static bool formatStringImpl(ValueObject &valobj, Stream &stream,
642 const TypeSummaryOptions &summary_options,
643 std::string prefix_token) {
644 StreamString scratch_stream;
645 const bool success = LibcxxStringSummaryProvider<element_type>(
646 valobj, scratch_stream, summary_options, prefix_token);
647 if (success)
648 stream << scratch_stream.GetData();
649 else
650 stream << "Summary Unavailable";
651 return true;
652 }
653
LibcxxStringSummaryProviderASCII(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & summary_options)654 bool lldb_private::formatters::LibcxxStringSummaryProviderASCII(
655 ValueObject &valobj, Stream &stream,
656 const TypeSummaryOptions &summary_options) {
657 return formatStringImpl<StringPrinter::StringElementType::ASCII>(
658 valobj, stream, summary_options, "");
659 }
660
LibcxxStringSummaryProviderUTF16(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & summary_options)661 bool lldb_private::formatters::LibcxxStringSummaryProviderUTF16(
662 ValueObject &valobj, Stream &stream,
663 const TypeSummaryOptions &summary_options) {
664 return formatStringImpl<StringPrinter::StringElementType::UTF16>(
665 valobj, stream, summary_options, "u");
666 }
667
LibcxxStringSummaryProviderUTF32(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & summary_options)668 bool lldb_private::formatters::LibcxxStringSummaryProviderUTF32(
669 ValueObject &valobj, Stream &stream,
670 const TypeSummaryOptions &summary_options) {
671 return formatStringImpl<StringPrinter::StringElementType::UTF32>(
672 valobj, stream, summary_options, "U");
673 }
674
675 static std::tuple<bool, ValueObjectSP, size_t>
LibcxxExtractStringViewData(ValueObject & valobj)676 LibcxxExtractStringViewData(ValueObject& valobj) {
677 auto dataobj = GetChildMemberWithName(
678 valobj, {ConstString("__data_"), ConstString("__data")});
679 auto sizeobj = GetChildMemberWithName(
680 valobj, {ConstString("__size_"), ConstString("__size")});
681 if (!dataobj || !sizeobj)
682 return std::make_tuple<bool,ValueObjectSP,size_t>(false, {}, {});
683
684 if (!dataobj->GetError().Success() || !sizeobj->GetError().Success())
685 return std::make_tuple<bool,ValueObjectSP,size_t>(false, {}, {});
686
687 bool success{false};
688 uint64_t size = sizeobj->GetValueAsUnsigned(0, &success);
689 if (!success)
690 return std::make_tuple<bool,ValueObjectSP,size_t>(false, {}, {});
691
692 return std::make_tuple(true,dataobj,size);
693 }
694
695 template <StringPrinter::StringElementType element_type>
formatStringViewImpl(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & summary_options,std::string prefix_token)696 static bool formatStringViewImpl(ValueObject &valobj, Stream &stream,
697 const TypeSummaryOptions &summary_options,
698 std::string prefix_token) {
699
700 bool success;
701 ValueObjectSP dataobj;
702 size_t size;
703 std::tie(success, dataobj, size) = LibcxxExtractStringViewData(valobj);
704
705 if (!success) {
706 stream << "Summary Unavailable";
707 return true;
708 }
709
710 return LibcxxStringSummaryProvider<element_type>(
711 valobj, stream, summary_options, prefix_token, dataobj, size);
712 }
713
LibcxxStringViewSummaryProviderASCII(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & summary_options)714 bool lldb_private::formatters::LibcxxStringViewSummaryProviderASCII(
715 ValueObject &valobj, Stream &stream,
716 const TypeSummaryOptions &summary_options) {
717 return formatStringViewImpl<StringPrinter::StringElementType::ASCII>(
718 valobj, stream, summary_options, "");
719 }
720
LibcxxStringViewSummaryProviderUTF16(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & summary_options)721 bool lldb_private::formatters::LibcxxStringViewSummaryProviderUTF16(
722 ValueObject &valobj, Stream &stream,
723 const TypeSummaryOptions &summary_options) {
724 return formatStringViewImpl<StringPrinter::StringElementType::UTF16>(
725 valobj, stream, summary_options, "u");
726 }
727
LibcxxStringViewSummaryProviderUTF32(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & summary_options)728 bool lldb_private::formatters::LibcxxStringViewSummaryProviderUTF32(
729 ValueObject &valobj, Stream &stream,
730 const TypeSummaryOptions &summary_options) {
731 return formatStringViewImpl<StringPrinter::StringElementType::UTF32>(
732 valobj, stream, summary_options, "U");
733 }
734
LibcxxWStringViewSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & summary_options)735 bool lldb_private::formatters::LibcxxWStringViewSummaryProvider(
736 ValueObject &valobj, Stream &stream,
737 const TypeSummaryOptions &summary_options) {
738
739 bool success;
740 ValueObjectSP dataobj;
741 size_t size;
742 std::tie(success, dataobj, size) = LibcxxExtractStringViewData(valobj);
743
744 if (!success) {
745 stream << "Summary Unavailable";
746 return true;
747 }
748
749 return ::LibcxxWStringSummaryProvider(valobj, stream, summary_options,
750 dataobj, size);
751 }
752
753 static bool
LibcxxChronoTimePointSecondsSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options,const char * fmt)754 LibcxxChronoTimePointSecondsSummaryProvider(ValueObject &valobj, Stream &stream,
755 const TypeSummaryOptions &options,
756 const char *fmt) {
757 ValueObjectSP ptr_sp = valobj.GetChildMemberWithName("__d_");
758 if (!ptr_sp)
759 return false;
760 ptr_sp = ptr_sp->GetChildMemberWithName("__rep_");
761 if (!ptr_sp)
762 return false;
763
764 #ifndef _WIN32
765 // The date time in the chrono library is valid in the range
766 // [-32767-01-01T00:00:00Z, 32767-12-31T23:59:59Z]. A 64-bit time_t has a
767 // larger range, the function strftime is not able to format the entire range
768 // of time_t. The exact point has not been investigated; it's limited to
769 // chrono's range.
770 const std::time_t chrono_timestamp_min =
771 -1'096'193'779'200; // -32767-01-01T00:00:00Z
772 const std::time_t chrono_timestamp_max =
773 971'890'963'199; // 32767-12-31T23:59:59Z
774 #else
775 const std::time_t chrono_timestamp_min = -43'200; // 1969-12-31T12:00:00Z
776 const std::time_t chrono_timestamp_max =
777 32'536'850'399; // 3001-01-19T21:59:59
778 #endif
779
780 const std::time_t seconds = ptr_sp->GetValueAsSigned(0);
781 if (seconds < chrono_timestamp_min || seconds > chrono_timestamp_max)
782 stream.Printf("timestamp=%" PRId64 " s", static_cast<int64_t>(seconds));
783 else {
784 std::array<char, 128> str;
785 std::size_t size =
786 std::strftime(str.data(), str.size(), fmt, gmtime(&seconds));
787 if (size == 0)
788 return false;
789
790 stream.Printf("date/time=%s timestamp=%" PRId64 " s", str.data(),
791 static_cast<int64_t>(seconds));
792 }
793
794 return true;
795 }
796
LibcxxChronoSysSecondsSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)797 bool lldb_private::formatters::LibcxxChronoSysSecondsSummaryProvider(
798 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
799 return LibcxxChronoTimePointSecondsSummaryProvider(valobj, stream, options,
800 "%FT%H:%M:%SZ");
801 }
802
LibcxxChronoLocalSecondsSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)803 bool lldb_private::formatters::LibcxxChronoLocalSecondsSummaryProvider(
804 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
805 return LibcxxChronoTimePointSecondsSummaryProvider(valobj, stream, options,
806 "%FT%H:%M:%S");
807 }
808
809 static bool
LibcxxChronoTimepointDaysSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options,const char * fmt)810 LibcxxChronoTimepointDaysSummaryProvider(ValueObject &valobj, Stream &stream,
811 const TypeSummaryOptions &options,
812 const char *fmt) {
813 ValueObjectSP ptr_sp = valobj.GetChildMemberWithName("__d_");
814 if (!ptr_sp)
815 return false;
816 ptr_sp = ptr_sp->GetChildMemberWithName("__rep_");
817 if (!ptr_sp)
818 return false;
819
820 #ifndef _WIN32
821 // The date time in the chrono library is valid in the range
822 // [-32767-01-01Z, 32767-12-31Z]. A 32-bit time_t has a larger range, the
823 // function strftime is not able to format the entire range of time_t. The
824 // exact point has not been investigated; it's limited to chrono's range.
825 const int chrono_timestamp_min = -12'687'428; // -32767-01-01Z
826 const int chrono_timestamp_max = 11'248'737; // 32767-12-31Z
827 #else
828 const int chrono_timestamp_min = 0; // 1970-01-01Z
829 const int chrono_timestamp_max = 376'583; // 3001-01-19Z
830 #endif
831
832 const int days = ptr_sp->GetValueAsSigned(0);
833 if (days < chrono_timestamp_min || days > chrono_timestamp_max)
834 stream.Printf("timestamp=%d days", days);
835
836 else {
837 const std::time_t seconds = std::time_t(86400) * days;
838
839 std::array<char, 128> str;
840 std::size_t size =
841 std::strftime(str.data(), str.size(), fmt, gmtime(&seconds));
842 if (size == 0)
843 return false;
844
845 stream.Printf("date=%s timestamp=%d days", str.data(), days);
846 }
847
848 return true;
849 }
850
LibcxxChronoSysDaysSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)851 bool lldb_private::formatters::LibcxxChronoSysDaysSummaryProvider(
852 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
853 return LibcxxChronoTimepointDaysSummaryProvider(valobj, stream, options,
854 "%FZ");
855 }
856
LibcxxChronoLocalDaysSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)857 bool lldb_private::formatters::LibcxxChronoLocalDaysSummaryProvider(
858 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
859 return LibcxxChronoTimepointDaysSummaryProvider(valobj, stream, options,
860 "%F");
861 }
862
LibcxxChronoMonthSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)863 bool lldb_private::formatters::LibcxxChronoMonthSummaryProvider(
864 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
865 // FIXME: These are the names used in the C++20 ostream operator. Since LLVM
866 // uses C++17 it's not possible to use the ostream operator directly.
867 static const std::array<std::string_view, 12> months = {
868 "January", "February", "March", "April", "May", "June",
869 "July", "August", "September", "October", "November", "December"};
870
871 ValueObjectSP ptr_sp = valobj.GetChildMemberWithName("__m_");
872 if (!ptr_sp)
873 return false;
874
875 const unsigned month = ptr_sp->GetValueAsUnsigned(0);
876 if (month >= 1 && month <= 12)
877 stream << "month=" << months[month - 1];
878 else
879 stream.Printf("month=%u", month);
880
881 return true;
882 }
883
LibcxxChronoWeekdaySummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)884 bool lldb_private::formatters::LibcxxChronoWeekdaySummaryProvider(
885 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
886 // FIXME: These are the names used in the C++20 ostream operator. Since LLVM
887 // uses C++17 it's not possible to use the ostream operator directly.
888 static const std::array<std::string_view, 7> weekdays = {
889 "Sunday", "Monday", "Tuesday", "Wednesday",
890 "Thursday", "Friday", "Saturday"};
891
892 ValueObjectSP ptr_sp = valobj.GetChildMemberWithName("__wd_");
893 if (!ptr_sp)
894 return false;
895
896 const unsigned weekday = ptr_sp->GetValueAsUnsigned(0);
897 if (weekday < 7)
898 stream << "weekday=" << weekdays[weekday];
899 else
900 stream.Printf("weekday=%u", weekday);
901
902 return true;
903 }
904
LibcxxChronoYearMonthDaySummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)905 bool lldb_private::formatters::LibcxxChronoYearMonthDaySummaryProvider(
906 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
907 ValueObjectSP ptr_sp = valobj.GetChildMemberWithName("__y_");
908 if (!ptr_sp)
909 return false;
910 ptr_sp = ptr_sp->GetChildMemberWithName("__y_");
911 if (!ptr_sp)
912 return false;
913 int year = ptr_sp->GetValueAsSigned(0);
914
915 ptr_sp = valobj.GetChildMemberWithName("__m_");
916 if (!ptr_sp)
917 return false;
918 ptr_sp = ptr_sp->GetChildMemberWithName("__m_");
919 if (!ptr_sp)
920 return false;
921 const unsigned month = ptr_sp->GetValueAsUnsigned(0);
922
923 ptr_sp = valobj.GetChildMemberWithName("__d_");
924 if (!ptr_sp)
925 return false;
926 ptr_sp = ptr_sp->GetChildMemberWithName("__d_");
927 if (!ptr_sp)
928 return false;
929 const unsigned day = ptr_sp->GetValueAsUnsigned(0);
930
931 stream << "date=";
932 if (year < 0) {
933 stream << '-';
934 year = -year;
935 }
936 stream.Printf("%04d-%02u-%02u", year, month, day);
937
938 return true;
939 }
940