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 def adjust_for_architecture(self): 29 pass 30 31 def __init__(self, valobj, params): 32 logger = lldb.formatters.Logger.Logger() 33 self.valobj = valobj 34 self.sys_params = params 35 if not (self.sys_params.types_cache.NSUInteger): 36 if self.sys_params.is_64_bit: 37 self.sys_params.types_cache.NSUInteger = ( 38 self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong) 39 ) 40 self.sys_params.types_cache.uint32 = self.valobj.GetType().GetBasicType( 41 lldb.eBasicTypeUnsignedInt 42 ) 43 else: 44 self.sys_params.types_cache.NSUInteger = ( 45 self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt) 46 ) 47 self.sys_params.types_cache.uint32 = self.valobj.GetType().GetBasicType( 48 lldb.eBasicTypeUnsignedInt 49 ) 50 if not (self.sys_params.types_cache.uint32): 51 self.sys_params.types_cache.uint32 = self.valobj.GetType().GetBasicType( 52 lldb.eBasicTypeUnsignedInt 53 ) 54 self.update() 55 56 def update(self): 57 logger = lldb.formatters.Logger.Logger() 58 self.adjust_for_architecture() 59 60 # NS(Mutable)IndexSet works in one of two modes: when having a compact block of data (e.g. a Range) 61 # the count is stored in the set itself, 3 pointers into it 62 # otherwise, it will store a pointer to an additional data structure (2 pointers into itself) and this 63 # additional structure will contain the count two pointers deep 64 # a bunch of flags allow us to detect an empty set, vs. a one-range set, 65 # vs. a multi-range set 66 def count(self): 67 logger = lldb.formatters.Logger.Logger() 68 mode_chooser_vo = self.valobj.CreateChildAtOffset( 69 "mode_chooser", 70 self.sys_params.pointer_size, 71 self.sys_params.types_cache.uint32, 72 ) 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 ) 91 else: 92 count_ptr = self.valobj.CreateChildAtOffset( 93 "count_ptr", 94 2 * self.sys_params.pointer_size, 95 self.sys_params.types_cache.NSUInteger, 96 ) 97 count_vo = self.valobj.CreateValueFromAddress( 98 "count", 99 count_ptr.GetValueAsUnsigned() + 2 * self.sys_params.pointer_size, 100 self.sys_params.types_cache.NSUInteger, 101 ) 102 return count_vo.GetValueAsUnsigned(0) 103 104 105class NSIndexSetUnknown_SummaryProvider: 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 ( 134 class_data, 135 wrapper, 136 ) = lldb.runtime.objc.objc_runtime.Utilities.prepare_class_detection( 137 valobj, statistics 138 ) 139 if wrapper: 140 return wrapper 141 142 name_string = class_data.class_name() 143 logger >> "class name is: " + str(name_string) 144 145 if name_string == "NSIndexSet" or name_string == "NSMutableIndexSet": 146 wrapper = NSIndexSetClass_SummaryProvider(valobj, class_data.sys_params) 147 statistics.metric_hit("code_notrun", valobj) 148 else: 149 wrapper = NSIndexSetUnknown_SummaryProvider(valobj, class_data.sys_params) 150 statistics.metric_hit( 151 "unknown_class", valobj.GetName() + " seen as " + name_string 152 ) 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, lldb.runtime.objc.objc_runtime.SpecialSituation_Description 162 ): 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, str): 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 ) 183