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 NS(Mutable)IndexSet 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 count of values for an NSIndexSet, so they need not 24# obey the interface specification for synthetic children providers 25 26 27class NSIndexSetClass_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 self.sys_params.types_cache.uint32 = self.valobj.GetType( 41 ).GetBasicType(lldb.eBasicTypeUnsignedInt) 42 else: 43 self.sys_params.types_cache.NSUInteger = self.valobj.GetType( 44 ).GetBasicType(lldb.eBasicTypeUnsignedInt) 45 self.sys_params.types_cache.uint32 = self.valobj.GetType( 46 ).GetBasicType(lldb.eBasicTypeUnsignedInt) 47 if not(self.sys_params.types_cache.uint32): 48 self.sys_params.types_cache.uint32 = self.valobj.GetType( 49 ).GetBasicType(lldb.eBasicTypeUnsignedInt) 50 self.update() 51 52 def update(self): 53 logger = lldb.formatters.Logger.Logger() 54 self.adjust_for_architecture() 55 56 # NS(Mutable)IndexSet works in one of two modes: when having a compact block of data (e.g. a Range) 57 # the count is stored in the set itself, 3 pointers into it 58 # otherwise, it will store a pointer to an additional data structure (2 pointers into itself) and this 59 # additional structure will contain the count two pointers deep 60 # a bunch of flags allow us to detect an empty set, vs. a one-range set, 61 # vs. a multi-range set 62 def count(self): 63 logger = lldb.formatters.Logger.Logger() 64 mode_chooser_vo = self.valobj.CreateChildAtOffset( 65 "mode_chooser", 66 self.sys_params.pointer_size, 67 self.sys_params.types_cache.uint32) 68 mode_chooser = mode_chooser_vo.GetValueAsUnsigned(0) 69 if self.sys_params.is_64_bit: 70 mode_chooser = mode_chooser & 0x00000000FFFFFFFF 71 # empty set 72 if mode_chooser & 0x01 == 1: 73 return 0 74 # single range 75 if mode_chooser & 0x02 == 2: 76 mode = 1 77 # multi range 78 else: 79 mode = 2 80 if mode == 1: 81 count_vo = self.valobj.CreateChildAtOffset( 82 "count", 83 3 * self.sys_params.pointer_size, 84 self.sys_params.types_cache.NSUInteger) 85 else: 86 count_ptr = self.valobj.CreateChildAtOffset( 87 "count_ptr", 88 2 * self.sys_params.pointer_size, 89 self.sys_params.types_cache.NSUInteger) 90 count_vo = self.valobj.CreateValueFromAddress( 91 "count", 92 count_ptr.GetValueAsUnsigned() + 93 2 * 94 self.sys_params.pointer_size, 95 self.sys_params.types_cache.NSUInteger) 96 return count_vo.GetValueAsUnsigned(0) 97 98 99class NSIndexSetUnknown_SummaryProvider: 100 101 def adjust_for_architecture(self): 102 pass 103 104 def __init__(self, valobj, params): 105 logger = lldb.formatters.Logger.Logger() 106 self.valobj = valobj 107 self.sys_params = params 108 self.update() 109 110 def update(self): 111 logger = lldb.formatters.Logger.Logger() 112 self.adjust_for_architecture() 113 114 def count(self): 115 logger = lldb.formatters.Logger.Logger() 116 stream = lldb.SBStream() 117 self.valobj.GetExpressionPath(stream) 118 expr = "(int)[" + stream.GetData() + " count]" 119 num_children_vo = self.valobj.CreateValueFromExpression("count", expr) 120 if num_children_vo.IsValid(): 121 return num_children_vo.GetValueAsUnsigned(0) 122 return '<variable is not NSIndexSet>' 123 124 125def GetSummary_Impl(valobj): 126 logger = lldb.formatters.Logger.Logger() 127 global statistics 128 class_data, wrapper = lldb.runtime.objc.objc_runtime.Utilities.prepare_class_detection( 129 valobj, statistics) 130 if wrapper: 131 return wrapper 132 133 name_string = class_data.class_name() 134 logger >> "class name is: " + str(name_string) 135 136 if name_string == 'NSIndexSet' or name_string == 'NSMutableIndexSet': 137 wrapper = NSIndexSetClass_SummaryProvider( 138 valobj, class_data.sys_params) 139 statistics.metric_hit('code_notrun', valobj) 140 else: 141 wrapper = NSIndexSetUnknown_SummaryProvider( 142 valobj, class_data.sys_params) 143 statistics.metric_hit( 144 'unknown_class', 145 valobj.GetName() + 146 " seen as " + 147 name_string) 148 return wrapper 149 150 151def NSIndexSet_SummaryProvider(valobj, dict): 152 logger = lldb.formatters.Logger.Logger() 153 provider = GetSummary_Impl(valobj) 154 if provider is not None: 155 if isinstance( 156 provider, 157 lldb.runtime.objc.objc_runtime.SpecialSituation_Description): 158 return provider.message() 159 try: 160 summary = provider.count() 161 except: 162 summary = None 163 logger >> "got summary " + str(summary) 164 if summary is None: 165 summary = '<variable is not NSIndexSet>' 166 if isinstance(summary, str): 167 return summary 168 else: 169 summary = str(summary) + (' indexes' if summary != 1 else ' index') 170 return summary 171 return 'Summary Unavailable' 172 173 174def __lldb_init_module(debugger, dict): 175 debugger.HandleCommand( 176 "type summary add -F NSIndexSet.NSIndexSet_SummaryProvider NSIndexSet NSMutableIndexSet") 177