diff options
author | 2021-01-02 20:29:13 +0000 | |
---|---|---|
committer | 2021-01-02 20:29:13 +0000 | |
commit | 46035553bfdd96e63c94e32da0210227ec2e3cf1 (patch) | |
tree | b191f708fb9a2995ba745b2f31cdeeaee4872b7f /gnu/llvm/libcxx/utils/gdb | |
parent | Move Makefiles for libc++ and libc++abi to gnu/lib in preparation for an (diff) | |
download | wireguard-openbsd-46035553bfdd96e63c94e32da0210227ec2e3cf1.tar.xz wireguard-openbsd-46035553bfdd96e63c94e32da0210227ec2e3cf1.zip |
Import libc++ 10.0.1 release.
Diffstat (limited to 'gnu/llvm/libcxx/utils/gdb')
-rw-r--r-- | gnu/llvm/libcxx/utils/gdb/libcxx/printers.py | 1008 |
1 files changed, 1008 insertions, 0 deletions
diff --git a/gnu/llvm/libcxx/utils/gdb/libcxx/printers.py b/gnu/llvm/libcxx/utils/gdb/libcxx/printers.py new file mode 100644 index 00000000000..b2d589424a3 --- /dev/null +++ b/gnu/llvm/libcxx/utils/gdb/libcxx/printers.py @@ -0,0 +1,1008 @@ +#===----------------------------------------------------------------------===## +# +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +#===----------------------------------------------------------------------===## +"""GDB pretty-printers for libc++. + +These should work for objects compiled when _LIBCPP_ABI_UNSTABLE is defined +and when it is undefined. +""" + +from __future__ import print_function + +import re +import gdb + +# One under-documented feature of the gdb pretty-printer API +# is that clients can call any other member of the API +# before they call to_string. +# Therefore all self.FIELDs must be set in the pretty-printer's +# __init__ function. + +_void_pointer_type = gdb.lookup_type("void").pointer() + + +_long_int_type = gdb.lookup_type("unsigned long long") + +_libcpp_big_endian = False + +def addr_as_long(addr): + return int(addr.cast(_long_int_type)) + + +# The size of a pointer in bytes. +_pointer_size = _void_pointer_type.sizeof + + +def _remove_cxx_namespace(typename): + """Removed libc++ specific namespace from the type. + + Arguments: + typename(string): A type, such as std::__u::something. + + Returns: + A string without the libc++ specific part, such as std::something. + """ + + return re.sub("std::__.*?::", "std::", typename) + + +def _remove_generics(typename): + """Remove generics part of the type. Assumes typename is not empty. + + Arguments: + typename(string): A type such as std::my_collection<element>. + + Returns: + The prefix up to the generic part, such as std::my_collection. + """ + + match = re.match("^([^<]+)", typename) + return match.group(1) + + +# Some common substitutions on the types to reduce visual clutter (A user who +# wants to see the actual details can always use print/r). +_common_substitutions = [ + ("std::basic_string<char, std::char_traits<char>, std::allocator<char> >", + "std::string"), +] + + +def _prettify_typename(gdb_type): + """Returns a pretty name for the type, or None if no name can be found. + + Arguments: + gdb_type(gdb.Type): A type object. + + Returns: + A string, without type_defs, libc++ namespaces, and common substitutions + applied. + """ + + type_without_typedefs = gdb_type.strip_typedefs() + typename = type_without_typedefs.name or type_without_typedefs.tag or \ + str(type_without_typedefs) + result = _remove_cxx_namespace(typename) + for find_str, subst_str in _common_substitutions: + result = re.sub(find_str, subst_str, result) + return result + + +def _typename_for_nth_generic_argument(gdb_type, n): + """Returns a pretty string for the nth argument of the given type. + + Arguments: + gdb_type(gdb.Type): A type object, such as the one for std::map<int, int> + n: The (zero indexed) index of the argument to return. + + Returns: + A string for the nth argument, such a "std::string" + """ + element_type = gdb_type.template_argument(n) + return _prettify_typename(element_type) + + +def _typename_with_n_generic_arguments(gdb_type, n): + """Return a string for the type with the first n (1, ...) generic args.""" + + base_type = _remove_generics(_prettify_typename(gdb_type)) + arg_list = [base_type] + template = "%s<" + for i in range(n): + arg_list.append(_typename_for_nth_generic_argument(gdb_type, i)) + template += "%s, " + result = (template[:-2] + ">") % tuple(arg_list) + return result + + +def _typename_with_first_generic_argument(gdb_type): + return _typename_with_n_generic_arguments(gdb_type, 1) + + +class StdTuplePrinter(object): + """Print a std::tuple.""" + + class _Children(object): + """Class to iterate over the tuple's children.""" + + def __init__(self, val): + self.val = val + self.child_iter = iter(self.val["__base_"].type.fields()) + self.count = 0 + + def __iter__(self): + return self + + def __next__(self): + # child_iter raises StopIteration when appropriate. + field_name = self.child_iter.next() + child = self.val["__base_"][field_name]["__value_"] + self.count += 1 + return ("[%d]" % self.count, child) + + # TODO Delete when we drop Python 2. + def next(self): + return self.__next__() + + def __init__(self, val): + self.val = val + + def to_string(self): + typename = _remove_generics(_prettify_typename(self.val.type)) + if not self.val.type.fields(): + return "empty %s" % typename + return "%s containing" % typename + + def children(self): + if not self.val.type.fields(): + return iter(()) + return self._Children(self.val) + + +def _get_base_subobject(child_class_value, index=0): + """Returns the object's value in the form of the parent class at index. + + This function effectively casts the child_class_value to the base_class's + type, but the type-to-cast to is stored in the field at index, and once + we know the field, we can just return the data. + + Args: + child_class_value: the value to cast + index: the parent class index + + Raises: + Exception: field at index was not a base-class field. + """ + + field = child_class_value.type.fields()[index] + if not field.is_base_class: + raise Exception("Not a base-class field.") + return child_class_value[field] + + +def _value_of_pair_first(value): + """Convenience for _get_base_subobject, for the common case.""" + return _get_base_subobject(value, 0)["__value_"] + + +class StdStringPrinter(object): + """Print a std::string.""" + + def _get_short_size(self, short_field, short_size): + """Short size depends on both endianness and a compile-time define.""" + + # If the padding field is present after all this indirection, then string + # was compiled with _LIBCPP_ABI_ALTERNATE_STRING_LAYOUT defined. + field = short_field.type.fields()[1].type.fields()[0] + libcpp_abi_alternate_string_layout = field.name and "__padding" in field.name + + # This logical structure closely follows the original code (which is clearer + # in C++). Keep them parallel to make them easier to compare. + if libcpp_abi_alternate_string_layout: + if _libcpp_big_endian: + return short_size >> 1 + else: + return short_size + elif _libcpp_big_endian: + return short_size + else: + return short_size >> 1 + + def __init__(self, val): + self.val = val + + def to_string(self): + """Build a python string from the data whether stored inline or separately.""" + + value_field = _value_of_pair_first(self.val["__r_"]) + short_field = value_field["__s"] + short_size = short_field["__size_"] + if short_size == 0: + return "" + short_mask = self.val["__short_mask"] + # Counter intuitive to compare the size and short_mask to see if the string + # is long, but that's the way the implementation does it. Note that + # __is_long() doesn't use get_short_size in C++. + is_long = short_size & short_mask + if is_long: + long_field = value_field["__l"] + data = long_field["__data_"] + size = long_field["__size_"] + else: + data = short_field["__data_"] + size = self._get_short_size(short_field, short_size) + if hasattr(data, "lazy_string"): + return data.lazy_string(length=size) + return data.string(length=size) + + def display_hint(self): + return "string" + + +class StdUniquePtrPrinter(object): + """Print a std::unique_ptr.""" + + def __init__(self, val): + self.val = val + self.addr = _value_of_pair_first(self.val["__ptr_"]) + self.pointee_type = self.val.type.template_argument(0) + + def to_string(self): + typename = _remove_generics(_prettify_typename(self.val.type)) + if not self.addr: + return "%s is nullptr" % typename + return ("%s<%s> containing" % + (typename, + _remove_generics(_prettify_typename(self.pointee_type)))) + + def __iter__(self): + if self.addr: + yield "__ptr_", self.addr.cast(self.pointee_type.pointer()) + + def children(self): + return self + + +class StdSharedPointerPrinter(object): + """Print a std::shared_ptr.""" + + def __init__(self, val): + self.val = val + self.addr = self.val["__ptr_"] + + def to_string(self): + """Returns self as a string.""" + typename = _remove_generics(_prettify_typename(self.val.type)) + pointee_type = _remove_generics( + _prettify_typename(self.val.type.template_argument(0))) + if not self.addr: + return "%s is nullptr" % typename + refcount = self.val["__cntrl_"] + if refcount != 0: + usecount = refcount["__shared_owners_"] + 1 + weakcount = refcount["__shared_weak_owners_"] + if usecount == 0: + state = "expired, weak %d" % weakcount + else: + state = "count %d, weak %d" % (usecount, weakcount) + return "%s<%s> %s containing" % (typename, pointee_type, state) + + def __iter__(self): + if self.addr: + yield "__ptr_", self.addr + + def children(self): + return self + + +class StdVectorPrinter(object): + """Print a std::vector.""" + + class _VectorBoolIterator(object): + """Class to iterate over the bool vector's children.""" + + def __init__(self, begin, size, bits_per_word): + self.item = begin + self.size = size + self.bits_per_word = bits_per_word + self.count = 0 + self.offset = 0 + + def __iter__(self): + return self + + def __next__(self): + """Retrieve the next element.""" + + self.count += 1 + if self.count > self.size: + raise StopIteration + entry = self.item.dereference() + if entry & (1 << self.offset): + outbit = 1 + else: + outbit = 0 + self.offset += 1 + if self.offset >= self.bits_per_word: + self.item += 1 + self.offset = 0 + return ("[%d]" % self.count, outbit) + + # TODO Delete when we drop Python 2. + def next(self): + return self.__next__() + + class _VectorIterator(object): + """Class to iterate over the non-bool vector's children.""" + + def __init__(self, begin, end): + self.item = begin + self.end = end + self.count = 0 + + def __iter__(self): + return self + + def __next__(self): + self.count += 1 + if self.item == self.end: + raise StopIteration + entry = self.item.dereference() + self.item += 1 + return ("[%d]" % self.count, entry) + + # TODO Delete when we drop Python 2. + def next(self): + return self.__next__() + + def __init__(self, val): + """Set val, length, capacity, and iterator for bool and normal vectors.""" + self.val = val + self.typename = _remove_generics(_prettify_typename(val.type)) + begin = self.val["__begin_"] + if self.val.type.template_argument(0).code == gdb.TYPE_CODE_BOOL: + self.typename += "<bool>" + self.length = self.val["__size_"] + bits_per_word = self.val["__bits_per_word"] + self.capacity = _value_of_pair_first( + self.val["__cap_alloc_"]) * bits_per_word + self.iterator = self._VectorBoolIterator( + begin, self.length, bits_per_word) + else: + end = self.val["__end_"] + self.length = end - begin + self.capacity = _get_base_subobject( + self.val["__end_cap_"])["__value_"] - begin + self.iterator = self._VectorIterator(begin, end) + + def to_string(self): + return ("%s of length %d, capacity %d" % + (self.typename, self.length, self.capacity)) + + def children(self): + return self.iterator + + def display_hint(self): + return "array" + + +class StdBitsetPrinter(object): + """Print a std::bitset.""" + + def __init__(self, val): + self.val = val + self.n_words = int(self.val["__n_words"]) + self.bits_per_word = int(self.val["__bits_per_word"]) + if self.n_words == 1: + self.values = [int(self.val["__first_"])] + else: + self.values = [int(self.val["__first_"][index]) + for index in range(self.n_words)] + + def to_string(self): + typename = _prettify_typename(self.val.type) + return "%s" % typename + + def _byte_it(self, value): + index = -1 + while value: + index += 1 + will_yield = value % 2 + value /= 2 + if will_yield: + yield index + + def _list_it(self): + for word_index in range(self.n_words): + current = self.values[word_index] + if current: + for n in self._byte_it(current): + yield ("[%d]" % (word_index * self.bits_per_word + n), 1) + + def __iter__(self): + return self._list_it() + + def children(self): + return self + + +class StdDequePrinter(object): + """Print a std::deque.""" + + def __init__(self, val): + self.val = val + self.size = int(_value_of_pair_first(val["__size_"])) + self.start_ptr = self.val["__map_"]["__begin_"] + self.first_block_start_index = int(self.val["__start_"]) + self.node_type = self.start_ptr.type + self.block_size = self._calculate_block_size( + val.type.template_argument(0)) + + def _calculate_block_size(self, element_type): + """Calculates the number of elements in a full block.""" + size = element_type.sizeof + # Copied from struct __deque_block_size implementation of libcxx. + return 4096 / size if size < 256 else 16 + + def _bucket_it(self, start_addr, start_index, end_index): + for i in range(start_index, end_index): + yield i, (start_addr.dereference() + i).dereference() + + def _list_it(self): + """Primary iteration worker.""" + num_emitted = 0 + current_addr = self.start_ptr + start_index = self.first_block_start_index + while num_emitted < self.size: + end_index = min(start_index + self.size - + num_emitted, self.block_size) + for _, elem in self._bucket_it(current_addr, start_index, end_index): + yield "", elem + num_emitted += end_index - start_index + current_addr = gdb.Value(addr_as_long(current_addr) + _pointer_size) \ + .cast(self.node_type) + start_index = 0 + + def to_string(self): + typename = _remove_generics(_prettify_typename(self.val.type)) + if self.size: + return "%s with %d elements" % (typename, self.size) + return "%s is empty" % typename + + def __iter__(self): + return self._list_it() + + def children(self): + return self + + def display_hint(self): + return "array" + + +class StdListPrinter(object): + """Print a std::list.""" + + def __init__(self, val): + self.val = val + size_alloc_field = self.val["__size_alloc_"] + self.size = int(_value_of_pair_first(size_alloc_field)) + dummy_node = self.val["__end_"] + self.nodetype = gdb.lookup_type( + re.sub("__list_node_base", "__list_node", + str(dummy_node.type.strip_typedefs()))).pointer() + self.first_node = dummy_node["__next_"] + + def to_string(self): + typename = _remove_generics(_prettify_typename(self.val.type)) + if self.size: + return "%s with %d elements" % (typename, self.size) + return "%s is empty" % typename + + def _list_iter(self): + current_node = self.first_node + for _ in range(self.size): + yield "", current_node.cast(self.nodetype).dereference()["__value_"] + current_node = current_node.dereference()["__next_"] + + def __iter__(self): + return self._list_iter() + + def children(self): + return self if self.nodetype else iter(()) + + def display_hint(self): + return "array" + + +class StdQueueOrStackPrinter(object): + """Print a std::queue or std::stack.""" + + def __init__(self, val): + self.val = val + self.underlying = val["c"] + + def to_string(self): + typename = _remove_generics(_prettify_typename(self.val.type)) + return "%s wrapping" % typename + + def children(self): + return iter([("", self.underlying)]) + + def display_hint(self): + return "array" + + +class StdPriorityQueuePrinter(object): + """Print a std::priority_queue.""" + + def __init__(self, val): + self.val = val + self.underlying = val["c"] + + def to_string(self): + # TODO(tamur): It would be nice to print the top element. The technical + # difficulty is that, the implementation refers to the underlying + # container, which is a generic class. libstdcxx pretty printers do not + # print the top element. + typename = _remove_generics(_prettify_typename(self.val.type)) + return "%s wrapping" % typename + + def children(self): + return iter([("", self.underlying)]) + + def display_hint(self): + return "array" + + +class RBTreeUtils(object): + """Utility class for std::(multi)map, and std::(multi)set and iterators.""" + + def __init__(self, cast_type, root): + self.cast_type = cast_type + self.root = root + + def left_child(self, node): + result = node.cast(self.cast_type).dereference()["__left_"] + return result + + def right_child(self, node): + result = node.cast(self.cast_type).dereference()["__right_"] + return result + + def parent(self, node): + """Return the parent of node, if it exists.""" + # If this is the root, then from the algorithm's point of view, it has no + # parent. + if node == self.root: + return None + + # We don't have enough information to tell if this is the end_node (which + # doesn't have a __parent_ field), or the root (which doesn't have a parent + # from the algorithm's point of view), so cast_type may not be correct for + # this particular node. Use heuristics. + + # The end_node's left child is the root. Note that when printing interators + # in isolation, the root is unknown. + if self.left_child(node) == self.root: + return None + + parent = node.cast(self.cast_type).dereference()["__parent_"] + # If the value at the offset of __parent_ doesn't look like a valid pointer, + # then assume that node is the end_node (and therefore has no parent). + # End_node type has a pointer embedded, so should have pointer alignment. + if addr_as_long(parent) % _void_pointer_type.alignof: + return None + # This is ugly, but the only other option is to dereference an invalid + # pointer. 0x8000 is fairly arbitrary, but has had good results in + # practice. If there was a way to tell if a pointer is invalid without + # actually dereferencing it and spewing error messages, that would be ideal. + if parent < 0x8000: + return None + return parent + + def is_left_child(self, node): + parent = self.parent(node) + return parent is not None and self.left_child(parent) == node + + def is_right_child(self, node): + parent = self.parent(node) + return parent is not None and self.right_child(parent) == node + + +class AbstractRBTreePrinter(object): + """Abstract super class for std::(multi)map, and std::(multi)set.""" + + def __init__(self, val): + self.val = val + tree = self.val["__tree_"] + self.size = int(_value_of_pair_first(tree["__pair3_"])) + dummy_root = tree["__pair1_"] + root = _value_of_pair_first(dummy_root)["__left_"] + cast_type = self._init_cast_type(val.type) + self.util = RBTreeUtils(cast_type, root) + + def _get_key_value(self, node): + """Subclasses should override to return a list of values to yield.""" + raise NotImplementedError + + def _traverse(self): + """Traverses the binary search tree in order.""" + current = self.util.root + skip_left_child = False + while True: + if not skip_left_child and self.util.left_child(current): + current = self.util.left_child(current) + continue + skip_left_child = False + for key_value in self._get_key_value(current): + yield "", key_value + right_child = self.util.right_child(current) + if right_child: + current = right_child + continue + while self.util.is_right_child(current): + current = self.util.parent(current) + if self.util.is_left_child(current): + current = self.util.parent(current) + skip_left_child = True + continue + break + + def __iter__(self): + return self._traverse() + + def children(self): + return self if self.util.cast_type and self.size > 0 else iter(()) + + def to_string(self): + typename = _remove_generics(_prettify_typename(self.val.type)) + if self.size: + return "%s with %d elements" % (typename, self.size) + return "%s is empty" % typename + + +class StdMapPrinter(AbstractRBTreePrinter): + """Print a std::map or std::multimap.""" + + def _init_cast_type(self, val_type): + map_it_type = gdb.lookup_type( + str(val_type) + "::iterator").strip_typedefs() + tree_it_type = map_it_type.template_argument(0) + node_ptr_type = tree_it_type.template_argument(1) + return node_ptr_type + + def display_hint(self): + return "map" + + def _get_key_value(self, node): + key_value = node.cast(self.util.cast_type).dereference()[ + "__value_"]["__cc"] + return [key_value["first"], key_value["second"]] + + +class StdSetPrinter(AbstractRBTreePrinter): + """Print a std::set.""" + + def _init_cast_type(self, val_type): + set_it_type = gdb.lookup_type( + str(val_type) + "::iterator").strip_typedefs() + node_ptr_type = set_it_type.template_argument(1) + return node_ptr_type + + def display_hint(self): + return "array" + + def _get_key_value(self, node): + key_value = node.cast(self.util.cast_type).dereference()["__value_"] + return [key_value] + + +class AbstractRBTreeIteratorPrinter(object): + """Abstract super class for std::(multi)map, and std::(multi)set iterator.""" + + def _initialize(self, val, typename): + self.typename = typename + self.val = val + self.addr = self.val["__ptr_"] + cast_type = self.val.type.template_argument(1) + self.util = RBTreeUtils(cast_type, None) + if self.addr: + self.node = self.addr.cast(cast_type).dereference() + + def _is_valid_node(self): + if not self.util.parent(self.addr): + return False + return self.util.is_left_child(self.addr) or \ + self.util.is_right_child(self.addr) + + def to_string(self): + if not self.addr: + return "%s is nullptr" % self.typename + return "%s " % self.typename + + def _get_node_value(self, node): + raise NotImplementedError + + def __iter__(self): + addr_str = "[%s]" % str(self.addr) + if not self._is_valid_node(): + yield addr_str, " end()" + else: + yield addr_str, self._get_node_value(self.node) + + def children(self): + return self if self.addr else iter(()) + + +class MapIteratorPrinter(AbstractRBTreeIteratorPrinter): + """Print a std::(multi)map iterator.""" + + def __init__(self, val): + self._initialize(val["__i_"], + _remove_generics(_prettify_typename(val.type))) + + def _get_node_value(self, node): + return node["__value_"]["__cc"] + + +class SetIteratorPrinter(AbstractRBTreeIteratorPrinter): + """Print a std::(multi)set iterator.""" + + def __init__(self, val): + self._initialize(val, _remove_generics(_prettify_typename(val.type))) + + def _get_node_value(self, node): + return node["__value_"] + + +class StdFposPrinter(object): + """Print a std::fpos or std::streampos.""" + + def __init__(self, val): + self.val = val + + def to_string(self): + typename = _remove_generics(_prettify_typename(self.val.type)) + offset = self.val["__off_"] + state = self.val["__st_"] + count = state["__count"] + value = state["__value"]["__wch"] + return "%s with stream offset:%s with state: {count:%s value:%s}" % ( + typename, offset, count, value) + + +class AbstractUnorderedCollectionPrinter(object): + """Abstract super class for std::unordered_(multi)[set|map].""" + + def __init__(self, val): + self.val = val + self.table = val["__table_"] + self.sentinel = self.table["__p1_"] + self.size = int(_value_of_pair_first(self.table["__p2_"])) + node_base_type = self.sentinel.type.template_argument(0) + self.cast_type = node_base_type.template_argument(0) + + def _list_it(self, sentinel_ptr): + next_ptr = _value_of_pair_first(sentinel_ptr)["__next_"] + while str(next_ptr.cast(_void_pointer_type)) != "0x0": + next_val = next_ptr.cast(self.cast_type).dereference() + for key_value in self._get_key_value(next_val): + yield "", key_value + next_ptr = next_val["__next_"] + + def to_string(self): + typename = _remove_generics(_prettify_typename(self.val.type)) + if self.size: + return "%s with %d elements" % (typename, self.size) + return "%s is empty" % typename + + def _get_key_value(self, node): + """Subclasses should override to return a list of values to yield.""" + raise NotImplementedError + + def children(self): + return self if self.cast_type and self.size > 0 else iter(()) + + def __iter__(self): + return self._list_it(self.sentinel) + + +class StdUnorderedSetPrinter(AbstractUnorderedCollectionPrinter): + """Print a std::unordered_(multi)set.""" + + def _get_key_value(self, node): + return [node["__value_"]] + + def display_hint(self): + return "array" + + +class StdUnorderedMapPrinter(AbstractUnorderedCollectionPrinter): + """Print a std::unordered_(multi)map.""" + + def _get_key_value(self, node): + key_value = node["__value_"]["__cc"] + return [key_value["first"], key_value["second"]] + + def display_hint(self): + return "map" + + +class AbstractHashMapIteratorPrinter(object): + """Abstract class for unordered collection iterators.""" + + def _initialize(self, val, addr): + self.val = val + self.typename = _remove_generics(_prettify_typename(self.val.type)) + self.addr = addr + if self.addr: + self.node = self.addr.cast(self.cast_type).dereference() + + def _get_key_value(self): + """Subclasses should override to return a list of values to yield.""" + raise NotImplementedError + + def to_string(self): + if not self.addr: + return "%s = end()" % self.typename + return "%s " % self.typename + + def children(self): + return self if self.addr else iter(()) + + def __iter__(self): + for key_value in self._get_key_value(): + yield "", key_value + + +class StdUnorderedSetIteratorPrinter(AbstractHashMapIteratorPrinter): + """Print a std::(multi)set iterator.""" + + def __init__(self, val): + self.cast_type = val.type.template_argument(0) + self._initialize(val, val["__node_"]) + + def _get_key_value(self): + return [self.node["__value_"]] + + def display_hint(self): + return "array" + + +class StdUnorderedMapIteratorPrinter(AbstractHashMapIteratorPrinter): + """Print a std::(multi)map iterator.""" + + def __init__(self, val): + self.cast_type = val.type.template_argument(0).template_argument(0) + self._initialize(val, val["__i_"]["__node_"]) + + def _get_key_value(self): + key_value = self.node["__value_"]["__cc"] + return [key_value["first"], key_value["second"]] + + def display_hint(self): + return "map" + + +def _remove_std_prefix(typename): + match = re.match("^std::(.+)", typename) + return match.group(1) if match is not None else "" + + +class LibcxxPrettyPrinter(object): + """PrettyPrinter object so gdb-commands like 'info pretty-printers' work.""" + + def __init__(self, name): + super(LibcxxPrettyPrinter, self).__init__() + self.name = name + self.enabled = True + + self.lookup = { + "basic_string": StdStringPrinter, + "string": StdStringPrinter, + "tuple": StdTuplePrinter, + "unique_ptr": StdUniquePtrPrinter, + "shared_ptr": StdSharedPointerPrinter, + "weak_ptr": StdSharedPointerPrinter, + "bitset": StdBitsetPrinter, + "deque": StdDequePrinter, + "list": StdListPrinter, + "queue": StdQueueOrStackPrinter, + "stack": StdQueueOrStackPrinter, + "priority_queue": StdPriorityQueuePrinter, + "map": StdMapPrinter, + "multimap": StdMapPrinter, + "set": StdSetPrinter, + "multiset": StdSetPrinter, + "vector": StdVectorPrinter, + "__map_iterator": MapIteratorPrinter, + "__map_const_iterator": MapIteratorPrinter, + "__tree_iterator": SetIteratorPrinter, + "__tree_const_iterator": SetIteratorPrinter, + "fpos": StdFposPrinter, + "unordered_set": StdUnorderedSetPrinter, + "unordered_multiset": StdUnorderedSetPrinter, + "unordered_map": StdUnorderedMapPrinter, + "unordered_multimap": StdUnorderedMapPrinter, + "__hash_map_iterator": StdUnorderedMapIteratorPrinter, + "__hash_map_const_iterator": StdUnorderedMapIteratorPrinter, + "__hash_iterator": StdUnorderedSetIteratorPrinter, + "__hash_const_iterator": StdUnorderedSetIteratorPrinter, + } + + self.subprinters = [] + for name, subprinter in self.lookup.items(): + # Subprinters and names are used only for the rarely used command "info + # pretty" (and related), so the name of the first data structure it prints + # is a reasonable choice. + if subprinter not in self.subprinters: + subprinter.name = name + self.subprinters.append(subprinter) + + def __call__(self, val): + """Return the pretty printer for a val, if the type is supported.""" + + # Do not handle any type that is not a struct/class. + if val.type.strip_typedefs().code != gdb.TYPE_CODE_STRUCT: + return None + + # Don't attempt types known to be inside libstdcxx. + typename = val.type.name or val.type.tag or str(val.type) + match = re.match("^std::(__.*?)::", typename) + if match is None or match.group(1) in ["__cxx1998", + "__debug", + "__7", + "__g"]: + return None + + # Handle any using declarations or other typedefs. + typename = _prettify_typename(val.type) + if not typename: + return None + without_generics = _remove_generics(typename) + lookup_name = _remove_std_prefix(without_generics) + if lookup_name in self.lookup: + return self.lookup[lookup_name](val) + return None + + +_libcxx_printer_name = "libcxx_pretty_printer" + + +# These are called for every binary object file, which could be thousands in +# certain pathological cases. Limit our pretty printers to the progspace. +def _register_libcxx_printers(event): + progspace = event.new_objfile.progspace + # It would be ideal to get the endianness at print time, but + # gdb.execute clears gdb's internal wrap buffer, removing any values + # already generated as part of a larger data structure, and there is + # no python api to get the endianness. Mixed-endianness debugging + # rare enough that this workaround should be adequate. + _libcpp_big_endian = "big endian" in gdb.execute("show endian", + to_string=True) + + if not getattr(progspace, _libcxx_printer_name, False): + print("Loading libc++ pretty-printers.") + gdb.printing.register_pretty_printer( + progspace, LibcxxPrettyPrinter(_libcxx_printer_name)) + setattr(progspace, _libcxx_printer_name, True) + + +def _unregister_libcxx_printers(event): + progspace = event.progspace + if getattr(progspace, _libcxx_printer_name, False): + for printer in progspace.pretty_printers: + if getattr(printer, "name", "none") == _libcxx_printer_name: + progspace.pretty_printers.remove(printer) + setattr(progspace, _libcxx_printer_name, False) + break + + +def register_libcxx_printer_loader(): + """Register event handlers to load libc++ pretty-printers.""" + gdb.events.new_objfile.connect(_register_libcxx_printers) + gdb.events.clear_objfiles.connect(_unregister_libcxx_printers) |