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 // C Includes 11 // C++ Includes 12 // Other libraries and framework includes 13 // Project includes 14 #include "Cocoa.h" 15 16 #include "lldb/Core/DataBufferHeap.h" 17 #include "lldb/Core/Error.h" 18 #include "lldb/Core/Mangled.h" 19 #include "lldb/Core/Stream.h" 20 #include "lldb/Core/ValueObject.h" 21 #include "lldb/Core/ValueObjectConstResult.h" 22 #include "lldb/DataFormatters/FormattersHelpers.h" 23 #include "lldb/DataFormatters/StringPrinter.h" 24 #include "lldb/DataFormatters/TypeSummary.h" 25 #include "lldb/Host/Endian.h" 26 #include "lldb/Symbol/ClangASTContext.h" 27 #include "lldb/Target/Language.h" 28 #include "lldb/Target/ObjCLanguageRuntime.h" 29 #include "lldb/Target/Target.h" 30 #include "lldb/Target/Process.h" 31 #include "lldb/Utility/ProcessStructReader.h" 32 33 #include "NSString.h" 34 35 using namespace lldb; 36 using namespace lldb_private; 37 using namespace lldb_private::formatters; 38 39 bool 40 lldb_private::formatters::NSBundleSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) 41 { 42 ProcessSP process_sp = valobj.GetProcessSP(); 43 if (!process_sp) 44 return false; 45 46 ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); 47 48 if (!runtime) 49 return false; 50 51 ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj)); 52 53 if (!descriptor || !descriptor->IsValid()) 54 return false; 55 56 uint32_t ptr_size = process_sp->GetAddressByteSize(); 57 58 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 59 60 if (!valobj_addr) 61 return false; 62 63 const char* class_name = descriptor->GetClassName().GetCString(); 64 65 if (!class_name || !*class_name) 66 return false; 67 68 if (!strcmp(class_name,"NSBundle")) 69 { 70 uint64_t offset = 5 * ptr_size; 71 ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset, valobj.GetCompilerType().GetBasicTypeFromAST(lldb::eBasicTypeObjCID), true)); 72 73 StreamString summary_stream; 74 bool was_nsstring_ok = NSStringSummaryProvider(*text, summary_stream, options); 75 if (was_nsstring_ok && summary_stream.GetSize() > 0) 76 { 77 stream.Printf("%s",summary_stream.GetData()); 78 return true; 79 } 80 } 81 // this is either an unknown subclass or an NSBundle that comes from [NSBundle mainBundle] 82 // which is encoded differently and needs to be handled by running code 83 return ExtractSummaryFromObjCExpression(valobj, "NSString*", "bundlePath", stream, options.GetLanguage()); 84 } 85 86 bool 87 lldb_private::formatters::NSTimeZoneSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) 88 { 89 ProcessSP process_sp = valobj.GetProcessSP(); 90 if (!process_sp) 91 return false; 92 93 ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); 94 95 if (!runtime) 96 return false; 97 98 ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj)); 99 100 if (!descriptor || !descriptor->IsValid()) 101 return false; 102 103 uint32_t ptr_size = process_sp->GetAddressByteSize(); 104 105 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 106 107 if (!valobj_addr) 108 return false; 109 110 const char* class_name = descriptor->GetClassName().GetCString(); 111 112 if (!class_name || !*class_name) 113 return false; 114 115 if (!strcmp(class_name,"__NSTimeZone")) 116 { 117 uint64_t offset = ptr_size; 118 ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset, valobj.GetCompilerType(), true)); 119 StreamString summary_stream; 120 bool was_nsstring_ok = NSStringSummaryProvider(*text, summary_stream, options); 121 if (was_nsstring_ok && summary_stream.GetSize() > 0) 122 { 123 stream.Printf("%s",summary_stream.GetData()); 124 return true; 125 } 126 } 127 return ExtractSummaryFromObjCExpression(valobj, "NSString*", "name", stream, options.GetLanguage()); 128 } 129 130 bool 131 lldb_private::formatters::NSNotificationSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) 132 { 133 ProcessSP process_sp = valobj.GetProcessSP(); 134 if (!process_sp) 135 return false; 136 137 ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); 138 139 if (!runtime) 140 return false; 141 142 ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj)); 143 144 if (!descriptor || !descriptor->IsValid()) 145 return false; 146 147 uint32_t ptr_size = process_sp->GetAddressByteSize(); 148 149 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 150 151 if (!valobj_addr) 152 return false; 153 154 const char* class_name = descriptor->GetClassName().GetCString(); 155 156 if (!class_name || !*class_name) 157 return false; 158 159 if (!strcmp(class_name,"NSConcreteNotification")) 160 { 161 uint64_t offset = ptr_size; 162 ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset, valobj.GetCompilerType(), true)); 163 StreamString summary_stream; 164 bool was_nsstring_ok = NSStringSummaryProvider(*text, summary_stream, options); 165 if (was_nsstring_ok && summary_stream.GetSize() > 0) 166 { 167 stream.Printf("%s",summary_stream.GetData()); 168 return true; 169 } 170 } 171 // this is either an unknown subclass or an NSBundle that comes from [NSBundle mainBundle] 172 // which is encoded differently and needs to be handled by running code 173 return ExtractSummaryFromObjCExpression(valobj, "NSString*", "name", stream, options.GetLanguage()); 174 } 175 176 bool 177 lldb_private::formatters::NSMachPortSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) 178 { 179 ProcessSP process_sp = valobj.GetProcessSP(); 180 if (!process_sp) 181 return false; 182 183 ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); 184 185 if (!runtime) 186 return false; 187 188 ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj)); 189 190 if (!descriptor || !descriptor->IsValid()) 191 return false; 192 193 uint32_t ptr_size = process_sp->GetAddressByteSize(); 194 195 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 196 197 if (!valobj_addr) 198 return false; 199 200 const char* class_name = descriptor->GetClassName().GetCString(); 201 202 if (!class_name || !*class_name) 203 return false; 204 205 uint64_t port_number = 0; 206 207 do 208 { 209 if (!strcmp(class_name,"NSMachPort")) 210 { 211 uint64_t offset = (ptr_size == 4 ? 12 : 20); 212 Error error; 213 port_number = process_sp->ReadUnsignedIntegerFromMemory(offset+valobj_addr, 4, 0, error); 214 if (error.Success()) 215 break; 216 } 217 if (!ExtractValueFromObjCExpression(valobj, "int", "machPort", port_number)) 218 return false; 219 } while (false); 220 221 stream.Printf("mach port: %u",(uint32_t)(port_number & 0x00000000FFFFFFFF)); 222 return true; 223 } 224 225 bool 226 lldb_private::formatters::NSIndexSetSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) 227 { 228 ProcessSP process_sp = valobj.GetProcessSP(); 229 if (!process_sp) 230 return false; 231 232 ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); 233 234 if (!runtime) 235 return false; 236 237 ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj)); 238 239 if (!descriptor || !descriptor->IsValid()) 240 return false; 241 242 uint32_t ptr_size = process_sp->GetAddressByteSize(); 243 244 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 245 246 if (!valobj_addr) 247 return false; 248 249 const char* class_name = descriptor->GetClassName().GetCString(); 250 251 if (!class_name || !*class_name) 252 return false; 253 254 uint64_t count = 0; 255 256 do { 257 if (!strcmp(class_name,"NSIndexSet") || !strcmp(class_name,"NSMutableIndexSet")) 258 { 259 Error error; 260 uint32_t mode = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+ptr_size, 4, 0, error); 261 if (error.Fail()) 262 return false; 263 // this means the set is empty - count = 0 264 if ((mode & 1) == 1) 265 { 266 count = 0; 267 break; 268 } 269 if ((mode & 2) == 2) 270 mode = 1; // this means the set only has one range 271 else 272 mode = 2; // this means the set has multiple ranges 273 if (mode == 1) 274 { 275 count = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+3*ptr_size, ptr_size, 0, error); 276 if (error.Fail()) 277 return false; 278 } 279 else 280 { 281 // read a pointer to the data at 2*ptr_size 282 count = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+2*ptr_size, ptr_size, 0, error); 283 if (error.Fail()) 284 return false; 285 // read the data at 2*ptr_size from the first location 286 count = process_sp->ReadUnsignedIntegerFromMemory(count+2*ptr_size, ptr_size, 0, error); 287 if (error.Fail()) 288 return false; 289 } 290 } 291 else 292 { 293 if (!ExtractValueFromObjCExpression(valobj, "unsigned long long int", "count", count)) 294 return false; 295 } 296 } while (false); 297 stream.Printf("%" PRIu64 " index%s", 298 count, 299 (count == 1 ? "" : "es")); 300 return true; 301 } 302 303 static void 304 NSNumber_FormatChar (ValueObject& valobj, 305 Stream& stream, 306 char value, 307 lldb::LanguageType lang) 308 { 309 static ConstString g_TypeHint("NSNumber:char"); 310 311 std::string prefix,suffix; 312 if (Language* language = Language::FindPlugin(lang)) 313 { 314 if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix, suffix)) 315 { 316 prefix.clear(); 317 suffix.clear(); 318 } 319 } 320 321 stream.Printf("%s%hhd%s", 322 prefix.c_str(), 323 value, 324 suffix.c_str()); 325 } 326 327 static void 328 NSNumber_FormatShort (ValueObject& valobj, 329 Stream& stream, 330 short value, 331 lldb::LanguageType lang) 332 { 333 static ConstString g_TypeHint("NSNumber:short"); 334 335 std::string prefix,suffix; 336 if (Language* language = Language::FindPlugin(lang)) 337 { 338 if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix, suffix)) 339 { 340 prefix.clear(); 341 suffix.clear(); 342 } 343 } 344 345 stream.Printf("%s%hd%s", 346 prefix.c_str(), 347 value, 348 suffix.c_str()); 349 } 350 351 static void 352 NSNumber_FormatInt (ValueObject& valobj, 353 Stream& stream, 354 int value, 355 lldb::LanguageType lang) 356 { 357 static ConstString g_TypeHint("NSNumber:int"); 358 359 std::string prefix,suffix; 360 if (Language* language = Language::FindPlugin(lang)) 361 { 362 if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix, suffix)) 363 { 364 prefix.clear(); 365 suffix.clear(); 366 } 367 } 368 369 stream.Printf("%s%d%s", 370 prefix.c_str(), 371 value, 372 suffix.c_str()); 373 } 374 375 static void 376 NSNumber_FormatLong (ValueObject& valobj, 377 Stream& stream, 378 uint64_t value, 379 lldb::LanguageType lang) 380 { 381 static ConstString g_TypeHint("NSNumber:long"); 382 383 std::string prefix,suffix; 384 if (Language* language = Language::FindPlugin(lang)) 385 { 386 if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix, suffix)) 387 { 388 prefix.clear(); 389 suffix.clear(); 390 } 391 } 392 393 stream.Printf("%s%" PRId64 "%s", 394 prefix.c_str(), 395 value, 396 suffix.c_str()); 397 } 398 399 static void 400 NSNumber_FormatFloat (ValueObject& valobj, 401 Stream& stream, 402 float value, 403 lldb::LanguageType lang) 404 { 405 static ConstString g_TypeHint("NSNumber:float"); 406 407 std::string prefix,suffix; 408 if (Language* language = Language::FindPlugin(lang)) 409 { 410 if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix, suffix)) 411 { 412 prefix.clear(); 413 suffix.clear(); 414 } 415 } 416 417 stream.Printf("%s%f%s", 418 prefix.c_str(), 419 value, 420 suffix.c_str()); 421 } 422 423 static void 424 NSNumber_FormatDouble (ValueObject& valobj, 425 Stream& stream, 426 double value, 427 lldb::LanguageType lang) 428 { 429 static ConstString g_TypeHint("NSNumber:double"); 430 431 std::string prefix,suffix; 432 if (Language* language = Language::FindPlugin(lang)) 433 { 434 if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix, suffix)) 435 { 436 prefix.clear(); 437 suffix.clear(); 438 } 439 } 440 441 stream.Printf("%s%g%s", 442 prefix.c_str(), 443 value, 444 suffix.c_str()); 445 } 446 447 bool 448 lldb_private::formatters::NSNumberSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) 449 { 450 ProcessSP process_sp = valobj.GetProcessSP(); 451 if (!process_sp) 452 return false; 453 454 ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); 455 456 if (!runtime) 457 return false; 458 459 ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj)); 460 461 if (!descriptor || !descriptor->IsValid()) 462 return false; 463 464 uint32_t ptr_size = process_sp->GetAddressByteSize(); 465 466 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 467 468 if (!valobj_addr) 469 return false; 470 471 const char* class_name = descriptor->GetClassName().GetCString(); 472 473 if (!class_name || !*class_name) 474 return false; 475 476 if (!strcmp(class_name,"NSNumber") || !strcmp(class_name,"__NSCFNumber")) 477 { 478 uint64_t value = 0; 479 uint64_t i_bits = 0; 480 if (descriptor->GetTaggedPointerInfo(&i_bits,&value)) 481 { 482 switch (i_bits) 483 { 484 case 0: 485 NSNumber_FormatChar(valobj, stream, (char)value, options.GetLanguage()); 486 break; 487 case 1: 488 case 4: 489 NSNumber_FormatShort(valobj, stream, (short)value, options.GetLanguage()); 490 break; 491 case 2: 492 case 8: 493 NSNumber_FormatInt(valobj, stream, (int)value, options.GetLanguage()); 494 break; 495 case 3: 496 case 12: 497 NSNumber_FormatLong(valobj, stream, value, options.GetLanguage()); 498 break; 499 default: 500 return false; 501 } 502 return true; 503 } 504 else 505 { 506 Error error; 507 uint8_t data_type = (process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, 1, 0, error) & 0x1F); 508 uint64_t data_location = valobj_addr + 2*ptr_size; 509 uint64_t value = 0; 510 if (error.Fail()) 511 return false; 512 switch (data_type) 513 { 514 case 1: // 0B00001 515 value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 1, 0, error); 516 if (error.Fail()) 517 return false; 518 NSNumber_FormatChar(valobj, stream, (char)value, options.GetLanguage()); 519 break; 520 case 2: // 0B0010 521 value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 2, 0, error); 522 if (error.Fail()) 523 return false; 524 NSNumber_FormatShort(valobj, stream, (short)value, options.GetLanguage()); 525 break; 526 case 3: // 0B0011 527 value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 4, 0, error); 528 if (error.Fail()) 529 return false; 530 NSNumber_FormatInt(valobj, stream, (int)value, options.GetLanguage()); 531 break; 532 case 17: // 0B10001 533 data_location += 8; 534 LLVM_FALLTHROUGH; 535 case 4: // 0B0100 536 value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 8, 0, error); 537 if (error.Fail()) 538 return false; 539 NSNumber_FormatLong(valobj, stream, value, options.GetLanguage()); 540 break; 541 case 5: // 0B0101 542 { 543 uint32_t flt_as_int = process_sp->ReadUnsignedIntegerFromMemory(data_location, 4, 0, error); 544 if (error.Fail()) 545 return false; 546 float flt_value = *((float*)&flt_as_int); 547 NSNumber_FormatFloat(valobj, stream, flt_value, options.GetLanguage()); 548 break; 549 } 550 case 6: // 0B0110 551 { 552 uint64_t dbl_as_lng = process_sp->ReadUnsignedIntegerFromMemory(data_location, 8, 0, error); 553 if (error.Fail()) 554 return false; 555 double dbl_value = *((double*)&dbl_as_lng); 556 NSNumber_FormatDouble(valobj, stream, dbl_value, options.GetLanguage()); 557 break; 558 } 559 default: 560 return false; 561 } 562 return true; 563 } 564 } 565 else 566 { 567 return ExtractSummaryFromObjCExpression(valobj, "NSString*", "stringValue", stream, options.GetLanguage()); 568 } 569 } 570 571 bool 572 lldb_private::formatters::NSURLSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) 573 { 574 ProcessSP process_sp = valobj.GetProcessSP(); 575 if (!process_sp) 576 return false; 577 578 ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); 579 580 if (!runtime) 581 return false; 582 583 ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj)); 584 585 if (!descriptor || !descriptor->IsValid()) 586 return false; 587 588 uint32_t ptr_size = process_sp->GetAddressByteSize(); 589 590 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 591 592 if (!valobj_addr) 593 return false; 594 595 const char* class_name = descriptor->GetClassName().GetCString(); 596 597 if (!class_name || !*class_name) 598 return false; 599 600 if (strcmp(class_name, "NSURL") == 0) 601 { 602 uint64_t offset_text = ptr_size + ptr_size + 8; // ISA + pointer + 8 bytes of data (even on 32bit) 603 uint64_t offset_base = offset_text + ptr_size; 604 CompilerType type(valobj.GetCompilerType()); 605 ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset_text, type, true)); 606 ValueObjectSP base(valobj.GetSyntheticChildAtOffset(offset_base, type, true)); 607 if (!text) 608 return false; 609 if (text->GetValueAsUnsigned(0) == 0) 610 return false; 611 StreamString summary; 612 if (!NSStringSummaryProvider(*text, summary, options)) 613 return false; 614 if (base && base->GetValueAsUnsigned(0)) 615 { 616 if (summary.GetSize() > 0) 617 summary.GetString().resize(summary.GetSize()-1); 618 summary.Printf(" -- "); 619 StreamString base_summary; 620 if (NSURLSummaryProvider(*base, base_summary, options) && base_summary.GetSize() > 0) 621 summary.Printf("%s",base_summary.GetSize() > 2 ? base_summary.GetData() + 2 : base_summary.GetData()); 622 } 623 if (summary.GetSize()) 624 { 625 stream.Printf("%s",summary.GetData()); 626 return true; 627 } 628 } 629 else 630 { 631 return ExtractSummaryFromObjCExpression(valobj, "NSString*", "description", stream, options.GetLanguage()); 632 } 633 return false; 634 } 635 636 bool 637 lldb_private::formatters::NSDateSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) 638 { 639 ProcessSP process_sp = valobj.GetProcessSP(); 640 if (!process_sp) 641 return false; 642 643 ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); 644 645 if (!runtime) 646 return false; 647 648 ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj)); 649 650 if (!descriptor || !descriptor->IsValid()) 651 return false; 652 653 uint32_t ptr_size = process_sp->GetAddressByteSize(); 654 655 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 656 657 if (!valobj_addr) 658 return false; 659 660 uint64_t date_value_bits = 0; 661 double date_value = 0.0; 662 663 const char* class_name = descriptor->GetClassName().GetCString(); 664 665 if (!class_name || !*class_name) 666 return false; 667 668 if (strcmp(class_name,"NSDate") == 0 || 669 strcmp(class_name,"__NSDate") == 0 || 670 strcmp(class_name,"__NSTaggedDate") == 0) 671 { 672 uint64_t info_bits=0,value_bits = 0; 673 if (descriptor->GetTaggedPointerInfo(&info_bits,&value_bits)) 674 { 675 date_value_bits = ((value_bits << 8) | (info_bits << 4)); 676 date_value = *((double*)&date_value_bits); 677 } 678 else 679 { 680 Error error; 681 date_value_bits = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+ptr_size, 8, 0, error); 682 date_value = *((double*)&date_value_bits); 683 if (error.Fail()) 684 return false; 685 } 686 } 687 else if (!strcmp(class_name,"NSCalendarDate")) 688 { 689 Error error; 690 date_value_bits = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+2*ptr_size, 8, 0, error); 691 date_value = *((double*)&date_value_bits); 692 if (error.Fail()) 693 return false; 694 } 695 else 696 { 697 if (!ExtractValueFromObjCExpression(valobj, "NSTimeInterval", "ExtractValueFromObjCExpression", date_value_bits)) 698 return false; 699 date_value = *((double*)&date_value_bits); 700 } 701 if (date_value == -63114076800) 702 { 703 stream.Printf("0001-12-30 00:00:00 +0000"); 704 return true; 705 } 706 // this snippet of code assumes that time_t == seconds since Jan-1-1970 707 // this is generally true and POSIXly happy, but might break if a library 708 // vendor decides to get creative 709 time_t epoch = GetOSXEpoch(); 710 epoch = epoch + (time_t)date_value; 711 tm *tm_date = gmtime(&epoch); 712 if (!tm_date) 713 return false; 714 std::string buffer(1024,0); 715 if (strftime (&buffer[0], 1023, "%Z", tm_date) == 0) 716 return false; 717 stream.Printf("%04d-%02d-%02d %02d:%02d:%02d %s", tm_date->tm_year+1900, tm_date->tm_mon+1, tm_date->tm_mday, tm_date->tm_hour, tm_date->tm_min, tm_date->tm_sec, buffer.c_str()); 718 return true; 719 } 720 721 bool 722 lldb_private::formatters::ObjCClassSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) 723 { 724 ProcessSP process_sp = valobj.GetProcessSP(); 725 if (!process_sp) 726 return false; 727 728 ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); 729 730 if (!runtime) 731 return false; 732 733 ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptorFromISA(valobj.GetValueAsUnsigned(0))); 734 735 if (!descriptor || !descriptor->IsValid()) 736 return false; 737 738 ConstString class_name = descriptor->GetClassName(); 739 740 if (class_name.IsEmpty()) 741 return false; 742 743 if (ConstString cs = Mangled(class_name).GetDemangledName(lldb::eLanguageTypeUnknown)) 744 class_name = cs; 745 746 stream.Printf("%s",class_name.AsCString("<unknown class>")); 747 return true; 748 } 749 750 class ObjCClassSyntheticChildrenFrontEnd : public SyntheticChildrenFrontEnd 751 { 752 public: 753 ObjCClassSyntheticChildrenFrontEnd (lldb::ValueObjectSP valobj_sp) : 754 SyntheticChildrenFrontEnd(*valobj_sp) 755 { 756 } 757 758 ~ObjCClassSyntheticChildrenFrontEnd() override = default; 759 760 size_t 761 CalculateNumChildren() override 762 { 763 return 0; 764 } 765 766 lldb::ValueObjectSP 767 GetChildAtIndex(size_t idx) override 768 { 769 return lldb::ValueObjectSP(); 770 } 771 772 bool 773 Update() override 774 { 775 return false; 776 } 777 778 bool 779 MightHaveChildren() override 780 { 781 return false; 782 } 783 784 size_t 785 GetIndexOfChildWithName(const ConstString &name) override 786 { 787 return UINT32_MAX; 788 } 789 }; 790 791 SyntheticChildrenFrontEnd* 792 lldb_private::formatters::ObjCClassSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP valobj_sp) 793 { 794 return new ObjCClassSyntheticChildrenFrontEnd(valobj_sp); 795 } 796 797 template<bool needs_at> 798 bool 799 lldb_private::formatters::NSDataSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) 800 { 801 ProcessSP process_sp = valobj.GetProcessSP(); 802 if (!process_sp) 803 return false; 804 805 ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); 806 807 if (!runtime) 808 return false; 809 810 ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj)); 811 812 if (!descriptor || !descriptor->IsValid()) 813 return false; 814 815 bool is_64bit = (process_sp->GetAddressByteSize() == 8); 816 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 817 818 if (!valobj_addr) 819 return false; 820 821 uint64_t value = 0; 822 823 const char* class_name = descriptor->GetClassName().GetCString(); 824 825 if (!class_name || !*class_name) 826 return false; 827 828 if (!strcmp(class_name,"NSConcreteData") || 829 !strcmp(class_name,"NSConcreteMutableData") || 830 !strcmp(class_name,"__NSCFData")) 831 { 832 uint32_t offset = (is_64bit ? 16 : 8); 833 Error error; 834 value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + offset, is_64bit ? 8 : 4, 0, error); 835 if (error.Fail()) 836 return false; 837 } 838 else 839 { 840 if (!ExtractValueFromObjCExpression(valobj, "int", "length", value)) 841 return false; 842 } 843 844 stream.Printf("%s%" PRIu64 " byte%s%s", 845 (needs_at ? "@\"" : ""), 846 value, 847 (value != 1 ? "s" : ""), 848 (needs_at ? "\"" : "")); 849 850 return true; 851 } 852 853 bool 854 lldb_private::formatters::ObjCBOOLSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) 855 { 856 const uint32_t type_info = valobj.GetCompilerType().GetTypeInfo(); 857 858 ValueObjectSP real_guy_sp = valobj.GetSP(); 859 860 if (type_info & eTypeIsPointer) 861 { 862 Error err; 863 real_guy_sp = valobj.Dereference(err); 864 if (err.Fail() || !real_guy_sp) 865 return false; 866 } 867 else if (type_info & eTypeIsReference) 868 { 869 real_guy_sp = valobj.GetChildAtIndex(0, true); 870 if (!real_guy_sp) 871 return false; 872 } 873 uint8_t value = (real_guy_sp->GetValueAsUnsigned(0) & 0xFF); 874 switch (value) 875 { 876 case 0: 877 stream.Printf("NO"); 878 break; 879 case 1: 880 stream.Printf("YES"); 881 break; 882 default: 883 stream.Printf("%u",value); 884 break; 885 } 886 return true; 887 } 888 889 template <bool is_sel_ptr> 890 bool 891 lldb_private::formatters::ObjCSELSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) 892 { 893 lldb::ValueObjectSP valobj_sp; 894 895 CompilerType charstar (valobj.GetCompilerType().GetBasicTypeFromAST(eBasicTypeChar).GetPointerType()); 896 897 if (!charstar) 898 return false; 899 900 ExecutionContext exe_ctx(valobj.GetExecutionContextRef()); 901 902 if (is_sel_ptr) 903 { 904 lldb::addr_t data_address = valobj.GetValueAsUnsigned(LLDB_INVALID_ADDRESS); 905 if (data_address == LLDB_INVALID_ADDRESS) 906 return false; 907 valobj_sp = ValueObject::CreateValueObjectFromAddress("text", data_address, exe_ctx, charstar); 908 } 909 else 910 { 911 DataExtractor data; 912 Error error; 913 valobj.GetData(data, error); 914 if (error.Fail()) 915 return false; 916 valobj_sp = ValueObject::CreateValueObjectFromData("text", data, exe_ctx, charstar); 917 } 918 919 if (!valobj_sp) 920 return false; 921 922 stream.Printf("%s",valobj_sp->GetSummaryAsCString()); 923 return true; 924 } 925 926 // POSIX has an epoch on Jan-1-1970, but Cocoa prefers Jan-1-2001 927 // this call gives the POSIX equivalent of the Cocoa epoch 928 time_t 929 lldb_private::formatters::GetOSXEpoch () 930 { 931 static time_t epoch = 0; 932 if (!epoch) 933 { 934 #ifndef _WIN32 935 tzset(); 936 tm tm_epoch; 937 tm_epoch.tm_sec = 0; 938 tm_epoch.tm_hour = 0; 939 tm_epoch.tm_min = 0; 940 tm_epoch.tm_mon = 0; 941 tm_epoch.tm_mday = 1; 942 tm_epoch.tm_year = 2001-1900; // for some reason, we need to subtract 1900 from this field. not sure why. 943 tm_epoch.tm_isdst = -1; 944 tm_epoch.tm_gmtoff = 0; 945 tm_epoch.tm_zone = nullptr; 946 epoch = timegm(&tm_epoch); 947 #endif 948 } 949 return epoch; 950 } 951 952 bool 953 lldb_private::formatters::RuntimeSpecificDescriptionSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) 954 { 955 if (const char* description = valobj.GetObjectDescription()) 956 { 957 stream.Printf("%s", description); 958 return true; 959 } 960 else 961 return false; 962 } 963 964 template bool 965 lldb_private::formatters::NSDataSummaryProvider<true> (ValueObject&, Stream&, const TypeSummaryOptions&); 966 967 template bool 968 lldb_private::formatters::NSDataSummaryProvider<false> (ValueObject&, Stream&, const TypeSummaryOptions&); 969 970 template bool 971 lldb_private::formatters::ObjCSELSummaryProvider<true> (ValueObject&, Stream&, const TypeSummaryOptions&); 972 973 template bool 974 lldb_private::formatters::ObjCSELSummaryProvider<false> (ValueObject&, Stream&, const TypeSummaryOptions&); 975