322 lines · python
1"""2LLDB AppKit formatters3 4Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.5See https://llvm.org/LICENSE.txt for license information.6SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception7"""8# example summary provider for NSDate9# the real summary is now C++ code built into LLDB10import lldb11import ctypes12import lldb.runtime.objc.objc_runtime13import lldb.formatters.metrics14import struct15import time16import datetime17import CFString18import lldb.formatters.Logger19 20statistics = lldb.formatters.metrics.Metrics()21statistics.add_metric("invalid_isa")22statistics.add_metric("invalid_pointer")23statistics.add_metric("unknown_class")24statistics.add_metric("code_notrun")25 26# Python promises to start counting time at midnight on Jan 1st on the epoch year27# hence, all we need to know is the epoch year28python_epoch = time.gmtime(0).tm_year29 30osx_epoch = datetime.date(2001, 1, 1).timetuple()31 32 33def mkgmtime(t):34 logger = lldb.formatters.Logger.Logger()35 return time.mktime(t) - time.timezone36 37 38osx_epoch = mkgmtime(osx_epoch)39 40 41def osx_to_python_time(osx):42 logger = lldb.formatters.Logger.Logger()43 if python_epoch <= 2001:44 return osx + osx_epoch45 else:46 return osx - osx_epoch47 48 49# represent a struct_time as a string in the format used by Xcode50 51 52def xcode_format_time(X):53 logger = lldb.formatters.Logger.Logger()54 return time.strftime("%Y-%m-%d %H:%M:%S %Z", X)55 56 57# represent a count-since-epoch as a string in the format used by Xcode58 59 60def xcode_format_count(X):61 logger = lldb.formatters.Logger.Logger()62 return xcode_format_time(time.localtime(X))63 64 65# despite the similary to synthetic children providers, these classes are not66# trying to provide anything but the summary for NSDate, so they need not67# obey the interface specification for synthetic children providers68 69 70class NSTaggedDate_SummaryProvider:71 def adjust_for_architecture(self):72 pass73 74 def __init__(self, valobj, info_bits, data, params):75 logger = lldb.formatters.Logger.Logger()76 self.valobj = valobj77 self.sys_params = params78 self.update()79 # NSDate is not using its info_bits for info like NSNumber is80 # so we need to regroup info_bits and data81 self.data = (data << 8) | (info_bits << 4)82 83 def update(self):84 logger = lldb.formatters.Logger.Logger()85 self.adjust_for_architecture()86 87 def value(self):88 logger = lldb.formatters.Logger.Logger()89 # the value of the date-time object is wrapped into the pointer value90 # unfortunately, it is made as a time-delta after Jan 1 2001 midnight GMT91 # while all Python knows about is the "epoch", which is a platform-dependent92 # year (1970 of *nix) whose Jan 1 at midnight is taken as reference93 value_double = struct.unpack("d", struct.pack("Q", self.data))[0]94 if value_double == -63114076800.0:95 return "0001-12-30 00:00:00 +0000"96 return xcode_format_count(osx_to_python_time(value_double))97 98 99class NSUntaggedDate_SummaryProvider:100 def adjust_for_architecture(self):101 pass102 103 def __init__(self, valobj, params):104 logger = lldb.formatters.Logger.Logger()105 self.valobj = valobj106 self.sys_params = params107 if not (self.sys_params.types_cache.double):108 self.sys_params.types_cache.double = self.valobj.GetType().GetBasicType(109 lldb.eBasicTypeDouble110 )111 self.update()112 113 def update(self):114 logger = lldb.formatters.Logger.Logger()115 self.adjust_for_architecture()116 117 def offset(self):118 logger = lldb.formatters.Logger.Logger()119 return self.sys_params.pointer_size120 121 def value(self):122 logger = lldb.formatters.Logger.Logger()123 value = self.valobj.CreateChildAtOffset(124 "value", self.offset(), self.sys_params.types_cache.double125 )126 value_double = struct.unpack("d", struct.pack("Q", value.GetData().uint64[0]))[127 0128 ]129 if value_double == -63114076800.0:130 return "0001-12-30 00:00:00 +0000"131 return xcode_format_count(osx_to_python_time(value_double))132 133 134class NSCalendarDate_SummaryProvider:135 def adjust_for_architecture(self):136 pass137 138 def __init__(self, valobj, params):139 logger = lldb.formatters.Logger.Logger()140 self.valobj = valobj141 self.sys_params = params142 if not (self.sys_params.types_cache.double):143 self.sys_params.types_cache.double = self.valobj.GetType().GetBasicType(144 lldb.eBasicTypeDouble145 )146 self.update()147 148 def update(self):149 logger = lldb.formatters.Logger.Logger()150 self.adjust_for_architecture()151 152 def offset(self):153 logger = lldb.formatters.Logger.Logger()154 return 2 * self.sys_params.pointer_size155 156 def value(self):157 logger = lldb.formatters.Logger.Logger()158 value = self.valobj.CreateChildAtOffset(159 "value", self.offset(), self.sys_params.types_cache.double160 )161 value_double = struct.unpack("d", struct.pack("Q", value.GetData().uint64[0]))[162 0163 ]164 return xcode_format_count(osx_to_python_time(value_double))165 166 167class NSTimeZoneClass_SummaryProvider:168 def adjust_for_architecture(self):169 pass170 171 def __init__(self, valobj, params):172 logger = lldb.formatters.Logger.Logger()173 self.valobj = valobj174 self.sys_params = params175 if not (self.sys_params.types_cache.voidptr):176 self.sys_params.types_cache.voidptr = (177 self.valobj.GetType().GetBasicType(lldb.eBasicTypeVoid).GetPointerType()178 )179 self.update()180 181 def update(self):182 logger = lldb.formatters.Logger.Logger()183 self.adjust_for_architecture()184 185 def offset(self):186 logger = lldb.formatters.Logger.Logger()187 return self.sys_params.pointer_size188 189 def timezone(self):190 logger = lldb.formatters.Logger.Logger()191 tz_string = self.valobj.CreateChildAtOffset(192 "tz_name", self.offset(), self.sys_params.types_cache.voidptr193 )194 return CFString.CFString_SummaryProvider(tz_string, None)195 196 197class NSUnknownDate_SummaryProvider:198 def adjust_for_architecture(self):199 pass200 201 def __init__(self, valobj):202 logger = lldb.formatters.Logger.Logger()203 self.valobj = valobj204 self.update()205 206 def update(self):207 logger = lldb.formatters.Logger.Logger()208 self.adjust_for_architecture()209 210 def value(self):211 logger = lldb.formatters.Logger.Logger()212 stream = lldb.SBStream()213 self.valobj.GetExpressionPath(stream)214 expr = "(NSString*)[" + stream.GetData() + " description]"215 num_children_vo = self.valobj.CreateValueFromExpression("str", expr)216 if num_children_vo.IsValid():217 return num_children_vo.GetSummary()218 return "<variable is not NSDate>"219 220 221def GetSummary_Impl(valobj):222 logger = lldb.formatters.Logger.Logger()223 global statistics224 (225 class_data,226 wrapper,227 ) = lldb.runtime.objc.objc_runtime.Utilities.prepare_class_detection(228 valobj, statistics229 )230 if wrapper:231 return wrapper232 233 name_string = class_data.class_name()234 logger >> "class name is: " + str(name_string)235 236 if (237 name_string == "NSDate"238 or name_string == "__NSDate"239 or name_string == "__NSTaggedDate"240 ):241 if class_data.is_tagged():242 wrapper = NSTaggedDate_SummaryProvider(243 valobj,244 class_data.info_bits(),245 class_data.value(),246 class_data.sys_params,247 )248 statistics.metric_hit("code_notrun", valobj)249 else:250 wrapper = NSUntaggedDate_SummaryProvider(valobj, class_data.sys_params)251 statistics.metric_hit("code_notrun", valobj)252 elif name_string == "NSCalendarDate":253 wrapper = NSCalendarDate_SummaryProvider(valobj, class_data.sys_params)254 statistics.metric_hit("code_notrun", valobj)255 elif name_string == "__NSTimeZone":256 wrapper = NSTimeZoneClass_SummaryProvider(valobj, class_data.sys_params)257 statistics.metric_hit("code_notrun", valobj)258 else:259 wrapper = NSUnknownDate_SummaryProvider(valobj)260 statistics.metric_hit(261 "unknown_class", valobj.GetName() + " seen as " + name_string262 )263 return wrapper264 265 266def NSDate_SummaryProvider(valobj, dict):267 logger = lldb.formatters.Logger.Logger()268 provider = GetSummary_Impl(valobj)269 if provider is not None:270 if isinstance(271 provider, lldb.runtime.objc.objc_runtime.SpecialSituation_Description272 ):273 return provider.message()274 try:275 summary = provider.value()276 except:277 summary = None278 if summary is None:279 summary = "<variable is not NSDate>"280 return str(summary)281 return "Summary Unavailable"282 283 284def NSTimeZone_SummaryProvider(valobj, dict):285 logger = lldb.formatters.Logger.Logger()286 provider = GetSummary_Impl(valobj)287 if provider is not None:288 if isinstance(289 provider, lldb.runtime.objc.objc_runtime.SpecialSituation_Description290 ):291 return provider.message()292 try:293 summary = provider.timezone()294 except:295 summary = None296 logger >> "got summary " + str(summary)297 if summary is None:298 summary = "<variable is not NSTimeZone>"299 return str(summary)300 return "Summary Unavailable"301 302 303def CFAbsoluteTime_SummaryProvider(valobj, dict):304 logger = lldb.formatters.Logger.Logger()305 try:306 value_double = struct.unpack("d", struct.pack("Q", valobj.GetData().uint64[0]))[307 0308 ]309 return xcode_format_count(osx_to_python_time(value_double))310 except:311 return "Summary Unavailable"312 313 314def __lldb_init_module(debugger, dict):315 debugger.HandleCommand("type summary add -F NSDate.NSDate_SummaryProvider NSDate")316 debugger.HandleCommand(317 "type summary add -F NSDate.CFAbsoluteTime_SummaryProvider CFAbsoluteTime"318 )319 debugger.HandleCommand(320 "type summary add -F NSDate.NSTimeZone_SummaryProvider NSTimeZone CFTimeZoneRef"321 )322