1# summary provider for NSDictionary 2import lldb 3import ctypes 4import objc_runtime 5import metrics 6 7statistics = metrics.Metrics() 8statistics.add_metric('invalid_isa') 9statistics.add_metric('invalid_pointer') 10statistics.add_metric('unknown_class') 11statistics.add_metric('code_notrun') 12 13# despite the similary to synthetic children providers, these classes are not 14# trying to provide anything but the count for an NSDictionary, so they need not 15# obey the interface specification for synthetic children providers 16class NSCFDictionary_SummaryProvider: 17 def adjust_for_architecture(self): 18 self.is_64_bit = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) 19 self.is_little = (self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle) 20 self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize() 21 22 def __init__(self, valobj): 23 self.valobj = valobj; 24 self.update(); 25 26 def update(self): 27 self.adjust_for_architecture(); 28 self.id_type = self.valobj.GetType().GetBasicType(lldb.eBasicTypeObjCID) 29 if self.is_64_bit: 30 self.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong) 31 else: 32 self.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt) 33 34 # empirically determined on both 32 and 64bit desktop Mac OS X 35 # probably boils down to 2 pointers and 4 bytes of data, but 36 # the description of __CFDictionary is not readily available so most 37 # of this is guesswork, plain and simple 38 def offset(self): 39 if self.is_64_bit: 40 return 20 41 else: 42 return 12 43 44 def num_children(self): 45 num_children_vo = self.valobj.CreateChildAtOffset("count", 46 self.offset(), 47 self.NSUInteger) 48 return num_children_vo.GetValueAsUnsigned(0) 49 50 51class NSDictionaryI_SummaryProvider: 52 def adjust_for_architecture(self): 53 self.is_64_bit = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) 54 self.is_little = (self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle) 55 self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize() 56 57 def __init__(self, valobj): 58 self.valobj = valobj; 59 self.update(); 60 61 def update(self): 62 self.adjust_for_architecture(); 63 self.id_type = self.valobj.GetType().GetBasicType(lldb.eBasicTypeObjCID) 64 if self.is_64_bit: 65 self.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong) 66 else: 67 self.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt) 68 69 # we just need to skip the ISA and the count immediately follows 70 def offset(self): 71 if self.is_64_bit: 72 return 8 73 else: 74 return 4 75 76 def num_children(self): 77 num_children_vo = self.valobj.CreateChildAtOffset("count", 78 self.offset(), 79 self.NSUInteger) 80 value = num_children_vo.GetValueAsUnsigned(0) 81 if value != None: 82 # the MS6bits on immutable dictionaries seem to be taken by the LSB of capacity 83 # not sure if it is a bug or some weird sort of feature, but masking that out 84 # gets the count right 85 if self.is_64_bit: 86 value = value & ~0xFC00000000000000 87 else: 88 value = value & ~0xFC000000 89 return value 90 91class NSDictionaryM_SummaryProvider: 92 def adjust_for_architecture(self): 93 self.is_64_bit = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) 94 self.is_little = (self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle) 95 self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize() 96 97 def __init__(self, valobj): 98 self.valobj = valobj; 99 self.update(); 100 101 def update(self): 102 self.adjust_for_architecture(); 103 self.id_type = self.valobj.GetType().GetBasicType(lldb.eBasicTypeObjCID) 104 if self.is_64_bit: 105 self.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong) 106 else: 107 self.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt) 108 109 # we just need to skip the ISA and the count immediately follows 110 def offset(self): 111 if self.is_64_bit: 112 return 8 113 else: 114 return 4 115 116 def num_children(self): 117 num_children_vo = self.valobj.CreateChildAtOffset("count", 118 self.offset(), 119 self.NSUInteger) 120 value = num_children_vo.GetValueAsUnsigned(0) 121 if value != None: 122 # the MS6bits on mutable dictionaries seem to be taken by flags for 123 # KVO and probably other features. however, masking it out does get 124 # the count right 125 if self.is_64_bit: 126 value = value & ~0xFC00000000000000 127 else: 128 value = value & ~0xFC000000 129 return value 130 131 132class NSDictionaryUnknown_SummaryProvider: 133 def adjust_for_architecture(self): 134 self.is_64_bit = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) 135 self.is_little = (self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle) 136 self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize() 137 138 def __init__(self, valobj): 139 self.valobj = valobj; 140 self.update() 141 142 def update(self): 143 self.adjust_for_architecture(); 144 self.id_type = self.valobj.GetType().GetBasicType(lldb.eBasicTypeObjCID) 145 146 def num_children(self): 147 stream = lldb.SBStream() 148 self.valobj.GetExpressionPath(stream) 149 num_children_vo = self.valobj.CreateValueFromExpression("count","(int)[" + stream.GetData() + " count]"); 150 return num_children_vo.GetValueAsUnsigned(0) 151 152 153def GetSummary_Impl(valobj): 154 global statistics 155 class_data = objc_runtime.ObjCRuntime(valobj) 156 if class_data.is_valid() == False: 157 statistics.metric_hit('invalid_pointer',valobj) 158 wrapper = None 159 return 160 class_data = class_data.read_class_data() 161 if class_data.is_valid() == False: 162 statistics.metric_hit('invalid_isa',valobj) 163 wrapper = None 164 return 165 if class_data.is_kvo(): 166 class_data = class_data.get_superclass() 167 if class_data.is_valid() == False: 168 statistics.metric_hit('invalid_isa',valobj) 169 wrapper = None 170 return 171 172 name_string = class_data.class_name() 173 if name_string == '__NSCFDictionary': 174 wrapper = NSCFDictionary_SummaryProvider(valobj) 175 statistics.metric_hit('code_notrun',valobj) 176 elif name_string == '__NSDictionaryI': 177 wrapper = NSDictionaryI_SummaryProvider(valobj) 178 statistics.metric_hit('code_notrun',valobj) 179 elif name_string == '__NSDictionaryM': 180 wrapper = NSDictionaryM_SummaryProvider(valobj) 181 statistics.metric_hit('code_notrun',valobj) 182 else: 183 wrapper = NSDictionaryUnknown_SummaryProvider(valobj) 184 statistics.metric_hit('unknown_class',str(valobj) + " seen as " + name_string) 185 return wrapper; 186 187def CFDictionary_SummaryProvider (valobj,dict): 188 provider = GetSummary_Impl(valobj); 189 if provider != None: 190 try: 191 summary = str(provider.num_children()); 192 except: 193 summary = None 194 if summary == None: 195 summary = 'no valid dictionary here' 196 return summary + " key/value pairs" 197 return '' 198 199def CFDictionary_SummaryProvider2 (valobj,dict): 200 provider = GetSummary_Impl(valobj); 201 if provider != None: 202 try: 203 summary = (provider.num_children()); 204 except: 205 summary = None 206 if summary == None: 207 summary = 'no valid dictionary here' 208 # needed on OSX Mountain Lion 209 elif provider.is_64_bit: 210 summary = int(summary) & ~0x0f1f000000000000 211 return str(summary) + " key/value pairs" 212 return '' 213 214def __lldb_init_module(debugger,dict): 215 debugger.HandleCommand("type summary add -F CFDictionary.CFDictionary_SummaryProvider NSDictionary") 216 debugger.HandleCommand("type summary add -F CFDictionary.CFDictionary_SummaryProvider2 CFDictionaryRef CFMutableDictionaryRef") 217