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