1""" 2LLDB AppKit formatters 3 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-exception 7""" 8# example summary provider for CFBinaryHeap 9# the real summary is now C++ code built into LLDB 10import lldb 11import ctypes 12import lldb.runtime.objc.objc_runtime 13import lldb.formatters.metrics 14import lldb.formatters.Logger 15 16statistics = lldb.formatters.metrics.Metrics() 17statistics.add_metric('invalid_isa') 18statistics.add_metric('invalid_pointer') 19statistics.add_metric('unknown_class') 20statistics.add_metric('code_notrun') 21 22# despite the similary to synthetic children providers, these classes are not 23# trying to provide anything but the length for an CFBinaryHeap, so they need not 24# obey the interface specification for synthetic children providers 25 26 27class CFBinaryHeapRef_SummaryProvider: 28 29 def adjust_for_architecture(self): 30 pass 31 32 def __init__(self, valobj, params): 33 logger = lldb.formatters.Logger.Logger() 34 self.valobj = valobj 35 self.sys_params = params 36 if not(self.sys_params.types_cache.NSUInteger): 37 if self.sys_params.is_64_bit: 38 self.sys_params.types_cache.NSUInteger = self.valobj.GetType( 39 ).GetBasicType(lldb.eBasicTypeUnsignedLong) 40 else: 41 self.sys_params.types_cache.NSUInteger = self.valobj.GetType( 42 ).GetBasicType(lldb.eBasicTypeUnsignedInt) 43 self.update() 44 45 def update(self): 46 logger = lldb.formatters.Logger.Logger() 47 self.adjust_for_architecture() 48 49 # 8 bytes on i386 50 # 16 bytes on x64 51 # most probably 2 pointers 52 def offset(self): 53 logger = lldb.formatters.Logger.Logger() 54 return 2 * self.sys_params.pointer_size 55 56 def length(self): 57 logger = lldb.formatters.Logger.Logger() 58 size = self.valobj.CreateChildAtOffset( 59 "count", self.offset(), self.sys_params.types_cache.NSUInteger) 60 return size.GetValueAsUnsigned(0) 61 62 63class CFBinaryHeapUnknown_SummaryProvider: 64 65 def adjust_for_architecture(self): 66 pass 67 68 def __init__(self, valobj, params): 69 logger = lldb.formatters.Logger.Logger() 70 self.valobj = valobj 71 self.sys_params = params 72 self.update() 73 74 def update(self): 75 logger = lldb.formatters.Logger.Logger() 76 self.adjust_for_architecture() 77 78 def length(self): 79 logger = lldb.formatters.Logger.Logger() 80 stream = lldb.SBStream() 81 self.valobj.GetExpressionPath(stream) 82 num_children_vo = self.valobj.CreateValueFromExpression( 83 "count", "(int)CFBinaryHeapGetCount(" + stream.GetData() + " )") 84 if num_children_vo.IsValid(): 85 return num_children_vo.GetValueAsUnsigned(0) 86 return '<variable is not CFBinaryHeap>' 87 88 89def GetSummary_Impl(valobj): 90 logger = lldb.formatters.Logger.Logger() 91 global statistics 92 class_data, wrapper = lldb.runtime.objc.objc_runtime.Utilities.prepare_class_detection( 93 valobj, statistics) 94 if wrapper: 95 return wrapper 96 97 name_string = class_data.class_name() 98 actual_name = class_data.class_name() 99 100 logger >> "name string got was " + \ 101 str(name_string) + " but actual name is " + str(actual_name) 102 103 if class_data.is_cftype(): 104 # CFBinaryHeap does not expose an actual NSWrapper type, so we have to check that this is 105 # an NSCFType and then check we are a pointer-to CFBinaryHeap 106 valobj_type = valobj.GetType() 107 if valobj_type.IsValid() and valobj_type.IsPointerType(): 108 valobj_type = valobj_type.GetPointeeType() 109 if valobj_type.IsValid(): 110 actual_name = valobj_type.GetName() 111 if actual_name == '__CFBinaryHeap': 112 wrapper = CFBinaryHeapRef_SummaryProvider( 113 valobj, class_data.sys_params) 114 statistics.metric_hit('code_notrun', valobj) 115 return wrapper 116 wrapper = CFBinaryHeapUnknown_SummaryProvider( 117 valobj, class_data.sys_params) 118 statistics.metric_hit( 119 'unknown_class', 120 valobj.GetName() + 121 " seen as " + 122 name_string) 123 return wrapper 124 125 126def CFBinaryHeap_SummaryProvider(valobj, dict): 127 logger = lldb.formatters.Logger.Logger() 128 provider = GetSummary_Impl(valobj) 129 if provider is not None: 130 if isinstance( 131 provider, 132 lldb.runtime.objc.objc_runtime.SpecialSituation_Description): 133 return provider.message() 134 try: 135 summary = provider.length() 136 except: 137 summary = None 138 logger >> "summary got from provider: " + str(summary) 139 # for some reason, one needs to clear some bits for the count 140 # to be correct when using CF(Mutable)BagRef on x64 141 # the bit mask was derived through experimentation 142 # (if counts start looking weird, then most probably 143 # the mask needs to be changed) 144 if summary is None: 145 summary = '<variable is not CFBinaryHeap>' 146 elif isinstance(summary, str): 147 pass 148 else: 149 if provider.sys_params.is_64_bit: 150 summary = summary & ~0x1fff000000000000 151 if summary == 1: 152 return '@"1 item"' 153 else: 154 summary = '@"' + str(summary) + ' items"' 155 return summary 156 return 'Summary Unavailable' 157 158 159def __lldb_init_module(debugger, dict): 160 debugger.HandleCommand( 161 "type summary add -F CFBinaryHeap.CFBinaryHeap_SummaryProvider CFBinaryHeapRef") 162