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 # summary provider for NSSet
      9 import lldb
     10 import ctypes
     11 import lldb.runtime.objc.objc_runtime
     12 import lldb.formatters.metrics
     13 import CFBag
     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 port number of an NSMachPort, so they need not
     24 # obey the interface specification for synthetic children providers
     25 class NSCFSet_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 	# one pointer is the ISA
     45 	# then we have one other internal pointer, plus
     46 	# 4 bytes worth of flags. hence, these values
     47 	def offset(self):
     48 		logger = lldb.formatters.Logger.Logger()
     49 		if self.sys_params.is_64_bit:
     50 			return 20
     51 		else:
     52 			return 12
     53 
     54 	def count(self):
     55 		logger = lldb.formatters.Logger.Logger()
     56 		vcount = self.valobj.CreateChildAtOffset("count",
     57 							self.offset(),
     58 							self.sys_params.types_cache.NSUInteger)
     59 		return vcount.GetValueAsUnsigned(0)
     60 
     61 
     62 class NSSetUnknown_SummaryProvider:
     63 	def adjust_for_architecture(self):
     64 		pass
     65 
     66 	def __init__(self, valobj, params):
     67 		logger = lldb.formatters.Logger.Logger()
     68 		self.valobj = valobj;
     69 		self.sys_params = params
     70 		self.update();
     71 
     72 	def update(self):
     73 		logger = lldb.formatters.Logger.Logger()
     74 		self.adjust_for_architecture();
     75 
     76 	def count(self):
     77 		logger = lldb.formatters.Logger.Logger()
     78 		stream = lldb.SBStream()
     79 		self.valobj.GetExpressionPath(stream)
     80 		expr = "(int)[" + stream.GetData() + " count]"
     81 		num_children_vo = self.valobj.CreateValueFromExpression("count",expr)
     82 		if num_children_vo.IsValid():
     83 			return num_children_vo.GetValueAsUnsigned(0)
     84 		return '<variable is not NSSet>'
     85 
     86 class NSSetI_SummaryProvider:
     87 	def adjust_for_architecture(self):
     88 		pass
     89 
     90 	def __init__(self, valobj, params):
     91 		logger = lldb.formatters.Logger.Logger()
     92 		self.valobj = valobj;
     93 		self.sys_params = params
     94 		if not(self.sys_params.types_cache.NSUInteger):
     95 			if self.sys_params.is_64_bit:
     96 				self.sys_params.types_cache.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong)
     97 			else:
     98 				self.sys_params.types_cache.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt)
     99 		self.update();
    100 
    101 	def update(self):
    102 		logger = lldb.formatters.Logger.Logger()
    103 		self.adjust_for_architecture();
    104 
    105 	# we just need to skip the ISA and the count immediately follows
    106 	def offset(self):
    107 		logger = lldb.formatters.Logger.Logger()
    108 		return self.sys_params.pointer_size
    109 
    110 	def count(self):
    111 		logger = lldb.formatters.Logger.Logger()
    112 		num_children_vo = self.valobj.CreateChildAtOffset("count",
    113 							self.offset(),
    114 							self.sys_params.types_cache.NSUInteger)
    115 		value = num_children_vo.GetValueAsUnsigned(0)
    116 		if value != None:
    117 			# the MSB on immutable sets seems to be taken by some other data
    118 			# not sure if it is a bug or some weird sort of feature, but masking it out
    119 			# gets the count right (unless, of course, someone's dictionaries grow
    120 			#                       too large - but I have not tested this)
    121 			if self.sys_params.is_64_bit:
    122 				value = value & ~0xFF00000000000000
    123 			else:
    124 				value = value & ~0xFF000000
    125 		return value
    126 
    127 class NSSetM_SummaryProvider:
    128 	def adjust_for_architecture(self):
    129 		pass
    130 
    131 	def __init__(self, valobj, params):
    132 		logger = lldb.formatters.Logger.Logger()
    133 		self.valobj = valobj;
    134 		self.sys_params = params
    135 		if not(self.sys_params.types_cache.NSUInteger):
    136 			if self.sys_params.is_64_bit:
    137 				self.sys_params.types_cache.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong)
    138 			else:
    139 				self.sys_params.types_cache.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt)
    140 		self.update();
    141 
    142 	def update(self):
    143 		logger = lldb.formatters.Logger.Logger()
    144 		self.adjust_for_architecture();
    145 
    146 	# we just need to skip the ISA and the count immediately follows
    147 	def offset(self):
    148 		logger = lldb.formatters.Logger.Logger()
    149 		return self.sys_params.pointer_size
    150 
    151 	def count(self):
    152 		logger = lldb.formatters.Logger.Logger()
    153 		num_children_vo = self.valobj.CreateChildAtOffset("count",
    154 							self.offset(),
    155 							self.sys_params.types_cache.NSUInteger)
    156 		return num_children_vo.GetValueAsUnsigned(0)
    157 
    158 
    159 class NSCountedSet_SummaryProvider:
    160 	def adjust_for_architecture(self):
    161 		pass
    162 
    163 	def __init__(self, valobj, params):
    164 		logger = lldb.formatters.Logger.Logger()
    165 		self.valobj = valobj;
    166 		self.sys_params = params
    167 		if not (self.sys_params.types_cache.voidptr):
    168 			self.sys_params.types_cache.voidptr = self.valobj.GetType().GetBasicType(lldb.eBasicTypeVoid).GetPointerType()
    169 		self.update();
    170 
    171 	def update(self):
    172 		logger = lldb.formatters.Logger.Logger()
    173 		self.adjust_for_architecture();
    174 
    175 	# an NSCountedSet is implemented using a CFBag whose pointer just follows the ISA
    176 	def offset(self):
    177 		logger = lldb.formatters.Logger.Logger()
    178 		return self.sys_params.pointer_size
    179 
    180 	def count(self):
    181 		logger = lldb.formatters.Logger.Logger()
    182 		cfbag_vo = self.valobj.CreateChildAtOffset("bag_impl",
    183 							self.offset(),
    184 							self.sys_params.types_cache.voidptr)
    185 		return CFBag.CFBagRef_SummaryProvider(cfbag_vo,self.sys_params).length()
    186 
    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 == '__NSCFSet':
    199 		wrapper = NSCFSet_SummaryProvider(valobj, class_data.sys_params)
    200 		statistics.metric_hit('code_notrun',valobj)
    201 	elif name_string == '__NSSetI':
    202 		wrapper = NSSetI_SummaryProvider(valobj, class_data.sys_params)
    203 		statistics.metric_hit('code_notrun',valobj)
    204 	elif name_string == '__NSSetM':
    205 		wrapper = NSSetM_SummaryProvider(valobj, class_data.sys_params)
    206 		statistics.metric_hit('code_notrun',valobj)
    207 	elif name_string == 'NSCountedSet':
    208 		wrapper = NSCountedSet_SummaryProvider(valobj, class_data.sys_params)
    209 		statistics.metric_hit('code_notrun',valobj)
    210 	else:
    211 		wrapper = NSSetUnknown_SummaryProvider(valobj, class_data.sys_params)
    212 		statistics.metric_hit('unknown_class',valobj.GetName() + " seen as " + name_string)
    213 	return wrapper;
    214 
    215 
    216 def NSSet_SummaryProvider (valobj,dict):
    217 	logger = lldb.formatters.Logger.Logger()
    218 	provider = GetSummary_Impl(valobj);
    219 	if provider != None:
    220 		try:
    221 			summary = provider.count();
    222 		except:
    223 			summary = None
    224 		if summary == None:
    225 			summary = '<variable is not NSSet>'
    226 		if isinstance(summary, basestring):
    227 			return summary
    228 		else:
    229 			summary = str(summary) + (' objects' if summary != 1 else ' object')
    230 		return summary
    231 	return 'Summary Unavailable'
    232 
    233 def NSSet_SummaryProvider2 (valobj,dict):
    234 	logger = lldb.formatters.Logger.Logger()
    235 	provider = GetSummary_Impl(valobj);
    236 	if provider != None:
    237 		if isinstance(provider,lldb.runtime.objc.objc_runtime.SpecialSituation_Description):
    238 			return provider.message()
    239 		try:
    240 			summary = provider.count();
    241 		except:
    242 			summary = None
    243 		logger >> "got summary " + str(summary)
    244 		# for some reason, one needs to clear some bits for the count returned
    245 		# to be correct when using directly CF*SetRef as compared to NS*Set
    246 		# this only happens on 64bit, and the bit mask was derived through
    247 		# experimentation (if counts start looking weird, then most probably
    248 		#                  the mask needs to be changed)
    249 		if summary == None:
    250 			summary = '<variable is not CFSet>'
    251 		if isinstance(summary, basestring):
    252 			return summary
    253 		else:
    254 			if provider.sys_params.is_64_bit:
    255 				summary = summary & ~0x1fff000000000000
    256 		 	summary = '@"' + str(summary) + (' values"' if summary != 1 else ' value"')
    257 		return summary
    258 	return 'Summary Unavailable'
    259 
    260 
    261 def __lldb_init_module(debugger,dict):
    262 	debugger.HandleCommand("type summary add -F NSSet.NSSet_SummaryProvider NSSet")
    263 	debugger.HandleCommand("type summary add -F NSSet.NSSet_SummaryProvider2 CFSetRef CFMutableSetRef")
    264