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 NSDate
      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 time
     16 import datetime
     17 import CFString
     18 import lldb.formatters.Logger
     19 
     20 statistics = lldb.formatters.metrics.Metrics()
     21 statistics.add_metric('invalid_isa')
     22 statistics.add_metric('invalid_pointer')
     23 statistics.add_metric('unknown_class')
     24 statistics.add_metric('code_notrun')
     25 
     26 # Python promises to start counting time at midnight on Jan 1st on the epoch year
     27 # hence, all we need to know is the epoch year
     28 python_epoch = time.gmtime(0).tm_year
     29 
     30 osx_epoch = datetime.date(2001,1,1).timetuple()
     31 
     32 def mkgmtime(t):
     33 	logger = lldb.formatters.Logger.Logger()
     34 	return time.mktime(t)-time.timezone
     35 
     36 osx_epoch = mkgmtime(osx_epoch)
     37 
     38 def osx_to_python_time(osx):
     39 	logger = lldb.formatters.Logger.Logger()
     40 	if python_epoch <= 2001:
     41 		return osx + osx_epoch
     42 	else:
     43 		return osx - osx_epoch
     44 
     45 # represent a struct_time as a string in the format used by Xcode
     46 def xcode_format_time(X):
     47 	logger = lldb.formatters.Logger.Logger()
     48 	return time.strftime('%Y-%m-%d %H:%M:%S %Z',X)
     49 
     50 # represent a count-since-epoch as a string in the format used by Xcode
     51 def xcode_format_count(X):
     52 	logger = lldb.formatters.Logger.Logger()
     53 	return xcode_format_time(time.localtime(X))
     54 
     55 # despite the similary to synthetic children providers, these classes are not
     56 # trying to provide anything but the summary for NSDate, so they need not
     57 # obey the interface specification for synthetic children providers
     58 class NSTaggedDate_SummaryProvider:
     59 	def adjust_for_architecture(self):
     60 		pass
     61 
     62 	def __init__(self, valobj, info_bits, data, params):
     63 		logger = lldb.formatters.Logger.Logger()
     64 		self.valobj = valobj;
     65 		self.sys_params = params
     66 		self.update();
     67 		# NSDate is not using its info_bits for info like NSNumber is
     68 		# so we need to regroup info_bits and data
     69 		self.data = ((data << 8) | (info_bits << 4))
     70 
     71 	def update(self):
     72 		logger = lldb.formatters.Logger.Logger()
     73 		self.adjust_for_architecture();
     74 
     75 	def value(self):
     76 		logger = lldb.formatters.Logger.Logger()
     77 		# the value of the date-time object is wrapped into the pointer value
     78 		# unfortunately, it is made as a time-delta after Jan 1 2001 midnight GMT
     79 		# while all Python knows about is the "epoch", which is a platform-dependent
     80 		# year (1970 of *nix) whose Jan 1 at midnight is taken as reference
     81 		value_double = struct.unpack('d', struct.pack('Q', self.data))[0]
     82 		if value_double == -63114076800.0:
     83 			return '0001-12-30 00:00:00 +0000'
     84 		return xcode_format_count(osx_to_python_time(value_double))
     85 
     86 
     87 class NSUntaggedDate_SummaryProvider:
     88 	def adjust_for_architecture(self):
     89 		pass
     90 
     91 	def __init__(self, valobj, params):
     92 		logger = lldb.formatters.Logger.Logger()
     93 		self.valobj = valobj;
     94 		self.sys_params = params
     95 		if not (self.sys_params.types_cache.double):
     96 			self.sys_params.types_cache.double = self.valobj.GetType().GetBasicType(lldb.eBasicTypeDouble)
     97 		self.update()
     98 
     99 	def update(self):
    100 		logger = lldb.formatters.Logger.Logger()
    101 		self.adjust_for_architecture();
    102 
    103 	def offset(self):
    104 		logger = lldb.formatters.Logger.Logger()
    105 		return self.sys_params.pointer_size
    106 
    107 	def value(self):
    108 		logger = lldb.formatters.Logger.Logger()
    109 		value = self.valobj.CreateChildAtOffset("value",
    110 							self.offset(),
    111 							self.sys_params.types_cache.double)
    112 		value_double = struct.unpack('d', struct.pack('Q', value.GetData().uint64[0]))[0]
    113 		if value_double == -63114076800.0:
    114 			return '0001-12-30 00:00:00 +0000'
    115 		return xcode_format_count(osx_to_python_time(value_double))
    116 
    117 class NSCalendarDate_SummaryProvider:
    118 	def adjust_for_architecture(self):
    119 		pass
    120 
    121 	def __init__(self, valobj, params):
    122 		logger = lldb.formatters.Logger.Logger()
    123 		self.valobj = valobj;
    124 		self.sys_params = params
    125 		if not (self.sys_params.types_cache.double):
    126 			self.sys_params.types_cache.double = self.valobj.GetType().GetBasicType(lldb.eBasicTypeDouble)
    127 		self.update()
    128 
    129 	def update(self):
    130 		logger = lldb.formatters.Logger.Logger()
    131 		self.adjust_for_architecture();
    132 
    133 	def offset(self):
    134 		logger = lldb.formatters.Logger.Logger()
    135 		return 2*self.sys_params.pointer_size
    136 
    137 	def value(self):
    138 		logger = lldb.formatters.Logger.Logger()
    139 		value = self.valobj.CreateChildAtOffset("value",
    140 							self.offset(),
    141 							self.sys_params.types_cache.double)
    142 		value_double = struct.unpack('d', struct.pack('Q', value.GetData().uint64[0]))[0]
    143 		return xcode_format_count(osx_to_python_time(value_double))
    144 
    145 class NSTimeZoneClass_SummaryProvider:
    146 	def adjust_for_architecture(self):
    147 		pass
    148 
    149 	def __init__(self, valobj, params):
    150 		logger = lldb.formatters.Logger.Logger()
    151 		self.valobj = valobj;
    152 		self.sys_params = params
    153 		if not (self.sys_params.types_cache.voidptr):
    154 			self.sys_params.types_cache.voidptr = self.valobj.GetType().GetBasicType(lldb.eBasicTypeVoid).GetPointerType()
    155 		self.update()
    156 
    157 	def update(self):
    158 		logger = lldb.formatters.Logger.Logger()
    159 		self.adjust_for_architecture();
    160 
    161 	def offset(self):
    162 		logger = lldb.formatters.Logger.Logger()
    163 		return self.sys_params.pointer_size
    164 
    165 	def timezone(self):
    166 		logger = lldb.formatters.Logger.Logger()
    167 		tz_string = self.valobj.CreateChildAtOffset("tz_name",
    168 							self.offset(),
    169 							self.sys_params.types_cache.voidptr)
    170 		return CFString.CFString_SummaryProvider(tz_string,None)
    171 
    172 class NSUnknownDate_SummaryProvider:
    173 	def adjust_for_architecture(self):
    174 		pass
    175 
    176 	def __init__(self, valobj):
    177 		logger = lldb.formatters.Logger.Logger()
    178 		self.valobj = valobj;
    179 		self.update()
    180 
    181 	def update(self):
    182 		logger = lldb.formatters.Logger.Logger()
    183 		self.adjust_for_architecture();
    184 
    185 	def value(self):
    186 		logger = lldb.formatters.Logger.Logger()
    187 		stream = lldb.SBStream()
    188 		self.valobj.GetExpressionPath(stream)
    189 		expr = "(NSString*)[" + stream.GetData() + " description]"
    190 		num_children_vo = self.valobj.CreateValueFromExpression("str",expr);
    191 		if num_children_vo.IsValid():
    192 			return num_children_vo.GetSummary()
    193 		return '<variable is not NSDate>'
    194 
    195 def GetSummary_Impl(valobj):
    196 	logger = lldb.formatters.Logger.Logger()
    197 	global statistics
    198 	class_data,wrapper =lldb.runtime.objc.objc_runtime.Utilities.prepare_class_detection(valobj,statistics)
    199 	if wrapper:
    200 		return wrapper
    201 	
    202 	name_string = class_data.class_name()
    203 	logger >> "class name is: " + str(name_string)
    204 
    205 	if name_string == 'NSDate' or name_string == '__NSDate' or name_string == '__NSTaggedDate':
    206 		if class_data.is_tagged():
    207 			wrapper = NSTaggedDate_SummaryProvider(valobj,class_data.info_bits(),class_data.value(), class_data.sys_params)
    208 			statistics.metric_hit('code_notrun',valobj)
    209 		else:
    210 			wrapper = NSUntaggedDate_SummaryProvider(valobj, class_data.sys_params)
    211 			statistics.metric_hit('code_notrun',valobj)
    212 	elif name_string == 'NSCalendarDate':
    213 		wrapper = NSCalendarDate_SummaryProvider(valobj, class_data.sys_params)
    214 		statistics.metric_hit('code_notrun',valobj)
    215 	elif name_string == '__NSTimeZone':
    216 		wrapper = NSTimeZoneClass_SummaryProvider(valobj, class_data.sys_params)
    217 		statistics.metric_hit('code_notrun',valobj)
    218 	else:
    219 		wrapper = NSUnknownDate_SummaryProvider(valobj)
    220 		statistics.metric_hit('unknown_class',valobj.GetName() + " seen as " + name_string)
    221 	return wrapper;
    222 
    223 
    224 def NSDate_SummaryProvider (valobj,dict):
    225 	logger = lldb.formatters.Logger.Logger()
    226 	provider = GetSummary_Impl(valobj);
    227 	if provider != None:
    228 		if isinstance(provider,lldb.runtime.objc.objc_runtime.SpecialSituation_Description):
    229 			return provider.message()
    230 		try:
    231 			summary = provider.value();
    232 		except:
    233 			summary = None
    234 		if summary == None:
    235 			summary = '<variable is not NSDate>'
    236 		return str(summary)
    237 	return 'Summary Unavailable'
    238 
    239 def NSTimeZone_SummaryProvider (valobj,dict):
    240 	logger = lldb.formatters.Logger.Logger()
    241 	provider = GetSummary_Impl(valobj);
    242 	if provider != None:
    243 		if isinstance(provider,lldb.runtime.objc.objc_runtime.SpecialSituation_Description):
    244 			return provider.message()
    245 		try:
    246 			summary = provider.timezone();
    247 		except:
    248 			summary = None
    249 		logger >> "got summary " + str(summary)
    250 		if summary == None:
    251 			summary = '<variable is not NSTimeZone>'
    252 		return str(summary)
    253 	return 'Summary Unavailable'
    254 
    255 
    256 def CFAbsoluteTime_SummaryProvider (valobj,dict):
    257 	logger = lldb.formatters.Logger.Logger()
    258 	try:
    259 		value_double = struct.unpack('d', struct.pack('Q', valobj.GetData().uint64[0]))[0]
    260 		return xcode_format_count(osx_to_python_time(value_double))
    261 	except:
    262 		return 'Summary Unavailable'
    263 
    264 
    265 def __lldb_init_module(debugger,dict):
    266 	debugger.HandleCommand("type summary add -F NSDate.NSDate_SummaryProvider NSDate")
    267 	debugger.HandleCommand("type summary add -F NSDate.CFAbsoluteTime_SummaryProvider CFAbsoluteTime")
    268 	debugger.HandleCommand("type summary add -F NSDate.NSTimeZone_SummaryProvider NSTimeZone CFTimeZoneRef")
    269 
    270