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