blob: ed6928ec7d18d1513411a26623c6d0f855e20f2a [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;
Enrico Granata66205ce2012-03-12 19:47:17 +000059
60 def num_children(self):
61 try:
62 start_val = self.start.GetValueAsUnsigned(0)
63 finish_val = self.finish.GetValueAsUnsigned(0)
64 # Before a vector has been constructed, it will contain bad values
65 # so we really need to be careful about the length we return since
66 # unitialized data can cause us to return a huge number. We need
67 # to also check for any of the start, finish or end of storage values
68 # being zero (NULL). If any are, then this vector has not been
69 # initialized yet and we should return zero
70
71 # Make sure nothing is NULL
72 if start_val == 0 or finish_val == 0:
73 return 0
74 # Make sure start is less than finish
75 if start_val >= finish_val:
76 return 0
77
78 num_children = (finish_val-start_val)
79 if (num_children % self.data_size) != 0:
80 return 0
81 else:
82 num_children = num_children/self.data_size
83 return num_children
84 except:
85 return 0;
86
87 def get_child_index(self,name):
88 try:
89 return int(name.lstrip('[').rstrip(']'))
90 except:
91 return -1
92
93 def get_child_at_index(self,index):
94 if index < 0:
95 return None;
96 if index >= self.num_children():
97 return None;
98 try:
99 offset = index * self.data_size
100 return self.start.CreateChildAtOffset('['+str(index)+']',offset,self.data_type)
101 except:
102 return None
103
104 def update(self):
105 try:
106 self.start = self.valobj.GetChildMemberWithName('__begin_')
107 self.finish = self.valobj.GetChildMemberWithName('__end_')
108 # the purpose of this field is unclear, but it is the only field whose type is clearly T* for a vector<T>
109 # if this ends up not being correct, we can use the APIs to get at template arguments
110 data_type_finder = self.valobj.GetChildMemberWithName('__end_cap_').GetChildMemberWithName('__first_')
111 self.data_type = data_type_finder.GetType().GetPointeeType()
112 self.data_size = self.data_type.GetByteSize()
113 except:
114 pass
115
Enrico Granatacf09f882012-03-19 22:58:49 +0000116# Just an example: the actual summary is produced by a summary string: size=${svar%#}
Enrico Granata66205ce2012-03-12 19:47:17 +0000117def 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
Enrico Granata66205ce2012-03-12 19:47:17 +0000183
184 def next_node(self,node):
185 return node.GetChildMemberWithName('__next_')
186
187 def value(self,node):
188 return node.GetValueAsUnsigned()
189
190 # Floyd's cyle-finding algorithm
191 # try to detect if this list has a loop
192 def has_loop(self):
193 slow = stdlist_entry(self.head)
194 fast1 = stdlist_entry(self.head)
195 fast2 = stdlist_entry(self.head)
196 while slow.next.value != self.node_address:
197 slow_value = slow.value
198 fast1 = fast2.next
199 fast2 = fast1.next
200 if fast1.value == slow_value or fast2.value == slow_value:
201 return True
202 slow = slow.next
203 return False
204
205 def num_children(self):
206 if self.count == None:
207 self.count = self.num_children_impl()
208 return self.count
209
210 def num_children_impl(self):
211 try:
212 next_val = self.head.GetValueAsUnsigned(0)
213 prev_val = self.tail.GetValueAsUnsigned(0)
214 # After a std::list has been initialized, both next and prev will be non-NULL
215 if next_val == 0 or prev_val == 0:
216 return 0
217 if next_val == self.node_address:
218 return 0
219 if next_val == prev_val:
220 return 1
221 if self.has_loop():
222 return 0
223 size = 2
224 current = stdlist_entry(self.head)
225 while current.next.value != self.node_address:
226 size = size + 1
227 current = current.next
228 return (size - 1)
229 except:
230 return 0;
231
232 def get_child_index(self,name):
233 try:
234 return int(name.lstrip('[').rstrip(']'))
235 except:
236 return -1
237
238 def get_child_at_index(self,index):
239 if index < 0:
240 return None;
241 if index >= self.num_children():
242 return None;
243 try:
244 current = stdlist_iterator(self.head)
245 current = current.advance(index)
246 # we do not return __value_ because then all our children would be named __value_
247 # we need to make a copy of __value__ with the right name - unfortunate
248 obj = current.GetChildMemberWithName('__value_')
249 obj_data = obj.GetData()
250 return self.valobj.CreateValueFromData('[' + str(index) + ']',obj_data,self.data_type)
251 except:
252 return None
253
254 def extract_type(self):
255 list_type = self.valobj.GetType().GetUnqualifiedType()
256 if list_type.GetNumberOfTemplateArguments() > 0:
257 data_type = list_type.GetTemplateArgumentType(0)
258 else:
259 data_type = None
260 return data_type
261
262 def update(self):
263 try:
264 impl = self.valobj.GetChildMemberWithName('__end_')
265 self.node_address = self.valobj.AddressOf().GetValueAsUnsigned(0)
266 self.head = impl.GetChildMemberWithName('__next_')
267 self.tail = impl.GetChildMemberWithName('__prev_')
268 self.data_type = self.extract_type()
269 self.data_size = self.data_type.GetByteSize()
270 self.count = None
271 except:
272 pass
273
Enrico Granatacf09f882012-03-19 22:58:49 +0000274# Just an example: the actual summary is produced by a summary string: size=${svar%#}
Enrico Granata66205ce2012-03-12 19:47:17 +0000275def 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()
Enrico Granata66205ce2012-03-12 19:47:17 +0000371
372 def update(self):
373 try:
374 self.tree = self.valobj.GetChildMemberWithName('__tree_')
375 self.root_node = self.tree.GetChildMemberWithName('__begin_node_')
376 # this data is either lazily-calculated, or cannot be inferred at this moment
377 # we still need to mark it as None, meaning "please set me ASAP"
378 self.data_type = None
379 self.data_size = None
380 self.skip_size = None
381 self.count = None
382 except:
383 pass
384
385 def num_children(self):
386 if self.count == None:
387 self.count = self.num_children_impl()
388 return self.count
389
390 def num_children_impl(self):
391 try:
392 return self.valobj.GetChildMemberWithName('__tree_').GetChildMemberWithName('__pair3_').GetChildMemberWithName('__first_').GetValueAsUnsigned()
393 except:
394 return 0;
395
396 def get_data_type(self):
397 if self.data_type == None or self.data_size == None:
398 if self.num_children() == 0:
399 return False
400 deref = self.root_node.Dereference()
401 if not(deref.IsValid()):
402 return False
403 value = deref.GetChildMemberWithName('__value_')
404 if not(value.IsValid()):
405 return False
406 self.data_type = value.GetType()
407 self.data_size = self.data_type.GetByteSize()
408 self.skip_size = None
409 return True
410 else:
411 return True
412
413 def get_value_offset(self,node):
414 if self.skip_size == None:
415 node_type = node.GetType()
416 fields_count = node_type.GetNumberOfFields()
417 for i in range(fields_count):
418 field = node_type.GetFieldAtIndex(i)
419 if field.GetName() == '__value_':
420 self.skip_size = field.GetOffsetInBytes()
421 break
422 return (self.skip_size != None)
423
424 def get_child_index(self,name):
425 try:
426 return int(name.lstrip('[').rstrip(']'))
427 except:
428 return -1
429
430 def get_child_at_index(self,index):
431 if index < 0:
432 return None
433 if index >= self.num_children():
434 return None;
435 try:
436 iterator = stdmap_iterator(self.root_node)
437 # the debug info for libc++ std::map is such that __begin_node_ has a very nice and useful type
438 # out of which we can grab the information we need - every other node has a less informative
439 # type which omits all value information and only contains housekeeping information for the RB tree
440 # hence, we need to know if we are at a node != 0, so that we can still get at the data
441 need_to_skip = (index > 0)
442 current = iterator.advance(index)
443 if self.get_data_type():
444 if not(need_to_skip):
445 current = current.Dereference()
446 obj = current.GetChildMemberWithName('__value_')
447 obj_data = obj.GetData()
448 self.get_value_offset(current) # make sure we have a valid offset for the next items
449 # we do not return __value_ because then we would end up with a child named
450 # __value_ instead of [0]
451 return self.valobj.CreateValueFromData('[' + str(index) + ']',obj_data,self.data_type)
452 else:
453 # FIXME we need to have accessed item 0 before accessing any other item!
454 if self.skip_size == None:
455 return None
456 return current.CreateChildAtOffset('[' + str(index) + ']',self.skip_size,self.data_type)
457 else:
458 print "foo"
459 return None
460 except Exception as err:
461 print err
462 return None
463
Enrico Granatacf09f882012-03-19 22:58:49 +0000464# Just an example: the actual summary is produced by a summary string: size=${svar%#}
Enrico Granata66205ce2012-03-12 19:47:17 +0000465def 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")