diff options
30 files changed, 869 insertions, 454 deletions
diff --git a/lldb/examples/summaries/cocoa/CFArray.py b/lldb/examples/summaries/cocoa/CFArray.py index 648737f7def..4545a1b2370 100644 --- a/lldb/examples/summaries/cocoa/CFArray.py +++ b/lldb/examples/summaries/cocoa/CFArray.py @@ -16,7 +16,7 @@ statistics.add_metric('code_notrun') class NSArrayKVC_SynthProvider: def adjust_for_architecture(self): - self.lp64 = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) + self.is_64_bit = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) self.is_little = (self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle) self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize() @@ -51,7 +51,7 @@ class NSArrayKVC_SynthProvider: class NSArrayCF_SynthProvider: def adjust_for_architecture(self): - self.lp64 = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) + self.is_64_bit = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) self.is_little = (self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle) self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize() self.cfruntime_size = self.size_of_cfruntime_base() @@ -61,7 +61,7 @@ class NSArrayCF_SynthProvider: # to get its size we add up sizeof(pointer)+4 # and then add 4 more bytes if we are on a 64bit system def size_of_cfruntime_base(self): - if self.lp64 == True: + if self.is_64_bit == True: return 8+4+4; else: return 4+4; @@ -93,7 +93,7 @@ class NSArrayCF_SynthProvider: class NSArrayI_SynthProvider: def adjust_for_architecture(self): - self.lp64 = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) + self.is_64_bit = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) self.is_little = (self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle) self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize() @@ -133,7 +133,7 @@ class NSArrayI_SynthProvider: class NSArrayM_SynthProvider: def adjust_for_architecture(self): - self.lp64 = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) + self.is_64_bit = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) self.is_little = (self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle) self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize() @@ -223,7 +223,7 @@ class NSArrayM_SynthProvider: class NSArray_SynthProvider: def adjust_for_architecture(self): - self.lp64 = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) + self.is_64_bit = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) self.is_little = (self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle) self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize() self.id_type = self.valobj.GetType().GetBasicType(lldb.eBasicTypeObjCID) @@ -314,7 +314,7 @@ def CFArray_SummaryProvider (valobj,dict): summary = None if summary == None: summary = 'no valid array here' - return 'size='+summary + return summary + " objects" return '' def __lldb_init_module(debugger,dict): diff --git a/lldb/examples/summaries/cocoa/CFBag.py b/lldb/examples/summaries/cocoa/CFBag.py index 596114bd5df..1403a122eb6 100644 --- a/lldb/examples/summaries/cocoa/CFBag.py +++ b/lldb/examples/summaries/cocoa/CFBag.py @@ -15,7 +15,7 @@ statistics.add_metric('code_notrun') # obey the interface specification for synthetic children providers class CFBagRef_SummaryProvider: def adjust_for_architecture(self): - self.lp64 = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) + self.is_64_bit = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) self.is_little = (self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle) self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize() @@ -26,7 +26,7 @@ class CFBagRef_SummaryProvider: def update(self): self.adjust_for_architecture(); self.id_type = self.valobj.GetType().GetBasicType(lldb.eBasicTypeObjCID) - if self.lp64: + if self.is_64_bit: self.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong) else: self.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt) @@ -35,7 +35,7 @@ class CFBagRef_SummaryProvider: # 20 bytes on x64 # most probably 2 pointers and 4 bytes of data def offset(self): - if self.lp64: + if self.is_64_bit: return 20 else: return 12 @@ -49,7 +49,7 @@ class CFBagRef_SummaryProvider: class CFBagUnknown_SummaryProvider: def adjust_for_architecture(self): - self.lp64 = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) + self.is_64_bit = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) self.is_little = (self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle) self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize() @@ -120,7 +120,7 @@ def CFBag_SummaryProvider (valobj,dict): if summary == None: summary = 'no valid set here' else: - if provider.lp64: + if provider.is_64_bit: summary = summary & ~0x1fff000000000000 if summary == 1: return '1 item' diff --git a/lldb/examples/summaries/cocoa/CFBinaryHeap.py b/lldb/examples/summaries/cocoa/CFBinaryHeap.py index d0542abfcc7..0a120745c3c 100644 --- a/lldb/examples/summaries/cocoa/CFBinaryHeap.py +++ b/lldb/examples/summaries/cocoa/CFBinaryHeap.py @@ -15,7 +15,7 @@ statistics.add_metric('code_notrun') # obey the interface specification for synthetic children providers class CFBinaryHeapRef_SummaryProvider: def adjust_for_architecture(self): - self.lp64 = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) + self.is_64_bit = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) self.is_little = (self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle) self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize() @@ -26,7 +26,7 @@ class CFBinaryHeapRef_SummaryProvider: def update(self): self.adjust_for_architecture(); self.id_type = self.valobj.GetType().GetBasicType(lldb.eBasicTypeObjCID) - if self.lp64: + if self.is_64_bit: self.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong) else: self.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt) @@ -35,7 +35,7 @@ class CFBinaryHeapRef_SummaryProvider: # 16 bytes on x64 # most probably 2 pointers def offset(self): - if self.lp64: + if self.is_64_bit: return 16 else: return 8 @@ -49,7 +49,7 @@ class CFBinaryHeapRef_SummaryProvider: class CFBinaryHeapUnknown_SummaryProvider: def adjust_for_architecture(self): - self.lp64 = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) + self.is_64_bit = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) self.is_little = (self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle) self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize() @@ -117,7 +117,7 @@ def CFBinaryHeap_SummaryProvider (valobj,dict): if summary == None: summary = 'no valid set here' else: - if provider.lp64: + if provider.is_64_bit: summary = summary & ~0x1fff000000000000 if summary == 1: return '1 item' diff --git a/lldb/examples/summaries/cocoa/CFDictionary.py b/lldb/examples/summaries/cocoa/CFDictionary.py index 944e05aca59..5bd9e690b36 100644 --- a/lldb/examples/summaries/cocoa/CFDictionary.py +++ b/lldb/examples/summaries/cocoa/CFDictionary.py @@ -15,7 +15,7 @@ statistics.add_metric('code_notrun') # obey the interface specification for synthetic children providers class NSCFDictionary_SummaryProvider: def adjust_for_architecture(self): - self.lp64 = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) + self.is_64_bit = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) self.is_little = (self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle) self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize() @@ -26,7 +26,7 @@ class NSCFDictionary_SummaryProvider: def update(self): self.adjust_for_architecture(); self.id_type = self.valobj.GetType().GetBasicType(lldb.eBasicTypeObjCID) - if self.lp64: + if self.is_64_bit: self.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong) else: self.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt) @@ -36,7 +36,7 @@ class NSCFDictionary_SummaryProvider: # the description of __CFDictionary is not readily available so most # of this is guesswork, plain and simple def offset(self): - if self.lp64: + if self.is_64_bit: return 20 else: return 12 @@ -50,7 +50,7 @@ class NSCFDictionary_SummaryProvider: class NSDictionaryI_SummaryProvider: def adjust_for_architecture(self): - self.lp64 = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) + self.is_64_bit = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) self.is_little = (self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle) self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize() @@ -61,14 +61,14 @@ class NSDictionaryI_SummaryProvider: def update(self): self.adjust_for_architecture(); self.id_type = self.valobj.GetType().GetBasicType(lldb.eBasicTypeObjCID) - if self.lp64: + if self.is_64_bit: self.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong) else: self.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt) # we just need to skip the ISA and the count immediately follows def offset(self): - if self.lp64: + if self.is_64_bit: return 8 else: return 4 @@ -79,19 +79,18 @@ class NSDictionaryI_SummaryProvider: self.NSUInteger) value = num_children_vo.GetValueAsUnsigned(0) if value != None: - # the MSB on immutable dictionaries seems to be taken by the LSB of capacity - # not sure if it is a bug or some weird sort of feature, but masking it out - # gets the count right (unless, of course, someone's dictionaries grow - # too large - but I have not tested this) - if self.lp64: - value = value & ~0xFF00000000000000 + # the MS6bits on immutable dictionaries seem to be taken by the LSB of capacity + # not sure if it is a bug or some weird sort of feature, but masking that out + # gets the count right + if self.is_64_bit: + value = value & ~0xFC00000000000000 else: - value = value & ~0xFF000000 + value = value & ~0xFC000000 return value class NSDictionaryM_SummaryProvider: def adjust_for_architecture(self): - self.lp64 = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) + self.is_64_bit = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) self.is_little = (self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle) self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize() @@ -102,14 +101,14 @@ class NSDictionaryM_SummaryProvider: def update(self): self.adjust_for_architecture(); self.id_type = self.valobj.GetType().GetBasicType(lldb.eBasicTypeObjCID) - if self.lp64: + if self.is_64_bit: self.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong) else: self.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt) # we just need to skip the ISA and the count immediately follows def offset(self): - if self.lp64: + if self.is_64_bit: return 8 else: return 4 @@ -118,12 +117,21 @@ class NSDictionaryM_SummaryProvider: num_children_vo = self.valobj.CreateChildAtOffset("count", self.offset(), self.NSUInteger) - return num_children_vo.GetValueAsUnsigned(0) + value = num_children_vo.GetValueAsUnsigned(0) + if value != None: + # the MS6bits on mutable dictionaries seem to be taken by flags for + # KVO and probably other features. however, masking it out does get + # the count right + if self.is_64_bit: + value = value & ~0xFC00000000000000 + else: + value = value & ~0xFC000000 + return value class NSDictionaryUnknown_SummaryProvider: def adjust_for_architecture(self): - self.lp64 = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) + self.is_64_bit = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) self.is_little = (self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle) self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize() @@ -198,7 +206,7 @@ def CFDictionary_SummaryProvider2 (valobj,dict): if summary == None: summary = 'no valid dictionary here' # needed on OSX Mountain Lion - elif provider.lp64: + elif provider.is_64_bit: summary = int(summary) & ~0x0f1f000000000000 return str(summary) + " key/value pairs" return '' diff --git a/lldb/examples/summaries/cocoa/CFString.py b/lldb/examples/summaries/cocoa/CFString.py index 478f972e529..13d825ee715 100644 --- a/lldb/examples/summaries/cocoa/CFString.py +++ b/lldb/examples/summaries/cocoa/CFString.py @@ -76,7 +76,7 @@ class CFStringSynthProvider: # handle the special case strings # only use the custom code for the tested LP64 case def handle_special(self): - if self.lp64 == False: + if self.is_64_bit == False: # for 32bit targets, use safe ObjC code return self.handle_unicode_string_safe() offset = 12 @@ -127,7 +127,7 @@ class CFStringSynthProvider: "(char*)\"" + pystr.encode('utf-8') + "\"") def handle_inline_explicit(self): - if self.lp64: + if self.is_64_bit: offset = 24 else: offset = 12 @@ -136,7 +136,7 @@ class CFStringSynthProvider: "(char*)(" + str(offset) + ")") def handle_mutable_string(self): - if self.lp64: + if self.is_64_bit: offset = 16 else: offset = 8 @@ -223,7 +223,7 @@ class CFStringSynthProvider: # to get its size we add up sizeof(pointer)+4 # and then add 4 more bytes if we are on a 64bit system def size_of_cfruntime_base(self): - if self.lp64 == True: + if self.is_64_bit == True: return 8+4+4; else: return 4+4; @@ -234,7 +234,7 @@ class CFStringSynthProvider: # on big-endian this means going to byte 3, if we are on # little endian (OSX & iOS), this means reading byte 0 def offset_of_info_bits(self): - if self.lp64 == True: + if self.is_64_bit == True: offset = 8; else: offset = 4; @@ -282,7 +282,7 @@ class CFStringSynthProvider: # preparing ourselves to read into memory # by adjusting architecture-specific info def adjust_for_architecture(self): - self.lp64 = self.is_64bit(); + self.is_64_bit = self.is_64bit(); self.is_little = self.is_little_endian(); # reading info bits out of the CFString and computing diff --git a/lldb/examples/summaries/cocoa/NSBundle.py b/lldb/examples/summaries/cocoa/NSBundle.py index 5e6b11a6fc0..cb748f5217f 100644 --- a/lldb/examples/summaries/cocoa/NSBundle.py +++ b/lldb/examples/summaries/cocoa/NSBundle.py @@ -16,7 +16,7 @@ statistics.add_metric('code_notrun') # obey the interface specification for synthetic children providers class NSBundleKnown_SummaryProvider: def adjust_for_architecture(self): - self.lp64 = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) + self.is_64_bit = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) self.is_little = (self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle) self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize() @@ -27,7 +27,7 @@ class NSBundleKnown_SummaryProvider: def update(self): self.adjust_for_architecture(); self.id_type = self.valobj.GetType().GetBasicType(lldb.eBasicTypeObjCID) - if self.lp64: + if self.is_64_bit: self.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong) self.pointer_size = 8 else: @@ -58,7 +58,7 @@ class NSBundleKnown_SummaryProvider: class NSBundleUnknown_SummaryProvider: def adjust_for_architecture(self): - self.lp64 = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) + self.is_64_bit = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) self.is_little = (self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle) self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize() diff --git a/lldb/examples/summaries/cocoa/NSData.py b/lldb/examples/summaries/cocoa/NSData.py index 362bc81cd8d..9acb2fefb32 100644 --- a/lldb/examples/summaries/cocoa/NSData.py +++ b/lldb/examples/summaries/cocoa/NSData.py @@ -15,7 +15,7 @@ statistics.add_metric('code_notrun') # obey the interface specification for synthetic children providers class NSConcreteData_SummaryProvider: def adjust_for_architecture(self): - self.lp64 = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) + self.is_64_bit = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) self.is_little = (self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle) self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize() @@ -26,7 +26,7 @@ class NSConcreteData_SummaryProvider: def update(self): self.adjust_for_architecture(); self.id_type = self.valobj.GetType().GetBasicType(lldb.eBasicTypeObjCID) - if self.lp64: + if self.is_64_bit: self.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong) else: self.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt) @@ -37,7 +37,7 @@ class NSConcreteData_SummaryProvider: # machine word long, which means we actually have two pointers # worth of data to skip def offset(self): - if self.lp64: + if self.is_64_bit: return 16 else: return 8 @@ -51,7 +51,7 @@ class NSConcreteData_SummaryProvider: class NSDataUnknown_SummaryProvider: def adjust_for_architecture(self): - self.lp64 = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) + self.is_64_bit = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) self.is_little = (self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle) self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize() diff --git a/lldb/examples/summaries/cocoa/NSDate.py b/lldb/examples/summaries/cocoa/NSDate.py new file mode 100644 index 00000000000..aa059e2e79c --- /dev/null +++ b/lldb/examples/summaries/cocoa/NSDate.py @@ -0,0 +1,171 @@ +# summary provider for NSDate +import lldb +import ctypes +import objc_runtime +import metrics +import struct +import time +import datetime + +statistics = metrics.Metrics() +statistics.add_metric('invalid_isa') +statistics.add_metric('invalid_pointer') +statistics.add_metric('unknown_class') +statistics.add_metric('code_notrun') + +# Python promises to start counting time at midnight on Jan 1st on the epoch year +# hence, all we need to know is the epoch year +python_epoch = time.gmtime(0).tm_year + +osx_epoch = datetime.date(2001,1,1).timetuple() + +def mkgmtime(t): + return time.mktime(t)-time.timezone + +osx_epoch = mkgmtime(osx_epoch) + +def osx_to_python_time(osx): + if python_epoch <= 2011: + return osx + osx_epoch + else: + return osx - osx_epoch + + +# despite the similary to synthetic children providers, these classes are not +# trying to provide anything but the port number of an NSDate, so they need not +# obey the interface specification for synthetic children providers +class NSTaggedDate_SummaryProvider: + def adjust_for_architecture(self): + self.is_64_bit = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) + self.is_little = (self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle) + self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize() + + def __init__(self, valobj, info_bits, data): + self.valobj = valobj; + self.update(); + self.info_bits = info_bits + self.data = data + + def update(self): + self.adjust_for_architecture(); + self.id_type = self.valobj.GetType().GetBasicType(lldb.eBasicTypeObjCID) + + self.char = self.valobj.GetType().GetBasicType(lldb.eBasicTypeChar) + self.short = self.valobj.GetType().GetBasicType(lldb.eBasicTypeShort) + self.ushort = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedShort) + self.int = self.valobj.GetType().GetBasicType(lldb.eBasicTypeInt) + self.long = self.valobj.GetType().GetBasicType(lldb.eBasicTypeLong) + self.ulong = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong) + self.longlong = self.valobj.GetType().GetBasicType(lldb.eBasicTypeLongLong) + self.ulonglong = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLongLong) + self.float = self.valobj.GetType().GetBasicType(lldb.eBasicTypeFloat) + self.double = self.valobj.GetType().GetBasicType(lldb.eBasicTypeDouble) + + def value(self): + # the value of the date-time object is wrapped into the pointer value + # unfortunately, it is made as a time-delta after Jan 1 2011 midnight GMT + # while all Python knows about is the "epoch", which is a platform-dependent + # year (1970 of *nix) whose Jan 1 at midnight is taken as reference + return time.ctime(osx_to_python_time(self.data)) + + +class NSUntaggedDate_SummaryProvider: + def adjust_for_architecture(self): + self.is_64_bit = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) + self.is_little = (self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle) + self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize() + + def __init__(self, valobj): + self.valobj = valobj; + self.update() + + def update(self): + self.adjust_for_architecture(); + self.id_type = self.valobj.GetType().GetBasicType(lldb.eBasicTypeObjCID) + self.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong) + self.double = self.valobj.GetType().GetBasicType(lldb.eBasicTypeDouble) + + def offset(self): + if self.is_64_bit: + return 8 + else: + return 4 + + + def value(self): + value = self.valobj.CreateChildAtOffset("value", + self.offset(), + self.double) + value_double = struct.unpack('d', struct.pack('Q', value.GetValueAsUnsigned(0)))[0] + return time.ctime(osx_to_python_time(value_double)) + +class NSUnknownDate_SummaryProvider: + def adjust_for_architecture(self): + self.is_64_bit = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) + self.is_little = (self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle) + self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize() + + def __init__(self, valobj): + self.valobj = valobj; + self.update() + + def update(self): + self.adjust_for_architecture(); + self.id_type = self.valobj.GetType().GetBasicType(lldb.eBasicTypeObjCID) + + def value(self): + stream = lldb.SBStream() + self.valobj.GetExpressionPath(stream) + expr = "(NSString*)[" + stream.GetData() + " description]" + num_children_vo = self.valobj.CreateValueFromExpression("str",expr); + return num_children_vo.GetSummary() + +def GetSummary_Impl(valobj): + global statistics + class_data = objc_runtime.ObjCRuntime(valobj) + if class_data.is_valid() == False: + statistics.metric_hit('invalid_pointer',valobj) + wrapper = None + return + class_data = class_data.read_class_data() + if class_data.is_valid() == False: + statistics.metric_hit('invalid_isa',valobj) + wrapper = None + return + if class_data.is_kvo(): + class_data = class_data.get_superclass() + if class_data.is_valid() == False: + statistics.metric_hit('invalid_isa',valobj) + wrapper = None + return + + name_string = class_data.class_name() + if name_string == 'NSDate' or name_string == '__NSDate' or name_string == '__NSTaggedDate': + if class_data.is_tagged(): + wrapper = NSTaggedDate_SummaryProvider(valobj,class_data.info_bits(),class_data.value()) + statistics.metric_hit('code_notrun',valobj) + else: + wrapper = NSUntaggedDate_SummaryProvider(valobj) + statistics.metric_hit('code_notrun',valobj) + else: + wrapper = NSUnknownDate_SummaryProvider(valobj) + statistics.metric_hit('unknown_class',str(valobj) + " seen as " + name_string) + return wrapper; + + +def NSDate_SummaryProvider (valobj,dict): + provider = GetSummary_Impl(valobj); + if provider != None: + #try: + summary = provider.value(); + #except: + # summary = None + if summary == None: + summary = 'no valid number here' + return str(summary) + return '' + + +def __lldb_init_module(debugger,dict): + debugger.HandleCommand("type summary add -F NSDate.NSDate_SummaryProvider NSDate") + diff --git a/lldb/examples/summaries/cocoa/NSException.py b/lldb/examples/summaries/cocoa/NSException.py index e5f0418eed9..ac85fe8c143 100644 --- a/lldb/examples/summaries/cocoa/NSException.py +++ b/lldb/examples/summaries/cocoa/NSException.py @@ -12,7 +12,7 @@ statistics.add_metric('code_notrun') class NSKnownException_SummaryProvider: def adjust_for_architecture(self): - self.lp64 = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) + self.is_64_bit = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) self.is_little = (self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle) self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize() @@ -27,7 +27,7 @@ class NSKnownException_SummaryProvider: # skip the ISA and go to the name pointer def offset(self): - if self.lp64: + if self.is_64_bit: return 8 else: return 4 @@ -44,7 +44,7 @@ class NSKnownException_SummaryProvider: class NSUnknownException_SummaryProvider: def adjust_for_architecture(self): - self.lp64 = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) + self.is_64_bit = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) self.is_little = (self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle) self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize() diff --git a/lldb/examples/summaries/cocoa/NSMachPort.py b/lldb/examples/summaries/cocoa/NSMachPort.py index ca714bc38ae..eceb2d9ccfe 100644 --- a/lldb/examples/summaries/cocoa/NSMachPort.py +++ b/lldb/examples/summaries/cocoa/NSMachPort.py @@ -15,7 +15,7 @@ statistics.add_metric('code_notrun') # obey the interface specification for synthetic children providers class NSMachPortKnown_SummaryProvider: def adjust_for_architecture(self): - self.lp64 = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) + self.is_64_bit = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) self.is_little = (self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle) self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize() @@ -32,7 +32,7 @@ class NSMachPortKnown_SummaryProvider: # then we have one other internal pointer, plus # 4 bytes worth of flags. hence, these values def offset(self): - if self.lp64: + if self.is_64_bit: return 20 else: return 12 @@ -46,7 +46,7 @@ class NSMachPortKnown_SummaryProvider: class NSMachPortUnknown_SummaryProvider: def adjust_for_architecture(self): - self.lp64 = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) + self.is_64_bit = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) self.is_little = (self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle) self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize() diff --git a/lldb/examples/summaries/cocoa/NSNotification.py b/lldb/examples/summaries/cocoa/NSNotification.py index 6170110354c..0f8cccd322c 100644 --- a/lldb/examples/summaries/cocoa/NSNotification.py +++ b/lldb/examples/summaries/cocoa/NSNotification.py @@ -12,7 +12,7 @@ statistics.add_metric('code_notrun') class NSConcreteNotification_SummaryProvider: def adjust_for_architecture(self): - self.lp64 = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) + self.is_64_bit = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) self.is_little = (self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle) self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize() @@ -27,7 +27,7 @@ class NSConcreteNotification_SummaryProvider: # skip the ISA and go to the name pointer def offset(self): - if self.lp64: + if self.is_64_bit: return 8 else: return 4 @@ -41,7 +41,7 @@ class NSConcreteNotification_SummaryProvider: class NSNotificationUnknown_SummaryProvider: def adjust_for_architecture(self): - self.lp64 = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) + self.is_64_bit = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) self.is_little = (self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle) self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize() diff --git a/lldb/examples/summaries/cocoa/NSNumber.py b/lldb/examples/summaries/cocoa/NSNumber.py index 81f3d28dbc1..dfe0917b11d 100644 --- a/lldb/examples/summaries/cocoa/NSNumber.py +++ b/lldb/examples/summaries/cocoa/NSNumber.py @@ -16,7 +16,7 @@ statistics.add_metric('code_notrun') # obey the interface specification for synthetic children providers class NSTaggedNumber_SummaryProvider: def adjust_for_architecture(self): - self.lp64 = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) + self.is_64_bit = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) self.is_little = (self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle) self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize() @@ -60,7 +60,7 @@ class NSTaggedNumber_SummaryProvider: class NSUntaggedNumber_SummaryProvider: def adjust_for_architecture(self): - self.lp64 = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) + self.is_64_bit = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) self.is_little = (self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle) self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize() @@ -114,7 +114,7 @@ class NSUntaggedNumber_SummaryProvider: self.int) statistics.metric_hit('code_notrun',self.valobj) return '(int)' + str(data_vo.GetValueAsUnsigned(0) % (256*256*256*256)) - # apparently, on lp64 architectures, these are the only values that will ever + # apparently, on is_64_bit architectures, these are the only values that will ever # be represented by a non tagged pointers elif data_type == 0B10001 or data_type == 0B0100: data_offset = data_offset + self.pointer_size @@ -146,7 +146,7 @@ class NSUntaggedNumber_SummaryProvider: class NSUnknownNumber_SummaryProvider: def adjust_for_architecture(self): - self.lp64 = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) + self.is_64_bit = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) self.is_little = (self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle) self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize() diff --git a/lldb/examples/summaries/cocoa/NSSet.py b/lldb/examples/summaries/cocoa/NSSet.py index 3ca561cd8f8..ee3072da2dc 100644 --- a/lldb/examples/summaries/cocoa/NSSet.py +++ b/lldb/examples/summaries/cocoa/NSSet.py @@ -15,7 +15,7 @@ statistics.add_metric('code_notrun') # obey the interface specification for synthetic children providers class NSCFSet_SummaryProvider: def adjust_for_architecture(self): - self.lp64 = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) + self.is_64_bit = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) self.is_little = (self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle) self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize() @@ -32,7 +32,7 @@ class NSCFSet_SummaryProvider: # then we have one other internal pointer, plus # 4 bytes worth of flags. hence, these values def offset(self): - if self.lp64: + if self.is_64_bit: return 20 else: return 12 @@ -46,7 +46,7 @@ class NSCFSet_SummaryProvider: class NSSetUnknown_SummaryProvider: def adjust_for_architecture(self): - self.lp64 = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) + self.is_64_bit = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) self.is_little = (self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle) self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize() @@ -67,7 +67,7 @@ class NSSetUnknown_SummaryProvider: class NSSetI_SummaryProvider: def adjust_for_architecture(self): - self.lp64 = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) + self.is_64_bit = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) self.is_little = (self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle) self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize() @@ -78,14 +78,14 @@ class NSSetI_SummaryProvider: def update(self): self.adjust_for_architecture(); self.id_type = self.valobj.GetType().GetBasicType(lldb.eBasicTypeObjCID) - if self.lp64: + if self.is_64_bit: self.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong) else: self.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt) # we just need to skip the ISA and the count immediately follows def offset(self): - if self.lp64: + if self.is_64_bit: return 8 else: return 4 @@ -100,7 +100,7 @@ class NSSetI_SummaryProvider: # not sure if it is a bug or some weird sort of feature, but masking it out # gets the count right (unless, of course, someone's dictionaries grow # too large - but I have not tested this) - if self.lp64: + if self.is_64_bit: value = value & ~0xFF00000000000000 else: value = value & ~0xFF000000 @@ -108,7 +108,7 @@ class NSSetI_SummaryProvider: class NSSetM_SummaryProvider: def adjust_for_architecture(self): - self.lp64 = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) + self.is_64_bit = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) self.is_little = (self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle) self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize() @@ -119,14 +119,14 @@ class NSSetM_SummaryProvider: def update(self): self.adjust_for_architecture(); self.id_type = self.valobj.GetType().GetBasicType(lldb.eBasicTypeObjCID) - if self.lp64: + if self.is_64_bit: self.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong) else: self.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt) # we just need to skip the ISA and the count immediately follows def offset(self): - if self.lp64: + if self.is_64_bit: return 8 else: return 4 @@ -200,7 +200,7 @@ def NSSet_SummaryProvider2 (valobj,dict): if summary == None: summary = 'no valid set here' else: - if provider.lp64: + if provider.is_64_bit: summary = int(summary) & ~0x1fff000000000000 return str(summary) + ' objects' return '' diff --git a/lldb/examples/summaries/cocoa/NSURL.py b/lldb/examples/summaries/cocoa/NSURL.py index 6c15b402213..b242323f4bf 100644 --- a/lldb/examples/summaries/cocoa/NSURL.py +++ b/lldb/examples/summaries/cocoa/NSURL.py @@ -16,7 +16,7 @@ statistics.add_metric('code_notrun') # obey the interface specification for synthetic children providers class NSURLKnown_SummaryProvider: def adjust_for_architecture(self): - self.lp64 = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) + self.is_64_bit = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) self.is_little = (self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle) self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize() @@ -27,7 +27,7 @@ class NSURLKnown_SummaryProvider: def update(self): self.adjust_for_architecture(); self.id_type = self.valobj.GetType().GetBasicType(lldb.eBasicTypeObjCID) - if self.lp64: + if self.is_64_bit: self.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong) self.pointer_size = 8 else: @@ -41,7 +41,7 @@ class NSURLKnown_SummaryProvider: # (which are also present on a 32-bit system) # plus another pointer, and then the real data def offset(self): - if self.lp64: + if self.is_64_bit: return 24 else: return 16 @@ -61,7 +61,7 @@ class NSURLKnown_SummaryProvider: class NSURLUnknown_SummaryProvider: def adjust_for_architecture(self): - self.lp64 = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) + self.is_64_bit = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) self.is_little = (self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle) self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize() diff --git a/lldb/examples/summaries/cocoa/objc_lldb.py b/lldb/examples/summaries/cocoa/objc_lldb.py index a81be013159..877674c24cd 100644 --- a/lldb/examples/summaries/cocoa/objc_lldb.py +++ b/lldb/examples/summaries/cocoa/objc_lldb.py @@ -15,7 +15,7 @@ class ObjCRuntime: self.adjust_for_architecture() def adjust_for_architecture(self): - self.lp64 = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) + self.is_64_bit = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) self.is_little = (self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle) self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize() self.addr_type = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong) @@ -112,7 +112,7 @@ class ObjCRuntime: # read ro pointer ro_pointer = data + 12 + self.pointer_size - if self.lp64: + if self.is_64_bit: ro_pointer += 4 ro_object = self.valobj.CreateValueFromAddress("ro", ro_pointer, diff --git a/lldb/examples/summaries/cocoa/objc_runtime.py b/lldb/examples/summaries/cocoa/objc_runtime.py index f62898ff803..b60f6c837a4 100644 --- a/lldb/examples/summaries/cocoa/objc_runtime.py +++ b/lldb/examples/summaries/cocoa/objc_runtime.py @@ -4,42 +4,25 @@ import cache class Utilities: @staticmethod - def read_ascii(process, pointer,max_len=128,when_over_return_none=True): + def read_ascii(process, pointer,max_len=128): error = lldb.SBError() - pystr = '' - count = 0 - # there is not any length byte to tell us how much to read - # however, there are most probably ways to optimize this - # in order to avoid doing the read byte-by-byte (caching is - # still occurring, but we could just fetch a larger chunk - # of memory instead of going one byte at a time) - while True: - content = process.ReadMemory(pointer, 1, error) - new_bytes = bytearray(content) - if new_bytes == None or len(new_bytes) == 0: - break - b0 = new_bytes[0] - pointer = pointer + 1 - if b0 == 0: - break - count = count + 1 - if count > max_len: - if when_over_return_none: - return None - else: - return pystr - pystr = pystr + chr(b0) - return pystr + content = None + try: + content = process.ReadCStringFromMemory(pointer,max_len,error) + except: + pass + if content == None or len(content) == 0 or error.fail == True: + return None + return content @staticmethod - def is_valid_pointer(pointer, pointer_size, allow_tagged): + def is_valid_pointer(pointer, pointer_size, allow_tagged=False, allow_NULL=False): if pointer == None: return False if pointer == 0: - return False - if allow_tagged: - if (pointer % 2) == 1: - return True + return allow_NULL + if allow_tagged and (pointer % 2) == 1: + return True return ((pointer % pointer_size) == 0) # Objective-C runtime has a rule that pointers in a class_t will only have bits 0 thru 46 set @@ -48,10 +31,7 @@ class Utilities: def is_allowed_pointer(pointer): if pointer == None: return False - mask = 0xFFFF800000000000 - if (pointer & mask) != 0: - return False - return True + return ((pointer & 0xFFFF800000000000) == 0) @staticmethod def read_child_of(valobj,offset,type): @@ -66,32 +46,36 @@ class Utilities: return None if len(name) == 0: return None - return True - # the rules below make perfect sense for compile-time class names, but the runtime is free - # to create classes with arbitrary names to implement functionality (e.g -poseAsClass) - # hence, we cannot really outlaw anything appearing in an identifier - #ok_first = dict.fromkeys("$ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_") - #ok_others = dict.fromkeys("$ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_1234567890") - #if name[0] not in ok_first: - # return False - #return all(c in ok_others for c in name[1:]) + # technically, the ObjC runtime does not enforce any rules about what name a class can have + # in practice, the commonly used byte values for a class name are the letters, digits and some + # symbols: $, %, -, _, . + # WARNING: this means that you cannot use this runtime implementation if you need to deal + # with class names that use anything but what is allowed here + ok_values = dict.fromkeys("$%_.-ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890") + return all(c in ok_values for c in name) + + @staticmethod + def check_is_osx_lion(target): + # assume the only thing that has a Foundation.framework is a Mac + # assume anything < Lion does not even exist + mod = target.module['Foundation'] + if mod == None or mod.IsValid() == False: + return None + ver = mod.GetVersion() + if ver == None or ver == []: + return None + return (ver[0] < 900) class RoT_Data: def __init__(self,rot_pointer,params): if (Utilities.is_valid_pointer(rot_pointer.GetValueAsUnsigned(),params.pointer_size, allow_tagged=False)): self.sys_params = params self.valobj = rot_pointer - self.flags = Utilities.read_child_of(self.valobj,0,self.sys_params.uint32_t) + #self.flags = Utilities.read_child_of(self.valobj,0,self.sys_params.uint32_t) self.instanceStart = Utilities.read_child_of(self.valobj,4,self.sys_params.uint32_t) self.instanceSize = Utilities.read_child_of(self.valobj,8,self.sys_params.uint32_t) - if self.sys_params.lp64: - self.reserved = Utilities.read_child_of(self.valobj,12,self.sys_params.uint32_t) - offset = 16 - else: - self.reserved = 0 - offset = 12 - self.ivarLayoutPtr = Utilities.read_child_of(self.valobj,offset,self.sys_params.addr_ptr_type) - offset = offset + self.sys_params.pointer_size + offset = 24 if self.sys_params.is_64_bit else 16 + #self.ivarLayoutPtr = Utilities.read_child_of(self.valobj,offset,self.sys_params.addr_ptr_type) self.namePointer = Utilities.read_child_of(self.valobj,offset,self.sys_params.addr_ptr_type) self.check_valid() else: @@ -110,11 +94,9 @@ class RoT_Data: # pass def __str__(self): - return 'flags = ' + str(self.flags) + "\n" + \ + return \ "instanceStart = " + hex(self.instanceStart) + "\n" + \ "instanceSize = " + hex(self.instanceSize) + "\n" + \ - "reserved = " + hex(self.reserved) + "\n" + \ - "ivarLayoutPtr = " + hex(self.ivarLayoutPtr) + "\n" + \ "namePointer = " + hex(self.namePointer) + " --> " + self.name def is_valid(self): @@ -126,8 +108,8 @@ class RwT_Data: if (Utilities.is_valid_pointer(rwt_pointer.GetValueAsUnsigned(),params.pointer_size, allow_tagged=False)): self.sys_params = params self.valobj = rwt_pointer - self.flags = Utilities.read_child_of(self.valobj,0,self.sys_params.uint32_t) - self.version = Utilities.read_child_of(self.valobj,4,self.sys_params.uint32_t) + #self.flags = Utilities.read_child_of(self.valobj,0,self.sys_params.uint32_t) + #self.version = Utilities.read_child_of(self.valobj,4,self.sys_params.uint32_t) self.roPointer = Utilities.read_child_of(self.valobj,8,self.sys_params.addr_ptr_type) self.check_valid() else: @@ -143,8 +125,7 @@ class RwT_Data: self.valid = False def __str__(self): - return 'flags = ' + str(self.flags) + "\n" + \ - "version = " + hex(self.version) + "\n" + \ + return \ "roPointer = " + hex(self.roPointer) def is_valid(self): @@ -207,12 +188,15 @@ class Class_Data_V2: self.valid = False return + # in general, KVO is implemented by transparently subclassing + # however, there could be exceptions where a class does something else + # internally to implement the feature - this method will have no clue that a class + # has been KVO'ed unless the standard implementation technique is used def is_kvo(self): if self.is_valid(): - if self.data.data.name.startswith("NSKVONotify"): + if self.class_name().startswith("NSKVONotifying_"): return True - else: - return None + return False def get_superclass(self): if self.is_valid(): @@ -249,7 +233,7 @@ class Class_Data_V2: return None if align: unalign = self.instance_size(False) - if self.sys_params.lp64: + if self.sys_params.is_64_bit: return ((unalign + 7) & ~7) % 0x100000000 else: return ((unalign + 3) & ~3) % 0x100000000 @@ -268,13 +252,6 @@ class Class_Data_V1: self.version = Utilities.read_child_of(self.valobj,3*self.sys_params.pointer_size,self.sys_params.addr_ptr_type) self.info = Utilities.read_child_of(self.valobj,4*self.sys_params.pointer_size,self.sys_params.addr_ptr_type) self.instanceSize = Utilities.read_child_of(self.valobj,5*self.sys_params.pointer_size,self.sys_params.addr_ptr_type) - # since we do not introspect ivars, methods, ... these four pointers need not be named in a meaningful way - # moreover, we do not care about their values, just that they are correctly aligned - self.ptr1 = Utilities.read_child_of(self.valobj,6*self.sys_params.pointer_size,self.sys_params.addr_ptr_type) - self.ptr2 = Utilities.read_child_of(self.valobj,7*self.sys_params.pointer_size,self.sys_params.addr_ptr_type) - self.ptr3 = Utilities.read_child_of(self.valobj,8*self.sys_params.pointer_size,self.sys_params.addr_ptr_type) - self.ptr4 = Utilities.read_child_of(self.valobj,9*self.sys_params.pointer_size,self.sys_params.addr_ptr_type) - self.check_valid() else: self.valid = False if self.valid: @@ -293,16 +270,19 @@ class Class_Data_V1: if self.superclassIsaPointer != 0: self.valid = False return - #if not(Utilities.is_valid_pointer(self.namePointer,self.sys_params.pointer_size,allow_tagged=False)): - # self.valid = False - # return + if not(Utilities.is_valid_pointer(self.namePointer,self.sys_params.pointer_size,allow_tagged=False,allow_NULL=True)): + self.valid = False + return + # in general, KVO is implemented by transparently subclassing + # however, there could be exceptions where a class does something else + # internally to implement the feature - this method will have no clue that a class + # has been KVO'ed unless the standard implementation technique is used def is_kvo(self): if self.is_valid(): - if self.name.startswith("NSKVONotify"): + if self.class_name().startswith("NSKVONotifying_"): return True - else: - return None + return False def get_superclass(self): if self.is_valid(): @@ -338,15 +318,19 @@ class Class_Data_V1: return None if align: unalign = self.instance_size(False) - if self.sys_params.lp64: + if self.sys_params.is_64_bit: return ((unalign + 7) & ~7) % 0x100000000 else: return ((unalign + 3) & ~3) % 0x100000000 else: return self.instanceSize +TaggedClass_Values_Lion = {3 : 'NSNumber', 6: 'NSDate'}; # double check NSDate +TaggedClass_Values_NMOS = {3 : 'NSNumber', 6: 'NSDate'}; # double check NSNumber + class TaggedClass_Data: def __init__(self,pointer,params): + global TaggedClass_Values_Lion, TaggedClass_Values_NMOS self.valid = True self.name = None self.sys_params = params @@ -354,28 +338,18 @@ class TaggedClass_Data: self.val = (pointer & ~0x0000000000000000FF) >> 8 self.class_bits = (pointer & 0xE) >> 1 self.i_bits = (pointer & 0xF0) >> 4 - # ignoring the LSB, NSNumber gets marked - # as 3 on Zin and as 1 on Lion - I can either make - # a difference or accept false positives - # ToT LLDB + some knowledge of framework versioning - # would let me do the right thing - for now I just - # act dumb and accept false positives - if self.class_bits == 0 or \ - self.class_bits == 5 or \ - self.class_bits == 7 or \ - self.class_bits == 9: - self.valid = False - return - elif self.class_bits == 3 or self.class_bits == 1: - self.name = 'NSNumber' - elif self.class_bits == 11: - self.name = 'NSManagedObject' - elif self.class_bits == 13: - self.name = 'NSDate' - elif self.class_bits == 15: - self.name = 'NSDateTS' # not sure what this is + + if self.sys_params.is_lion: + if self.class_bits in TaggedClass_Values_Lion: + self.name = TaggedClass_Values_Lion[self.class_bits] + else: + self.valid = False else: - self.valid = False + if self.class_bits in TaggedClass_Values_NMOS: + self.name = TaggedClass_Values_NMOS[self.class_bits] + else: + self.valid = False + def is_valid(self): return self.valid @@ -384,7 +358,7 @@ class TaggedClass_Data: if self.is_valid(): return self.name else: - return None + return False def value(self): return self.val if self.is_valid() else None @@ -409,7 +383,7 @@ class TaggedClass_Data: def instance_size(self,align=False): if self.is_valid() == False: return None - return 8 if self.sys_params.lp64 else 4 + return 8 if self.sys_params.is_64_bit else 4 class InvalidClass_Data: @@ -419,6 +393,7 @@ class InvalidClass_Data: return False runtime_version = cache.Cache() +os_version = cache.Cache() class SystemParameters: def __init__(self,valobj): @@ -426,19 +401,25 @@ class SystemParameters: def adjust_for_architecture(self,valobj): self.process = valobj.GetTarget().GetProcess() - self.lp64 = (self.process.GetAddressByteSize() == 8) + self.is_64_bit = (self.process.GetAddressByteSize() == 8) self.is_little = (self.process.GetByteOrder() == lldb.eByteOrderLittle) self.pointer_size = self.process.GetAddressByteSize() self.addr_type = valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong) self.addr_ptr_type = self.addr_type.GetPointerType() self.uint32_t = valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt) global runtime_version + global os_version pid = self.process.GetProcessID() if runtime_version.look_for_key(pid): self.runtime_version = runtime_version.get_value(pid) else: self.runtime_version = ObjCRuntime.runtime_version(self.process) runtime_version.add_item(pid,self.runtime_version) + if os_version.look_for_key(pid): + self.is_lion = os_version.get_value(pid) + else: + self.is_lion = Utilities.check_is_osx_lion(valobj.GetTarget()) + os_version.add_item(pid,self.is_lion) isa_cache = cache.Cache() @@ -476,9 +457,11 @@ class ObjCRuntime: self.valobj = valobj self.adjust_for_architecture() self.sys_params = SystemParameters(self.valobj) + self.unsigned_value = self.valobj.GetValueAsUnsigned() + self.isa_value = None def adjust_for_architecture(self): - self.lp64 = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) + self.is_64_bit = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) self.is_little = (self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle) self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize() self.addr_type = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong) @@ -488,16 +471,19 @@ class ObjCRuntime: def is_tagged(self): if self.valobj is None: return False - return (Utilities.is_valid_pointer(self.valobj.GetValueAsUnsigned(),self.pointer_size, allow_tagged=True) and not(Utilities.is_valid_pointer(self.valobj.GetValueAsUnsigned(),self.pointer_size, allow_tagged=False))) + return (Utilities.is_valid_pointer(self.unsigned_value,self.pointer_size, allow_tagged=True) and \ + not(Utilities.is_valid_pointer(self.unsigned_value,self.pointer_size, allow_tagged=False))) def is_valid(self): if self.valobj is None: return False if self.valobj.IsInScope() == False: return False - return Utilities.is_valid_pointer(self.valobj.GetValueAsUnsigned(),self.pointer_size, allow_tagged=True) + return Utilities.is_valid_pointer(self.unsigned_value,self.pointer_size, allow_tagged=True) def read_isa(self): + if self.isa_value != None: + return self.isa_value isa_pointer = self.valobj.CreateChildAtOffset("cfisa", 0, self.addr_ptr_type) @@ -505,6 +491,7 @@ class ObjCRuntime: return None; if isa_pointer.GetValueAsUnsigned(1) == 1: return None; + self.isa_value = isa_pointer return isa_pointer def read_class_data(self): @@ -515,7 +502,7 @@ class ObjCRuntime: # not every odd-valued pointer is actually tagged. most are just plain wrong # we could try and predetect this before even creating a TaggedClass_Data object # but unless performance requires it, this seems a cleaner way to tackle the task - tentative_tagged = TaggedClass_Data(self.valobj.GetValueAsUnsigned(0),self.sys_params) + tentative_tagged = TaggedClass_Data(self.unsigned_value,self.sys_params) return tentative_tagged if tentative_tagged.is_valid() else InvalidClass_Data() else: return InvalidClass_Data() diff --git a/lldb/include/lldb/Core/FormatClasses.h b/lldb/include/lldb/Core/FormatClasses.h index 865a8c48f35..f0af7e70f07 100644 --- a/lldb/include/lldb/Core/FormatClasses.h +++ b/lldb/include/lldb/Core/FormatClasses.h @@ -1158,8 +1158,9 @@ public: { } - virtual std::string - FormatObject (lldb::ValueObjectSP object) = 0; + virtual bool + FormatObject (lldb::ValueObjectSP object, + std::string& dest) = 0; virtual std::string GetDescription () = 0; @@ -1211,8 +1212,9 @@ struct StringSummaryFormat : public TypeSummaryImpl { } - virtual std::string - FormatObject(lldb::ValueObjectSP object); + virtual bool + FormatObject(lldb::ValueObjectSP object, + std::string& dest); virtual std::string GetDescription(); @@ -1232,6 +1234,7 @@ struct ScriptSummaryFormat : public TypeSummaryImpl { std::string m_function_name; std::string m_python_script; + lldb::ScriptInterpreterObjectSP m_script_function_sp; ScriptSummaryFormat(const TypeSummaryImpl::Flags& flags, const char *function_name, @@ -1273,8 +1276,9 @@ struct ScriptSummaryFormat : public TypeSummaryImpl { } - virtual std::string - FormatObject(lldb::ValueObjectSP object); + virtual bool + FormatObject(lldb::ValueObjectSP object, + std::string& dest); virtual std::string GetDescription(); diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreter.h b/lldb/include/lldb/Interpreter/ScriptInterpreter.h index 7cecbbad700..4bd41d053b7 100644 --- a/lldb/include/lldb/Interpreter/ScriptInterpreter.h +++ b/lldb/include/lldb/Interpreter/ScriptInterpreter.h @@ -20,6 +20,43 @@ namespace lldb_private { +class ScriptInterpreterObject +{ +public: + ScriptInterpreterObject() : + m_object(NULL) + {} + + ScriptInterpreterObject(void* obj) : + m_object(obj) + {} + + ScriptInterpreterObject(const ScriptInterpreterObject& rhs) + : m_object(rhs.m_object) + {} + + virtual void* + GetObject() + { + return m_object; + } + + ScriptInterpreterObject& + operator = (const ScriptInterpreterObject& rhs) + { + if (this != &rhs) + m_object = rhs.m_object; + return *this; + } + + virtual + ~ScriptInterpreterObject() + {} + +protected: + void* m_object; +}; + class ScriptInterpreter { public: @@ -31,9 +68,11 @@ public: const lldb::StackFrameSP& frame_sp, const lldb::BreakpointLocationSP &bp_loc_sp); - typedef std::string (*SWIGPythonTypeScriptCallbackFunction) (const char *python_function_name, - const char *session_dictionary_name, - const lldb::ValueObjectSP& valobj_sp); + typedef bool (*SWIGPythonTypeScriptCallbackFunction) (const char *python_function_name, + void *session_dictionary, + const lldb::ValueObjectSP& valobj_sp, + void** pyfunct_wrapper, + std::string& retval); typedef void* (*SWIGPythonCreateSyntheticProvider) (const std::string python_class_name, const char *session_dictionary_name, @@ -163,6 +202,15 @@ public: return; } + virtual bool + GetScriptedSummary (const char *function_name, + lldb::ValueObjectSP valobj, + lldb::ScriptInterpreterObjectSP& callee_wrapper_sp, + std::string& retval) + { + return false; + } + virtual uint32_t CalculateNumChildren (void *implementor) { @@ -211,6 +259,12 @@ public: return false; } + virtual lldb::ScriptInterpreterObjectSP + MakeScriptObject (void* object) + { + return lldb::ScriptInterpreterObjectSP(new ScriptInterpreterObject(object)); + } + const char * GetScriptInterpreterPtyName (); @@ -224,17 +278,7 @@ public: LanguageToString (lldb::ScriptLanguage language); static void - InitializeInterpreter (SWIGInitCallback python_swig_init_callback, - SWIGBreakpointCallbackFunction python_swig_breakpoint_callback, - SWIGPythonTypeScriptCallbackFunction python_swig_typescript_callback, - SWIGPythonCreateSyntheticProvider python_swig_synthetic_script, - SWIGPythonCalculateNumChildren python_swig_calc_children, - SWIGPythonGetChildAtIndex python_swig_get_child_index, - SWIGPythonGetIndexOfChildWithName python_swig_get_index_child, - SWIGPythonCastPyObjectToSBValue python_swig_cast_to_sbvalue, - SWIGPythonUpdateSynthProviderInstance python_swig_update_provider, - SWIGPythonCallCommand python_swig_call_command, - SWIGPythonCallModuleInit python_swig_call_mod_init); + InitializeInterpreter (SWIGInitCallback python_swig_init_callback); static void TerminateInterpreter (); diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreterPython.h b/lldb/include/lldb/Interpreter/ScriptInterpreterPython.h index f8718d3e9ff..afd713f63b8 100644 --- a/lldb/include/lldb/Interpreter/ScriptInterpreterPython.h +++ b/lldb/include/lldb/Interpreter/ScriptInterpreterPython.h @@ -113,9 +113,11 @@ public: lldb::user_id_t break_id, lldb::user_id_t break_loc_id); - static std::string - CallPythonScriptFunction (const char *python_function_name, - lldb::ValueObjectSP valobj); + virtual bool + GetScriptedSummary (const char *function_name, + lldb::ValueObjectSP valobj, + lldb::ScriptInterpreterObjectSP& callee_wrapper_sp, + std::string& retval); virtual std::string GetDocumentationForItem (const char* item); @@ -124,7 +126,10 @@ public: LoadScriptingModule (const char* filename, bool can_reload, lldb_private::Error& error); - + + virtual lldb::ScriptInterpreterObjectSP + MakeScriptObject (void* object); + void CollectDataForBreakpointCommandCallback (BreakpointOptions *bp_options, CommandReturnObject &result); @@ -147,17 +152,7 @@ public: InitializePrivate (); static void - InitializeInterpreter (SWIGInitCallback python_swig_init_callback, - SWIGBreakpointCallbackFunction python_swig_breakpoint_callback, - SWIGPythonTypeScriptCallbackFunction python_swig_typescript_callback, - SWIGPythonCreateSyntheticProvider python_swig_synthetic_script, - SWIGPythonCalculateNumChildren python_swig_calc_children, - SWIGPythonGetChildAtIndex python_swig_get_child_index, - SWIGPythonGetIndexOfChildWithName python_swig_get_index_child, - SWIGPythonCastPyObjectToSBValue python_swig_cast_to_sbvalue, - SWIGPythonUpdateSynthProviderInstance python_swig_update_provider, - SWIGPythonCallCommand python_swig_call_command, - SWIGPythonCallModuleInit python_swig_call_mod_init); + InitializeInterpreter (SWIGInitCallback python_swig_init_callback); protected: @@ -187,6 +182,29 @@ private: ~SynchronicityHandler(); }; + class ScriptInterpreterPythonObject : public ScriptInterpreterObject + { + public: + ScriptInterpreterPythonObject() : + ScriptInterpreterObject() + {} + + ScriptInterpreterPythonObject(void* obj) : + ScriptInterpreterObject(obj) + { + Py_XINCREF(m_object); + } + + virtual + ~ScriptInterpreterPythonObject() + { + Py_XDECREF(m_object); + m_object = NULL; + } + private: + DISALLOW_COPY_AND_ASSIGN (ScriptInterpreterPythonObject); + }; + class Locker { public: diff --git a/lldb/include/lldb/lldb-forward.h b/lldb/include/lldb/lldb-forward.h index 23dd54df977..364b3b362ea 100644 --- a/lldb/include/lldb/lldb-forward.h +++ b/lldb/include/lldb/lldb-forward.h @@ -136,6 +136,7 @@ class RegisterValue; class RegularExpression; class Scalar; class ScriptInterpreter; +class ScriptInterpreterObject; #ifndef LLDB_DISABLE_PYTHON class ScriptInterpreterPython; struct ScriptSummaryFormat; @@ -276,6 +277,7 @@ namespace lldb { typedef std::tr1::shared_ptr<lldb_private::Section> SectionSP; typedef std::tr1::weak_ptr<lldb_private::Section> SectionWP; typedef std::tr1::shared_ptr<lldb_private::SearchFilter> SearchFilterSP; + typedef std::tr1::shared_ptr<lldb_private::ScriptInterpreterObject> ScriptInterpreterObjectSP; #ifndef LLDB_DISABLE_PYTHON typedef std::tr1::shared_ptr<lldb_private::ScriptSummaryFormat> ScriptSummaryFormatSP; #endif // #ifndef LLDB_DISABLE_PYTHON diff --git a/lldb/scripts/Python/finish-swig-Python-LLDB.sh b/lldb/scripts/Python/finish-swig-Python-LLDB.sh index 5f5eb8b1778..aabd94d8087 100755 --- a/lldb/scripts/Python/finish-swig-Python-LLDB.sh +++ b/lldb/scripts/Python/finish-swig-Python-LLDB.sh @@ -382,6 +382,20 @@ else fi fi +if [ -f "${SRC_ROOT}/examples/summaries/cocoa/NSDate.py" ] +then + if [ $Debug == 1 ] + then + echo "Copying NSDate.py to ${framework_python_dir}" + fi + cp "${SRC_ROOT}/examples/summaries/cocoa/NSDate.py" "${framework_python_dir}" +else + if [ $Debug == 1 ] + then + echo "Unable to find ${SRC_ROOT}/examples/summaries/cocoa/NSDate.py" + fi +fi + if [ -f "${SRC_ROOT}/examples/summaries/cocoa/cache.py" ] then if [ $Debug == 1 ] diff --git a/lldb/scripts/Python/python-wrapper.swig b/lldb/scripts/Python/python-wrapper.swig index f6e3afd4c39..3277e780e26 100644 --- a/lldb/scripts/Python/python-wrapper.swig +++ b/lldb/scripts/Python/python-wrapper.swig @@ -31,10 +31,7 @@ static PyObject* ResolvePythonName(const char* name, PyObject* pmodule = NULL) { - - //printf("Resolving %s\n", name); - - if (!name || !name[0]) + if (!name) return pmodule; PyErr_Cleaner pyerr_cleanup(true); // show Python errors @@ -65,40 +62,33 @@ ResolvePythonName(const char* name, if (!dot_pos) { - if (PyDict_Check (main_dict)) + dest_object = NULL; + while (PyDict_Next (main_dict, &pos, &key, &value)) { - dest_object = NULL; - while (PyDict_Next (main_dict, &pos, &key, &value)) + // We have stolen references to the key and value objects in the dictionary; we need to increment + // them now so that Python's garbage collector doesn't collect them out from under us. + Py_INCREF (key); + Py_INCREF (value); + if (strcmp (PyString_AsString (key), name) == 0) { - // We have stolen references to the key and value objects in the dictionary; we need to increment - // them now so that Python's garbage collector doesn't collect them out from under us. - Py_INCREF (key); - Py_INCREF (value); - //printf("Comparing %s and %s\n", name, PyString_AsString (key)); - if (strcmp (PyString_AsString (key), name) == 0) - { - dest_object = value; - break; - } + dest_object = value; + break; } - } - + } if (!dest_object || dest_object == Py_None) return NULL; return dest_object; } - // foo.bar.ba - // 0123456789 - // len = 3 - 0 - size_t len = dot_pos - name; - std::string piece(name,len); - dest_object = ResolvePythonName(piece.c_str(), main_dict); - //printf("Resolved %s to %p\n", piece.c_str(), dest_object); - if (!dest_object) - return NULL; - //printf("Now moving to resolve %s\n", dot_pos+1); - return ResolvePythonName(dot_pos+1,dest_object); // tail recursion.. should be optimized by the compiler - + else + { + size_t len = dot_pos - name; + std::string piece(name,len); + pmodule = ResolvePythonName(piece.c_str(), main_dict); + if (!pmodule) + return NULL; + name = dot_pos+1; + return ResolvePythonName(dot_pos+1,pmodule); // tail recursion.. should be optimized by the compiler + } } static PyObject* @@ -186,85 +176,69 @@ LLDBSwigPythonBreakpointCallbackFunction return stop_at_breakpoint; } -SWIGEXPORT std::string +SWIGEXPORT bool LLDBSwigPythonCallTypeScript ( const char *python_function_name, - const char *session_dictionary_name, - const lldb::ValueObjectSP& valobj_sp + const void *session_dictionary, + const lldb::ValueObjectSP& valobj_sp, + void** pyfunct_wrapper, + std::string& retval ) { lldb::SBValue sb_value (valobj_sp); - std::string retval = ""; - PyObject *ValObj_PyObj = SWIG_NewPointerObj((void *) &sb_value, SWIGTYPE_p_lldb__SBValue, 0); if (ValObj_PyObj == NULL) - return retval; + return false; - if (!python_function_name || !session_dictionary_name) - return retval; + if (!python_function_name || !session_dictionary) + return false; - PyObject *session_dict, *pfunc; - PyObject *pargs, *pvalue; + PyObject *session_dict = (PyObject*)session_dictionary, *pfunc = NULL, *pargs = NULL, *pvalue = NULL; - session_dict = FindSessionDictionary (session_dictionary_name); - if (session_dict != NULL) + if (pyfunct_wrapper && *pyfunct_wrapper && PyFunction_Check (*pyfunct_wrapper)) { - pfunc = ResolvePythonName (python_function_name, session_dict); - if (pfunc != NULL) + pfunc = (PyObject*)(*pyfunct_wrapper); + if (pfunc->ob_refcnt == 1) { - // Set up the arguments and call the function. - - if (PyCallable_Check (pfunc)) - { - pargs = PyTuple_New (2); - if (pargs == NULL) - { - if (PyErr_Occurred()) - PyErr_Clear(); - return retval; - } - - PyTuple_SetItem (pargs, 0, ValObj_PyObj); // This "steals" a reference to ValObj_PyObj - PyTuple_SetItem (pargs, 1, session_dict); // This "steals" a reference to session_dict - pvalue = PyObject_CallObject (pfunc, pargs); - Py_DECREF (pargs); - - if (pvalue != NULL) - { - if (pvalue != Py_None && PyString_CheckExact(pvalue)) - retval = std::string(PyString_AsString(pvalue)); - else - retval = "None"; - Py_DECREF (pvalue); - } - else if (PyErr_Occurred ()) - { - PyErr_Print(); - PyErr_Clear(); - } - Py_INCREF (session_dict); - } - else if (PyErr_Occurred()) - { - PyErr_Print(); - PyErr_Clear(); - } - } - else if (PyErr_Occurred()) - { - PyErr_Print(); - PyErr_Clear(); + Py_XDECREF(pfunc); + pfunc = NULL; } } - else if (PyErr_Occurred ()) + + if (PyDict_Check(session_dict)) { - PyErr_Print(); - PyErr_Clear (); + PyErr_Cleaner pyerr_cleanup(true); // show Python errors + + if (!pfunc) + { + pfunc = ResolvePythonName (python_function_name, session_dict); + if (!pfunc || !PyFunction_Check (pfunc)) + return false; + else + { + if (pyfunct_wrapper) + *pyfunct_wrapper = pfunc; + } + } + /*else + printf("caching works!!!!\n");*/ + + pargs = PyTuple_Pack(2, ValObj_PyObj, session_dict); + if (pargs == NULL) + return false; + + pvalue = PyObject_CallObject (pfunc, pargs); + Py_DECREF (pargs); + + if (pvalue != NULL && pvalue != Py_None && PyString_Check(pvalue)) + retval.assign(PyString_AsString(pvalue)); + Py_XDECREF (pvalue); + Py_INCREF (session_dict); } - return retval; + return true; } SWIGEXPORT void* diff --git a/lldb/source/API/SBCommandInterpreter.cpp b/lldb/source/API/SBCommandInterpreter.cpp index cbf2f914089..66ebf69df99 100644 --- a/lldb/source/API/SBCommandInterpreter.cpp +++ b/lldb/source/API/SBCommandInterpreter.cpp @@ -312,59 +312,11 @@ SBCommandInterpreter::GetArgumentDescriptionAsCString (const lldb::CommandArgume #ifndef LLDB_DISABLE_PYTHON -extern "C" bool -LLDBSwigPythonBreakpointCallbackFunction -( - const char *python_function_name, - const char *session_dictionary_name, - const lldb::StackFrameSP& sb_frame, - const lldb::BreakpointLocationSP& sb_bp_loc -); - -extern "C" std::string -LLDBSwigPythonCallTypeScript -( - const char *python_function_name, - const char *session_dictionary_name, - const lldb::ValueObjectSP& valobj_sp -); - -extern "C" void* -LLDBSwigPythonCreateSyntheticProvider -( - const std::string python_class_name, - const char *session_dictionary_name, - const lldb::ValueObjectSP& valobj_sp -); - - -extern "C" uint32_t LLDBSwigPython_CalculateNumChildren (void *implementor); -extern "C" void* LLDBSwigPython_GetChildAtIndex (void *implementor, uint32_t idx); -extern "C" int LLDBSwigPython_GetIndexOfChildWithName (void *implementor, const char* child_name); -extern "C" void* LLDBSWIGPython_CastPyObjectToSBValue (void* data); -extern "C" void LLDBSwigPython_UpdateSynthProviderInstance (void* implementor); - -extern "C" bool LLDBSwigPythonCallCommand -( - const char *python_function_name, - const char *session_dictionary_name, - lldb::DebuggerSP& debugger, - const char* args, - std::string& err_msg, - lldb_private::CommandReturnObject& cmd_retobj -); // Defined in the SWIG source file extern "C" void init_lldb(void); -extern "C" bool LLDBSwigPythonCallModuleInit -( - const std::string python_module_name, - const char *session_dictionary_name, - lldb::DebuggerSP& debugger -); - #else extern "C" void init_lldb(void); @@ -385,17 +337,7 @@ SBCommandInterpreter::InitializeSWIG () { g_initialized = true; #ifndef LLDB_DISABLE_PYTHON - ScriptInterpreter::InitializeInterpreter (init_lldb, - LLDBSwigPythonBreakpointCallbackFunction, - LLDBSwigPythonCallTypeScript, - LLDBSwigPythonCreateSyntheticProvider, - LLDBSwigPython_CalculateNumChildren, - LLDBSwigPython_GetChildAtIndex, - LLDBSwigPython_GetIndexOfChildWithName, - LLDBSWIGPython_CastPyObjectToSBValue, - LLDBSwigPython_UpdateSynthProviderInstance, - LLDBSwigPythonCallCommand, - LLDBSwigPythonCallModuleInit); + ScriptInterpreter::InitializeInterpreter (init_lldb); #endif } } diff --git a/lldb/source/Core/FormatClasses.cpp b/lldb/source/Core/FormatClasses.cpp index d59da37c487..4dd51de8a2c 100644 --- a/lldb/source/Core/FormatClasses.cpp +++ b/lldb/source/Core/FormatClasses.cpp @@ -35,6 +35,7 @@ struct PyObject; #include "lldb/Core/Debugger.h" #include "lldb/Core/FormatClasses.h" #include "lldb/Core/StreamString.h" +#include "lldb/Core/Timer.h" #include "lldb/Core/ValueObjectConstResult.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Symbol/ClangASTType.h" @@ -78,11 +79,15 @@ StringSummaryFormat::StringSummaryFormat(const TypeSummaryImpl::Flags& flags, m_format.assign(format_cstr); } -std::string -StringSummaryFormat::FormatObject(lldb::ValueObjectSP object) +bool +StringSummaryFormat::FormatObject(lldb::ValueObjectSP object, + std::string& retval) { if (!object.get()) - return "NULL"; + { + retval.assign("NULL sp"); + return false; + } StreamString s; ExecutionContext exe_ctx (object->GetExecutionContextRef()); @@ -117,18 +122,28 @@ StringSummaryFormat::FormatObject(lldb::ValueObjectSP object) s.PutChar(')'); - return s.GetString(); + retval.assign(s.GetString()); + return true; } else - return ""; + { + retval.assign("error: oneliner for no children"); + return false; + } } else { if (Debugger::FormatPrompt(m_format.c_str(), &sc, &exe_ctx, &sc.line_entry.range.GetBaseAddress(), s, NULL, object.get())) - return s.GetString(); + { + retval.assign(s.GetString()); + return true; + } else - return ""; + { + retval.assign("error: summary string parsing error"); + return false; + } } } @@ -156,7 +171,8 @@ ScriptSummaryFormat::ScriptSummaryFormat(const TypeSummaryImpl::Flags& flags, const char * python_script) : TypeSummaryImpl(flags), m_function_name(), - m_python_script() + m_python_script(), + m_script_function_sp() { if (function_name) m_function_name.assign(function_name); @@ -164,11 +180,26 @@ ScriptSummaryFormat::ScriptSummaryFormat(const TypeSummaryImpl::Flags& flags, m_python_script.assign(python_script); } -std::string -ScriptSummaryFormat::FormatObject(lldb::ValueObjectSP object) +bool +ScriptSummaryFormat::FormatObject(lldb::ValueObjectSP object, + std::string& retval) { - return std::string(ScriptInterpreterPython::CallPythonScriptFunction(m_function_name.c_str(), - object).c_str()); + Timer scoped_timer (__PRETTY_FUNCTION__, __PRETTY_FUNCTION__); + + Debugger& dbg = object->GetTargetSP()->GetDebugger(); + ScriptInterpreter *script_interpreter = dbg.GetCommandInterpreter().GetScriptInterpreter(); + + if (!script_interpreter) + { + retval.assign("error: no ScriptInterpreter"); + return false; + } + + return script_interpreter->GetScriptedSummary(m_function_name.c_str(), + object, + m_script_function_sp, + retval); + } std::string diff --git a/lldb/source/Core/FormatManager.cpp b/lldb/source/Core/FormatManager.cpp index 9552f7f5656..af0ef7954c6 100644 --- a/lldb/source/Core/FormatManager.cpp +++ b/lldb/source/Core/FormatManager.cpp @@ -854,6 +854,8 @@ FormatManager::LoadObjCFormatters() AddScriptSummary(appkit_category_sp, "NSURL.NSURL_SummaryProvider", ConstString("NSURL"), appkit_flags); AddScriptSummary(appkit_category_sp, "NSURL.NSURL_SummaryProvider", ConstString("CFURLRef"), appkit_flags); + AddScriptSummary(appkit_category_sp, "NSDate.NSDate_SummaryProvider", ConstString("NSDate"), appkit_flags); + TypeCategoryImpl::SharedPointer vectors_category_sp = GetCategory(m_vectortypes_category_name); TypeSummaryImpl::Flags vector_flags; diff --git a/lldb/source/Core/ValueObject.cpp b/lldb/source/Core/ValueObject.cpp index 023234cf99f..3986246e55d 100644 --- a/lldb/source/Core/ValueObject.cpp +++ b/lldb/source/Core/ValueObject.cpp @@ -674,7 +674,8 @@ ValueObject::GetSummaryAsCString () if (summary_format) { - m_summary_str = summary_format->FormatObject(GetSP()); + summary_format->FormatObject(GetSP(), + m_summary_str); } else { diff --git a/lldb/source/Interpreter/ScriptInterpreter.cpp b/lldb/source/Interpreter/ScriptInterpreter.cpp index 826a0021e7e..5bdb70e1d1d 100644 --- a/lldb/source/Interpreter/ScriptInterpreter.cpp +++ b/lldb/source/Interpreter/ScriptInterpreter.cpp @@ -69,30 +69,10 @@ ScriptInterpreter::LanguageToString (lldb::ScriptLanguage language) } void -ScriptInterpreter::InitializeInterpreter (SWIGInitCallback python_swig_init_callback, - SWIGBreakpointCallbackFunction python_swig_breakpoint_callback, - SWIGPythonTypeScriptCallbackFunction python_swig_typescript_callback, - SWIGPythonCreateSyntheticProvider python_swig_synthetic_script, - SWIGPythonCalculateNumChildren python_swig_calc_children, - SWIGPythonGetChildAtIndex python_swig_get_child_index, - SWIGPythonGetIndexOfChildWithName python_swig_get_index_child, - SWIGPythonCastPyObjectToSBValue python_swig_cast_to_sbvalue, - SWIGPythonUpdateSynthProviderInstance python_swig_update_provider, - SWIGPythonCallCommand python_swig_call_command, - SWIGPythonCallModuleInit python_swig_call_mod_init) +ScriptInterpreter::InitializeInterpreter (SWIGInitCallback python_swig_init_callback) { #ifndef LLDB_DISABLE_PYTHON - ScriptInterpreterPython::InitializeInterpreter (python_swig_init_callback, - python_swig_breakpoint_callback, - python_swig_typescript_callback, - python_swig_synthetic_script, - python_swig_calc_children, - python_swig_get_child_index, - python_swig_get_index_child, - python_swig_cast_to_sbvalue, - python_swig_update_provider, - python_swig_call_command, - python_swig_call_mod_init); + ScriptInterpreterPython::InitializeInterpreter (python_swig_init_callback); #endif // #ifndef LLDB_DISABLE_PYTHON } diff --git a/lldb/source/Interpreter/ScriptInterpreterPython.cpp b/lldb/source/Interpreter/ScriptInterpreterPython.cpp index ccdf7486ca4..246e3bd7b9a 100644 --- a/lldb/source/Interpreter/ScriptInterpreterPython.cpp +++ b/lldb/source/Interpreter/ScriptInterpreterPython.cpp @@ -54,6 +54,62 @@ static ScriptInterpreter::SWIGPythonUpdateSynthProviderInstance g_swig_update_pr static ScriptInterpreter::SWIGPythonCallCommand g_swig_call_command = NULL; static ScriptInterpreter::SWIGPythonCallModuleInit g_swig_call_module_init = NULL; +// these are the Pythonic implementations of the required callbacks +// these are scripting-language specific, which is why they belong here +// we still need to use function pointers to them instead of relying +// on linkage-time resolution because the SWIG stuff and this file +// get built at different times +extern "C" bool +LLDBSwigPythonBreakpointCallbackFunction +( + const char *python_function_name, + const char *session_dictionary_name, + const lldb::StackFrameSP& sb_frame, + const lldb::BreakpointLocationSP& sb_bp_loc + ); + +extern "C" bool +LLDBSwigPythonCallTypeScript +( + const char *python_function_name, + void *session_dictionary, + const lldb::ValueObjectSP& valobj_sp, + void** pyfunct_wrapper, + std::string& retval + ); + +extern "C" void* +LLDBSwigPythonCreateSyntheticProvider +( + const std::string python_class_name, + const char *session_dictionary_name, + const lldb::ValueObjectSP& valobj_sp + ); + + +extern "C" uint32_t LLDBSwigPython_CalculateNumChildren (void *implementor); +extern "C" void* LLDBSwigPython_GetChildAtIndex (void *implementor, uint32_t idx); +extern "C" int LLDBSwigPython_GetIndexOfChildWithName (void *implementor, const char* child_name); +extern "C" void* LLDBSWIGPython_CastPyObjectToSBValue (void* data); +extern "C" void LLDBSwigPython_UpdateSynthProviderInstance (void* implementor); + +extern "C" bool LLDBSwigPythonCallCommand +( + const char *python_function_name, + const char *session_dictionary_name, + lldb::DebuggerSP& debugger, + const char* args, + std::string& err_msg, + lldb_private::CommandReturnObject& cmd_retobj + ); + +extern "C" bool LLDBSwigPythonCallModuleInit +( + const std::string python_module_name, + const char *session_dictionary_name, + lldb::DebuggerSP& debugger + ); + static int _check_and_flush (FILE *stream) { @@ -226,7 +282,7 @@ ScriptInterpreterPython::ScriptInterpreterPython (CommandInterpreter &interprete // WARNING: temporary code that loads Cocoa formatters - this should be done on a per-platform basis rather than loading the whole set // and letting the individual formatter classes exploit APIs to check whether they can/cannot do their task run_string.Clear(); - run_string.Printf ("run_one_line (%s, 'import CFString, CFArray, CFDictionary, NSData, NSMachPort, NSSet, NSNotification, NSException, CFBag, CFBinaryHeap, NSURL, NSBundle, NSNumber')", m_dictionary_name.c_str()); + run_string.Printf ("run_one_line (%s, 'import CFString, CFArray, CFDictionary, NSData, NSMachPort, NSSet, NSNotification, NSException, CFBag, CFBinaryHeap, NSURL, NSBundle, NSNumber, NSDate')", m_dictionary_name.c_str()); PyRun_SimpleString (run_string.GetData()); int new_count = Debugger::TestDebuggerRefCount(); @@ -1454,44 +1510,88 @@ ScriptInterpreterPython::GenerateBreakpointCommandCallbackData (StringList &user return true; } -std::string -ScriptInterpreterPython::CallPythonScriptFunction (const char *python_function_name, - lldb::ValueObjectSP valobj) +static PyObject* +FindSessionDictionary(const char* dict_name) { + static std::map<ConstString,PyObject*> g_dict_map; - if (!python_function_name || !(*python_function_name)) - return "<no function>"; + ConstString dict(dict_name); - if (!valobj.get()) - return "<no object>"; - - ExecutionContext exe_ctx (valobj->GetExecutionContextRef()); - Target *target = exe_ctx.GetTargetPtr(); + std::map<ConstString,PyObject*>::iterator iter = g_dict_map.find(dict); - if (!target) - return "<no target>"; + if (iter != g_dict_map.end()) + return iter->second; - Debugger &debugger = target->GetDebugger(); - ScriptInterpreter *script_interpreter = debugger.GetCommandInterpreter().GetScriptInterpreter(); - ScriptInterpreterPython *python_interpreter = (ScriptInterpreterPython *) script_interpreter; + PyObject *main_mod = PyImport_AddModule ("__main__"); + if (main_mod != NULL) + { + PyObject *main_dict = PyModule_GetDict (main_mod); + if ((main_dict != NULL) + && PyDict_Check (main_dict)) + { + // Go through the main dictionary looking for the correct python script interpreter dictionary + PyObject *key, *value; + Py_ssize_t pos = 0; + + while (PyDict_Next (main_dict, &pos, &key, &value)) + { + // We have stolen references to the key and value objects in the dictionary; we need to increment + // them now so that Python's garbage collector doesn't collect them out from under us. + Py_INCREF (key); + Py_INCREF (value); + if (strcmp (PyString_AsString (key), dict_name) == 0) + { + g_dict_map[dict] = value; + return value; + } + } + } + } + return NULL; +} + +bool +ScriptInterpreterPython::GetScriptedSummary (const char *python_function_name, + lldb::ValueObjectSP valobj, + lldb::ScriptInterpreterObjectSP& callee_wrapper_sp, + std::string& retval) +{ - if (!script_interpreter) - return "<no python>"; + Timer scoped_timer (__PRETTY_FUNCTION__, __PRETTY_FUNCTION__); - std::string ret_val; + if (!valobj.get()) + { + retval.assign("<no object>"); + return false; + } + + void* old_callee = (callee_wrapper_sp ? callee_wrapper_sp->GetObject() : NULL); + void* new_callee = old_callee; + bool ret_val; if (python_function_name && *python_function_name) { { - Locker py_lock(python_interpreter); - ret_val = g_swig_typescript_callback (python_function_name, - python_interpreter->m_dictionary_name.c_str(), - valobj); + Locker py_lock(this); + { + Timer scoped_timer ("g_swig_typescript_callback","g_swig_typescript_callback"); + ret_val = g_swig_typescript_callback (python_function_name, + FindSessionDictionary(m_dictionary_name.c_str()), + valobj, + &new_callee, + retval); + } } } else - return "<no function name>"; + { + retval.assign("<no function name>"); + return false; + } + + if (new_callee && old_callee != new_callee) + callee_wrapper_sp = MakeScriptObject(new_callee); return ret_val; @@ -1822,6 +1922,12 @@ ScriptInterpreterPython::LoadScriptingModule (const char* pathname, } } +lldb::ScriptInterpreterObjectSP +ScriptInterpreterPython::MakeScriptObject (void* object) +{ + return lldb::ScriptInterpreterObjectSP(new ScriptInterpreterPythonObject(object)); +} + ScriptInterpreterPython::SynchronicityHandler::SynchronicityHandler (lldb::DebuggerSP debugger_sp, ScriptedCommandSynchronicity synchro) : m_debugger_sp(debugger_sp), @@ -1914,29 +2020,19 @@ ScriptInterpreterPython::GetDocumentationForItem(const char* item) } void -ScriptInterpreterPython::InitializeInterpreter (SWIGInitCallback python_swig_init_callback, - SWIGBreakpointCallbackFunction python_swig_breakpoint_callback, - SWIGPythonTypeScriptCallbackFunction python_swig_typescript_callback, - SWIGPythonCreateSyntheticProvider python_swig_synthetic_script, - SWIGPythonCalculateNumChildren python_swig_calc_children, - SWIGPythonGetChildAtIndex python_swig_get_child_index, - SWIGPythonGetIndexOfChildWithName python_swig_get_index_child, - SWIGPythonCastPyObjectToSBValue python_swig_cast_to_sbvalue, - SWIGPythonUpdateSynthProviderInstance python_swig_update_provider, - SWIGPythonCallCommand python_swig_call_command, - SWIGPythonCallModuleInit python_swig_call_mod_init) +ScriptInterpreterPython::InitializeInterpreter (SWIGInitCallback python_swig_init_callback) { g_swig_init_callback = python_swig_init_callback; - g_swig_breakpoint_callback = python_swig_breakpoint_callback; - g_swig_typescript_callback = python_swig_typescript_callback; - g_swig_synthetic_script = python_swig_synthetic_script; - g_swig_calc_children = python_swig_calc_children; - g_swig_get_child_index = python_swig_get_child_index; - g_swig_get_index_child = python_swig_get_index_child; - g_swig_cast_to_sbvalue = python_swig_cast_to_sbvalue; - g_swig_update_provider = python_swig_update_provider; - g_swig_call_command = python_swig_call_command; - g_swig_call_module_init = python_swig_call_mod_init; + g_swig_breakpoint_callback = LLDBSwigPythonBreakpointCallbackFunction; + g_swig_typescript_callback = LLDBSwigPythonCallTypeScript; + g_swig_synthetic_script = LLDBSwigPythonCreateSyntheticProvider; + g_swig_calc_children = LLDBSwigPython_CalculateNumChildren; + g_swig_get_child_index = LLDBSwigPython_GetChildAtIndex; + g_swig_get_index_child = LLDBSwigPython_GetIndexOfChildWithName; + g_swig_cast_to_sbvalue = LLDBSWIGPython_CastPyObjectToSBValue; + g_swig_update_provider = LLDBSwigPython_UpdateSynthProviderInstance; + g_swig_call_command = LLDBSwigPythonCallCommand; + g_swig_call_module_init = LLDBSwigPythonCallModuleInit; } void diff --git a/lldb/test/functionalities/data-formatter/data-formatter-objc/TestDataFormatterObjC.py b/lldb/test/functionalities/data-formatter/data-formatter-objc/TestDataFormatterObjC.py index f85d783fdb0..a6a442a71e9 100644 --- a/lldb/test/functionalities/data-formatter/data-formatter-objc/TestDataFormatterObjC.py +++ b/lldb/test/functionalities/data-formatter/data-formatter-objc/TestDataFormatterObjC.py @@ -17,6 +17,7 @@ class ObjCDataFormatterTestCase(TestBase): self.buildDsym() self.plain_data_formatter_commands() + @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") def test_plain_objc_with_dwarf_and_run_command(self): """Test basic ObjC formatting behavior.""" self.buildDwarf() @@ -28,6 +29,7 @@ class ObjCDataFormatterTestCase(TestBase): self.buildDsym() self.appkit_data_formatter_commands() + @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") def test_appkit_with_dwarf_and_run_command(self): """Test formatters for AppKit classes.""" self.buildDwarf() @@ -39,11 +41,24 @@ class ObjCDataFormatterTestCase(TestBase): self.buildDsym() self.cf_data_formatter_commands() + @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") def test_coreframeworks_with_dwarf_and_run_command(self): """Test formatters for Core OSX frameworks.""" self.buildDwarf() self.cf_data_formatter_commands() + @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") + def test_kvo_with_dsym_and_run_command(self): + """Test the behavior of formatters when KVO is in use.""" + self.buildDsym() + self.kvo_data_formatter_commands() + + @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") + def test_kvo_with_dwarf_and_run_command(self): + """Test the behavior of formatters when KVO is in use.""" + self.buildDwarf() + self.kvo_data_formatter_commands() + def setUp(self): # Call super's setUp(). TestBase.setUp(self) @@ -153,7 +168,6 @@ class ObjCDataFormatterTestCase(TestBase): self.runCmd('type category disable CoreServices', check=False) self.runCmd('type category disable AppKit', check=False) - # Execute the cleanup function during test case tear down. self.addTearDownHook(cleanup) @@ -189,13 +203,13 @@ class ObjCDataFormatterTestCase(TestBase): '(NSString *) str12 = ',' @"Process Name: a.out Process Id:']) self.expect('frame variable newArray newDictionary newMutableDictionary cfdict_ref mutable_dict_ref cfarray_ref mutable_array_ref', - substrs = ['(NSArray *) newArray = ',' size=50', + substrs = ['(NSArray *) newArray = ',' 50 objects', '(NSDictionary *) newDictionary = ',' 12 key/value pairs', '(NSDictionary *) newMutableDictionary = ',' 21 key/value pairs', '(CFDictionaryRef) cfdict_ref = ',' 3 key/value pairs', '(CFMutableDictionaryRef) mutable_dict_ref = ',' 12 key/value pairs', - '(CFArrayRef) cfarray_ref = ',' size=3', - '(CFMutableArrayRef) mutable_array_ref = ',' size=11']) + '(CFArrayRef) cfarray_ref = ',' 3 objects', + '(CFMutableArrayRef) mutable_array_ref = ',' 11 objects']) self.expect('frame variable attrString mutableAttrString mutableGetConst', substrs = ['(NSAttributedString *) attrString = ',' @"hello world from foo"', @@ -278,6 +292,7 @@ class ObjCDataFormatterTestCase(TestBase): self.runCmd('type category disable CoreGraphics', check=False) self.runCmd('type category disable CoreServices', check=False) self.runCmd('type category disable AppKit', check=False) + self.runCmd('log timers disable', check=False) # Execute the cleanup function during test case tear down. @@ -287,6 +302,8 @@ class ObjCDataFormatterTestCase(TestBase): self.runCmd('type category enable CoreFoundation') self.runCmd('type category enable CoreGraphics') self.runCmd('type category enable CoreServices') + self.runCmd("type category enable AppKit") + self.runCmd("log timers enable") self.expect("frame variable", substrs = ['(CFGregorianUnits) cf_greg_units = 1 years, 3 months, 5 days, 12 hours, 5 minutes 7 seconds', '(CFRange) cf_range = location=4 length=4', @@ -307,8 +324,59 @@ class ObjCDataFormatterTestCase(TestBase): '(Point) point = (v=7, h=12)', '(Point *) point_ptr = (v=7, h=12)', '(HIPoint) hi_point = (x=7, y=12)', - '(HIRect) hi_rect = origin=(x=3, y=5) size=(width=4, height=6)']) + '(HIRect) hi_rect = origin=(x=3, y=5) size=(width=4, height=6)', + '@"TheGuyWhoHasNoName" @"cuz it\'s funny"']) + self.runCmd('log timers dump') + + + def kvo_data_formatter_commands(self): + """Test the behavior of formatters when KVO is in use.""" + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + self.expect("breakpoint set -f main.m -l %d" % self.line, + BREAKPOINT_CREATED, + startstr = "Breakpoint created: 1: file ='main.m', line = %d, locations = 1" % + self.line) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + self.runCmd('type synth clear', check=False) + self.runCmd('type category disable CoreFoundation', check=False) + self.runCmd('type category disable CoreGraphics', check=False) + self.runCmd('type category disable CoreServices', check=False) + self.runCmd('type category disable AppKit', check=False) + + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + # check formatters for common Objective-C types + self.runCmd('type category enable AppKit') + + # as long as KVO is implemented by subclassing, this test should succeed + # we should be able to dynamically figure out that the KVO implementor class + # is a subclass of Molecule, and use the appropriate summary for it + self.runCmd("type summary add -s JustAMoleculeHere Molecule") + self.expect('frame variable molecule', substrs = ['JustAMoleculeHere']) + self.runCmd("next") + self.expect("thread list", + substrs = ['stopped', + 'step over']) + self.expect('frame variable molecule', substrs = ['JustAMoleculeHere']) + self.runCmd("next") + # check that NSMutableDictionary's formatter is not confused when dealing with a KVO'd dictionary + self.expect('frame variable newMutableDictionary', substrs = ['(NSDictionary *) newMutableDictionary = ',' 21 key/value pairs']) if __name__ == '__main__': import atexit diff --git a/lldb/test/functionalities/data-formatter/data-formatter-objc/main.m b/lldb/test/functionalities/data-formatter/data-formatter-objc/main.m index 45fc7c9be9b..20c41229e55 100644 --- a/lldb/test/functionalities/data-formatter/data-formatter-objc/main.m +++ b/lldb/test/functionalities/data-formatter/data-formatter-objc/main.m @@ -66,6 +66,70 @@ @end +@interface Atom : NSObject { + float mass; +} +-(void)setMass:(float)newMass; +-(float)mass; +@end + +@interface Molecule : NSObject { + NSArray *atoms; +} +-(void)setAtoms:(NSArray *)newAtoms; +-(NSArray *)atoms; +@end + +@implementation Atom + +-(void)setMass:(float)newMass +{ + mass = newMass; +} +-(float)mass +{ + return mass; +} + +@end + +@implementation Molecule + +-(void)setAtoms:(NSArray *)newAtoms +{ + atoms = newAtoms; +} +-(NSArray *)atoms +{ + return atoms; +} +@end + +@interface My_KVO_Observer : NSObject +-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change + context:(void *)context; +- (id) init; +- (void) dealloc; +@end + +@implementation My_KVO_Observer +-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change + context:(void *)context { + // we do not really care about KVO'ing - do nothing + return; +} +- (id) init +{ + self = [super init]; + return self; +} + +- (void) dealloc +{ + [super dealloc]; +} +@end + int main (int argc, const char * argv[]) { @@ -479,7 +543,16 @@ int main (int argc, const char * argv[]) HIPoint hi_point = {7,12}; HIRect hi_rect = {{3,5},{4,6}}; + Molecule *molecule = [Molecule new]; + // Set break point at this line. + + [molecule addObserver:[My_KVO_Observer new] forKeyPath:@"atoms" options:0 context:NULL]; + [newMutableDictionary addObserver:[My_KVO_Observer new] forKeyPath:@"weirdKeyToKVO" options:NSKeyValueObservingOptionNew context:NULL]; + + [molecule setAtoms:nil]; + [molecule setAtoms:[NSMutableArray new]]; + [pool drain]; return 0; } |