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