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