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 NSData 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 length for an NSData, so they need not 24# obey the interface specification for synthetic children providers 25 26 27class NSConcreteData_SummaryProvider: 28 def adjust_for_architecture(self): 29 pass 30 31 def __init__(self, valobj, params): 32 logger = lldb.formatters.Logger.Logger() 33 logger >> "NSConcreteData_SummaryProvider __init__" 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 = ( 39 self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong) 40 ) 41 else: 42 self.sys_params.types_cache.NSUInteger = ( 43 self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt) 44 ) 45 self.update() 46 47 def update(self): 48 self.adjust_for_architecture() 49 50 # one pointer is the ISA 51 # then there are 32 bit worth of flags and other data 52 # however, on 64bit systems these are padded to be a full 53 # machine word long, which means we actually have two pointers 54 # worth of data to skip 55 def offset(self): 56 return 2 * self.sys_params.pointer_size 57 58 def length(self): 59 logger = lldb.formatters.Logger.Logger() 60 logger >> "NSConcreteData_SummaryProvider length" 61 size = self.valobj.CreateChildAtOffset( 62 "count", self.offset(), self.sys_params.types_cache.NSUInteger 63 ) 64 logger >> str(size) 65 logger >> str(size.GetValueAsUnsigned(0)) 66 return size.GetValueAsUnsigned(0) 67 68 69class NSDataUnknown_SummaryProvider: 70 def adjust_for_architecture(self): 71 pass 72 73 def __init__(self, valobj, params): 74 logger = lldb.formatters.Logger.Logger() 75 logger >> "NSDataUnknown_SummaryProvider __init__" 76 self.valobj = valobj 77 self.sys_params = params 78 self.update() 79 80 def update(self): 81 self.adjust_for_architecture() 82 83 def length(self): 84 logger = lldb.formatters.Logger.Logger() 85 logger >> "NSDataUnknown_SummaryProvider length" 86 stream = lldb.SBStream() 87 self.valobj.GetExpressionPath(stream) 88 logger >> stream.GetData() 89 num_children_vo = self.valobj.CreateValueFromExpression( 90 "count", "(int)[" + stream.GetData() + " length]" 91 ) 92 logger >> "still in after expression: " + str(num_children_vo) 93 if num_children_vo.IsValid(): 94 logger >> "wow - expr output is valid: " + str( 95 num_children_vo.GetValueAsUnsigned() 96 ) 97 return num_children_vo.GetValueAsUnsigned(0) 98 logger >> "invalid expr output - too bad" 99 return "<variable is not NSData>" 100 101 102def GetSummary_Impl(valobj): 103 global statistics 104 logger = lldb.formatters.Logger.Logger() 105 logger >> "NSData GetSummary_Impl" 106 ( 107 class_data, 108 wrapper, 109 ) = lldb.runtime.objc.objc_runtime.Utilities.prepare_class_detection( 110 valobj, statistics 111 ) 112 if wrapper: 113 logger >> "got a wrapper summary - using it" 114 return wrapper 115 116 name_string = class_data.class_name() 117 logger >> "class name: " + name_string 118 if ( 119 name_string == "NSConcreteData" 120 or name_string == "NSConcreteMutableData" 121 or name_string == "__NSCFData" 122 ): 123 wrapper = NSConcreteData_SummaryProvider(valobj, class_data.sys_params) 124 statistics.metric_hit("code_notrun", valobj) 125 else: 126 wrapper = NSDataUnknown_SummaryProvider(valobj, class_data.sys_params) 127 statistics.metric_hit( 128 "unknown_class", valobj.GetName() + " seen as " + name_string 129 ) 130 return wrapper 131 132 133def NSData_SummaryProvider(valobj, dict): 134 logger = lldb.formatters.Logger.Logger() 135 logger >> "NSData_SummaryProvider" 136 provider = GetSummary_Impl(valobj) 137 logger >> "found a summary provider, it is: " + str(provider) 138 if provider is not None: 139 try: 140 summary = provider.length() 141 except: 142 summary = None 143 logger >> "got a summary: it is " + str(summary) 144 if summary is None: 145 summary = "<variable is not NSData>" 146 elif isinstance(summary, str): 147 pass 148 else: 149 if summary == 1: 150 summary = "1 byte" 151 else: 152 summary = str(summary) + " bytes" 153 return summary 154 return "Summary Unavailable" 155 156 157def NSData_SummaryProvider2(valobj, dict): 158 logger = lldb.formatters.Logger.Logger() 159 logger >> "NSData_SummaryProvider2" 160 provider = GetSummary_Impl(valobj) 161 logger >> "found a summary provider, it is: " + str(provider) 162 if provider is not None: 163 if isinstance( 164 provider, lldb.runtime.objc.objc_runtime.SpecialSituation_Description 165 ): 166 return provider.message() 167 try: 168 summary = provider.length() 169 except: 170 summary = None 171 logger >> "got a summary: it is " + str(summary) 172 if summary is None: 173 summary = "<variable is not CFData>" 174 elif isinstance(summary, str): 175 pass 176 else: 177 if summary == 1: 178 summary = '@"1 byte"' 179 else: 180 summary = '@"' + str(summary) + ' bytes"' 181 return summary 182 return "Summary Unavailable" 183 184 185def __lldb_init_module(debugger, dict): 186 debugger.HandleCommand("type summary add -F NSData.NSData_SummaryProvider NSData") 187 debugger.HandleCommand( 188 "type summary add -F NSData.NSData_SummaryProvider2 CFDataRef CFMutableDataRef" 189 ) 190