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