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.get() || !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.get(), 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.get() || !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.get(), 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.get() || !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.get(), 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.get() || !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.get() || !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.get() || !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 case 4: // 0B0100 535 value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 8, 0, error); 536 if (error.Fail()) 537 return false; 538 NSNumber_FormatLong(valobj, stream, value, options.GetLanguage()); 539 break; 540 case 5: // 0B0101 541 { 542 uint32_t flt_as_int = process_sp->ReadUnsignedIntegerFromMemory(data_location, 4, 0, error); 543 if (error.Fail()) 544 return false; 545 float flt_value = *((float*)&flt_as_int); 546 NSNumber_FormatFloat(valobj, stream, flt_value, options.GetLanguage()); 547 break; 548 } 549 case 6: // 0B0110 550 { 551 uint64_t dbl_as_lng = process_sp->ReadUnsignedIntegerFromMemory(data_location, 8, 0, error); 552 if (error.Fail()) 553 return false; 554 double dbl_value = *((double*)&dbl_as_lng); 555 NSNumber_FormatDouble(valobj, stream, dbl_value, options.GetLanguage()); 556 break; 557 } 558 default: 559 return false; 560 } 561 return true; 562 } 563 } 564 else 565 { 566 return ExtractSummaryFromObjCExpression(valobj, "NSString*", "stringValue", stream, options.GetLanguage()); 567 } 568 } 569 570 bool 571 lldb_private::formatters::NSURLSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) 572 { 573 ProcessSP process_sp = valobj.GetProcessSP(); 574 if (!process_sp) 575 return false; 576 577 ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); 578 579 if (!runtime) 580 return false; 581 582 ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj)); 583 584 if (!descriptor.get() || !descriptor->IsValid()) 585 return false; 586 587 uint32_t ptr_size = process_sp->GetAddressByteSize(); 588 589 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 590 591 if (!valobj_addr) 592 return false; 593 594 const char* class_name = descriptor->GetClassName().GetCString(); 595 596 if (!class_name || !*class_name) 597 return false; 598 599 if (strcmp(class_name, "NSURL") == 0) 600 { 601 uint64_t offset_text = ptr_size + ptr_size + 8; // ISA + pointer + 8 bytes of data (even on 32bit) 602 uint64_t offset_base = offset_text + ptr_size; 603 CompilerType type(valobj.GetCompilerType()); 604 ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset_text, type, true)); 605 ValueObjectSP base(valobj.GetSyntheticChildAtOffset(offset_base, type, true)); 606 if (!text) 607 return false; 608 if (text->GetValueAsUnsigned(0) == 0) 609 return false; 610 StreamString summary; 611 if (!NSStringSummaryProvider(*text, summary, options)) 612 return false; 613 if (base && base->GetValueAsUnsigned(0)) 614 { 615 if (summary.GetSize() > 0) 616 summary.GetString().resize(summary.GetSize()-1); 617 summary.Printf(" -- "); 618 StreamString base_summary; 619 if (NSURLSummaryProvider(*base, base_summary, options) && base_summary.GetSize() > 0) 620 summary.Printf("%s",base_summary.GetSize() > 2 ? base_summary.GetData() + 2 : base_summary.GetData()); 621 } 622 if (summary.GetSize()) 623 { 624 stream.Printf("%s",summary.GetData()); 625 return true; 626 } 627 } 628 else 629 { 630 return ExtractSummaryFromObjCExpression(valobj, "NSString*", "description", stream, options.GetLanguage()); 631 } 632 return false; 633 } 634 635 bool 636 lldb_private::formatters::NSDateSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) 637 { 638 ProcessSP process_sp = valobj.GetProcessSP(); 639 if (!process_sp) 640 return false; 641 642 ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); 643 644 if (!runtime) 645 return false; 646 647 ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj)); 648 649 if (!descriptor.get() || !descriptor->IsValid()) 650 return false; 651 652 uint32_t ptr_size = process_sp->GetAddressByteSize(); 653 654 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 655 656 if (!valobj_addr) 657 return false; 658 659 uint64_t date_value_bits = 0; 660 double date_value = 0.0; 661 662 const char* class_name = descriptor->GetClassName().GetCString(); 663 664 if (!class_name || !*class_name) 665 return false; 666 667 if (strcmp(class_name,"NSDate") == 0 || 668 strcmp(class_name,"__NSDate") == 0 || 669 strcmp(class_name,"__NSTaggedDate") == 0) 670 { 671 uint64_t info_bits=0,value_bits = 0; 672 if (descriptor->GetTaggedPointerInfo(&info_bits,&value_bits)) 673 { 674 date_value_bits = ((value_bits << 8) | (info_bits << 4)); 675 date_value = *((double*)&date_value_bits); 676 } 677 else 678 { 679 Error error; 680 date_value_bits = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+ptr_size, 8, 0, error); 681 date_value = *((double*)&date_value_bits); 682 if (error.Fail()) 683 return false; 684 } 685 } 686 else if (!strcmp(class_name,"NSCalendarDate")) 687 { 688 Error error; 689 date_value_bits = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+2*ptr_size, 8, 0, error); 690 date_value = *((double*)&date_value_bits); 691 if (error.Fail()) 692 return false; 693 } 694 else 695 { 696 if (ExtractValueFromObjCExpression(valobj, "NSTimeInterval", "ExtractValueFromObjCExpression", date_value_bits) == false) 697 return false; 698 date_value = *((double*)&date_value_bits); 699 } 700 if (date_value == -63114076800) 701 { 702 stream.Printf("0001-12-30 00:00:00 +0000"); 703 return true; 704 } 705 // this snippet of code assumes that time_t == seconds since Jan-1-1970 706 // this is generally true and POSIXly happy, but might break if a library 707 // vendor decides to get creative 708 time_t epoch = GetOSXEpoch(); 709 epoch = epoch + (time_t)date_value; 710 tm *tm_date = gmtime(&epoch); 711 if (!tm_date) 712 return false; 713 std::string buffer(1024,0); 714 if (strftime (&buffer[0], 1023, "%Z", tm_date) == 0) 715 return false; 716 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()); 717 return true; 718 } 719 720 bool 721 lldb_private::formatters::ObjCClassSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) 722 { 723 ProcessSP process_sp = valobj.GetProcessSP(); 724 if (!process_sp) 725 return false; 726 727 ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); 728 729 if (!runtime) 730 return false; 731 732 ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptorFromISA(valobj.GetValueAsUnsigned(0))); 733 734 if (!descriptor.get() || !descriptor->IsValid()) 735 return false; 736 737 ConstString class_name = descriptor->GetClassName(); 738 739 if (class_name.IsEmpty()) 740 return false; 741 742 if (ConstString cs = Mangled(class_name).GetDemangledName(lldb::eLanguageTypeUnknown)) 743 class_name = cs; 744 745 stream.Printf("%s",class_name.AsCString("<unknown class>")); 746 return true; 747 } 748 749 class ObjCClassSyntheticChildrenFrontEnd : public SyntheticChildrenFrontEnd 750 { 751 public: 752 ObjCClassSyntheticChildrenFrontEnd (lldb::ValueObjectSP valobj_sp) : 753 SyntheticChildrenFrontEnd(*valobj_sp.get()) 754 { 755 } 756 757 ~ObjCClassSyntheticChildrenFrontEnd() override = default; 758 759 size_t 760 CalculateNumChildren() override 761 { 762 return 0; 763 } 764 765 lldb::ValueObjectSP 766 GetChildAtIndex(size_t idx) override 767 { 768 return lldb::ValueObjectSP(); 769 } 770 771 bool 772 Update() override 773 { 774 return false; 775 } 776 777 bool 778 MightHaveChildren() override 779 { 780 return false; 781 } 782 783 size_t 784 GetIndexOfChildWithName(const ConstString &name) override 785 { 786 return UINT32_MAX; 787 } 788 }; 789 790 SyntheticChildrenFrontEnd* 791 lldb_private::formatters::ObjCClassSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP valobj_sp) 792 { 793 return new ObjCClassSyntheticChildrenFrontEnd(valobj_sp); 794 } 795 796 template<bool needs_at> 797 bool 798 lldb_private::formatters::NSDataSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) 799 { 800 ProcessSP process_sp = valobj.GetProcessSP(); 801 if (!process_sp) 802 return false; 803 804 ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); 805 806 if (!runtime) 807 return false; 808 809 ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj)); 810 811 if (!descriptor.get() || !descriptor->IsValid()) 812 return false; 813 814 bool is_64bit = (process_sp->GetAddressByteSize() == 8); 815 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 816 817 if (!valobj_addr) 818 return false; 819 820 uint64_t value = 0; 821 822 const char* class_name = descriptor->GetClassName().GetCString(); 823 824 if (!class_name || !*class_name) 825 return false; 826 827 if (!strcmp(class_name,"NSConcreteData") || 828 !strcmp(class_name,"NSConcreteMutableData") || 829 !strcmp(class_name,"__NSCFData")) 830 { 831 uint32_t offset = (is_64bit ? 16 : 8); 832 Error error; 833 value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + offset, is_64bit ? 8 : 4, 0, error); 834 if (error.Fail()) 835 return false; 836 } 837 else 838 { 839 if (!ExtractValueFromObjCExpression(valobj, "int", "length", value)) 840 return false; 841 } 842 843 stream.Printf("%s%" PRIu64 " byte%s%s", 844 (needs_at ? "@\"" : ""), 845 value, 846 (value != 1 ? "s" : ""), 847 (needs_at ? "\"" : "")); 848 849 return true; 850 } 851 852 bool 853 lldb_private::formatters::ObjCBOOLSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) 854 { 855 const uint32_t type_info = valobj.GetCompilerType().GetTypeInfo(); 856 857 ValueObjectSP real_guy_sp = valobj.GetSP(); 858 859 if (type_info & eTypeIsPointer) 860 { 861 Error err; 862 real_guy_sp = valobj.Dereference(err); 863 if (err.Fail() || !real_guy_sp) 864 return false; 865 } 866 else if (type_info & eTypeIsReference) 867 { 868 real_guy_sp = valobj.GetChildAtIndex(0, true); 869 if (!real_guy_sp) 870 return false; 871 } 872 uint64_t value = real_guy_sp->GetValueAsUnsigned(0); 873 if (value == 0) 874 { 875 stream.Printf("NO"); 876 return true; 877 } 878 stream.Printf("YES"); 879 return true; 880 } 881 882 template <bool is_sel_ptr> 883 bool 884 lldb_private::formatters::ObjCSELSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) 885 { 886 lldb::ValueObjectSP valobj_sp; 887 888 CompilerType charstar (valobj.GetCompilerType().GetBasicTypeFromAST(eBasicTypeChar).GetPointerType()); 889 890 if (!charstar) 891 return false; 892 893 ExecutionContext exe_ctx(valobj.GetExecutionContextRef()); 894 895 if (is_sel_ptr) 896 { 897 lldb::addr_t data_address = valobj.GetValueAsUnsigned(LLDB_INVALID_ADDRESS); 898 if (data_address == LLDB_INVALID_ADDRESS) 899 return false; 900 valobj_sp = ValueObject::CreateValueObjectFromAddress("text", data_address, exe_ctx, charstar); 901 } 902 else 903 { 904 DataExtractor data; 905 Error error; 906 valobj.GetData(data, error); 907 if (error.Fail()) 908 return false; 909 valobj_sp = ValueObject::CreateValueObjectFromData("text", data, exe_ctx, charstar); 910 } 911 912 if (!valobj_sp) 913 return false; 914 915 stream.Printf("%s",valobj_sp->GetSummaryAsCString()); 916 return true; 917 } 918 919 // POSIX has an epoch on Jan-1-1970, but Cocoa prefers Jan-1-2001 920 // this call gives the POSIX equivalent of the Cocoa epoch 921 time_t 922 lldb_private::formatters::GetOSXEpoch () 923 { 924 static time_t epoch = 0; 925 if (!epoch) 926 { 927 #ifndef _WIN32 928 tzset(); 929 tm tm_epoch; 930 tm_epoch.tm_sec = 0; 931 tm_epoch.tm_hour = 0; 932 tm_epoch.tm_min = 0; 933 tm_epoch.tm_mon = 0; 934 tm_epoch.tm_mday = 1; 935 tm_epoch.tm_year = 2001-1900; // for some reason, we need to subtract 1900 from this field. not sure why. 936 tm_epoch.tm_isdst = -1; 937 tm_epoch.tm_gmtoff = 0; 938 tm_epoch.tm_zone = NULL; 939 epoch = timegm(&tm_epoch); 940 #endif 941 } 942 return epoch; 943 } 944 945 bool 946 lldb_private::formatters::RuntimeSpecificDescriptionSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) 947 { 948 if (const char* description = valobj.GetObjectDescription()) 949 { 950 stream.Printf("%s", description); 951 return true; 952 } 953 else 954 return false; 955 } 956 957 template bool 958 lldb_private::formatters::NSDataSummaryProvider<true> (ValueObject&, Stream&, const TypeSummaryOptions&); 959 960 template bool 961 lldb_private::formatters::NSDataSummaryProvider<false> (ValueObject&, Stream&, const TypeSummaryOptions&); 962 963 template bool 964 lldb_private::formatters::ObjCSELSummaryProvider<true> (ValueObject&, Stream&, const TypeSummaryOptions&); 965 966 template bool 967 lldb_private::formatters::ObjCSELSummaryProvider<false> (ValueObject&, Stream&, const TypeSummaryOptions&); 968