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