Run DCE after a LoopFlatten test to reduce spurious output [nfc]
[llvm-project.git] / lldb / examples / synthetic / gnu_libstdcpp.py
blobf778065aaca37710be22e84124f27828344b108a
1 import lldb.formatters.Logger
3 # C++ STL formatters for LLDB
4 # As there are many versions of the libstdc++, you are encouraged to look at the STL
5 # implementation for your platform before relying on these formatters to do the right
6 # thing for your setup
9 def ForwardListSummaryProvider(valobj, dict):
10 list_capping_size = valobj.GetTarget().GetMaximumNumberOfChildrenToDisplay()
11 text = "size=" + str(valobj.GetNumChildren())
12 if valobj.GetNumChildren() > list_capping_size:
13 return "(capped) " + text
14 else:
15 return text
18 def StdOptionalSummaryProvider(valobj, dict):
19 has_value = valobj.GetNumChildren() > 0
20 # We add wrapping spaces for consistency with the libcxx formatter
21 return " Has Value=" + ("true" if has_value else "false") + " "
24 class StdOptionalSynthProvider:
25 def __init__(self, valobj, dict):
26 self.valobj = valobj
28 def update(self):
29 try:
30 self.payload = self.valobj.GetChildMemberWithName("_M_payload")
31 self.value = self.payload.GetChildMemberWithName("_M_payload")
32 self.has_value = (
33 self.payload.GetChildMemberWithName("_M_engaged").GetValueAsUnsigned(0)
34 != 0
36 except:
37 self.has_value = False
38 return False
40 def num_children(self):
41 return 1 if self.has_value else 0
43 def get_child_index(self, name):
44 return 0
46 def get_child_at_index(self, index):
47 # some versions of libstdcpp have an additional _M_value child with the actual value
48 possible_value = self.value.GetChildMemberWithName("_M_value")
49 if possible_value.IsValid():
50 return possible_value.Clone("Value")
51 return self.value.Clone("Value")
54 """
55 This formatter can be applied to all
56 unordered map-like structures (unordered_map, unordered_multimap, unordered_set, unordered_multiset)
57 """
60 class StdUnorderedMapSynthProvider:
61 def __init__(self, valobj, dict):
62 self.valobj = valobj
63 self.count = None
64 self.kind = self.get_object_kind(valobj)
66 def get_object_kind(self, valobj):
67 type_name = valobj.GetTypeName()
68 return "set" if "set" in type_name else "map"
70 def extract_type(self):
71 type = self.valobj.GetType()
72 # type of std::pair<key, value> is the first template
73 # argument type of the 4th template argument to std::map and
74 # 3rd template argument for std::set. That's why
75 # we need to know kind of the object
76 template_arg_num = 4 if self.kind == "map" else 3
77 allocator_type = type.GetTemplateArgumentType(template_arg_num)
78 data_type = allocator_type.GetTemplateArgumentType(0)
79 return data_type
81 def update(self):
82 # preemptively setting this to None - we might end up changing our mind
83 # later
84 self.count = None
85 try:
86 self.head = self.valobj.GetChildMemberWithName("_M_h")
87 self.before_begin = self.head.GetChildMemberWithName("_M_before_begin")
88 self.next = self.before_begin.GetChildMemberWithName("_M_nxt")
89 self.data_type = self.extract_type()
90 self.skip_size = self.next.GetType().GetByteSize()
91 self.data_size = self.data_type.GetByteSize()
92 if (not self.data_type.IsValid()) or (not self.next.IsValid()):
93 self.count = 0
94 except:
95 self.count = 0
96 return False
98 def get_child_index(self, name):
99 try:
100 return int(name.lstrip("[").rstrip("]"))
101 except:
102 return -1
104 def get_child_at_index(self, index):
105 logger = lldb.formatters.Logger.Logger()
106 logger >> "Being asked to fetch child[" + str(index) + "]"
107 if index < 0:
108 return None
109 if index >= self.num_children():
110 return None
111 try:
112 offset = index
113 current = self.next
114 while offset > 0:
115 current = current.GetChildMemberWithName("_M_nxt")
116 offset = offset - 1
117 return current.CreateChildAtOffset(
118 "[" + str(index) + "]", self.skip_size, self.data_type
121 except:
122 logger >> "Cannot get child"
123 return None
125 def num_children(self):
126 if self.count is None:
127 self.count = self.num_children_impl()
128 return self.count
130 def num_children_impl(self):
131 logger = lldb.formatters.Logger.Logger()
132 try:
133 count = self.head.GetChildMemberWithName(
134 "_M_element_count"
135 ).GetValueAsUnsigned(0)
136 return count
137 except:
138 logger >> "Could not determine the size"
139 return 0
142 class AbstractListSynthProvider:
143 def __init__(self, valobj, dict, has_prev):
145 :param valobj: The value object of the list
146 :param dict: A dict with metadata provided by LLDB
147 :param has_prev: Whether the list supports a 'prev' pointer besides a 'next' one
149 logger = lldb.formatters.Logger.Logger()
150 self.valobj = valobj
151 self.count = None
152 self.has_prev = has_prev
153 self.list_capping_size = (
154 self.valobj.GetTarget().GetMaximumNumberOfChildrenToDisplay()
156 logger >> "Providing synthetic children for a list named " + str(
157 valobj.GetName()
160 def next_node(self, node):
161 logger = lldb.formatters.Logger.Logger()
162 return node.GetChildMemberWithName("_M_next")
164 def is_valid(self, node):
165 logger = lldb.formatters.Logger.Logger()
166 valid = self.value(self.next_node(node)) != self.get_end_of_list_address()
167 if valid:
168 logger >> "%s is valid" % str(self.valobj.GetName())
169 else:
170 logger >> "synthetic value is not valid"
171 return valid
173 def value(self, node):
174 logger = lldb.formatters.Logger.Logger()
175 value = node.GetValueAsUnsigned()
176 logger >> "synthetic value for {}: {}".format(str(self.valobj.GetName()), value)
177 return value
179 # Floyd's cycle-finding algorithm
180 # try to detect if this list has a loop
181 def has_loop(self):
182 global _list_uses_loop_detector
183 logger = lldb.formatters.Logger.Logger()
184 if not _list_uses_loop_detector:
185 logger >> "Asked not to use loop detection"
186 return False
187 slow = self.next
188 fast1 = self.next
189 fast2 = self.next
190 while self.is_valid(slow):
191 slow_value = self.value(slow)
192 fast1 = self.next_node(fast2)
193 fast2 = self.next_node(fast1)
194 if self.value(fast1) == slow_value or self.value(fast2) == slow_value:
195 return True
196 slow = self.next_node(slow)
197 return False
199 def num_children(self):
200 logger = lldb.formatters.Logger.Logger()
201 if self.count is None:
202 # libstdc++ 6.0.21 added dedicated count field.
203 count_child = self.node.GetChildMemberWithName("_M_data")
204 if count_child and count_child.IsValid():
205 self.count = count_child.GetValueAsUnsigned(0)
206 if self.count is None:
207 self.count = self.num_children_impl()
208 return self.count
210 def num_children_impl(self):
211 logger = lldb.formatters.Logger.Logger()
212 try:
213 # After a std::list has been initialized, both next and prev will
214 # be non-NULL
215 next_val = self.next.GetValueAsUnsigned(0)
216 if next_val == 0:
217 return 0
218 if self.has_loop():
219 return 0
220 if self.has_prev:
221 prev_val = self.prev.GetValueAsUnsigned(0)
222 if prev_val == 0:
223 return 0
224 if next_val == self.node_address:
225 return 0
226 if next_val == prev_val:
227 return 1
228 size = 1
229 current = self.next
230 while (
231 current.GetChildMemberWithName("_M_next").GetValueAsUnsigned(0)
232 != self.get_end_of_list_address()
234 current = current.GetChildMemberWithName("_M_next")
235 if not current.IsValid():
236 break
237 size = size + 1
238 if size >= self.list_capping_size:
239 break
241 return size
242 except:
243 logger >> "Error determining the size"
244 return 0
246 def get_child_index(self, name):
247 logger = lldb.formatters.Logger.Logger()
248 try:
249 return int(name.lstrip("[").rstrip("]"))
250 except:
251 return -1
253 def get_child_at_index(self, index):
254 logger = lldb.formatters.Logger.Logger()
255 logger >> "Fetching child " + str(index)
256 if index < 0:
257 return None
258 if index >= self.num_children():
259 return None
260 try:
261 offset = index
262 current = self.next
263 while offset > 0:
264 current = current.GetChildMemberWithName("_M_next")
265 offset = offset - 1
266 # C++ lists store the data of a node after its pointers. In the case of a forward list, there's just one pointer (next), and
267 # in the case of a double-linked list, there's an additional pointer (prev).
268 return current.CreateChildAtOffset(
269 "[" + str(index) + "]",
270 (2 if self.has_prev else 1) * current.GetType().GetByteSize(),
271 self.data_type,
273 except:
274 return None
276 def extract_type(self):
277 logger = lldb.formatters.Logger.Logger()
278 list_type = self.valobj.GetType().GetUnqualifiedType()
279 if list_type.IsReferenceType():
280 list_type = list_type.GetDereferencedType()
281 if list_type.GetNumberOfTemplateArguments() > 0:
282 return list_type.GetTemplateArgumentType(0)
283 return lldb.SBType()
285 def update(self):
286 logger = lldb.formatters.Logger.Logger()
287 # preemptively setting this to None - we might end up changing our mind
288 # later
289 self.count = None
290 try:
291 self.impl = self.valobj.GetChildMemberWithName("_M_impl")
292 self.data_type = self.extract_type()
293 if (not self.data_type.IsValid()) or (not self.impl.IsValid()):
294 self.count = 0
295 elif not self.updateNodes():
296 self.count = 0
297 else:
298 self.data_size = self.data_type.GetByteSize()
299 except:
300 self.count = 0
301 return False
304 Method is used to extract the list pointers into the variables (e.g self.node, self.next, and optionally to self.prev)
305 and is mandatory to be overriden in each AbstractListSynthProvider subclass.
306 This should return True or False depending on wheter it found valid data.
309 def updateNodes(self):
310 raise NotImplementedError
312 def has_children(self):
313 return True
316 Method is used to identify if a node traversal has reached its end
317 and is mandatory to be overriden in each AbstractListSynthProvider subclass
320 def get_end_of_list_address(self):
321 raise NotImplementedError
324 class StdForwardListSynthProvider(AbstractListSynthProvider):
325 def __init__(self, valobj, dict):
326 has_prev = False
327 super().__init__(valobj, dict, has_prev)
329 def updateNodes(self):
330 self.node = self.impl.GetChildMemberWithName("_M_head")
331 self.next = self.node.GetChildMemberWithName("_M_next")
332 if (not self.node.IsValid()) or (not self.next.IsValid()):
333 return False
334 return True
336 def get_end_of_list_address(self):
337 return 0
340 class StdListSynthProvider(AbstractListSynthProvider):
341 def __init__(self, valobj, dict):
342 has_prev = True
343 super().__init__(valobj, dict, has_prev)
345 def updateNodes(self):
346 self.node_address = self.valobj.AddressOf().GetValueAsUnsigned(0)
347 self.node = self.impl.GetChildMemberWithName("_M_node")
348 self.prev = self.node.GetChildMemberWithName("_M_prev")
349 self.next = self.node.GetChildMemberWithName("_M_next")
350 if (
351 self.node_address == 0
352 or (not self.node.IsValid())
353 or (not self.next.IsValid())
354 or (not self.prev.IsValid())
356 return False
357 return True
359 def get_end_of_list_address(self):
360 return self.node_address
363 class StdVectorSynthProvider:
364 class StdVectorImplementation(object):
365 def __init__(self, valobj):
366 self.valobj = valobj
367 self.count = None
369 def num_children(self):
370 if self.count is None:
371 self.count = self.num_children_impl()
372 return self.count
374 def num_children_impl(self):
375 try:
376 start_val = self.start.GetValueAsUnsigned(0)
377 finish_val = self.finish.GetValueAsUnsigned(0)
378 end_val = self.end.GetValueAsUnsigned(0)
379 # Before a vector has been constructed, it will contain bad values
380 # so we really need to be careful about the length we return since
381 # uninitialized data can cause us to return a huge number. We need
382 # to also check for any of the start, finish or end of storage values
383 # being zero (NULL). If any are, then this vector has not been
384 # initialized yet and we should return zero
386 # Make sure nothing is NULL
387 if start_val == 0 or finish_val == 0 or end_val == 0:
388 return 0
389 # Make sure start is less than finish
390 if start_val >= finish_val:
391 return 0
392 # Make sure finish is less than or equal to end of storage
393 if finish_val > end_val:
394 return 0
396 # if we have a struct (or other data type that the compiler pads to native word size)
397 # this check might fail, unless the sizeof() we get is itself incremented to take the
398 # padding bytes into account - on current clang it looks like
399 # this is the case
400 num_children = finish_val - start_val
401 if (num_children % self.data_size) != 0:
402 return 0
403 else:
404 num_children = num_children // self.data_size
405 return num_children
406 except:
407 return 0
409 def get_child_at_index(self, index):
410 logger = lldb.formatters.Logger.Logger()
411 logger >> "Retrieving child " + str(index)
412 if index < 0:
413 return None
414 if index >= self.num_children():
415 return None
416 try:
417 offset = index * self.data_size
418 return self.start.CreateChildAtOffset(
419 "[" + str(index) + "]", offset, self.data_type
421 except:
422 return None
424 def update(self):
425 # preemptively setting this to None - we might end up changing our
426 # mind later
427 self.count = None
428 try:
429 impl = self.valobj.GetChildMemberWithName("_M_impl")
430 self.start = impl.GetChildMemberWithName("_M_start")
431 self.finish = impl.GetChildMemberWithName("_M_finish")
432 self.end = impl.GetChildMemberWithName("_M_end_of_storage")
433 self.data_type = self.start.GetType().GetPointeeType()
434 self.data_size = self.data_type.GetByteSize()
435 # if any of these objects is invalid, it means there is no
436 # point in trying to fetch anything
437 if (
438 self.start.IsValid()
439 and self.finish.IsValid()
440 and self.end.IsValid()
441 and self.data_type.IsValid()
443 self.count = None
444 else:
445 self.count = 0
446 except:
447 self.count = 0
448 return False
450 class StdVBoolImplementation(object):
451 def __init__(self, valobj, bool_type):
452 self.valobj = valobj
453 self.bool_type = bool_type
454 self.valid = False
456 def num_children(self):
457 if self.valid:
458 start = self.start_p.GetValueAsUnsigned(0)
459 finish = self.finish_p.GetValueAsUnsigned(0)
460 offset = self.offset.GetValueAsUnsigned(0)
461 if finish >= start:
462 return (finish - start) * 8 + offset
463 return 0
465 def get_child_at_index(self, index):
466 if index >= self.num_children():
467 return None
468 element_type = self.start_p.GetType().GetPointeeType()
469 element_bits = 8 * element_type.GetByteSize()
470 element_offset = (index // element_bits) * element_type.GetByteSize()
471 bit_offset = index % element_bits
472 element = self.start_p.CreateChildAtOffset(
473 "[" + str(index) + "]", element_offset, element_type
475 bit = element.GetValueAsUnsigned(0) & (1 << bit_offset)
476 if bit != 0:
477 value_expr = "(bool)true"
478 else:
479 value_expr = "(bool)false"
480 return self.valobj.CreateValueFromExpression("[%d]" % index, value_expr)
482 def update(self):
483 try:
484 m_impl = self.valobj.GetChildMemberWithName("_M_impl")
485 self.m_start = m_impl.GetChildMemberWithName("_M_start")
486 self.m_finish = m_impl.GetChildMemberWithName("_M_finish")
487 self.start_p = self.m_start.GetChildMemberWithName("_M_p")
488 self.finish_p = self.m_finish.GetChildMemberWithName("_M_p")
489 self.offset = self.m_finish.GetChildMemberWithName("_M_offset")
490 if (
491 self.offset.IsValid()
492 and self.start_p.IsValid()
493 and self.finish_p.IsValid()
495 self.valid = True
496 else:
497 self.valid = False
498 except:
499 self.valid = False
500 return False
502 def __init__(self, valobj, dict):
503 logger = lldb.formatters.Logger.Logger()
504 first_template_arg_type = valobj.GetType().GetTemplateArgumentType(0)
505 if str(first_template_arg_type.GetName()) == "bool":
506 self.impl = self.StdVBoolImplementation(valobj, first_template_arg_type)
507 else:
508 self.impl = self.StdVectorImplementation(valobj)
509 logger >> "Providing synthetic children for a vector named " + str(
510 valobj.GetName()
513 def num_children(self):
514 return self.impl.num_children()
516 def get_child_index(self, name):
517 try:
518 return int(name.lstrip("[").rstrip("]"))
519 except:
520 return -1
522 def get_child_at_index(self, index):
523 return self.impl.get_child_at_index(index)
525 def update(self):
526 return self.impl.update()
528 def has_children(self):
529 return True
532 This formatter can be applied to all
533 map-like structures (map, multimap, set, multiset)
537 class StdMapLikeSynthProvider:
538 def __init__(self, valobj, dict):
539 logger = lldb.formatters.Logger.Logger()
540 self.valobj = valobj
541 self.count = None
542 self.kind = self.get_object_kind(valobj)
544 logger
545 >> "Providing synthetic children for a "
546 + self.kind
547 + " named "
548 + str(valobj.GetName())
551 def get_object_kind(self, valobj):
552 type_name = valobj.GetTypeName()
553 for kind in ["multiset", "multimap", "set", "map"]:
554 if kind in type_name:
555 return kind
556 return type_name
558 # we need this function as a temporary workaround for rdar://problem/10801549
559 # which prevents us from extracting the std::pair<K,V> SBType out of the template
560 # arguments for _Rep_Type _M_t in the object itself - because we have to make up the
561 # typename and then find it, we may hit the situation were std::string has multiple
562 # names but only one is actually referenced in the debug information. hence, we need
563 # to replace the longer versions of std::string with the shorter one in order to be able
564 # to find the type name
565 def fixup_class_name(self, class_name):
566 logger = lldb.formatters.Logger.Logger()
567 if (
568 class_name
569 == "std::basic_string<char, std::char_traits<char>, std::allocator<char> >"
571 return "std::basic_string<char>", True
572 if (
573 class_name
574 == "basic_string<char, std::char_traits<char>, std::allocator<char> >"
576 return "std::basic_string<char>", True
577 if (
578 class_name
579 == "std::basic_string<char, std::char_traits<char>, std::allocator<char> >"
581 return "std::basic_string<char>", True
582 if (
583 class_name
584 == "basic_string<char, std::char_traits<char>, std::allocator<char> >"
586 return "std::basic_string<char>", True
587 return class_name, False
589 def update(self):
590 logger = lldb.formatters.Logger.Logger()
591 # preemptively setting this to None - we might end up changing our mind
592 # later
593 self.count = None
594 try:
595 # we will set this to True if we find out that discovering a node in the object takes more steps than the overall size of the RB tree
596 # if this gets set to True, then we will merrily return None for
597 # any child from that moment on
598 self.garbage = False
599 self.Mt = self.valobj.GetChildMemberWithName("_M_t")
600 self.Mimpl = self.Mt.GetChildMemberWithName("_M_impl")
601 self.Mheader = self.Mimpl.GetChildMemberWithName("_M_header")
602 if not self.Mheader.IsValid():
603 self.count = 0
604 else:
605 map_type = self.valobj.GetType()
606 if map_type.IsReferenceType():
607 logger >> "Dereferencing type"
608 map_type = map_type.GetDereferencedType()
610 # Get the type of std::pair<key, value>. It is the first template
611 # argument type of the 4th template argument to std::map.
612 allocator_type = map_type.GetTemplateArgumentType(3)
613 self.data_type = allocator_type.GetTemplateArgumentType(0)
614 if not self.data_type:
615 # GCC does not emit DW_TAG_template_type_parameter for
616 # std::allocator<...>. For such a case, get the type of
617 # std::pair from a member of std::map.
618 rep_type = self.valobj.GetChildMemberWithName("_M_t").GetType()
619 self.data_type = (
620 rep_type.GetTypedefedType().GetTemplateArgumentType(1)
623 # from libstdc++ implementation of _M_root for rbtree
624 self.Mroot = self.Mheader.GetChildMemberWithName("_M_parent")
625 self.data_size = self.data_type.GetByteSize()
626 self.skip_size = self.Mheader.GetType().GetByteSize()
627 except:
628 self.count = 0
629 return False
631 def num_children(self):
632 logger = lldb.formatters.Logger.Logger()
633 if self.count is None:
634 self.count = self.num_children_impl()
635 return self.count
637 def num_children_impl(self):
638 logger = lldb.formatters.Logger.Logger()
639 try:
640 root_ptr_val = self.node_ptr_value(self.Mroot)
641 if root_ptr_val == 0:
642 return 0
643 count = self.Mimpl.GetChildMemberWithName(
644 "_M_node_count"
645 ).GetValueAsUnsigned(0)
646 logger >> "I have " + str(count) + " children available"
647 return count
648 except:
649 return 0
651 def get_child_index(self, name):
652 logger = lldb.formatters.Logger.Logger()
653 try:
654 return int(name.lstrip("[").rstrip("]"))
655 except:
656 return -1
658 def get_child_at_index(self, index):
659 logger = lldb.formatters.Logger.Logger()
660 logger >> "Being asked to fetch child[" + str(index) + "]"
661 if index < 0:
662 return None
663 if index >= self.num_children():
664 return None
665 if self.garbage:
666 logger >> "Returning None since we are a garbage tree"
667 return None
668 try:
669 offset = index
670 current = self.left(self.Mheader)
671 while offset > 0:
672 current = self.increment_node(current)
673 offset = offset - 1
674 # skip all the base stuff and get at the data
675 return current.CreateChildAtOffset(
676 "[" + str(index) + "]", self.skip_size, self.data_type
678 except:
679 return None
681 # utility functions
682 def node_ptr_value(self, node):
683 logger = lldb.formatters.Logger.Logger()
684 return node.GetValueAsUnsigned(0)
686 def right(self, node):
687 logger = lldb.formatters.Logger.Logger()
688 return node.GetChildMemberWithName("_M_right")
690 def left(self, node):
691 logger = lldb.formatters.Logger.Logger()
692 return node.GetChildMemberWithName("_M_left")
694 def parent(self, node):
695 logger = lldb.formatters.Logger.Logger()
696 return node.GetChildMemberWithName("_M_parent")
698 # from libstdc++ implementation of iterator for rbtree
699 def increment_node(self, node):
700 logger = lldb.formatters.Logger.Logger()
701 max_steps = self.num_children()
702 if self.node_ptr_value(self.right(node)) != 0:
703 x = self.right(node)
704 max_steps -= 1
705 while self.node_ptr_value(self.left(x)) != 0:
706 x = self.left(x)
707 max_steps -= 1
708 logger >> str(max_steps) + " more to go before giving up"
709 if max_steps <= 0:
710 self.garbage = True
711 return None
712 return x
713 else:
714 x = node
715 y = self.parent(x)
716 max_steps -= 1
717 while self.node_ptr_value(x) == self.node_ptr_value(self.right(y)):
718 x = y
719 y = self.parent(y)
720 max_steps -= 1
721 logger >> str(max_steps) + " more to go before giving up"
722 if max_steps <= 0:
723 self.garbage = True
724 return None
725 if self.node_ptr_value(self.right(x)) != self.node_ptr_value(y):
726 x = y
727 return x
729 def has_children(self):
730 return True
733 _list_uses_loop_detector = True
736 class StdDequeSynthProvider:
737 def __init__(self, valobj, d):
738 self.valobj = valobj
739 self.pointer_size = self.valobj.GetProcess().GetAddressByteSize()
740 self.count = None
741 self.block_size = -1
742 self.element_size = -1
743 self.find_block_size()
745 def find_block_size(self):
746 # in order to use the deque we must have the block size, or else
747 # it's impossible to know what memory addresses are valid
748 self.element_type = self.valobj.GetType().GetTemplateArgumentType(0)
749 if not self.element_type.IsValid():
750 return
751 self.element_size = self.element_type.GetByteSize()
752 # The block size (i.e. number of elements per subarray) is defined in
753 # this piece of code, so we need to replicate it.
755 # #define _GLIBCXX_DEQUE_BUF_SIZE 512
757 # return (__size < _GLIBCXX_DEQUE_BUF_SIZE
758 # ? size_t(_GLIBCXX_DEQUE_BUF_SIZE / __size) : size_t(1));
759 if self.element_size < 512:
760 self.block_size = 512 // self.element_size
761 else:
762 self.block_size = 1
764 def num_children(self):
765 if self.count is None:
766 return 0
767 return self.count
769 def has_children(self):
770 return True
772 def get_child_index(self, name):
773 try:
774 return int(name.lstrip("[").rstrip("]"))
775 except:
776 return -1
778 def get_child_at_index(self, index):
779 if index < 0 or self.count is None:
780 return None
781 if index >= self.num_children():
782 return None
783 try:
784 name = "[" + str(index) + "]"
785 # We first look for the element in the first subarray,
786 # which might be incomplete.
787 if index < self.first_node_size:
788 # The following statement is valid because self.first_elem is the pointer
789 # to the first element
790 return self.first_elem.CreateChildAtOffset(
791 name, index * self.element_size, self.element_type
794 # Now the rest of the subarrays except for maybe the last one
795 # are going to be complete, so the final expression is simpler
796 i, j = divmod(index - self.first_node_size, self.block_size)
798 # We first move to the beginning of the node/subarray were our element is
799 node = self.start_node.CreateChildAtOffset(
801 (1 + i) * self.valobj.GetProcess().GetAddressByteSize(),
802 self.element_type.GetPointerType(),
804 return node.CreateChildAtOffset(
805 name, j * self.element_size, self.element_type
808 except:
809 return None
811 def update(self):
812 logger = lldb.formatters.Logger.Logger()
813 self.count = 0
814 try:
815 # A deque is effectively a two-dim array, with fixed width.
816 # However, only a subset of this memory contains valid data
817 # since a deque may have some slack at the front and back in
818 # order to have O(1) insertion at both ends.
819 # The rows in active use are delimited by '_M_start' and
820 # '_M_finish'.
822 # To find the elements that are actually constructed, the 'start'
823 # variable tells which element in this NxM array is the 0th
824 # one.
825 if self.block_size < 0 or self.element_size < 0:
826 return False
828 count = 0
830 impl = self.valobj.GetChildMemberWithName("_M_impl")
832 # we calculate the size of the first node (i.e. first internal array)
833 self.start = impl.GetChildMemberWithName("_M_start")
834 self.start_node = self.start.GetChildMemberWithName("_M_node")
835 first_node_address = self.start_node.GetValueAsUnsigned(0)
836 first_node_last_elem = self.start.GetChildMemberWithName(
837 "_M_last"
838 ).GetValueAsUnsigned(0)
839 self.first_elem = self.start.GetChildMemberWithName("_M_cur")
840 first_node_first_elem = self.first_elem.GetValueAsUnsigned(0)
842 finish = impl.GetChildMemberWithName("_M_finish")
843 last_node_address = finish.GetChildMemberWithName(
844 "_M_node"
845 ).GetValueAsUnsigned(0)
846 last_node_first_elem = finish.GetChildMemberWithName(
847 "_M_first"
848 ).GetValueAsUnsigned(0)
849 last_node_last_elem = finish.GetChildMemberWithName(
850 "_M_cur"
851 ).GetValueAsUnsigned(0)
853 if (
854 first_node_first_elem == 0
855 or first_node_last_elem == 0
856 or first_node_first_elem > first_node_last_elem
858 return False
859 if (
860 last_node_first_elem == 0
861 or last_node_last_elem == 0
862 or last_node_first_elem > last_node_last_elem
864 return False
866 if last_node_address == first_node_address:
867 self.first_node_size = (
868 last_node_last_elem - first_node_first_elem
869 ) // self.element_size
870 count += self.first_node_size
871 else:
872 self.first_node_size = (
873 first_node_last_elem - first_node_first_elem
874 ) // self.element_size
875 count += self.first_node_size
877 # we calculate the size of the last node
878 finish = impl.GetChildMemberWithName("_M_finish")
879 last_node_address = finish.GetChildMemberWithName(
880 "_M_node"
881 ).GetValueAsUnsigned(0)
882 count += (
883 last_node_last_elem - last_node_first_elem
884 ) // self.element_size
886 # we calculate the size of the intermediate nodes
887 num_intermediate_nodes = (
888 last_node_address - first_node_address - 1
889 ) // self.valobj.GetProcess().GetAddressByteSize()
890 count += self.block_size * num_intermediate_nodes
891 self.count = count
892 except:
893 pass
894 return False
897 def VariantSummaryProvider(valobj, dict):
898 raw_obj = valobj.GetNonSyntheticValue()
899 index_obj = raw_obj.GetChildMemberWithName("_M_index")
900 data_obj = raw_obj.GetChildMemberWithName("_M_u")
901 if not (index_obj and index_obj.IsValid() and data_obj and data_obj.IsValid()):
902 return "<Can't find _M_index or _M_u>"
904 def get_variant_npos_value(index_byte_size):
905 if index_byte_size == 1:
906 return 0xFF
907 elif index_byte_size == 2:
908 return 0xFFFF
909 else:
910 return 0xFFFFFFFF
912 npos_value = get_variant_npos_value(index_obj.GetByteSize())
913 index = index_obj.GetValueAsUnsigned(0)
914 if index == npos_value:
915 return " No Value"
917 # Invalid index can happen when the variant is not initialized yet.
918 template_arg_count = data_obj.GetType().GetNumberOfTemplateArguments()
919 if index >= template_arg_count:
920 return " <Invalid>"
922 active_type = data_obj.GetType().GetTemplateArgumentType(index)
923 return f" Active Type = {active_type.GetDisplayTypeName()} "
926 class VariantSynthProvider:
927 def __init__(self, valobj, dict):
928 self.raw_obj = valobj.GetNonSyntheticValue()
929 self.is_valid = False
930 self.index = None
931 self.data_obj = None
933 def update(self):
934 try:
935 self.index = self.raw_obj.GetChildMemberWithName(
936 "_M_index"
937 ).GetValueAsSigned(-1)
938 self.is_valid = self.index != -1
939 self.data_obj = self.raw_obj.GetChildMemberWithName("_M_u")
940 except:
941 self.is_valid = False
942 return False
944 def has_children(self):
945 return True
947 def num_children(self):
948 return 1 if self.is_valid else 0
950 def get_child_index(self, name):
951 return 0
953 def get_child_at_index(self, index):
954 if not self.is_valid:
955 return None
956 cur = 0
957 node = self.data_obj
958 while cur < self.index:
959 node = node.GetChildMemberWithName("_M_rest")
960 cur += 1
962 # _M_storage's type depends on variant field's type "_Type".
963 # 1. if '_Type' is literal type: _Type _M_storage.
964 # 2. otherwise, __gnu_cxx::__aligned_membuf<_Type> _M_storage.
966 # For 2. we have to cast it to underlying template _Type.
968 value = node.GetChildMemberWithName("_M_first").GetChildMemberWithName(
969 "_M_storage"
971 template_type = value.GetType().GetTemplateArgumentType(0)
973 # Literal type will return None for GetTemplateArgumentType(0)
974 if (
975 template_type
976 and "__gnu_cxx::__aligned_membuf" in value.GetType().GetDisplayTypeName()
977 and template_type.IsValid()
979 value = value.Cast(template_type)
981 if value.IsValid():
982 return value.Clone("Value")
983 return None