1061da546Spatrick""" 2061da546SpatrickLLDB AppKit formatters 3061da546Spatrick 4061da546SpatrickPart of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 5061da546SpatrickSee https://llvm.org/LICENSE.txt for license information. 6061da546SpatrickSPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 7061da546Spatrick""" 8061da546Spatrick# summary provider for NSSet 9061da546Spatrickimport lldb 10061da546Spatrickimport ctypes 11061da546Spatrickimport lldb.runtime.objc.objc_runtime 12061da546Spatrickimport lldb.formatters.metrics 13061da546Spatrickimport CFBag 14061da546Spatrickimport lldb.formatters.Logger 15061da546Spatrick 16061da546Spatrickstatistics = lldb.formatters.metrics.Metrics() 17061da546Spatrickstatistics.add_metric('invalid_isa') 18061da546Spatrickstatistics.add_metric('invalid_pointer') 19061da546Spatrickstatistics.add_metric('unknown_class') 20061da546Spatrickstatistics.add_metric('code_notrun') 21061da546Spatrick 22061da546Spatrick# despite the similary to synthetic children providers, these classes are not 23061da546Spatrick# trying to provide anything but the port number of an NSMachPort, so they need not 24061da546Spatrick# obey the interface specification for synthetic children providers 25061da546Spatrick 26061da546Spatrick 27061da546Spatrickclass NSCFSet_SummaryProvider: 28061da546Spatrick 29061da546Spatrick def adjust_for_architecture(self): 30061da546Spatrick pass 31061da546Spatrick 32061da546Spatrick def __init__(self, valobj, params): 33061da546Spatrick logger = lldb.formatters.Logger.Logger() 34061da546Spatrick self.valobj = valobj 35061da546Spatrick self.sys_params = params 36061da546Spatrick if not(self.sys_params.types_cache.NSUInteger): 37061da546Spatrick if self.sys_params.is_64_bit: 38061da546Spatrick self.sys_params.types_cache.NSUInteger = self.valobj.GetType( 39061da546Spatrick ).GetBasicType(lldb.eBasicTypeUnsignedLong) 40061da546Spatrick else: 41061da546Spatrick self.sys_params.types_cache.NSUInteger = self.valobj.GetType( 42061da546Spatrick ).GetBasicType(lldb.eBasicTypeUnsignedInt) 43061da546Spatrick self.update() 44061da546Spatrick 45061da546Spatrick def update(self): 46061da546Spatrick logger = lldb.formatters.Logger.Logger() 47061da546Spatrick self.adjust_for_architecture() 48061da546Spatrick 49061da546Spatrick # one pointer is the ISA 50061da546Spatrick # then we have one other internal pointer, plus 51061da546Spatrick # 4 bytes worth of flags. hence, these values 52061da546Spatrick def offset(self): 53061da546Spatrick logger = lldb.formatters.Logger.Logger() 54061da546Spatrick if self.sys_params.is_64_bit: 55061da546Spatrick return 20 56061da546Spatrick else: 57061da546Spatrick return 12 58061da546Spatrick 59061da546Spatrick def count(self): 60061da546Spatrick logger = lldb.formatters.Logger.Logger() 61061da546Spatrick vcount = self.valobj.CreateChildAtOffset( 62061da546Spatrick "count", self.offset(), self.sys_params.types_cache.NSUInteger) 63061da546Spatrick return vcount.GetValueAsUnsigned(0) 64061da546Spatrick 65061da546Spatrick 66061da546Spatrickclass NSSetUnknown_SummaryProvider: 67061da546Spatrick 68061da546Spatrick def adjust_for_architecture(self): 69061da546Spatrick pass 70061da546Spatrick 71061da546Spatrick def __init__(self, valobj, params): 72061da546Spatrick logger = lldb.formatters.Logger.Logger() 73061da546Spatrick self.valobj = valobj 74061da546Spatrick self.sys_params = params 75061da546Spatrick self.update() 76061da546Spatrick 77061da546Spatrick def update(self): 78061da546Spatrick logger = lldb.formatters.Logger.Logger() 79061da546Spatrick self.adjust_for_architecture() 80061da546Spatrick 81061da546Spatrick def count(self): 82061da546Spatrick logger = lldb.formatters.Logger.Logger() 83061da546Spatrick stream = lldb.SBStream() 84061da546Spatrick self.valobj.GetExpressionPath(stream) 85061da546Spatrick expr = "(int)[" + stream.GetData() + " count]" 86061da546Spatrick num_children_vo = self.valobj.CreateValueFromExpression("count", expr) 87061da546Spatrick if num_children_vo.IsValid(): 88061da546Spatrick return num_children_vo.GetValueAsUnsigned(0) 89061da546Spatrick return '<variable is not NSSet>' 90061da546Spatrick 91061da546Spatrick 92061da546Spatrickclass NSSetI_SummaryProvider: 93061da546Spatrick 94061da546Spatrick def adjust_for_architecture(self): 95061da546Spatrick pass 96061da546Spatrick 97061da546Spatrick def __init__(self, valobj, params): 98061da546Spatrick logger = lldb.formatters.Logger.Logger() 99061da546Spatrick self.valobj = valobj 100061da546Spatrick self.sys_params = params 101061da546Spatrick if not(self.sys_params.types_cache.NSUInteger): 102061da546Spatrick if self.sys_params.is_64_bit: 103061da546Spatrick self.sys_params.types_cache.NSUInteger = self.valobj.GetType( 104061da546Spatrick ).GetBasicType(lldb.eBasicTypeUnsignedLong) 105061da546Spatrick else: 106061da546Spatrick self.sys_params.types_cache.NSUInteger = self.valobj.GetType( 107061da546Spatrick ).GetBasicType(lldb.eBasicTypeUnsignedInt) 108061da546Spatrick self.update() 109061da546Spatrick 110061da546Spatrick def update(self): 111061da546Spatrick logger = lldb.formatters.Logger.Logger() 112061da546Spatrick self.adjust_for_architecture() 113061da546Spatrick 114061da546Spatrick # we just need to skip the ISA and the count immediately follows 115061da546Spatrick def offset(self): 116061da546Spatrick logger = lldb.formatters.Logger.Logger() 117061da546Spatrick return self.sys_params.pointer_size 118061da546Spatrick 119061da546Spatrick def count(self): 120061da546Spatrick logger = lldb.formatters.Logger.Logger() 121061da546Spatrick num_children_vo = self.valobj.CreateChildAtOffset( 122061da546Spatrick "count", self.offset(), self.sys_params.types_cache.NSUInteger) 123061da546Spatrick value = num_children_vo.GetValueAsUnsigned(0) 124061da546Spatrick if value is not None: 125061da546Spatrick # the MSB on immutable sets seems to be taken by some other data 126061da546Spatrick # not sure if it is a bug or some weird sort of feature, but masking it out 127061da546Spatrick # gets the count right (unless, of course, someone's dictionaries grow 128061da546Spatrick # too large - but I have not tested this) 129061da546Spatrick if self.sys_params.is_64_bit: 130061da546Spatrick value = value & ~0xFF00000000000000 131061da546Spatrick else: 132061da546Spatrick value = value & ~0xFF000000 133061da546Spatrick return value 134061da546Spatrick 135061da546Spatrick 136061da546Spatrickclass NSSetM_SummaryProvider: 137061da546Spatrick 138061da546Spatrick def adjust_for_architecture(self): 139061da546Spatrick pass 140061da546Spatrick 141061da546Spatrick def __init__(self, valobj, params): 142061da546Spatrick logger = lldb.formatters.Logger.Logger() 143061da546Spatrick self.valobj = valobj 144061da546Spatrick self.sys_params = params 145061da546Spatrick if not(self.sys_params.types_cache.NSUInteger): 146061da546Spatrick if self.sys_params.is_64_bit: 147061da546Spatrick self.sys_params.types_cache.NSUInteger = self.valobj.GetType( 148061da546Spatrick ).GetBasicType(lldb.eBasicTypeUnsignedLong) 149061da546Spatrick else: 150061da546Spatrick self.sys_params.types_cache.NSUInteger = self.valobj.GetType( 151061da546Spatrick ).GetBasicType(lldb.eBasicTypeUnsignedInt) 152061da546Spatrick self.update() 153061da546Spatrick 154061da546Spatrick def update(self): 155061da546Spatrick logger = lldb.formatters.Logger.Logger() 156061da546Spatrick self.adjust_for_architecture() 157061da546Spatrick 158061da546Spatrick # we just need to skip the ISA and the count immediately follows 159061da546Spatrick def offset(self): 160061da546Spatrick logger = lldb.formatters.Logger.Logger() 161061da546Spatrick return self.sys_params.pointer_size 162061da546Spatrick 163061da546Spatrick def count(self): 164061da546Spatrick logger = lldb.formatters.Logger.Logger() 165061da546Spatrick num_children_vo = self.valobj.CreateChildAtOffset( 166061da546Spatrick "count", self.offset(), self.sys_params.types_cache.NSUInteger) 167061da546Spatrick return num_children_vo.GetValueAsUnsigned(0) 168061da546Spatrick 169061da546Spatrick 170061da546Spatrickclass NSCountedSet_SummaryProvider: 171061da546Spatrick 172061da546Spatrick def adjust_for_architecture(self): 173061da546Spatrick pass 174061da546Spatrick 175061da546Spatrick def __init__(self, valobj, params): 176061da546Spatrick logger = lldb.formatters.Logger.Logger() 177061da546Spatrick self.valobj = valobj 178061da546Spatrick self.sys_params = params 179061da546Spatrick if not (self.sys_params.types_cache.voidptr): 180061da546Spatrick self.sys_params.types_cache.voidptr = self.valobj.GetType( 181061da546Spatrick ).GetBasicType(lldb.eBasicTypeVoid).GetPointerType() 182061da546Spatrick self.update() 183061da546Spatrick 184061da546Spatrick def update(self): 185061da546Spatrick logger = lldb.formatters.Logger.Logger() 186061da546Spatrick self.adjust_for_architecture() 187061da546Spatrick 188061da546Spatrick # an NSCountedSet is implemented using a CFBag whose pointer just follows 189061da546Spatrick # the ISA 190061da546Spatrick def offset(self): 191061da546Spatrick logger = lldb.formatters.Logger.Logger() 192061da546Spatrick return self.sys_params.pointer_size 193061da546Spatrick 194061da546Spatrick def count(self): 195061da546Spatrick logger = lldb.formatters.Logger.Logger() 196061da546Spatrick cfbag_vo = self.valobj.CreateChildAtOffset( 197061da546Spatrick "bag_impl", self.offset(), self.sys_params.types_cache.voidptr) 198061da546Spatrick return CFBag.CFBagRef_SummaryProvider( 199061da546Spatrick cfbag_vo, self.sys_params).length() 200061da546Spatrick 201061da546Spatrick 202061da546Spatrickdef GetSummary_Impl(valobj): 203061da546Spatrick logger = lldb.formatters.Logger.Logger() 204061da546Spatrick global statistics 205061da546Spatrick class_data, wrapper = lldb.runtime.objc.objc_runtime.Utilities.prepare_class_detection( 206061da546Spatrick valobj, statistics) 207061da546Spatrick if wrapper: 208061da546Spatrick return wrapper 209061da546Spatrick 210061da546Spatrick name_string = class_data.class_name() 211061da546Spatrick logger >> "class name is: " + str(name_string) 212061da546Spatrick 213061da546Spatrick if name_string == '__NSCFSet': 214061da546Spatrick wrapper = NSCFSet_SummaryProvider(valobj, class_data.sys_params) 215061da546Spatrick statistics.metric_hit('code_notrun', valobj) 216061da546Spatrick elif name_string == '__NSSetI': 217061da546Spatrick wrapper = NSSetI_SummaryProvider(valobj, class_data.sys_params) 218061da546Spatrick statistics.metric_hit('code_notrun', valobj) 219061da546Spatrick elif name_string == '__NSSetM': 220061da546Spatrick wrapper = NSSetM_SummaryProvider(valobj, class_data.sys_params) 221061da546Spatrick statistics.metric_hit('code_notrun', valobj) 222061da546Spatrick elif name_string == 'NSCountedSet': 223061da546Spatrick wrapper = NSCountedSet_SummaryProvider(valobj, class_data.sys_params) 224061da546Spatrick statistics.metric_hit('code_notrun', valobj) 225061da546Spatrick else: 226061da546Spatrick wrapper = NSSetUnknown_SummaryProvider(valobj, class_data.sys_params) 227061da546Spatrick statistics.metric_hit( 228061da546Spatrick 'unknown_class', 229061da546Spatrick valobj.GetName() + 230061da546Spatrick " seen as " + 231061da546Spatrick name_string) 232061da546Spatrick return wrapper 233061da546Spatrick 234061da546Spatrick 235061da546Spatrickdef NSSet_SummaryProvider(valobj, dict): 236061da546Spatrick logger = lldb.formatters.Logger.Logger() 237061da546Spatrick provider = GetSummary_Impl(valobj) 238061da546Spatrick if provider is not None: 239061da546Spatrick try: 240061da546Spatrick summary = provider.count() 241061da546Spatrick except: 242061da546Spatrick summary = None 243061da546Spatrick if summary is None: 244061da546Spatrick summary = '<variable is not NSSet>' 245*f6aab3d8Srobert if isinstance(summary, str): 246061da546Spatrick return summary 247061da546Spatrick else: 248061da546Spatrick summary = str(summary) + \ 249061da546Spatrick (' objects' if summary != 1 else ' object') 250061da546Spatrick return summary 251061da546Spatrick return 'Summary Unavailable' 252061da546Spatrick 253061da546Spatrick 254061da546Spatrickdef NSSet_SummaryProvider2(valobj, dict): 255061da546Spatrick logger = lldb.formatters.Logger.Logger() 256061da546Spatrick provider = GetSummary_Impl(valobj) 257061da546Spatrick if provider is not None: 258061da546Spatrick if isinstance( 259061da546Spatrick provider, 260061da546Spatrick lldb.runtime.objc.objc_runtime.SpecialSituation_Description): 261061da546Spatrick return provider.message() 262061da546Spatrick try: 263061da546Spatrick summary = provider.count() 264061da546Spatrick except: 265061da546Spatrick summary = None 266061da546Spatrick logger >> "got summary " + str(summary) 267061da546Spatrick # for some reason, one needs to clear some bits for the count returned 268061da546Spatrick # to be correct when using directly CF*SetRef as compared to NS*Set 269061da546Spatrick # this only happens on 64bit, and the bit mask was derived through 270061da546Spatrick # experimentation (if counts start looking weird, then most probably 271061da546Spatrick # the mask needs to be changed) 272061da546Spatrick if summary is None: 273061da546Spatrick summary = '<variable is not CFSet>' 274*f6aab3d8Srobert if isinstance(summary, str): 275061da546Spatrick return summary 276061da546Spatrick else: 277061da546Spatrick if provider.sys_params.is_64_bit: 278061da546Spatrick summary = summary & ~0x1fff000000000000 279061da546Spatrick summary = '@"' + str(summary) + \ 280061da546Spatrick (' values"' if summary != 1 else ' value"') 281061da546Spatrick return summary 282061da546Spatrick return 'Summary Unavailable' 283061da546Spatrick 284061da546Spatrick 285061da546Spatrickdef __lldb_init_module(debugger, dict): 286061da546Spatrick debugger.HandleCommand( 287061da546Spatrick "type summary add -F NSSet.NSSet_SummaryProvider NSSet") 288061da546Spatrick debugger.HandleCommand( 289061da546Spatrick "type summary add -F NSSet.NSSet_SummaryProvider2 CFSetRef CFMutableSetRef") 290