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