1 """ 2 LLDB AppKit formatters 3 4 part of The LLVM Compiler Infrastructure 5 This file is distributed under the University of Illinois Open Source 6 License. See LICENSE.TXT for details. 7 """ 8 # example summary provider for NSArray 9 # the real summary is now C++ code built into LLDB 10 import lldb 11 import ctypes 12 import lldb.runtime.objc.objc_runtime 13 import lldb.formatters.metrics 14 import lldb.formatters.Logger 15 16 statistics = lldb.formatters.metrics.Metrics() 17 statistics.add_metric('invalid_isa') 18 statistics.add_metric('invalid_pointer') 19 statistics.add_metric('unknown_class') 20 statistics.add_metric('code_notrun') 21 22 # much less functional than the other two cases below 23 # just runs code to get to the count and then returns 24 # no children 25 class NSArrayKVC_SynthProvider: 26 27 def adjust_for_architecture(self): 28 pass 29 30 def __init__(self, valobj, dict, params): 31 logger = lldb.formatters.Logger.Logger() 32 self.valobj = valobj; 33 self.update() 34 35 def update(self): 36 logger = lldb.formatters.Logger.Logger() 37 self.adjust_for_architecture(); 38 39 def num_children(self): 40 logger = lldb.formatters.Logger.Logger() 41 stream = lldb.SBStream() 42 self.valobj.GetExpressionPath(stream) 43 num_children_vo = self.valobj.CreateValueFromExpression("count","(int)[" + stream.GetData() + " count]"); 44 if num_children_vo.IsValid(): 45 return num_children_vo.GetValueAsUnsigned(0) 46 return "<variable is not NSArray>" 47 48 # much less functional than the other two cases below 49 # just runs code to get to the count and then returns 50 # no children 51 class NSArrayCF_SynthProvider: 52 53 def adjust_for_architecture(self): 54 pass 55 56 def __init__(self, valobj, dict, params): 57 logger = lldb.formatters.Logger.Logger() 58 self.valobj = valobj; 59 self.sys_params = params 60 if not (self.sys_params.types_cache.ulong): 61 self.sys_params.types_cache.ulong = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong) 62 self.update() 63 64 def update(self): 65 logger = lldb.formatters.Logger.Logger() 66 self.adjust_for_architecture(); 67 68 def num_children(self): 69 logger = lldb.formatters.Logger.Logger() 70 num_children_vo = self.valobj.CreateChildAtOffset("count", 71 self.sys_params.cfruntime_size, 72 self.sys_params.types_cache.ulong) 73 return num_children_vo.GetValueAsUnsigned(0) 74 75 class NSArrayI_SynthProvider: 76 def adjust_for_architecture(self): 77 pass 78 79 def __init__(self, valobj, dict, params): 80 logger = lldb.formatters.Logger.Logger() 81 self.valobj = valobj; 82 self.sys_params = params 83 if not(self.sys_params.types_cache.long): 84 self.sys_params.types_cache.long = self.valobj.GetType().GetBasicType(lldb.eBasicTypeLong) 85 self.update() 86 87 def update(self): 88 logger = lldb.formatters.Logger.Logger() 89 self.adjust_for_architecture(); 90 91 # skip the isa pointer and get at the size 92 def num_children(self): 93 logger = lldb.formatters.Logger.Logger() 94 count = self.valobj.CreateChildAtOffset("count", 95 self.sys_params.pointer_size, 96 self.sys_params.types_cache.long); 97 return count.GetValueAsUnsigned(0) 98 99 class NSArrayM_SynthProvider: 100 def adjust_for_architecture(self): 101 pass 102 103 def __init__(self, valobj, dict, params): 104 logger = lldb.formatters.Logger.Logger() 105 self.valobj = valobj; 106 self.sys_params = params 107 if not(self.sys_params.types_cache.long): 108 self.sys_params.types_cache.long = self.valobj.GetType().GetBasicType(lldb.eBasicTypeLong) 109 self.update() 110 111 def update(self): 112 logger = lldb.formatters.Logger.Logger() 113 self.adjust_for_architecture(); 114 115 # skip the isa pointer and get at the size 116 def num_children(self): 117 logger = lldb.formatters.Logger.Logger() 118 count = self.valobj.CreateChildAtOffset("count", 119 self.sys_params.pointer_size, 120 self.sys_params.types_cache.long); 121 return count.GetValueAsUnsigned(0) 122 123 # this is the actual synth provider, but is just a wrapper that checks 124 # whether valobj is an instance of __NSArrayI or __NSArrayM and sets up an 125 # appropriate backend layer to do the computations 126 class NSArray_SynthProvider: 127 def adjust_for_architecture(self): 128 pass 129 130 def __init__(self, valobj, dict): 131 logger = lldb.formatters.Logger.Logger() 132 self.valobj = valobj; 133 self.adjust_for_architecture() 134 self.error = False 135 self.wrapper = self.make_wrapper() 136 self.invalid = (self.wrapper == None) 137 138 def num_children(self): 139 logger = lldb.formatters.Logger.Logger() 140 if self.wrapper == None: 141 return 0; 142 return self.wrapper.num_children() 143 144 def update(self): 145 logger = lldb.formatters.Logger.Logger() 146 if self.wrapper == None: 147 return 148 self.wrapper.update() 149 150 # this code acts as our defense against NULL and unitialized 151 # NSArray pointers, which makes it much longer than it would be otherwise 152 def make_wrapper(self): 153 logger = lldb.formatters.Logger.Logger() 154 if self.valobj.GetValueAsUnsigned() == 0: 155 self.error = True 156 return lldb.runtime.objc.objc_runtime.InvalidPointer_Description(True) 157 else: 158 global statistics 159 class_data,wrapper =lldb.runtime.objc.objc_runtime.Utilities.prepare_class_detection(self.valobj,statistics) 160 if wrapper: 161 self.error = True 162 return wrapper 163 164 name_string = class_data.class_name() 165 166 logger >> "Class name is " + str(name_string) 167 168 if name_string == '__NSArrayI': 169 wrapper = NSArrayI_SynthProvider(self.valobj, dict, class_data.sys_params) 170 statistics.metric_hit('code_notrun',self.valobj.GetName()) 171 elif name_string == '__NSArrayM': 172 wrapper = NSArrayM_SynthProvider(self.valobj, dict, class_data.sys_params) 173 statistics.metric_hit('code_notrun',self.valobj.GetName()) 174 elif name_string == '__NSCFArray': 175 wrapper = NSArrayCF_SynthProvider(self.valobj, dict, class_data.sys_params) 176 statistics.metric_hit('code_notrun',self.valobj.GetName()) 177 else: 178 wrapper = NSArrayKVC_SynthProvider(self.valobj, dict, class_data.sys_params) 179 statistics.metric_hit('unknown_class',str(self.valobj.GetName()) + " seen as " + name_string) 180 return wrapper; 181 182 def CFArray_SummaryProvider (valobj,dict): 183 logger = lldb.formatters.Logger.Logger() 184 provider = NSArray_SynthProvider(valobj,dict); 185 if provider.invalid == False: 186 if provider.error == True: 187 return provider.wrapper.message() 188 try: 189 summary = int(provider.num_children()); 190 except: 191 summary = None 192 logger >> "provider gave me " + str(summary) 193 if summary == None: 194 summary = '<variable is not NSArray>' 195 elif isinstance(summary,basestring): 196 pass 197 else: 198 # we format it like it were a CFString to make it look the same as the summary from Xcode 199 summary = '@"' + str(summary) + (" objects" if summary != 1 else " object") + '"' 200 return summary 201 return 'Summary Unavailable' 202 203 def __lldb_init_module(debugger,dict): 204 debugger.HandleCommand("type summary add -F CFArray.CFArray_SummaryProvider NSArray CFArrayRef CFMutableArrayRef") 205