blob: 4545a1b23700bf8ff3013b6e74cda7ae9d3ac9e0 [file] [log] [blame]
Enrico Granataeb4a4792012-02-23 23:10:27 +00001# synthetic children provider for NSArray
2import lldb
3import ctypes
4import objc_runtime
5import metrics
6
7statistics = metrics.Metrics()
8statistics.add_metric('invalid_isa')
9statistics.add_metric('invalid_pointer')
10statistics.add_metric('unknown_class')
11statistics.add_metric('code_notrun')
12
13# much less functional than the other two cases below
14# just runs code to get to the count and then returns
15# no children
16class NSArrayKVC_SynthProvider:
17
18 def adjust_for_architecture(self):
Enrico Granata7bc0ec32012-02-29 03:28:49 +000019 self.is_64_bit = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8)
Enrico Granataeb4a4792012-02-23 23:10:27 +000020 self.is_little = (self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle)
21 self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize()
22
23 def __init__(self, valobj, dict):
24 self.valobj = valobj;
25 self.update()
26
27 def update(self):
28 self.adjust_for_architecture();
29 self.id_type = self.valobj.GetType().GetBasicType(lldb.eBasicTypeObjCID)
30
31 def num_children(self):
32 stream = lldb.SBStream()
33 self.valobj.GetExpressionPath(stream)
34 num_children_vo = self.valobj.CreateValueFromExpression("count","(int)[" + stream.GetData() + " count]");
35 return num_children_vo.GetValueAsUnsigned(0)
36
37 def get_child_index(self,name):
38 if name == "len":
39 return self.num_children();
40 else:
41 return None
42
43 def get_child_at_index(self, index):
44 return None
45
46
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
51class NSArrayCF_SynthProvider:
52
53 def adjust_for_architecture(self):
Enrico Granata7bc0ec32012-02-29 03:28:49 +000054 self.is_64_bit = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8)
Enrico Granataeb4a4792012-02-23 23:10:27 +000055 self.is_little = (self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle)
56 self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize()
57 self.cfruntime_size = self.size_of_cfruntime_base()
58
59 # CFRuntimeBase is defined as having an additional
60 # 4 bytes (padding?) on LP64 architectures
61 # to get its size we add up sizeof(pointer)+4
62 # and then add 4 more bytes if we are on a 64bit system
63 def size_of_cfruntime_base(self):
Enrico Granata7bc0ec32012-02-29 03:28:49 +000064 if self.is_64_bit == True:
Enrico Granataeb4a4792012-02-23 23:10:27 +000065 return 8+4+4;
66 else:
67 return 4+4;
68
69 def __init__(self, valobj, dict):
70 self.valobj = valobj;
71 self.update()
72
73 def update(self):
74 self.adjust_for_architecture();
75 self.id_type = self.valobj.GetType().GetBasicType(lldb.eBasicTypeObjCID)
76
77 def num_children(self):
78 num_children_vo = self.valobj.CreateChildAtOffset("count",
79 self.cfruntime_size,
80 self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong))
81 return num_children_vo.GetValueAsUnsigned(0)
82
83 def get_child_index(self,name):
84 if name == "len":
85 return self.num_children();
86 else:
87 return None
88
89 def get_child_at_index(self, index):
90 return None
91
92
93class NSArrayI_SynthProvider:
94
95 def adjust_for_architecture(self):
Enrico Granata7bc0ec32012-02-29 03:28:49 +000096 self.is_64_bit = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8)
Enrico Granataeb4a4792012-02-23 23:10:27 +000097 self.is_little = (self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle)
98 self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize()
99
100 def __init__(self, valobj, dict):
101 self.valobj = valobj;
102 self.update()
103
104 def update(self):
105 self.adjust_for_architecture();
106 self.id_type = self.valobj.GetType().GetBasicType(lldb.eBasicTypeObjCID)
107
108 # skip the isa pointer and get at the size
109 def num_children(self):
110 offset = self.pointer_size;
111 datatype = self.valobj.GetType().GetBasicType(lldb.eBasicTypeLong)
112 count = self.valobj.CreateChildAtOffset("count",
113 offset,
114 datatype);
115 return int(count.GetValue(), 0)
116
117 def get_child_index(self,name):
118 if name == "len":
119 return self.num_children();
120 else:
121 return int(name.lstrip('[').rstrip(']'), 0)
122
123 def get_child_at_index(self, index):
124 if index == self.num_children():
125 return self.valobj.CreateValueFromExpression("len",
126 str(index))
127 offset = 2 * self.pointer_size + self.id_type.GetByteSize()*index
128 return self.valobj.CreateChildAtOffset('[' + str(index) + ']',
129 offset,
130 self.id_type)
131
132
133class NSArrayM_SynthProvider:
134
135 def adjust_for_architecture(self):
Enrico Granata7bc0ec32012-02-29 03:28:49 +0000136 self.is_64_bit = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8)
Enrico Granataeb4a4792012-02-23 23:10:27 +0000137 self.is_little = (self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle)
138 self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize()
139
140 def __init__(self, valobj, dict):
141 self.valobj = valobj;
142 self.update();
143
144 def update(self):
145 self.adjust_for_architecture();
146 self.id_type = self.valobj.GetType().GetBasicType(lldb.eBasicTypeObjCID)
147
148 # skip the isa pointer and get at the size
149 def num_children(self):
150 offset = self.pointer_size;
151 datatype = self.valobj.GetType().GetBasicType(lldb.eBasicTypeLong)
152 count = self.valobj.CreateChildAtOffset("count",
153 offset,
154 datatype);
155 return int(count.GetValue(), 0)
156
157 def get_child_index(self,name):
158 if name == "len":
159 return self.num_children();
160 else:
161 return int(name.lstrip('[').rstrip(']'), 0)
162
163 def data_offset(self):
164 offset = self.pointer_size; # isa
165 offset += self.pointer_size; # _used
166 offset += self.pointer_size; # _doHardRetain, _doWeakAccess, _size
167 offset += self.pointer_size; # _hasObjects, _hasStrongReferences, _offset
168 offset += self.pointer_size; # _mutations
169 return offset;
170
171 # the _offset field is used to calculate the actual offset
172 # when reading a value out of the array. we need to read it
173 # to do so we read a whole pointer_size of data from the
174 # right spot, and then zero out the two LSB
175 def read_offset_field(self):
176 disp = self.pointer_size; # isa
177 disp += self.pointer_size; # _used
178 disp += self.pointer_size; # _doHardRetain, _doWeakAccess, _size
179 offset = self.valobj.CreateChildAtOffset("offset",
180 disp,
181 self.valobj.GetType().GetBasicType(lldb.eBasicTypeLong))
182 offset_value = int(offset.GetValue(), 0)
183 offset_value = ctypes.c_uint32((offset_value & 0xFFFFFFFC) >> 2).value
184 return offset_value
185
186 # the _used field tells how many items are in the array
187 # but since this is a mutable array, it allocates more space
188 # for performance reasons. we need to get the real _size of
189 # the array to calculate the actual offset of each element
190 # in get_child_at_index() (see NSArray.m for details)
191 def read_size_field(self):
192 disp = self.pointer_size; # isa
193 disp += self.pointer_size; # _used
194 size = self.valobj.CreateChildAtOffset("size",
195 disp,
196 self.valobj.GetType().GetBasicType(lldb.eBasicTypeLong))
197 size_value = int(size.GetValue(), 0)
198 size_value = ctypes.c_uint32((size_value & 0xFFFFFFFA) >> 2).value
199 return size_value
200
201 def get_child_at_index(self, index):
202 if index == self.num_children():
203 return self.valobj.CreateValueFromExpression("len",
204 str(index))
205 size = self.read_size_field()
206 offset = self.read_offset_field()
207 phys_idx = offset + index
208 if size <= phys_idx:
209 phys_idx -=size;
210 # we still need to multiply by element size to do a correct pointer read
211 phys_idx *= self.id_type.GetByteSize()
212 list_ptr = self.valobj.CreateChildAtOffset("_list",
213 self.data_offset(),
214 self.id_type.GetBasicType(lldb.eBasicTypeUnsignedLongLong))
215 list_addr = int(list_ptr.GetValue(), 0)
216 return self.valobj.CreateValueFromAddress('[' + str(index) + ']',
217 list_addr + phys_idx,
218 self.id_type)
219
220# this is the actual synth provider, but is just a wrapper that checks
221# whether valobj is an instance of __NSArrayI or __NSArrayM and sets up an
222# appropriate backend layer to do the computations
223class NSArray_SynthProvider:
224
225 def adjust_for_architecture(self):
Enrico Granata7bc0ec32012-02-29 03:28:49 +0000226 self.is_64_bit = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8)
Enrico Granataeb4a4792012-02-23 23:10:27 +0000227 self.is_little = (self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle)
228 self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize()
229 self.id_type = self.valobj.GetType().GetBasicType(lldb.eBasicTypeObjCID)
230
231 def __init__(self, valobj, dict):
232 self.valobj = valobj;
233 self.adjust_for_architecture()
234 self.wrapper = self.make_wrapper(valobj,dict)
235 self.invalid = (self.wrapper == None)
236
237 def get_child_at_index(self, index):
238 if self.wrapper == None:
239 return None;
240 return self.wrapper.get_child_at_index(index)
241
242 def get_child_index(self,name):
243 if self.wrapper == None:
244 return None;
245 return self.wrapper.get_child_index(name)
246
247 def num_children(self):
248 if self.wrapper == None:
249 return 0;
250 return self.wrapper.num_children()
251
252 def update(self):
253 if self.wrapper == None:
254 return None;
255 return self.wrapper.update()
256
257 def read_ascii(self, pointer):
258 process = self.valobj.GetTarget().GetProcess()
259 error = lldb.SBError()
260 pystr = ''
261 # cannot do the read at once because there is no length byte
262 while True:
263 content = process.ReadMemory(pointer, 1, error)
264 new_bytes = bytearray(content)
265 b0 = new_bytes[0]
266 pointer = pointer + 1
267 if b0 == 0:
268 break
269 pystr = pystr + chr(b0)
270 return pystr
271
272 # this code acts as our defense against NULL and unitialized
273 # NSArray pointers, which makes it much longer than it would be otherwise
274 def make_wrapper(self,valobj,dict):
275 global statistics
276 class_data = objc_runtime.ObjCRuntime(valobj)
277 if class_data.is_valid() == False:
278 statistics.metric_hit('invalid_pointer',valobj)
279 wrapper = None
280 return
281 class_data = class_data.read_class_data()
282 if class_data.is_valid() == False:
283 statistics.metric_hit('invalid_isa',valobj)
284 wrapper = None
285 return
286 if class_data.is_kvo():
287 class_data = class_data.get_superclass()
288 if class_data.is_valid() == False:
289 statistics.metric_hit('invalid_isa',valobj)
290 wrapper = None
291 return
292
293 name_string = class_data.class_name()
294 if name_string == '__NSArrayI':
295 wrapper = NSArrayI_SynthProvider(valobj, dict)
296 statistics.metric_hit('code_notrun',valobj)
297 elif name_string == '__NSArrayM':
298 wrapper = NSArrayM_SynthProvider(valobj, dict)
299 statistics.metric_hit('code_notrun',valobj)
300 elif name_string == '__NSCFArray':
301 wrapper = NSArrayCF_SynthProvider(valobj, dict)
302 statistics.metric_hit('code_notrun',valobj)
303 else:
304 wrapper = NSArrayKVC_SynthProvider(valobj, dict)
305 statistics.metric_hit('unknown_class',str(valobj) + " seen as " + name_string)
306 return wrapper;
307
308def CFArray_SummaryProvider (valobj,dict):
309 provider = NSArray_SynthProvider(valobj,dict);
310 if provider.invalid == False:
311 try:
312 summary = str(provider.num_children());
313 except:
314 summary = None
315 if summary == None:
316 summary = 'no valid array here'
Enrico Granata7bc0ec32012-02-29 03:28:49 +0000317 return summary + " objects"
Enrico Granataeb4a4792012-02-23 23:10:27 +0000318 return ''
319
320def __lldb_init_module(debugger,dict):
321 debugger.HandleCommand("type summary add -F CFArray.CFArray_SummaryProvider NSArray CFArrayRef CFMutableArrayRef")