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 # example summary provider for NSDictionary 9 # the real summary is now C++ code built into LLDB 10 import lldb 11 import ctypes 12 import lldb.runtime.objc.objc_runtime 13 import lldb.formatters.metrics 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 count for an NSDictionary, so they need not 24 # obey the interface specification for synthetic children providers 25 class NSCFDictionary_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 # empirically determined on both 32 and 64bit desktop Mac OS X 45 # probably boils down to 2 pointers and 4 bytes of data, but 46 # the description of __CFDictionary is not readily available so most 47 # of this is guesswork, plain and simple 48 def offset(self): 49 logger = lldb.formatters.Logger.Logger() 50 if self.sys_params.is_64_bit: 51 return 20 52 else: 53 return 12 54 55 def num_children(self): 56 logger = lldb.formatters.Logger.Logger() 57 num_children_vo = self.valobj.CreateChildAtOffset("count", 58 self.offset(), 59 self.sys_params.types_cache.NSUInteger) 60 return num_children_vo.GetValueAsUnsigned(0) 61 62 63 class NSDictionaryI_SummaryProvider: 64 def adjust_for_architecture(self): 65 pass 66 67 def __init__(self, valobj, params): 68 logger = lldb.formatters.Logger.Logger() 69 self.valobj = valobj; 70 self.sys_params = params 71 if not(self.sys_params.types_cache.NSUInteger): 72 if self.sys_params.is_64_bit: 73 self.sys_params.types_cache.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong) 74 else: 75 self.sys_params.types_cache.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt) 76 self.update(); 77 78 def update(self): 79 logger = lldb.formatters.Logger.Logger() 80 self.adjust_for_architecture(); 81 82 # we just need to skip the ISA and the count immediately follows 83 def offset(self): 84 logger = lldb.formatters.Logger.Logger() 85 return self.sys_params.pointer_size 86 87 def num_children(self): 88 logger = lldb.formatters.Logger.Logger() 89 num_children_vo = self.valobj.CreateChildAtOffset("count", 90 self.offset(), 91 self.sys_params.types_cache.NSUInteger) 92 value = num_children_vo.GetValueAsUnsigned(0) 93 if value != None: 94 # the MS6bits on immutable dictionaries seem to be taken by the LSB of capacity 95 # not sure if it is a bug or some weird sort of feature, but masking that out 96 # gets the count right 97 if self.sys_params.is_64_bit: 98 value = value & ~0xFC00000000000000 99 else: 100 value = value & ~0xFC000000 101 return value 102 103 class NSDictionaryM_SummaryProvider: 104 def adjust_for_architecture(self): 105 pass 106 107 def __init__(self, valobj, params): 108 logger = lldb.formatters.Logger.Logger() 109 self.valobj = valobj; 110 self.sys_params = params 111 if not(self.sys_params.types_cache.NSUInteger): 112 if self.sys_params.is_64_bit: 113 self.sys_params.types_cache.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong) 114 else: 115 self.sys_params.types_cache.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt) 116 self.update(); 117 118 def update(self): 119 logger = lldb.formatters.Logger.Logger() 120 self.adjust_for_architecture(); 121 122 # we just need to skip the ISA and the count immediately follows 123 def offset(self): 124 return self.sys_params.pointer_size 125 126 def num_children(self): 127 logger = lldb.formatters.Logger.Logger() 128 num_children_vo = self.valobj.CreateChildAtOffset("count", 129 self.offset(), 130 self.sys_params.types_cache.NSUInteger) 131 value = num_children_vo.GetValueAsUnsigned(0) 132 if value != None: 133 # the MS6bits on immutable dictionaries seem to be taken by the LSB of capacity 134 # not sure if it is a bug or some weird sort of feature, but masking that out 135 # gets the count right 136 if self.sys_params.is_64_bit: 137 value = value & ~0xFC00000000000000 138 else: 139 value = value & ~0xFC000000 140 return value 141 142 class NSDictionaryUnknown_SummaryProvider: 143 def adjust_for_architecture(self): 144 pass 145 146 def __init__(self, valobj, params): 147 logger = lldb.formatters.Logger.Logger() 148 self.valobj = valobj; 149 self.sys_params = params 150 self.update(); 151 152 def update(self): 153 logger = lldb.formatters.Logger.Logger() 154 self.adjust_for_architecture(); 155 156 def num_children(self): 157 logger = lldb.formatters.Logger.Logger() 158 stream = lldb.SBStream() 159 self.valobj.GetExpressionPath(stream) 160 num_children_vo = self.valobj.CreateValueFromExpression("count","(int)[" + stream.GetData() + " count]"); 161 if num_children_vo.IsValid(): 162 return num_children_vo.GetValueAsUnsigned(0) 163 return '<variable is not NSDictionary>' 164 165 166 def GetSummary_Impl(valobj): 167 logger = lldb.formatters.Logger.Logger() 168 global statistics 169 class_data,wrapper =lldb.runtime.objc.objc_runtime.Utilities.prepare_class_detection(valobj,statistics) 170 if wrapper: 171 return wrapper 172 173 name_string = class_data.class_name() 174 175 logger >> "class name is: " + str(name_string) 176 177 if name_string == '__NSCFDictionary': 178 wrapper = NSCFDictionary_SummaryProvider(valobj, class_data.sys_params) 179 statistics.metric_hit('code_notrun',valobj) 180 elif name_string == '__NSDictionaryI': 181 wrapper = NSDictionaryI_SummaryProvider(valobj, class_data.sys_params) 182 statistics.metric_hit('code_notrun',valobj) 183 elif name_string == '__NSDictionaryM': 184 wrapper = NSDictionaryM_SummaryProvider(valobj, class_data.sys_params) 185 statistics.metric_hit('code_notrun',valobj) 186 else: 187 wrapper = NSDictionaryUnknown_SummaryProvider(valobj, class_data.sys_params) 188 statistics.metric_hit('unknown_class',valobj.GetName() + " seen as " + name_string) 189 return wrapper; 190 191 def CFDictionary_SummaryProvider (valobj,dict): 192 logger = lldb.formatters.Logger.Logger() 193 provider = GetSummary_Impl(valobj); 194 if provider != None: 195 if isinstance(provider,lldb.runtime.objc.objc_runtime.SpecialSituation_Description): 196 return provider.message() 197 try: 198 summary = provider.num_children(); 199 except: 200 summary = None 201 logger >> "got summary " + str(summary) 202 if summary == None: 203 return '<variable is not NSDictionary>' 204 if isinstance(summary,basestring): 205 return summary 206 return str(summary) + (" key/value pairs" if summary != 1 else " key/value pair") 207 return 'Summary Unavailable' 208 209 def CFDictionary_SummaryProvider2 (valobj,dict): 210 logger = lldb.formatters.Logger.Logger() 211 provider = GetSummary_Impl(valobj); 212 if provider != None: 213 if isinstance(provider,lldb.runtime.objc.objc_runtime.SpecialSituation_Description): 214 return provider.message() 215 try: 216 summary = provider.num_children(); 217 except: 218 summary = None 219 logger >> "got summary " + str(summary) 220 if summary == None: 221 summary = '<variable is not CFDictionary>' 222 if isinstance(summary,basestring): 223 return summary 224 else: 225 # needed on OSX Mountain Lion 226 if provider.sys_params.is_64_bit: 227 summary = summary & ~0x0f1f000000000000 228 summary = '@"' + str(summary) + (' entries"' if summary != 1 else ' entry"') 229 return summary 230 return 'Summary Unavailable' 231 232 def __lldb_init_module(debugger,dict): 233 debugger.HandleCommand("type summary add -F CFDictionary.CFDictionary_SummaryProvider NSDictionary") 234 debugger.HandleCommand("type summary add -F CFDictionary.CFDictionary_SummaryProvider2 CFDictionaryRef CFMutableDictionaryRef") 235