This commit:

a) adds a Python summary provider for NSDate
 b) changes the initialization for ScriptInterpreter so that we are not passing a bulk of Python-specific function pointers around
 c) provides a new ScriptInterpreterObject class that allows for ref-count safe wrapping of scripting objects on the C++ side
 d) contains much needed performance improvements:
    1) the pointer to the Python function generating a scripted summary is now cached instead of looked up every time
    2) redundant memory reads in the Python ObjC runtime wrapper are eliminated
    3) summaries now use the m_summary_str in ValueObject to store their data instead of passing around ( == copying) an std::string object
 e) contains other minor fixes, such as adding descriptive error messages for some cases of summary generation failure

llvm-svn: 151703
This commit is contained in:
Enrico Granata 2012-02-29 03:28:49 +00:00
parent 3203f6b9da
commit 7bc0ec3aad
30 changed files with 871 additions and 456 deletions

View file

@ -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):

View file

@ -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'

View file

@ -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'

View file

@ -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 ''

View file

@ -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

View file

@ -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()

View file

@ -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()

View file

@ -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")

View file

@ -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()

View file

@ -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()

View file

@ -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()

View file

@ -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()

View file

@ -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 ''

View file

@ -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()

View file

@ -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,

View file

@ -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()

View file

@ -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();

View file

@ -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 ();

View file

@ -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:

View file

@ -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

View file

@ -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 ]

View file

@ -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*

View file

@ -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
}
}

View file

@ -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

View file

@ -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;

View file

@ -674,7 +674,8 @@ ValueObject::GetSummaryAsCString ()
if (summary_format)
{
m_summary_str = summary_format->FormatObject(GetSP());
summary_format->FormatObject(GetSP(),
m_summary_str);
}
else
{

View file

@ -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
}

View file

@ -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;
ConstString dict(dict_name);
std::map<ConstString,PyObject*>::iterator iter = g_dict_map.find(dict);
if (iter != g_dict_map.end())
return iter->second;
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 (!python_function_name || !(*python_function_name))
return "<no function>";
Timer scoped_timer (__PRETTY_FUNCTION__, __PRETTY_FUNCTION__);
if (!valobj.get())
return "<no object>";
{
retval.assign("<no object>");
return false;
}
ExecutionContext exe_ctx (valobj->GetExecutionContextRef());
Target *target = exe_ctx.GetTargetPtr();
if (!target)
return "<no target>";
Debugger &debugger = target->GetDebugger();
ScriptInterpreter *script_interpreter = debugger.GetCommandInterpreter().GetScriptInterpreter();
ScriptInterpreterPython *python_interpreter = (ScriptInterpreterPython *) script_interpreter;
if (!script_interpreter)
return "<no python>";
std::string ret_val;
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

View file

@ -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,9 +324,60 @@ 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
lldb.SBDebugger.Initialize()

View file

@ -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;
}