summaryrefslogtreecommitdiffstats
path: root/lldb/examples/summaries/cocoa
diff options
context:
space:
mode:
Diffstat (limited to 'lldb/examples/summaries/cocoa')
-rw-r--r--lldb/examples/summaries/cocoa/CFArray.py321
-rw-r--r--lldb/examples/summaries/cocoa/CFBag.py131
-rw-r--r--lldb/examples/summaries/cocoa/CFBinaryHeap.py128
-rw-r--r--lldb/examples/summaries/cocoa/CFDictionary.py208
-rw-r--r--lldb/examples/summaries/cocoa/CFString.py302
-rw-r--r--lldb/examples/summaries/cocoa/NSBundle.py125
-rw-r--r--lldb/examples/summaries/cocoa/NSData.py118
-rw-r--r--lldb/examples/summaries/cocoa/NSException.py108
-rw-r--r--lldb/examples/summaries/cocoa/NSMachPort.py109
-rw-r--r--lldb/examples/summaries/cocoa/NSNotification.py104
-rw-r--r--lldb/examples/summaries/cocoa/NSNumber.py217
-rw-r--r--lldb/examples/summaries/cocoa/NSSet.py211
-rw-r--r--lldb/examples/summaries/cocoa/NSURL.py124
-rw-r--r--lldb/examples/summaries/cocoa/cache.py28
-rw-r--r--lldb/examples/summaries/cocoa/metrics.py62
-rw-r--r--lldb/examples/summaries/cocoa/objc_lldb.py132
-rw-r--r--lldb/examples/summaries/cocoa/objc_runtime.py500
17 files changed, 2928 insertions, 0 deletions
diff --git a/lldb/examples/summaries/cocoa/CFArray.py b/lldb/examples/summaries/cocoa/CFArray.py
new file mode 100644
index 00000000000..648737f7def
--- /dev/null
+++ b/lldb/examples/summaries/cocoa/CFArray.py
@@ -0,0 +1,321 @@
+# synthetic children provider for NSArray
+import lldb
+import ctypes
+import objc_runtime
+import metrics
+
+statistics = metrics.Metrics()
+statistics.add_metric('invalid_isa')
+statistics.add_metric('invalid_pointer')
+statistics.add_metric('unknown_class')
+statistics.add_metric('code_notrun')
+
+# much less functional than the other two cases below
+# just runs code to get to the count and then returns
+# no children
+class NSArrayKVC_SynthProvider:
+
+ def adjust_for_architecture(self):
+ self.lp64 = (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, dict):
+ self.valobj = valobj;
+ self.update()
+
+ def update(self):
+ self.adjust_for_architecture();
+ self.id_type = self.valobj.GetType().GetBasicType(lldb.eBasicTypeObjCID)
+
+ def num_children(self):
+ stream = lldb.SBStream()
+ self.valobj.GetExpressionPath(stream)
+ num_children_vo = self.valobj.CreateValueFromExpression("count","(int)[" + stream.GetData() + " count]");
+ return num_children_vo.GetValueAsUnsigned(0)
+
+ def get_child_index(self,name):
+ if name == "len":
+ return self.num_children();
+ else:
+ return None
+
+ def get_child_at_index(self, index):
+ return None
+
+
+
+# much less functional than the other two cases below
+# just runs code to get to the count and then returns
+# no children
+class NSArrayCF_SynthProvider:
+
+ def adjust_for_architecture(self):
+ self.lp64 = (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()
+
+ # CFRuntimeBase is defined as having an additional
+ # 4 bytes (padding?) on LP64 architectures
+ # 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:
+ return 8+4+4;
+ else:
+ return 4+4;
+
+ def __init__(self, valobj, dict):
+ self.valobj = valobj;
+ self.update()
+
+ def update(self):
+ self.adjust_for_architecture();
+ self.id_type = self.valobj.GetType().GetBasicType(lldb.eBasicTypeObjCID)
+
+ def num_children(self):
+ num_children_vo = self.valobj.CreateChildAtOffset("count",
+ self.cfruntime_size,
+ self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong))
+ return num_children_vo.GetValueAsUnsigned(0)
+
+ def get_child_index(self,name):
+ if name == "len":
+ return self.num_children();
+ else:
+ return None
+
+ def get_child_at_index(self, index):
+ return None
+
+
+class NSArrayI_SynthProvider:
+
+ def adjust_for_architecture(self):
+ self.lp64 = (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, dict):
+ self.valobj = valobj;
+ self.update()
+
+ def update(self):
+ self.adjust_for_architecture();
+ self.id_type = self.valobj.GetType().GetBasicType(lldb.eBasicTypeObjCID)
+
+ # skip the isa pointer and get at the size
+ def num_children(self):
+ offset = self.pointer_size;
+ datatype = self.valobj.GetType().GetBasicType(lldb.eBasicTypeLong)
+ count = self.valobj.CreateChildAtOffset("count",
+ offset,
+ datatype);
+ return int(count.GetValue(), 0)
+
+ def get_child_index(self,name):
+ if name == "len":
+ return self.num_children();
+ else:
+ return int(name.lstrip('[').rstrip(']'), 0)
+
+ def get_child_at_index(self, index):
+ if index == self.num_children():
+ return self.valobj.CreateValueFromExpression("len",
+ str(index))
+ offset = 2 * self.pointer_size + self.id_type.GetByteSize()*index
+ return self.valobj.CreateChildAtOffset('[' + str(index) + ']',
+ offset,
+ self.id_type)
+
+
+class NSArrayM_SynthProvider:
+
+ def adjust_for_architecture(self):
+ self.lp64 = (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, dict):
+ self.valobj = valobj;
+ self.update();
+
+ def update(self):
+ self.adjust_for_architecture();
+ self.id_type = self.valobj.GetType().GetBasicType(lldb.eBasicTypeObjCID)
+
+ # skip the isa pointer and get at the size
+ def num_children(self):
+ offset = self.pointer_size;
+ datatype = self.valobj.GetType().GetBasicType(lldb.eBasicTypeLong)
+ count = self.valobj.CreateChildAtOffset("count",
+ offset,
+ datatype);
+ return int(count.GetValue(), 0)
+
+ def get_child_index(self,name):
+ if name == "len":
+ return self.num_children();
+ else:
+ return int(name.lstrip('[').rstrip(']'), 0)
+
+ def data_offset(self):
+ offset = self.pointer_size; # isa
+ offset += self.pointer_size; # _used
+ offset += self.pointer_size; # _doHardRetain, _doWeakAccess, _size
+ offset += self.pointer_size; # _hasObjects, _hasStrongReferences, _offset
+ offset += self.pointer_size; # _mutations
+ return offset;
+
+ # the _offset field is used to calculate the actual offset
+ # when reading a value out of the array. we need to read it
+ # to do so we read a whole pointer_size of data from the
+ # right spot, and then zero out the two LSB
+ def read_offset_field(self):
+ disp = self.pointer_size; # isa
+ disp += self.pointer_size; # _used
+ disp += self.pointer_size; # _doHardRetain, _doWeakAccess, _size
+ offset = self.valobj.CreateChildAtOffset("offset",
+ disp,
+ self.valobj.GetType().GetBasicType(lldb.eBasicTypeLong))
+ offset_value = int(offset.GetValue(), 0)
+ offset_value = ctypes.c_uint32((offset_value & 0xFFFFFFFC) >> 2).value
+ return offset_value
+
+ # the _used field tells how many items are in the array
+ # but since this is a mutable array, it allocates more space
+ # for performance reasons. we need to get the real _size of
+ # the array to calculate the actual offset of each element
+ # in get_child_at_index() (see NSArray.m for details)
+ def read_size_field(self):
+ disp = self.pointer_size; # isa
+ disp += self.pointer_size; # _used
+ size = self.valobj.CreateChildAtOffset("size",
+ disp,
+ self.valobj.GetType().GetBasicType(lldb.eBasicTypeLong))
+ size_value = int(size.GetValue(), 0)
+ size_value = ctypes.c_uint32((size_value & 0xFFFFFFFA) >> 2).value
+ return size_value
+
+ def get_child_at_index(self, index):
+ if index == self.num_children():
+ return self.valobj.CreateValueFromExpression("len",
+ str(index))
+ size = self.read_size_field()
+ offset = self.read_offset_field()
+ phys_idx = offset + index
+ if size <= phys_idx:
+ phys_idx -=size;
+ # we still need to multiply by element size to do a correct pointer read
+ phys_idx *= self.id_type.GetByteSize()
+ list_ptr = self.valobj.CreateChildAtOffset("_list",
+ self.data_offset(),
+ self.id_type.GetBasicType(lldb.eBasicTypeUnsignedLongLong))
+ list_addr = int(list_ptr.GetValue(), 0)
+ return self.valobj.CreateValueFromAddress('[' + str(index) + ']',
+ list_addr + phys_idx,
+ self.id_type)
+
+# this is the actual synth provider, but is just a wrapper that checks
+# whether valobj is an instance of __NSArrayI or __NSArrayM and sets up an
+# appropriate backend layer to do the computations
+class NSArray_SynthProvider:
+
+ def adjust_for_architecture(self):
+ self.lp64 = (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)
+
+ def __init__(self, valobj, dict):
+ self.valobj = valobj;
+ self.adjust_for_architecture()
+ self.wrapper = self.make_wrapper(valobj,dict)
+ self.invalid = (self.wrapper == None)
+
+ def get_child_at_index(self, index):
+ if self.wrapper == None:
+ return None;
+ return self.wrapper.get_child_at_index(index)
+
+ def get_child_index(self,name):
+ if self.wrapper == None:
+ return None;
+ return self.wrapper.get_child_index(name)
+
+ def num_children(self):
+ if self.wrapper == None:
+ return 0;
+ return self.wrapper.num_children()
+
+ def update(self):
+ if self.wrapper == None:
+ return None;
+ return self.wrapper.update()
+
+ def read_ascii(self, pointer):
+ process = self.valobj.GetTarget().GetProcess()
+ error = lldb.SBError()
+ pystr = ''
+ # cannot do the read at once because there is no length byte
+ while True:
+ content = process.ReadMemory(pointer, 1, error)
+ new_bytes = bytearray(content)
+ b0 = new_bytes[0]
+ pointer = pointer + 1
+ if b0 == 0:
+ break
+ pystr = pystr + chr(b0)
+ return pystr
+
+ # this code acts as our defense against NULL and unitialized
+ # NSArray pointers, which makes it much longer than it would be otherwise
+ def make_wrapper(self,valobj,dict):
+ 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 == '__NSArrayI':
+ wrapper = NSArrayI_SynthProvider(valobj, dict)
+ statistics.metric_hit('code_notrun',valobj)
+ elif name_string == '__NSArrayM':
+ wrapper = NSArrayM_SynthProvider(valobj, dict)
+ statistics.metric_hit('code_notrun',valobj)
+ elif name_string == '__NSCFArray':
+ wrapper = NSArrayCF_SynthProvider(valobj, dict)
+ statistics.metric_hit('code_notrun',valobj)
+ else:
+ wrapper = NSArrayKVC_SynthProvider(valobj, dict)
+ statistics.metric_hit('unknown_class',str(valobj) + " seen as " + name_string)
+ return wrapper;
+
+def CFArray_SummaryProvider (valobj,dict):
+ provider = NSArray_SynthProvider(valobj,dict);
+ if provider.invalid == False:
+ try:
+ summary = str(provider.num_children());
+ except:
+ summary = None
+ if summary == None:
+ summary = 'no valid array here'
+ return 'size='+summary
+ return ''
+
+def __lldb_init_module(debugger,dict):
+ debugger.HandleCommand("type summary add -F CFArray.CFArray_SummaryProvider NSArray CFArrayRef CFMutableArrayRef")
diff --git a/lldb/examples/summaries/cocoa/CFBag.py b/lldb/examples/summaries/cocoa/CFBag.py
new file mode 100644
index 00000000000..596114bd5df
--- /dev/null
+++ b/lldb/examples/summaries/cocoa/CFBag.py
@@ -0,0 +1,131 @@
+# summary provider for CFBag
+import lldb
+import ctypes
+import objc_runtime
+import metrics
+
+statistics = metrics.Metrics()
+statistics.add_metric('invalid_isa')
+statistics.add_metric('invalid_pointer')
+statistics.add_metric('unknown_class')
+statistics.add_metric('code_notrun')
+
+# despite the similary to synthetic children providers, these classes are not
+# trying to provide anything but the length for an CFBag, so they need not
+# 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_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)
+ if self.lp64:
+ self.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong)
+ else:
+ self.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt)
+
+ # 12 bytes on i386
+ # 20 bytes on x64
+ # most probably 2 pointers and 4 bytes of data
+ def offset(self):
+ if self.lp64:
+ return 20
+ else:
+ return 12
+
+ def length(self):
+ size = self.valobj.CreateChildAtOffset("count",
+ self.offset(),
+ self.NSUInteger)
+ return size.GetValueAsUnsigned(0)
+
+
+class CFBagUnknown_SummaryProvider:
+ def adjust_for_architecture(self):
+ self.lp64 = (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 length(self):
+ stream = lldb.SBStream()
+ self.valobj.GetExpressionPath(stream)
+ num_children_vo = self.valobj.CreateValueFromExpression("count","(int)CFBagGetCount(" + stream.GetData() + " )");
+ return num_children_vo.GetValueAsUnsigned(0)
+
+
+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()
+ actual_name = name_string
+ if name_string == '__NSCFType':
+ # CFBag does not expose an actual NSWrapper type, so we have to check that this is
+ # an NSCFType and then check we are a pointer-to __CFBag
+ valobj_type = valobj.GetType()
+ if valobj_type.IsValid() and valobj_type.IsPointerType():
+ pointee_type = valobj_type.GetPointeeType()
+ actual_name = pointee_type.GetName()
+ if actual_name == '__CFBag' or \
+ actual_name == 'const struct __CFBag':
+ wrapper = CFBagRef_SummaryProvider(valobj)
+ statistics.metric_hit('code_notrun',valobj)
+ return wrapper
+ wrapper = CFBagUnknown_SummaryProvider(valobj)
+ statistics.metric_hit('unknown_class',str(valobj) + " seen as " + actual_name)
+ return wrapper;
+
+def CFBag_SummaryProvider (valobj,dict):
+ provider = GetSummary_Impl(valobj);
+ if provider != None:
+ try:
+ summary = provider.length();
+ except:
+ summary = None
+ # for some reason, one needs to clear some bits for the count
+ # to be correct when using CF(Mutable)BagRef on x64
+ # the bit mask was derived through experimentation
+ # (if counts start looking weird, then most probably
+ # the mask needs to be changed)
+ if summary == None:
+ summary = 'no valid set here'
+ else:
+ if provider.lp64:
+ summary = summary & ~0x1fff000000000000
+ if summary == 1:
+ return '1 item'
+ return str(summary) + " items"
+ return ''
+
+def __lldb_init_module(debugger,dict):
+ debugger.HandleCommand("type summary add -F CFBag.CFBag_SummaryProvider CFBagRef CFMutableBagRef")
diff --git a/lldb/examples/summaries/cocoa/CFBinaryHeap.py b/lldb/examples/summaries/cocoa/CFBinaryHeap.py
new file mode 100644
index 00000000000..d0542abfcc7
--- /dev/null
+++ b/lldb/examples/summaries/cocoa/CFBinaryHeap.py
@@ -0,0 +1,128 @@
+# summary provider for CFBinaryHeap
+import lldb
+import ctypes
+import objc_runtime
+import metrics
+
+statistics = metrics.Metrics()
+statistics.add_metric('invalid_isa')
+statistics.add_metric('invalid_pointer')
+statistics.add_metric('unknown_class')
+statistics.add_metric('code_notrun')
+
+# despite the similary to synthetic children providers, these classes are not
+# trying to provide anything but the length for an CFBinaryHeap, so they need not
+# 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_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)
+ if self.lp64:
+ self.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong)
+ else:
+ self.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt)
+
+ # 8 bytes on i386
+ # 16 bytes on x64
+ # most probably 2 pointers
+ def offset(self):
+ if self.lp64:
+ return 16
+ else:
+ return 8
+
+ def length(self):
+ size = self.valobj.CreateChildAtOffset("count",
+ self.offset(),
+ self.NSUInteger)
+ return size.GetValueAsUnsigned(0)
+
+
+class CFBinaryHeapUnknown_SummaryProvider:
+ def adjust_for_architecture(self):
+ self.lp64 = (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 length(self):
+ stream = lldb.SBStream()
+ self.valobj.GetExpressionPath(stream)
+ num_children_vo = self.valobj.CreateValueFromExpression("count","(int)CFBinaryHeapGetCount(" + stream.GetData() + " )");
+ return num_children_vo.GetValueAsUnsigned(0)
+
+
+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 == '__NSCFType':
+ # CFBinaryHeap does not expose an actual NSWrapper type, so we have to check that this is
+ # an NSCFType and then check we are a pointer-to CFBinaryHeap
+ valobj_type = valobj.GetType()
+ if valobj_type.IsValid() and valobj_type.IsPointerType():
+ pointee_type = valobj_type.GetPointeeType()
+ if pointee_type.GetName() == '__CFBinaryHeap':
+ wrapper = CFBinaryHeapRef_SummaryProvider(valobj)
+ statistics.metric_hit('code_notrun',valobj)
+ return wrapper
+ wrapper = CFBinaryHeapUnknown_SummaryProvider(valobj)
+ statistics.metric_hit('unknown_class',str(valobj) + " seen as " + name_string)
+ return wrapper;
+
+def CFBinaryHeap_SummaryProvider (valobj,dict):
+ provider = GetSummary_Impl(valobj);
+ if provider != None:
+ try:
+ summary = provider.length();
+ except:
+ summary = None
+ # for some reason, one needs to clear some bits for the count
+ # to be correct when using CF(Mutable)BagRef on x64
+ # the bit mask was derived through experimentation
+ # (if counts start looking weird, then most probably
+ # the mask needs to be changed)
+ if summary == None:
+ summary = 'no valid set here'
+ else:
+ if provider.lp64:
+ summary = summary & ~0x1fff000000000000
+ if summary == 1:
+ return '1 item'
+ return str(summary) + " items"
+ return ''
+
+def __lldb_init_module(debugger,dict):
+ debugger.HandleCommand("type summary add -F CFBinaryHeap.CFBinaryHeap_SummaryProvider CFBinaryHeapRef")
diff --git a/lldb/examples/summaries/cocoa/CFDictionary.py b/lldb/examples/summaries/cocoa/CFDictionary.py
new file mode 100644
index 00000000000..944e05aca59
--- /dev/null
+++ b/lldb/examples/summaries/cocoa/CFDictionary.py
@@ -0,0 +1,208 @@
+# summary provider for NSDictionary
+import lldb
+import ctypes
+import objc_runtime
+import metrics
+
+statistics = metrics.Metrics()
+statistics.add_metric('invalid_isa')
+statistics.add_metric('invalid_pointer')
+statistics.add_metric('unknown_class')
+statistics.add_metric('code_notrun')
+
+# despite the similary to synthetic children providers, these classes are not
+# trying to provide anything but the count for an NSDictionary, so they need not
+# 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_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)
+ if self.lp64:
+ self.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong)
+ else:
+ self.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt)
+
+ # empirically determined on both 32 and 64bit desktop Mac OS X
+ # probably boils down to 2 pointers and 4 bytes of data, but
+ # the description of __CFDictionary is not readily available so most
+ # of this is guesswork, plain and simple
+ def offset(self):
+ if self.lp64:
+ return 20
+ else:
+ return 12
+
+ def num_children(self):
+ num_children_vo = self.valobj.CreateChildAtOffset("count",
+ self.offset(),
+ self.NSUInteger)
+ return num_children_vo.GetValueAsUnsigned(0)
+
+
+class NSDictionaryI_SummaryProvider:
+ def adjust_for_architecture(self):
+ self.lp64 = (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)
+ if self.lp64:
+ 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:
+ return 8
+ else:
+ return 4
+
+ def num_children(self):
+ num_children_vo = self.valobj.CreateChildAtOffset("count",
+ self.offset(),
+ 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
+ else:
+ value = value & ~0xFF000000
+ return value
+
+class NSDictionaryM_SummaryProvider:
+ def adjust_for_architecture(self):
+ self.lp64 = (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)
+ if self.lp64:
+ 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:
+ return 8
+ else:
+ return 4
+
+ def num_children(self):
+ num_children_vo = self.valobj.CreateChildAtOffset("count",
+ self.offset(),
+ self.NSUInteger)
+ return num_children_vo.GetValueAsUnsigned(0)
+
+
+class NSDictionaryUnknown_SummaryProvider:
+ def adjust_for_architecture(self):
+ self.lp64 = (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 num_children(self):
+ stream = lldb.SBStream()
+ self.valobj.GetExpressionPath(stream)
+ num_children_vo = self.valobj.CreateValueFromExpression("count","(int)[" + stream.GetData() + " count]");
+ return num_children_vo.GetValueAsUnsigned(0)
+
+
+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 == '__NSCFDictionary':
+ wrapper = NSCFDictionary_SummaryProvider(valobj)
+ statistics.metric_hit('code_notrun',valobj)
+ elif name_string == '__NSDictionaryI':
+ wrapper = NSDictionaryI_SummaryProvider(valobj)
+ statistics.metric_hit('code_notrun',valobj)
+ elif name_string == '__NSDictionaryM':
+ wrapper = NSDictionaryM_SummaryProvider(valobj)
+ statistics.metric_hit('code_notrun',valobj)
+ else:
+ wrapper = NSDictionaryUnknown_SummaryProvider(valobj)
+ statistics.metric_hit('unknown_class',str(valobj) + " seen as " + name_string)
+ return wrapper;
+
+def CFDictionary_SummaryProvider (valobj,dict):
+ provider = GetSummary_Impl(valobj);
+ if provider != None:
+ try:
+ summary = str(provider.num_children());
+ except:
+ summary = None
+ if summary == None:
+ summary = 'no valid dictionary here'
+ return summary + " key/value pairs"
+ return ''
+
+def CFDictionary_SummaryProvider2 (valobj,dict):
+ provider = GetSummary_Impl(valobj);
+ if provider != None:
+ try:
+ summary = (provider.num_children());
+ except:
+ summary = None
+ if summary == None:
+ summary = 'no valid dictionary here'
+ # needed on OSX Mountain Lion
+ elif provider.lp64:
+ summary = int(summary) & ~0x0f1f000000000000
+ return str(summary) + " key/value pairs"
+ return ''
+
+def __lldb_init_module(debugger,dict):
+ debugger.HandleCommand("type summary add -F CFDictionary.CFDictionary_SummaryProvider NSDictionary")
+ debugger.HandleCommand("type summary add -F CFDictionary.CFDictionary_SummaryProvider2 CFDictionaryRef CFMutableDictionaryRef")
diff --git a/lldb/examples/summaries/cocoa/CFString.py b/lldb/examples/summaries/cocoa/CFString.py
new file mode 100644
index 00000000000..478f972e529
--- /dev/null
+++ b/lldb/examples/summaries/cocoa/CFString.py
@@ -0,0 +1,302 @@
+# synthetic children and summary provider for CFString
+# (and related NSString class)
+import lldb
+
+def CFString_SummaryProvider (valobj,dict):
+ provider = CFStringSynthProvider(valobj,dict);
+ if provider.invalid == False:
+ try:
+ summary = provider.get_child_at_index(provider.get_child_index("content")).GetSummary();
+ except:
+ summary = None
+ if summary == None:
+ summary = 'no valid string here'
+ return '@'+summary
+ return ''
+
+def CFAttributedString_SummaryProvider (valobj,dict):
+ offset = valobj.GetTarget().GetProcess().GetAddressByteSize()
+ pointee = valobj.GetValueAsUnsigned(0)
+ summary = 'no valid string here'
+ if pointee != None and pointee != 0:
+ pointee = pointee + offset
+ child_ptr = valobj.CreateValueFromAddress("string_ptr",pointee,valobj.GetType())
+ child = child_ptr.CreateValueFromAddress("string_data",child_ptr.GetValueAsUnsigned(),valobj.GetType()).AddressOf()
+ provider = CFStringSynthProvider(child,dict);
+ if provider.invalid == False:
+ try:
+ summary = provider.get_child_at_index(provider.get_child_index("content")).GetSummary();
+ except:
+ summary = 'no valid string here'
+ if summary == None:
+ summary = 'no valid string here'
+ return '@'+summary
+
+
+def __lldb_init_module(debugger,dict):
+ debugger.HandleCommand("type summary add -F CFString.CFString_SummaryProvider NSString CFStringRef CFMutableStringRef")
+ debugger.HandleCommand("type summary add -F CFString.CFAttributedString_SummaryProvider NSAttributedString")
+
+class CFStringSynthProvider:
+ def __init__(self,valobj,dict):
+ self.valobj = valobj;
+ self.update()
+
+ # children other than "content" are for debugging only and must not be used in production code
+ def num_children(self):
+ if self.invalid:
+ return 0;
+ return 6;
+
+ def read_unicode(self, pointer):
+ process = self.valobj.GetTarget().GetProcess()
+ error = lldb.SBError()
+ pystr = u''
+ # cannot do the read at once because the length value has
+ # a weird encoding. better play it safe here
+ while True:
+ content = process.ReadMemory(pointer, 2, error)
+ new_bytes = bytearray(content)
+ b0 = new_bytes[0]
+ b1 = new_bytes[1]
+ pointer = pointer + 2
+ if b0 == 0 and b1 == 0:
+ break
+ # rearrange bytes depending on endianness
+ # (do we really need this or is Cocoa going to
+ # use Windows-compatible little-endian even
+ # if the target is big endian?)
+ if self.is_little:
+ value = b1 * 256 + b0
+ else:
+ value = b0 * 256 + b1
+ pystr = pystr + unichr(value)
+ return pystr
+
+ # handle the special case strings
+ # only use the custom code for the tested LP64 case
+ def handle_special(self):
+ if self.lp64 == False:
+ # for 32bit targets, use safe ObjC code
+ return self.handle_unicode_string_safe()
+ offset = 12
+ pointer = self.valobj.GetValueAsUnsigned(0) + offset
+ pystr = self.read_unicode(pointer)
+ return self.valobj.CreateValueFromExpression("content",
+ "(char*)\"" + pystr.encode('utf-8') + "\"")
+
+ # last resort call, use ObjC code to read; the final aim is to
+ # be able to strip this call away entirely and only do the read
+ # ourselves
+ def handle_unicode_string_safe(self):
+ return self.valobj.CreateValueFromExpression("content",
+ "(char*)\"" + self.valobj.GetObjectDescription() + "\"");
+
+ def handle_unicode_string(self):
+ # step 1: find offset
+ if self.inline:
+ pointer = self.valobj.GetValueAsUnsigned(0) + self.size_of_cfruntime_base();
+ if self.explicit == False:
+ # untested, use the safe code path
+ return self.handle_unicode_string_safe();
+ else:
+ # not sure why 8 bytes are skipped here
+ # (lldb) mem read -c 50 0x00000001001154f0
+ # 0x1001154f0: 98 1a 85 71 ff 7f 00 00 90 07 00 00 01 00 00 00 ...q?...........
+ # 0x100115500: 03 00 00 00 00 00 00 00 *c3 03 78 00 78 00 00 00 ........?.x.x...
+ # 0x100115510: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+ # 0x100115520: 00 00 ..
+ # content begins at * (i.e. 8 bytes into variants, skipping void* buffer in
+ # __notInlineImmutable1 entirely, while the length byte is correctly located
+ # for an inline string)
+ pointer = pointer + 8;
+ else:
+ pointer = self.valobj.GetValueAsUnsigned(0) + self.size_of_cfruntime_base();
+ # read 8 bytes here and make an address out of them
+ try:
+ vopointer = self.valobj.CreateChildAtOffset("dummy",
+ pointer,self.valobj.GetType().GetBasicType(lldb.eBasicTypeChar).GetPointerType());
+ pointer = vopointer.GetValueAsUnsigned(0)
+ except:
+ return self.valobj.CreateValueFromExpression("content",
+ '(char*)"@\"invalid NSString\""')
+ # step 2: read Unicode data at pointer
+ pystr = self.read_unicode(pointer)
+ # step 3: return it
+ return self.valobj.CreateValueFromExpression("content",
+ "(char*)\"" + pystr.encode('utf-8') + "\"")
+
+ def handle_inline_explicit(self):
+ if self.lp64:
+ offset = 24
+ else:
+ offset = 12
+ offset = offset + self.valobj.GetValueAsUnsigned(0)
+ return self.valobj.CreateValueFromExpression("content",
+ "(char*)(" + str(offset) + ")")
+
+ def handle_mutable_string(self):
+ if self.lp64:
+ offset = 16
+ else:
+ offset = 8
+ data = self.valobj.CreateChildAtOffset("content",
+ offset, self.valobj.GetType().GetBasicType(lldb.eBasicTypeChar).GetPointerType());
+ data_value = data.GetValueAsUnsigned(0)
+ data_value = data_value + 1
+ return self.valobj.CreateValueFromExpression("content", "(char*)(" + str(data_value) + ")")
+
+ def handle_UTF8_inline(self):
+ offset = self.valobj.GetValueAsUnsigned(0) + self.size_of_cfruntime_base();
+ if self.explicit == False:
+ offset = offset + 1;
+ return self.valobj.CreateValueFromAddress("content",
+ offset, self.valobj.GetType().GetBasicType(lldb.eBasicTypeChar)).AddressOf();
+
+ def handle_UTF8_not_inline(self):
+ offset = self.size_of_cfruntime_base();
+ return self.valobj.CreateChildAtOffset("content",
+ offset,self.valobj.GetType().GetBasicType(lldb.eBasicTypeChar).GetPointerType());
+
+ def get_child_at_index(self,index):
+ if index == 0:
+ return self.valobj.CreateValueFromExpression("mutable",
+ str(int(self.mutable)));
+ if index == 1:
+ return self.valobj.CreateValueFromExpression("inline",
+ str(int(self.inline)));
+ if index == 2:
+ return self.valobj.CreateValueFromExpression("explicit",
+ str(int(self.explicit)));
+ if index == 3:
+ return self.valobj.CreateValueFromExpression("unicode",
+ str(int(self.unicode)));
+ if index == 4:
+ return self.valobj.CreateValueFromExpression("special",
+ str(int(self.special)));
+ if index == 5:
+ # we are handling the several possible combinations of flags.
+ # for each known combination we have a function that knows how to
+ # go fetch the data from memory instead of running code. if a string is not
+ # correctly displayed, one should start by finding a combination of flags that
+ # makes it different from these known cases, and provide a new reader function
+ # if this is not possible, a new flag might have to be made up (like the "special" flag
+ # below, which is not a real flag in CFString), or alternatively one might need to use
+ # the ObjC runtime helper to detect the new class and deal with it accordingly
+ if self.mutable == True:
+ return self.handle_mutable_string()
+ elif self.inline == True and self.explicit == True and \
+ self.unicode == False and self.special == False and \
+ self.mutable == False:
+ return self.handle_inline_explicit()
+ elif self.unicode == True:
+ return self.handle_unicode_string();
+ elif self.special == True:
+ return self.handle_special();
+ elif self.inline == True:
+ return self.handle_UTF8_inline();
+ else:
+ return self.handle_UTF8_not_inline();
+
+ def get_child_index(self,name):
+ if name == "content":
+ return self.num_children() - 1;
+ if name == "mutable":
+ return 0;
+ if name == "inline":
+ return 1;
+ if name == "explicit":
+ return 2;
+ if name == "unicode":
+ return 3;
+ if name == "special":
+ return 4;
+
+ def is_64bit(self):
+ return self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8
+
+ def is_little_endian(self):
+ return self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle
+
+ # CFRuntimeBase is defined as having an additional
+ # 4 bytes (padding?) on LP64 architectures
+ # 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:
+ return 8+4+4;
+ else:
+ return 4+4;
+
+ # the info bits are part of the CFRuntimeBase structure
+ # to get at them we have to skip a uintptr_t and then get
+ # at the least-significant byte of a 4 byte array. If we are
+ # 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:
+ offset = 8;
+ else:
+ offset = 4;
+ if self.is_little == False:
+ offset = offset + 3;
+ return offset;
+
+ def read_info_bits(self):
+ cfinfo = self.valobj.CreateChildAtOffset("cfinfo",
+ self.offset_of_info_bits(),
+ self.valobj.GetType().GetBasicType(lldb.eBasicTypeChar));
+ cfinfo.SetFormat(11)
+ info = cfinfo.GetValue();
+ if info != None:
+ self.invalid = False;
+ return int(info,0);
+ else:
+ self.invalid = True;
+ return None;
+
+ # calculating internal flag bits of the CFString object
+ # this stuff is defined and discussed in CFString.c
+ def is_mutable(self):
+ return (self.info_bits & 1) == 1;
+
+ def is_inline(self):
+ return (self.info_bits & 0x60) == 0;
+
+ # this flag's name is ambiguous, it turns out
+ # we must skip a length byte to get at the data
+ # when this flag is False
+ def has_explicit_length(self):
+ return (self.info_bits & (1 | 4)) != 4;
+
+ # probably a subclass of NSString. obtained this from [str pathExtension]
+ # here info_bits = 0 and Unicode data at the start of the padding word
+ # in the long run using the isa value might be safer as a way to identify this
+ # instead of reading the info_bits
+ def is_special_case(self):
+ return self.info_bits == 0;
+
+ def is_unicode(self):
+ return (self.info_bits & 0x10) == 0x10;
+
+ # preparing ourselves to read into memory
+ # by adjusting architecture-specific info
+ def adjust_for_architecture(self):
+ self.lp64 = self.is_64bit();
+ self.is_little = self.is_little_endian();
+
+ # reading info bits out of the CFString and computing
+ # useful values to get at the real data
+ def compute_flags(self):
+ self.info_bits = self.read_info_bits();
+ if self.info_bits == None:
+ return;
+ self.mutable = self.is_mutable();
+ self.inline = self.is_inline();
+ self.explicit = self.has_explicit_length();
+ self.unicode = self.is_unicode();
+ self.special = self.is_special_case();
+
+ def update(self):
+ self.adjust_for_architecture();
+ self.compute_flags(); \ No newline at end of file
diff --git a/lldb/examples/summaries/cocoa/NSBundle.py b/lldb/examples/summaries/cocoa/NSBundle.py
new file mode 100644
index 00000000000..5e6b11a6fc0
--- /dev/null
+++ b/lldb/examples/summaries/cocoa/NSBundle.py
@@ -0,0 +1,125 @@
+# summary provider for NSBundle
+import lldb
+import ctypes
+import objc_runtime
+import metrics
+import NSURL
+
+statistics = metrics.Metrics()
+statistics.add_metric('invalid_isa')
+statistics.add_metric('invalid_pointer')
+statistics.add_metric('unknown_class')
+statistics.add_metric('code_notrun')
+
+# despite the similary to synthetic children providers, these classes are not
+# trying to provide anything but a summary for an NSURL, so they need not
+# 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_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)
+ if self.lp64:
+ self.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong)
+ self.pointer_size = 8
+ else:
+ self.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt)
+ self.pointer_size = 4
+ self.NSString = self.valobj.GetTarget().FindFirstType('NSString')
+ self.NSURL = self.valobj.GetTarget().FindFirstType('NSURL')
+
+ # we need to skip the ISA, plus four other values
+ # that are luckily each a pointer in size
+ # which makes our computation trivial :-)
+ def offset(self):
+ return 5 * self.pointer_size
+
+ def url_text(self):
+ global statistics
+ text = self.valobj.CreateChildAtOffset("text",
+ self.offset(),
+ self.NSString.GetPointerType())
+ my_string = text.GetSummary()
+ if (my_string == None) or (my_string == ''):
+ statistics.metric_hit('unknown_class',str(self.valobj) + " triggered unkown pointer location")
+ return NSBundleUnknown_SummaryProvider(self.valobj).url_text()
+ else:
+ statistics.metric_hit('code_notrun',self.valobj)
+ return my_string
+
+
+class NSBundleUnknown_SummaryProvider:
+ def adjust_for_architecture(self):
+ self.lp64 = (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 url_text(self):
+ stream = lldb.SBStream()
+ self.valobj.GetExpressionPath(stream)
+ expr = "(NSString*)[" + stream.GetData() + " bundlePath]"
+ url_text_vo = self.valobj.CreateValueFromExpression("path",expr);
+ return url_text_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 == 'NSBundle':
+ wrapper = NSBundleKnown_SummaryProvider(valobj)
+ # [NSBundle mainBundle] does return an object that is
+ # not correctly filled out for our purposes, so we still
+ # end up having to run code in that case
+ #statistics.metric_hit('code_notrun',valobj)
+ else:
+ wrapper = NSBundleUnknown_SummaryProvider(valobj)
+ statistics.metric_hit('unknown_class',str(valobj) + " seen as " + name_string)
+ return wrapper;
+
+def NSBundle_SummaryProvider (valobj,dict):
+ provider = GetSummary_Impl(valobj);
+ if provider != None:
+ try:
+ summary = provider.url_text();
+ except:
+ summary = None
+ if summary == None or summary == '':
+ summary = 'no valid NSBundle here'
+ return summary
+ return ''
+
+def __lldb_init_module(debugger,dict):
+ debugger.HandleCommand("type summary add -F NSBundle.NSBundle_SummaryProvider NSBundle")
diff --git a/lldb/examples/summaries/cocoa/NSData.py b/lldb/examples/summaries/cocoa/NSData.py
new file mode 100644
index 00000000000..362bc81cd8d
--- /dev/null
+++ b/lldb/examples/summaries/cocoa/NSData.py
@@ -0,0 +1,118 @@
+# summary provider for NSData
+import lldb
+import ctypes
+import objc_runtime
+import metrics
+
+statistics = metrics.Metrics()
+statistics.add_metric('invalid_isa')
+statistics.add_metric('invalid_pointer')
+statistics.add_metric('unknown_class')
+statistics.add_metric('code_notrun')
+
+# despite the similary to synthetic children providers, these classes are not
+# trying to provide anything but the length for an NSData, so they need not
+# 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_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)
+ if self.lp64:
+ self.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong)
+ else:
+ self.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt)
+
+ # one pointer is the ISA
+ # then there are 32 bit worth of flags and other data
+ # however, on 64bit systems these are padded to be a full
+ # machine word long, which means we actually have two pointers
+ # worth of data to skip
+ def offset(self):
+ if self.lp64:
+ return 16
+ else:
+ return 8
+
+ def length(self):
+ size = self.valobj.CreateChildAtOffset("count",
+ self.offset(),
+ self.NSUInteger)
+ return size.GetValueAsUnsigned(0)
+
+
+class NSDataUnknown_SummaryProvider:
+ def adjust_for_architecture(self):
+ self.lp64 = (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 length(self):
+ stream = lldb.SBStream()
+ self.valobj.GetExpressionPath(stream)
+ num_children_vo = self.valobj.CreateValueFromExpression("count","(int)[" + stream.GetData() + " length]");
+ return num_children_vo.GetValueAsUnsigned(0)
+
+
+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 == 'NSConcreteData' or \
+ name_string == 'NSConcreteMutableData' or \
+ name_string == '__NSCFData':
+ wrapper = NSConcreteData_SummaryProvider(valobj)
+ statistics.metric_hit('code_notrun',valobj)
+ else:
+ wrapper = NSDataUnknown_SummaryProvider(valobj)
+ statistics.metric_hit('unknown_class',str(valobj) + " seen as " + name_string)
+ return wrapper;
+
+def NSData_SummaryProvider (valobj,dict):
+ provider = GetSummary_Impl(valobj);
+ if provider != None:
+ try:
+ summary = provider.length();
+ except:
+ summary = None
+ if summary == None:
+ summary = 'no valid data here'
+ if summary == 1:
+ return '1 byte'
+ return str(summary) + " bytes"
+ return ''
+
+def __lldb_init_module(debugger,dict):
+ debugger.HandleCommand("type summary add -F NSData.NSData_SummaryProvider NSData CFDataRef CFMutableDataRef")
diff --git a/lldb/examples/summaries/cocoa/NSException.py b/lldb/examples/summaries/cocoa/NSException.py
new file mode 100644
index 00000000000..e5f0418eed9
--- /dev/null
+++ b/lldb/examples/summaries/cocoa/NSException.py
@@ -0,0 +1,108 @@
+# summary provider for class NSException
+import objc_runtime
+import metrics
+import CFString
+import lldb
+
+statistics = metrics.Metrics()
+statistics.add_metric('invalid_isa')
+statistics.add_metric('invalid_pointer')
+statistics.add_metric('unknown_class')
+statistics.add_metric('code_notrun')
+
+class NSKnownException_SummaryProvider:
+ def adjust_for_architecture(self):
+ self.lp64 = (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)
+
+ # skip the ISA and go to the name pointer
+ def offset(self):
+ if self.lp64:
+ return 8
+ else:
+ return 4
+
+ def description(self):
+ name_ptr = self.valobj.CreateChildAtOffset("name",
+ self.offset(),
+ self.id_type)
+ reason_ptr = self.valobj.CreateChildAtOffset("reason",
+ 2*self.offset(),
+ self.id_type)
+ return CFString.CFString_SummaryProvider(name_ptr,None) + " " + CFString.CFString_SummaryProvider(reason_ptr,None)
+
+
+class NSUnknownException_SummaryProvider:
+ def adjust_for_architecture(self):
+ self.lp64 = (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 description(self):
+ stream = lldb.SBStream()
+ self.valobj.GetExpressionPath(stream)
+ name_vo = self.valobj.CreateValueFromExpression("name","(NSString*)[" + stream.GetData() + " name]");
+ reason_vo = self.valobj.CreateValueFromExpression("reason","(NSString*)[" + stream.GetData() + " reason]");
+ return CFString.CFString_SummaryProvider(name_vo,None) + ' ' + CFString.CFString_SummaryProvider(reason_vo,None)
+
+
+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 == 'NSException':
+ wrapper = NSKnownException_SummaryProvider(valobj)
+ statistics.metric_hit('code_notrun',valobj)
+ else:
+ wrapper = NSUnknownException_SummaryProvider(valobj)
+ statistics.metric_hit('unknown_class',str(valobj) + " seen as " + name_string)
+ return wrapper;
+
+def NSException_SummaryProvider (valobj,dict):
+ provider = GetSummary_Impl(valobj);
+ if provider != None:
+ try:
+ summary = provider.description();
+ except:
+ summary = None
+ if summary == None:
+ summary = 'no valid exception here'
+ return str(summary)
+ return ''
+
+def __lldb_init_module(debugger,dict):
+ debugger.HandleCommand("type summary add -F NSException.NSException_SummaryProvider NSException")
diff --git a/lldb/examples/summaries/cocoa/NSMachPort.py b/lldb/examples/summaries/cocoa/NSMachPort.py
new file mode 100644
index 00000000000..ca714bc38ae
--- /dev/null
+++ b/lldb/examples/summaries/cocoa/NSMachPort.py
@@ -0,0 +1,109 @@
+# summary provider for NSData
+import lldb
+import ctypes
+import objc_runtime
+import metrics
+
+statistics = metrics.Metrics()
+statistics.add_metric('invalid_isa')
+statistics.add_metric('invalid_pointer')
+statistics.add_metric('unknown_class')
+statistics.add_metric('code_notrun')
+
+# despite the similary to synthetic children providers, these classes are not
+# trying to provide anything but the port number of an NSMachPort, so they need not
+# 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_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)
+
+ # one pointer is the ISA
+ # then we have one other internal pointer, plus
+ # 4 bytes worth of flags. hence, these values
+ def offset(self):
+ if self.lp64:
+ return 20
+ else:
+ return 12
+
+ def port(self):
+ vport = self.valobj.CreateChildAtOffset("port",
+ self.offset(),
+ self.NSUInteger)
+ return vport.GetValueAsUnsigned(0)
+
+
+class NSMachPortUnknown_SummaryProvider:
+ def adjust_for_architecture(self):
+ self.lp64 = (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 port(self):
+ stream = lldb.SBStream()
+ self.valobj.GetExpressionPath(stream)
+ num_children_vo = self.valobj.CreateValueFromExpression("port","(int)[" + stream.GetData() + " machPort]");
+ return num_children_vo.GetValueAsUnsigned(0)
+
+
+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 == 'NSMachPort':
+ wrapper = NSMachPortKnown_SummaryProvider(valobj)
+ statistics.metric_hit('code_notrun',valobj)
+ else:
+ wrapper = NSMachPortUnknown_SummaryProvider(valobj)
+ statistics.metric_hit('unknown_class',str(valobj) + " seen as " + name_string)
+ return wrapper;
+
+def NSMachPort_SummaryProvider (valobj,dict):
+ provider = GetSummary_Impl(valobj);
+ if provider != None:
+ try:
+ summary = provider.port();
+ except:
+ summary = None
+ if summary == None:
+ summary = 'no valid mach port here'
+ return 'mach port: ' + str(summary)
+ return ''
+
+def __lldb_init_module(debugger,dict):
+ debugger.HandleCommand("type summary add -F NSMachPort.NSMachPort_SummaryProvider NSMachPort")
diff --git a/lldb/examples/summaries/cocoa/NSNotification.py b/lldb/examples/summaries/cocoa/NSNotification.py
new file mode 100644
index 00000000000..6170110354c
--- /dev/null
+++ b/lldb/examples/summaries/cocoa/NSNotification.py
@@ -0,0 +1,104 @@
+# summary provider for class NSNotification
+import objc_runtime
+import metrics
+import CFString
+import lldb
+
+statistics = metrics.Metrics()
+statistics.add_metric('invalid_isa')
+statistics.add_metric('invalid_pointer')
+statistics.add_metric('unknown_class')
+statistics.add_metric('code_notrun')
+
+class NSConcreteNotification_SummaryProvider:
+ def adjust_for_architecture(self):
+ self.lp64 = (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)
+
+ # skip the ISA and go to the name pointer
+ def offset(self):
+ if self.lp64:
+ return 8
+ else:
+ return 4
+
+ def name(self):
+ string_ptr = self.valobj.CreateChildAtOffset("name",
+ self.offset(),
+ self.id_type)
+ return CFString.CFString_SummaryProvider(string_ptr,None)
+
+
+class NSNotificationUnknown_SummaryProvider:
+ def adjust_for_architecture(self):
+ self.lp64 = (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 name(self):
+ stream = lldb.SBStream()
+ self.valobj.GetExpressionPath(stream)
+ name_vo = self.valobj.CreateValueFromExpression("name","(NSString*)[" + stream.GetData() + " name]");
+ return CFString.CFString_SummaryProvider(name_vo,None)
+
+
+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 == 'NSConcreteNotification':
+ wrapper = NSConcreteNotification_SummaryProvider(valobj)
+ statistics.metric_hit('code_notrun',valobj)
+ else:
+ wrapper = NSNotificationUnknown_SummaryProvider(valobj)
+ statistics.metric_hit('unknown_class',str(valobj) + " seen as " + name_string)
+ return wrapper;
+
+def NSNotification_SummaryProvider (valobj,dict):
+ provider = GetSummary_Impl(valobj);
+ if provider != None:
+ try:
+ summary = provider.name();
+ except:
+ summary = None
+ if summary == None:
+ summary = 'no valid notification here'
+ return str(summary)
+ return ''
+
+def __lldb_init_module(debugger,dict):
+ debugger.HandleCommand("type summary add -F NSNotification.NSNotification_SummaryProvider NSNotification")
diff --git a/lldb/examples/summaries/cocoa/NSNumber.py b/lldb/examples/summaries/cocoa/NSNumber.py
new file mode 100644
index 00000000000..81f3d28dbc1
--- /dev/null
+++ b/lldb/examples/summaries/cocoa/NSNumber.py
@@ -0,0 +1,217 @@
+# summary provider for NSNumber
+import lldb
+import ctypes
+import objc_runtime
+import metrics
+import struct
+
+statistics = metrics.Metrics()
+statistics.add_metric('invalid_isa')
+statistics.add_metric('invalid_pointer')
+statistics.add_metric('unknown_class')
+statistics.add_metric('code_notrun')
+
+# despite the similary to synthetic children providers, these classes are not
+# trying to provide anything but the port number of an NSNumber, so they need not
+# 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_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):
+ # in spite of the plenty of types made available by the public NSNumber API
+ # only a bunch of these are actually used in the internal implementation
+ # unfortunately, the original type information appears to be lost
+ # so we try to at least recover the proper magnitude of the data
+ if self.info_bits == 0:
+ return '(char)' + str(self.data % 256)
+ if self.info_bits == 4:
+ return '(short)' + str(self.data % (256*256))
+ if self.info_bits == 8:
+ return '(int)' + str(self.data % (256*256*256*256))
+ if self.info_bits == 12:
+ return '(long)' + str(self.data)
+ else:
+ return 'absurd value:(info=' + str(self.info_bits) + ", value = " + str(self.data) + ')'
+
+
+class NSUntaggedNumber_SummaryProvider:
+ def adjust_for_architecture(self):
+ self.lp64 = (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.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):
+ global statistics
+ # we need to skip the ISA, then the next byte tells us what to read
+ # we then skip one other full pointer worth of data and then fetch the contents
+ # if we are fetching an int64 value, one more pointer must be skipped to get at our data
+ data_type_vo = self.valobj.CreateChildAtOffset("dt",
+ self.pointer_size,
+ self.char)
+ data_type = ((data_type_vo.GetValueAsUnsigned(0) % 256) & 0x1F)
+ data_offset = 2 * self.pointer_size
+ if data_type == 0B00001:
+ data_vo = self.valobj.CreateChildAtOffset("data",
+ data_offset,
+ self.char)
+ statistics.metric_hit('code_notrun',self.valobj)
+ return '(char)' + str(data_vo.GetValueAsUnsigned(0))
+ elif data_type == 0B0010:
+ data_vo = self.valobj.CreateChildAtOffset("data",
+ data_offset,
+ self.short)
+ statistics.metric_hit('code_notrun',self.valobj)
+ return '(short)' + str(data_vo.GetValueAsUnsigned(0) % (256*256))
+ # IF tagged pointers are possible on 32bit+v2 runtime
+ # (of which the only existing instance should be iOS)
+ # then values of this type might be tagged
+ elif data_type == 0B0011:
+ data_vo = self.valobj.CreateChildAtOffset("data",
+ data_offset,
+ 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
+ # be represented by a non tagged pointers
+ elif data_type == 0B10001 or data_type == 0B0100:
+ data_offset = data_offset + self.pointer_size
+ data_vo = self.valobj.CreateChildAtOffset("data",
+ data_offset,
+ self.longlong)
+ statistics.metric_hit('code_notrun',self.valobj)
+ return '(long)' + str(data_vo.GetValueAsUnsigned(0))
+ elif data_type == 0B0101:
+ data_vo = self.valobj.CreateChildAtOffset("data",
+ data_offset,
+ self.longlong)
+ data_plain = int(str(data_vo.GetValueAsUnsigned(0) & 0x00000000FFFFFFFF))
+ packed = struct.pack('I', data_plain)
+ data_float = struct.unpack('f', packed)[0]
+ statistics.metric_hit('code_notrun',self.valobj)
+ return '(float)' + str(data_float)
+ elif data_type == 0B0110:
+ data_vo = self.valobj.CreateChildAtOffset("data",
+ data_offset,
+ self.longlong)
+ data_plain = data_vo.GetValueAsUnsigned(0)
+ data_double = struct.unpack('d', struct.pack('Q', data_plain))[0]
+ statistics.metric_hit('code_notrun',self.valobj)
+ return '(double)' + str(data_double)
+ statistics.metric_hit('unknown_class',str(self.valobj) + " had unknown data_type " + str(data_type))
+ return 'absurd: dt = ' + str(data_type)
+
+
+class NSUnknownNumber_SummaryProvider:
+ def adjust_for_architecture(self):
+ self.lp64 = (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() + " stringValue]"
+ 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 == 'NSNumber' or name_string == '__NSCFNumber':
+ if class_data.is_tagged():
+ wrapper = NSTaggedNumber_SummaryProvider(valobj,class_data.info_bits(),class_data.value())
+ statistics.metric_hit('code_notrun',valobj)
+ else:
+ # the wrapper might be unable to decipher what is into the NSNumber
+ # and then have to run code on it
+ wrapper = NSUntaggedNumber_SummaryProvider(valobj)
+ else:
+ wrapper = NSUnknownNumber_SummaryProvider(valobj)
+ statistics.metric_hit('unknown_class',str(valobj) + " seen as " + name_string)
+ return wrapper;
+
+
+def NSNumber_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 NSNumber.NSNumber_SummaryProvider NSNumber")
+
diff --git a/lldb/examples/summaries/cocoa/NSSet.py b/lldb/examples/summaries/cocoa/NSSet.py
new file mode 100644
index 00000000000..3ca561cd8f8
--- /dev/null
+++ b/lldb/examples/summaries/cocoa/NSSet.py
@@ -0,0 +1,211 @@
+# summary provider for NSSet
+import lldb
+import ctypes
+import objc_runtime
+import metrics
+
+statistics = metrics.Metrics()
+statistics.add_metric('invalid_isa')
+statistics.add_metric('invalid_pointer')
+statistics.add_metric('unknown_class')
+statistics.add_metric('code_notrun')
+
+# despite the similary to synthetic children providers, these classes are not
+# trying to provide anything but the port number of an NSMachPort, so they need not
+# 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_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)
+
+ # one pointer is the ISA
+ # then we have one other internal pointer, plus
+ # 4 bytes worth of flags. hence, these values
+ def offset(self):
+ if self.lp64:
+ return 20
+ else:
+ return 12
+
+ def count(self):
+ vcount = self.valobj.CreateChildAtOffset("count",
+ self.offset(),
+ self.NSUInteger)
+ return vcount.GetValueAsUnsigned(0)
+
+
+class NSSetUnknown_SummaryProvider:
+ def adjust_for_architecture(self):
+ self.lp64 = (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 count(self):
+ stream = lldb.SBStream()
+ self.valobj.GetExpressionPath(stream)
+ expr = "(int)[" + stream.GetData() + " count]"
+ num_children_vo = self.valobj.CreateValueFromExpression("count",expr);
+ return num_children_vo.GetValueAsUnsigned(0)
+
+class NSSetI_SummaryProvider:
+ def adjust_for_architecture(self):
+ self.lp64 = (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)
+ if self.lp64:
+ 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:
+ return 8
+ else:
+ return 4
+
+ def count(self):
+ num_children_vo = self.valobj.CreateChildAtOffset("count",
+ self.offset(),
+ self.NSUInteger)
+ value = num_children_vo.GetValueAsUnsigned(0)
+ if value != None:
+ # the MSB on immutable sets seems to be taken by some other data
+ # 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
+ else:
+ value = value & ~0xFF000000
+ return value
+
+class NSSetM_SummaryProvider:
+ def adjust_for_architecture(self):
+ self.lp64 = (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)
+ if self.lp64:
+ 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:
+ return 8
+ else:
+ return 4
+
+ def count(self):
+ num_children_vo = self.valobj.CreateChildAtOffset("count",
+ self.offset(),
+ self.NSUInteger)
+ return num_children_vo.GetValueAsUnsigned(0)
+
+
+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 == '__NSCFSet':
+ wrapper = NSCFSet_SummaryProvider(valobj)
+ statistics.metric_hit('code_notrun',valobj)
+ elif name_string == '__NSSetI':
+ wrapper = NSSetI_SummaryProvider(valobj)
+ statistics.metric_hit('code_notrun',valobj)
+ elif name_string == '__NSSetM':
+ wrapper = NSSetM_SummaryProvider(valobj)
+ statistics.metric_hit('code_notrun',valobj)
+ else:
+ wrapper = NSSetUnknown_SummaryProvider(valobj)
+ statistics.metric_hit('unknown_class',str(valobj) + " seen as " + name_string)
+ return wrapper;
+
+
+def NSSet_SummaryProvider (valobj,dict):
+ provider = GetSummary_Impl(valobj);
+ if provider != None:
+ #try:
+ summary = provider.count();
+ #except:
+ # summary = None
+ if summary == None:
+ summary = 'no valid set here'
+ return str(summary) + ' objects'
+ return ''
+
+def NSSet_SummaryProvider2 (valobj,dict):
+ provider = GetSummary_Impl(valobj);
+ if provider != None:
+ try:
+ summary = provider.count();
+ except:
+ summary = None
+ # for some reason, one needs to clear some bits for the count returned
+ # to be correct when using directly CF*SetRef as compared to NS*Set
+ # this only happens on 64bit, and the bit mask was derived through
+ # experimentation (if counts start looking weird, then most probably
+ # the mask needs to be changed)
+ if summary == None:
+ summary = 'no valid set here'
+ else:
+ if provider.lp64:
+ summary = int(summary) & ~0x1fff000000000000
+ return str(summary) + ' objects'
+ return ''
+
+
+def __lldb_init_module(debugger,dict):
+ debugger.HandleCommand("type summary add -F NSSet.NSSet_SummaryProvider NSSet")
+ debugger.HandleCommand("type summary add -F NSSet.NSSet_SummaryProvider2 CFSetRef CFMutableSetRef")
diff --git a/lldb/examples/summaries/cocoa/NSURL.py b/lldb/examples/summaries/cocoa/NSURL.py
new file mode 100644
index 00000000000..6c15b402213
--- /dev/null
+++ b/lldb/examples/summaries/cocoa/NSURL.py
@@ -0,0 +1,124 @@
+# summary provider for NSURL
+import lldb
+import ctypes
+import objc_runtime
+import metrics
+import CFString
+
+statistics = metrics.Metrics()
+statistics.add_metric('invalid_isa')
+statistics.add_metric('invalid_pointer')
+statistics.add_metric('unknown_class')
+statistics.add_metric('code_notrun')
+
+# despite the similary to synthetic children providers, these classes are not
+# trying to provide anything but a summary for an NSURL, so they need not
+# 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_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)
+ if self.lp64:
+ self.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong)
+ self.pointer_size = 8
+ else:
+ self.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt)
+ self.pointer_size = 4
+ self.NSString = self.valobj.GetTarget().FindFirstType('NSString')
+ self.NSURL = self.valobj.GetTarget().FindFirstType('NSURL')
+
+ # one pointer is the ISA
+ # then there is one more pointer and 8 bytes of plain data
+ # (which are also present on a 32-bit system)
+ # plus another pointer, and then the real data
+ def offset(self):
+ if self.lp64:
+ return 24
+ else:
+ return 16
+
+ def url_text(self):
+ text = self.valobj.CreateChildAtOffset("text",
+ self.offset(),
+ self.NSString.GetPointerType())
+ base = self.valobj.CreateChildAtOffset("base",
+ self.offset()+self.pointer_size,
+ self.NSURL.GetPointerType())
+ my_string = CFString.CFString_SummaryProvider(text,None)
+ if base.GetValueAsUnsigned(0) != 0:
+ my_string = my_string + " (base path: " + NSURL_SummaryProvider(base,None) + ")"
+ return my_string
+
+
+class NSURLUnknown_SummaryProvider:
+ def adjust_for_architecture(self):
+ self.lp64 = (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 url_text(self):
+ stream = lldb.SBStream()
+ self.valobj.GetExpressionPath(stream)
+ url_text_vo = self.valobj.CreateValueFromExpression("url","(NSString*)[" + stream.GetData() + " description]");
+ return CFString.CFString_SummaryProvider(url_text_vo,None)
+
+
+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 == 'NSURL':
+ wrapper = NSURLKnown_SummaryProvider(valobj)
+ statistics.metric_hit('code_notrun',valobj)
+ else:
+ wrapper = NSURLUnknown_SummaryProvider(valobj)
+ statistics.metric_hit('unknown_class',str(valobj) + " seen as " + name_string)
+ return wrapper;
+
+def NSURL_SummaryProvider (valobj,dict):
+ provider = GetSummary_Impl(valobj);
+ if provider != None:
+ try:
+ summary = provider.url_text();
+ except:
+ summary = None
+ if summary == None or summary == '':
+ summary = 'no valid NSURL here'
+ return summary
+ return ''
+
+def __lldb_init_module(debugger,dict):
+ debugger.HandleCommand("type summary add -F NSURL.NSURL_SummaryProvider NSURL CFURLRef")
diff --git a/lldb/examples/summaries/cocoa/cache.py b/lldb/examples/summaries/cocoa/cache.py
new file mode 100644
index 00000000000..01a79a45196
--- /dev/null
+++ b/lldb/examples/summaries/cocoa/cache.py
@@ -0,0 +1,28 @@
+import metrics
+
+class Cache:
+ def __init__(self):
+ self.data = {}
+ self.statistics = metrics.Metrics()
+ self.statistics.add_metric('hit')
+ self.statistics.add_metric('miss')
+
+ def look_for_key(self,key):
+ if key in self.data:
+ return True
+ return False
+
+ def add_item(self,key,value,ok_to_replace=True):
+ if not(ok_to_replace) and self.look_for_key(key):
+ return False
+ self.data[key] = value
+ return True
+
+ def get_value(self,key,default=None):
+ if self.look_for_key(key):
+ self.statistics.metric_hit('hit',key)
+ return self.data[key]
+ else:
+ self.statistics.metric_hit('miss',key)
+ return default
+
diff --git a/lldb/examples/summaries/cocoa/metrics.py b/lldb/examples/summaries/cocoa/metrics.py
new file mode 100644
index 00000000000..bf9a025d183
--- /dev/null
+++ b/lldb/examples/summaries/cocoa/metrics.py
@@ -0,0 +1,62 @@
+import lldb
+
+class Counter:
+ def __init__(self):
+ self.count = 0
+ self.list = []
+ def update(self,name):
+ self.count = self.count + 1
+ self.list.append(str(name))
+ def __str__(self):
+ return str(self.count) + " times, for items [" + str(self.list) + "]"
+
+class MetricsPrinter_Verbose:
+ def __init__(self,metrics):
+ self.metrics = metrics
+ def __str__(self):
+ string = ""
+ for key,value in self.metrics.metrics.items():
+ string = string + "metric " + str(key) + ": " + str(value) + "\n"
+ return string
+
+class MetricsPrinter_Compact:
+ def __init__(self,metrics):
+ self.metrics = metrics
+ def __str__(self):
+ string = ""
+ for key,value in self.metrics.metrics.items():
+ string = string + "metric " + str(key) + " was hit " + str(value.count) + " times\n"
+ return string
+
+class Metrics:
+ def __init__(self):
+ self.metrics = {}
+
+ def add_metric(self,name):
+ self.metrics[name] = Counter()
+
+ def metric_hit(self,metric,trigger):
+ self.metrics[metric].update(trigger)
+
+ def __getitem__(self,key):
+ return self.metrics[key]
+
+ def __getattr__(self,name):
+ if name == 'compact':
+ return MetricsPrinter_Compact(self)
+ if name == 'verbose':
+ return MetricsPrinter_Verbose(self)
+ raise AttributeError("%r object has no attribute %r" %
+ (type(self).__name__, name))
+
+ def __str__(self):
+ return str(self.verbose)
+
+ def metric_success(self,metric):
+ total_count = 0
+ metric_count = self[metric].count
+ for key,value in self.metrics.items():
+ total_count = total_count + value.count
+ if total_count > 0:
+ return metric_count / float(total_count)
+ return 0
diff --git a/lldb/examples/summaries/cocoa/objc_lldb.py b/lldb/examples/summaries/cocoa/objc_lldb.py
new file mode 100644
index 00000000000..a81be013159
--- /dev/null
+++ b/lldb/examples/summaries/cocoa/objc_lldb.py
@@ -0,0 +1,132 @@
+"""
+Objective-C runtime wrapper - Replicates the behavior of AppleObjCRuntimeV2.cpp in Python code
+for the benefit of synthetic children providers and Python summaries
+
+part of The LLVM Compiler Infrastructure
+This file is distributed under the University of Illinois Open Source
+License. See LICENSE.TXT for details.
+"""
+import lldb
+
+class ObjCRuntime:
+
+ def __init__(self,valobj = None):
+ self.valobj = valobj;
+ self.adjust_for_architecture()
+
+ def adjust_for_architecture(self):
+ self.lp64 = (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)
+ self.addr_ptr_type = self.addr_type.GetPointerType()
+
+ def is_tagged(self):
+ if valobj is None:
+ return None
+ ptr_value = self.valobj.GetPointerValue()
+ if (ptr_value % 2) == 1:
+ return True
+ else:
+ return False
+
+ def read_ascii(self, pointer):
+ process = self.valobj.GetTarget().GetProcess()
+ error = lldb.SBError()
+ pystr = ''
+ # cannot do the read at once because there is no length byte
+ while True:
+ content = process.ReadMemory(pointer, 1, error)
+ new_bytes = bytearray(content)
+ b0 = new_bytes[0]
+ pointer = pointer + 1
+ if b0 == 0:
+ break
+ pystr = pystr + chr(b0)
+ return pystr
+
+ def read_isa(self):
+ # read ISA pointer
+ isa_pointer = self.valobj.CreateChildAtOffset("cfisa",
+ 0,
+ self.addr_ptr_type)
+ if isa_pointer == None or isa_pointer.IsValid() == False:
+ return None;
+ if isa_pointer.GetValue() == None:
+ return None;
+ isa = int(isa_pointer.GetValue(), 0)
+ if isa == 0 or isa == None:
+ return None;
+ return isa
+
+
+ def get_parent_class(self, isa = None):
+ if isa is None:
+ isa = self.read_isa()
+ if isa is None:
+ return None
+ # read superclass pointer
+ rw_pointer = isa + self.pointer_size
+ rw_object = self.valobj.CreateValueFromAddress("parent_isa",
+ rw_pointer,
+ self.addr_type)
+ if rw_object == None or rw_object.IsValid() == False:
+ return None;
+ if rw_object.GetValue() == None:
+ return None;
+ rw = int(rw_object.GetValue(), 0)
+ if rw == 0 or rw == None:
+ return None;
+ return rw
+
+ def get_class_name(self, isa = None):
+ if isa is None:
+ isa = self.read_isa()
+ if isa is None:
+ return None
+ # read rw pointer
+ rw_pointer = isa + 4 * self.pointer_size
+ rw_object = self.valobj.CreateValueFromAddress("rw",
+ rw_pointer,
+ self.addr_type)
+ if rw_object == None or rw_object.IsValid() == False:
+ return None;
+ if rw_object.GetValue() == None:
+ return None;
+ rw = int(rw_object.GetValue(), 0)
+ if rw == 0 or rw == None:
+ return None;
+
+ # read data pointer
+ data_pointer = rw + 8
+ data_object = self.valobj.CreateValueFromAddress("data",
+ data_pointer,
+ self.addr_type)
+ if data_object == None or data_object.IsValid() == False:
+ return None;
+ if data_object.GetValue() == None:
+ return None;
+ data = int(data_object.GetValue(), 0)
+ if data == 0 or data == None:
+ return None;
+
+ # read ro pointer
+ ro_pointer = data + 12 + self.pointer_size
+ if self.lp64:
+ ro_pointer += 4
+ ro_object = self.valobj.CreateValueFromAddress("ro",
+ ro_pointer,
+ self.addr_type)
+ if ro_object == None or ro_object.IsValid() == False:
+ return None;
+ if ro_object.GetValue() == None:
+ return None;
+ name_pointer = int(ro_object.GetValue(), 0)
+ if name_pointer == 0 or name_pointer == None:
+ return None;
+
+ # now read the actual name and compare it to known stuff
+ name_string = self.read_ascii(name_pointer)
+ if (name_string.startswith("NSKVONotify")):
+ return self.get_class_name(self.get_parent_class())
+ return name_string
diff --git a/lldb/examples/summaries/cocoa/objc_runtime.py b/lldb/examples/summaries/cocoa/objc_runtime.py
new file mode 100644
index 00000000000..6766bf307cc
--- /dev/null
+++ b/lldb/examples/summaries/cocoa/objc_runtime.py
@@ -0,0 +1,500 @@
+# a wrapper for the Objective-C runtime for use by LLDB
+import lldb
+import cache
+
+class Utilities:
+ @staticmethod
+ def read_ascii(process, pointer,max_len=128,when_over_return_none=True):
+ 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
+
+ @staticmethod
+ def is_valid_pointer(pointer, pointer_size, allow_tagged):
+ if pointer == None:
+ return False
+ if pointer == 0:
+ return False
+ if allow_tagged:
+ if (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
+ # so if any pointer has bits 47 thru 63 high we know that this is not a valid isa
+ @staticmethod
+ def is_allowed_pointer(pointer):
+ if pointer == None:
+ return False
+ mask = 0xFFFF800000000000
+ if (pointer & mask) != 0:
+ return False
+ return True
+
+ @staticmethod
+ def read_child_of(valobj,offset,type):
+ child = valobj.CreateChildAtOffset("childUNK",offset,type)
+ if child == None or child.IsValid() == False:
+ return None;
+ return child.GetValueAsUnsigned()
+
+ @staticmethod
+ def is_valid_identifier(name):
+ if name is None:
+ 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:])
+
+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.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
+ self.namePointer = Utilities.read_child_of(self.valobj,offset,self.sys_params.addr_ptr_type)
+ self.check_valid()
+ else:
+ self.valid = False
+ if self.valid:
+ self.name = Utilities.read_ascii(self.valobj.GetTarget().GetProcess(),self.namePointer)
+ if not(Utilities.is_valid_identifier(self.name)):
+ self.valid = False
+
+ # perform sanity checks on the contents of this class_rw_t
+ def check_valid(self):
+ self.valid = True
+ # misaligned pointers seem to be possible for this field
+ #if not(Utilities.is_valid_pointer(self.namePointer,self.sys_params.pointer_size,allow_tagged=False)):
+ # self.valid = False
+ # pass
+
+ def __str__(self):
+ return 'flags = ' + str(self.flags) + "\n" + \
+ "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):
+ return self.valid
+
+
+class RwT_Data:
+ def __init__(self,rwt_pointer,params):
+ 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.roPointer = Utilities.read_child_of(self.valobj,8,self.sys_params.addr_ptr_type)
+ self.check_valid()
+ else:
+ self.valid = False
+ if self.valid:
+ self.rot = self.valobj.CreateValueFromAddress("rot",self.roPointer,self.sys_params.addr_ptr_type).AddressOf()
+ self.data = RoT_Data(self.rot,self.sys_params)
+
+ # perform sanity checks on the contents of this class_rw_t
+ def check_valid(self):
+ self.valid = True
+ if not(Utilities.is_valid_pointer(self.roPointer,self.sys_params.pointer_size,allow_tagged=False)):
+ self.valid = False
+
+ def __str__(self):
+ return 'flags = ' + str(self.flags) + "\n" + \
+ "version = " + hex(self.version) + "\n" + \
+ "roPointer = " + hex(self.roPointer)
+
+ def is_valid(self):
+ if self.valid:
+ return self.data.is_valid()
+ return False
+
+class Class_Data_V2:
+ def __init__(self,isa_pointer,params):
+ if (isa_pointer != None) and (Utilities.is_valid_pointer(isa_pointer.GetValueAsUnsigned(),params.pointer_size, allow_tagged=False)):
+ self.sys_params = params
+ self.valobj = isa_pointer
+ self.isaPointer = Utilities.read_child_of(self.valobj,0,self.sys_params.addr_ptr_type)
+ self.superclassIsaPointer = Utilities.read_child_of(self.valobj,1*self.sys_params.pointer_size,self.sys_params.addr_ptr_type)
+ self.cachePointer = Utilities.read_child_of(self.valobj,2*self.sys_params.pointer_size,self.sys_params.addr_ptr_type)
+ self.vtablePointer = Utilities.read_child_of(self.valobj,3*self.sys_params.pointer_size,self.sys_params.addr_ptr_type)
+ self.dataPointer = Utilities.read_child_of(self.valobj,4*self.sys_params.pointer_size,self.sys_params.addr_ptr_type)
+ self.check_valid()
+ else:
+ self.valid = False
+ if self.valid:
+ self.rwt = self.valobj.CreateValueFromAddress("rwt",self.dataPointer,self.sys_params.addr_ptr_type).AddressOf()
+ self.data = RwT_Data(self.rwt,self.sys_params)
+
+ # perform sanity checks on the contents of this class_t
+ def check_valid(self):
+ self.valid = True
+ if not(Utilities.is_valid_pointer(self.isaPointer,self.sys_params.pointer_size,allow_tagged=False)):
+ self.valid = False
+ return
+ if not(Utilities.is_valid_pointer(self.superclassIsaPointer,self.sys_params.pointer_size,allow_tagged=False)):
+ # NULL is a valid value for superclass (it means we have reached NSObject)
+ if self.superclassIsaPointer != 0:
+ self.valid = False
+ return
+ if not(Utilities.is_valid_pointer(self.cachePointer,self.sys_params.pointer_size,allow_tagged=False)):
+ self.valid = False
+ return
+ if not(Utilities.is_valid_pointer(self.vtablePointer,self.sys_params.pointer_size,allow_tagged=False)):
+ self.valid = False
+ return
+ if not(Utilities.is_valid_pointer(self.dataPointer,self.sys_params.pointer_size,allow_tagged=False)):
+ self.valid = False
+ return
+ if not(Utilities.is_allowed_pointer(self.isaPointer)):
+ self.valid = False
+ return
+ if not(Utilities.is_allowed_pointer(self.superclassIsaPointer)):
+ # NULL is a valid value for superclass (it means we have reached NSObject)
+ if self.superclassIsaPointer != 0:
+ self.valid = False
+ return
+ if not(Utilities.is_allowed_pointer(self.cachePointer)):
+ self.valid = False
+ return
+ if not(Utilities.is_allowed_pointer(self.vtablePointer)):
+ self.valid = False
+ return
+ if not(Utilities.is_allowed_pointer(self.dataPointer)):
+ self.valid = False
+ return
+
+ def is_kvo(self):
+ if self.is_valid():
+ if self.data.data.name.startswith("NSKVONotify"):
+ return True
+ else:
+ return None
+
+ def get_superclass(self):
+ if self.is_valid():
+ parent_isa_pointer = self.valobj.CreateChildAtOffset("parent_isa",
+ self.sys_params.pointer_size,
+ self.sys_params.addr_ptr_type)
+ return Class_Data_V2(parent_isa_pointer,self.sys_params)
+ else:
+ return None
+
+ def class_name(self):
+ if self.is_valid():
+ return self.data.data.name
+ else:
+ return None
+
+ def is_valid(self):
+ if self.valid:
+ return self.data.is_valid()
+ return False
+
+ def __str__(self):
+ return 'isaPointer = ' + hex(self.isaPointer) + "\n" + \
+ "superclassIsaPointer = " + hex(self.superclassIsaPointer) + "\n" + \
+ "cachePointer = " + hex(self.cachePointer) + "\n" + \
+ "vtablePointer = " + hex(self.vtablePointer) + "\n" + \
+ "data = " + hex(self.dataPointer)
+
+ def is_tagged(self):
+ return False
+
+# runtime v1 is much less intricate than v2 and stores relevant information directly in the class_t object
+# idea for improvement: read additional information just to check for faulty pointers (not too worried about it
+# since v1 is only used for 32bit desktop development)
+class Class_Data_V1:
+ def __init__(self,isa_pointer,params):
+ if (isa_pointer != None) and (Utilities.is_valid_pointer(isa_pointer.GetValueAsUnsigned(),params.pointer_size, allow_tagged=False)):
+ self.sys_params = params
+ self.valobj = isa_pointer
+ self.isaPointer = Utilities.read_child_of(self.valobj,0,self.sys_params.addr_ptr_type)
+ self.superclassIsaPointer = Utilities.read_child_of(self.valobj,1*self.sys_params.pointer_size,self.sys_params.addr_ptr_type)
+ self.namePointer = Utilities.read_child_of(self.valobj,2*self.sys_params.pointer_size,self.sys_params.addr_ptr_type)
+ self.check_valid()
+ else:
+ self.valid = False
+ if self.valid:
+ self.name = Utilities.read_ascii(self.valobj.GetTarget().GetProcess(),self.namePointer)
+ if not(Utilities.is_valid_identifier(self.name)):
+ self.valid = False
+
+ # perform sanity checks on the contents of this class_t
+ def check_valid(self):
+ self.valid = True
+ if not(Utilities.is_valid_pointer(self.isaPointer,self.sys_params.pointer_size,allow_tagged=False)):
+ self.valid = False
+ return
+ if not(Utilities.is_valid_pointer(self.superclassIsaPointer,self.sys_params.pointer_size,allow_tagged=False)):
+ # NULL is a valid value for superclass (it means we have reached NSObject)
+ 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
+
+ def is_kvo(self):
+ if self.is_valid():
+ if self.name.startswith("NSKVONotify"):
+ return True
+ else:
+ return None
+
+ def get_superclass(self):
+ if self.is_valid():
+ parent_isa_pointer = self.valobj.CreateChildAtOffset("parent_isa",
+ self.sys_params.pointer_size,
+ self.sys_params.addr_ptr_type)
+ return Class_Data_V1(parent_isa_pointer,self.sys_params)
+ else:
+ return None
+
+ def class_name(self):
+ if self.is_valid():
+ return self.name
+ else:
+ return None
+
+ def is_valid(self):
+ return self.valid
+
+ def __str__(self):
+ return 'isaPointer = ' + hex(self.isaPointer) + "\n" + \
+ "superclassIsaPointer = " + hex(self.superclassIsaPointer) + "\n" + \
+ "namePointer = " + hex(self.namePointer) + " --> " + self.name
+
+ def is_tagged(self):
+ return False
+
+class TaggedClass_Data:
+ def __init__(self,pointer,params):
+ self.valid = True
+ self.name = None
+ self.sys_params = params
+ self.valobj = pointer
+ 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
+ else:
+ self.valid = False
+
+ def is_valid(self):
+ return self.valid
+
+ def class_name(self):
+ if self.is_valid():
+ return self.name
+ else:
+ return None
+
+ def value(self):
+ return self.val if self.is_valid() else None
+
+ def info_bits(self):
+ return self.i_bits if self.is_valid() else None
+
+ def is_kvo(self):
+ return False
+
+ # we would need to go around looking for the superclass or ask the runtime
+ # for now, we seem not to require support for this operation so we will merrily
+ # pretend to be at a root point in the hierarchy
+ def get_superclass(self):
+ return None
+
+ # anything that is handled here is tagged
+ def is_tagged(self):
+ return True
+
+class InvalidClass_Data:
+ def __init__(self):
+ pass
+ def is_valid(self):
+ return False
+
+runtime_version = cache.Cache()
+
+class SystemParameters:
+ def __init__(self,valobj):
+ self.adjust_for_architecture(valobj)
+
+ def adjust_for_architecture(self,valobj):
+ self.process = valobj.GetTarget().GetProcess()
+ self.lp64 = (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
+ pid = self.process.GetProcessID()
+ if runtime_version.look_for_key(pid) == None:
+ 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)
+
+isa_cache = cache.Cache()
+
+class ObjCRuntime:
+
+ # the ObjC runtime has no explicit "version" field that we can use
+ # instead, we discriminate v1 from v2 by looking for the presence
+ # of a well-known section only present in v1
+ @staticmethod
+ def runtime_version(process):
+ if process.IsValid() == False:
+ return None
+ target = process.GetTarget()
+ num_modules = target.GetNumModules()
+ module_objc = None
+ for idx in range(num_modules):
+ module = target.GetModuleAtIndex(idx)
+ if module.GetFileSpec().GetFilename() == 'libobjc.A.dylib':
+ module_objc = module
+ break
+ if module_objc == None or module_objc.IsValid() == False:
+ return None
+ num_sections = module.GetNumSections()
+ section_objc = None
+ for idx in range(num_sections):
+ section = module.GetSectionAtIndex(idx)
+ if section.GetName() == '__OBJC':
+ section_objc = section
+ break
+ if section_objc != None and section_objc.IsValid():
+ return 1
+ return 2
+
+ def __init__(self,valobj):
+ self.valobj = valobj
+ self.adjust_for_architecture()
+ self.sys_params = SystemParameters(self.valobj)
+
+ def adjust_for_architecture(self):
+ self.lp64 = (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)
+ self.addr_ptr_type = self.addr_type.GetPointerType()
+
+# an ObjC pointer can either be tagged or must be aligned
+ 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)))
+
+ 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)
+
+ def read_isa(self):
+ isa_pointer = self.valobj.CreateChildAtOffset("cfisa",
+ 0,
+ self.addr_ptr_type)
+ if isa_pointer == None or isa_pointer.IsValid() == False:
+ return None;
+ if isa_pointer.GetValueAsUnsigned(1) == 1:
+ return None;
+ return isa_pointer
+
+ def read_class_data(self):
+ global isa_cache
+ if self.is_tagged():
+ # tagged pointers only exist in ObjC v2
+ if self.sys_params.runtime_version == 2:
+ # 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)
+ return tentative_tagged if tentative_tagged.is_valid() else InvalidClass_Data()
+ else:
+ return InvalidClass_Data()
+ if self.is_valid() == False:
+ return InvalidClass_Data()
+ isa = self.read_isa()
+ if isa == None:
+ return InvalidClass_Data()
+ isa_value = isa.GetValueAsUnsigned(1)
+ if isa_value == 1:
+ return InvalidClass_Data()
+ data = isa_cache.get_value(isa_value,default=None)
+ if data != None:
+ return data
+ if self.sys_params.runtime_version == 2:
+ data = Class_Data_V2(isa,self.sys_params)
+ else:
+ data = Class_Data_V1(isa,self.sys_params)
+ if data == None:
+ return InvalidClass_Data()
+ if data.is_valid():
+ isa_cache.add_item(isa_value,data,ok_to_replace=True)
+ return data
OpenPOWER on IntegriCloud