blob: 5e405df451a6df79f12101a111b63278bab5e776 [file] [log] [blame]
Enrico Granata66205ce2012-03-12 19:47:17 +00001import lldb
2
3# libcxx STL formatters for LLDB
4# These formatters are based upon the implementation of libc++ that
5# ships with current releases of OS X - They will not work for other implementations
6# of the standard C++ library - and they are bound to use the libc++-specific namespace
7
8# this could probably be made more efficient but since it only reads a handful of bytes at a time
9# we probably don't need to worry too much about this for the time being
10def make_string(F,L):
11 strval = ''
12 G = F.GetData().uint8
13 for X in range(L):
14 V = G[X]
15 if V == 0:
16 break
17 strval = strval + chr(V % 256)
18 return '"' + strval + '"'
19
20# if we ever care about big-endian, these two functions might need to change
21def is_short_string(value):
22 return True if (value & 1) == 0 else False
23def extract_short_size(value):
24 return ((value >> 1) % 256)
25
26# some of the members of libc++ std::string are anonymous or have internal names that convey
27# no external significance - we access them by index since this saves a name lookup that would add
28# no information for readers of the code, but when possible try to use meaningful variable names
29def stdstring_SummaryProvider(valobj,dict):
30 r = valobj.GetChildAtIndex(0)
31 B = r.GetChildAtIndex(0)
32 first = B.GetChildAtIndex(0)
33 D = first.GetChildAtIndex(0)
34 l = D.GetChildAtIndex(0)
35 s = D.GetChildAtIndex(1)
36 D20 = s.GetChildAtIndex(0)
37 size_mode = D20.GetChildAtIndex(0).GetValueAsUnsigned(0)
38 if is_short_string(size_mode):
39 size = extract_short_size(size_mode)
40 return make_string(s.GetChildAtIndex(1),size)
41 else:
42 data_ptr = l.GetChildAtIndex(2)
43 size_vo = l.GetChildAtIndex(1)
44 size = size_vo.GetValueAsUnsigned(0)+1 # the NULL terminator must be accounted for
45 if size <= 1: # should never be the case
46 return '""'
47 data = data_ptr.GetPointeeData(0,size)
48 error = lldb.SBError()
49 strval = data.GetString(error,0)
50 if error.Fail():
51 return '<error:' + error.GetCString() + '>'
52 else:
53 return '"' + strval + '"'
54
55class stdvector_SynthProvider:
56
57 def __init__(self, valobj, dict):
58 self.valobj = valobj;
59 self.update()
60
61 def num_children(self):
62 try:
63 start_val = self.start.GetValueAsUnsigned(0)
64 finish_val = self.finish.GetValueAsUnsigned(0)
65 # Before a vector has been constructed, it will contain bad values
66 # so we really need to be careful about the length we return since
67 # unitialized data can cause us to return a huge number. We need
68 # to also check for any of the start, finish or end of storage values
69 # being zero (NULL). If any are, then this vector has not been
70 # initialized yet and we should return zero
71
72 # Make sure nothing is NULL
73 if start_val == 0 or finish_val == 0:
74 return 0
75 # Make sure start is less than finish
76 if start_val >= finish_val:
77 return 0
78
79 num_children = (finish_val-start_val)
80 if (num_children % self.data_size) != 0:
81 return 0
82 else:
83 num_children = num_children/self.data_size
84 return num_children
85 except:
86 return 0;
87
88 def get_child_index(self,name):
89 try:
90 return int(name.lstrip('[').rstrip(']'))
91 except:
92 return -1
93
94 def get_child_at_index(self,index):
95 if index < 0:
96 return None;
97 if index >= self.num_children():
98 return None;
99 try:
100 offset = index * self.data_size
101 return self.start.CreateChildAtOffset('['+str(index)+']',offset,self.data_type)
102 except:
103 return None
104
105 def update(self):
106 try:
107 self.start = self.valobj.GetChildMemberWithName('__begin_')
108 self.finish = self.valobj.GetChildMemberWithName('__end_')
109 # the purpose of this field is unclear, but it is the only field whose type is clearly T* for a vector<T>
110 # if this ends up not being correct, we can use the APIs to get at template arguments
111 data_type_finder = self.valobj.GetChildMemberWithName('__end_cap_').GetChildMemberWithName('__first_')
112 self.data_type = data_type_finder.GetType().GetPointeeType()
113 self.data_size = self.data_type.GetByteSize()
114 except:
115 pass
116
117def stdvector_SummaryProvider(valobj,dict):
118 prov = stdvector_SynthProvider(valobj,None)
119 return 'size=' + str(prov.num_children())
120
121class stdlist_entry:
122
123 def __init__(self,entry):
124 self.entry = entry
125
126 def _next_impl(self):
127 return stdlist_entry(self.entry.GetChildMemberWithName('__next_'))
128
129 def _prev_impl(self):
130 return stdlist_entry(self.entry.GetChildMemberWithName('__prev_'))
131
132 def _value_impl(self):
133 return self.entry.GetValueAsUnsigned(0)
134
135 def _isnull_impl(self):
136 return self._value_impl() == 0
137
138 def _sbvalue_impl(self):
139 return self.entry
140
141 next = property(_next_impl,None)
142 value = property(_value_impl,None)
143 is_null = property(_isnull_impl,None)
144 sbvalue = property(_sbvalue_impl,None)
145
146class stdlist_iterator:
147
148 def increment_node(self,node):
149 if node.is_null:
150 return None
151 return node.next
152
153 def __init__(self,node):
154 self.node = stdlist_entry(node) # we convert the SBValue to an internal node object on entry
155
156 def value(self):
157 return self.node.sbvalue # and return the SBValue back on exit
158
159 def next(self):
160 node = self.increment_node(self.node)
161 if node != None and node.sbvalue.IsValid() and not(node.is_null):
162 self.node = node
163 return self.value()
164 else:
165 return None
166
167 def advance(self,N):
168 if N < 0:
169 return None
170 if N == 0:
171 return self.value()
172 if N == 1:
173 return self.next()
174 while N > 0:
175 self.next()
176 N = N - 1
177 return self.value()
178
179
180class stdlist_SynthProvider:
181 def __init__(self, valobj, dict):
182 self.valobj = valobj
183 self.update()
184
185 def next_node(self,node):
186 return node.GetChildMemberWithName('__next_')
187
188 def value(self,node):
189 return node.GetValueAsUnsigned()
190
191 # Floyd's cyle-finding algorithm
192 # try to detect if this list has a loop
193 def has_loop(self):
194 slow = stdlist_entry(self.head)
195 fast1 = stdlist_entry(self.head)
196 fast2 = stdlist_entry(self.head)
197 while slow.next.value != self.node_address:
198 slow_value = slow.value
199 fast1 = fast2.next
200 fast2 = fast1.next
201 if fast1.value == slow_value or fast2.value == slow_value:
202 return True
203 slow = slow.next
204 return False
205
206 def num_children(self):
207 if self.count == None:
208 self.count = self.num_children_impl()
209 return self.count
210
211 def num_children_impl(self):
212 try:
213 next_val = self.head.GetValueAsUnsigned(0)
214 prev_val = self.tail.GetValueAsUnsigned(0)
215 # After a std::list has been initialized, both next and prev will be non-NULL
216 if next_val == 0 or prev_val == 0:
217 return 0
218 if next_val == self.node_address:
219 return 0
220 if next_val == prev_val:
221 return 1
222 if self.has_loop():
223 return 0
224 size = 2
225 current = stdlist_entry(self.head)
226 while current.next.value != self.node_address:
227 size = size + 1
228 current = current.next
229 return (size - 1)
230 except:
231 return 0;
232
233 def get_child_index(self,name):
234 try:
235 return int(name.lstrip('[').rstrip(']'))
236 except:
237 return -1
238
239 def get_child_at_index(self,index):
240 if index < 0:
241 return None;
242 if index >= self.num_children():
243 return None;
244 try:
245 current = stdlist_iterator(self.head)
246 current = current.advance(index)
247 # we do not return __value_ because then all our children would be named __value_
248 # we need to make a copy of __value__ with the right name - unfortunate
249 obj = current.GetChildMemberWithName('__value_')
250 obj_data = obj.GetData()
251 return self.valobj.CreateValueFromData('[' + str(index) + ']',obj_data,self.data_type)
252 except:
253 return None
254
255 def extract_type(self):
256 list_type = self.valobj.GetType().GetUnqualifiedType()
257 if list_type.GetNumberOfTemplateArguments() > 0:
258 data_type = list_type.GetTemplateArgumentType(0)
259 else:
260 data_type = None
261 return data_type
262
263 def update(self):
264 try:
265 impl = self.valobj.GetChildMemberWithName('__end_')
266 self.node_address = self.valobj.AddressOf().GetValueAsUnsigned(0)
267 self.head = impl.GetChildMemberWithName('__next_')
268 self.tail = impl.GetChildMemberWithName('__prev_')
269 self.data_type = self.extract_type()
270 self.data_size = self.data_type.GetByteSize()
271 self.count = None
272 except:
273 pass
274
275def stdlist_SummaryProvider(valobj,dict):
276 prov = stdlist_SynthProvider(valobj,None)
277 return 'size=' + str(prov.num_children())
278
279# a tree node - this class makes the syntax in the actual iterator nicer to read and maintain
280class stdmap_iterator_node:
281 def _left_impl(self):
282 return stdmap_iterator_node(self.node.GetChildMemberWithName("__left_"))
283
284 def _right_impl(self):
285 return stdmap_iterator_node(self.node.GetChildMemberWithName("__right_"))
286
287 def _parent_impl(self):
288 return stdmap_iterator_node(self.node.GetChildMemberWithName("__parent_"))
289
290 def _value_impl(self):
291 return self.node.GetValueAsUnsigned(0)
292
293 def _sbvalue_impl(self):
294 return self.node
295
296 def _null_impl(self):
297 return self.value == 0
298
299 def __init__(self,node):
300 self.node = node
301
302 left = property(_left_impl,None)
303 right = property(_right_impl,None)
304 parent = property(_parent_impl,None)
305 value = property(_value_impl,None)
306 is_null = property(_null_impl,None)
307 sbvalue = property(_sbvalue_impl,None)
308
309# a Python implementation of the tree iterator used by libc++
310class stdmap_iterator:
311
312 def tree_min(self,x):
313 if x.is_null:
314 return None
315 while (not x.left.is_null):
316 x = x.left
317 return x
318
319 def tree_max(self,x):
320 if x.is_null:
321 return None
322 while (not x.right.is_null):
323 x = x.right
324 return x
325
326 def tree_is_left_child(self,x):
327 if x.is_null:
328 return None
329 return True if x.value == x.parent.left.value else False
330
331 def increment_node(self,node):
332 if node.is_null:
333 return None
334 if not node.right.is_null:
335 return self.tree_min(node.right)
336 while (not self.tree_is_left_child(node)):
337 node = node.parent
338 return node.parent
339
340 def __init__(self,node):
341 self.node = stdmap_iterator_node(node) # we convert the SBValue to an internal node object on entry
342
343 def value(self):
344 return self.node.sbvalue # and return the SBValue back on exit
345
346 def next(self):
347 node = self.increment_node(self.node)
348 if node != None and node.sbvalue.IsValid() and not(node.is_null):
349 self.node = node
350 return self.value()
351 else:
352 return None
353
354 def advance(self,N):
355 if N < 0:
356 return None
357 if N == 0:
358 return self.value()
359 if N == 1:
360 return self.next()
361 while N > 0:
362 self.next()
363 N = N - 1
364 return self.value()
365
366class stdmap_SynthProvider:
367
368 def __init__(self, valobj, dict):
369 self.valobj = valobj;
370 self.pointer_size = self.valobj.GetProcess().GetAddressByteSize()
371 self.update()
372
373 def update(self):
374 try:
375 self.tree = self.valobj.GetChildMemberWithName('__tree_')
376 self.root_node = self.tree.GetChildMemberWithName('__begin_node_')
377 # this data is either lazily-calculated, or cannot be inferred at this moment
378 # we still need to mark it as None, meaning "please set me ASAP"
379 self.data_type = None
380 self.data_size = None
381 self.skip_size = None
382 self.count = None
383 except:
384 pass
385
386 def num_children(self):
387 if self.count == None:
388 self.count = self.num_children_impl()
389 return self.count
390
391 def num_children_impl(self):
392 try:
393 return self.valobj.GetChildMemberWithName('__tree_').GetChildMemberWithName('__pair3_').GetChildMemberWithName('__first_').GetValueAsUnsigned()
394 except:
395 return 0;
396
397 def get_data_type(self):
398 if self.data_type == None or self.data_size == None:
399 if self.num_children() == 0:
400 return False
401 deref = self.root_node.Dereference()
402 if not(deref.IsValid()):
403 return False
404 value = deref.GetChildMemberWithName('__value_')
405 if not(value.IsValid()):
406 return False
407 self.data_type = value.GetType()
408 self.data_size = self.data_type.GetByteSize()
409 self.skip_size = None
410 return True
411 else:
412 return True
413
414 def get_value_offset(self,node):
415 if self.skip_size == None:
416 node_type = node.GetType()
417 fields_count = node_type.GetNumberOfFields()
418 for i in range(fields_count):
419 field = node_type.GetFieldAtIndex(i)
420 if field.GetName() == '__value_':
421 self.skip_size = field.GetOffsetInBytes()
422 break
423 return (self.skip_size != None)
424
425 def get_child_index(self,name):
426 try:
427 return int(name.lstrip('[').rstrip(']'))
428 except:
429 return -1
430
431 def get_child_at_index(self,index):
432 if index < 0:
433 return None
434 if index >= self.num_children():
435 return None;
436 try:
437 iterator = stdmap_iterator(self.root_node)
438 # the debug info for libc++ std::map is such that __begin_node_ has a very nice and useful type
439 # out of which we can grab the information we need - every other node has a less informative
440 # type which omits all value information and only contains housekeeping information for the RB tree
441 # hence, we need to know if we are at a node != 0, so that we can still get at the data
442 need_to_skip = (index > 0)
443 current = iterator.advance(index)
444 if self.get_data_type():
445 if not(need_to_skip):
446 current = current.Dereference()
447 obj = current.GetChildMemberWithName('__value_')
448 obj_data = obj.GetData()
449 self.get_value_offset(current) # make sure we have a valid offset for the next items
450 # we do not return __value_ because then we would end up with a child named
451 # __value_ instead of [0]
452 return self.valobj.CreateValueFromData('[' + str(index) + ']',obj_data,self.data_type)
453 else:
454 # FIXME we need to have accessed item 0 before accessing any other item!
455 if self.skip_size == None:
456 return None
457 return current.CreateChildAtOffset('[' + str(index) + ']',self.skip_size,self.data_type)
458 else:
459 print "foo"
460 return None
461 except Exception as err:
462 print err
463 return None
464
465def stdmap_SummaryProvider(valobj,dict):
466 prov = stdmap_SynthProvider(valobj,None)
467 return 'size=' + str(prov.num_children())
468
469
470# we can use two different categories for old and new formatters - type names are different enough that we should make no confusion
471# talking with libc++ developer: "std::__1::class_name is set in stone until we decide to change the ABI. That shouldn't happen within a 5 year time frame"
472def __lldb_init_module(debugger,dict):
473 debugger.HandleCommand('type summary add -F libcxx.stdstring_SummaryProvider "std::__1::string" -w libcxx')
474 debugger.HandleCommand('type summary add -F libcxx.stdstring_SummaryProvider "std::__1::basic_string<char, class std::__1::char_traits<char>, class std::__1::allocator<char> >" -w libcxx')
475 debugger.HandleCommand('type synthetic add -l libcxx.stdvector_SynthProvider -x "^(std::__1::)vector<.+>$" -w libcxx')
476 debugger.HandleCommand('type summary add -F libcxx.stdvector_SummaryProvider -e -x "^(std::__1::)vector<.+>$" -w libcxx')
477 debugger.HandleCommand('type synthetic add -l libcxx.stdlist_SynthProvider -x "^(std::__1::)list<.+>$" -w libcxx')
478 debugger.HandleCommand('type summary add -F libcxx.stdlist_SummaryProvider -e -x "^(std::__1::)list<.+>$" -w libcxx')
479 debugger.HandleCommand('type synthetic add -l libcxx.stdmap_SynthProvider -x "^(std::__1::)map<.+> >$" -w libcxx')
480 debugger.HandleCommand('type summary add -F libcxx.stdmap_SummaryProvider -e -x "^(std::__1::)map<.+> >$" -w libcxx')
481 debugger.HandleCommand("type category enable libcxx")