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