Home | History | Annotate | Download | only in DataFormatters
      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 "lldb/lldb-python.h"
     11 
     12 #include "lldb/DataFormatters/CXXFormatterFunctions.h"
     13 
     14 #include "lldb/Core/DataBufferHeap.h"
     15 #include "lldb/Core/Error.h"
     16 #include "lldb/Core/Stream.h"
     17 #include "lldb/Core/ValueObject.h"
     18 #include "lldb/Core/ValueObjectConstResult.h"
     19 #include "lldb/Host/Endian.h"
     20 #include "lldb/Symbol/ClangASTContext.h"
     21 #include "lldb/Target/ObjCLanguageRuntime.h"
     22 #include "lldb/Target/Target.h"
     23 
     24 using namespace lldb;
     25 using namespace lldb_private;
     26 using namespace lldb_private::formatters;
     27 
     28 bool
     29 lldb_private::formatters::NSBundleSummaryProvider (ValueObject& valobj, Stream& stream)
     30 {
     31     ProcessSP process_sp = valobj.GetProcessSP();
     32     if (!process_sp)
     33         return false;
     34 
     35     ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
     36 
     37     if (!runtime)
     38         return false;
     39 
     40     ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
     41 
     42     if (!descriptor.get() || !descriptor->IsValid())
     43         return false;
     44 
     45     uint32_t ptr_size = process_sp->GetAddressByteSize();
     46 
     47     lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
     48 
     49     if (!valobj_addr)
     50         return false;
     51 
     52     const char* class_name = descriptor->GetClassName().GetCString();
     53 
     54     if (!class_name || !*class_name)
     55         return false;
     56 
     57     if (!strcmp(class_name,"NSBundle"))
     58     {
     59         uint64_t offset = 5 * ptr_size;
     60         ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset, valobj.GetClangType().GetBasicTypeFromAST(lldb::eBasicTypeObjCID), true));
     61 
     62         StreamString summary_stream;
     63         bool was_nsstring_ok = NSStringSummaryProvider(*text.get(), summary_stream);
     64         if (was_nsstring_ok && summary_stream.GetSize() > 0)
     65         {
     66             stream.Printf("%s",summary_stream.GetData());
     67             return true;
     68         }
     69     }
     70     // this is either an unknown subclass or an NSBundle that comes from [NSBundle mainBundle]
     71     // which is encoded differently and needs to be handled by running code
     72     return ExtractSummaryFromObjCExpression(valobj, "NSString*", "bundlePath", stream);
     73 }
     74 
     75 bool
     76 lldb_private::formatters::NSTimeZoneSummaryProvider (ValueObject& valobj, Stream& stream)
     77 {
     78     ProcessSP process_sp = valobj.GetProcessSP();
     79     if (!process_sp)
     80         return false;
     81 
     82     ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
     83 
     84     if (!runtime)
     85         return false;
     86 
     87     ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
     88 
     89     if (!descriptor.get() || !descriptor->IsValid())
     90         return false;
     91 
     92     uint32_t ptr_size = process_sp->GetAddressByteSize();
     93 
     94     lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
     95 
     96     if (!valobj_addr)
     97         return false;
     98 
     99     const char* class_name = descriptor->GetClassName().GetCString();
    100 
    101     if (!class_name || !*class_name)
    102         return false;
    103 
    104     if (!strcmp(class_name,"__NSTimeZone"))
    105     {
    106         uint64_t offset = ptr_size;
    107         ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset, valobj.GetClangType(), true));
    108         StreamString summary_stream;
    109         bool was_nsstring_ok = NSStringSummaryProvider(*text.get(), summary_stream);
    110         if (was_nsstring_ok && summary_stream.GetSize() > 0)
    111         {
    112             stream.Printf("%s",summary_stream.GetData());
    113             return true;
    114         }
    115     }
    116     return ExtractSummaryFromObjCExpression(valobj, "NSString*", "name", stream);
    117 }
    118 
    119 bool
    120 lldb_private::formatters::NSNotificationSummaryProvider (ValueObject& valobj, Stream& stream)
    121 {
    122     ProcessSP process_sp = valobj.GetProcessSP();
    123     if (!process_sp)
    124         return false;
    125 
    126     ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
    127 
    128     if (!runtime)
    129         return false;
    130 
    131     ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
    132 
    133     if (!descriptor.get() || !descriptor->IsValid())
    134         return false;
    135 
    136     uint32_t ptr_size = process_sp->GetAddressByteSize();
    137 
    138     lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
    139 
    140     if (!valobj_addr)
    141         return false;
    142 
    143     const char* class_name = descriptor->GetClassName().GetCString();
    144 
    145     if (!class_name || !*class_name)
    146         return false;
    147 
    148     if (!strcmp(class_name,"NSConcreteNotification"))
    149     {
    150         uint64_t offset = ptr_size;
    151         ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset, valobj.GetClangType(), true));
    152         StreamString summary_stream;
    153         bool was_nsstring_ok = NSStringSummaryProvider(*text.get(), summary_stream);
    154         if (was_nsstring_ok && summary_stream.GetSize() > 0)
    155         {
    156             stream.Printf("%s",summary_stream.GetData());
    157             return true;
    158         }
    159     }
    160     // this is either an unknown subclass or an NSBundle that comes from [NSBundle mainBundle]
    161     // which is encoded differently and needs to be handled by running code
    162     return ExtractSummaryFromObjCExpression(valobj, "NSString*", "name", stream);
    163 }
    164 
    165 bool
    166 lldb_private::formatters::NSMachPortSummaryProvider (ValueObject& valobj, Stream& stream)
    167 {
    168     ProcessSP process_sp = valobj.GetProcessSP();
    169     if (!process_sp)
    170         return false;
    171 
    172     ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
    173 
    174     if (!runtime)
    175         return false;
    176 
    177     ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
    178 
    179     if (!descriptor.get() || !descriptor->IsValid())
    180         return false;
    181 
    182     uint32_t ptr_size = process_sp->GetAddressByteSize();
    183 
    184     lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
    185 
    186     if (!valobj_addr)
    187         return false;
    188 
    189     const char* class_name = descriptor->GetClassName().GetCString();
    190 
    191     if (!class_name || !*class_name)
    192         return false;
    193 
    194     uint64_t port_number = 0;
    195 
    196     do
    197     {
    198         if (!strcmp(class_name,"NSMachPort"))
    199         {
    200             uint64_t offset = (ptr_size == 4 ? 12 : 20);
    201             Error error;
    202             port_number = process_sp->ReadUnsignedIntegerFromMemory(offset+valobj_addr, 4, 0, error);
    203             if (error.Success())
    204                 break;
    205         }
    206         if (!ExtractValueFromObjCExpression(valobj, "int", "machPort", port_number))
    207             return false;
    208     } while (false);
    209 
    210     stream.Printf("mach port: %u",(uint32_t)(port_number & 0x00000000FFFFFFFF));
    211     return true;
    212 }
    213 
    214 bool
    215 lldb_private::formatters::NSIndexSetSummaryProvider (ValueObject& valobj, Stream& stream)
    216 {
    217     ProcessSP process_sp = valobj.GetProcessSP();
    218     if (!process_sp)
    219         return false;
    220 
    221     ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
    222 
    223     if (!runtime)
    224         return false;
    225 
    226     ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
    227 
    228     if (!descriptor.get() || !descriptor->IsValid())
    229         return false;
    230 
    231     uint32_t ptr_size = process_sp->GetAddressByteSize();
    232 
    233     lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
    234 
    235     if (!valobj_addr)
    236         return false;
    237 
    238     const char* class_name = descriptor->GetClassName().GetCString();
    239 
    240     if (!class_name || !*class_name)
    241         return false;
    242 
    243     uint64_t count = 0;
    244 
    245     do {
    246         if (!strcmp(class_name,"NSIndexSet") || !strcmp(class_name,"NSMutableIndexSet"))
    247         {
    248             Error error;
    249             uint32_t mode = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+ptr_size, 4, 0, error);
    250             if (error.Fail())
    251                 return false;
    252             // this means the set is empty - count = 0
    253             if ((mode & 1) == 1)
    254             {
    255                 count = 0;
    256                 break;
    257             }
    258             if ((mode & 2) == 2)
    259                 mode = 1; // this means the set only has one range
    260             else
    261                 mode = 2; // this means the set has multiple ranges
    262             if (mode == 1)
    263             {
    264                 count = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+3*ptr_size, ptr_size, 0, error);
    265                 if (error.Fail())
    266                     return false;
    267             }
    268             else
    269             {
    270                 // read a pointer to the data at 2*ptr_size
    271                 count = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+2*ptr_size, ptr_size, 0, error);
    272                 if (error.Fail())
    273                     return false;
    274                 // read the data at 2*ptr_size from the first location
    275                 count = process_sp->ReadUnsignedIntegerFromMemory(count+2*ptr_size, ptr_size, 0, error);
    276                 if (error.Fail())
    277                     return false;
    278             }
    279         }
    280         else
    281         {
    282             if (!ExtractValueFromObjCExpression(valobj, "unsigned long long int", "count", count))
    283                 return false;
    284         }
    285     }  while (false);
    286     stream.Printf("%" PRIu64 " index%s",
    287                   count,
    288                   (count == 1 ? "" : "es"));
    289     return true;
    290 }
    291 
    292 bool
    293 lldb_private::formatters::NSNumberSummaryProvider (ValueObject& valobj, Stream& stream)
    294 {
    295     ProcessSP process_sp = valobj.GetProcessSP();
    296     if (!process_sp)
    297         return false;
    298 
    299     ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
    300 
    301     if (!runtime)
    302         return false;
    303 
    304     ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
    305 
    306     if (!descriptor.get() || !descriptor->IsValid())
    307         return false;
    308 
    309     uint32_t ptr_size = process_sp->GetAddressByteSize();
    310 
    311     lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
    312 
    313     if (!valobj_addr)
    314         return false;
    315 
    316     const char* class_name = descriptor->GetClassName().GetCString();
    317 
    318     if (!class_name || !*class_name)
    319         return false;
    320 
    321     if (!strcmp(class_name,"NSNumber") || !strcmp(class_name,"__NSCFNumber"))
    322     {
    323         uint64_t value = 0;
    324         uint64_t i_bits = 0;
    325         if (descriptor->GetTaggedPointerInfo(&i_bits,&value))
    326         {
    327             switch (i_bits)
    328             {
    329                 case 0:
    330                     stream.Printf("(char)%hhd",(char)value);
    331                     break;
    332                 case 1:
    333                 case 4:
    334                     stream.Printf("(short)%hd",(short)value);
    335                     break;
    336                 case 2:
    337                 case 8:
    338                     stream.Printf("(int)%d",(int)value);
    339                     break;
    340                 case 3:
    341                 case 12:
    342                     stream.Printf("(long)%" PRId64,value);
    343                     break;
    344                 default:
    345                     stream.Printf("unexpected value:(info=%" PRIu64 ", value=%" PRIu64,i_bits,value);
    346                     break;
    347             }
    348             return true;
    349         }
    350         else
    351         {
    352             Error error;
    353             uint8_t data_type = (process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, 1, 0, error) & 0x1F);
    354             uint64_t data_location = valobj_addr + 2*ptr_size;
    355             uint64_t value = 0;
    356             if (error.Fail())
    357                 return false;
    358             switch (data_type)
    359             {
    360                 case 1: // 0B00001
    361                     value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 1, 0, error);
    362                     if (error.Fail())
    363                         return false;
    364                     stream.Printf("(char)%hhd",(char)value);
    365                     break;
    366                 case 2: // 0B0010
    367                     value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 2, 0, error);
    368                     if (error.Fail())
    369                         return false;
    370                     stream.Printf("(short)%hd",(short)value);
    371                     break;
    372                 case 3: // 0B0011
    373                     value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 4, 0, error);
    374                     if (error.Fail())
    375                         return false;
    376                     stream.Printf("(int)%d",(int)value);
    377                     break;
    378                 case 17: // 0B10001
    379                     data_location += 8;
    380                 case 4: // 0B0100
    381                     value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 8, 0, error);
    382                     if (error.Fail())
    383                         return false;
    384                     stream.Printf("(long)%" PRId64,value);
    385                     break;
    386                 case 5: // 0B0101
    387                 {
    388                     uint32_t flt_as_int = process_sp->ReadUnsignedIntegerFromMemory(data_location, 4, 0, error);
    389                     if (error.Fail())
    390                         return false;
    391                     float flt_value = *((float*)&flt_as_int);
    392                     stream.Printf("(float)%f",flt_value);
    393                     break;
    394                 }
    395                 case 6: // 0B0110
    396                 {
    397                     uint64_t dbl_as_lng = process_sp->ReadUnsignedIntegerFromMemory(data_location, 8, 0, error);
    398                     if (error.Fail())
    399                         return false;
    400                     double dbl_value = *((double*)&dbl_as_lng);
    401                     stream.Printf("(double)%g",dbl_value);
    402                     break;
    403                 }
    404                 default:
    405                     stream.Printf("unexpected value: dt=%d",data_type);
    406                     break;
    407             }
    408             return true;
    409         }
    410     }
    411     else
    412     {
    413         return ExtractSummaryFromObjCExpression(valobj, "NSString*", "stringValue", stream);
    414     }
    415 }
    416 
    417 bool
    418 lldb_private::formatters::NSURLSummaryProvider (ValueObject& valobj, Stream& stream)
    419 {
    420     ProcessSP process_sp = valobj.GetProcessSP();
    421     if (!process_sp)
    422         return false;
    423 
    424     ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
    425 
    426     if (!runtime)
    427         return false;
    428 
    429     ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
    430 
    431     if (!descriptor.get() || !descriptor->IsValid())
    432         return false;
    433 
    434     uint32_t ptr_size = process_sp->GetAddressByteSize();
    435 
    436     lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
    437 
    438     if (!valobj_addr)
    439         return false;
    440 
    441     const char* class_name = descriptor->GetClassName().GetCString();
    442 
    443     if (!class_name || !*class_name)
    444         return false;
    445 
    446     if (strcmp(class_name, "NSURL") == 0)
    447     {
    448         uint64_t offset_text = ptr_size + ptr_size + 8; // ISA + pointer + 8 bytes of data (even on 32bit)
    449         uint64_t offset_base = offset_text + ptr_size;
    450         ClangASTType type(valobj.GetClangType());
    451         ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset_text, type, true));
    452         ValueObjectSP base(valobj.GetSyntheticChildAtOffset(offset_base, type, true));
    453         if (!text)
    454             return false;
    455         if (text->GetValueAsUnsigned(0) == 0)
    456             return false;
    457         StreamString summary;
    458         if (!NSStringSummaryProvider(*text, summary))
    459             return false;
    460         if (base && base->GetValueAsUnsigned(0))
    461         {
    462             if (summary.GetSize() > 0)
    463                 summary.GetString().resize(summary.GetSize()-1);
    464             summary.Printf(" -- ");
    465             StreamString base_summary;
    466             if (NSURLSummaryProvider(*base, base_summary) && base_summary.GetSize() > 0)
    467                 summary.Printf("%s",base_summary.GetSize() > 2 ? base_summary.GetData() + 2 : base_summary.GetData());
    468         }
    469         if (summary.GetSize())
    470         {
    471             stream.Printf("%s",summary.GetData());
    472             return true;
    473         }
    474     }
    475     else
    476     {
    477         return ExtractSummaryFromObjCExpression(valobj, "NSString*", "description", stream);
    478     }
    479     return false;
    480 }
    481 
    482 bool
    483 lldb_private::formatters::NSDateSummaryProvider (ValueObject& valobj, Stream& stream)
    484 {
    485     ProcessSP process_sp = valobj.GetProcessSP();
    486     if (!process_sp)
    487         return false;
    488 
    489     ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
    490 
    491     if (!runtime)
    492         return false;
    493 
    494     ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
    495 
    496     if (!descriptor.get() || !descriptor->IsValid())
    497         return false;
    498 
    499     uint32_t ptr_size = process_sp->GetAddressByteSize();
    500 
    501     lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
    502 
    503     if (!valobj_addr)
    504         return false;
    505 
    506     uint64_t date_value_bits = 0;
    507     double date_value = 0.0;
    508 
    509     const char* class_name = descriptor->GetClassName().GetCString();
    510 
    511     if (!class_name || !*class_name)
    512         return false;
    513 
    514     if (strcmp(class_name,"NSDate") == 0 ||
    515         strcmp(class_name,"__NSDate") == 0 ||
    516         strcmp(class_name,"__NSTaggedDate") == 0)
    517     {
    518         uint64_t info_bits=0,value_bits = 0;
    519         if (descriptor->GetTaggedPointerInfo(&info_bits,&value_bits))
    520         {
    521             date_value_bits = ((value_bits << 8) | (info_bits << 4));
    522             date_value = *((double*)&date_value_bits);
    523         }
    524         else
    525         {
    526             Error error;
    527             date_value_bits = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+ptr_size, 8, 0, error);
    528             date_value = *((double*)&date_value_bits);
    529             if (error.Fail())
    530                 return false;
    531         }
    532     }
    533     else if (!strcmp(class_name,"NSCalendarDate"))
    534     {
    535         Error error;
    536         date_value_bits = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+2*ptr_size, 8, 0, error);
    537         date_value = *((double*)&date_value_bits);
    538         if (error.Fail())
    539             return false;
    540     }
    541     else
    542     {
    543         if (ExtractValueFromObjCExpression(valobj, "NSTimeInterval", "ExtractValueFromObjCExpression", date_value_bits) == false)
    544             return false;
    545         date_value = *((double*)&date_value_bits);
    546     }
    547     if (date_value == -63114076800)
    548     {
    549         stream.Printf("0001-12-30 00:00:00 +0000");
    550         return true;
    551     }
    552     // this snippet of code assumes that time_t == seconds since Jan-1-1970
    553     // this is generally true and POSIXly happy, but might break if a library
    554     // vendor decides to get creative
    555     time_t epoch = GetOSXEpoch();
    556     epoch = epoch + (time_t)date_value;
    557     tm *tm_date = localtime(&epoch);
    558     if (!tm_date)
    559         return false;
    560     std::string buffer(1024,0);
    561     if (strftime (&buffer[0], 1023, "%Z", tm_date) == 0)
    562         return false;
    563     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());
    564     return true;
    565 }
    566