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