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 9# summary provider for CF(Mutable)BitVector 10import lldb 11import ctypes 12import lldb.runtime.objc.objc_runtime 13import lldb.formatters.metrics 14import lldb.formatters.Logger 15 16# first define some utility functions 17 18 19def byte_index(abs_pos): 20 logger = lldb.formatters.Logger.Logger() 21 return abs_pos / 8 22 23 24def bit_index(abs_pos): 25 logger = lldb.formatters.Logger.Logger() 26 return abs_pos & 7 27 28 29def get_bit(byte, index): 30 logger = lldb.formatters.Logger.Logger() 31 if index < 0 or index > 7: 32 return None 33 return (byte >> (7 - index)) & 1 34 35 36def grab_array_item_data(pointer, index): 37 logger = lldb.formatters.Logger.Logger() 38 return pointer.GetPointeeData(index, 1) 39 40 41statistics = lldb.formatters.metrics.Metrics() 42statistics.add_metric("invalid_isa") 43statistics.add_metric("invalid_pointer") 44statistics.add_metric("unknown_class") 45statistics.add_metric("code_notrun") 46 47# despite the similary to synthetic children providers, these classes are not 48# trying to provide anything but a summary for a CF*BitVector, so they need not 49# obey the interface specification for synthetic children providers 50 51 52class CFBitVectorKnown_SummaryProvider: 53 def adjust_for_architecture(self): 54 logger = lldb.formatters.Logger.Logger() 55 self.uiint_size = self.sys_params.types_cache.NSUInteger.GetByteSize() 56 pass 57 58 def __init__(self, valobj, params): 59 logger = lldb.formatters.Logger.Logger() 60 self.valobj = valobj 61 self.sys_params = params 62 if not (self.sys_params.types_cache.NSUInteger): 63 if self.sys_params.is_64_bit: 64 self.sys_params.types_cache.NSUInteger = ( 65 self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong) 66 ) 67 else: 68 self.sys_params.types_cache.NSUInteger = ( 69 self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt) 70 ) 71 if not (self.sys_params.types_cache.charptr): 72 self.sys_params.types_cache.charptr = ( 73 self.valobj.GetType().GetBasicType(lldb.eBasicTypeChar).GetPointerType() 74 ) 75 self.update() 76 77 def update(self): 78 logger = lldb.formatters.Logger.Logger() 79 self.adjust_for_architecture() 80 81 # we skip the CFRuntimeBase 82 # then the next CFIndex is the count 83 # then we skip another CFIndex and then we get at a byte array 84 # that wraps the individual bits 85 86 def contents(self): 87 logger = lldb.formatters.Logger.Logger() 88 count_vo = self.valobj.CreateChildAtOffset( 89 "count", 90 self.sys_params.cfruntime_size, 91 self.sys_params.types_cache.NSUInteger, 92 ) 93 count = count_vo.GetValueAsUnsigned(0) 94 if count == 0: 95 return "(empty)" 96 97 array_vo = self.valobj.CreateChildAtOffset( 98 "data", 99 self.sys_params.cfruntime_size + 2 * self.uiint_size, 100 self.sys_params.types_cache.charptr, 101 ) 102 103 data_list = [] 104 cur_byte_pos = None 105 for i in range(0, count): 106 if cur_byte_pos is None: 107 cur_byte_pos = byte_index(i) 108 cur_byte = grab_array_item_data(array_vo, cur_byte_pos) 109 cur_byte_val = cur_byte.uint8[0] 110 else: 111 byte_pos = byte_index(i) 112 # do not fetch the pointee data every single time through 113 if byte_pos != cur_byte_pos: 114 cur_byte_pos = byte_pos 115 cur_byte = grab_array_item_data(array_vo, cur_byte_pos) 116 cur_byte_val = cur_byte.uint8[0] 117 bit = get_bit(cur_byte_val, bit_index(i)) 118 if (i % 4) == 0: 119 data_list.append(" ") 120 if bit == 1: 121 data_list.append("1") 122 else: 123 data_list.append("0") 124 return "".join(data_list) 125 126 127class CFBitVectorUnknown_SummaryProvider: 128 def adjust_for_architecture(self): 129 pass 130 131 def __init__(self, valobj, params): 132 logger = lldb.formatters.Logger.Logger() 133 self.valobj = valobj 134 self.sys_params = params 135 self.update() 136 137 def update(self): 138 logger = lldb.formatters.Logger.Logger() 139 self.adjust_for_architecture() 140 141 def contents(self): 142 logger = lldb.formatters.Logger.Logger() 143 return "<unable to summarize this CFBitVector>" 144 145 146def GetSummary_Impl(valobj): 147 logger = lldb.formatters.Logger.Logger() 148 global statistics 149 ( 150 class_data, 151 wrapper, 152 ) = lldb.runtime.objc.objc_runtime.Utilities.prepare_class_detection( 153 valobj, statistics 154 ) 155 if wrapper: 156 return wrapper 157 158 name_string = class_data.class_name() 159 actual_name = name_string 160 161 logger >> "name string got was " + str(name_string) + " but actual name is " + str( 162 actual_name 163 ) 164 165 if class_data.is_cftype(): 166 # CFBitVectorRef does not expose an actual NSWrapper type, so we have to check that this is 167 # an NSCFType and then check we are a pointer-to CFBitVectorRef 168 valobj_type = valobj.GetType() 169 if valobj_type.IsValid() and valobj_type.IsPointerType(): 170 valobj_type = valobj_type.GetPointeeType() 171 if valobj_type.IsValid(): 172 actual_name = valobj_type.GetName() 173 if actual_name == "__CFBitVector" or actual_name == "__CFMutableBitVector": 174 wrapper = CFBitVectorKnown_SummaryProvider(valobj, class_data.sys_params) 175 statistics.metric_hit("code_notrun", valobj) 176 else: 177 wrapper = CFBitVectorUnknown_SummaryProvider(valobj, class_data.sys_params) 178 print(actual_name) 179 else: 180 wrapper = CFBitVectorUnknown_SummaryProvider(valobj, class_data.sys_params) 181 print(name_string) 182 statistics.metric_hit( 183 "unknown_class", valobj.GetName() + " seen as " + name_string 184 ) 185 return wrapper 186 187 188def CFBitVector_SummaryProvider(valobj, dict): 189 logger = lldb.formatters.Logger.Logger() 190 provider = GetSummary_Impl(valobj) 191 if provider is not None: 192 if isinstance( 193 provider, lldb.runtime.objc.objc_runtime.SpecialSituation_Description 194 ): 195 return provider.message() 196 try: 197 summary = provider.contents() 198 except: 199 summary = None 200 logger >> "summary got from provider: " + str(summary) 201 if summary is None or summary == "": 202 summary = "<variable is not CFBitVector>" 203 return summary 204 return "Summary Unavailable" 205 206 207def __lldb_init_module(debugger, dict): 208 debugger.HandleCommand( 209 "type summary add -F CFBitVector.CFBitVector_SummaryProvider CFBitVectorRef CFMutableBitVectorRef" 210 ) 211