1 """ 2 LLDB AppKit formatters 3 4 part of The LLVM Compiler Infrastructure 5 This file is distributed under the University of Illinois Open Source 6 License. See LICENSE.TXT for details. 7 """ 8 # summary provider for NSSet 9 import lldb 10 import ctypes 11 import lldb.runtime.objc.objc_runtime 12 import lldb.formatters.metrics 13 import CFBag 14 import lldb.formatters.Logger 15 16 statistics = lldb.formatters.metrics.Metrics() 17 statistics.add_metric('invalid_isa') 18 statistics.add_metric('invalid_pointer') 19 statistics.add_metric('unknown_class') 20 statistics.add_metric('code_notrun') 21 22 # despite the similary to synthetic children providers, these classes are not 23 # trying to provide anything but the port number of an NSMachPort, so they need not 24 # obey the interface specification for synthetic children providers 25 class NSCFSet_SummaryProvider: 26 def adjust_for_architecture(self): 27 pass 28 29 def __init__(self, valobj, params): 30 logger = lldb.formatters.Logger.Logger() 31 self.valobj = valobj; 32 self.sys_params = params 33 if not(self.sys_params.types_cache.NSUInteger): 34 if self.sys_params.is_64_bit: 35 self.sys_params.types_cache.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong) 36 else: 37 self.sys_params.types_cache.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt) 38 self.update(); 39 40 def update(self): 41 logger = lldb.formatters.Logger.Logger() 42 self.adjust_for_architecture(); 43 44 # one pointer is the ISA 45 # then we have one other internal pointer, plus 46 # 4 bytes worth of flags. hence, these values 47 def offset(self): 48 logger = lldb.formatters.Logger.Logger() 49 if self.sys_params.is_64_bit: 50 return 20 51 else: 52 return 12 53 54 def count(self): 55 logger = lldb.formatters.Logger.Logger() 56 vcount = self.valobj.CreateChildAtOffset("count", 57 self.offset(), 58 self.sys_params.types_cache.NSUInteger) 59 return vcount.GetValueAsUnsigned(0) 60 61 62 class NSSetUnknown_SummaryProvider: 63 def adjust_for_architecture(self): 64 pass 65 66 def __init__(self, valobj, params): 67 logger = lldb.formatters.Logger.Logger() 68 self.valobj = valobj; 69 self.sys_params = params 70 self.update(); 71 72 def update(self): 73 logger = lldb.formatters.Logger.Logger() 74 self.adjust_for_architecture(); 75 76 def count(self): 77 logger = lldb.formatters.Logger.Logger() 78 stream = lldb.SBStream() 79 self.valobj.GetExpressionPath(stream) 80 expr = "(int)[" + stream.GetData() + " count]" 81 num_children_vo = self.valobj.CreateValueFromExpression("count",expr) 82 if num_children_vo.IsValid(): 83 return num_children_vo.GetValueAsUnsigned(0) 84 return '<variable is not NSSet>' 85 86 class NSSetI_SummaryProvider: 87 def adjust_for_architecture(self): 88 pass 89 90 def __init__(self, valobj, params): 91 logger = lldb.formatters.Logger.Logger() 92 self.valobj = valobj; 93 self.sys_params = params 94 if not(self.sys_params.types_cache.NSUInteger): 95 if self.sys_params.is_64_bit: 96 self.sys_params.types_cache.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong) 97 else: 98 self.sys_params.types_cache.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt) 99 self.update(); 100 101 def update(self): 102 logger = lldb.formatters.Logger.Logger() 103 self.adjust_for_architecture(); 104 105 # we just need to skip the ISA and the count immediately follows 106 def offset(self): 107 logger = lldb.formatters.Logger.Logger() 108 return self.sys_params.pointer_size 109 110 def count(self): 111 logger = lldb.formatters.Logger.Logger() 112 num_children_vo = self.valobj.CreateChildAtOffset("count", 113 self.offset(), 114 self.sys_params.types_cache.NSUInteger) 115 value = num_children_vo.GetValueAsUnsigned(0) 116 if value != None: 117 # the MSB on immutable sets seems to be taken by some other data 118 # not sure if it is a bug or some weird sort of feature, but masking it out 119 # gets the count right (unless, of course, someone's dictionaries grow 120 # too large - but I have not tested this) 121 if self.sys_params.is_64_bit: 122 value = value & ~0xFF00000000000000 123 else: 124 value = value & ~0xFF000000 125 return value 126 127 class NSSetM_SummaryProvider: 128 def adjust_for_architecture(self): 129 pass 130 131 def __init__(self, valobj, params): 132 logger = lldb.formatters.Logger.Logger() 133 self.valobj = valobj; 134 self.sys_params = params 135 if not(self.sys_params.types_cache.NSUInteger): 136 if self.sys_params.is_64_bit: 137 self.sys_params.types_cache.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong) 138 else: 139 self.sys_params.types_cache.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt) 140 self.update(); 141 142 def update(self): 143 logger = lldb.formatters.Logger.Logger() 144 self.adjust_for_architecture(); 145 146 # we just need to skip the ISA and the count immediately follows 147 def offset(self): 148 logger = lldb.formatters.Logger.Logger() 149 return self.sys_params.pointer_size 150 151 def count(self): 152 logger = lldb.formatters.Logger.Logger() 153 num_children_vo = self.valobj.CreateChildAtOffset("count", 154 self.offset(), 155 self.sys_params.types_cache.NSUInteger) 156 return num_children_vo.GetValueAsUnsigned(0) 157 158 159 class NSCountedSet_SummaryProvider: 160 def adjust_for_architecture(self): 161 pass 162 163 def __init__(self, valobj, params): 164 logger = lldb.formatters.Logger.Logger() 165 self.valobj = valobj; 166 self.sys_params = params 167 if not (self.sys_params.types_cache.voidptr): 168 self.sys_params.types_cache.voidptr = self.valobj.GetType().GetBasicType(lldb.eBasicTypeVoid).GetPointerType() 169 self.update(); 170 171 def update(self): 172 logger = lldb.formatters.Logger.Logger() 173 self.adjust_for_architecture(); 174 175 # an NSCountedSet is implemented using a CFBag whose pointer just follows the ISA 176 def offset(self): 177 logger = lldb.formatters.Logger.Logger() 178 return self.sys_params.pointer_size 179 180 def count(self): 181 logger = lldb.formatters.Logger.Logger() 182 cfbag_vo = self.valobj.CreateChildAtOffset("bag_impl", 183 self.offset(), 184 self.sys_params.types_cache.voidptr) 185 return CFBag.CFBagRef_SummaryProvider(cfbag_vo,self.sys_params).length() 186 187 188 def GetSummary_Impl(valobj): 189 logger = lldb.formatters.Logger.Logger() 190 global statistics 191 class_data,wrapper =lldb.runtime.objc.objc_runtime.Utilities.prepare_class_detection(valobj,statistics) 192 if wrapper: 193 return wrapper 194 195 name_string = class_data.class_name() 196 logger >> "class name is: " + str(name_string) 197 198 if name_string == '__NSCFSet': 199 wrapper = NSCFSet_SummaryProvider(valobj, class_data.sys_params) 200 statistics.metric_hit('code_notrun',valobj) 201 elif name_string == '__NSSetI': 202 wrapper = NSSetI_SummaryProvider(valobj, class_data.sys_params) 203 statistics.metric_hit('code_notrun',valobj) 204 elif name_string == '__NSSetM': 205 wrapper = NSSetM_SummaryProvider(valobj, class_data.sys_params) 206 statistics.metric_hit('code_notrun',valobj) 207 elif name_string == 'NSCountedSet': 208 wrapper = NSCountedSet_SummaryProvider(valobj, class_data.sys_params) 209 statistics.metric_hit('code_notrun',valobj) 210 else: 211 wrapper = NSSetUnknown_SummaryProvider(valobj, class_data.sys_params) 212 statistics.metric_hit('unknown_class',valobj.GetName() + " seen as " + name_string) 213 return wrapper; 214 215 216 def NSSet_SummaryProvider (valobj,dict): 217 logger = lldb.formatters.Logger.Logger() 218 provider = GetSummary_Impl(valobj); 219 if provider != None: 220 try: 221 summary = provider.count(); 222 except: 223 summary = None 224 if summary == None: 225 summary = '<variable is not NSSet>' 226 if isinstance(summary, basestring): 227 return summary 228 else: 229 summary = str(summary) + (' objects' if summary != 1 else ' object') 230 return summary 231 return 'Summary Unavailable' 232 233 def NSSet_SummaryProvider2 (valobj,dict): 234 logger = lldb.formatters.Logger.Logger() 235 provider = GetSummary_Impl(valobj); 236 if provider != None: 237 if isinstance(provider,lldb.runtime.objc.objc_runtime.SpecialSituation_Description): 238 return provider.message() 239 try: 240 summary = provider.count(); 241 except: 242 summary = None 243 logger >> "got summary " + str(summary) 244 # for some reason, one needs to clear some bits for the count returned 245 # to be correct when using directly CF*SetRef as compared to NS*Set 246 # this only happens on 64bit, and the bit mask was derived through 247 # experimentation (if counts start looking weird, then most probably 248 # the mask needs to be changed) 249 if summary == None: 250 summary = '<variable is not CFSet>' 251 if isinstance(summary, basestring): 252 return summary 253 else: 254 if provider.sys_params.is_64_bit: 255 summary = summary & ~0x1fff000000000000 256 summary = '@"' + str(summary) + (' values"' if summary != 1 else ' value"') 257 return summary 258 return 'Summary Unavailable' 259 260 261 def __lldb_init_module(debugger,dict): 262 debugger.HandleCommand("type summary add -F NSSet.NSSet_SummaryProvider NSSet") 263 debugger.HandleCommand("type summary add -F NSSet.NSSet_SummaryProvider2 CFSetRef CFMutableSetRef") 264