xref: /aosp_15_r20/external/cronet/third_party/libc++/src/utils/gdb/libcxx/printers.py (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1# ===----------------------------------------------------------------------===##
2#
3# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4# See https://llvm.org/LICENSE.txt for license information.
5# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6#
7# ===----------------------------------------------------------------------===##
8"""GDB pretty-printers for libc++.
9
10These should work for objects compiled with either the stable ABI or the unstable ABI.
11"""
12
13from __future__ import print_function
14
15import re
16import gdb
17
18# One under-documented feature of the gdb pretty-printer API
19# is that clients can call any other member of the API
20# before they call to_string.
21# Therefore all self.FIELDs must be set in the pretty-printer's
22# __init__ function.
23
24_void_pointer_type = gdb.lookup_type("void").pointer()
25
26
27_long_int_type = gdb.lookup_type("unsigned long long")
28
29_libcpp_big_endian = False
30
31
32def addr_as_long(addr):
33    return int(addr.cast(_long_int_type))
34
35
36# The size of a pointer in bytes.
37_pointer_size = _void_pointer_type.sizeof
38
39
40def _remove_cxx_namespace(typename):
41    """Removed libc++ specific namespace from the type.
42
43    Arguments:
44      typename(string): A type, such as std::__u::something.
45
46    Returns:
47      A string without the libc++ specific part, such as std::something.
48    """
49
50    return re.sub("std::__.*?::", "std::", typename)
51
52
53def _remove_generics(typename):
54    """Remove generics part of the type. Assumes typename is not empty.
55
56    Arguments:
57      typename(string): A type such as std::my_collection<element>.
58
59    Returns:
60      The prefix up to the generic part, such as std::my_collection.
61    """
62
63    match = re.match("^([^<]+)", typename)
64    return match.group(1)
65
66
67def _cc_field(node):
68    """Previous versions of libcxx had inconsistent field naming naming. Handle
69    both types.
70    """
71    try:
72        return node["__value_"]["__cc_"]
73    except:
74        return node["__value_"]["__cc"]
75
76
77def _data_field(node):
78    """Previous versions of libcxx had inconsistent field naming naming. Handle
79    both types.
80    """
81    try:
82        return node["__data_"]
83    except:
84        return node["__data"]
85
86
87def _size_field(node):
88    """Previous versions of libcxx had inconsistent field naming naming. Handle
89    both types.
90    """
91    try:
92        return node["__size_"]
93    except:
94        return node["__size"]
95
96
97# Some common substitutions on the types to reduce visual clutter (A user who
98# wants to see the actual details can always use print/r).
99_common_substitutions = [
100    (
101        "std::basic_string<char, std::char_traits<char>, std::allocator<char> >",
102        "std::string",
103    ),
104    ("std::basic_string_view<char, std::char_traits<char> >", "std::string_view"),
105]
106
107
108def _prettify_typename(gdb_type):
109    """Returns a pretty name for the type, or None if no name can be found.
110
111    Arguments:
112      gdb_type(gdb.Type): A type object.
113
114    Returns:
115      A string, without type_defs, libc++ namespaces, and common substitutions
116      applied.
117    """
118
119    type_without_typedefs = gdb_type.strip_typedefs()
120    typename = (
121        type_without_typedefs.name
122        or type_without_typedefs.tag
123        or str(type_without_typedefs)
124    )
125    result = _remove_cxx_namespace(typename)
126    for find_str, subst_str in _common_substitutions:
127        result = re.sub(find_str, subst_str, result)
128    return result
129
130
131def _typename_for_nth_generic_argument(gdb_type, n):
132    """Returns a pretty string for the nth argument of the given type.
133
134    Arguments:
135      gdb_type(gdb.Type): A type object, such as the one for std::map<int, int>
136      n: The (zero indexed) index of the argument to return.
137
138    Returns:
139      A string for the nth argument, such a "std::string"
140    """
141    element_type = gdb_type.template_argument(n)
142    return _prettify_typename(element_type)
143
144
145def _typename_with_n_generic_arguments(gdb_type, n):
146    """Return a string for the type with the first n (1, ...) generic args."""
147
148    base_type = _remove_generics(_prettify_typename(gdb_type))
149    arg_list = [base_type]
150    template = "%s<"
151    for i in range(n):
152        arg_list.append(_typename_for_nth_generic_argument(gdb_type, i))
153        template += "%s, "
154    result = (template[:-2] + ">") % tuple(arg_list)
155    return result
156
157
158def _typename_with_first_generic_argument(gdb_type):
159    return _typename_with_n_generic_arguments(gdb_type, 1)
160
161
162class StdTuplePrinter(object):
163    """Print a std::tuple."""
164
165    class _Children(object):
166        """Class to iterate over the tuple's children."""
167
168        def __init__(self, val):
169            self.val = val
170            self.child_iter = iter(self.val["__base_"].type.fields())
171            self.count = 0
172
173        def __iter__(self):
174            return self
175
176        def __next__(self):
177            # child_iter raises StopIteration when appropriate.
178            field_name = next(self.child_iter)
179            child = self.val["__base_"][field_name]["__value_"]
180            self.count += 1
181            return ("[%d]" % self.count, child)
182
183        next = __next__  # Needed for GDB built against Python 2.7.
184
185    def __init__(self, val):
186        self.val = val
187
188    def to_string(self):
189        typename = _remove_generics(_prettify_typename(self.val.type))
190        if not self.val.type.fields():
191            return "empty %s" % typename
192        return "%s containing" % typename
193
194    def children(self):
195        if not self.val.type.fields():
196            return iter(())
197        return self._Children(self.val)
198
199
200def _get_base_subobject(child_class_value, index=0):
201    """Returns the object's value in the form of the parent class at index.
202
203    This function effectively casts the child_class_value to the base_class's
204    type, but the type-to-cast to is stored in the field at index, and once
205    we know the field, we can just return the data.
206
207    Args:
208      child_class_value: the value to cast
209      index: the parent class index
210
211    Raises:
212      Exception: field at index was not a base-class field.
213    """
214
215    field = child_class_value.type.fields()[index]
216    if not field.is_base_class:
217        raise Exception("Not a base-class field.")
218    return child_class_value[field]
219
220
221def _value_of_pair_first(value):
222    """Convenience for _get_base_subobject, for the common case."""
223    return _get_base_subobject(value, 0)["__value_"]
224
225
226class StdStringPrinter(object):
227    """Print a std::string."""
228
229    def __init__(self, val):
230        self.val = val
231
232    def to_string(self):
233        """Build a python string from the data whether stored inline or separately."""
234        value_field = _value_of_pair_first(self.val["__r_"])
235        short_field = value_field["__s"]
236        short_size = short_field["__size_"]
237        if short_field["__is_long_"]:
238            long_field = value_field["__l"]
239            data = long_field["__data_"]
240            size = long_field["__size_"]
241        else:
242            data = short_field["__data_"]
243            size = short_field["__size_"]
244        return data.lazy_string(length=size)
245
246    def display_hint(self):
247        return "string"
248
249
250class StdStringViewPrinter(object):
251    """Print a std::string_view."""
252
253    def __init__(self, val):
254        self.val = val
255
256    def display_hint(self):
257        return "string"
258
259    def to_string(self):  # pylint: disable=g-bad-name
260        """GDB calls this to compute the pretty-printed form."""
261
262        ptr = _data_field(self.val)
263        ptr = ptr.cast(ptr.type.target().strip_typedefs().pointer())
264        size = _size_field(self.val)
265        return ptr.lazy_string(length=size)
266
267
268class StdUniquePtrPrinter(object):
269    """Print a std::unique_ptr."""
270
271    def __init__(self, val):
272        self.val = val
273        self.addr = _value_of_pair_first(self.val["__ptr_"])
274        self.pointee_type = self.val.type.template_argument(0)
275
276    def to_string(self):
277        typename = _remove_generics(_prettify_typename(self.val.type))
278        if not self.addr:
279            return "%s is nullptr" % typename
280        return "%s<%s> containing" % (
281            typename,
282            _remove_generics(_prettify_typename(self.pointee_type)),
283        )
284
285    def __iter__(self):
286        if self.addr:
287            yield "__ptr_", self.addr.cast(self.pointee_type.pointer())
288
289    def children(self):
290        return self
291
292
293class StdSharedPointerPrinter(object):
294    """Print a std::shared_ptr."""
295
296    def __init__(self, val):
297        self.val = val
298        self.addr = self.val["__ptr_"]
299
300    def to_string(self):
301        """Returns self as a string."""
302        typename = _remove_generics(_prettify_typename(self.val.type))
303        pointee_type = _remove_generics(
304            _prettify_typename(self.val.type.template_argument(0))
305        )
306        if not self.addr:
307            return "%s is nullptr" % typename
308        refcount = self.val["__cntrl_"]
309        if refcount != 0:
310            try:
311                usecount = refcount["__shared_owners_"] + 1
312                weakcount = refcount["__shared_weak_owners_"]
313                if usecount == 0:
314                    state = "expired, weak %d" % weakcount
315                else:
316                    state = "count %d, weak %d" % (usecount, weakcount)
317            except:
318                # Debug info for a class with virtual functions is emitted
319                # in the same place as its key function. That means that
320                # for std::shared_ptr, __shared_owners_ is emitted into
321                # into libcxx.[so|a] itself, rather than into the shared_ptr
322                # instantiation point. So if libcxx.so was built without
323                # debug info, these fields will be missing.
324                state = "count ?, weak ? (libc++ missing debug info)"
325        return "%s<%s> %s containing" % (typename, pointee_type, state)
326
327    def __iter__(self):
328        if self.addr:
329            yield "__ptr_", self.addr
330
331    def children(self):
332        return self
333
334
335class StdVectorPrinter(object):
336    """Print a std::vector."""
337
338    class _VectorBoolIterator(object):
339        """Class to iterate over the bool vector's children."""
340
341        def __init__(self, begin, size, bits_per_word):
342            self.item = begin
343            self.size = size
344            self.bits_per_word = bits_per_word
345            self.count = 0
346            self.offset = 0
347
348        def __iter__(self):
349            return self
350
351        def __next__(self):
352            """Retrieve the next element."""
353
354            self.count += 1
355            if self.count > self.size:
356                raise StopIteration
357            entry = self.item.dereference()
358            if entry & (1 << self.offset):
359                outbit = 1
360            else:
361                outbit = 0
362            self.offset += 1
363            if self.offset >= self.bits_per_word:
364                self.item += 1
365                self.offset = 0
366            return ("[%d]" % self.count, outbit)
367
368        next = __next__  # Needed for GDB built against Python 2.7.
369
370    class _VectorIterator(object):
371        """Class to iterate over the non-bool vector's children."""
372
373        def __init__(self, begin, end):
374            self.item = begin
375            self.end = end
376            self.count = 0
377
378        def __iter__(self):
379            return self
380
381        def __next__(self):
382            self.count += 1
383            if self.item == self.end:
384                raise StopIteration
385            entry = self.item.dereference()
386            self.item += 1
387            return ("[%d]" % self.count, entry)
388
389        next = __next__  # Needed for GDB built against Python 2.7.
390
391    def __init__(self, val):
392        """Set val, length, capacity, and iterator for bool and normal vectors."""
393        self.val = val
394        self.typename = _remove_generics(_prettify_typename(val.type))
395        begin = self.val["__begin_"]
396        if self.val.type.template_argument(0).code == gdb.TYPE_CODE_BOOL:
397            self.typename += "<bool>"
398            self.length = self.val["__size_"]
399            bits_per_word = self.val["__bits_per_word"]
400            self.capacity = (
401                _value_of_pair_first(self.val["__cap_alloc_"]) * bits_per_word
402            )
403            self.iterator = self._VectorBoolIterator(begin, self.length, bits_per_word)
404        else:
405            end = self.val["__end_"]
406            self.length = end - begin
407            self.capacity = (
408                _get_base_subobject(self.val["__end_cap_"])["__value_"] - begin
409            )
410            self.iterator = self._VectorIterator(begin, end)
411
412    def to_string(self):
413        return "%s of length %d, capacity %d" % (
414            self.typename,
415            self.length,
416            self.capacity,
417        )
418
419    def children(self):
420        return self.iterator
421
422    def display_hint(self):
423        return "array"
424
425
426class StdBitsetPrinter(object):
427    """Print a std::bitset."""
428
429    def __init__(self, val):
430        self.val = val
431        self.n_words = int(self.val["__n_words"])
432        self.bits_per_word = int(self.val["__bits_per_word"])
433        self.bit_count = self.val.type.template_argument(0)
434        if self.n_words == 1:
435            self.values = [int(self.val["__first_"])]
436        else:
437            self.values = [
438                int(self.val["__first_"][index]) for index in range(self.n_words)
439            ]
440
441    def to_string(self):
442        typename = _prettify_typename(self.val.type)
443        return "%s" % typename
444
445    def _list_it(self):
446        for bit in range(self.bit_count):
447            word = bit // self.bits_per_word
448            word_bit = bit % self.bits_per_word
449            if self.values[word] & (1 << word_bit):
450                yield ("[%d]" % bit, 1)
451
452    def __iter__(self):
453        return self._list_it()
454
455    def children(self):
456        return self
457
458
459class StdDequePrinter(object):
460    """Print a std::deque."""
461
462    def __init__(self, val):
463        self.val = val
464        self.size = int(_value_of_pair_first(val["__size_"]))
465        self.start_ptr = self.val["__map_"]["__begin_"]
466        self.first_block_start_index = int(self.val["__start_"])
467        self.node_type = self.start_ptr.type
468        self.block_size = self._calculate_block_size(val.type.template_argument(0))
469
470    def _calculate_block_size(self, element_type):
471        """Calculates the number of elements in a full block."""
472        size = element_type.sizeof
473        # Copied from struct __deque_block_size implementation of libcxx.
474        return 4096 / size if size < 256 else 16
475
476    def _bucket_it(self, start_addr, start_index, end_index):
477        for i in range(start_index, end_index):
478            yield i, (start_addr.dereference() + i).dereference()
479
480    def _list_it(self):
481        """Primary iteration worker."""
482        num_emitted = 0
483        current_addr = self.start_ptr
484        start_index = self.first_block_start_index
485        while num_emitted < self.size:
486            end_index = min(start_index + self.size - num_emitted, self.block_size)
487            for _, elem in self._bucket_it(current_addr, start_index, end_index):
488                yield "", elem
489            num_emitted += end_index - start_index
490            current_addr = gdb.Value(addr_as_long(current_addr) + _pointer_size).cast(
491                self.node_type
492            )
493            start_index = 0
494
495    def to_string(self):
496        typename = _remove_generics(_prettify_typename(self.val.type))
497        if self.size:
498            return "%s with %d elements" % (typename, self.size)
499        return "%s is empty" % typename
500
501    def __iter__(self):
502        return self._list_it()
503
504    def children(self):
505        return self
506
507    def display_hint(self):
508        return "array"
509
510
511class StdListPrinter(object):
512    """Print a std::list."""
513
514    def __init__(self, val):
515        self.val = val
516        size_alloc_field = self.val["__size_alloc_"]
517        self.size = int(_value_of_pair_first(size_alloc_field))
518        dummy_node = self.val["__end_"]
519        self.nodetype = gdb.lookup_type(
520            re.sub(
521                "__list_node_base", "__list_node", str(dummy_node.type.strip_typedefs())
522            )
523        ).pointer()
524        self.first_node = dummy_node["__next_"]
525
526    def to_string(self):
527        typename = _remove_generics(_prettify_typename(self.val.type))
528        if self.size:
529            return "%s with %d elements" % (typename, self.size)
530        return "%s is empty" % typename
531
532    def _list_iter(self):
533        current_node = self.first_node
534        for _ in range(self.size):
535            yield "", current_node.cast(self.nodetype).dereference()["__value_"]
536            current_node = current_node.dereference()["__next_"]
537
538    def __iter__(self):
539        return self._list_iter()
540
541    def children(self):
542        return self if self.nodetype else iter(())
543
544    def display_hint(self):
545        return "array"
546
547
548class StdQueueOrStackPrinter(object):
549    """Print a std::queue or std::stack."""
550
551    def __init__(self, val):
552        self.val = val
553        self.underlying = val["c"]
554
555    def to_string(self):
556        typename = _remove_generics(_prettify_typename(self.val.type))
557        return "%s wrapping" % typename
558
559    def children(self):
560        return iter([("", self.underlying)])
561
562    def display_hint(self):
563        return "array"
564
565
566class StdPriorityQueuePrinter(object):
567    """Print a std::priority_queue."""
568
569    def __init__(self, val):
570        self.val = val
571        self.underlying = val["c"]
572
573    def to_string(self):
574        # TODO(tamur): It would be nice to print the top element. The technical
575        # difficulty is that, the implementation refers to the underlying
576        # container, which is a generic class. libstdcxx pretty printers do not
577        # print the top element.
578        typename = _remove_generics(_prettify_typename(self.val.type))
579        return "%s wrapping" % typename
580
581    def children(self):
582        return iter([("", self.underlying)])
583
584    def display_hint(self):
585        return "array"
586
587
588class RBTreeUtils(object):
589    """Utility class for std::(multi)map, and std::(multi)set and iterators."""
590
591    def __init__(self, cast_type, root):
592        self.cast_type = cast_type
593        self.root = root
594
595    def left_child(self, node):
596        result = node.cast(self.cast_type).dereference()["__left_"]
597        return result
598
599    def right_child(self, node):
600        result = node.cast(self.cast_type).dereference()["__right_"]
601        return result
602
603    def parent(self, node):
604        """Return the parent of node, if it exists."""
605        # If this is the root, then from the algorithm's point of view, it has no
606        # parent.
607        if node == self.root:
608            return None
609
610        # We don't have enough information to tell if this is the end_node (which
611        # doesn't have a __parent_ field), or the root (which doesn't have a parent
612        # from the algorithm's point of view), so cast_type may not be correct for
613        # this particular node. Use heuristics.
614
615        # The end_node's left child is the root. Note that when printing interators
616        # in isolation, the root is unknown.
617        if self.left_child(node) == self.root:
618            return None
619
620        parent = node.cast(self.cast_type).dereference()["__parent_"]
621        # If the value at the offset of __parent_ doesn't look like a valid pointer,
622        # then assume that node is the end_node (and therefore has no parent).
623        # End_node type has a pointer embedded, so should have pointer alignment.
624        if addr_as_long(parent) % _void_pointer_type.alignof:
625            return None
626        # This is ugly, but the only other option is to dereference an invalid
627        # pointer.  0x8000 is fairly arbitrary, but has had good results in
628        # practice.  If there was a way to tell if a pointer is invalid without
629        # actually dereferencing it and spewing error messages, that would be ideal.
630        if parent < 0x8000:
631            return None
632        return parent
633
634    def is_left_child(self, node):
635        parent = self.parent(node)
636        return parent is not None and self.left_child(parent) == node
637
638    def is_right_child(self, node):
639        parent = self.parent(node)
640        return parent is not None and self.right_child(parent) == node
641
642
643class AbstractRBTreePrinter(object):
644    """Abstract super class for std::(multi)map, and std::(multi)set."""
645
646    def __init__(self, val):
647        self.val = val
648        tree = self.val["__tree_"]
649        self.size = int(_value_of_pair_first(tree["__pair3_"]))
650        dummy_root = tree["__pair1_"]
651        root = _value_of_pair_first(dummy_root)["__left_"]
652        cast_type = self._init_cast_type(val.type)
653        self.util = RBTreeUtils(cast_type, root)
654
655    def _get_key_value(self, node):
656        """Subclasses should override to return a list of values to yield."""
657        raise NotImplementedError
658
659    def _traverse(self):
660        """Traverses the binary search tree in order."""
661        current = self.util.root
662        skip_left_child = False
663        while True:
664            if not skip_left_child and self.util.left_child(current):
665                current = self.util.left_child(current)
666                continue
667            skip_left_child = False
668            for key_value in self._get_key_value(current):
669                yield "", key_value
670            right_child = self.util.right_child(current)
671            if right_child:
672                current = right_child
673                continue
674            while self.util.is_right_child(current):
675                current = self.util.parent(current)
676            if self.util.is_left_child(current):
677                current = self.util.parent(current)
678                skip_left_child = True
679                continue
680            break
681
682    def __iter__(self):
683        return self._traverse()
684
685    def children(self):
686        return self if self.util.cast_type and self.size > 0 else iter(())
687
688    def to_string(self):
689        typename = _remove_generics(_prettify_typename(self.val.type))
690        if self.size:
691            return "%s with %d elements" % (typename, self.size)
692        return "%s is empty" % typename
693
694
695class StdMapPrinter(AbstractRBTreePrinter):
696    """Print a std::map or std::multimap."""
697
698    def _init_cast_type(self, val_type):
699        map_it_type = gdb.lookup_type(
700            str(val_type.strip_typedefs()) + "::iterator"
701        ).strip_typedefs()
702        tree_it_type = map_it_type.template_argument(0)
703        node_ptr_type = tree_it_type.template_argument(1)
704        return node_ptr_type
705
706    def display_hint(self):
707        return "map"
708
709    def _get_key_value(self, node):
710        key_value = _cc_field(node.cast(self.util.cast_type).dereference())
711        return [key_value["first"], key_value["second"]]
712
713
714class StdSetPrinter(AbstractRBTreePrinter):
715    """Print a std::set."""
716
717    def _init_cast_type(self, val_type):
718        set_it_type = gdb.lookup_type(
719            str(val_type.strip_typedefs()) + "::iterator"
720        ).strip_typedefs()
721        node_ptr_type = set_it_type.template_argument(1)
722        return node_ptr_type
723
724    def display_hint(self):
725        return "array"
726
727    def _get_key_value(self, node):
728        key_value = node.cast(self.util.cast_type).dereference()["__value_"]
729        return [key_value]
730
731
732class AbstractRBTreeIteratorPrinter(object):
733    """Abstract super class for std::(multi)map, and std::(multi)set iterator."""
734
735    def _initialize(self, val, typename):
736        self.typename = typename
737        self.val = val
738        self.addr = self.val["__ptr_"]
739        cast_type = self.val.type.template_argument(1)
740        self.util = RBTreeUtils(cast_type, None)
741        if self.addr:
742            self.node = self.addr.cast(cast_type).dereference()
743
744    def _is_valid_node(self):
745        if not self.util.parent(self.addr):
746            return False
747        return self.util.is_left_child(self.addr) or self.util.is_right_child(self.addr)
748
749    def to_string(self):
750        if not self.addr:
751            return "%s is nullptr" % self.typename
752        return "%s " % self.typename
753
754    def _get_node_value(self, node):
755        raise NotImplementedError
756
757    def __iter__(self):
758        addr_str = "[%s]" % str(self.addr)
759        if not self._is_valid_node():
760            yield addr_str, " end()"
761        else:
762            yield addr_str, self._get_node_value(self.node)
763
764    def children(self):
765        return self if self.addr else iter(())
766
767
768class MapIteratorPrinter(AbstractRBTreeIteratorPrinter):
769    """Print a std::(multi)map iterator."""
770
771    def __init__(self, val):
772        self._initialize(val["__i_"], _remove_generics(_prettify_typename(val.type)))
773
774    def _get_node_value(self, node):
775        return _cc_field(node)
776
777
778class SetIteratorPrinter(AbstractRBTreeIteratorPrinter):
779    """Print a std::(multi)set iterator."""
780
781    def __init__(self, val):
782        self._initialize(val, _remove_generics(_prettify_typename(val.type)))
783
784    def _get_node_value(self, node):
785        return node["__value_"]
786
787
788class StdFposPrinter(object):
789    """Print a std::fpos or std::streampos."""
790
791    def __init__(self, val):
792        self.val = val
793
794    def to_string(self):
795        typename = _remove_generics(_prettify_typename(self.val.type))
796        offset = self.val["__off_"]
797        state = self.val["__st_"]
798
799        state_fields = []
800        if state.type.code == gdb.TYPE_CODE_STRUCT:
801            state_fields = [f.name for f in state.type.fields()]
802
803        state_string = ""
804        if "__count" in state_fields and "__value" in state_fields:
805            count = state["__count"]
806            value = state["__value"]["__wch"]
807            state_string = " with state: {count:%s value:%s}" % (count, value)
808
809        return "%s with stream offset:%s%s" % (typename, offset, state_string)
810
811
812class AbstractUnorderedCollectionPrinter(object):
813    """Abstract super class for std::unordered_(multi)[set|map]."""
814
815    def __init__(self, val):
816        self.val = val
817        self.table = val["__table_"]
818        self.sentinel = self.table["__p1_"]
819        self.size = int(_value_of_pair_first(self.table["__p2_"]))
820        node_base_type = self.sentinel.type.template_argument(0)
821        self.cast_type = node_base_type.template_argument(0)
822
823    def _list_it(self, sentinel_ptr):
824        next_ptr = _value_of_pair_first(sentinel_ptr)["__next_"]
825        while str(next_ptr.cast(_void_pointer_type)) != "0x0":
826            next_val = next_ptr.cast(self.cast_type).dereference()
827            for key_value in self._get_key_value(next_val):
828                yield "", key_value
829            next_ptr = next_val["__next_"]
830
831    def to_string(self):
832        typename = _remove_generics(_prettify_typename(self.val.type))
833        if self.size:
834            return "%s with %d elements" % (typename, self.size)
835        return "%s is empty" % typename
836
837    def _get_key_value(self, node):
838        """Subclasses should override to return a list of values to yield."""
839        raise NotImplementedError
840
841    def children(self):
842        return self if self.cast_type and self.size > 0 else iter(())
843
844    def __iter__(self):
845        return self._list_it(self.sentinel)
846
847
848class StdUnorderedSetPrinter(AbstractUnorderedCollectionPrinter):
849    """Print a std::unordered_(multi)set."""
850
851    def _get_key_value(self, node):
852        return [node["__value_"]]
853
854    def display_hint(self):
855        return "array"
856
857
858class StdUnorderedMapPrinter(AbstractUnorderedCollectionPrinter):
859    """Print a std::unordered_(multi)map."""
860
861    def _get_key_value(self, node):
862        key_value = _cc_field(node)
863        return [key_value["first"], key_value["second"]]
864
865    def display_hint(self):
866        return "map"
867
868
869class AbstractHashMapIteratorPrinter(object):
870    """Abstract class for unordered collection iterators."""
871
872    def _initialize(self, val, addr):
873        self.val = val
874        self.typename = _remove_generics(_prettify_typename(self.val.type))
875        self.addr = addr
876        if self.addr:
877            self.node = self.addr.cast(self.cast_type).dereference()
878
879    def _get_key_value(self):
880        """Subclasses should override to return a list of values to yield."""
881        raise NotImplementedError
882
883    def to_string(self):
884        if not self.addr:
885            return "%s = end()" % self.typename
886        return "%s " % self.typename
887
888    def children(self):
889        return self if self.addr else iter(())
890
891    def __iter__(self):
892        for key_value in self._get_key_value():
893            yield "", key_value
894
895
896class StdUnorderedSetIteratorPrinter(AbstractHashMapIteratorPrinter):
897    """Print a std::(multi)set iterator."""
898
899    def __init__(self, val):
900        self.cast_type = val.type.template_argument(0)
901        self._initialize(val, val["__node_"])
902
903    def _get_key_value(self):
904        return [self.node["__value_"]]
905
906    def display_hint(self):
907        return "array"
908
909
910class StdUnorderedMapIteratorPrinter(AbstractHashMapIteratorPrinter):
911    """Print a std::(multi)map iterator."""
912
913    def __init__(self, val):
914        self.cast_type = val.type.template_argument(0).template_argument(0)
915        self._initialize(val, val["__i_"]["__node_"])
916
917    def _get_key_value(self):
918        key_value = _cc_field(self.node)
919        return [key_value["first"], key_value["second"]]
920
921    def display_hint(self):
922        return "map"
923
924
925def _remove_std_prefix(typename):
926    match = re.match("^std::(.+)", typename)
927    return match.group(1) if match is not None else ""
928
929
930class LibcxxPrettyPrinter(object):
931    """PrettyPrinter object so gdb-commands like 'info pretty-printers' work."""
932
933    def __init__(self, name):
934        super(LibcxxPrettyPrinter, self).__init__()
935        self.name = name
936        self.enabled = True
937
938        self.lookup = {
939            "basic_string": StdStringPrinter,
940            "string": StdStringPrinter,
941            "string_view": StdStringViewPrinter,
942            "tuple": StdTuplePrinter,
943            "unique_ptr": StdUniquePtrPrinter,
944            "shared_ptr": StdSharedPointerPrinter,
945            "weak_ptr": StdSharedPointerPrinter,
946            "bitset": StdBitsetPrinter,
947            "deque": StdDequePrinter,
948            "list": StdListPrinter,
949            "queue": StdQueueOrStackPrinter,
950            "stack": StdQueueOrStackPrinter,
951            "priority_queue": StdPriorityQueuePrinter,
952            "map": StdMapPrinter,
953            "multimap": StdMapPrinter,
954            "set": StdSetPrinter,
955            "multiset": StdSetPrinter,
956            "vector": StdVectorPrinter,
957            "__map_iterator": MapIteratorPrinter,
958            "__map_const_iterator": MapIteratorPrinter,
959            "__tree_iterator": SetIteratorPrinter,
960            "__tree_const_iterator": SetIteratorPrinter,
961            "fpos": StdFposPrinter,
962            "unordered_set": StdUnorderedSetPrinter,
963            "unordered_multiset": StdUnorderedSetPrinter,
964            "unordered_map": StdUnorderedMapPrinter,
965            "unordered_multimap": StdUnorderedMapPrinter,
966            "__hash_map_iterator": StdUnorderedMapIteratorPrinter,
967            "__hash_map_const_iterator": StdUnorderedMapIteratorPrinter,
968            "__hash_iterator": StdUnorderedSetIteratorPrinter,
969            "__hash_const_iterator": StdUnorderedSetIteratorPrinter,
970        }
971
972        self.subprinters = []
973        for name, subprinter in self.lookup.items():
974            # Subprinters and names are used only for the rarely used command "info
975            # pretty" (and related), so the name of the first data structure it prints
976            # is a reasonable choice.
977            if subprinter not in self.subprinters:
978                subprinter.name = name
979                self.subprinters.append(subprinter)
980
981    def __call__(self, val):
982        """Return the pretty printer for a val, if the type is supported."""
983
984        # Do not handle any type that is not a struct/class.
985        if val.type.strip_typedefs().code != gdb.TYPE_CODE_STRUCT:
986            return None
987
988        # Don't attempt types known to be inside libstdcxx.
989        typename = val.type.name or val.type.tag or str(val.type)
990        match = re.match("^std::(__.*?)::", typename)
991        if match is not None and match.group(1) in [
992            "__cxx1998",
993            "__debug",
994            "__7",
995            "__g",
996        ]:
997            return None
998
999        # Handle any using declarations or other typedefs.
1000        typename = _prettify_typename(val.type)
1001        if not typename:
1002            return None
1003        without_generics = _remove_generics(typename)
1004        lookup_name = _remove_std_prefix(without_generics)
1005        if lookup_name in self.lookup:
1006            return self.lookup[lookup_name](val)
1007        return None
1008
1009
1010_libcxx_printer_name = "libcxx_pretty_printer"
1011
1012
1013# These are called for every binary object file, which could be thousands in
1014# certain pathological cases. Limit our pretty printers to the progspace.
1015def _register_libcxx_printers(event):
1016    progspace = event.new_objfile.progspace
1017    # It would be ideal to get the endianness at print time, but
1018    # gdb.execute clears gdb's internal wrap buffer, removing any values
1019    # already generated as part of a larger data structure, and there is
1020    # no python api to get the endianness. Mixed-endianness debugging
1021    # rare enough that this workaround should be adequate.
1022    _libcpp_big_endian = "big endian" in gdb.execute("show endian", to_string=True)
1023
1024    if not getattr(progspace, _libcxx_printer_name, False):
1025        print("Loading libc++ pretty-printers.")
1026        gdb.printing.register_pretty_printer(
1027            progspace, LibcxxPrettyPrinter(_libcxx_printer_name)
1028        )
1029        setattr(progspace, _libcxx_printer_name, True)
1030
1031
1032def _unregister_libcxx_printers(event):
1033    progspace = event.progspace
1034    if getattr(progspace, _libcxx_printer_name, False):
1035        for printer in progspace.pretty_printers:
1036            if getattr(printer, "name", "none") == _libcxx_printer_name:
1037                progspace.pretty_printers.remove(printer)
1038                setattr(progspace, _libcxx_printer_name, False)
1039                break
1040
1041
1042def register_libcxx_printer_loader():
1043    """Register event handlers to load libc++ pretty-printers."""
1044    gdb.events.new_objfile.connect(_register_libcxx_printers)
1045    gdb.events.clear_objfiles.connect(_unregister_libcxx_printers)
1046