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