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