1 //===-- Cocoa.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 "Cocoa.h" 10 11 #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" 12 #include "lldb/Core/Mangled.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/Host/Time.h" 19 #include "lldb/Target/Language.h" 20 #include "lldb/Target/Process.h" 21 #include "lldb/Target/ProcessStructReader.h" 22 #include "lldb/Target/Target.h" 23 #include "lldb/Utility/DataBufferHeap.h" 24 #include "lldb/Utility/Endian.h" 25 #include "lldb/Utility/Status.h" 26 #include "lldb/Utility/Stream.h" 27 28 #include "llvm/ADT/APInt.h" 29 #include "llvm/ADT/bit.h" 30 31 #include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h" 32 33 #include "NSString.h" 34 35 using namespace lldb; 36 using namespace lldb_private; 37 using namespace lldb_private::formatters; 38 39 bool lldb_private::formatters::NSBundleSummaryProvider( 40 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 41 ProcessSP process_sp = valobj.GetProcessSP(); 42 if (!process_sp) 43 return false; 44 45 ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); 46 47 if (!runtime) 48 return false; 49 50 ObjCLanguageRuntime::ClassDescriptorSP descriptor( 51 runtime->GetClassDescriptor(valobj)); 52 53 if (!descriptor || !descriptor->IsValid()) 54 return false; 55 56 uint32_t ptr_size = process_sp->GetAddressByteSize(); 57 58 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 59 60 if (!valobj_addr) 61 return false; 62 63 llvm::StringRef class_name(descriptor->GetClassName().GetCString()); 64 65 if (class_name.empty()) 66 return false; 67 68 if (class_name == "NSBundle") { 69 uint64_t offset = 5 * ptr_size; 70 ValueObjectSP text(valobj.GetSyntheticChildAtOffset( 71 offset, 72 valobj.GetCompilerType().GetBasicTypeFromAST(lldb::eBasicTypeObjCID), 73 true)); 74 75 if (!text) 76 return false; 77 78 StreamString summary_stream; 79 bool was_nsstring_ok = 80 NSStringSummaryProvider(*text, summary_stream, options); 81 if (was_nsstring_ok && summary_stream.GetSize() > 0) { 82 stream.Printf("%s", summary_stream.GetData()); 83 return true; 84 } 85 } 86 87 return false; 88 } 89 90 bool lldb_private::formatters::NSTimeZoneSummaryProvider( 91 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 92 ProcessSP process_sp = valobj.GetProcessSP(); 93 if (!process_sp) 94 return false; 95 96 ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); 97 98 if (!runtime) 99 return false; 100 101 ObjCLanguageRuntime::ClassDescriptorSP descriptor( 102 runtime->GetClassDescriptor(valobj)); 103 104 if (!descriptor || !descriptor->IsValid()) 105 return false; 106 107 uint32_t ptr_size = process_sp->GetAddressByteSize(); 108 109 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 110 111 if (!valobj_addr) 112 return false; 113 114 llvm::StringRef class_name(descriptor->GetClassName().GetCString()); 115 116 if (class_name.empty()) 117 return false; 118 119 if (class_name == "__NSTimeZone") { 120 uint64_t offset = ptr_size; 121 ValueObjectSP text(valobj.GetSyntheticChildAtOffset( 122 offset, valobj.GetCompilerType(), true)); 123 124 if (!text) 125 return false; 126 127 StreamString summary_stream; 128 bool was_nsstring_ok = 129 NSStringSummaryProvider(*text, summary_stream, options); 130 if (was_nsstring_ok && summary_stream.GetSize() > 0) { 131 stream.Printf("%s", summary_stream.GetData()); 132 return true; 133 } 134 } 135 136 return false; 137 } 138 139 bool lldb_private::formatters::NSNotificationSummaryProvider( 140 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 141 ProcessSP process_sp = valobj.GetProcessSP(); 142 if (!process_sp) 143 return false; 144 145 ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); 146 147 if (!runtime) 148 return false; 149 150 ObjCLanguageRuntime::ClassDescriptorSP descriptor( 151 runtime->GetClassDescriptor(valobj)); 152 153 if (!descriptor || !descriptor->IsValid()) 154 return false; 155 156 uint32_t ptr_size = process_sp->GetAddressByteSize(); 157 158 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 159 160 if (!valobj_addr) 161 return false; 162 163 llvm::StringRef class_name(descriptor->GetClassName().GetCString()); 164 165 if (class_name.empty()) 166 return false; 167 168 if (class_name == "NSConcreteNotification") { 169 uint64_t offset = ptr_size; 170 ValueObjectSP text(valobj.GetSyntheticChildAtOffset( 171 offset, valobj.GetCompilerType(), true)); 172 173 if (!text) 174 return false; 175 176 StreamString summary_stream; 177 bool was_nsstring_ok = 178 NSStringSummaryProvider(*text, summary_stream, options); 179 if (was_nsstring_ok && summary_stream.GetSize() > 0) { 180 stream.Printf("%s", summary_stream.GetData()); 181 return true; 182 } 183 } 184 185 return false; 186 } 187 188 bool lldb_private::formatters::NSMachPortSummaryProvider( 189 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 190 ProcessSP process_sp = valobj.GetProcessSP(); 191 if (!process_sp) 192 return false; 193 194 ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); 195 196 if (!runtime) 197 return false; 198 199 ObjCLanguageRuntime::ClassDescriptorSP descriptor( 200 runtime->GetClassDescriptor(valobj)); 201 202 if (!descriptor || !descriptor->IsValid()) 203 return false; 204 205 uint32_t ptr_size = process_sp->GetAddressByteSize(); 206 207 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 208 209 if (!valobj_addr) 210 return false; 211 212 llvm::StringRef class_name(descriptor->GetClassName().GetCString()); 213 214 if (class_name.empty()) 215 return false; 216 217 uint64_t port_number = 0; 218 219 if (class_name == "NSMachPort") { 220 uint64_t offset = (ptr_size == 4 ? 12 : 20); 221 Status error; 222 port_number = process_sp->ReadUnsignedIntegerFromMemory( 223 offset + valobj_addr, 4, 0, error); 224 if (error.Success()) { 225 stream.Printf("mach port: %u", 226 (uint32_t)(port_number & 0x00000000FFFFFFFF)); 227 return true; 228 } 229 } 230 231 return false; 232 } 233 234 bool lldb_private::formatters::NSIndexSetSummaryProvider( 235 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 236 ProcessSP process_sp = valobj.GetProcessSP(); 237 if (!process_sp) 238 return false; 239 240 ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); 241 242 if (!runtime) 243 return false; 244 245 ObjCLanguageRuntime::ClassDescriptorSP descriptor( 246 runtime->GetClassDescriptor(valobj)); 247 248 if (!descriptor || !descriptor->IsValid()) 249 return false; 250 251 uint32_t ptr_size = process_sp->GetAddressByteSize(); 252 253 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 254 255 if (!valobj_addr) 256 return false; 257 258 llvm::StringRef class_name(descriptor->GetClassName().GetCString()); 259 260 if (class_name.empty()) 261 return false; 262 263 uint64_t count = 0; 264 265 do { 266 if (class_name == "NSIndexSet" || class_name == "NSMutableIndexSet") { 267 Status error; 268 uint32_t mode = process_sp->ReadUnsignedIntegerFromMemory( 269 valobj_addr + ptr_size, 4, 0, error); 270 if (error.Fail()) 271 return false; 272 // this means the set is empty - count = 0 273 if ((mode & 1) == 1) { 274 count = 0; 275 break; 276 } 277 if ((mode & 2) == 2) 278 mode = 1; // this means the set only has one range 279 else 280 mode = 2; // this means the set has multiple ranges 281 if (mode == 1) { 282 count = process_sp->ReadUnsignedIntegerFromMemory( 283 valobj_addr + 3 * ptr_size, ptr_size, 0, error); 284 if (error.Fail()) 285 return false; 286 } else { 287 // read a pointer to the data at 2*ptr_size 288 count = process_sp->ReadUnsignedIntegerFromMemory( 289 valobj_addr + 2 * ptr_size, ptr_size, 0, error); 290 if (error.Fail()) 291 return false; 292 // read the data at 2*ptr_size from the first location 293 count = process_sp->ReadUnsignedIntegerFromMemory(count + 2 * ptr_size, 294 ptr_size, 0, error); 295 if (error.Fail()) 296 return false; 297 } 298 } else 299 return false; 300 } while (false); 301 stream.Printf("%" PRIu64 " index%s", count, (count == 1 ? "" : "es")); 302 return true; 303 } 304 305 static void NSNumber_FormatChar(ValueObject &valobj, Stream &stream, char value, 306 lldb::LanguageType lang) { 307 static ConstString g_TypeHint("NSNumber:char"); 308 309 std::string prefix, suffix; 310 if (Language *language = Language::FindPlugin(lang)) { 311 if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix, 312 suffix)) { 313 prefix.clear(); 314 suffix.clear(); 315 } 316 } 317 318 stream.Printf("%s%hhd%s", prefix.c_str(), value, suffix.c_str()); 319 } 320 321 static void NSNumber_FormatShort(ValueObject &valobj, Stream &stream, 322 short value, lldb::LanguageType lang) { 323 static ConstString g_TypeHint("NSNumber:short"); 324 325 std::string prefix, suffix; 326 if (Language *language = Language::FindPlugin(lang)) { 327 if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix, 328 suffix)) { 329 prefix.clear(); 330 suffix.clear(); 331 } 332 } 333 334 stream.Printf("%s%hd%s", prefix.c_str(), value, suffix.c_str()); 335 } 336 337 static void NSNumber_FormatInt(ValueObject &valobj, Stream &stream, int value, 338 lldb::LanguageType lang) { 339 static ConstString g_TypeHint("NSNumber:int"); 340 341 std::string prefix, suffix; 342 if (Language *language = Language::FindPlugin(lang)) { 343 if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix, 344 suffix)) { 345 prefix.clear(); 346 suffix.clear(); 347 } 348 } 349 350 stream.Printf("%s%d%s", prefix.c_str(), value, suffix.c_str()); 351 } 352 353 static void NSNumber_FormatLong(ValueObject &valobj, Stream &stream, 354 int64_t value, lldb::LanguageType lang) { 355 static ConstString g_TypeHint("NSNumber:long"); 356 357 std::string prefix, suffix; 358 if (Language *language = Language::FindPlugin(lang)) { 359 if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix, 360 suffix)) { 361 prefix.clear(); 362 suffix.clear(); 363 } 364 } 365 366 stream.Printf("%s%" PRId64 "%s", prefix.c_str(), value, suffix.c_str()); 367 } 368 369 static void NSNumber_FormatInt128(ValueObject &valobj, Stream &stream, 370 const llvm::APInt &value, 371 lldb::LanguageType lang) { 372 static ConstString g_TypeHint("NSNumber:int128_t"); 373 374 std::string prefix, suffix; 375 if (Language *language = Language::FindPlugin(lang)) { 376 if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix, 377 suffix)) { 378 prefix.clear(); 379 suffix.clear(); 380 } 381 } 382 383 stream.PutCString(prefix.c_str()); 384 const int radix = 10; 385 const bool isSigned = true; 386 std::string str = llvm::toString(value, radix, isSigned); 387 stream.PutCString(str.c_str()); 388 stream.PutCString(suffix.c_str()); 389 } 390 391 static void NSNumber_FormatFloat(ValueObject &valobj, Stream &stream, 392 float value, lldb::LanguageType lang) { 393 static ConstString g_TypeHint("NSNumber:float"); 394 395 std::string prefix, suffix; 396 if (Language *language = Language::FindPlugin(lang)) { 397 if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix, 398 suffix)) { 399 prefix.clear(); 400 suffix.clear(); 401 } 402 } 403 404 stream.Printf("%s%f%s", prefix.c_str(), value, suffix.c_str()); 405 } 406 407 static void NSNumber_FormatDouble(ValueObject &valobj, Stream &stream, 408 double value, lldb::LanguageType lang) { 409 static ConstString g_TypeHint("NSNumber:double"); 410 411 std::string prefix, suffix; 412 if (Language *language = Language::FindPlugin(lang)) { 413 if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix, 414 suffix)) { 415 prefix.clear(); 416 suffix.clear(); 417 } 418 } 419 420 stream.Printf("%s%g%s", prefix.c_str(), value, suffix.c_str()); 421 } 422 423 bool lldb_private::formatters::NSNumberSummaryProvider( 424 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 425 ProcessSP process_sp = valobj.GetProcessSP(); 426 if (!process_sp) 427 return false; 428 429 Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DATAFORMATTERS); 430 ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); 431 432 if (!runtime) 433 return false; 434 435 ObjCLanguageRuntime::ClassDescriptorSP descriptor( 436 runtime->GetClassDescriptor(valobj)); 437 438 if (!descriptor || !descriptor->IsValid()) 439 return false; 440 441 uint32_t ptr_size = process_sp->GetAddressByteSize(); 442 443 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 444 445 if (!valobj_addr) 446 return false; 447 448 llvm::StringRef class_name(descriptor->GetClassName().GetCString()); 449 450 if (class_name.empty()) 451 return false; 452 453 if (class_name == "__NSCFBoolean") 454 return ObjCBooleanSummaryProvider(valobj, stream, options); 455 456 if (class_name == "NSDecimalNumber") 457 return NSDecimalNumberSummaryProvider(valobj, stream, options); 458 459 if (class_name == "NSNumber" || class_name == "__NSCFNumber") { 460 int64_t value = 0; 461 uint64_t i_bits = 0; 462 if (descriptor->GetTaggedPointerInfoSigned(&i_bits, &value)) { 463 // Check for "preserved" numbers. We still don't support them yet. 464 if (i_bits & 0x8) { 465 if (log) 466 log->Printf( 467 "Unsupported (preserved) NSNumber tagged pointer 0x%" PRIu64, 468 valobj_addr); 469 return false; 470 } 471 472 switch (i_bits) { 473 case 0: 474 NSNumber_FormatChar(valobj, stream, (char)value, options.GetLanguage()); 475 break; 476 case 1: 477 case 4: 478 NSNumber_FormatShort(valobj, stream, (short)value, 479 options.GetLanguage()); 480 break; 481 case 2: 482 case 8: 483 NSNumber_FormatInt(valobj, stream, (int)value, options.GetLanguage()); 484 break; 485 case 3: 486 case 12: 487 NSNumber_FormatLong(valobj, stream, value, options.GetLanguage()); 488 break; 489 default: 490 return false; 491 } 492 return true; 493 } else { 494 Status error; 495 496 AppleObjCRuntime *runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>( 497 ObjCLanguageRuntime::Get(*process_sp)); 498 499 const bool new_format = 500 (runtime && runtime->GetFoundationVersion() >= 1400); 501 502 enum class TypeCodes : int { 503 sint8 = 0x0, 504 sint16 = 0x1, 505 sint32 = 0x2, 506 sint64 = 0x3, 507 f32 = 0x4, 508 f64 = 0x5, 509 sint128 = 0x6 510 }; 511 512 uint64_t data_location = valobj_addr + 2 * ptr_size; 513 TypeCodes type_code; 514 515 if (new_format) { 516 uint64_t cfinfoa = process_sp->ReadUnsignedIntegerFromMemory( 517 valobj_addr + ptr_size, ptr_size, 0, error); 518 519 if (error.Fail()) 520 return false; 521 522 bool is_preserved_number = cfinfoa & 0x8; 523 if (is_preserved_number) { 524 if (log) 525 log->Printf( 526 "Unsupported preserved NSNumber tagged pointer 0x%" PRIu64, 527 valobj_addr); 528 return false; 529 } 530 531 type_code = static_cast<TypeCodes>(cfinfoa & 0x7); 532 } else { 533 uint8_t data_type = process_sp->ReadUnsignedIntegerFromMemory( 534 valobj_addr + ptr_size, 1, 0, error) & 535 0x1F; 536 537 if (error.Fail()) 538 return false; 539 540 switch (data_type) { 541 case 1: 542 type_code = TypeCodes::sint8; 543 break; 544 case 2: 545 type_code = TypeCodes::sint16; 546 break; 547 case 3: 548 type_code = TypeCodes::sint32; 549 break; 550 case 17: 551 data_location += 8; 552 LLVM_FALLTHROUGH; 553 case 4: 554 type_code = TypeCodes::sint64; 555 break; 556 case 5: 557 type_code = TypeCodes::f32; 558 break; 559 case 6: 560 type_code = TypeCodes::f64; 561 break; 562 default: 563 return false; 564 } 565 } 566 567 uint64_t value = 0; 568 bool success = false; 569 switch (type_code) { 570 case TypeCodes::sint8: 571 value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 1, 0, 572 error); 573 if (error.Fail()) 574 return false; 575 NSNumber_FormatChar(valobj, stream, (char)value, options.GetLanguage()); 576 success = true; 577 break; 578 case TypeCodes::sint16: 579 value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 2, 0, 580 error); 581 if (error.Fail()) 582 return false; 583 NSNumber_FormatShort(valobj, stream, (short)value, 584 options.GetLanguage()); 585 success = true; 586 break; 587 case TypeCodes::sint32: 588 value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 4, 0, 589 error); 590 if (error.Fail()) 591 return false; 592 NSNumber_FormatInt(valobj, stream, (int)value, options.GetLanguage()); 593 success = true; 594 break; 595 case TypeCodes::sint64: 596 value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 8, 0, 597 error); 598 if (error.Fail()) 599 return false; 600 NSNumber_FormatLong(valobj, stream, value, options.GetLanguage()); 601 success = true; 602 break; 603 case TypeCodes::f32: { 604 uint32_t flt_as_int = process_sp->ReadUnsignedIntegerFromMemory( 605 data_location, 4, 0, error); 606 if (error.Fail()) 607 return false; 608 float flt_value = 0.0f; 609 memcpy(&flt_value, &flt_as_int, sizeof(flt_as_int)); 610 NSNumber_FormatFloat(valobj, stream, flt_value, options.GetLanguage()); 611 success = true; 612 break; 613 } 614 case TypeCodes::f64: { 615 uint64_t dbl_as_lng = process_sp->ReadUnsignedIntegerFromMemory( 616 data_location, 8, 0, error); 617 if (error.Fail()) 618 return false; 619 double dbl_value = 0.0; 620 memcpy(&dbl_value, &dbl_as_lng, sizeof(dbl_as_lng)); 621 NSNumber_FormatDouble(valobj, stream, dbl_value, options.GetLanguage()); 622 success = true; 623 break; 624 } 625 case TypeCodes::sint128: // internally, this is the same 626 { 627 uint64_t words[2]; 628 words[1] = process_sp->ReadUnsignedIntegerFromMemory(data_location, 8, 629 0, error); 630 if (error.Fail()) 631 return false; 632 words[0] = process_sp->ReadUnsignedIntegerFromMemory(data_location + 8, 633 8, 0, error); 634 if (error.Fail()) 635 return false; 636 llvm::APInt i128_value(128, words); 637 NSNumber_FormatInt128(valobj, stream, i128_value, 638 options.GetLanguage()); 639 success = true; 640 break; 641 } 642 } 643 return success; 644 } 645 } 646 647 return false; 648 } 649 650 bool lldb_private::formatters::NSDecimalNumberSummaryProvider( 651 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 652 ProcessSP process_sp = valobj.GetProcessSP(); 653 if (!process_sp) 654 return false; 655 656 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 657 uint32_t ptr_size = process_sp->GetAddressByteSize(); 658 659 Status error; 660 int8_t exponent = process_sp->ReadUnsignedIntegerFromMemory( 661 valobj_addr + ptr_size, 1, 0, error); 662 if (error.Fail()) 663 return false; 664 665 uint8_t length_and_negative = process_sp->ReadUnsignedIntegerFromMemory( 666 valobj_addr + ptr_size + 1, 1, 0, error); 667 if (error.Fail()) 668 return false; 669 670 // Fifth bit marks negativity. 671 const bool is_negative = (length_and_negative >> 4) & 1; 672 673 // Zero length and negative means NaN. 674 uint8_t length = length_and_negative & 0xf; 675 const bool is_nan = is_negative && (length == 0); 676 677 if (is_nan) { 678 stream.Printf("NaN"); 679 return true; 680 } 681 682 if (length == 0) { 683 stream.Printf("0"); 684 return true; 685 } 686 687 uint64_t mantissa = process_sp->ReadUnsignedIntegerFromMemory( 688 valobj_addr + ptr_size + 4, 8, 0, error); 689 if (error.Fail()) 690 return false; 691 692 if (is_negative) 693 stream.Printf("-"); 694 695 stream.Printf("%" PRIu64 " x 10^%" PRIi8, mantissa, exponent); 696 return true; 697 } 698 699 bool lldb_private::formatters::NSURLSummaryProvider( 700 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 701 ProcessSP process_sp = valobj.GetProcessSP(); 702 if (!process_sp) 703 return false; 704 705 ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); 706 707 if (!runtime) 708 return false; 709 710 ObjCLanguageRuntime::ClassDescriptorSP descriptor( 711 runtime->GetClassDescriptor(valobj)); 712 713 if (!descriptor || !descriptor->IsValid()) 714 return false; 715 716 uint32_t ptr_size = process_sp->GetAddressByteSize(); 717 718 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 719 720 if (!valobj_addr) 721 return false; 722 723 llvm::StringRef class_name = descriptor->GetClassName().GetStringRef(); 724 725 if (!class_name.equals("NSURL")) 726 return false; 727 728 uint64_t offset_text = ptr_size + ptr_size + 729 8; // ISA + pointer + 8 bytes of data (even on 32bit) 730 uint64_t offset_base = offset_text + ptr_size; 731 CompilerType type(valobj.GetCompilerType()); 732 ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset_text, type, true)); 733 ValueObjectSP base(valobj.GetSyntheticChildAtOffset(offset_base, type, true)); 734 if (!text || text->GetValueAsUnsigned(0) == 0) 735 return false; 736 737 StreamString base_summary; 738 if (base && base->GetValueAsUnsigned(0)) { 739 if (!NSURLSummaryProvider(*base, base_summary, options)) 740 base_summary.Clear(); 741 } 742 if (base_summary.Empty()) 743 return NSStringSummaryProvider(*text, stream, options); 744 745 StreamString summary; 746 if (!NSStringSummaryProvider(*text, summary, options) || summary.Empty()) 747 return false; 748 749 const char quote_char = '"'; 750 std::string prefix, suffix; 751 if (Language *language = Language::FindPlugin(options.GetLanguage())) { 752 if (!language->GetFormatterPrefixSuffix(*text, ConstString("NSString"), 753 prefix, suffix)) { 754 prefix.clear(); 755 suffix.clear(); 756 } 757 } 758 // @"A" -> @"A 759 llvm::StringRef summary_str = summary.GetString(); 760 bool back_consumed = summary_str.consume_back(quote_char + suffix); 761 assert(back_consumed); 762 UNUSED_IF_ASSERT_DISABLED(back_consumed); 763 // @"B" -> B" 764 llvm::StringRef base_summary_str = base_summary.GetString(); 765 bool front_consumed = base_summary_str.consume_front(prefix + quote_char); 766 assert(front_consumed); 767 UNUSED_IF_ASSERT_DISABLED(front_consumed); 768 // @"A -- B" 769 if (!summary_str.empty() && !base_summary_str.empty()) { 770 stream.Printf("%s -- %s", summary_str.str().c_str(), 771 base_summary_str.str().c_str()); 772 return true; 773 } 774 775 return false; 776 } 777 778 /// Bias value for tagged pointer exponents. 779 /// Recommended values: 780 /// 0x3e3: encodes all dates between distantPast and distantFuture 781 /// except for the range within about 1e-28 second of the reference date. 782 /// 0x3ef: encodes all dates for a few million years beyond distantPast and 783 /// distantFuture, except within about 1e-25 second of the reference date. 784 const int TAGGED_DATE_EXPONENT_BIAS = 0x3ef; 785 786 struct DoubleBits { 787 uint64_t fraction : 52; // unsigned 788 uint64_t exponent : 11; // signed 789 uint64_t sign : 1; 790 }; 791 792 struct TaggedDoubleBits { 793 uint64_t fraction : 52; // unsigned 794 uint64_t exponent : 7; // signed 795 uint64_t sign : 1; 796 uint64_t unused : 4; // placeholder for pointer tag bits 797 }; 798 799 static uint64_t decodeExponent(uint64_t exp) { 800 // Tagged exponent field is 7-bit signed. Sign-extend the value to 64 bits 801 // before performing arithmetic. 802 return llvm::SignExtend64<7>(exp) + TAGGED_DATE_EXPONENT_BIAS; 803 } 804 805 static double decodeTaggedTimeInterval(uint64_t encodedTimeInterval) { 806 if (encodedTimeInterval == 0) 807 return 0.0; 808 if (encodedTimeInterval == std::numeric_limits<uint64_t>::max()) 809 return (uint64_t)-0.0; 810 811 TaggedDoubleBits encodedBits = 812 llvm::bit_cast<TaggedDoubleBits>(encodedTimeInterval); 813 assert(encodedBits.unused == 0); 814 815 // Sign and fraction are represented exactly. 816 // Exponent is encoded. 817 DoubleBits decodedBits; 818 decodedBits.sign = encodedBits.sign; 819 decodedBits.fraction = encodedBits.fraction; 820 decodedBits.exponent = decodeExponent(encodedBits.exponent); 821 822 return llvm::bit_cast<double>(decodedBits); 823 } 824 825 bool lldb_private::formatters::NSDateSummaryProvider( 826 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 827 ProcessSP process_sp = valobj.GetProcessSP(); 828 if (!process_sp) 829 return false; 830 831 ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); 832 833 if (!runtime) 834 return false; 835 836 ObjCLanguageRuntime::ClassDescriptorSP descriptor( 837 runtime->GetClassDescriptor(valobj)); 838 839 if (!descriptor || !descriptor->IsValid()) 840 return false; 841 842 uint32_t ptr_size = process_sp->GetAddressByteSize(); 843 844 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 845 846 if (!valobj_addr) 847 return false; 848 849 uint64_t date_value_bits = 0; 850 double date_value = 0.0; 851 852 ConstString class_name = descriptor->GetClassName(); 853 854 static const ConstString g_NSDate("NSDate"); 855 static const ConstString g___NSDate("__NSDate"); 856 static const ConstString g___NSTaggedDate("__NSTaggedDate"); 857 static const ConstString g_NSCalendarDate("NSCalendarDate"); 858 static const ConstString g_NSConstantDate("NSConstantDate"); 859 860 if (class_name.IsEmpty()) 861 return false; 862 863 uint64_t info_bits = 0, value_bits = 0; 864 if ((class_name == g_NSDate) || (class_name == g___NSDate) || 865 (class_name == g___NSTaggedDate) || (class_name == g_NSConstantDate)) { 866 if (descriptor->GetTaggedPointerInfo(&info_bits, &value_bits)) { 867 date_value_bits = ((value_bits << 8) | (info_bits << 4)); 868 memcpy(&date_value, &date_value_bits, sizeof(date_value_bits)); 869 } else { 870 llvm::Triple triple( 871 process_sp->GetTarget().GetArchitecture().GetTriple()); 872 uint32_t delta = 873 (triple.isWatchOS() && triple.isWatchABI()) ? 8 : ptr_size; 874 Status error; 875 date_value_bits = process_sp->ReadUnsignedIntegerFromMemory( 876 valobj_addr + delta, 8, 0, error); 877 memcpy(&date_value, &date_value_bits, sizeof(date_value_bits)); 878 if (error.Fail()) 879 return false; 880 } 881 } else if (class_name == g_NSCalendarDate) { 882 Status error; 883 date_value_bits = process_sp->ReadUnsignedIntegerFromMemory( 884 valobj_addr + 2 * ptr_size, 8, 0, error); 885 memcpy(&date_value, &date_value_bits, sizeof(date_value_bits)); 886 if (error.Fail()) 887 return false; 888 } else 889 return false; 890 891 // FIXME: It seems old dates are not formatted according to NSDate's calendar 892 // so we hardcode distantPast's value so that it looks like LLDB is doing 893 // the right thing. 894 895 // The relative time in seconds from Cocoa Epoch to [NSDate distantPast]. 896 const double RelSecondsFromCocoaEpochToNSDateDistantPast = -63114076800; 897 if (date_value == RelSecondsFromCocoaEpochToNSDateDistantPast) { 898 stream.Printf("0001-01-01 00:00:00 UTC"); 899 return true; 900 } 901 902 // Accomodate for the __NSTaggedDate format introduced in Foundation 1600. 903 if (class_name == g___NSTaggedDate) { 904 auto *runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>( 905 ObjCLanguageRuntime::Get(*process_sp)); 906 if (runtime && runtime->GetFoundationVersion() >= 1600) 907 date_value = decodeTaggedTimeInterval(value_bits << 4); 908 } 909 910 // this snippet of code assumes that time_t == seconds since Jan-1-1970 this 911 // is generally true and POSIXly happy, but might break if a library vendor 912 // decides to get creative 913 time_t epoch = GetOSXEpoch(); 914 epoch = epoch + static_cast<time_t>(std::floor(date_value)); 915 tm *tm_date = gmtime(&epoch); 916 if (!tm_date) 917 return false; 918 std::string buffer(1024, 0); 919 if (strftime(&buffer[0], 1023, "%Z", tm_date) == 0) 920 return false; 921 stream.Printf("%04d-%02d-%02d %02d:%02d:%02d %s", tm_date->tm_year + 1900, 922 tm_date->tm_mon + 1, tm_date->tm_mday, tm_date->tm_hour, 923 tm_date->tm_min, tm_date->tm_sec, buffer.c_str()); 924 return true; 925 } 926 927 bool lldb_private::formatters::ObjCClassSummaryProvider( 928 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 929 ProcessSP process_sp = valobj.GetProcessSP(); 930 if (!process_sp) 931 return false; 932 933 ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); 934 935 if (!runtime) 936 return false; 937 938 ObjCLanguageRuntime::ClassDescriptorSP descriptor( 939 runtime->GetClassDescriptorFromISA(valobj.GetValueAsUnsigned(0))); 940 941 if (!descriptor || !descriptor->IsValid()) 942 return false; 943 944 ConstString class_name = descriptor->GetClassName(); 945 946 if (class_name.IsEmpty()) 947 return false; 948 949 if (ConstString cs = Mangled(class_name).GetDemangledName()) 950 class_name = cs; 951 952 stream.Printf("%s", class_name.AsCString("<unknown class>")); 953 return true; 954 } 955 956 class ObjCClassSyntheticChildrenFrontEnd : public SyntheticChildrenFrontEnd { 957 public: 958 ObjCClassSyntheticChildrenFrontEnd(lldb::ValueObjectSP valobj_sp) 959 : SyntheticChildrenFrontEnd(*valobj_sp) {} 960 961 ~ObjCClassSyntheticChildrenFrontEnd() override = default; 962 963 size_t CalculateNumChildren() override { return 0; } 964 965 lldb::ValueObjectSP GetChildAtIndex(size_t idx) override { 966 return lldb::ValueObjectSP(); 967 } 968 969 bool Update() override { return false; } 970 971 bool MightHaveChildren() override { return false; } 972 973 size_t GetIndexOfChildWithName(ConstString name) override { 974 return UINT32_MAX; 975 } 976 }; 977 978 SyntheticChildrenFrontEnd * 979 lldb_private::formatters::ObjCClassSyntheticFrontEndCreator( 980 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { 981 return new ObjCClassSyntheticChildrenFrontEnd(valobj_sp); 982 } 983 984 template <bool needs_at> 985 bool lldb_private::formatters::NSDataSummaryProvider( 986 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 987 ProcessSP process_sp = valobj.GetProcessSP(); 988 if (!process_sp) 989 return false; 990 991 ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); 992 993 if (!runtime) 994 return false; 995 996 ObjCLanguageRuntime::ClassDescriptorSP descriptor( 997 runtime->GetClassDescriptor(valobj)); 998 999 if (!descriptor || !descriptor->IsValid()) 1000 return false; 1001 1002 bool is_64bit = (process_sp->GetAddressByteSize() == 8); 1003 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 1004 1005 if (!valobj_addr) 1006 return false; 1007 1008 uint64_t value = 0; 1009 1010 llvm::StringRef class_name = descriptor->GetClassName().GetCString(); 1011 1012 if (class_name.empty()) 1013 return false; 1014 1015 bool isNSConcreteData = class_name == "NSConcreteData"; 1016 bool isNSConcreteMutableData = class_name == "NSConcreteMutableData"; 1017 bool isNSCFData = class_name == "__NSCFData"; 1018 if (isNSConcreteData || isNSConcreteMutableData || isNSCFData) { 1019 uint32_t offset; 1020 if (isNSConcreteData) 1021 offset = is_64bit ? 8 : 4; 1022 else 1023 offset = is_64bit ? 16 : 8; 1024 1025 Status error; 1026 value = process_sp->ReadUnsignedIntegerFromMemory( 1027 valobj_addr + offset, is_64bit ? 8 : 4, 0, error); 1028 if (error.Fail()) 1029 return false; 1030 } else if (class_name == "_NSInlineData") { 1031 uint32_t offset = (is_64bit ? 8 : 4); 1032 Status error; 1033 value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + offset, 2, 1034 0, error); 1035 if (error.Fail()) 1036 return false; 1037 } else if (class_name == "_NSZeroData") { 1038 value = 0; 1039 } else 1040 return false; 1041 1042 stream.Printf("%s%" PRIu64 " byte%s%s", (needs_at ? "@\"" : ""), value, 1043 (value != 1 ? "s" : ""), (needs_at ? "\"" : "")); 1044 1045 return true; 1046 } 1047 1048 bool lldb_private::formatters::ObjCBOOLSummaryProvider( 1049 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 1050 const uint32_t type_info = valobj.GetCompilerType().GetTypeInfo(); 1051 1052 ValueObjectSP real_guy_sp = valobj.GetSP(); 1053 1054 if (type_info & eTypeIsPointer) { 1055 Status err; 1056 real_guy_sp = valobj.Dereference(err); 1057 if (err.Fail() || !real_guy_sp) 1058 return false; 1059 } else if (type_info & eTypeIsReference) { 1060 real_guy_sp = valobj.GetChildAtIndex(0, true); 1061 if (!real_guy_sp) 1062 return false; 1063 } 1064 int8_t value = (real_guy_sp->GetValueAsSigned(0) & 0xFF); 1065 switch (value) { 1066 case 0: 1067 stream.Printf("NO"); 1068 break; 1069 case 1: 1070 stream.Printf("YES"); 1071 break; 1072 default: 1073 stream.Printf("%d", value); 1074 break; 1075 } 1076 return true; 1077 } 1078 1079 bool lldb_private::formatters::ObjCBooleanSummaryProvider( 1080 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 1081 lldb::addr_t valobj_ptr_value = 1082 valobj.GetValueAsUnsigned(LLDB_INVALID_ADDRESS); 1083 if (valobj_ptr_value == LLDB_INVALID_ADDRESS) 1084 return false; 1085 1086 ProcessSP process_sp(valobj.GetProcessSP()); 1087 if (!process_sp) 1088 return false; 1089 1090 if (AppleObjCRuntime *objc_runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>( 1091 ObjCLanguageRuntime::Get(*process_sp))) { 1092 lldb::addr_t cf_true = LLDB_INVALID_ADDRESS, 1093 cf_false = LLDB_INVALID_ADDRESS; 1094 objc_runtime->GetValuesForGlobalCFBooleans(cf_true, cf_false); 1095 if (valobj_ptr_value == cf_true) { 1096 stream.PutCString("YES"); 1097 return true; 1098 } 1099 if (valobj_ptr_value == cf_false) { 1100 stream.PutCString("NO"); 1101 return true; 1102 } 1103 } 1104 1105 return false; 1106 } 1107 1108 template <bool is_sel_ptr> 1109 bool lldb_private::formatters::ObjCSELSummaryProvider( 1110 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 1111 lldb::ValueObjectSP valobj_sp; 1112 1113 CompilerType charstar(valobj.GetCompilerType() 1114 .GetBasicTypeFromAST(eBasicTypeChar) 1115 .GetPointerType()); 1116 1117 if (!charstar) 1118 return false; 1119 1120 ExecutionContext exe_ctx(valobj.GetExecutionContextRef()); 1121 1122 if (is_sel_ptr) { 1123 lldb::addr_t data_address = valobj.GetValueAsUnsigned(LLDB_INVALID_ADDRESS); 1124 if (data_address == LLDB_INVALID_ADDRESS) 1125 return false; 1126 valobj_sp = ValueObject::CreateValueObjectFromAddress("text", data_address, 1127 exe_ctx, charstar); 1128 } else { 1129 DataExtractor data; 1130 Status error; 1131 valobj.GetData(data, error); 1132 if (error.Fail()) 1133 return false; 1134 valobj_sp = 1135 ValueObject::CreateValueObjectFromData("text", data, exe_ctx, charstar); 1136 } 1137 1138 if (!valobj_sp) 1139 return false; 1140 1141 stream.Printf("%s", valobj_sp->GetSummaryAsCString()); 1142 return true; 1143 } 1144 1145 // POSIX has an epoch on Jan-1-1970, but Cocoa prefers Jan-1-2001 1146 // this call gives the POSIX equivalent of the Cocoa epoch 1147 time_t lldb_private::formatters::GetOSXEpoch() { 1148 static time_t epoch = 0; 1149 if (!epoch) { 1150 #ifndef _WIN32 1151 tzset(); 1152 tm tm_epoch; 1153 tm_epoch.tm_sec = 0; 1154 tm_epoch.tm_hour = 0; 1155 tm_epoch.tm_min = 0; 1156 tm_epoch.tm_mon = 0; 1157 tm_epoch.tm_mday = 1; 1158 tm_epoch.tm_year = 2001 - 1900; 1159 tm_epoch.tm_isdst = -1; 1160 tm_epoch.tm_gmtoff = 0; 1161 tm_epoch.tm_zone = nullptr; 1162 epoch = timegm(&tm_epoch); 1163 #endif 1164 } 1165 return epoch; 1166 } 1167 1168 template bool lldb_private::formatters::NSDataSummaryProvider<true>( 1169 ValueObject &, Stream &, const TypeSummaryOptions &); 1170 1171 template bool lldb_private::formatters::NSDataSummaryProvider<false>( 1172 ValueObject &, Stream &, const TypeSummaryOptions &); 1173 1174 template bool lldb_private::formatters::ObjCSELSummaryProvider<true>( 1175 ValueObject &, Stream &, const TypeSummaryOptions &); 1176 1177 template bool lldb_private::formatters::ObjCSELSummaryProvider<false>( 1178 ValueObject &, Stream &, const TypeSummaryOptions &); 1179