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