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 int64_t exp7 = exp; 774 // Tagged exponent field is 7-bit signed. Sign-extend the value to 64 bits 775 // before performing arithmetic. 776 int64_t exp11 = ((exp7 << 57) >> 57) + TAGGED_DATE_EXPONENT_BIAS; 777 return exp11; 778 } 779 780 static uint64_t decodeTaggedTimeInterval(uint64_t encodedTimeInterval) { 781 if (encodedTimeInterval == 0) 782 return 0.0; 783 if (encodedTimeInterval == std::numeric_limits<uint64_t>::max()) 784 return (uint64_t)-0.0; 785 786 TaggedDoubleBits encodedBits = {}; 787 encodedBits.i = encodedTimeInterval; 788 DoubleBits decodedBits; 789 790 // Sign and fraction are represented exactly. 791 // Exponent is encoded. 792 assert(encodedBits.unused == 0); 793 decodedBits.sign = encodedBits.sign; 794 decodedBits.fraction = encodedBits.fraction; 795 decodedBits.exponent = decodeExponent(encodedBits.exponent); 796 797 return decodedBits.d; 798 } 799 800 bool lldb_private::formatters::NSDateSummaryProvider( 801 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 802 ProcessSP process_sp = valobj.GetProcessSP(); 803 if (!process_sp) 804 return false; 805 806 ObjCLanguageRuntime *runtime = 807 (ObjCLanguageRuntime *)process_sp->GetLanguageRuntime( 808 lldb::eLanguageTypeObjC); 809 810 if (!runtime) 811 return false; 812 813 ObjCLanguageRuntime::ClassDescriptorSP descriptor( 814 runtime->GetClassDescriptor(valobj)); 815 816 if (!descriptor || !descriptor->IsValid()) 817 return false; 818 819 uint32_t ptr_size = process_sp->GetAddressByteSize(); 820 821 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 822 823 if (!valobj_addr) 824 return false; 825 826 uint64_t date_value_bits = 0; 827 double date_value = 0.0; 828 829 ConstString class_name = descriptor->GetClassName(); 830 831 static const ConstString g_NSDate("NSDate"); 832 static const ConstString g___NSDate("__NSDate"); 833 static const ConstString g___NSTaggedDate("__NSTaggedDate"); 834 static const ConstString g_NSCalendarDate("NSCalendarDate"); 835 836 if (class_name.IsEmpty()) 837 return false; 838 839 uint64_t info_bits = 0, value_bits = 0; 840 if ((class_name == g_NSDate) || (class_name == g___NSDate) || 841 (class_name == g___NSTaggedDate)) { 842 if (descriptor->GetTaggedPointerInfo(&info_bits, &value_bits)) { 843 date_value_bits = ((value_bits << 8) | (info_bits << 4)); 844 memcpy(&date_value, &date_value_bits, sizeof(date_value_bits)); 845 } else { 846 llvm::Triple triple( 847 process_sp->GetTarget().GetArchitecture().GetTriple()); 848 uint32_t delta = 849 (triple.isWatchOS() && triple.isWatchABI()) ? 8 : ptr_size; 850 Status error; 851 date_value_bits = process_sp->ReadUnsignedIntegerFromMemory( 852 valobj_addr + delta, 8, 0, error); 853 memcpy(&date_value, &date_value_bits, sizeof(date_value_bits)); 854 if (error.Fail()) 855 return false; 856 } 857 } else if (class_name == g_NSCalendarDate) { 858 Status error; 859 date_value_bits = process_sp->ReadUnsignedIntegerFromMemory( 860 valobj_addr + 2 * ptr_size, 8, 0, error); 861 memcpy(&date_value, &date_value_bits, sizeof(date_value_bits)); 862 if (error.Fail()) 863 return false; 864 } else 865 return false; 866 867 if (date_value == -63114076800) { 868 stream.Printf("0001-12-30 00:00:00 +0000"); 869 return true; 870 } 871 872 // Accomodate for the __NSTaggedDate format introduced in Foundation 1600. 873 if (class_name == g___NSTaggedDate) { 874 auto *runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>(process_sp->GetObjCLanguageRuntime()); 875 if (runtime && runtime->GetFoundationVersion() >= 1600) 876 date_value = decodeTaggedTimeInterval(value_bits << 4); 877 } 878 879 // this snippet of code assumes that time_t == seconds since Jan-1-1970 this 880 // is generally true and POSIXly happy, but might break if a library vendor 881 // decides to get creative 882 time_t epoch = GetOSXEpoch(); 883 epoch = epoch + (time_t)date_value; 884 tm *tm_date = gmtime(&epoch); 885 if (!tm_date) 886 return false; 887 std::string buffer(1024, 0); 888 if (strftime(&buffer[0], 1023, "%Z", tm_date) == 0) 889 return false; 890 stream.Printf("%04d-%02d-%02d %02d:%02d:%02d %s", tm_date->tm_year + 1900, 891 tm_date->tm_mon + 1, tm_date->tm_mday, tm_date->tm_hour, 892 tm_date->tm_min, tm_date->tm_sec, buffer.c_str()); 893 return true; 894 } 895 896 bool lldb_private::formatters::ObjCClassSummaryProvider( 897 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 898 ProcessSP process_sp = valobj.GetProcessSP(); 899 if (!process_sp) 900 return false; 901 902 ObjCLanguageRuntime *runtime = 903 (ObjCLanguageRuntime *)process_sp->GetLanguageRuntime( 904 lldb::eLanguageTypeObjC); 905 906 if (!runtime) 907 return false; 908 909 ObjCLanguageRuntime::ClassDescriptorSP descriptor( 910 runtime->GetClassDescriptorFromISA(valobj.GetValueAsUnsigned(0))); 911 912 if (!descriptor || !descriptor->IsValid()) 913 return false; 914 915 ConstString class_name = descriptor->GetClassName(); 916 917 if (class_name.IsEmpty()) 918 return false; 919 920 if (ConstString cs = 921 Mangled(class_name).GetDemangledName(lldb::eLanguageTypeUnknown)) 922 class_name = cs; 923 924 stream.Printf("%s", class_name.AsCString("<unknown class>")); 925 return true; 926 } 927 928 class ObjCClassSyntheticChildrenFrontEnd : public SyntheticChildrenFrontEnd { 929 public: 930 ObjCClassSyntheticChildrenFrontEnd(lldb::ValueObjectSP valobj_sp) 931 : SyntheticChildrenFrontEnd(*valobj_sp) {} 932 933 ~ObjCClassSyntheticChildrenFrontEnd() override = default; 934 935 size_t CalculateNumChildren() override { return 0; } 936 937 lldb::ValueObjectSP GetChildAtIndex(size_t idx) override { 938 return lldb::ValueObjectSP(); 939 } 940 941 bool Update() override { return false; } 942 943 bool MightHaveChildren() override { return false; } 944 945 size_t GetIndexOfChildWithName(const ConstString &name) override { 946 return UINT32_MAX; 947 } 948 }; 949 950 SyntheticChildrenFrontEnd * 951 lldb_private::formatters::ObjCClassSyntheticFrontEndCreator( 952 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { 953 return new ObjCClassSyntheticChildrenFrontEnd(valobj_sp); 954 } 955 956 template <bool needs_at> 957 bool lldb_private::formatters::NSDataSummaryProvider( 958 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 959 ProcessSP process_sp = valobj.GetProcessSP(); 960 if (!process_sp) 961 return false; 962 963 ObjCLanguageRuntime *runtime = 964 (ObjCLanguageRuntime *)process_sp->GetLanguageRuntime( 965 lldb::eLanguageTypeObjC); 966 967 if (!runtime) 968 return false; 969 970 ObjCLanguageRuntime::ClassDescriptorSP descriptor( 971 runtime->GetClassDescriptor(valobj)); 972 973 if (!descriptor || !descriptor->IsValid()) 974 return false; 975 976 bool is_64bit = (process_sp->GetAddressByteSize() == 8); 977 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 978 979 if (!valobj_addr) 980 return false; 981 982 uint64_t value = 0; 983 984 llvm::StringRef class_name = descriptor->GetClassName().GetCString(); 985 986 if (class_name.empty()) 987 return false; 988 989 bool isNSConcreteData = class_name == "NSConcreteData"; 990 bool isNSConcreteMutableData = class_name == "NSConcreteMutableData"; 991 bool isNSCFData = class_name == "__NSCFData"; 992 if (isNSConcreteData || isNSConcreteMutableData || isNSCFData) { 993 uint32_t offset; 994 if (isNSConcreteData) 995 offset = is_64bit ? 8 : 4; 996 else 997 offset = is_64bit ? 16 : 8; 998 999 Status error; 1000 value = process_sp->ReadUnsignedIntegerFromMemory( 1001 valobj_addr + offset, is_64bit ? 8 : 4, 0, error); 1002 if (error.Fail()) 1003 return false; 1004 } else if (class_name == "_NSInlineData") { 1005 uint32_t offset = (is_64bit ? 8 : 4); 1006 Status error; 1007 value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + offset, 2, 1008 0, error); 1009 if (error.Fail()) 1010 return false; 1011 } else if (class_name == "_NSZeroData") { 1012 value = 0; 1013 } else 1014 return false; 1015 1016 stream.Printf("%s%" PRIu64 " byte%s%s", (needs_at ? "@\"" : ""), value, 1017 (value != 1 ? "s" : ""), (needs_at ? "\"" : "")); 1018 1019 return true; 1020 } 1021 1022 bool lldb_private::formatters::ObjCBOOLSummaryProvider( 1023 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 1024 const uint32_t type_info = valobj.GetCompilerType().GetTypeInfo(); 1025 1026 ValueObjectSP real_guy_sp = valobj.GetSP(); 1027 1028 if (type_info & eTypeIsPointer) { 1029 Status err; 1030 real_guy_sp = valobj.Dereference(err); 1031 if (err.Fail() || !real_guy_sp) 1032 return false; 1033 } else if (type_info & eTypeIsReference) { 1034 real_guy_sp = valobj.GetChildAtIndex(0, true); 1035 if (!real_guy_sp) 1036 return false; 1037 } 1038 uint8_t value = (real_guy_sp->GetValueAsUnsigned(0) & 0xFF); 1039 switch (value) { 1040 case 0: 1041 stream.Printf("NO"); 1042 break; 1043 case 1: 1044 stream.Printf("YES"); 1045 break; 1046 default: 1047 stream.Printf("%u", value); 1048 break; 1049 } 1050 return true; 1051 } 1052 1053 bool lldb_private::formatters::ObjCBooleanSummaryProvider( 1054 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 1055 lldb::addr_t valobj_ptr_value = 1056 valobj.GetValueAsUnsigned(LLDB_INVALID_ADDRESS); 1057 if (valobj_ptr_value == LLDB_INVALID_ADDRESS) 1058 return false; 1059 1060 ProcessSP process_sp(valobj.GetProcessSP()); 1061 if (!process_sp) 1062 return false; 1063 1064 if (AppleObjCRuntime *objc_runtime = 1065 (AppleObjCRuntime *)process_sp->GetObjCLanguageRuntime()) { 1066 lldb::addr_t cf_true = LLDB_INVALID_ADDRESS, 1067 cf_false = LLDB_INVALID_ADDRESS; 1068 objc_runtime->GetValuesForGlobalCFBooleans(cf_true, cf_false); 1069 if (valobj_ptr_value == cf_true) { 1070 stream.PutCString("YES"); 1071 return true; 1072 } 1073 if (valobj_ptr_value == cf_false) { 1074 stream.PutCString("NO"); 1075 return true; 1076 } 1077 } 1078 1079 return false; 1080 } 1081 1082 template <bool is_sel_ptr> 1083 bool lldb_private::formatters::ObjCSELSummaryProvider( 1084 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 1085 lldb::ValueObjectSP valobj_sp; 1086 1087 CompilerType charstar(valobj.GetCompilerType() 1088 .GetBasicTypeFromAST(eBasicTypeChar) 1089 .GetPointerType()); 1090 1091 if (!charstar) 1092 return false; 1093 1094 ExecutionContext exe_ctx(valobj.GetExecutionContextRef()); 1095 1096 if (is_sel_ptr) { 1097 lldb::addr_t data_address = valobj.GetValueAsUnsigned(LLDB_INVALID_ADDRESS); 1098 if (data_address == LLDB_INVALID_ADDRESS) 1099 return false; 1100 valobj_sp = ValueObject::CreateValueObjectFromAddress("text", data_address, 1101 exe_ctx, charstar); 1102 } else { 1103 DataExtractor data; 1104 Status error; 1105 valobj.GetData(data, error); 1106 if (error.Fail()) 1107 return false; 1108 valobj_sp = 1109 ValueObject::CreateValueObjectFromData("text", data, exe_ctx, charstar); 1110 } 1111 1112 if (!valobj_sp) 1113 return false; 1114 1115 stream.Printf("%s", valobj_sp->GetSummaryAsCString()); 1116 return true; 1117 } 1118 1119 // POSIX has an epoch on Jan-1-1970, but Cocoa prefers Jan-1-2001 1120 // this call gives the POSIX equivalent of the Cocoa epoch 1121 time_t lldb_private::formatters::GetOSXEpoch() { 1122 static time_t epoch = 0; 1123 if (!epoch) { 1124 #ifndef _WIN32 1125 tzset(); 1126 tm tm_epoch; 1127 tm_epoch.tm_sec = 0; 1128 tm_epoch.tm_hour = 0; 1129 tm_epoch.tm_min = 0; 1130 tm_epoch.tm_mon = 0; 1131 tm_epoch.tm_mday = 1; 1132 tm_epoch.tm_year = 2001 - 1900; 1133 tm_epoch.tm_isdst = -1; 1134 tm_epoch.tm_gmtoff = 0; 1135 tm_epoch.tm_zone = nullptr; 1136 epoch = timegm(&tm_epoch); 1137 #endif 1138 } 1139 return epoch; 1140 } 1141 1142 template bool lldb_private::formatters::NSDataSummaryProvider<true>( 1143 ValueObject &, Stream &, const TypeSummaryOptions &); 1144 1145 template bool lldb_private::formatters::NSDataSummaryProvider<false>( 1146 ValueObject &, Stream &, const TypeSummaryOptions &); 1147 1148 template bool lldb_private::formatters::ObjCSELSummaryProvider<true>( 1149 ValueObject &, Stream &, const TypeSummaryOptions &); 1150 1151 template bool lldb_private::formatters::ObjCSELSummaryProvider<false>( 1152 ValueObject &, Stream &, const TypeSummaryOptions &); 1153