• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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        while num_emitted < self.size:
450            end_index = min(start_index + self.size - num_emitted, self.block_size)
451            for _, elem in self._bucket_it(current_addr, start_index, end_index):
452                yield "", elem
453            num_emitted += end_index - start_index
454            current_addr = gdb.Value(addr_as_long(current_addr) + _pointer_size).cast(
455                self.node_type
456            )
457            start_index = 0
458
459    def to_string(self):
460        typename = _remove_generics(_prettify_typename(self.val.type))
461        if self.size:
462            return "%s with %d elements" % (typename, self.size)
463        return "%s is empty" % typename
464
465    def __iter__(self):
466        return self._list_it()
467
468    def children(self):
469        return self
470
471    def display_hint(self):
472        return "array"
473
474
475class StdListPrinter(object):
476    """Print a std::list."""
477
478    def __init__(self, val):
479        self.val = val
480        self.size = int(self.val["__size_"])
481        dummy_node = self.val["__end_"]
482        self.nodetype = gdb.lookup_type(
483            re.sub(
484                "__list_node_base", "__list_node", str(dummy_node.type.strip_typedefs())
485            )
486        ).pointer()
487        self.first_node = dummy_node["__next_"]
488
489    def to_string(self):
490        typename = _remove_generics(_prettify_typename(self.val.type))
491        if self.size:
492            return "%s with %d elements" % (typename, self.size)
493        return "%s is empty" % typename
494
495    def _list_iter(self):
496        current_node = self.first_node
497        for _ in range(self.size):
498            yield "", current_node.cast(self.nodetype).dereference()["__value_"]
499            current_node = current_node.dereference()["__next_"]
500
501    def __iter__(self):
502        return self._list_iter()
503
504    def children(self):
505        return self if self.nodetype else iter(())
506
507    def display_hint(self):
508        return "array"
509
510
511class StdQueueOrStackPrinter(object):
512    """Print a std::queue or std::stack."""
513
514    def __init__(self, val):
515        self.val = val
516        self.underlying = val["c"]
517
518    def to_string(self):
519        typename = _remove_generics(_prettify_typename(self.val.type))
520        return "%s wrapping" % typename
521
522    def children(self):
523        return iter([("", self.underlying)])
524
525    def display_hint(self):
526        return "array"
527
528
529class StdPriorityQueuePrinter(object):
530    """Print a std::priority_queue."""
531
532    def __init__(self, val):
533        self.val = val
534        self.underlying = val["c"]
535
536    def to_string(self):
537        # TODO(tamur): It would be nice to print the top element. The technical
538        # difficulty is that, the implementation refers to the underlying
539        # container, which is a generic class. libstdcxx pretty printers do not
540        # print the top element.
541        typename = _remove_generics(_prettify_typename(self.val.type))
542        return "%s wrapping" % typename
543
544    def children(self):
545        return iter([("", self.underlying)])
546
547    def display_hint(self):
548        return "array"
549
550
551class RBTreeUtils(object):
552    """Utility class for std::(multi)map, and std::(multi)set and iterators."""
553
554    def __init__(self, cast_type, root):
555        self.cast_type = cast_type
556        self.root = root
557
558    def left_child(self, node):
559        result = node.cast(self.cast_type).dereference()["__left_"]
560        return result
561
562    def right_child(self, node):
563        result = node.cast(self.cast_type).dereference()["__right_"]
564        return result
565
566    def parent(self, node):
567        """Return the parent of node, if it exists."""
568        # If this is the root, then from the algorithm's point of view, it has no
569        # parent.
570        if node == self.root:
571            return None
572
573        # We don't have enough information to tell if this is the end_node (which
574        # doesn't have a __parent_ field), or the root (which doesn't have a parent
575        # from the algorithm's point of view), so cast_type may not be correct for
576        # this particular node. Use heuristics.
577
578        # The end_node's left child is the root. Note that when printing interators
579        # in isolation, the root is unknown.
580        if self.left_child(node) == self.root:
581            return None
582
583        parent = node.cast(self.cast_type).dereference()["__parent_"]
584        # If the value at the offset of __parent_ doesn't look like a valid pointer,
585        # then assume that node is the end_node (and therefore has no parent).
586        # End_node type has a pointer embedded, so should have pointer alignment.
587        if addr_as_long(parent) % _void_pointer_type.alignof:
588            return None
589        # This is ugly, but the only other option is to dereference an invalid
590        # pointer.  0x8000 is fairly arbitrary, but has had good results in
591        # practice.  If there was a way to tell if a pointer is invalid without
592        # actually dereferencing it and spewing error messages, that would be ideal.
593        if parent < 0x8000:
594            return None
595        return parent
596
597    def is_left_child(self, node):
598        parent = self.parent(node)
599        return parent is not None and self.left_child(parent) == node
600
601    def is_right_child(self, node):
602        parent = self.parent(node)
603        return parent is not None and self.right_child(parent) == node
604
605
606class AbstractRBTreePrinter(object):
607    """Abstract super class for std::(multi)map, and std::(multi)set."""
608
609    def __init__(self, val):
610        self.val = val
611        tree = self.val["__tree_"]
612        self.size = int(tree["__size_"])
613        root = tree["__end_node_"]["__left_"]
614        cast_type = self._init_cast_type(val.type)
615        self.util = RBTreeUtils(cast_type, root)
616
617    def _get_key_value(self, node):
618        """Subclasses should override to return a list of values to yield."""
619        raise NotImplementedError
620
621    def _traverse(self):
622        """Traverses the binary search tree in order."""
623        current = self.util.root
624        skip_left_child = False
625        while True:
626            if not skip_left_child and self.util.left_child(current):
627                current = self.util.left_child(current)
628                continue
629            skip_left_child = False
630            for key_value in self._get_key_value(current):
631                yield "", key_value
632            right_child = self.util.right_child(current)
633            if right_child:
634                current = right_child
635                continue
636            while self.util.is_right_child(current):
637                current = self.util.parent(current)
638            if self.util.is_left_child(current):
639                current = self.util.parent(current)
640                skip_left_child = True
641                continue
642            break
643
644    def __iter__(self):
645        return self._traverse()
646
647    def children(self):
648        return self if self.util.cast_type and self.size > 0 else iter(())
649
650    def to_string(self):
651        typename = _remove_generics(_prettify_typename(self.val.type))
652        if self.size:
653            return "%s with %d elements" % (typename, self.size)
654        return "%s is empty" % typename
655
656
657class StdMapPrinter(AbstractRBTreePrinter):
658    """Print a std::map or std::multimap."""
659
660    def _init_cast_type(self, val_type):
661        map_it_type = gdb.lookup_type(
662            str(val_type.strip_typedefs()) + "::iterator"
663        ).strip_typedefs()
664        tree_it_type = map_it_type.template_argument(0)
665        node_ptr_type = tree_it_type.template_argument(1)
666        return node_ptr_type
667
668    def display_hint(self):
669        return "map"
670
671    def _get_key_value(self, node):
672        key_value = _cc_field(node.cast(self.util.cast_type).dereference())
673        return [key_value["first"], key_value["second"]]
674
675
676class StdSetPrinter(AbstractRBTreePrinter):
677    """Print a std::set."""
678
679    def _init_cast_type(self, val_type):
680        set_it_type = gdb.lookup_type(
681            str(val_type.strip_typedefs()) + "::iterator"
682        ).strip_typedefs()
683        node_ptr_type = set_it_type.template_argument(1)
684        return node_ptr_type
685
686    def display_hint(self):
687        return "array"
688
689    def _get_key_value(self, node):
690        key_value = node.cast(self.util.cast_type).dereference()["__value_"]
691        return [key_value]
692
693
694class AbstractRBTreeIteratorPrinter(object):
695    """Abstract super class for std::(multi)map, and std::(multi)set iterator."""
696
697    def _initialize(self, val, typename):
698        self.typename = typename
699        self.val = val
700        self.addr = self.val["__ptr_"]
701        cast_type = self.val.type.template_argument(1)
702        self.util = RBTreeUtils(cast_type, None)
703        if self.addr:
704            self.node = self.addr.cast(cast_type).dereference()
705
706    def _is_valid_node(self):
707        if not self.util.parent(self.addr):
708            return False
709        return self.util.is_left_child(self.addr) or self.util.is_right_child(self.addr)
710
711    def to_string(self):
712        if not self.addr:
713            return "%s is nullptr" % self.typename
714        return "%s " % self.typename
715
716    def _get_node_value(self, node):
717        raise NotImplementedError
718
719    def __iter__(self):
720        addr_str = "[%s]" % str(self.addr)
721        if not self._is_valid_node():
722            yield addr_str, " end()"
723        else:
724            yield addr_str, self._get_node_value(self.node)
725
726    def children(self):
727        return self if self.addr else iter(())
728
729
730class MapIteratorPrinter(AbstractRBTreeIteratorPrinter):
731    """Print a std::(multi)map iterator."""
732
733    def __init__(self, val):
734        self._initialize(val["__i_"], _remove_generics(_prettify_typename(val.type)))
735
736    def _get_node_value(self, node):
737        return _cc_field(node)
738
739
740class SetIteratorPrinter(AbstractRBTreeIteratorPrinter):
741    """Print a std::(multi)set iterator."""
742
743    def __init__(self, val):
744        self._initialize(val, _remove_generics(_prettify_typename(val.type)))
745
746    def _get_node_value(self, node):
747        return node["__value_"]
748
749
750class StdFposPrinter(object):
751    """Print a std::fpos or std::streampos."""
752
753    def __init__(self, val):
754        self.val = val
755
756    def to_string(self):
757        typename = _remove_generics(_prettify_typename(self.val.type))
758        offset = self.val["__off_"]
759        state = self.val["__st_"]
760
761        state_fields = []
762        if state.type.code == gdb.TYPE_CODE_STRUCT:
763            state_fields = [f.name for f in state.type.fields()]
764
765        state_string = ""
766        if "__count" in state_fields and "__value" in state_fields:
767            count = state["__count"]
768            value = state["__value"]["__wch"]
769            state_string = " with state: {count:%s value:%s}" % (count, value)
770
771        return "%s with stream offset:%s%s" % (typename, offset, state_string)
772
773
774class AbstractUnorderedCollectionPrinter(object):
775    """Abstract super class for std::unordered_(multi)[set|map]."""
776
777    def __init__(self, val):
778        self.val = val
779        self.table = val["__table_"]
780        self.sentinel = self.table["__first_node_"]
781        self.size = int(self.table["__size_"])
782        node_base_type = self.sentinel.type
783        self.cast_type = node_base_type.template_argument(0)
784
785    def _list_it(self, sentinel_ptr):
786        next_ptr = sentinel_ptr["__next_"]
787        while str(next_ptr.cast(_void_pointer_type)) != "0x0":
788            next_val = next_ptr.cast(self.cast_type).dereference()
789            for key_value in self._get_key_value(next_val):
790                yield "", key_value
791            next_ptr = next_val["__next_"]
792
793    def to_string(self):
794        typename = _remove_generics(_prettify_typename(self.val.type))
795        if self.size:
796            return "%s with %d elements" % (typename, self.size)
797        return "%s is empty" % typename
798
799    def _get_key_value(self, node):
800        """Subclasses should override to return a list of values to yield."""
801        raise NotImplementedError
802
803    def children(self):
804        return self if self.cast_type and self.size > 0 else iter(())
805
806    def __iter__(self):
807        return self._list_it(self.sentinel)
808
809
810class StdUnorderedSetPrinter(AbstractUnorderedCollectionPrinter):
811    """Print a std::unordered_(multi)set."""
812
813    def _get_key_value(self, node):
814        return [node["__value_"]]
815
816    def display_hint(self):
817        return "array"
818
819
820class StdUnorderedMapPrinter(AbstractUnorderedCollectionPrinter):
821    """Print a std::unordered_(multi)map."""
822
823    def _get_key_value(self, node):
824        key_value = _cc_field(node)
825        return [key_value["first"], key_value["second"]]
826
827    def display_hint(self):
828        return "map"
829
830
831class AbstractHashMapIteratorPrinter(object):
832    """Abstract class for unordered collection iterators."""
833
834    def _initialize(self, val, addr):
835        self.val = val
836        self.typename = _remove_generics(_prettify_typename(self.val.type))
837        self.addr = addr
838        if self.addr:
839            self.node = self.addr.cast(self.cast_type).dereference()
840
841    def _get_key_value(self):
842        """Subclasses should override to return a list of values to yield."""
843        raise NotImplementedError
844
845    def to_string(self):
846        if not self.addr:
847            return "%s = end()" % self.typename
848        return "%s " % self.typename
849
850    def children(self):
851        return self if self.addr else iter(())
852
853    def __iter__(self):
854        for key_value in self._get_key_value():
855            yield "", key_value
856
857
858class StdUnorderedSetIteratorPrinter(AbstractHashMapIteratorPrinter):
859    """Print a std::(multi)set iterator."""
860
861    def __init__(self, val):
862        self.cast_type = val.type.template_argument(0)
863        self._initialize(val, val["__node_"])
864
865    def _get_key_value(self):
866        return [self.node["__value_"]]
867
868    def display_hint(self):
869        return "array"
870
871
872class StdUnorderedMapIteratorPrinter(AbstractHashMapIteratorPrinter):
873    """Print a std::(multi)map iterator."""
874
875    def __init__(self, val):
876        self.cast_type = val.type.template_argument(0).template_argument(0)
877        self._initialize(val, val["__i_"]["__node_"])
878
879    def _get_key_value(self):
880        key_value = _cc_field(self.node)
881        return [key_value["first"], key_value["second"]]
882
883    def display_hint(self):
884        return "map"
885
886
887def _remove_std_prefix(typename):
888    match = re.match("^std::(.+)", typename)
889    return match.group(1) if match is not None else ""
890
891
892class LibcxxPrettyPrinter(object):
893    """PrettyPrinter object so gdb-commands like 'info pretty-printers' work."""
894
895    def __init__(self, name):
896        super(LibcxxPrettyPrinter, self).__init__()
897        self.name = name
898        self.enabled = True
899
900        self.lookup = {
901            "basic_string": StdStringPrinter,
902            "string": StdStringPrinter,
903            "string_view": StdStringViewPrinter,
904            "tuple": StdTuplePrinter,
905            "unique_ptr": StdUniquePtrPrinter,
906            "shared_ptr": StdSharedPointerPrinter,
907            "weak_ptr": StdSharedPointerPrinter,
908            "bitset": StdBitsetPrinter,
909            "deque": StdDequePrinter,
910            "list": StdListPrinter,
911            "queue": StdQueueOrStackPrinter,
912            "stack": StdQueueOrStackPrinter,
913            "priority_queue": StdPriorityQueuePrinter,
914            "map": StdMapPrinter,
915            "multimap": StdMapPrinter,
916            "set": StdSetPrinter,
917            "multiset": StdSetPrinter,
918            "vector": StdVectorPrinter,
919            "__map_iterator": MapIteratorPrinter,
920            "__map_const_iterator": MapIteratorPrinter,
921            "__tree_iterator": SetIteratorPrinter,
922            "__tree_const_iterator": SetIteratorPrinter,
923            "fpos": StdFposPrinter,
924            "unordered_set": StdUnorderedSetPrinter,
925            "unordered_multiset": StdUnorderedSetPrinter,
926            "unordered_map": StdUnorderedMapPrinter,
927            "unordered_multimap": StdUnorderedMapPrinter,
928            "__hash_map_iterator": StdUnorderedMapIteratorPrinter,
929            "__hash_map_const_iterator": StdUnorderedMapIteratorPrinter,
930            "__hash_iterator": StdUnorderedSetIteratorPrinter,
931            "__hash_const_iterator": StdUnorderedSetIteratorPrinter,
932        }
933
934        self.subprinters = []
935        for name, subprinter in self.lookup.items():
936            # Subprinters and names are used only for the rarely used command "info
937            # pretty" (and related), so the name of the first data structure it prints
938            # is a reasonable choice.
939            if subprinter not in self.subprinters:
940                subprinter.name = name
941                self.subprinters.append(subprinter)
942
943    def __call__(self, val):
944        """Return the pretty printer for a val, if the type is supported."""
945
946        # Do not handle any type that is not a struct/class.
947        if val.type.strip_typedefs().code != gdb.TYPE_CODE_STRUCT:
948            return None
949
950        # Don't attempt types known to be inside libstdcxx.
951        typename = val.type.name or val.type.tag or str(val.type)
952        match = re.match("^std::(__.*?)::", typename)
953        if match is not None and match.group(1) in [
954            "__cxx1998",
955            "__debug",
956            "__7",
957            "__g",
958        ]:
959            return None
960
961        # Handle any using declarations or other typedefs.
962        typename = _prettify_typename(val.type)
963        if not typename:
964            return None
965        without_generics = _remove_generics(typename)
966        lookup_name = _remove_std_prefix(without_generics)
967        if lookup_name in self.lookup:
968            return self.lookup[lookup_name](val)
969        return None
970
971
972_libcxx_printer_name = "libcxx_pretty_printer"
973
974
975# These are called for every binary object file, which could be thousands in
976# certain pathological cases. Limit our pretty printers to the progspace.
977def _register_libcxx_printers(event):
978    progspace = event.new_objfile.progspace
979    # It would be ideal to get the endianness at print time, but
980    # gdb.execute clears gdb's internal wrap buffer, removing any values
981    # already generated as part of a larger data structure, and there is
982    # no python api to get the endianness. Mixed-endianness debugging
983    # rare enough that this workaround should be adequate.
984    _libcpp_big_endian = "big endian" in gdb.execute("show endian", to_string=True)
985
986    if not getattr(progspace, _libcxx_printer_name, False):
987        print("Loading libc++ pretty-printers.")
988        gdb.printing.register_pretty_printer(
989            progspace, LibcxxPrettyPrinter(_libcxx_printer_name)
990        )
991        setattr(progspace, _libcxx_printer_name, True)
992
993
994def _unregister_libcxx_printers(event):
995    progspace = event.progspace
996    if getattr(progspace, _libcxx_printer_name, False):
997        for printer in progspace.pretty_printers:
998            if getattr(printer, "name", "none") == _libcxx_printer_name:
999                progspace.pretty_printers.remove(printer)
1000                setattr(progspace, _libcxx_printer_name, False)
1001                break
1002
1003
1004def register_libcxx_printer_loader():
1005    """Register event handlers to load libc++ pretty-printers."""
1006    gdb.events.new_objfile.connect(_register_libcxx_printers)
1007    gdb.events.clear_objfiles.connect(_unregister_libcxx_printers)
1008