1 //===-- Cocoa.cpp -----------------------------------------------*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "Cocoa.h" 11 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/Symbol/ClangASTContext.h" 20 #include "lldb/Target/Language.h" 21 #include "lldb/Target/ObjCLanguageRuntime.h" 22 #include "lldb/Target/Process.h" 23 #include "lldb/Target/ProcessStructReader.h" 24 #include "lldb/Target/Target.h" 25 #include "lldb/Utility/DataBufferHeap.h" 26 #include "lldb/Utility/Endian.h" 27 #include "lldb/Utility/Status.h" 28 #include "lldb/Utility/Stream.h" 29 30 #include "llvm/ADT/APInt.h" 31 32 #include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h" 33 34 #include "NSString.h" 35 36 using namespace lldb; 37 using namespace lldb_private; 38 using namespace lldb_private::formatters; 39 40 bool lldb_private::formatters::NSBundleSummaryProvider( 41 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 42 ProcessSP process_sp = valobj.GetProcessSP(); 43 if (!process_sp) 44 return false; 45 46 ObjCLanguageRuntime *runtime = 47 (ObjCLanguageRuntime *)process_sp->GetLanguageRuntime( 48 lldb::eLanguageTypeObjC); 49 50 if (!runtime) 51 return false; 52 53 ObjCLanguageRuntime::ClassDescriptorSP descriptor( 54 runtime->GetClassDescriptor(valobj)); 55 56 if (!descriptor || !descriptor->IsValid()) 57 return false; 58 59 uint32_t ptr_size = process_sp->GetAddressByteSize(); 60 61 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 62 63 if (!valobj_addr) 64 return false; 65 66 llvm::StringRef class_name(descriptor->GetClassName().GetCString()); 67 68 if (class_name.empty()) 69 return false; 70 71 if (class_name == "NSBundle") { 72 uint64_t offset = 5 * ptr_size; 73 ValueObjectSP text(valobj.GetSyntheticChildAtOffset( 74 offset, 75 valobj.GetCompilerType().GetBasicTypeFromAST(lldb::eBasicTypeObjCID), 76 true)); 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 = 97 (ObjCLanguageRuntime *)process_sp->GetLanguageRuntime( 98 lldb::eLanguageTypeObjC); 99 100 if (!runtime) 101 return false; 102 103 ObjCLanguageRuntime::ClassDescriptorSP descriptor( 104 runtime->GetClassDescriptor(valobj)); 105 106 if (!descriptor || !descriptor->IsValid()) 107 return false; 108 109 uint32_t ptr_size = process_sp->GetAddressByteSize(); 110 111 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 112 113 if (!valobj_addr) 114 return false; 115 116 llvm::StringRef class_name(descriptor->GetClassName().GetCString()); 117 118 if (class_name.empty()) 119 return false; 120 121 if (class_name == "__NSTimeZone") { 122 uint64_t offset = ptr_size; 123 ValueObjectSP text(valobj.GetSyntheticChildAtOffset( 124 offset, valobj.GetCompilerType(), true)); 125 StreamString summary_stream; 126 bool was_nsstring_ok = 127 NSStringSummaryProvider(*text, summary_stream, options); 128 if (was_nsstring_ok && summary_stream.GetSize() > 0) { 129 stream.Printf("%s", summary_stream.GetData()); 130 return true; 131 } 132 } 133 134 return false; 135 } 136 137 bool lldb_private::formatters::NSNotificationSummaryProvider( 138 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 139 ProcessSP process_sp = valobj.GetProcessSP(); 140 if (!process_sp) 141 return false; 142 143 ObjCLanguageRuntime *runtime = 144 (ObjCLanguageRuntime *)process_sp->GetLanguageRuntime( 145 lldb::eLanguageTypeObjC); 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 StreamString summary_stream; 173 bool was_nsstring_ok = 174 NSStringSummaryProvider(*text, summary_stream, options); 175 if (was_nsstring_ok && summary_stream.GetSize() > 0) { 176 stream.Printf("%s", summary_stream.GetData()); 177 return true; 178 } 179 } 180 181 return false; 182 } 183 184 bool lldb_private::formatters::NSMachPortSummaryProvider( 185 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 186 ProcessSP process_sp = valobj.GetProcessSP(); 187 if (!process_sp) 188 return false; 189 190 ObjCLanguageRuntime *runtime = 191 (ObjCLanguageRuntime *)process_sp->GetLanguageRuntime( 192 lldb::eLanguageTypeObjC); 193 194 if (!runtime) 195 return false; 196 197 ObjCLanguageRuntime::ClassDescriptorSP descriptor( 198 runtime->GetClassDescriptor(valobj)); 199 200 if (!descriptor || !descriptor->IsValid()) 201 return false; 202 203 uint32_t ptr_size = process_sp->GetAddressByteSize(); 204 205 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 206 207 if (!valobj_addr) 208 return false; 209 210 llvm::StringRef class_name(descriptor->GetClassName().GetCString()); 211 212 if (class_name.empty()) 213 return false; 214 215 uint64_t port_number = 0; 216 217 if (class_name == "NSMachPort") { 218 uint64_t offset = (ptr_size == 4 ? 12 : 20); 219 Status error; 220 port_number = process_sp->ReadUnsignedIntegerFromMemory( 221 offset + valobj_addr, 4, 0, error); 222 if (error.Success()) { 223 stream.Printf("mach port: %u", 224 (uint32_t)(port_number & 0x00000000FFFFFFFF)); 225 return true; 226 } 227 } 228 229 return false; 230 } 231 232 bool lldb_private::formatters::NSIndexSetSummaryProvider( 233 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 234 ProcessSP process_sp = valobj.GetProcessSP(); 235 if (!process_sp) 236 return false; 237 238 ObjCLanguageRuntime *runtime = 239 (ObjCLanguageRuntime *)process_sp->GetLanguageRuntime( 240 lldb::eLanguageTypeObjC); 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 uint64_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 = value.toString(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 ObjCLanguageRuntime *runtime = 430 (ObjCLanguageRuntime *)process_sp->GetLanguageRuntime( 431 lldb::eLanguageTypeObjC); 432 433 if (!runtime) 434 return false; 435 436 ObjCLanguageRuntime::ClassDescriptorSP descriptor( 437 runtime->GetClassDescriptor(valobj)); 438 439 if (!descriptor || !descriptor->IsValid()) 440 return false; 441 442 uint32_t ptr_size = process_sp->GetAddressByteSize(); 443 444 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 445 446 if (!valobj_addr) 447 return false; 448 449 llvm::StringRef class_name(descriptor->GetClassName().GetCString()); 450 451 if (class_name.empty()) 452 return false; 453 454 if (class_name == "__NSCFBoolean") 455 return ObjCBooleanSummaryProvider(valobj, stream, options); 456 457 if (class_name == "NSDecimalNumber") 458 return NSDecimalNumberSummaryProvider(valobj, stream, options); 459 460 if (class_name == "NSNumber" || class_name == "__NSCFNumber") { 461 uint64_t value = 0; 462 uint64_t i_bits = 0; 463 if (descriptor->GetTaggedPointerInfo(&i_bits, &value)) { 464 switch (i_bits) { 465 case 0: 466 NSNumber_FormatChar(valobj, stream, (char)value, options.GetLanguage()); 467 break; 468 case 1: 469 case 4: 470 NSNumber_FormatShort(valobj, stream, (short)value, 471 options.GetLanguage()); 472 break; 473 case 2: 474 case 8: 475 NSNumber_FormatInt(valobj, stream, (int)value, options.GetLanguage()); 476 break; 477 case 3: 478 case 12: 479 NSNumber_FormatLong(valobj, stream, value, options.GetLanguage()); 480 break; 481 default: 482 return false; 483 } 484 return true; 485 } else { 486 Status error; 487 488 AppleObjCRuntime *runtime = 489 llvm::dyn_cast_or_null<AppleObjCRuntime>( 490 process_sp->GetObjCLanguageRuntime()); 491 492 const bool new_format = 493 (runtime && runtime->GetFoundationVersion() >= 1400); 494 495 enum class TypeCodes : int { 496 sint8 = 0x0, 497 sint16 = 0x1, 498 sint32 = 0x2, 499 sint64 = 0x3, 500 f32 = 0x4, 501 f64 = 0x5, 502 sint128 = 0x6 503 }; 504 505 uint64_t data_location = valobj_addr + 2 * ptr_size; 506 TypeCodes type_code; 507 508 if (new_format) { 509 uint64_t cfinfoa = 510 process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, 511 ptr_size, 0, error); 512 513 if (error.Fail()) 514 return false; 515 516 bool is_preserved_number = cfinfoa & 0x8; 517 if (is_preserved_number) { 518 lldbassert(!static_cast<bool>("We should handle preserved numbers!")); 519 return false; 520 } 521 522 type_code = static_cast<TypeCodes>(cfinfoa & 0x7); 523 } else { 524 uint8_t data_type = 525 process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, 1, 526 0, error) & 0x1F; 527 528 if (error.Fail()) 529 return false; 530 531 switch (data_type) { 532 case 1: type_code = TypeCodes::sint8; break; 533 case 2: type_code = TypeCodes::sint16; break; 534 case 3: type_code = TypeCodes::sint32; break; 535 case 17: data_location += 8; LLVM_FALLTHROUGH; 536 case 4: type_code = TypeCodes::sint64; break; 537 case 5: type_code = TypeCodes::f32; break; 538 case 6: type_code = TypeCodes::f64; break; 539 default: return false; 540 } 541 } 542 543 uint64_t value = 0; 544 bool success = false; 545 switch (type_code) { 546 case TypeCodes::sint8: 547 value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 1, 0, 548 error); 549 if (error.Fail()) 550 return false; 551 NSNumber_FormatChar(valobj, stream, (char)value, options.GetLanguage()); 552 success = true; 553 break; 554 case TypeCodes::sint16: 555 value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 2, 0, 556 error); 557 if (error.Fail()) 558 return false; 559 NSNumber_FormatShort(valobj, stream, (short)value, 560 options.GetLanguage()); 561 success = true; 562 break; 563 case TypeCodes::sint32: 564 value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 4, 0, 565 error); 566 if (error.Fail()) 567 return false; 568 NSNumber_FormatInt(valobj, stream, (int)value, options.GetLanguage()); 569 success = true; 570 break; 571 case TypeCodes::sint64: 572 value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 8, 0, 573 error); 574 if (error.Fail()) 575 return false; 576 NSNumber_FormatLong(valobj, stream, value, options.GetLanguage()); 577 success = true; 578 break; 579 case TypeCodes::f32: 580 { 581 uint32_t flt_as_int = process_sp->ReadUnsignedIntegerFromMemory( 582 data_location, 4, 0, error); 583 if (error.Fail()) 584 return false; 585 float flt_value = 0.0f; 586 memcpy(&flt_value, &flt_as_int, sizeof(flt_as_int)); 587 NSNumber_FormatFloat(valobj, stream, flt_value, options.GetLanguage()); 588 success = true; 589 break; 590 } 591 case TypeCodes::f64: 592 { 593 uint64_t dbl_as_lng = process_sp->ReadUnsignedIntegerFromMemory( 594 data_location, 8, 0, error); 595 if (error.Fail()) 596 return false; 597 double dbl_value = 0.0; 598 memcpy(&dbl_value, &dbl_as_lng, sizeof(dbl_as_lng)); 599 NSNumber_FormatDouble(valobj, stream, dbl_value, options.GetLanguage()); 600 success = true; 601 break; 602 } 603 case TypeCodes::sint128: // internally, this is the same 604 { 605 uint64_t words[2]; 606 words[1] = process_sp->ReadUnsignedIntegerFromMemory( 607 data_location, 8, 0, error); 608 if (error.Fail()) 609 return false; 610 words[0] = process_sp->ReadUnsignedIntegerFromMemory( 611 data_location + 8, 8, 0, error); 612 if (error.Fail()) 613 return false; 614 llvm::APInt i128_value(128, words); 615 NSNumber_FormatInt128(valobj, stream, i128_value, options.GetLanguage()); 616 success = true; 617 break; 618 } 619 } 620 return success; 621 } 622 } 623 624 return false; 625 } 626 627 bool lldb_private::formatters::NSDecimalNumberSummaryProvider( 628 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 629 ProcessSP process_sp = valobj.GetProcessSP(); 630 if (!process_sp) 631 return false; 632 633 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 634 uint32_t ptr_size = process_sp->GetAddressByteSize(); 635 636 Status error; 637 int8_t exponent = process_sp->ReadUnsignedIntegerFromMemory( 638 valobj_addr + ptr_size, 1, 0, error); 639 if (error.Fail()) 640 return false; 641 642 uint8_t length_and_negative = process_sp->ReadUnsignedIntegerFromMemory( 643 valobj_addr + ptr_size + 1, 1, 0, error); 644 if (error.Fail()) 645 return false; 646 647 // Fifth bit marks negativity. 648 const bool is_negative = (length_and_negative >> 4) & 1; 649 650 // Zero length and negative means NaN. 651 uint8_t length = length_and_negative & 0xf; 652 const bool is_nan = is_negative && (length == 0); 653 654 if (is_nan) { 655 stream.Printf("NaN"); 656 return true; 657 } 658 659 if (length == 0) { 660 stream.Printf("0"); 661 return true; 662 } 663 664 uint64_t mantissa = process_sp->ReadUnsignedIntegerFromMemory( 665 valobj_addr + ptr_size + 4, 8, 0, error); 666 if (error.Fail()) 667 return false; 668 669 if (is_negative) 670 stream.Printf("-"); 671 672 stream.Printf("%" PRIu64 " x 10^%" PRIi8, mantissa, exponent); 673 return true; 674 } 675 676 bool lldb_private::formatters::NSURLSummaryProvider( 677 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 678 ProcessSP process_sp = valobj.GetProcessSP(); 679 if (!process_sp) 680 return false; 681 682 ObjCLanguageRuntime *runtime = 683 (ObjCLanguageRuntime *)process_sp->GetLanguageRuntime( 684 lldb::eLanguageTypeObjC); 685 686 if (!runtime) 687 return false; 688 689 ObjCLanguageRuntime::ClassDescriptorSP descriptor( 690 runtime->GetClassDescriptor(valobj)); 691 692 if (!descriptor || !descriptor->IsValid()) 693 return false; 694 695 uint32_t ptr_size = process_sp->GetAddressByteSize(); 696 697 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 698 699 if (!valobj_addr) 700 return false; 701 702 llvm::StringRef class_name = descriptor->GetClassName().GetStringRef(); 703 704 if (!class_name.equals("NSURL")) 705 return false; 706 707 uint64_t offset_text = ptr_size + ptr_size + 708 8; // ISA + pointer + 8 bytes of data (even on 32bit) 709 uint64_t offset_base = offset_text + ptr_size; 710 CompilerType type(valobj.GetCompilerType()); 711 ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset_text, type, true)); 712 ValueObjectSP base(valobj.GetSyntheticChildAtOffset(offset_base, type, true)); 713 if (!text) 714 return false; 715 if (text->GetValueAsUnsigned(0) == 0) 716 return false; 717 StreamString summary; 718 if (!NSStringSummaryProvider(*text, summary, options)) 719 return false; 720 if (base && base->GetValueAsUnsigned(0)) { 721 std::string summary_str = summary.GetString(); 722 723 if (!summary_str.empty()) 724 summary_str.pop_back(); 725 summary_str += " -- "; 726 StreamString base_summary; 727 if (NSURLSummaryProvider(*base, base_summary, options) && 728 !base_summary.Empty()) { 729 llvm::StringRef base_str = base_summary.GetString(); 730 if (base_str.size() > 2) 731 base_str = base_str.drop_front(2); 732 summary_str += base_str; 733 } 734 summary.Clear(); 735 summary.PutCString(summary_str); 736 } 737 if (!summary.Empty()) { 738 stream.PutCString(summary.GetString()); 739 return true; 740 } 741 742 return false; 743 } 744 745 /// Bias value for tagged pointer exponents. 746 /// Recommended values: 747 /// 0x3e3: encodes all dates between distantPast and distantFuture 748 /// except for the range within about 1e-28 second of the reference date. 749 /// 0x3ef: encodes all dates for a few million years beyond distantPast and 750 /// distantFuture, except within about 1e-25 second of the reference date. 751 const int TAGGED_DATE_EXPONENT_BIAS = 0x3ef; 752 753 typedef union { 754 struct { 755 uint64_t fraction:52; // unsigned 756 uint64_t exponent:11; // signed 757 uint64_t sign:1; 758 }; 759 uint64_t i; 760 double d; 761 } DoubleBits; 762 typedef union { 763 struct { 764 uint64_t fraction:52; // unsigned 765 uint64_t exponent:7; // signed 766 uint64_t sign:1; 767 uint64_t unused:4; // placeholder for pointer tag bits 768 }; 769 uint64_t i; 770 } TaggedDoubleBits; 771 772 static uint64_t decodeExponent(uint64_t exp) { 773 // Tagged exponent field is 7-bit signed. Sign-extend the value to 64 bits 774 // before performing arithmetic. 775 return llvm::SignExtend64<7>(exp) + TAGGED_DATE_EXPONENT_BIAS; 776 } 777 778 static uint64_t decodeTaggedTimeInterval(uint64_t encodedTimeInterval) { 779 if (encodedTimeInterval == 0) 780 return 0.0; 781 if (encodedTimeInterval == std::numeric_limits<uint64_t>::max()) 782 return (uint64_t)-0.0; 783 784 TaggedDoubleBits encodedBits = {}; 785 encodedBits.i = encodedTimeInterval; 786 DoubleBits decodedBits; 787 788 // Sign and fraction are represented exactly. 789 // Exponent is encoded. 790 assert(encodedBits.unused == 0); 791 decodedBits.sign = encodedBits.sign; 792 decodedBits.fraction = encodedBits.fraction; 793 decodedBits.exponent = decodeExponent(encodedBits.exponent); 794 795 return decodedBits.d; 796 } 797 798 bool lldb_private::formatters::NSDateSummaryProvider( 799 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 800 ProcessSP process_sp = valobj.GetProcessSP(); 801 if (!process_sp) 802 return false; 803 804 ObjCLanguageRuntime *runtime = 805 (ObjCLanguageRuntime *)process_sp->GetLanguageRuntime( 806 lldb::eLanguageTypeObjC); 807 808 if (!runtime) 809 return false; 810 811 ObjCLanguageRuntime::ClassDescriptorSP descriptor( 812 runtime->GetClassDescriptor(valobj)); 813 814 if (!descriptor || !descriptor->IsValid()) 815 return false; 816 817 uint32_t ptr_size = process_sp->GetAddressByteSize(); 818 819 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 820 821 if (!valobj_addr) 822 return false; 823 824 uint64_t date_value_bits = 0; 825 double date_value = 0.0; 826 827 ConstString class_name = descriptor->GetClassName(); 828 829 static const ConstString g_NSDate("NSDate"); 830 static const ConstString g___NSDate("__NSDate"); 831 static const ConstString g___NSTaggedDate("__NSTaggedDate"); 832 static const ConstString g_NSCalendarDate("NSCalendarDate"); 833 834 if (class_name.IsEmpty()) 835 return false; 836 837 uint64_t info_bits = 0, value_bits = 0; 838 if ((class_name == g_NSDate) || (class_name == g___NSDate) || 839 (class_name == g___NSTaggedDate)) { 840 if (descriptor->GetTaggedPointerInfo(&info_bits, &value_bits)) { 841 date_value_bits = ((value_bits << 8) | (info_bits << 4)); 842 memcpy(&date_value, &date_value_bits, sizeof(date_value_bits)); 843 } else { 844 llvm::Triple triple( 845 process_sp->GetTarget().GetArchitecture().GetTriple()); 846 uint32_t delta = 847 (triple.isWatchOS() && triple.isWatchABI()) ? 8 : ptr_size; 848 Status error; 849 date_value_bits = process_sp->ReadUnsignedIntegerFromMemory( 850 valobj_addr + delta, 8, 0, error); 851 memcpy(&date_value, &date_value_bits, sizeof(date_value_bits)); 852 if (error.Fail()) 853 return false; 854 } 855 } else if (class_name == g_NSCalendarDate) { 856 Status error; 857 date_value_bits = process_sp->ReadUnsignedIntegerFromMemory( 858 valobj_addr + 2 * ptr_size, 8, 0, error); 859 memcpy(&date_value, &date_value_bits, sizeof(date_value_bits)); 860 if (error.Fail()) 861 return false; 862 } else 863 return false; 864 865 if (date_value == -63114076800) { 866 stream.Printf("0001-12-30 00:00:00 +0000"); 867 return true; 868 } 869 870 // Accomodate for the __NSTaggedDate format introduced in Foundation 1600. 871 if (class_name == g___NSTaggedDate) { 872 auto *runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>(process_sp->GetObjCLanguageRuntime()); 873 if (runtime && runtime->GetFoundationVersion() >= 1600) 874 date_value = decodeTaggedTimeInterval(value_bits << 4); 875 } 876 877 // this snippet of code assumes that time_t == seconds since Jan-1-1970 this 878 // is generally true and POSIXly happy, but might break if a library vendor 879 // decides to get creative 880 time_t epoch = GetOSXEpoch(); 881 epoch = epoch + (time_t)date_value; 882 tm *tm_date = gmtime(&epoch); 883 if (!tm_date) 884 return false; 885 std::string buffer(1024, 0); 886 if (strftime(&buffer[0], 1023, "%Z", tm_date) == 0) 887 return false; 888 stream.Printf("%04d-%02d-%02d %02d:%02d:%02d %s", tm_date->tm_year + 1900, 889 tm_date->tm_mon + 1, tm_date->tm_mday, tm_date->tm_hour, 890 tm_date->tm_min, tm_date->tm_sec, buffer.c_str()); 891 return true; 892 } 893 894 bool lldb_private::formatters::ObjCClassSummaryProvider( 895 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 896 ProcessSP process_sp = valobj.GetProcessSP(); 897 if (!process_sp) 898 return false; 899 900 ObjCLanguageRuntime *runtime = 901 (ObjCLanguageRuntime *)process_sp->GetLanguageRuntime( 902 lldb::eLanguageTypeObjC); 903 904 if (!runtime) 905 return false; 906 907 ObjCLanguageRuntime::ClassDescriptorSP descriptor( 908 runtime->GetClassDescriptorFromISA(valobj.GetValueAsUnsigned(0))); 909 910 if (!descriptor || !descriptor->IsValid()) 911 return false; 912 913 ConstString class_name = descriptor->GetClassName(); 914 915 if (class_name.IsEmpty()) 916 return false; 917 918 if (ConstString cs = 919 Mangled(class_name).GetDemangledName(lldb::eLanguageTypeUnknown)) 920 class_name = cs; 921 922 stream.Printf("%s", class_name.AsCString("<unknown class>")); 923 return true; 924 } 925 926 class ObjCClassSyntheticChildrenFrontEnd : public SyntheticChildrenFrontEnd { 927 public: 928 ObjCClassSyntheticChildrenFrontEnd(lldb::ValueObjectSP valobj_sp) 929 : SyntheticChildrenFrontEnd(*valobj_sp) {} 930 931 ~ObjCClassSyntheticChildrenFrontEnd() override = default; 932 933 size_t CalculateNumChildren() override { return 0; } 934 935 lldb::ValueObjectSP GetChildAtIndex(size_t idx) override { 936 return lldb::ValueObjectSP(); 937 } 938 939 bool Update() override { return false; } 940 941 bool MightHaveChildren() override { return false; } 942 943 size_t GetIndexOfChildWithName(const ConstString &name) override { 944 return UINT32_MAX; 945 } 946 }; 947 948 SyntheticChildrenFrontEnd * 949 lldb_private::formatters::ObjCClassSyntheticFrontEndCreator( 950 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { 951 return new ObjCClassSyntheticChildrenFrontEnd(valobj_sp); 952 } 953 954 template <bool needs_at> 955 bool lldb_private::formatters::NSDataSummaryProvider( 956 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 957 ProcessSP process_sp = valobj.GetProcessSP(); 958 if (!process_sp) 959 return false; 960 961 ObjCLanguageRuntime *runtime = 962 (ObjCLanguageRuntime *)process_sp->GetLanguageRuntime( 963 lldb::eLanguageTypeObjC); 964 965 if (!runtime) 966 return false; 967 968 ObjCLanguageRuntime::ClassDescriptorSP descriptor( 969 runtime->GetClassDescriptor(valobj)); 970 971 if (!descriptor || !descriptor->IsValid()) 972 return false; 973 974 bool is_64bit = (process_sp->GetAddressByteSize() == 8); 975 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 976 977 if (!valobj_addr) 978 return false; 979 980 uint64_t value = 0; 981 982 llvm::StringRef class_name = descriptor->GetClassName().GetCString(); 983 984 if (class_name.empty()) 985 return false; 986 987 bool isNSConcreteData = class_name == "NSConcreteData"; 988 bool isNSConcreteMutableData = class_name == "NSConcreteMutableData"; 989 bool isNSCFData = class_name == "__NSCFData"; 990 if (isNSConcreteData || isNSConcreteMutableData || isNSCFData) { 991 uint32_t offset; 992 if (isNSConcreteData) 993 offset = is_64bit ? 8 : 4; 994 else 995 offset = is_64bit ? 16 : 8; 996 997 Status error; 998 value = process_sp->ReadUnsignedIntegerFromMemory( 999 valobj_addr + offset, is_64bit ? 8 : 4, 0, error); 1000 if (error.Fail()) 1001 return false; 1002 } else if (class_name == "_NSInlineData") { 1003 uint32_t offset = (is_64bit ? 8 : 4); 1004 Status error; 1005 value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + offset, 2, 1006 0, error); 1007 if (error.Fail()) 1008 return false; 1009 } else if (class_name == "_NSZeroData") { 1010 value = 0; 1011 } else 1012 return false; 1013 1014 stream.Printf("%s%" PRIu64 " byte%s%s", (needs_at ? "@\"" : ""), value, 1015 (value != 1 ? "s" : ""), (needs_at ? "\"" : "")); 1016 1017 return true; 1018 } 1019 1020 bool lldb_private::formatters::ObjCBOOLSummaryProvider( 1021 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 1022 const uint32_t type_info = valobj.GetCompilerType().GetTypeInfo(); 1023 1024 ValueObjectSP real_guy_sp = valobj.GetSP(); 1025 1026 if (type_info & eTypeIsPointer) { 1027 Status err; 1028 real_guy_sp = valobj.Dereference(err); 1029 if (err.Fail() || !real_guy_sp) 1030 return false; 1031 } else if (type_info & eTypeIsReference) { 1032 real_guy_sp = valobj.GetChildAtIndex(0, true); 1033 if (!real_guy_sp) 1034 return false; 1035 } 1036 uint8_t value = (real_guy_sp->GetValueAsUnsigned(0) & 0xFF); 1037 switch (value) { 1038 case 0: 1039 stream.Printf("NO"); 1040 break; 1041 case 1: 1042 stream.Printf("YES"); 1043 break; 1044 default: 1045 stream.Printf("%u", value); 1046 break; 1047 } 1048 return true; 1049 } 1050 1051 bool lldb_private::formatters::ObjCBooleanSummaryProvider( 1052 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 1053 lldb::addr_t valobj_ptr_value = 1054 valobj.GetValueAsUnsigned(LLDB_INVALID_ADDRESS); 1055 if (valobj_ptr_value == LLDB_INVALID_ADDRESS) 1056 return false; 1057 1058 ProcessSP process_sp(valobj.GetProcessSP()); 1059 if (!process_sp) 1060 return false; 1061 1062 if (AppleObjCRuntime *objc_runtime = 1063 (AppleObjCRuntime *)process_sp->GetObjCLanguageRuntime()) { 1064 lldb::addr_t cf_true = LLDB_INVALID_ADDRESS, 1065 cf_false = LLDB_INVALID_ADDRESS; 1066 objc_runtime->GetValuesForGlobalCFBooleans(cf_true, cf_false); 1067 if (valobj_ptr_value == cf_true) { 1068 stream.PutCString("YES"); 1069 return true; 1070 } 1071 if (valobj_ptr_value == cf_false) { 1072 stream.PutCString("NO"); 1073 return true; 1074 } 1075 } 1076 1077 return false; 1078 } 1079 1080 template <bool is_sel_ptr> 1081 bool lldb_private::formatters::ObjCSELSummaryProvider( 1082 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 1083 lldb::ValueObjectSP valobj_sp; 1084 1085 CompilerType charstar(valobj.GetCompilerType() 1086 .GetBasicTypeFromAST(eBasicTypeChar) 1087 .GetPointerType()); 1088 1089 if (!charstar) 1090 return false; 1091 1092 ExecutionContext exe_ctx(valobj.GetExecutionContextRef()); 1093 1094 if (is_sel_ptr) { 1095 lldb::addr_t data_address = valobj.GetValueAsUnsigned(LLDB_INVALID_ADDRESS); 1096 if (data_address == LLDB_INVALID_ADDRESS) 1097 return false; 1098 valobj_sp = ValueObject::CreateValueObjectFromAddress("text", data_address, 1099 exe_ctx, charstar); 1100 } else { 1101 DataExtractor data; 1102 Status error; 1103 valobj.GetData(data, error); 1104 if (error.Fail()) 1105 return false; 1106 valobj_sp = 1107 ValueObject::CreateValueObjectFromData("text", data, exe_ctx, charstar); 1108 } 1109 1110 if (!valobj_sp) 1111 return false; 1112 1113 stream.Printf("%s", valobj_sp->GetSummaryAsCString()); 1114 return true; 1115 } 1116 1117 // POSIX has an epoch on Jan-1-1970, but Cocoa prefers Jan-1-2001 1118 // this call gives the POSIX equivalent of the Cocoa epoch 1119 time_t lldb_private::formatters::GetOSXEpoch() { 1120 static time_t epoch = 0; 1121 if (!epoch) { 1122 #ifndef _WIN32 1123 tzset(); 1124 tm tm_epoch; 1125 tm_epoch.tm_sec = 0; 1126 tm_epoch.tm_hour = 0; 1127 tm_epoch.tm_min = 0; 1128 tm_epoch.tm_mon = 0; 1129 tm_epoch.tm_mday = 1; 1130 tm_epoch.tm_year = 2001 - 1900; 1131 tm_epoch.tm_isdst = -1; 1132 tm_epoch.tm_gmtoff = 0; 1133 tm_epoch.tm_zone = nullptr; 1134 epoch = timegm(&tm_epoch); 1135 #endif 1136 } 1137 return epoch; 1138 } 1139 1140 template bool lldb_private::formatters::NSDataSummaryProvider<true>( 1141 ValueObject &, Stream &, const TypeSummaryOptions &); 1142 1143 template bool lldb_private::formatters::NSDataSummaryProvider<false>( 1144 ValueObject &, Stream &, const TypeSummaryOptions &); 1145 1146 template bool lldb_private::formatters::ObjCSELSummaryProvider<true>( 1147 ValueObject &, Stream &, const TypeSummaryOptions &); 1148 1149 template bool lldb_private::formatters::ObjCSELSummaryProvider<false>( 1150 ValueObject &, Stream &, const TypeSummaryOptions &); 1151