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 NSDictionary
      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 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 count for an NSDictionary, so they need not
     24 # obey the interface specification for synthetic children providers
     25 class NSCFDictionary_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 	# empirically determined on both 32 and 64bit desktop Mac OS X
     45 	# probably boils down to 2 pointers and 4 bytes of data, but
     46 	# the description of __CFDictionary is not readily available so most
     47 	# of this is guesswork, plain and simple
     48 	def offset(self):
     49 		logger = lldb.formatters.Logger.Logger()
     50 		if self.sys_params.is_64_bit:
     51 			return 20
     52 		else:
     53 			return 12
     54 
     55 	def num_children(self):
     56 		logger = lldb.formatters.Logger.Logger()
     57 		num_children_vo = self.valobj.CreateChildAtOffset("count",
     58 							self.offset(),
     59 							self.sys_params.types_cache.NSUInteger)
     60 		return num_children_vo.GetValueAsUnsigned(0)
     61 
     62 
     63 class NSDictionaryI_SummaryProvider:
     64 	def adjust_for_architecture(self):
     65 		pass
     66 
     67 	def __init__(self, valobj, params):
     68 		logger = lldb.formatters.Logger.Logger()
     69 		self.valobj = valobj;
     70 		self.sys_params = params
     71 		if not(self.sys_params.types_cache.NSUInteger):
     72 			if self.sys_params.is_64_bit:
     73 				self.sys_params.types_cache.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong)
     74 			else:
     75 				self.sys_params.types_cache.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt)
     76 		self.update();
     77 
     78 	def update(self):
     79 		logger = lldb.formatters.Logger.Logger()
     80 		self.adjust_for_architecture();
     81 
     82 	# we just need to skip the ISA and the count immediately follows
     83 	def offset(self):
     84 		logger = lldb.formatters.Logger.Logger()
     85 		return self.sys_params.pointer_size
     86 
     87 	def num_children(self):
     88 		logger = lldb.formatters.Logger.Logger()
     89 		num_children_vo = self.valobj.CreateChildAtOffset("count",
     90 							self.offset(),
     91 							self.sys_params.types_cache.NSUInteger)
     92 		value = num_children_vo.GetValueAsUnsigned(0)
     93 		if value != None:
     94 			# the MS6bits on immutable dictionaries seem to be taken by the LSB of capacity
     95 			# not sure if it is a bug or some weird sort of feature, but masking that out
     96 			# gets the count right
     97 			if self.sys_params.is_64_bit:
     98 				value = value & ~0xFC00000000000000
     99 			else:
    100 				value = value & ~0xFC000000
    101 		return value
    102 
    103 class NSDictionaryM_SummaryProvider:
    104 	def adjust_for_architecture(self):
    105 		pass
    106 
    107 	def __init__(self, valobj, params):
    108 		logger = lldb.formatters.Logger.Logger()
    109 		self.valobj = valobj;
    110 		self.sys_params = params
    111 		if not(self.sys_params.types_cache.NSUInteger):
    112 			if self.sys_params.is_64_bit:
    113 				self.sys_params.types_cache.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong)
    114 			else:
    115 				self.sys_params.types_cache.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt)
    116 		self.update();
    117 
    118 	def update(self):
    119 		logger = lldb.formatters.Logger.Logger()
    120 		self.adjust_for_architecture();
    121 
    122 	# we just need to skip the ISA and the count immediately follows
    123 	def offset(self):
    124 		return self.sys_params.pointer_size
    125 
    126 	def num_children(self):
    127 		logger = lldb.formatters.Logger.Logger()
    128 		num_children_vo = self.valobj.CreateChildAtOffset("count",
    129 							self.offset(),
    130 							self.sys_params.types_cache.NSUInteger)
    131 		value = num_children_vo.GetValueAsUnsigned(0)
    132 		if value != None:
    133 			# the MS6bits on immutable dictionaries seem to be taken by the LSB of capacity
    134 			# not sure if it is a bug or some weird sort of feature, but masking that out
    135 			# gets the count right
    136 			if self.sys_params.is_64_bit:
    137 				value = value & ~0xFC00000000000000
    138 			else:
    139 				value = value & ~0xFC000000
    140 		return value
    141 
    142 class NSDictionaryUnknown_SummaryProvider:
    143 	def adjust_for_architecture(self):
    144 		pass
    145 
    146 	def __init__(self, valobj, params):
    147 		logger = lldb.formatters.Logger.Logger()
    148 		self.valobj = valobj;
    149 		self.sys_params = params
    150 		self.update();
    151 
    152 	def update(self):
    153 		logger = lldb.formatters.Logger.Logger()
    154 		self.adjust_for_architecture();
    155 
    156 	def num_children(self):
    157 		logger = lldb.formatters.Logger.Logger()
    158 		stream = lldb.SBStream()
    159 		self.valobj.GetExpressionPath(stream)
    160 		num_children_vo = self.valobj.CreateValueFromExpression("count","(int)[" + stream.GetData() + " count]");
    161 		if num_children_vo.IsValid():
    162 			return num_children_vo.GetValueAsUnsigned(0)
    163 		return '<variable is not NSDictionary>'
    164 
    165 
    166 def GetSummary_Impl(valobj):
    167 	logger = lldb.formatters.Logger.Logger()
    168 	global statistics
    169 	class_data,wrapper =lldb.runtime.objc.objc_runtime.Utilities.prepare_class_detection(valobj,statistics)
    170 	if wrapper:
    171 		return wrapper
    172 	
    173 	name_string = class_data.class_name()
    174 	
    175 	logger >> "class name is: " + str(name_string)
    176 	
    177 	if name_string == '__NSCFDictionary':
    178 		wrapper = NSCFDictionary_SummaryProvider(valobj, class_data.sys_params)
    179 		statistics.metric_hit('code_notrun',valobj)
    180 	elif name_string == '__NSDictionaryI':
    181 		wrapper = NSDictionaryI_SummaryProvider(valobj, class_data.sys_params)
    182 		statistics.metric_hit('code_notrun',valobj)
    183 	elif name_string == '__NSDictionaryM':
    184 		wrapper = NSDictionaryM_SummaryProvider(valobj, class_data.sys_params)
    185 		statistics.metric_hit('code_notrun',valobj)
    186 	else:
    187 		wrapper = NSDictionaryUnknown_SummaryProvider(valobj, class_data.sys_params)
    188 		statistics.metric_hit('unknown_class',valobj.GetName() + " seen as " + name_string)
    189 	return wrapper;
    190 
    191 def CFDictionary_SummaryProvider (valobj,dict):
    192 	logger = lldb.formatters.Logger.Logger()
    193 	provider = GetSummary_Impl(valobj);
    194 	if provider != None:
    195 		if isinstance(provider,lldb.runtime.objc.objc_runtime.SpecialSituation_Description):
    196 			return provider.message()
    197 		try:
    198 			summary = provider.num_children();
    199 		except:
    200 			summary = None
    201 		logger >> "got summary " + str(summary)
    202 		if summary == None:
    203 			return '<variable is not NSDictionary>'
    204 		if isinstance(summary,basestring):
    205 			return summary
    206 		return str(summary) + (" key/value pairs" if summary != 1 else " key/value pair")
    207 	return 'Summary Unavailable'
    208 
    209 def CFDictionary_SummaryProvider2 (valobj,dict):
    210 	logger = lldb.formatters.Logger.Logger()
    211 	provider = GetSummary_Impl(valobj);
    212 	if provider != None:
    213 		if isinstance(provider,lldb.runtime.objc.objc_runtime.SpecialSituation_Description):
    214 			return provider.message()
    215 		try:
    216 			summary = provider.num_children();
    217 		except:
    218 			summary = None
    219 		logger >> "got summary " + str(summary)
    220 		if summary == None:
    221 			summary = '<variable is not CFDictionary>'
    222 		if isinstance(summary,basestring):
    223 			return summary
    224 		else:
    225 		# needed on OSX Mountain Lion
    226 			if provider.sys_params.is_64_bit:
    227 				summary = summary & ~0x0f1f000000000000
    228 			summary = '@"' + str(summary) + (' entries"' if summary != 1 else ' entry"')
    229 		return summary
    230 	return 'Summary Unavailable'
    231 
    232 def __lldb_init_module(debugger,dict):
    233 	debugger.HandleCommand("type summary add -F CFDictionary.CFDictionary_SummaryProvider NSDictionary")
    234 	debugger.HandleCommand("type summary add -F CFDictionary.CFDictionary_SummaryProvider2 CFDictionaryRef CFMutableDictionaryRef")
    235