1""" 2LLDB AppKit formatters 3 4Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 5See https://llvm.org/LICENSE.txt for license information. 6SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 7""" 8# example summary provider for NSNumber 9# the real summary is now C++ code built into LLDB 10 11import lldb 12import ctypes 13import lldb.runtime.objc.objc_runtime 14import lldb.formatters.metrics 15import struct 16import lldb.formatters.Logger 17 18statistics = lldb.formatters.metrics.Metrics() 19statistics.add_metric('invalid_isa') 20statistics.add_metric('invalid_pointer') 21statistics.add_metric('unknown_class') 22statistics.add_metric('code_notrun') 23 24# despite the similary to synthetic children providers, these classes are not 25# trying to provide anything but the port number of an NSNumber, so they need not 26# obey the interface specification for synthetic children providers 27 28 29class NSTaggedNumber_SummaryProvider: 30 31 def adjust_for_architecture(self): 32 pass 33 34 def __init__(self, valobj, info_bits, data, params): 35 logger = lldb.formatters.Logger.Logger() 36 self.valobj = valobj 37 self.sys_params = params 38 self.info_bits = info_bits 39 self.data = data 40 self.update() 41 42 def update(self): 43 logger = lldb.formatters.Logger.Logger() 44 self.adjust_for_architecture() 45 46 def value(self): 47 logger = lldb.formatters.Logger.Logger() 48 # in spite of the plenty of types made available by the public NSNumber API 49 # only a bunch of these are actually used in the internal implementation 50 # unfortunately, the original type information appears to be lost 51 # so we try to at least recover the proper magnitude of the data 52 if self.info_bits == 0: 53 return '(char)' + \ 54 str(ord(ctypes.c_char(chr(self.data % 256)).value)) 55 if self.info_bits == 4: 56 return '(short)' + \ 57 str(ctypes.c_short(self.data % (256 * 256)).value) 58 if self.info_bits == 8: 59 return '(int)' + str(ctypes.c_int(self.data % 60 (256 * 256 * 256 * 256)).value) 61 if self.info_bits == 12: 62 return '(long)' + str(ctypes.c_long(self.data).value) 63 else: 64 return 'unexpected value:(info=' + str(self.info_bits) + \ 65 ", value = " + str(self.data) + ')' 66 67 68class NSUntaggedNumber_SummaryProvider: 69 70 def adjust_for_architecture(self): 71 pass 72 73 def __init__(self, valobj, params): 74 logger = lldb.formatters.Logger.Logger() 75 self.valobj = valobj 76 self.sys_params = params 77 if not(self.sys_params.types_cache.char): 78 self.sys_params.types_cache.char = self.valobj.GetType( 79 ).GetBasicType(lldb.eBasicTypeChar) 80 if not(self.sys_params.types_cache.short): 81 self.sys_params.types_cache.short = self.valobj.GetType( 82 ).GetBasicType(lldb.eBasicTypeShort) 83 if not(self.sys_params.types_cache.ushort): 84 self.sys_params.types_cache.ushort = self.valobj.GetType( 85 ).GetBasicType(lldb.eBasicTypeUnsignedShort) 86 if not(self.sys_params.types_cache.int): 87 self.sys_params.types_cache.int = self.valobj.GetType().GetBasicType(lldb.eBasicTypeInt) 88 if not(self.sys_params.types_cache.long): 89 self.sys_params.types_cache.long = self.valobj.GetType( 90 ).GetBasicType(lldb.eBasicTypeLong) 91 if not(self.sys_params.types_cache.ulong): 92 self.sys_params.types_cache.ulong = self.valobj.GetType( 93 ).GetBasicType(lldb.eBasicTypeUnsignedLong) 94 if not(self.sys_params.types_cache.longlong): 95 self.sys_params.types_cache.longlong = self.valobj.GetType( 96 ).GetBasicType(lldb.eBasicTypeLongLong) 97 if not(self.sys_params.types_cache.ulonglong): 98 self.sys_params.types_cache.ulonglong = self.valobj.GetType( 99 ).GetBasicType(lldb.eBasicTypeUnsignedLongLong) 100 if not(self.sys_params.types_cache.float): 101 self.sys_params.types_cache.float = self.valobj.GetType( 102 ).GetBasicType(lldb.eBasicTypeFloat) 103 if not(self.sys_params.types_cache.double): 104 self.sys_params.types_cache.double = self.valobj.GetType( 105 ).GetBasicType(lldb.eBasicTypeDouble) 106 self.update() 107 108 def update(self): 109 logger = lldb.formatters.Logger.Logger() 110 self.adjust_for_architecture() 111 112 def value(self): 113 logger = lldb.formatters.Logger.Logger() 114 global statistics 115 # we need to skip the ISA, then the next byte tells us what to read 116 # we then skip one other full pointer worth of data and then fetch the contents 117 # if we are fetching an int64 value, one more pointer must be skipped 118 # to get at our data 119 data_type_vo = self.valobj.CreateChildAtOffset( 120 "dt", self.sys_params.pointer_size, self.sys_params.types_cache.char) 121 data_type = ((data_type_vo.GetValueAsUnsigned(0) % 256) & 0x1F) 122 data_offset = 2 * self.sys_params.pointer_size 123 if data_type == 0B00001: 124 data_vo = self.valobj.CreateChildAtOffset( 125 "data", data_offset, self.sys_params.types_cache.char) 126 statistics.metric_hit('code_notrun', self.valobj) 127 return '(char)' + \ 128 str(ord(ctypes.c_char(chr(data_vo.GetValueAsUnsigned(0))).value)) 129 elif data_type == 0B0010: 130 data_vo = self.valobj.CreateChildAtOffset( 131 "data", data_offset, self.sys_params.types_cache.short) 132 statistics.metric_hit('code_notrun', self.valobj) 133 return '(short)' + str( 134 ctypes.c_short( 135 data_vo.GetValueAsUnsigned(0) % 136 (256 * 256)).value) 137 # IF tagged pointers are possible on 32bit+v2 runtime 138 # (of which the only existing instance should be iOS) 139 # then values of this type might be tagged 140 elif data_type == 0B0011: 141 data_vo = self.valobj.CreateChildAtOffset( 142 "data", data_offset, self.sys_params.types_cache.int) 143 statistics.metric_hit('code_notrun', self.valobj) 144 return '(int)' + str(ctypes.c_int(data_vo.GetValueAsUnsigned(0) % 145 (256 * 256 * 256 * 256)).value) 146 # apparently, on is_64_bit architectures, these are the only values that will ever 147 # be represented by a non tagged pointers 148 elif data_type == 0B10001: 149 data_offset = data_offset + 8 # 8 is needed even if we are on 32bit 150 data_vo = self.valobj.CreateChildAtOffset( 151 "data", data_offset, self.sys_params.types_cache.longlong) 152 statistics.metric_hit('code_notrun', self.valobj) 153 return '(long)' + \ 154 str(ctypes.c_long(data_vo.GetValueAsUnsigned(0)).value) 155 elif data_type == 0B0100: 156 if self.sys_params.is_64_bit: 157 data_offset = data_offset + self.sys_params.pointer_size 158 data_vo = self.valobj.CreateChildAtOffset( 159 "data", data_offset, self.sys_params.types_cache.longlong) 160 statistics.metric_hit('code_notrun', self.valobj) 161 return '(long)' + \ 162 str(ctypes.c_long(data_vo.GetValueAsUnsigned(0)).value) 163 elif data_type == 0B0101: 164 data_vo = self.valobj.CreateChildAtOffset( 165 "data", data_offset, self.sys_params.types_cache.longlong) 166 data_plain = int( 167 str(data_vo.GetValueAsUnsigned(0) & 0x00000000FFFFFFFF)) 168 packed = struct.pack('I', data_plain) 169 data_float = struct.unpack('f', packed)[0] 170 statistics.metric_hit('code_notrun', self.valobj) 171 return '(float)' + str(data_float) 172 elif data_type == 0B0110: 173 data_vo = self.valobj.CreateChildAtOffset( 174 "data", data_offset, self.sys_params.types_cache.longlong) 175 data_plain = data_vo.GetValueAsUnsigned(0) 176 data_double = struct.unpack('d', struct.pack('Q', data_plain))[0] 177 statistics.metric_hit('code_notrun', self.valobj) 178 return '(double)' + str(data_double) 179 statistics.metric_hit( 180 'unknown_class', str( 181 valobj.GetName()) + " had unknown data_type " + str(data_type)) 182 return 'unexpected: dt = ' + str(data_type) 183 184 185class NSUnknownNumber_SummaryProvider: 186 187 def adjust_for_architecture(self): 188 pass 189 190 def __init__(self, valobj, params): 191 logger = lldb.formatters.Logger.Logger() 192 self.valobj = valobj 193 self.sys_params = params 194 self.update() 195 196 def update(self): 197 logger = lldb.formatters.Logger.Logger() 198 self.adjust_for_architecture() 199 200 def value(self): 201 logger = lldb.formatters.Logger.Logger() 202 stream = lldb.SBStream() 203 self.valobj.GetExpressionPath(stream) 204 expr = "(NSString*)[" + stream.GetData() + " stringValue]" 205 num_children_vo = self.valobj.CreateValueFromExpression("str", expr) 206 if num_children_vo.IsValid(): 207 return num_children_vo.GetSummary() 208 return '<variable is not NSNumber>' 209 210 211def GetSummary_Impl(valobj): 212 logger = lldb.formatters.Logger.Logger() 213 global statistics 214 class_data, wrapper = lldb.runtime.objc.objc_runtime.Utilities.prepare_class_detection( 215 valobj, statistics) 216 if wrapper: 217 return wrapper 218 219 name_string = class_data.class_name() 220 logger >> "class name is: " + str(name_string) 221 222 if name_string == 'NSNumber' or name_string == '__NSCFNumber': 223 if class_data.is_tagged(): 224 wrapper = NSTaggedNumber_SummaryProvider( 225 valobj, class_data.info_bits(), class_data.value(), class_data.sys_params) 226 statistics.metric_hit('code_notrun', valobj) 227 else: 228 # the wrapper might be unable to decipher what is into the NSNumber 229 # and then have to run code on it 230 wrapper = NSUntaggedNumber_SummaryProvider( 231 valobj, class_data.sys_params) 232 else: 233 wrapper = NSUnknownNumber_SummaryProvider( 234 valobj, class_data.sys_params) 235 statistics.metric_hit( 236 'unknown_class', 237 valobj.GetName() + 238 " seen as " + 239 name_string) 240 return wrapper 241 242 243def NSNumber_SummaryProvider(valobj, dict): 244 logger = lldb.formatters.Logger.Logger() 245 provider = GetSummary_Impl(valobj) 246 if provider is not None: 247 if isinstance( 248 provider, 249 lldb.runtime.objc.objc_runtime.SpecialSituation_Description): 250 return provider.message() 251 try: 252 summary = provider.value() 253 except Exception as foo: 254 print(foo) 255# except: 256 summary = None 257 logger >> "got summary " + str(summary) 258 if summary is None: 259 summary = '<variable is not NSNumber>' 260 return str(summary) 261 return 'Summary Unavailable' 262 263 264def __lldb_init_module(debugger, dict): 265 debugger.HandleCommand( 266 "type summary add -F NSNumber.NSNumber_SummaryProvider NSNumber") 267 debugger.HandleCommand( 268 "type summary add -F NSNumber.NSNumber_SummaryProvider __NSCFBoolean") 269 debugger.HandleCommand( 270 "type summary add -F NSNumber.NSNumber_SummaryProvider __NSCFNumber") 271