libstdc++-v3/python/libstdcxx/v6/__init__.py | 4 +- libstdc++-v3/python/libstdcxx/v6/printers.py | 2593 +++++++++++++++++++------- libstdc++-v3/python/libstdcxx/v6/xmethods.py | 170 +- 3 files changed, 2017 insertions(+), 750 deletions(-) diff --git a/libstdc++-v3/python/libstdcxx/v6/__init__.py b/libstdc++-v3/python/libstdcxx/v6/__init__.py index b001c8520b6..f40acd922af 100644 --- a/libstdc++-v3/python/libstdcxx/v6/__init__.py +++ b/libstdc++-v3/python/libstdcxx/v6/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2014-2018 Free Software Foundation, Inc. +# Copyright (C) 2014-2024 Free Software Foundation, Inc. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -13,8 +13,6 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import gdb - # Load the xmethods if GDB supports them. def gdb_has_xmethods(): try: diff --git a/libstdc++-v3/python/libstdcxx/v6/printers.py b/libstdc++-v3/python/libstdcxx/v6/printers.py index ba9bbc096a0..3026de35bbd 100644 --- a/libstdc++-v3/python/libstdcxx/v6/printers.py +++ b/libstdc++-v3/python/libstdcxx/v6/printers.py @@ -1,6 +1,6 @@ # Pretty-printers for libstdc++. -# Copyright (C) 2008-2018 Free Software Foundation, Inc. +# Copyright (C) 2008-2024 Free Software Foundation, Inc. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -19,8 +19,10 @@ import gdb import itertools import re import sys +import errno +import datetime -### Python 2 + Python 3 compatibility code +# Python 2 + Python 3 compatibility code # Resources about compatibility: # @@ -37,15 +39,16 @@ import sys # if sys.version_info[0] > 2: - ### Python 3 stuff + # Python 3 stuff Iterator = object # Python 3 folds these into the normal functions. imap = map izip = zip # Also, int subsumes long long = int + _utc_timezone = datetime.timezone.utc else: - ### Python 2 stuff + # Python 2 stuff class Iterator: """Compatibility mixin for iterators @@ -63,6 +66,20 @@ else: # In Python 2, we still need these from itertools from itertools import imap, izip + # Python 2 does not provide the datetime.UTC singleton. + class UTC(datetime.tzinfo): + """Concrete tzinfo class representing the UTC time zone.""" + + def utcoffset(self, dt): + return datetime.timedelta(0) + + def tzname(self, dt): + return "UTC" + + def dst(self, dt): + return datetime.timedelta(0) + _utc_timezone = UTC() + # Try to use the new-style pretty-printing if available. _use_gdb_pp = True try: @@ -79,14 +96,22 @@ try: except ImportError: pass +# Use the base class if available. +if hasattr(gdb, 'ValuePrinter'): + printer_base = gdb.ValuePrinter +else: + printer_base = object + # Starting with the type ORIG, search for the member type NAME. This # handles searching upward through superclasses. This is needed to # work around http://sourceware.org/bugzilla/show_bug.cgi?id=13615. + + def find_type(orig, name): typ = orig.strip_typedefs() while True: - # Strip cv-qualifiers. PR 67440. - search = '%s::%s' % (typ.unqualified(), name) + # Use Type.tag to ignore cv-qualifiers. PR 67440. + search = '%s::%s' % (typ.tag, name) try: return gdb.lookup_type(search) except RuntimeError: @@ -94,39 +119,131 @@ def find_type(orig, name): # The type was not found, so try the superclass. We only need # to check the first superclass, so we don't bother with # anything fancier here. - field = typ.fields()[0] - if not field.is_base_class: + fields = typ.fields() + if len(fields) and fields[0].is_base_class: + typ = fields[0].type + else: raise ValueError("Cannot find type %s::%s" % (str(orig), name)) - typ = field.type + _versioned_namespace = '__8::' -def is_specialization_of(type, template_name): - "Test if a type is a given template instantiation." + +def lookup_templ_spec(templ, *args): + """ + Lookup template specialization templ. + """ + t = '{}<{}>'.format(templ, ', '.join([str(a) for a in args])) + try: + return gdb.lookup_type(t) + except gdb.error as e: + # Type not found, try again in versioned namespace. + global _versioned_namespace + if _versioned_namespace not in templ: + t = t.replace('::', '::' + _versioned_namespace, 1) + try: + return gdb.lookup_type(t) + except gdb.error: + # If that also fails, rethrow the original exception + pass + raise e + +# Use this to find container node types instead of find_type, +# see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91997 for details. +def lookup_node_type(nodename, containertype): + """ + Lookup specialization of template nodename corresponding to containertype. + + nodename - The name of a class template, as a String + containertype - The container, as a gdb.Type + + Return a gdb.Type for the corresponding specialization of nodename, + or None if the type cannot be found. + + e.g. lookup_node_type('_List_node', gdb.lookup_type('std::list')) + will return a gdb.Type for the type std::_List_node. + """ + # If nodename is unqualified, assume it's in namespace std. + if '::' not in nodename: + nodename = 'std::' + nodename + # Use either containertype's value_type or its first template argument. + try: + valtype = find_type(containertype, 'value_type') + except: + valtype = containertype.template_argument(0) + valtype = valtype.strip_typedefs() + try: + return lookup_templ_spec(nodename, valtype) + except gdb.error: + # For debug mode containers the node is in std::__cxx1998. + if is_member_of_namespace(nodename, 'std'): + if is_member_of_namespace(containertype, 'std::__cxx1998', + 'std::__debug', '__gnu_debug'): + nodename = nodename.replace('::', '::__cxx1998::', 1) + try: + return lookup_templ_spec(nodename, valtype) + except gdb.error: + pass + return None + + +def is_member_of_namespace(typ, *namespaces): + """ + Test whether a type is a member of one of the specified namespaces. + The type can be specified as a string or a gdb.Type object. + """ + if isinstance(typ, gdb.Type): + typ = str(typ) + typ = strip_versioned_namespace(typ) + for namespace in namespaces: + if typ.startswith(namespace + '::'): + return True + return False + + +def is_specialization_of(x, template_name): + """ + Test whether a type is a specialization of the named class template. + The type can be specified as a string or a gdb.Type object. + The template should be the name of a class template as a string, + without any 'std' qualification. + """ global _versioned_namespace - if _versioned_namespace: - return re.match('^std::(%s)?%s<.*>$' % (_versioned_namespace, template_name), type) is not None - return re.match('^std::%s<.*>$' % template_name, type) is not None + if isinstance(x, gdb.Type): + x = x.tag + template_name = '(%s)?%s' % (_versioned_namespace, template_name) + return re.match('^std::%s<.*>$' % template_name, x) is not None + def strip_versioned_namespace(typename): global _versioned_namespace - if _versioned_namespace: - return typename.replace(_versioned_namespace, '') - return typename + return typename.replace(_versioned_namespace, '') + + +def strip_fundts_namespace(typ): + """Remove "fundamentals_vN" inline namespace from qualified type name.""" + pattern = r'^std::experimental::fundamentals_v\d::' + repl = 'std::experimental::' + if sys.version_info[0] == 2: + return re.sub(pattern, repl, typ, 1) + else: # Technically this needs Python 3.1 but nobody should be using 3.0 + return re.sub(pattern, repl, typ, count=1) + def strip_inline_namespaces(type_str): - "Remove known inline namespaces from the canonical name of a type." + """Remove known inline namespaces from the canonical name of a type.""" type_str = strip_versioned_namespace(type_str) type_str = type_str.replace('std::__cxx11::', 'std::') expt_ns = 'std::experimental::' for lfts_ns in ('fundamentals_v1', 'fundamentals_v2'): - type_str = type_str.replace(expt_ns+lfts_ns+'::', expt_ns) + type_str = type_str.replace(expt_ns + lfts_ns + '::', expt_ns) fs_ns = expt_ns + 'filesystem::' - type_str = type_str.replace(fs_ns+'v1::', fs_ns) + type_str = type_str.replace(fs_ns + 'v1::', fs_ns) return type_str + def get_template_arg_list(type_obj): - "Return a type's template arguments as a list" + """Return a type's template arguments as a list.""" n = 0 template_args = [] while True: @@ -136,78 +253,140 @@ def get_template_arg_list(type_obj): return template_args n += 1 + class SmartPtrIterator(Iterator): - "An iterator for smart pointer types with a single 'child' value" + """An iterator for smart pointer types with a single 'child' value.""" def __init__(self, val): - self.val = val + self._val = val def __iter__(self): return self def __next__(self): - if self.val is None: + if self._val is None: raise StopIteration - self.val, val = None, self.val + self._val, val = None, self._val return ('get()', val) -class SharedPointerPrinter: - "Print a shared_ptr or weak_ptr" - def __init__ (self, typename, val): - self.typename = strip_versioned_namespace(typename) - self.val = val - self.pointer = val['_M_ptr'] +class SharedPointerPrinter(printer_base): + """ + Print a shared_ptr, weak_ptr, atomic, or atomic. + """ + + def __init__(self, typename, val): + self._typename = strip_versioned_namespace(typename) + self._val = val + self._pointer = val['_M_ptr'] - def children (self): - return SmartPtrIterator(self.pointer) + def children(self): + return SmartPtrIterator(self._pointer) + + # Return the _Sp_counted_base<>* that holds the refcounts. + def _get_refcounts(self): + if self._typename == 'std::atomic': + # A tagged pointer is stored as uintptr_t. + ptr_val = self._val['_M_refcount']['_M_val']['_M_i'] + ptr_val = ptr_val - (ptr_val % 2) # clear lock bit + ptr_type = find_type(self._val['_M_refcount'].type, 'pointer') + return ptr_val.cast(ptr_type) + return self._val['_M_refcount']['_M_pi'] - def to_string (self): + def to_string(self): state = 'empty' - refcounts = self.val['_M_refcount']['_M_pi'] + refcounts = self._get_refcounts() + targ = self._val.type.template_argument(0) + targ = strip_versioned_namespace(str(targ)) + if refcounts != 0: usecount = refcounts['_M_use_count'] weakcount = refcounts['_M_weak_count'] if usecount == 0: state = 'expired, weak count %d' % weakcount else: - state = 'use count %d, weak count %d' % (usecount, weakcount - 1) - return '%s<%s> (%s)' % (self.typename, str(self.val.type.template_argument(0)), state) - -class UniquePointerPrinter: - "Print a unique_ptr" - - def __init__ (self, typename, val): - self.val = val - impl_type = val.type.fields()[0].type.strip_typedefs() - if is_specialization_of(str(impl_type), '__uniq_ptr_impl'): # New implementation - tuple_member = val['_M_t']['_M_t'] - elif is_specialization_of(str(impl_type), 'tuple'): - tuple_member = val['_M_t'] - else: - raise ValueError("Unsupported implementation for unique_ptr: %s" % str(impl_type)) - tuple_impl_type = tuple_member.type.fields()[0].type # _Tuple_impl - tuple_head_type = tuple_impl_type.fields()[1].type # _Head_base - head_field = tuple_head_type.fields()[0] - if head_field.name == '_M_head_impl': - self.pointer = tuple_member['_M_head_impl'] - elif head_field.is_base_class: - self.pointer = tuple_member.cast(head_field.type) - else: - raise ValueError("Unsupported implementation for tuple in unique_ptr: %s" % str(impl_type)) + state = 'use count %d, weak count %d' % ( + usecount, weakcount - 1) + return '%s<%s> (%s)' % (self._typename, targ, state) + + +def _tuple_impl_get(val): + """Return the tuple element stored in a _Tuple_impl base class.""" + bases = val.type.fields() + if not bases[-1].is_base_class: + raise ValueError( + "Unsupported implementation for std::tuple: %s" % str(val.type)) + # Get the _Head_base base class: + head_base = val.cast(bases[-1].type) + fields = head_base.type.fields() + if len(fields) == 0: + raise ValueError( + "Unsupported implementation for std::tuple: %s" % str(val.type)) + if fields[0].name == '_M_head_impl': + # The tuple element is the _Head_base::_M_head_impl data member. + return head_base['_M_head_impl'] + elif fields[0].is_base_class: + # The tuple element is an empty base class of _Head_base. + # Cast to that empty base class. + return head_base.cast(fields[0].type) + else: + raise ValueError( + "Unsupported implementation for std::tuple: %s" % str(val.type)) + + +def tuple_get(n, val): + """Return the result of std::get(val) on a std::tuple.""" + tuple_size = len(get_template_arg_list(val.type)) + if n > tuple_size: + raise ValueError("Out of range index for std::get on std::tuple") + # Get the first _Tuple_impl<0, T...> base class: + node = val.cast(val.type.fields()[0].type) + while n > 0: + # Descend through the base classes until the Nth one. + node = node.cast(node.type.fields()[0].type) + n -= 1 + return _tuple_impl_get(node) + + +def unique_ptr_get(val): + """Return the result of val.get() on a std::unique_ptr.""" + # std::unique_ptr contains a std::tuple, + # either as a direct data member _M_t (the old implementation) + # or within a data member of type __uniq_ptr_data. + impl_type = val.type.fields()[0].type.strip_typedefs() + # Check for new implementations first: + if is_specialization_of(impl_type, '__uniq_ptr_data') \ + or is_specialization_of(impl_type, '__uniq_ptr_impl'): + tuple_member = val['_M_t']['_M_t'] + elif is_specialization_of(impl_type, 'tuple'): + tuple_member = val['_M_t'] + else: + raise ValueError( + "Unsupported implementation for unique_ptr: %s" % str(impl_type)) + return tuple_get(0, tuple_member) + - def children (self): - return SmartPtrIterator(self.pointer) +class UniquePointerPrinter(printer_base): + """Print a unique_ptr.""" + + def __init__(self, typename, val): + self._val = val + + def children(self): + return SmartPtrIterator(unique_ptr_get(self._val)) + + def to_string(self): + t = self._val.type.template_argument(0) + return 'std::unique_ptr<{}>'.format(str(t)) - def to_string (self): - return ('std::unique_ptr<%s>' % (str(self.val.type.template_argument(0)))) def get_value_from_aligned_membuf(buf, valtype): - """Returns the value held in a __gnu_cxx::__aligned_membuf.""" + """Return the value held in a __gnu_cxx::__aligned_membuf.""" return buf['_M_storage'].address.cast(valtype.pointer()).dereference() + def get_value_from_list_node(node): - """Returns the value held in an _List_node<_Val>""" + """Return the value held in an _List_node<_Val>.""" try: member = node.type.fields()[1].name if member == '_M_data': @@ -221,240 +400,281 @@ def get_value_from_list_node(node): pass raise ValueError("Unsupported implementation for %s" % str(node.type)) -class StdListPrinter: - "Print a std::list" + +class StdListPrinter(printer_base): + """Print a std::list.""" class _iterator(Iterator): def __init__(self, nodetype, head): - self.nodetype = nodetype - self.base = head['_M_next'] - self.head = head.address - self.count = 0 + self._nodetype = nodetype + self._base = head['_M_next'] + self._head = head.address + self._count = 0 def __iter__(self): return self def __next__(self): - if self.base == self.head: + if self._base == self._head: raise StopIteration - elt = self.base.cast(self.nodetype).dereference() - self.base = elt['_M_next'] - count = self.count - self.count = self.count + 1 + elt = self._base.cast(self._nodetype).dereference() + self._base = elt['_M_next'] + count = self._count + self._count = self._count + 1 val = get_value_from_list_node(elt) return ('[%d]' % count, val) def __init__(self, typename, val): - self.typename = strip_versioned_namespace(typename) - self.val = val + self._typename = strip_versioned_namespace(typename) + self._val = val def children(self): - nodetype = find_type(self.val.type, '_Node') - nodetype = nodetype.strip_typedefs().pointer() - return self._iterator(nodetype, self.val['_M_impl']['_M_node']) + nodetype = lookup_node_type('_List_node', self._val.type).pointer() + return self._iterator(nodetype, self._val['_M_impl']['_M_node']) def to_string(self): - if self.val['_M_impl']['_M_node'].address == self.val['_M_impl']['_M_node']['_M_next']: - return 'empty %s' % (self.typename) - return '%s' % (self.typename) + headnode = self._val['_M_impl']['_M_node'] + if headnode['_M_next'] == headnode.address: + return 'empty %s' % (self._typename) + return '%s' % (self._typename) -class NodeIteratorPrinter: - def __init__(self, typename, val, contname): - self.val = val - self.typename = typename - self.contname = contname + +class NodeIteratorPrinter(printer_base): + def __init__(self, typename, val, contname, nodename): + self._val = val + self._typename = typename + self._contname = contname + self._nodetype = lookup_node_type(nodename, val.type) def to_string(self): - if not self.val['_M_node']: - return 'non-dereferenceable iterator for std::%s' % (self.contname) - nodetype = find_type(self.val.type, '_Node') - nodetype = nodetype.strip_typedefs().pointer() - node = self.val['_M_node'].cast(nodetype).dereference() + if not self._val['_M_node']: + return 'non-dereferenceable iterator for std::%s' % (self._contname) + node = self._val['_M_node'].cast( + self._nodetype.pointer()).dereference() return str(get_value_from_list_node(node)) + class StdListIteratorPrinter(NodeIteratorPrinter): - "Print std::list::iterator" + """Print std::list::iterator.""" def __init__(self, typename, val): - NodeIteratorPrinter.__init__(self, typename, val, 'list') + NodeIteratorPrinter.__init__(self, typename, val, 'list', '_List_node') + class StdFwdListIteratorPrinter(NodeIteratorPrinter): - "Print std::forward_list::iterator" + """Print std::forward_list::iterator.""" def __init__(self, typename, val): - NodeIteratorPrinter.__init__(self, typename, val, 'forward_list') + NodeIteratorPrinter.__init__(self, typename, val, 'forward_list', + '_Fwd_list_node') -class StdSlistPrinter: - "Print a __gnu_cxx::slist" + +class StdSlistPrinter(printer_base): + """Print a __gnu_cxx::slist.""" class _iterator(Iterator): def __init__(self, nodetype, head): - self.nodetype = nodetype - self.base = head['_M_head']['_M_next'] - self.count = 0 + self._nodetype = nodetype + self._base = head['_M_head']['_M_next'] + self._count = 0 def __iter__(self): return self def __next__(self): - if self.base == 0: + if self._base == 0: raise StopIteration - elt = self.base.cast(self.nodetype).dereference() - self.base = elt['_M_next'] - count = self.count - self.count = self.count + 1 + elt = self._base.cast(self._nodetype).dereference() + self._base = elt['_M_next'] + count = self._count + self._count = self._count + 1 return ('[%d]' % count, elt['_M_data']) def __init__(self, typename, val): - self.val = val + self._val = val def children(self): - nodetype = find_type(self.val.type, '_Node') - nodetype = nodetype.strip_typedefs().pointer() - return self._iterator(nodetype, self.val) + nodetype = lookup_node_type('__gnu_cxx::_Slist_node', self._val.type) + return self._iterator(nodetype.pointer(), self._val) def to_string(self): - if self.val['_M_head']['_M_next'] == 0: + if self._val['_M_head']['_M_next'] == 0: return 'empty __gnu_cxx::slist' return '__gnu_cxx::slist' -class StdSlistIteratorPrinter: - "Print __gnu_cxx::slist::iterator" + +class StdSlistIteratorPrinter(printer_base): + """Print __gnu_cxx::slist::iterator.""" def __init__(self, typename, val): - self.val = val + self._val = val def to_string(self): - if not self.val['_M_node']: + if not self._val['_M_node']: return 'non-dereferenceable iterator for __gnu_cxx::slist' - nodetype = find_type(self.val.type, '_Node') - nodetype = nodetype.strip_typedefs().pointer() - return str(self.val['_M_node'].cast(nodetype).dereference()['_M_data']) + nodetype = lookup_node_type( + '__gnu_cxx::_Slist_node', self._val.type).pointer() + return str(self._val['_M_node'].cast(nodetype).dereference()['_M_data']) + -class StdVectorPrinter: - "Print a std::vector" +class StdVectorPrinter(printer_base): + """Print a std::vector.""" class _iterator(Iterator): - def __init__ (self, start, finish, bitvec): - self.bitvec = bitvec + def __init__(self, start, finish, bitvec): + self._bitvec = bitvec if bitvec: - self.item = start['_M_p'] - self.so = start['_M_offset'] - self.finish = finish['_M_p'] - self.fo = finish['_M_offset'] - itype = self.item.dereference().type - self.isize = 8 * itype.sizeof + self._item = start['_M_p'] + self._so = 0 + self._finish = finish['_M_p'] + self._fo = finish['_M_offset'] + itype = self._item.dereference().type + self._isize = 8 * itype.sizeof else: - self.item = start - self.finish = finish - self.count = 0 + self._item = start + self._finish = finish + self._count = 0 def __iter__(self): return self def __next__(self): - count = self.count - self.count = self.count + 1 - if self.bitvec: - if self.item == self.finish and self.so >= self.fo: + count = self._count + self._count = self._count + 1 + if self._bitvec: + if self._item == self._finish and self._so >= self._fo: raise StopIteration - elt = self.item.dereference() - if elt & (1 << self.so): - obit = 1 - else: - obit = 0 - self.so = self.so + 1 - if self.so >= self.isize: - self.item = self.item + 1 - self.so = 0 - return ('[%d]' % count, obit) + elt = bool(self._item.dereference() & (1 << self._so)) + self._so = self._so + 1 + if self._so >= self._isize: + self._item = self._item + 1 + self._so = 0 + return ('[%d]' % count, elt) else: - if self.item == self.finish: + if self._item == self._finish: raise StopIteration - elt = self.item.dereference() - self.item = self.item + 1 + elt = self._item.dereference() + self._item = self._item + 1 return ('[%d]' % count, elt) def __init__(self, typename, val): - self.typename = strip_versioned_namespace(typename) - self.val = val - self.is_bool = val.type.template_argument(0).code == gdb.TYPE_CODE_BOOL + self._typename = strip_versioned_namespace(typename) + self._val = val + self._is_bool = val.type.template_argument( + 0).code == gdb.TYPE_CODE_BOOL def children(self): - return self._iterator(self.val['_M_impl']['_M_start'], - self.val['_M_impl']['_M_finish'], - self.is_bool) + return self._iterator(self._val['_M_impl']['_M_start'], + self._val['_M_impl']['_M_finish'], + self._is_bool) def to_string(self): - start = self.val['_M_impl']['_M_start'] - finish = self.val['_M_impl']['_M_finish'] - end = self.val['_M_impl']['_M_end_of_storage'] - if self.is_bool: - start = self.val['_M_impl']['_M_start']['_M_p'] - so = self.val['_M_impl']['_M_start']['_M_offset'] - finish = self.val['_M_impl']['_M_finish']['_M_p'] - fo = self.val['_M_impl']['_M_finish']['_M_offset'] + start = self._val['_M_impl']['_M_start'] + finish = self._val['_M_impl']['_M_finish'] + end = self._val['_M_impl']['_M_end_of_storage'] + if self._is_bool: + start = self._val['_M_impl']['_M_start']['_M_p'] + finish = self._val['_M_impl']['_M_finish']['_M_p'] + fo = self._val['_M_impl']['_M_finish']['_M_offset'] itype = start.dereference().type bl = 8 * itype.sizeof - length = (bl - so) + bl * ((finish - start) - 1) + fo + length = bl * (finish - start) + fo capacity = bl * (end - start) return ('%s of length %d, capacity %d' - % (self.typename, int (length), int (capacity))) + % (self._typename, int(length), int(capacity))) else: return ('%s of length %d, capacity %d' - % (self.typename, int (finish - start), int (end - start))) + % (self._typename, int(finish - start), int(end - start))) def display_hint(self): return 'array' -class StdVectorIteratorPrinter: - "Print std::vector::iterator" + +class StdVectorIteratorPrinter(printer_base): + """Print std::vector::iterator.""" def __init__(self, typename, val): - self.val = val + self._val = val def to_string(self): - if not self.val['_M_current']: + if not self._val['_M_current']: return 'non-dereferenceable iterator for std::vector' - return str(self.val['_M_current'].dereference()) + return str(self._val['_M_current'].dereference()) + + +class StdBitIteratorPrinter(printer_base): + """Print std::vector's _Bit_iterator and _Bit_const_iterator.""" + + def __init__(self, typename, val): + self._val = val + + def to_string(self): + if not self._val['_M_p']: + return 'non-dereferenceable iterator for std::vector' + return bool(self._val['_M_p'].dereference() + & (1 << self._val['_M_offset'])) -class StdTuplePrinter: - "Print a std::tuple" + +class StdBitReferencePrinter(printer_base): + """Print std::vector::reference.""" + + def __init__(self, typename, val): + self._val = val + + def to_string(self): + if not self._val['_M_p']: + return 'invalid std::vector::reference' + return bool(self._val['_M_p'].dereference() & (self._val['_M_mask'])) + + +class StdTuplePrinter(printer_base): + """Print a std::tuple.""" class _iterator(Iterator): - def __init__ (self, head): - self.head = head + @staticmethod + def _is_nonempty_tuple(nodes): + if len(nodes) == 2: + if is_specialization_of(nodes[1].type, '__tuple_base'): + return True + elif len(nodes) == 1: + return True + elif len(nodes) == 0: + return False + raise ValueError( + "Top of tuple tree does not consist of a single node.") + + def __init__(self, head): + self._head = head # Set the base class as the initial head of the # tuple. - nodes = self.head.type.fields () - if len (nodes) == 1: + nodes = self._head.type.fields() + if self._is_nonempty_tuple(nodes): # Set the actual head to the first pair. - self.head = self.head.cast (nodes[0].type) - elif len (nodes) != 0: - raise ValueError("Top of tuple tree does not consist of a single node.") - self.count = 0 + self._head = self._head.cast(nodes[0].type) + self._count = 0 - def __iter__ (self): + def __iter__(self): return self - def __next__ (self): + def __next__(self): # Check for further recursions in the inheritance tree. - # For a GCC 5+ tuple self.head is None after visiting all nodes: - if not self.head: + # For a GCC 5+ tuple self._head is None after visiting all nodes: + if not self._head: raise StopIteration - nodes = self.head.type.fields () + nodes = self._head.type.fields() # For a GCC 4.x tuple there is a final node with no fields: - if len (nodes) == 0: + if len(nodes) == 0: raise StopIteration # Check that this iteration has an expected structure. - if len (nodes) > 2: - raise ValueError("Cannot parse more than 2 nodes in a tuple tree.") + if len(nodes) > 2: + raise ValueError( + "Cannot parse more than 2 nodes in a tuple tree.") - if len (nodes) == 1: + if len(nodes) == 1: # This is the last node of a GCC 5+ std::tuple. - impl = self.head.cast (nodes[0].type) - self.head = None + impl = self._head.cast(nodes[0].type) + self._head = None else: # Either a node before the last node, or the last node of # a GCC 4.x tuple (which has an empty parent). @@ -463,53 +683,55 @@ class StdTuplePrinter: # - Right node is the actual class contained in the tuple. # Process right node. - impl = self.head.cast (nodes[1].type) + impl = self._head.cast(nodes[1].type) # Process left node and set it as head. - self.head = self.head.cast (nodes[0].type) + self._head = self._head.cast(nodes[0].type) - self.count = self.count + 1 + self._count = self._count + 1 # Finally, check the implementation. If it is # wrapped in _M_head_impl return that, otherwise return # the value "as is". - fields = impl.type.fields () - if len (fields) < 1 or fields[0].name != "_M_head_impl": - return ('[%d]' % self.count, impl) + fields = impl.type.fields() + if len(fields) < 1 or fields[0].name != "_M_head_impl": + return ('[%d]' % (self._count - 1), impl) else: - return ('[%d]' % self.count, impl['_M_head_impl']) + return ('[%d]' % (self._count - 1), impl['_M_head_impl']) - def __init__ (self, typename, val): - self.typename = strip_versioned_namespace(typename) - self.val = val; + def __init__(self, typename, val): + self._typename = strip_versioned_namespace(typename) + self._val = val - def children (self): - return self._iterator (self.val) + def children(self): + return self._iterator(self._val) - def to_string (self): - if len (self.val.type.fields ()) == 0: - return 'empty %s' % (self.typename) - return '%s containing' % (self.typename) + def to_string(self): + if len(self._val.type.fields()) == 0: + return 'empty %s' % (self._typename) + return '%s containing' % (self._typename) -class StdStackOrQueuePrinter: - "Print a std::stack or std::queue" - def __init__ (self, typename, val): - self.typename = strip_versioned_namespace(typename) - self.visualizer = gdb.default_visualizer(val['c']) +class StdStackOrQueuePrinter(printer_base): + """Print a std::stack or std::queue.""" - def children (self): - return self.visualizer.children() + def __init__(self, typename, val): + self._typename = strip_versioned_namespace(typename) + self._visualizer = gdb.default_visualizer(val['c']) - def to_string (self): - return '%s wrapping: %s' % (self.typename, - self.visualizer.to_string()) + def children(self): + return self._visualizer.children() + + def to_string(self): + return '%s wrapping: %s' % (self._typename, + self._visualizer.to_string()) - def display_hint (self): - if hasattr (self.visualizer, 'display_hint'): - return self.visualizer.display_hint () + def display_hint(self): + if hasattr(self._visualizer, 'display_hint'): + return self._visualizer.display_hint() return None + class RbtreeIterator(Iterator): """ Turn an RB-tree-based container (std::map, std::set etc.) into @@ -517,24 +739,24 @@ class RbtreeIterator(Iterator): """ def __init__(self, rbtree): - self.size = rbtree['_M_t']['_M_impl']['_M_node_count'] - self.node = rbtree['_M_t']['_M_impl']['_M_header']['_M_left'] - self.count = 0 + self._size = rbtree['_M_t']['_M_impl']['_M_node_count'] + self._node = rbtree['_M_t']['_M_impl']['_M_header']['_M_left'] + self._count = 0 def __iter__(self): return self def __len__(self): - return int (self.size) + return int(self._size) def __next__(self): - if self.count == self.size: + if self._count == self._size: raise StopIteration - result = self.node - self.count = self.count + 1 - if self.count < self.size: + result = self._node + self._count = self._count + 1 + if self._count < self._size: # Compute the next node. - node = self.node + node = self._node if node.dereference()['_M_right']: node = node.dereference()['_M_right'] while node.dereference()['_M_left']: @@ -546,11 +768,12 @@ class RbtreeIterator(Iterator): parent = parent.dereference()['_M_parent'] if node.dereference()['_M_right'] != parent: node = parent - self.node = node + self._node = node return result + def get_value_from_Rb_tree_node(node): - """Returns the value held in an _Rb_tree_node<_Val>""" + """Return the value held in an _Rb_tree_node<_Val>.""" try: member = node.type.fields()[1].name if member == '_M_value_field': @@ -567,143 +790,142 @@ def get_value_from_Rb_tree_node(node): # This is a pretty printer for std::_Rb_tree_iterator (which is # std::map::iterator), and has nothing to do with the RbtreeIterator # class above. -class StdRbtreeIteratorPrinter: - "Print std::map::iterator, std::set::iterator, etc." - - def __init__ (self, typename, val): - self.val = val - valtype = self.val.type.template_argument(0).strip_typedefs() - nodetype = '_Rb_tree_node<' + str(valtype) + '>' - if _versioned_namespace and typename.startswith('std::' + _versioned_namespace): - nodetype = _versioned_namespace + nodetype - nodetype = gdb.lookup_type('std::' + nodetype) - self.link_type = nodetype.strip_typedefs().pointer() - - def to_string (self): - if not self.val['_M_node']: + + +class StdRbtreeIteratorPrinter(printer_base): + """Print std::map::iterator, std::set::iterator, etc.""" + + def __init__(self, typename, val): + self._val = val + nodetype = lookup_node_type('_Rb_tree_node', self._val.type) + self._link_type = nodetype.pointer() + + def to_string(self): + if not self._val['_M_node']: return 'non-dereferenceable iterator for associative container' - node = self.val['_M_node'].cast(self.link_type).dereference() + node = self._val['_M_node'].cast(self._link_type).dereference() return str(get_value_from_Rb_tree_node(node)) -class StdDebugIteratorPrinter: - "Print a debug enabled version of an iterator" - def __init__ (self, typename, val): - self.val = val +class StdDebugIteratorPrinter(printer_base): + """Print a debug enabled version of an iterator.""" + + def __init__(self, typename, val): + self._val = val # Just strip away the encapsulating __gnu_debug::_Safe_iterator # and return the wrapped iterator value. - def to_string (self): + def to_string(self): base_type = gdb.lookup_type('__gnu_debug::_Safe_iterator_base') - itype = self.val.type.template_argument(0) - safe_seq = self.val.cast(base_type)['_M_sequence'] + itype = self._val.type.template_argument(0) + safe_seq = self._val.cast(base_type)['_M_sequence'] if not safe_seq: - return str(self.val.cast(itype)) - if self.val['_M_version'] != safe_seq['_M_version']: + return str(self._val.cast(itype)) + if self._val['_M_version'] != safe_seq['_M_version']: return "invalid iterator" - return str(self.val.cast(itype)) + return str(self._val.cast(itype)) + def num_elements(num): """Return either "1 element" or "N elements" depending on the argument.""" return '1 element' if num == 1 else '%d elements' % num -class StdMapPrinter: - "Print a std::map or std::multimap" + +class StdMapPrinter(printer_base): + """Print a std::map or std::multimap.""" # Turn an RbtreeIterator into a pretty-print iterator. class _iter(Iterator): def __init__(self, rbiter, type): - self.rbiter = rbiter - self.count = 0 - self.type = type + self._rbiter = rbiter + self._count = 0 + self._type = type def __iter__(self): return self def __next__(self): - if self.count % 2 == 0: - n = next(self.rbiter) - n = n.cast(self.type).dereference() + if self._count % 2 == 0: + n = next(self._rbiter) + n = n.cast(self._type).dereference() n = get_value_from_Rb_tree_node(n) - self.pair = n + self._pair = n item = n['first'] else: - item = self.pair['second'] - result = ('[%d]' % self.count, item) - self.count = self.count + 1 + item = self._pair['second'] + result = ('[%d]' % self._count, item) + self._count = self._count + 1 return result - def __init__ (self, typename, val): - self.typename = strip_versioned_namespace(typename) - self.val = val + def __init__(self, typename, val): + self._typename = strip_versioned_namespace(typename) + self._val = val - def to_string (self): - return '%s with %s' % (self.typename, - num_elements(len(RbtreeIterator (self.val)))) + def to_string(self): + return '%s with %s' % (self._typename, + num_elements(len(RbtreeIterator(self._val)))) - def children (self): - rep_type = find_type(self.val.type, '_Rep_type') - node = find_type(rep_type, '_Link_type') - node = node.strip_typedefs() - return self._iter (RbtreeIterator (self.val), node) + def children(self): + node = lookup_node_type('_Rb_tree_node', self._val.type).pointer() + return self._iter(RbtreeIterator(self._val), node) - def display_hint (self): + def display_hint(self): return 'map' -class StdSetPrinter: - "Print a std::set or std::multiset" + +class StdSetPrinter(printer_base): + """Print a std::set or std::multiset.""" # Turn an RbtreeIterator into a pretty-print iterator. class _iter(Iterator): def __init__(self, rbiter, type): - self.rbiter = rbiter - self.count = 0 - self.type = type + self._rbiter = rbiter + self._count = 0 + self._type = type def __iter__(self): return self def __next__(self): - item = next(self.rbiter) - item = item.cast(self.type).dereference() + item = next(self._rbiter) + item = item.cast(self._type).dereference() item = get_value_from_Rb_tree_node(item) # FIXME: this is weird ... what to do? # Maybe a 'set' display hint? - result = ('[%d]' % self.count, item) - self.count = self.count + 1 + result = ('[%d]' % self._count, item) + self._count = self._count + 1 return result - def __init__ (self, typename, val): - self.typename = strip_versioned_namespace(typename) - self.val = val + def __init__(self, typename, val): + self._typename = strip_versioned_namespace(typename) + self._val = val - def to_string (self): - return '%s with %s' % (self.typename, - num_elements(len(RbtreeIterator (self.val)))) + def to_string(self): + return '%s with %s' % (self._typename, + num_elements(len(RbtreeIterator(self._val)))) - def children (self): - rep_type = find_type(self.val.type, '_Rep_type') - node = find_type(rep_type, '_Link_type') - node = node.strip_typedefs() - return self._iter (RbtreeIterator (self.val), node) + def children(self): + node = lookup_node_type('_Rb_tree_node', self._val.type).pointer() + return self._iter(RbtreeIterator(self._val), node) -class StdBitsetPrinter: - "Print a std::bitset" + +class StdBitsetPrinter(printer_base): + """Print a std::bitset.""" def __init__(self, typename, val): - self.typename = strip_versioned_namespace(typename) - self.val = val + self._typename = strip_versioned_namespace(typename) + self._val = val - def to_string (self): + def to_string(self): # If template_argument handled values, we could print the # size. Or we could use a regexp on the type. - return '%s' % (self.typename) + return '%s' % (self._typename) - def children (self): + def children(self): try: # An empty bitset may not have any members which will # result in an exception being thrown. - words = self.val['_M_w'] + words = self._val['_M_w'] except: return [] @@ -713,7 +935,7 @@ class StdBitsetPrinter: # array. This depends on the template specialization used. # If it is a single long, convert to a single element list. if wtype.code == gdb.TYPE_CODE_ARRAY: - tsize = wtype.target ().sizeof + tsize = wtype.target().sizeof else: words = [words] tsize = wtype.sizeof @@ -733,279 +955,349 @@ class StdBitsetPrinter: byte = byte + 1 return result -class StdDequePrinter: - "Print a std::deque" + +class StdDequePrinter(printer_base): + """Print a std::deque.""" class _iter(Iterator): def __init__(self, node, start, end, last, buffer_size): - self.node = node - self.p = start - self.end = end - self.last = last - self.buffer_size = buffer_size - self.count = 0 + self._node = node + self._p = start + self._end = end + self._last = last + self._buffer_size = buffer_size + self._count = 0 def __iter__(self): return self def __next__(self): - if self.p == self.last: + if self._p == self._last: raise StopIteration - result = ('[%d]' % self.count, self.p.dereference()) - self.count = self.count + 1 + result = ('[%d]' % self._count, self._p.dereference()) + self._count = self._count + 1 # Advance the 'cur' pointer. - self.p = self.p + 1 - if self.p == self.end: + self._p = self._p + 1 + if self._p == self._end: # If we got to the end of this bucket, move to the # next bucket. - self.node = self.node + 1 - self.p = self.node[0] - self.end = self.p + self.buffer_size + self._node = self._node + 1 + self._p = self._node[0] + self._end = self._p + self._buffer_size return result def __init__(self, typename, val): - self.typename = strip_versioned_namespace(typename) - self.val = val - self.elttype = val.type.template_argument(0) - size = self.elttype.sizeof + self._typename = strip_versioned_namespace(typename) + self._val = val + self._elttype = val.type.template_argument(0) + size = self._elttype.sizeof if size < 512: - self.buffer_size = int (512 / size) + self._buffer_size = int(512 / size) else: - self.buffer_size = 1 + self._buffer_size = 1 def to_string(self): - start = self.val['_M_impl']['_M_start'] - end = self.val['_M_impl']['_M_finish'] + start = self._val['_M_impl']['_M_start'] + end = self._val['_M_impl']['_M_finish'] delta_n = end['_M_node'] - start['_M_node'] - 1 delta_s = start['_M_last'] - start['_M_cur'] delta_e = end['_M_cur'] - end['_M_first'] - size = self.buffer_size * delta_n + delta_s + delta_e + size = self._buffer_size * delta_n + delta_s + delta_e - return '%s with %s' % (self.typename, num_elements(long(size))) + return '%s with %s' % (self._typename, num_elements(long(size))) def children(self): - start = self.val['_M_impl']['_M_start'] - end = self.val['_M_impl']['_M_finish'] + start = self._val['_M_impl']['_M_start'] + end = self._val['_M_impl']['_M_finish'] return self._iter(start['_M_node'], start['_M_cur'], start['_M_last'], - end['_M_cur'], self.buffer_size) + end['_M_cur'], self._buffer_size) - def display_hint (self): + def display_hint(self): return 'array' -class StdDequeIteratorPrinter: - "Print std::deque::iterator" + +class StdDequeIteratorPrinter(printer_base): + """Print std::deque::iterator.""" def __init__(self, typename, val): - self.val = val + self._val = val def to_string(self): - if not self.val['_M_cur']: + if not self._val['_M_cur']: return 'non-dereferenceable iterator for std::deque' - return str(self.val['_M_cur'].dereference()) + return str(self._val['_M_cur'].dereference()) -class StdStringPrinter: - "Print a std::basic_string of some kind" + +class StdStringPrinter(printer_base): + """Print a std::basic_string of some kind.""" def __init__(self, typename, val): - self.val = val - self.new_string = typename.find("::__cxx11::basic_string") != -1 + self._val = val + self._new_string = typename.find("::__cxx11::basic_string") != -1 def to_string(self): # Make sure &string works, too. - type = self.val.type + type = self._val.type if type.code == gdb.TYPE_CODE_REF: - type = type.target () + type = type.target() # Calculate the length of the string so that to_string returns # the string according to length, not according to first null # encountered. - ptr = self.val ['_M_dataplus']['_M_p'] - if self.new_string: - length = self.val['_M_string_length'] + ptr = self._val['_M_dataplus']['_M_p'] + if self._new_string: + length = self._val['_M_string_length'] # https://sourceware.org/bugzilla/show_bug.cgi?id=17728 ptr = ptr.cast(ptr.type.strip_typedefs()) else: - realtype = type.unqualified ().strip_typedefs () - reptype = gdb.lookup_type (str (realtype) + '::_Rep').pointer () + realtype = type.unqualified().strip_typedefs() + reptype = gdb.lookup_type(str(realtype) + '::_Rep').pointer() header = ptr.cast(reptype) - 1 - length = header.dereference ()['_M_length'] + length = header.dereference()['_M_length'] if hasattr(ptr, "lazy_string"): - return ptr.lazy_string (length = length) - return ptr.string (length = length) + return ptr.lazy_string(length=length) + return ptr.string(length=length) + + def display_hint(self): + return 'string' + + +def access_streambuf_ptrs(streambuf): + """Access the streambuf put area pointers.""" + pbase = streambuf['_M_out_beg'] + pptr = streambuf['_M_out_cur'] + egptr = streambuf['_M_in_end'] + return pbase, pptr, egptr - def display_hint (self): + +class StdStringBufPrinter(printer_base): + """Print a std::basic_stringbuf.""" + + def __init__(self, _, val): + self._val = val + + def to_string(self): + (pbase, pptr, egptr) = access_streambuf_ptrs(self._val) + # Logic from basic_stringbuf::_M_high_mark() + if pptr: + if not egptr or pptr > egptr: + return pbase.string(length=pptr - pbase) + else: + return pbase.string(length=egptr - pbase) + return self._val['_M_string'] + + def display_hint(self): return 'string' + +class StdStringStreamPrinter(printer_base): + """Print a std::basic_stringstream.""" + + def __init__(self, typename, val): + self._val = val + self._typename = typename + + # Check if the stream was redirected. This is essentially: + # val['_M_streambuf'] != val['_M_stringbuf'].address + # However, GDB can't resolve the virtual inheritance, so we do that + # manually. + basetype = [f.type for f in val.type.fields() if f.is_base_class][0] + gdb.set_convenience_variable('__stream', val.cast(basetype).address) + self._streambuf = gdb.parse_and_eval('$__stream->rdbuf()') + self._was_redirected = self._streambuf != val['_M_stringbuf'].address + + def to_string(self): + if self._was_redirected: + return "%s redirected to %s" % ( + self._typename, self._streambuf.dereference()) + return self._val['_M_stringbuf'] + + def display_hint(self): + if self._was_redirected: + return None + return 'string' + + class Tr1HashtableIterator(Iterator): - def __init__ (self, hash): - self.buckets = hash['_M_buckets'] - self.bucket = 0 - self.bucket_count = hash['_M_bucket_count'] - self.node_type = find_type(hash.type, '_Node').pointer() - self.node = 0 - while self.bucket != self.bucket_count: - self.node = self.buckets[self.bucket] - if self.node: + def __init__(self, hashtable): + self._buckets = hashtable['_M_buckets'] + self._bucket = 0 + self._bucket_count = hashtable['_M_bucket_count'] + self._node_type = find_type(hashtable.type, '_Node').pointer() + self._node = 0 + while self._bucket != self._bucket_count: + self._node = self._buckets[self._bucket] + if self._node: break - self.bucket = self.bucket + 1 + self._bucket = self._bucket + 1 - def __iter__ (self): + def __iter__(self): return self - def __next__ (self): - if self.node == 0: + def __next__(self): + if self._node == 0: raise StopIteration - node = self.node.cast(self.node_type) + node = self._node.cast(self._node_type) result = node.dereference()['_M_v'] - self.node = node.dereference()['_M_next']; - if self.node == 0: - self.bucket = self.bucket + 1 - while self.bucket != self.bucket_count: - self.node = self.buckets[self.bucket] - if self.node: + self._node = node.dereference()['_M_next'] + if self._node == 0: + self._bucket = self._bucket + 1 + while self._bucket != self._bucket_count: + self._node = self._buckets[self._bucket] + if self._node: break - self.bucket = self.bucket + 1 + self._bucket = self._bucket + 1 return result + class StdHashtableIterator(Iterator): - def __init__(self, hash): - self.node = hash['_M_before_begin']['_M_nxt'] - self.node_type = find_type(hash.type, '__node_type').pointer() + def __init__(self, hashtable): + self._node = hashtable['_M_before_begin']['_M_nxt'] + valtype = hashtable.type.template_argument(1) + cached = hashtable.type.template_argument(9).template_argument(0) + node_type = lookup_templ_spec('std::__detail::_Hash_node', str(valtype), + 'true' if cached else 'false') + self._node_type = node_type.pointer() def __iter__(self): return self def __next__(self): - if self.node == 0: + if self._node == 0: raise StopIteration - elt = self.node.cast(self.node_type).dereference() - self.node = elt['_M_nxt'] + elt = self._node.cast(self._node_type).dereference() + self._node = elt['_M_nxt'] valptr = elt['_M_storage'].address valptr = valptr.cast(elt.type.template_argument(0).pointer()) return valptr.dereference() -class Tr1UnorderedSetPrinter: - "Print a tr1::unordered_set" - def __init__ (self, typename, val): - self.typename = strip_versioned_namespace(typename) - self.val = val +class Tr1UnorderedSetPrinter(printer_base): + """Print a std::unordered_set or tr1::unordered_set.""" + + def __init__(self, typename, val): + self._typename = strip_versioned_namespace(typename) + self._val = val - def hashtable (self): - if self.typename.startswith('std::tr1'): - return self.val - return self.val['_M_h'] + def _hashtable(self): + if self._typename.startswith('std::tr1'): + return self._val + return self._val['_M_h'] - def to_string (self): - count = self.hashtable()['_M_element_count'] - return '%s with %s' % (self.typename, num_elements(count)) + def to_string(self): + count = self._hashtable()['_M_element_count'] + return '%s with %s' % (self._typename, num_elements(count)) @staticmethod - def format_count (i): + def _format_count(i): return '[%d]' % i - def children (self): - counter = imap (self.format_count, itertools.count()) - if self.typename.startswith('std::tr1'): - return izip (counter, Tr1HashtableIterator (self.hashtable())) - return izip (counter, StdHashtableIterator (self.hashtable())) + def children(self): + counter = imap(self._format_count, itertools.count()) + if self._typename.startswith('std::tr1'): + return izip(counter, Tr1HashtableIterator(self._hashtable())) + return izip(counter, StdHashtableIterator(self._hashtable())) -class Tr1UnorderedMapPrinter: - "Print a tr1::unordered_map" - def __init__ (self, typename, val): - self.typename = strip_versioned_namespace(typename) - self.val = val +class Tr1UnorderedMapPrinter(printer_base): + """Print a std::unordered_map or tr1::unordered_map.""" - def hashtable (self): - if self.typename.startswith('std::tr1'): - return self.val - return self.val['_M_h'] + def __init__(self, typename, val): + self._typename = strip_versioned_namespace(typename) + self._val = val + + def _hashtable(self): + if self._typename.startswith('std::tr1'): + return self._val + return self._val['_M_h'] - def to_string (self): - count = self.hashtable()['_M_element_count'] - return '%s with %s' % (self.typename, num_elements(count)) + def to_string(self): + count = self._hashtable()['_M_element_count'] + return '%s with %s' % (self._typename, num_elements(count)) @staticmethod - def flatten (list): + def _flatten(list): for elt in list: for i in elt: yield i @staticmethod - def format_one (elt): + def _format_one(elt): return (elt['first'], elt['second']) @staticmethod - def format_count (i): + def _format_count(i): return '[%d]' % i - def children (self): - counter = imap (self.format_count, itertools.count()) + def children(self): + counter = imap(self._format_count, itertools.count()) # Map over the hash table and flatten the result. - if self.typename.startswith('std::tr1'): - data = self.flatten (imap (self.format_one, Tr1HashtableIterator (self.hashtable()))) + if self._typename.startswith('std::tr1'): + data = self._flatten( + imap(self._format_one, Tr1HashtableIterator(self._hashtable()))) # Zip the two iterators together. - return izip (counter, data) - data = self.flatten (imap (self.format_one, StdHashtableIterator (self.hashtable()))) + return izip(counter, data) + data = self._flatten( + imap(self._format_one, StdHashtableIterator(self._hashtable()))) # Zip the two iterators together. - return izip (counter, data) + return izip(counter, data) - def display_hint (self): + def display_hint(self): return 'map' -class StdForwardListPrinter: - "Print a std::forward_list" + +class StdForwardListPrinter(printer_base): + """Print a std::forward_list.""" class _iterator(Iterator): def __init__(self, nodetype, head): - self.nodetype = nodetype - self.base = head['_M_next'] - self.count = 0 + self._nodetype = nodetype + self._base = head['_M_next'] + self._count = 0 def __iter__(self): return self def __next__(self): - if self.base == 0: + if self._base == 0: raise StopIteration - elt = self.base.cast(self.nodetype).dereference() - self.base = elt['_M_next'] - count = self.count - self.count = self.count + 1 + elt = self._base.cast(self._nodetype).dereference() + self._base = elt['_M_next'] + count = self._count + self._count = self._count + 1 valptr = elt['_M_storage'].address valptr = valptr.cast(elt.type.template_argument(0).pointer()) return ('[%d]' % count, valptr.dereference()) def __init__(self, typename, val): - self.val = val - self.typename = strip_versioned_namespace(typename) + self._val = val + self._typename = strip_versioned_namespace(typename) def children(self): - nodetype = find_type(self.val.type, '_Node') - nodetype = nodetype.strip_typedefs().pointer() - return self._iterator(nodetype, self.val['_M_impl']['_M_head']) + nodetype = lookup_node_type('_Fwd_list_node', self._val.type).pointer() + return self._iterator(nodetype, self._val['_M_impl']['_M_head']) def to_string(self): - if self.val['_M_impl']['_M_head']['_M_next'] == 0: - return 'empty %s' % self.typename - return '%s' % self.typename + if self._val['_M_impl']['_M_head']['_M_next'] == 0: + return 'empty %s' % self._typename + return '%s' % self._typename + -class SingleObjContainerPrinter(object): - "Base class for printers of containers of single objects" +class SingleObjContainerPrinter(printer_base): + """Base class for printers of containers of single objects.""" - def __init__ (self, val, viz, hint = None): - self.contained_value = val - self.visualizer = viz - self.hint = hint + def __init__(self, val, viz, hint=None): + self._contained_value = val + self._visualizer = viz + self._hint = hint def _recognize(self, type): - """Return TYPE as a string after applying type printers""" + """Return type as a string after applying type printers.""" global _use_type_printing if not _use_type_printing: return str(type) @@ -1013,35 +1305,37 @@ class SingleObjContainerPrinter(object): type) or str(type) class _contained(Iterator): - def __init__ (self, val): - self.val = val + def __init__(self, val): + self._val = val - def __iter__ (self): + def __iter__(self): return self def __next__(self): - if self.val is None: + if self._val is None: raise StopIteration - retval = self.val - self.val = None + retval = self._val + self._val = None return ('[contained value]', retval) - def children (self): - if self.contained_value is None: - return self._contained (None) - if hasattr (self.visualizer, 'children'): - return self.visualizer.children () - return self._contained (self.contained_value) + def children(self): + if self._contained_value is None: + return self._contained(None) + if hasattr(self._visualizer, 'children'): + return self._visualizer.children() + return self._contained(self._contained_value) + + def display_hint(self): + if (hasattr(self._visualizer, 'children') + and hasattr(self._visualizer, 'display_hint')): + # If contained value is a map we want to display in the same way. + return self._visualizer.display_hint() + return self._hint - def display_hint (self): - # if contained value is a map we want to display in the same way - if hasattr (self.visualizer, 'children') and hasattr (self.visualizer, 'display_hint'): - return self.visualizer.display_hint () - return self.hint def function_pointer_to_name(f): - "Find the name of the function referred to by the gdb.Value f, " - " which should contain a function pointer from the program." + """Find the name of the function referred to by the gdb.Value f, + which should contain a function pointer from the program.""" # Turn the function pointer into an actual address. # This is needed to unpack ppc64 function descriptors. @@ -1062,252 +1356,1003 @@ def function_pointer_to_name(f): except: return None + class StdExpAnyPrinter(SingleObjContainerPrinter): - "Print a std::any or std::experimental::any" + """Print a std::any or std::experimental::any.""" - def __init__ (self, typename, val): - self.typename = strip_versioned_namespace(typename) - self.typename = re.sub('^std::experimental::fundamentals_v\d::', 'std::experimental::', self.typename, 1) - self.val = val - self.contained_type = None + def __init__(self, typename, val): + self._typename = strip_versioned_namespace(typename) + self._typename = strip_fundts_namespace(self._typename) + self._val = val + self._contained_type = None contained_value = None visualizer = None - mgr = self.val['_M_manager'] + mgr = self._val['_M_manager'] if mgr != 0: func = function_pointer_to_name(mgr) if not func: - raise ValueError("Invalid function pointer in %s" % (self.typename)) - rx = r"""({0}::_Manager_\w+<.*>)::_S_manage""".format(typename) + raise ValueError( + "Invalid function pointer in %s" % (self._typename)) + # We want to use this regular expression: + # T::_Manager_xxx<.*>::_S_manage\(T::_Op, const T\*, T::_Arg\*\) + # where T is std::any or std::experimental::any. + # But we need to account for variances in demangled names + # between GDB versions, e.g. 'enum T::_Op' instead of 'T::_Op'. + rx = ( + r"({0}::_Manager_\w+<.*>)::_S_manage\(" + r"(enum )?{0}::_Op, (const {0}|{0} const) ?\*, " + r"(union )?{0}::_Arg ?\*\)" + ).format(typename) m = re.match(rx, func) if not m: - raise ValueError("Unknown manager function in %s" % self.typename) + raise ValueError( + "Unknown manager function in %s" % self._typename) mgrname = m.group(1) # FIXME need to expand 'std::string' so that gdb.lookup_type works if 'std::string' in mgrname: - mgrname = re.sub("std::string(?!\w)", str(gdb.lookup_type('std::string').strip_typedefs()), m.group(1)) - - mgrtype = gdb.lookup_type(mgrname) - self.contained_type = mgrtype.template_argument(0) + mgrtypes = [] + for s in StdExpAnyPrinter._string_types(): + try: + x = re.sub(r"std::string(?!\w)", s, m.group(1)) + # The following lookup might raise gdb.error if the + # manager function was never instantiated for 's' in + # the program, because there will be no such type. + mgrtypes.append(gdb.lookup_type(x)) + except gdb.error: + pass + if len(mgrtypes) != 1: + # FIXME: this is unlikely in practice, but possible for + # programs that use both old and new string types with + # std::any in a single program. Can we do better? + # Maybe find the address of each type's _S_manage and + # compare to the address stored in _M_manager? + raise ValueError( + 'Cannot uniquely determine std::string type ' + 'used in std::any' + ) + mgrtype = mgrtypes[0] + else: + mgrtype = gdb.lookup_type(mgrname) + self._contained_type = mgrtype.template_argument(0) valptr = None if '::_Manager_internal' in mgrname: - valptr = self.val['_M_storage']['_M_buffer'].address + valptr = self._val['_M_storage']['_M_buffer'].address elif '::_Manager_external' in mgrname: - valptr = self.val['_M_storage']['_M_ptr'] + valptr = self._val['_M_storage']['_M_ptr'] else: - raise ValueError("Unknown manager function in %s" % self.typename) - contained_value = valptr.cast(self.contained_type.pointer()).dereference() + raise ValueError( + "Unknown manager function in %s" % self._typename) + contained_value = valptr.cast( + self._contained_type.pointer()).dereference() visualizer = gdb.default_visualizer(contained_value) - super(StdExpAnyPrinter, self).__init__ (contained_value, visualizer) - - def to_string (self): - if self.contained_type is None: - return '%s [no contained value]' % self.typename - desc = "%s containing " % self.typename - if hasattr (self.visualizer, 'children'): - return desc + self.visualizer.to_string () - valtype = self._recognize (self.contained_type) + super(StdExpAnyPrinter, self).__init__(contained_value, visualizer) + + def to_string(self): + if self._contained_type is None: + return '%s [no contained value]' % self._typename + desc = "%s containing " % self._typename + if hasattr(self._visualizer, 'children'): + return desc + self._visualizer.to_string() + valtype = self._recognize(self._contained_type) return desc + strip_versioned_namespace(str(valtype)) + @staticmethod + def _string_types(): + # This lookup for std::string might return the __cxx11 version, + # but that's not necessarily the one used by the std::any + # manager function we're trying to find. + strings = {str(gdb.lookup_type('std::string').strip_typedefs())} + # So also consider all the other possible std::string types! + s = 'basic_string, std::allocator >' + quals = ['std::', 'std::__cxx11::', + 'std::' + _versioned_namespace] + strings |= {q + s for q in quals} # set of unique strings + return strings + + class StdExpOptionalPrinter(SingleObjContainerPrinter): - "Print a std::optional or std::experimental::optional" - - def __init__ (self, typename, val): - valtype = self._recognize (val.type.template_argument(0)) - self.typename = strip_versioned_namespace(typename) - self.typename = re.sub('^std::(experimental::|)(fundamentals_v\d::|)(.*)', r'std::\1\3<%s>' % valtype, self.typename, 1) - if not self.typename.startswith('std::experimental'): - val = val['_M_payload'] - self.val = val - contained_value = val['_M_payload'] if self.val['_M_engaged'] else None - visualizer = gdb.default_visualizer (val['_M_payload']) - super (StdExpOptionalPrinter, self).__init__ (contained_value, visualizer) - - def to_string (self): - if self.contained_value is None: - return "%s [no contained value]" % self.typename - if hasattr (self.visualizer, 'children'): - return "%s containing %s" % (self.typename, - self.visualizer.to_string()) - return self.typename + """Print a std::optional or std::experimental::optional.""" + + def __init__(self, typename, val): + self._typename = strip_versioned_namespace(typename) + self._typename = strip_fundts_namespace(self._typename) + payload = val['_M_payload'] + if self._typename.startswith('std::experimental'): + engaged = val['_M_engaged'] + contained_value = payload + else: + engaged = payload['_M_engaged'] + contained_value = payload['_M_payload'] + try: + # Since GCC 9 + contained_value = contained_value['_M_value'] + except: + pass + visualizer = gdb.default_visualizer(contained_value) + if not engaged: + contained_value = None + super(StdExpOptionalPrinter, self).__init__( + contained_value, visualizer) + + def to_string(self): + if self._contained_value is None: + return "%s [no contained value]" % self._typename + if hasattr(self._visualizer, 'children'): + return "%s containing %s" % (self._typename, + self._visualizer.to_string()) + return self._typename + class StdVariantPrinter(SingleObjContainerPrinter): - "Print a std::variant" + """Print a std::variant.""" def __init__(self, typename, val): alternatives = get_template_arg_list(val.type) - self.typename = strip_versioned_namespace(typename) - self.typename = "%s<%s>" % (self.typename, ', '.join([self._recognize(alt) for alt in alternatives])) - self.index = val['_M_index'] - if self.index >= len(alternatives): - self.contained_type = None + self._typename = strip_versioned_namespace(typename) + self._index = val['_M_index'] + if self._index >= len(alternatives): + self._contained_type = None contained_value = None visualizer = None else: - self.contained_type = alternatives[int(self.index)] + self._contained_type = alternatives[int(self._index)] addr = val['_M_u']['_M_first']['_M_storage'].address - contained_value = addr.cast(self.contained_type.pointer()).dereference() + contained_value = addr.cast( + self._contained_type.pointer()).dereference() visualizer = gdb.default_visualizer(contained_value) - super (StdVariantPrinter, self).__init__(contained_value, visualizer, 'array') + super(StdVariantPrinter, self).__init__( + contained_value, visualizer, 'array') def to_string(self): - if self.contained_value is None: - return "%s [no contained value]" % self.typename - if hasattr(self.visualizer, 'children'): - return "%s [index %d] containing %s" % (self.typename, self.index, - self.visualizer.to_string()) - return "%s [index %d]" % (self.typename, self.index) + if self._contained_value is None: + return "%s [no contained value]" % self._typename + if hasattr(self._visualizer, 'children'): + return "%s [index %d] containing %s" % (self._typename, self._index, + self._visualizer.to_string()) + return "%s [index %d]" % (self._typename, self._index) + class StdNodeHandlePrinter(SingleObjContainerPrinter): - "Print a container node handle" + """Print a container node handle.""" def __init__(self, typename, val): - self.value_type = val.type.template_argument(1) + self._value_type = val.type.template_argument(1) nodetype = val.type.template_argument(2).template_argument(0) - self.is_rb_tree_node = is_specialization_of(nodetype.name, '_Rb_tree_node') - self.is_map_node = val.type.template_argument(0) != self.value_type + self._is_rb_tree_node = is_specialization_of( + nodetype.name, '_Rb_tree_node') + self._is_map_node = val.type.template_argument(0) != self._value_type nodeptr = val['_M_ptr'] if nodeptr: - if self.is_rb_tree_node: - contained_value = get_value_from_Rb_tree_node(nodeptr.dereference()) + if self._is_rb_tree_node: + contained_value = get_value_from_Rb_tree_node( + nodeptr.dereference()) else: contained_value = get_value_from_aligned_membuf(nodeptr['_M_storage'], - self.value_type) + self._value_type) visualizer = gdb.default_visualizer(contained_value) else: contained_value = None visualizer = None optalloc = val['_M_alloc'] - self.alloc = optalloc['_M_payload'] if optalloc['_M_engaged'] else None + self._alloc = optalloc['_M_payload'] if optalloc['_M_engaged'] else None super(StdNodeHandlePrinter, self).__init__(contained_value, visualizer, 'array') def to_string(self): desc = 'node handle for ' - if not self.is_rb_tree_node: + if not self._is_rb_tree_node: desc += 'unordered ' - if self.is_map_node: - desc += 'map'; + if self._is_map_node: + desc += 'map' else: - desc += 'set'; + desc += 'set' - if self.contained_value: + if self._contained_value: desc += ' with element' - if hasattr(self.visualizer, 'children'): - return "%s = %s" % (desc, self.visualizer.to_string()) + if hasattr(self._visualizer, 'children'): + return "%s = %s" % (desc, self._visualizer.to_string()) return desc else: return 'empty %s' % desc -class StdExpStringViewPrinter: - "Print a std::basic_string_view or std::experimental::basic_string_view" - def __init__ (self, typename, val): - self.val = val +class StdExpStringViewPrinter(printer_base): + """ + Print a std::basic_string_view or std::experimental::basic_string_view + """ + + def __init__(self, typename, val): + self._val = val - def to_string (self): - ptr = self.val['_M_str'] - len = self.val['_M_len'] - if hasattr (ptr, "lazy_string"): - return ptr.lazy_string (length = len) - return ptr.string (length = len) + def to_string(self): + ptr = self._val['_M_str'] + len = self._val['_M_len'] + if hasattr(ptr, "lazy_string"): + return ptr.lazy_string(length=len) + return ptr.string(length=len) - def display_hint (self): + def display_hint(self): return 'string' -class StdExpPathPrinter: - "Print a std::experimental::filesystem::path or std::filesystem::path" - def __init__ (self, typename, val): - self.val = val - self.typename = typename - start = self.val['_M_cmpts']['_M_impl']['_M_start'] - finish = self.val['_M_cmpts']['_M_impl']['_M_finish'] - self.num_cmpts = int (finish - start) +class StdExpPathPrinter(printer_base): + """Print a std::experimental::filesystem::path.""" + + def __init__(self, typename, val): + self._val = val + self._typename = typename + start = self._val['_M_cmpts']['_M_impl']['_M_start'] + finish = self._val['_M_cmpts']['_M_impl']['_M_finish'] + self._num_cmpts = int(finish - start) def _path_type(self): - t = str(self.val['_M_type']) + t = str(self._val['_M_type']) if t[-9:] == '_Root_dir': return "root-directory" if t[-10:] == '_Root_name': return "root-name" return None - def to_string (self): - path = "%s" % self.val ['_M_pathname'] - if self.num_cmpts == 0: + def to_string(self): + path = "%s" % self._val['_M_pathname'] + if self._num_cmpts == 0: t = self._path_type() if t: path = '%s [%s]' % (path, t) - return "filesystem::path %s" % path + return "experimental::filesystem::path %s" % path class _iterator(Iterator): def __init__(self, cmpts, pathtype): - self.pathtype = pathtype - self.item = cmpts['_M_impl']['_M_start'] - self.finish = cmpts['_M_impl']['_M_finish'] - self.count = 0 + self._pathtype = pathtype + self._item = cmpts['_M_impl']['_M_start'] + self._finish = cmpts['_M_impl']['_M_finish'] + self._count = 0 + + def __iter__(self): + return self + + def __next__(self): + if self._item == self._finish: + raise StopIteration + item = self._item.dereference() + count = self._count + self._count = self._count + 1 + self._item = self._item + 1 + path = item['_M_pathname'] + t = StdExpPathPrinter(self._pathtype, item)._path_type() + if not t: + t = count + return ('[%s]' % t, path) + + def children(self): + return self._iterator(self._val['_M_cmpts'], self._typename) + + +class StdPathPrinter(printer_base): + """Print a std::filesystem::path.""" + + def __init__(self, typename, val): + self._val = val + self._typename = typename + impl = unique_ptr_get(self._val['_M_cmpts']['_M_impl']) + self._type = impl.cast(gdb.lookup_type('uintptr_t')) & 3 + if self._type == 0: + self._impl = impl + else: + self._impl = None + + def _path_type(self): + t = str(self._type.cast(gdb.lookup_type(self._typename + '::_Type'))) + if t[-9:] == '_Root_dir': + return "root-directory" + if t[-10:] == '_Root_name': + return "root-name" + return None + + def to_string(self): + path = "%s" % self._val['_M_pathname'] + if self._type != 0: + t = self._path_type() + if t: + path = '%s [%s]' % (path, t) + return "filesystem::path %s" % path + + class _iterator(Iterator): + def __init__(self, impl, pathtype): + self._pathtype = pathtype + if impl: + # We can't access _Impl::_M_size because _Impl is incomplete + # so cast to int* to access the _M_size member at offset zero, + int_type = gdb.lookup_type('int') + cmpt_type = gdb.lookup_type(pathtype + '::_Cmpt') + char_type = gdb.lookup_type('char') + impl = impl.cast(int_type.pointer()) + size = impl.dereference() + #self._capacity = (impl + 1).dereference() + if hasattr(gdb.Type, 'alignof'): + sizeof_Impl = max(2 * int_type.sizeof, cmpt_type.alignof) + else: + sizeof_Impl = 2 * int_type.sizeof + begin = impl.cast(char_type.pointer()) + sizeof_Impl + self._item = begin.cast(cmpt_type.pointer()) + self._finish = self._item + size + self._count = 0 + else: + self._item = None + self._finish = None def __iter__(self): return self def __next__(self): - if self.item == self.finish: + if self._item == self._finish: raise StopIteration - item = self.item.dereference() - count = self.count - self.count = self.count + 1 - self.item = self.item + 1 + item = self._item.dereference() + count = self._count + self._count = self._count + 1 + self._item = self._item + 1 path = item['_M_pathname'] - t = StdExpPathPrinter(self.pathtype, item)._path_type() + t = StdPathPrinter(self._pathtype, item)._path_type() if not t: t = count return ('[%s]' % t, path) def children(self): - return self._iterator(self.val['_M_cmpts'], self.typename) + return self._iterator(self._impl, self._typename) -class StdPairPrinter: - "Print a std::pair object, with 'first' and 'second' as children" +class StdPairPrinter(printer_base): + """Print a std::pair object, with 'first' and 'second' as children.""" def __init__(self, typename, val): - self.val = val + self._val = val class _iter(Iterator): - "An iterator for std::pair types. Returns 'first' then 'second'." + """An iterator for std::pair types. Returns 'first' then 'second'.""" def __init__(self, val): - self.val = val - self.which = 'first' + self._val = val + self._which = 'first' def __iter__(self): return self def __next__(self): - if self.which is None: + if self._which is None: raise StopIteration - which = self.which + which = self._which if which == 'first': - self.which = 'second' + self._which = 'second' else: - self.which = None - return (which, self.val[which]) + self._which = None + return (which, self._val[which]) def children(self): - return self._iter(self.val) + return self._iter(self._val) def to_string(self): return None +class StdCmpCatPrinter(printer_base): + """Print a comparison category object.""" + + def __init__(self, typename, val): + self._typename = typename[typename.rfind(':') + 1:] + self._val = val['_M_value'] + + def to_string(self): + if self._typename == 'strong_ordering' and self._val == 0: + name = 'equal' + else: + names = {2: 'unordered', -1: 'less', 0: 'equivalent', 1: 'greater'} + name = names[int(self._val)] + return 'std::{}::{}'.format(self._typename, name) + + +class StdErrorCodePrinter(printer_base): + """Print a std::error_code or std::error_condition.""" + + _system_is_posix = None # Whether std::system_category() use errno values. + + def __init__(self, typename, val): + self._val = val + self._typename = strip_versioned_namespace(typename) + # Do this only once ... + if StdErrorCodePrinter._system_is_posix is None: + try: + import posix + StdErrorCodePrinter._system_is_posix = True + except ImportError: + StdErrorCodePrinter._system_is_posix = False + + @staticmethod + def _find_errc_enum(name): + typ = gdb.lookup_type(name) + if typ is not None and typ.code == gdb.TYPE_CODE_ENUM: + return typ + return None + + @classmethod + def _find_standard_errc_enum(cls, name): + for ns in ['', _versioned_namespace]: + try: + qname = 'std::{}{}'.format(ns, name) + return cls._find_errc_enum(qname) + except RuntimeError: + pass + + @classmethod + def _match_net_ts_category(cls, cat): + net_cats = ['stream', 'socket', 'ip::resolver'] + for c in net_cats: + func = c + '_category()' + for ns in ['', _versioned_namespace]: + ns = 'std::{}experimental::net::v1'.format(ns) + sym = gdb.lookup_symbol('{}::{}::__c'.format(ns, func))[0] + if sym is not None: + if cat == sym.value().address: + name = 'net::' + func + enum = cls._find_errc_enum('{}::{}_errc'.format(ns, c)) + return (name, enum) + return (None, None) + + @classmethod + def _category_info(cls, cat): + """Return details of a std::error_category.""" + + name = None + enum = None + is_errno = False + + # Try these first, or we get "warning: RTTI symbol not found" when + # using cat.dynamic_type on the local class types for Net TS + # categories. + func, enum = cls._match_net_ts_category(cat) + if func is not None: + return (None, func, enum, is_errno) + + # This might give a warning for a program-defined category defined as + # a local class, but there doesn't seem to be any way to avoid that. + typ = cat.dynamic_type.target() + # Shortcuts for the known categories defined by libstdc++. + if typ.tag.endswith('::generic_error_category'): + name = 'generic' + is_errno = True + if typ.tag.endswith('::system_error_category'): + name = 'system' + is_errno = cls._system_is_posix + if typ.tag.endswith('::future_error_category'): + name = 'future' + enum = cls._find_standard_errc_enum('future_errc') + if typ.tag.endswith('::io_error_category'): + name = 'io' + enum = cls._find_standard_errc_enum('io_errc') + + if name is None: + try: + # Want to call std::error_category::name() override, but it's + # unsafe: https://sourceware.org/bugzilla/show_bug.cgi?id=28856 + # gdb.set_convenience_variable('__cat', cat) + # return '"%s"' % gdb.parse_and_eval('$__cat->name()').string() + pass + except: + pass + return (name, typ.tag, enum, is_errno) + + @staticmethod + def _unqualified_name(name): + """ + Strip any nested-name-specifier from name to give an unqualified name. + """ + return name.split('::')[-1] + + def to_string(self): + value = self._val['_M_value'] + cat = self._val['_M_cat'] + name, alt_name, enum, is_errno = self._category_info(cat) + if value == 0: + default_cats = {'error_code': 'system', + 'error_condition': 'generic'} + if name == default_cats[self._unqualified_name(self._typename)]: + return self._typename + ' = { }' # default-constructed value + + strval = str(value) + if is_errno and value != 0: + try: + strval = errno.errorcode[int(value)] + except: + pass + elif enum is not None: + strval = self._unqualified_name(str(value.cast(enum))) + + if name is not None: + name = '"%s"' % name + else: + name = alt_name + return '%s = {%s: %s}' % (self._typename, name, strval) + + +class StdRegexStatePrinter(printer_base): + """Print a state node in the NFA for a std::regex.""" + + def __init__(self, typename, val): + self._val = val + self._typename = typename + + def to_string(self): + opcode = str(self._val['_M_opcode']) + if opcode: + opcode = opcode[25:] + next_id = self._val['_M_next'] + + variants = {'repeat': 'alt', 'alternative': 'alt', + 'subexpr_begin': 'subexpr', 'subexpr_end': 'subexpr', + 'line_begin_assertion': None, 'line_end_assertion': None, + 'word_boundary': 'neg', 'subexpr_lookahead': 'neg', + 'backref': 'backref_index', + 'match': None, 'accept': None, + 'dummy': None, 'unknown': None + } + v = variants[opcode] + + s = "opcode={}, next={}".format(opcode, next_id) + if v is not None and self._val['_M_' + v] is not None: + s = "{}, {}={}".format(s, v, self._val['_M_' + v]) + return "{%s}" % (s) + + +class StdSpanPrinter(printer_base): + """Print a std::span.""" + + class _iterator(Iterator): + def __init__(self, begin, size): + self._count = 0 + self._begin = begin + self._size = size + + def __iter__(self): + return self + + def __next__(self): + if self._count == self._size: + raise StopIteration + + count = self._count + self._count = self._count + 1 + return '[%d]' % count, (self._begin + count).dereference() + + def __init__(self, typename, val): + self._typename = strip_versioned_namespace(typename) + self._val = val + size_max = gdb.parse_and_eval('static_cast(-1)') + if val.type.template_argument(1) == size_max: + self._size = val['_M_extent']['_M_extent_value'] + else: + self._size = val.type.template_argument(1) + + def to_string(self): + return '%s of length %d' % (self._typename, self._size) + + def children(self): + return self._iterator(self._val['_M_ptr'], self._size) + + def display_hint(self): + return 'array' + + +class StdInitializerListPrinter(printer_base): + """Print a std::initializer_list.""" + + def __init__(self, typename, val): + self._typename = typename + self._val = val + self._size = val['_M_len'] + + def to_string(self): + return '%s of length %d' % (self._typename, self._size) + + def children(self): + return StdSpanPrinter._iterator(self._val['_M_array'], self._size) + + def display_hint(self): + return 'array' + + +class StdAtomicPrinter(printer_base): + """Print a std:atomic.""" + + def __init__(self, typename, val): + self._typename = strip_versioned_namespace(typename) + self._val = val + self._shptr_printer = None + self._value_type = self._val.type.template_argument(0) + if self._value_type.tag is not None: + typ = strip_versioned_namespace(self._value_type.tag) + if (typ.startswith('std::shared_ptr<') + or typ.startswith('std::weak_ptr<')): + impl = val['_M_impl'] + self._shptr_printer = SharedPointerPrinter(typename, impl) + self.children = self._shptr_children + + def _shptr_children(self): + return SmartPtrIterator(self._shptr_printer._pointer) + + def to_string(self): + if self._shptr_printer is not None: + return self._shptr_printer.to_string() + + if self._value_type.code == gdb.TYPE_CODE_INT: + val = self._val['_M_i'] + elif self._value_type.code == gdb.TYPE_CODE_FLT: + val = self._val['_M_fp'] + elif self._value_type.code == gdb.TYPE_CODE_PTR: + val = self._val['_M_b']['_M_p'] + elif self._value_type.code == gdb.TYPE_CODE_BOOL: + val = self._val['_M_base']['_M_i'] + else: + val = self._val['_M_i'] + return '%s<%s> = { %s }' % (self._typename, str(self._value_type), val) + + +class StdFormatArgsPrinter(printer_base): + """Print a std::basic_format_args.""" + # TODO: add printer for basic_format_arg and print out children. + # TODO: add printer for __format::_ArgStore. + + def __init__(self, typename, val): + self._typename = strip_versioned_namespace(typename) + self._val = val + + def to_string(self): + targs = get_template_arg_list(self._val.type) + char_type = get_template_arg_list(targs[0])[1] + if char_type == gdb.lookup_type('char'): + typ = 'std::format_args' + elif char_type == gdb.lookup_type('wchar_t'): + typ = 'std::wformat_args' + else: + typ = 'std::basic_format_args' + + size = self._val['_M_packed_size'] + if size == 1: + return "%s with 1 argument" % (typ) + if size == 0: + size = self._val['_M_unpacked_size'] + return "%s with %d arguments" % (typ, size) + + +class StdChronoDurationPrinter(printer_base): + """Print a std::chrono::duration.""" + + def __init__(self, typename, val): + self._typename = strip_versioned_namespace(typename) + self._val = val + + def _ratio(self): + # TODO use reduced period i.e. duration::period + period = self._val.type.template_argument(1) + num = period.template_argument(0) + den = period.template_argument(1) + return (num, den) + + def _suffix(self): + num, den = self._ratio() + if num == 1: + if den == 1: + return 's' + if den == 1000: + return 'ms' + if den == 1000000: + return 'us' + if den == 1000000000: + return 'ns' + elif den == 1: + if num == 60: + return 'min' + if num == 3600: + return 'h' + if num == 86400: + return 'd' + return '[{}]s'.format(num) + return "[{}/{}]s".format(num, den) + + def to_string(self): + r = self._val['__r'] + if r.type.strip_typedefs().code == gdb.TYPE_CODE_FLT: + r = "%g" % r + return "std::chrono::duration = {{ {}{} }}".format(r, self._suffix()) + + +class StdChronoTimePointPrinter(printer_base): + """Print a std::chrono::time_point.""" + + def __init__(self, typename, val): + self._typename = strip_versioned_namespace(typename) + self._val = val + + def _clock(self): + clock = self._val.type.template_argument(0) + name = strip_versioned_namespace(clock.name) + if name == 'std::chrono::_V2::system_clock' \ + or name == 'std::chrono::system_clock': + return ('std::chrono::sys_time', 0) + # XXX need to remove leap seconds from utc, gps, and tai + if name == 'std::chrono::utc_clock': + return ('std::chrono::utc_time', None) # XXX + if name == 'std::chrono::gps_clock': + return ('std::chrono::gps_time', None) # XXX 315964809 + if name == 'std::chrono::tai_clock': + return ('std::chrono::tai_time', None) # XXX -378691210 + if name == 'std::filesystem::__file_clock': + return ('std::chrono::file_time', 6437664000) + if name == 'std::chrono::local_t': + return ('std::chrono::local_time', 0) + return ('{} time_point'.format(name), None) + + def to_string(self, abbrev=False): + clock, offset = self._clock() + d = self._val['__d'] + r = d['__r'] + printer = StdChronoDurationPrinter(d.type.name, d) + suffix = printer._suffix() + time = '' + if offset is not None: + num, den = printer._ratio() + secs = (r * num / den) + offset + try: + dt = datetime.datetime.fromtimestamp(secs, _utc_timezone) + time = ' [{:%Y-%m-%d %H:%M:%S}]'.format(dt) + except: + pass + s = '%d%s%s' % (r, suffix, time) + if abbrev: + return s + return '%s = { %s }' % (clock, s) + + +class StdChronoZonedTimePrinter(printer_base): + """Print a std::chrono::zoned_time.""" + + def __init__(self, typename, val): + self._typename = strip_versioned_namespace(typename) + self._val = val + + def to_string(self): + zone = self._val['_M_zone'].dereference()['_M_name'] + time = self._val['_M_tp'] + printer = StdChronoTimePointPrinter(time.type.name, time) + time = printer.to_string(True) + return 'std::chrono::zoned_time = {{ {} {} }}'.format(zone, time) + + +months = [None, 'January', 'February', 'March', 'April', 'May', 'June', + 'July', 'August', 'September', 'October', 'November', 'December'] + +weekdays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', + 'Saturday', 'Sunday'] + + +class StdChronoCalendarPrinter(printer_base): + """Print a std::chrono::day, std::chrono::month, std::chrono::year etc.""" + + def __init__(self, typename, val): + self._typename = strip_versioned_namespace(typename) + self._val = val + + def to_string(self): + val = self._val + typ = self._typename + if 'month' in typ and typ != 'std::chrono::year_month_day_last': + m = val['_M_m'] + if typ.startswith('std::chrono::year'): + y = val['_M_y'] + + if typ == 'std::chrono::day': + return '{}'.format(int(val['_M_d'])) + if typ == 'std::chrono::month': + if m < 1 or m >= len(months): + return "%d is not a valid month" % m + return months[m] + if typ == 'std::chrono::year': + return '{}y'.format(y) + if typ == 'std::chrono::weekday': + wd = val['_M_wd'] + if wd < 0 or wd >= len(weekdays): + return "%d is not a valid weekday" % wd + return '{}'.format(weekdays[wd]) + if typ == 'std::chrono::weekday_indexed': + return '{}[{}]'.format(val['_M_wd'], int(val['_M_index'])) + if typ == 'std::chrono::weekday_last': + return '{}[last]'.format(val['_M_wd']) + if typ == 'std::chrono::month_day': + return '{}/{}'.format(m, val['_M_d']) + if typ == 'std::chrono::month_day_last': + return '{}/last'.format(m) + if typ == 'std::chrono::month_weekday': + return '{}/{}'.format(m, val['_M_wdi']) + if typ == 'std::chrono::month_weekday_last': + return '{}/{}'.format(m, val['_M_wdl']) + if typ == 'std::chrono::year_month': + return '{}/{}'.format(y, m) + if typ == 'std::chrono::year_month_day': + return '{}/{}/{}'.format(y, m, val['_M_d']) + if typ == 'std::chrono::year_month_day_last': + return '{}/{}'.format(y, val['_M_mdl']) + if typ == 'std::chrono::year_month_weekday': + return '{}/{}/{}'.format(y, m, val['_M_wdi']) + if typ == 'std::chrono::year_month_weekday_last': + return '{}/{}/{}'.format(y, m, val['_M_wdl']) + if typ.startswith('std::chrono::hh_mm_ss'): + fract = '' + if val['fractional_width'] != 0: + fract = '.{:0{}d}'.format(int(val['_M_ss']['_M_r']), + int(val['fractional_width'])) + h = int(val['_M_h']['__r']) + m = int(val['_M_m']['__r']) + s = int(val['_M_s']['__r']) + if val['_M_is_neg']: + h = -h + return '{:02}:{:02}:{:02}{}'.format(h, m, s, fract) + + +class StdChronoTimeZonePrinter(printer_base): + """Print a chrono::time_zone or chrono::time_zone_link.""" + + def __init__(self, typename, val): + self._typename = strip_versioned_namespace(typename) + self._val = val + + def to_string(self): + str = '%s = %s' % (self._typename, self._val['_M_name']) + if self._typename.endswith("_link"): + str += ' -> %s' % (self._val['_M_target']) + return str + + +class StdChronoLeapSecondPrinter(printer_base): + """Print a chrono::leap_second.""" + + def __init__(self, typename, val): + self._typename = strip_versioned_namespace(typename) + self._val = val + + def to_string(self): + date = self._val['_M_s']['__r'] + neg = '+-'[date < 0] + return '%s %d (%c)' % (self._typename, abs(date), neg) + + +class StdChronoTzdbPrinter(printer_base): + """Print a chrono::tzdb.""" + + def __init__(self, typename, val): + self._typename = strip_versioned_namespace(typename) + self._val = val + + def to_string(self): + return '%s %s' % (self._typename, self._val['version']) + + +class StdChronoTimeZoneRulePrinter(printer_base): + """Print a chrono::time_zone rule.""" + + def __init__(self, typename, val): + self._typename = strip_versioned_namespace(typename) + self._val = val + + def to_string(self): + on = self._val['on'] + kind = on['kind'] + month = months[on['month']] + suffixes = {1: 'st', 2: 'nd', 3: 'rd', + 21: 'st', 22: 'nd', 23: 'rd', 31: 'st'} + day = on['day_of_month'] + ordinal_day = '{}{}'.format(day, suffixes.get(day, 'th')) + if kind == 0: # DayOfMonth + start = '{} {}'.format(month, ordinal_day) + else: + weekday = weekdays[on['day_of_week']] + if kind == 1: # LastWeekDay + start = 'last {} in {}'.format(weekday, month) + else: + if kind == 2: # LessEq + direction = ('last', '<=') + else: + direction = ('first', '>=') + day = on['day_of_month'] + start = '{} {} {} {} {}'.format(direction[0], weekday, + direction[1], month, + ordinal_day) + return 'time_zone rule {} from {} to {} starting on {}'.format( + self._val['name'], self._val['from'], self._val['to'], start) + + +class StdLocalePrinter(printer_base): + """Print a std::locale.""" + + def __init__(self, typename, val): + self._val = val + self._typename = typename + + def to_string(self): + names = self._val['_M_impl']['_M_names'] + mod = '' + if names[0] == 0: + name = '*' + else: + cats = gdb.parse_and_eval(self._typename + '::_S_categories') + ncat = gdb.parse_and_eval(self._typename + '::_S_categories_size') + n = names[0].string() + cat = cats[0].string() + name = '{}={}'.format(cat, n) + cat_names = {cat: n} + i = 1 + while i < ncat and names[i] != 0: + n = names[i].string() + cat = cats[i].string() + name = '{};{}={}'.format(name, cat, n) + cat_names[cat] = n + i = i + 1 + uniq_names = set(cat_names.values()) + if len(uniq_names) == 1: + name = n + elif len(uniq_names) == 2: + n1, n2 = (uniq_names) + name_list = list(cat_names.values()) + other = None + if name_list.count(n1) == 1: + name = n2 + other = n1 + elif name_list.count(n2) == 1: + name = n1 + other = n2 + if other is not None: + cat = next(c for c, n in cat_names.items() if n == other) + mod = ' with "{}={}"'.format(cat, other) + return 'std::locale = "{}"{}'.format(name, mod) + +class StdIntegralConstantPrinter(printer_base): + """Print a std::true_type or std::false_type.""" + + def __init__(self, typename, val): + self._val = val + self._typename = typename + + def to_string(self): + value_type = self._val.type.template_argument(0) + value = self._val.type.template_argument(1) + if value_type.code == gdb.TYPE_CODE_BOOL: + if value: + return "std::true_type" + else: + return "std::false_type" + typename = strip_versioned_namespace(self._typename) + return "{}<{}, {}>".format(typename, value_type, value) + +class StdTextEncodingPrinter(printer_base): + """Print a std::text_encoding.""" + + def __init__(self, typename, val): + self._val = val + self._typename = typename + + def to_string(self): + rep = self._val['_M_rep'].dereference() + if rep['_M_id'] == 1: + return self._val['_M_name'] + if rep['_M_id'] == 2: + return 'unknown' + return rep['_M_name'] + # A "regular expression" printer which conforms to the # "SubPrettyPrinter" protocol from gdb.printing. class RxPrinter(object): def __init__(self, name, function): super(RxPrinter, self).__init__() self.name = name - self.function = function + self._function = function self.enabled = True def invoke(self, value): @@ -1315,36 +2360,40 @@ class RxPrinter(object): return None if value.type.code == gdb.TYPE_CODE_REF: - if hasattr(gdb.Value,"referenced_value"): + if hasattr(gdb.Value, "referenced_value"): value = value.referenced_value() - return self.function(self.name, value) + return self._function(self.name, value) # A pretty-printer that conforms to the "PrettyPrinter" protocol from # gdb.printing. It can also be used directly as an old-style printer. + + class Printer(object): def __init__(self, name): super(Printer, self).__init__() self.name = name - self.subprinters = [] - self.lookup = {} + self._subprinters = [] + self._lookup = {} self.enabled = True - self.compiled_rx = re.compile('^([a-zA-Z0-9_:]+)(<.*>)?$') + self._compiled_rx = re.compile('^([a-zA-Z0-9_:]+)(<.*>)?$') def add(self, name, function): # A small sanity check. # FIXME - if not self.compiled_rx.match(name): - raise ValueError('libstdc++ programming error: "%s" does not match' % name) + if not self._compiled_rx.match(name): + raise ValueError( + 'libstdc++ programming error: "%s" does not match' % name) printer = RxPrinter(name, function) - self.subprinters.append(printer) - self.lookup[name] = printer + self._subprinters.append(printer) + self._lookup[name] = printer # Add a name using _GLIBCXX_BEGIN_NAMESPACE_VERSION. def add_version(self, base, name, function): self.add(base + name, function) - if _versioned_namespace: - vbase = re.sub('^(std|__gnu_cxx)::', r'\g<0>%s' % _versioned_namespace, base) + if '__cxx11' not in base: + vbase = re.sub('^(std|__gnu_cxx)::', r'\g<0>%s' % + _versioned_namespace, base) self.add(vbase + name, function) # Add a name using _GLIBCXX_BEGIN_NAMESPACE_CONTAINER. @@ -1356,10 +2405,10 @@ class Printer(object): def get_basic_type(type): # If it points to a reference, get the reference. if type.code == gdb.TYPE_CODE_REF: - type = type.target () + type = type.target() # Get the unqualified type, stripped of typedefs. - type = type.unqualified ().strip_typedefs () + type = type.unqualified().strip_typedefs() return type.tag @@ -1370,47 +2419,49 @@ class Printer(object): # All the types we match are template types, so we can use a # dictionary. - match = self.compiled_rx.match(typename) + match = self._compiled_rx.match(typename) if not match: return None basename = match.group(1) if val.type.code == gdb.TYPE_CODE_REF: - if hasattr(gdb.Value,"referenced_value"): + if hasattr(gdb.Value, "referenced_value"): val = val.referenced_value() - if basename in self.lookup: - return self.lookup[basename].invoke(val) + if basename in self._lookup: + return self._lookup[basename].invoke(val) # Cannot find a pretty printer. Return None. return None + libstdcxx_printer = None + class TemplateTypePrinter(object): - r""" + """ A type printer for class templates with default template arguments. Recognizes specializations of class templates and prints them without any template arguments that use a default template argument. Type printers are recursively applied to the template arguments. - e.g. replace "std::vector >" with "std::vector". + e.g. replace 'std::vector >' with 'std::vector'. """ def __init__(self, name, defargs): self.name = name - self.defargs = defargs + self._defargs = defargs self.enabled = True class _recognizer(object): - "The recognizer class for TemplateTypePrinter." + """The recognizer class for TemplateTypePrinter.""" def __init__(self, name, defargs): self.name = name - self.defargs = defargs - # self.type_obj = None + self._defargs = defargs + # self._type_obj = None def recognize(self, type_obj): """ @@ -1433,7 +2484,7 @@ class TemplateTypePrinter(object): # The actual template argument in the type: targ = template_args[n] # The default template argument for the class template: - defarg = self.defargs.get(n) + defarg = self._defargs.get(n) if defarg is not None: # Substitute other template arguments into the default: defarg = defarg.format(*template_args) @@ -1470,7 +2521,7 @@ class TemplateTypePrinter(object): if type_obj.code == gdb.TYPE_CODE_ARRAY: type_str = self._recognize_subtype(type_obj.target()) if str(type_obj.strip_typedefs()).endswith('[]'): - return type_str + '[]' # array of unknown bound + return type_str + '[]' # array of unknown bound return "%s[%d]" % (type_str, type_obj.range()[1] + 1) if type_obj.code == gdb.TYPE_CODE_REF: return self._recognize_subtype(type_obj.target()) + '&' @@ -1479,17 +2530,18 @@ class TemplateTypePrinter(object): return self._recognize_subtype(type_obj.target()) + '&&' type_str = gdb.types.apply_type_recognizers( - gdb.types.get_type_recognizers(), type_obj) + gdb.types.get_type_recognizers(), type_obj) if type_str: return type_str return str(type_obj) def instantiate(self): - "Return a recognizer object for this type printer." - return self._recognizer(self.name, self.defargs) + """Return a recognizer object for this type printer.""" + return self._recognizer(self.name, self._defargs) + def add_one_template_type_printer(obj, name, defargs): - r""" + """ Add a type printer for a class template with default template arguments. Args: @@ -1503,78 +2555,121 @@ def add_one_template_type_printer(obj, name, defargs): { 2: 'std::hash<{0}>', 3: 'std::equal_to<{0}>', 4: 'std::allocator >' } - """ - printer = TemplateTypePrinter('std::'+name, defargs) + printer = TemplateTypePrinter('std::' + name, defargs) + gdb.types.register_type_printer(obj, printer) + + # Add type printer for same type in debug namespace: + printer = TemplateTypePrinter('std::__debug::' + name, defargs) gdb.types.register_type_printer(obj, printer) - if _versioned_namespace: + + if '__cxx11' not in name: # Add second type printer for same type in versioned namespace: ns = 'std::' + _versioned_namespace # PR 86112 Cannot use dict comprehension here: - defargs = dict((n, d.replace('std::', ns)) for (n,d) in defargs.items()) - printer = TemplateTypePrinter(ns+name, defargs) + defargs = dict((n, d.replace('std::', ns)) + for (n, d) in defargs.items()) + printer = TemplateTypePrinter(ns + name, defargs) + gdb.types.register_type_printer(obj, printer) + + # Add type printer for same type in debug namespace: + printer = TemplateTypePrinter('std::__debug::' + name, defargs) gdb.types.register_type_printer(obj, printer) + class FilteringTypePrinter(object): - r""" + """ A type printer that uses typedef names for common template specializations. Args: - match (str): The class template to recognize. + template (str): The class template to recognize. name (str): The typedef-name that will be used instead. + targ1 (str, optional): The first template argument. Defaults to None. - Checks if a specialization of the class template 'match' is the same type + Checks if a specialization of the class template 'template' is the same type as the typedef 'name', and prints it as 'name' instead. e.g. if an instantiation of std::basic_istream is the same type as std::istream then print it as std::istream. + + If targ1 is provided (not None), match only template specializations with + this type as the first template argument, e.g. if template='basic_string' + and targ1='char' then only match 'basic_string' and not + 'basic_string'. This rejects non-matching specializations + more quickly, without needing to do GDB type lookups. """ - def __init__(self, match, name): - self.match = match + def __init__(self, template, name, targ1=None): + self._template = template self.name = name + self._targ1 = targ1 self.enabled = True class _recognizer(object): - "The recognizer class for TemplateTypePrinter." + """The recognizer class for FilteringTypePrinter.""" - def __init__(self, match, name): - self.match = match + def __init__(self, template, name, targ1): + self._template = template self.name = name - self.type_obj = None + self._targ1 = targ1 + self._type_obj = None def recognize(self, type_obj): """ - If type_obj starts with self.match and is the same type as + If type_obj starts with self._template and is the same type as self.name then return self.name, otherwise None. """ if type_obj.tag is None: return None - if self.type_obj is None: - if not type_obj.tag.startswith(self.match): + if self._type_obj is None: + if self._targ1 is not None: + s = '{}<{}'.format(self._template, self._targ1) + if not type_obj.tag.startswith(s): + # Filter didn't match. + return None + elif not type_obj.tag.startswith(self._template): # Filter didn't match. return None + try: - self.type_obj = gdb.lookup_type(self.name).strip_typedefs() + self._type_obj = gdb.lookup_type( + self.name).strip_typedefs() except: pass - if self.type_obj == type_obj: + + if self._type_obj is None: + return None + + t1 = gdb.types.get_basic_type(self._type_obj) + t2 = gdb.types.get_basic_type(type_obj) + if t1 == t2: return strip_inline_namespaces(self.name) + + # Workaround ambiguous typedefs matching both std:: and + # std::__cxx11:: symbols. + if self._template.split('::')[-1] == 'basic_string': + s1 = self._type_obj.tag.replace('__cxx11::', '') + s2 = type_obj.tag.replace('__cxx11::', '') + if s1 == s2: + return strip_inline_namespaces(self.name) + return None def instantiate(self): - "Return a recognizer object for this type printer." - return self._recognizer(self.match, self.name) + """Return a recognizer object for this type printer.""" + return self._recognizer(self._template, self.name, self._targ1) -def add_one_type_printer(obj, match, name): - printer = FilteringTypePrinter('std::' + match, 'std::' + name) + +def add_one_type_printer(obj, template, name, targ1=None): + printer = FilteringTypePrinter('std::' + template, 'std::' + name, targ1) gdb.types.register_type_printer(obj, printer) - if _versioned_namespace: + if '__cxx11' not in template: ns = 'std::' + _versioned_namespace - printer = FilteringTypePrinter(ns + match, ns + name) + printer = FilteringTypePrinter(ns + template, ns + name, targ1) gdb.types.register_type_printer(obj, printer) + def register_type_printers(obj): global _use_type_printing @@ -1582,29 +2677,39 @@ def register_type_printers(obj): return # Add type printers for typedefs std::string, std::wstring etc. - for ch in ('', 'w', 'u16', 'u32'): - add_one_type_printer(obj, 'basic_string', ch + 'string') + for ch in (('', 'char'), + ('w', 'wchar_t'), + ('u8', 'char8_t'), + ('u16', 'char16_t'), + ('u32', 'char32_t')): + add_one_type_printer(obj, 'basic_string', ch[0] + 'string', ch[1]) + add_one_type_printer(obj, '__cxx11::basic_string', + ch[0] + 'string', ch[1]) + # Typedefs for __cxx11::basic_string used to be in namespace __cxx11: add_one_type_printer(obj, '__cxx11::basic_string', - '__cxx11::' + ch + 'string') - add_one_type_printer(obj, 'basic_string_view', ch + 'string_view') + '__cxx11::' + ch[0] + 'string', ch[1]) + add_one_type_printer(obj, 'basic_string_view', + ch[0] + 'string_view', ch[1]) # Add type printers for typedefs std::istream, std::wistream etc. - for ch in ('', 'w'): + for ch in (('', 'char'), ('w', 'wchar_t')): for x in ('ios', 'streambuf', 'istream', 'ostream', 'iostream', 'filebuf', 'ifstream', 'ofstream', 'fstream'): - add_one_type_printer(obj, 'basic_' + x, ch + x) + add_one_type_printer(obj, 'basic_' + x, ch[0] + x, ch[1]) for x in ('stringbuf', 'istringstream', 'ostringstream', 'stringstream'): - add_one_type_printer(obj, 'basic_' + x, ch + x) - # types are in __cxx11 namespace, but typedefs aren'x: - add_one_type_printer(obj, '__cxx11::basic_' + x, ch + x) + add_one_type_printer(obj, 'basic_' + x, ch[0] + x, ch[1]) + # types are in __cxx11 namespace, but typedefs aren't: + add_one_type_printer(obj, '__cxx11::basic_' + x, ch[0] + x, ch[1]) # Add type printers for typedefs regex, wregex, cmatch, wcmatch etc. for abi in ('', '__cxx11::'): - for ch in ('', 'w'): - add_one_type_printer(obj, abi + 'basic_regex', abi + ch + 'regex') + for ch in (('', 'char'), ('w', 'wchar_t')): + add_one_type_printer(obj, abi + 'basic_regex', + abi + ch[0] + 'regex', ch[1]) for ch in ('c', 's', 'wc', 'ws'): - add_one_type_printer(obj, abi + 'match_results', abi + ch + 'match') + add_one_type_printer( + obj, abi + 'match_results', abi + ch + 'match') for x in ('sub_match', 'regex_iterator', 'regex_token_iterator'): add_one_type_printer(obj, abi + x, abi + ch + x) @@ -1613,9 +2718,9 @@ def register_type_printers(obj): add_one_type_printer(obj, 'fpos', 'streampos') # Add type printers for typedefs. - for dur in ('nanoseconds', 'microseconds', 'milliseconds', - 'seconds', 'minutes', 'hours'): - add_one_type_printer(obj, 'duration', dur) + for dur in ('nanoseconds', 'microseconds', 'milliseconds', 'seconds', + 'minutes', 'hours', 'days', 'weeks', 'years', 'months'): + add_one_type_printer(obj, 'chrono::duration', 'chrono::' + dur) # Add type printers for typedefs. add_one_type_printer(obj, 'linear_congruential_engine', 'minstd_rand0') @@ -1630,47 +2735,54 @@ def register_type_printers(obj): # Add type printers for experimental::basic_string_view typedefs. ns = 'experimental::fundamentals_v1::' - for ch in ('', 'w', 'u16', 'u32'): + for ch in (('', 'char'), + ('w', 'wchar_t'), + ('u8', 'char8_t'), + ('u16', 'char16_t'), + ('u32', 'char32_t')): add_one_type_printer(obj, ns + 'basic_string_view', - ns + ch + 'string_view') + ns + ch[0] + 'string_view', ch[1]) # Do not show defaulted template arguments in class templates. add_one_template_type_printer(obj, 'unique_ptr', - { 1: 'std::default_delete<{0}>' }) - add_one_template_type_printer(obj, 'deque', { 1: 'std::allocator<{0}>'}) - add_one_template_type_printer(obj, 'forward_list', { 1: 'std::allocator<{0}>'}) - add_one_template_type_printer(obj, 'list', { 1: 'std::allocator<{0}>'}) - add_one_template_type_printer(obj, '__cxx11::list', { 1: 'std::allocator<{0}>'}) - add_one_template_type_printer(obj, 'vector', { 1: 'std::allocator<{0}>'}) + {1: 'std::default_delete<{0}>'}) + add_one_template_type_printer(obj, 'deque', {1: 'std::allocator<{0}>'}) + add_one_template_type_printer( + obj, 'forward_list', {1: 'std::allocator<{0}>'}) + add_one_template_type_printer(obj, 'list', {1: 'std::allocator<{0}>'}) + add_one_template_type_printer( + obj, '__cxx11::list', {1: 'std::allocator<{0}>'}) + add_one_template_type_printer(obj, 'vector', {1: 'std::allocator<{0}>'}) add_one_template_type_printer(obj, 'map', - { 2: 'std::less<{0}>', - 3: 'std::allocator>' }) + {2: 'std::less<{0}>', + 3: 'std::allocator>'}) add_one_template_type_printer(obj, 'multimap', - { 2: 'std::less<{0}>', - 3: 'std::allocator>' }) + {2: 'std::less<{0}>', + 3: 'std::allocator>'}) add_one_template_type_printer(obj, 'set', - { 1: 'std::less<{0}>', 2: 'std::allocator<{0}>' }) + {1: 'std::less<{0}>', 2: 'std::allocator<{0}>'}) add_one_template_type_printer(obj, 'multiset', - { 1: 'std::less<{0}>', 2: 'std::allocator<{0}>' }) + {1: 'std::less<{0}>', 2: 'std::allocator<{0}>'}) add_one_template_type_printer(obj, 'unordered_map', - { 2: 'std::hash<{0}>', - 3: 'std::equal_to<{0}>', - 4: 'std::allocator>'}) + {2: 'std::hash<{0}>', + 3: 'std::equal_to<{0}>', + 4: 'std::allocator>'}) add_one_template_type_printer(obj, 'unordered_multimap', - { 2: 'std::hash<{0}>', - 3: 'std::equal_to<{0}>', - 4: 'std::allocator>'}) + {2: 'std::hash<{0}>', + 3: 'std::equal_to<{0}>', + 4: 'std::allocator>'}) add_one_template_type_printer(obj, 'unordered_set', - { 1: 'std::hash<{0}>', - 2: 'std::equal_to<{0}>', - 3: 'std::allocator<{0}>'}) + {1: 'std::hash<{0}>', + 2: 'std::equal_to<{0}>', + 3: 'std::allocator<{0}>'}) add_one_template_type_printer(obj, 'unordered_multiset', - { 1: 'std::hash<{0}>', - 2: 'std::equal_to<{0}>', - 3: 'std::allocator<{0}>'}) + {1: 'std::hash<{0}>', + 2: 'std::equal_to<{0}>', + 3: 'std::allocator<{0}>'}) -def register_libstdcxx_printers (obj): - "Register libstdc++ pretty-printers with objfile Obj." + +def register_libstdcxx_printers(obj): + """Register libstdc++ pretty-printers with objfile Obj.""" global _use_gdb_pp global libstdcxx_printer @@ -1684,7 +2796,8 @@ def register_libstdcxx_printers (obj): register_type_printers(obj) -def build_libstdcxx_dictionary (): + +def build_libstdcxx_dictionary(): global libstdcxx_printer libstdcxx_printer = Printer("libstdc++-v6") @@ -1693,7 +2806,8 @@ def build_libstdcxx_dictionary (): # In order from: # http://gcc.gnu.org/onlinedocs/libstdc++/latest-doxygen/a01847.html libstdcxx_printer.add_version('std::', 'basic_string', StdStringPrinter) - libstdcxx_printer.add_version('std::__cxx11::', 'basic_string', StdStringPrinter) + libstdcxx_printer.add_version( + 'std::__cxx11::', 'basic_string', StdStringPrinter) libstdcxx_printer.add_container('std::', 'bitset', StdBitsetPrinter) libstdcxx_printer.add_container('std::', 'deque', StdDequePrinter) libstdcxx_printer.add_container('std::', 'list', StdListPrinter) @@ -1711,6 +2825,18 @@ def build_libstdcxx_dictionary (): libstdcxx_printer.add_version('std::', 'unique_ptr', UniquePointerPrinter) libstdcxx_printer.add_container('std::', 'vector', StdVectorPrinter) # vector + libstdcxx_printer.add_version('std::', 'locale', StdLocalePrinter) + + libstdcxx_printer.add_version('std::', 'integral_constant', + StdIntegralConstantPrinter) + libstdcxx_printer.add_version('std::', 'text_encoding', + StdTextEncodingPrinter) + + if hasattr(gdb.Value, 'dynamic_type'): + libstdcxx_printer.add_version('std::', 'error_code', + StdErrorCodePrinter) + libstdcxx_printer.add_version('std::', 'error_condition', + StdErrorCodePrinter) # Printer registrations for classes compiled with -D_GLIBCXX_DEBUG. libstdcxx_printer.add('std::__debug::bitset', StdBitsetPrinter) @@ -1719,12 +2845,7 @@ def build_libstdcxx_dictionary (): libstdcxx_printer.add('std::__debug::map', StdMapPrinter) libstdcxx_printer.add('std::__debug::multimap', StdMapPrinter) libstdcxx_printer.add('std::__debug::multiset', StdSetPrinter) - libstdcxx_printer.add('std::__debug::priority_queue', - StdStackOrQueuePrinter) - libstdcxx_printer.add('std::__debug::queue', StdStackOrQueuePrinter) libstdcxx_printer.add('std::__debug::set', StdSetPrinter) - libstdcxx_printer.add('std::__debug::stack', StdStackOrQueuePrinter) - libstdcxx_printer.add('std::__debug::unique_ptr', UniquePointerPrinter) libstdcxx_printer.add('std::__debug::vector', StdVectorPrinter) # These are the TR1 and C++11 printers. @@ -1742,8 +2863,10 @@ def build_libstdcxx_dictionary (): libstdcxx_printer.add_container('std::', 'forward_list', StdForwardListPrinter) - libstdcxx_printer.add_version('std::tr1::', 'shared_ptr', SharedPointerPrinter) - libstdcxx_printer.add_version('std::tr1::', 'weak_ptr', SharedPointerPrinter) + libstdcxx_printer.add_version( + 'std::tr1::', 'shared_ptr', SharedPointerPrinter) + libstdcxx_printer.add_version( + 'std::tr1::', 'weak_ptr', SharedPointerPrinter) libstdcxx_printer.add_version('std::tr1::', 'unordered_map', Tr1UnorderedMapPrinter) libstdcxx_printer.add_version('std::tr1::', 'unordered_set', @@ -1753,6 +2876,28 @@ def build_libstdcxx_dictionary (): libstdcxx_printer.add_version('std::tr1::', 'unordered_multiset', Tr1UnorderedSetPrinter) + libstdcxx_printer.add_version('std::', 'initializer_list', + StdInitializerListPrinter) + libstdcxx_printer.add_version('std::', 'atomic', StdAtomicPrinter) + libstdcxx_printer.add_version( + 'std::', 'basic_stringbuf', StdStringBufPrinter) + libstdcxx_printer.add_version( + 'std::__cxx11::', 'basic_stringbuf', StdStringBufPrinter) + for sstream in ('istringstream', 'ostringstream', 'stringstream'): + libstdcxx_printer.add_version( + 'std::', 'basic_' + sstream, StdStringStreamPrinter) + libstdcxx_printer.add_version( + 'std::__cxx11::', 'basic_' + sstream, StdStringStreamPrinter) + + libstdcxx_printer.add_version('std::chrono::', 'duration', + StdChronoDurationPrinter) + libstdcxx_printer.add_version('std::chrono::', 'time_point', + StdChronoTimePointPrinter) + + # std::regex components + libstdcxx_printer.add_version('std::__detail::', '_State', + StdRegexStatePrinter) + # These are the C++11 printer registrations for -D_GLIBCXX_DEBUG cases. # The tr1 namespace containers do not have any debug equivalents, # so do not register printers for them. @@ -1780,9 +2925,9 @@ def build_libstdcxx_dictionary (): libstdcxx_printer.add_version('std::experimental::filesystem::v1::__cxx11::', 'path', StdExpPathPrinter) libstdcxx_printer.add_version('std::filesystem::', - 'path', StdExpPathPrinter) + 'path', StdPathPrinter) libstdcxx_printer.add_version('std::filesystem::__cxx11::', - 'path', StdExpPathPrinter) + 'path', StdPathPrinter) # C++17 components libstdcxx_printer.add_version('std::', @@ -1796,6 +2941,33 @@ def build_libstdcxx_dictionary (): libstdcxx_printer.add_version('std::', '_Node_handle', StdNodeHandlePrinter) + # C++20 components + libstdcxx_printer.add_version( + 'std::', 'partial_ordering', StdCmpCatPrinter) + libstdcxx_printer.add_version('std::', 'weak_ordering', StdCmpCatPrinter) + libstdcxx_printer.add_version('std::', 'strong_ordering', StdCmpCatPrinter) + libstdcxx_printer.add_version('std::', 'span', StdSpanPrinter) + libstdcxx_printer.add_version('std::', 'basic_format_args', + StdFormatArgsPrinter) + for c in ['day', 'month', 'year', 'weekday', 'weekday_indexed', 'weekday_last', + 'month_day', 'month_day_last', 'month_weekday', 'month_weekday_last', + 'year_month', 'year_month_day', 'year_month_day_last', + 'year_month_weekday', 'year_month_weekday_last', 'hh_mm_ss']: + libstdcxx_printer.add_version('std::chrono::', c, + StdChronoCalendarPrinter) + libstdcxx_printer.add_version('std::chrono::', 'time_zone', + StdChronoTimeZonePrinter) + libstdcxx_printer.add_version('std::chrono::', 'time_zone_link', + StdChronoTimeZonePrinter) + libstdcxx_printer.add_version('std::chrono::', 'zoned_time', + StdChronoZonedTimePrinter) + libstdcxx_printer.add_version('std::chrono::', 'leap_second', + StdChronoLeapSecondPrinter) + libstdcxx_printer.add_version( + 'std::chrono::', 'tzdb', StdChronoTzdbPrinter) + # libstdcxx_printer.add_version('std::chrono::(anonymous namespace)', 'Rule', + # StdChronoTimeZoneRulePrinter) + # Extensions. libstdcxx_printer.add_version('__gnu_cxx::', 'slist', StdSlistPrinter) @@ -1816,6 +2988,12 @@ def build_libstdcxx_dictionary (): StdDequeIteratorPrinter) libstdcxx_printer.add_version('__gnu_cxx::', '__normal_iterator', StdVectorIteratorPrinter) + libstdcxx_printer.add_container('std::', '_Bit_iterator', + StdBitIteratorPrinter) + libstdcxx_printer.add_container('std::', '_Bit_const_iterator', + StdBitIteratorPrinter) + libstdcxx_printer.add_container('std::', '_Bit_reference', + StdBitReferencePrinter) libstdcxx_printer.add_version('__gnu_cxx::', '_Slist_iterator', StdSlistIteratorPrinter) libstdcxx_printer.add_container('std::', '_Fwd_list_iterator', @@ -1828,4 +3006,5 @@ def build_libstdcxx_dictionary (): libstdcxx_printer.add('__gnu_debug::_Safe_iterator', StdDebugIteratorPrinter) -build_libstdcxx_dictionary () + +build_libstdcxx_dictionary() diff --git a/libstdc++-v3/python/libstdcxx/v6/xmethods.py b/libstdc++-v3/python/libstdcxx/v6/xmethods.py index 12fefdb041c..436c866e001 100644 --- a/libstdc++-v3/python/libstdcxx/v6/xmethods.py +++ b/libstdc++-v3/python/libstdcxx/v6/xmethods.py @@ -1,6 +1,6 @@ # Xmethods for libstdc++. -# Copyright (C) 2014-2018 Free Software Foundation, Inc. +# Copyright (C) 2014-2024 Free Software Foundation, Inc. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -21,12 +21,27 @@ import re matcher_name_prefix = 'libstdc++::' + def get_bool_type(): return gdb.lookup_type('bool') def get_std_size_type(): return gdb.lookup_type('std::size_t') +_versioned_namespace = '__8::' + +def is_specialization_of(x, template_name): + """ + Test whether a type is a specialization of the named class template. + The type can be specified as a string or a gdb.Type object. + The template should be the name of a class template as a string, + without any 'std' qualification. + """ + if isinstance(x, gdb.Type): + x = x.tag + template_name = '(%s)?%s' % (_versioned_namespace, template_name) + return re.match(r'^std::(__\d::)?%s<.*>$' % template_name, x) is not None + class LibStdCxxXMethod(gdb.xmethod.XMethod): def __init__(self, name, worker_class): gdb.xmethod.XMethod.__init__(self, name) @@ -34,6 +49,7 @@ class LibStdCxxXMethod(gdb.xmethod.XMethod): # Xmethods for std::array + class ArrayWorkerBase(gdb.xmethod.XMethodWorker): def __init__(self, val_type, size): self._val_type = val_type @@ -43,6 +59,7 @@ class ArrayWorkerBase(gdb.xmethod.XMethodWorker): nullptr = gdb.parse_and_eval('(void *) 0') return nullptr.cast(self._val_type.pointer()).dereference() + class ArraySizeWorker(ArrayWorkerBase): def __init__(self, val_type, size): ArrayWorkerBase.__init__(self, val_type, size) @@ -56,6 +73,7 @@ class ArraySizeWorker(ArrayWorkerBase): def __call__(self, obj): return self._size + class ArrayEmptyWorker(ArrayWorkerBase): def __init__(self, val_type, size): ArrayWorkerBase.__init__(self, val_type, size) @@ -69,6 +87,7 @@ class ArrayEmptyWorker(ArrayWorkerBase): def __call__(self, obj): return (int(self._size) == 0) + class ArrayFrontWorker(ArrayWorkerBase): def __init__(self, val_type, size): ArrayWorkerBase.__init__(self, val_type, size) @@ -85,6 +104,7 @@ class ArrayFrontWorker(ArrayWorkerBase): else: return self.null_value() + class ArrayBackWorker(ArrayWorkerBase): def __init__(self, val_type, size): ArrayWorkerBase.__init__(self, val_type, size) @@ -101,6 +121,7 @@ class ArrayBackWorker(ArrayWorkerBase): else: return self.null_value() + class ArrayAtWorker(ArrayWorkerBase): def __init__(self, val_type, size): ArrayWorkerBase.__init__(self, val_type, size) @@ -117,6 +138,7 @@ class ArrayAtWorker(ArrayWorkerBase): ((int(index), self._size))) return obj['_M_elems'][index] + class ArraySubscriptWorker(ArrayWorkerBase): def __init__(self, val_type, size): ArrayWorkerBase.__init__(self, val_type, size) @@ -133,6 +155,7 @@ class ArraySubscriptWorker(ArrayWorkerBase): else: return self.null_value() + class ArrayMethodsMatcher(gdb.xmethod.XMethodMatcher): def __init__(self): gdb.xmethod.XMethodMatcher.__init__(self, @@ -148,7 +171,7 @@ class ArrayMethodsMatcher(gdb.xmethod.XMethodMatcher): self.methods = [self._method_dict[m] for m in self._method_dict] def match(self, class_type, method_name): - if not re.match('^std::(__\d+::)?array<.*>$', class_type.tag): + if not is_specialization_of(class_type, 'array'): return None method = self._method_dict.get(method_name) if method is None or not method.enabled: @@ -160,24 +183,34 @@ class ArrayMethodsMatcher(gdb.xmethod.XMethodMatcher): return None return method.worker_class(value_type, size) + # Xmethods for std::deque + class DequeWorkerBase(gdb.xmethod.XMethodWorker): def __init__(self, val_type): self._val_type = val_type self._bufsize = 512 // val_type.sizeof or 1 def size(self, obj): - first_node = obj['_M_impl']['_M_start']['_M_node'] - last_node = obj['_M_impl']['_M_finish']['_M_node'] - cur = obj['_M_impl']['_M_finish']['_M_cur'] - first = obj['_M_impl']['_M_finish']['_M_first'] - return (last_node - first_node) * self._bufsize + (cur - first) + start = obj['_M_impl']['_M_start'] + finish = obj['_M_impl']['_M_finish'] + if start['_M_cur'] == finish['_M_cur']: + return 0 + return (self._bufsize + * (finish['_M_node'] - start['_M_node'] - 1) + + (finish['_M_cur'] - finish['_M_first']) + + (start['_M_last'] - start['_M_cur'])) def index(self, obj, idx): - first_node = obj['_M_impl']['_M_start']['_M_node'] - index_node = first_node + int(idx) // self._bufsize - return index_node[0][idx % self._bufsize] + start = obj['_M_impl']['_M_start'] + first_node_size = start['_M_last'] - start['_M_cur'] + if idx < first_node_size: + return start['_M_cur'][idx] + idx = idx - first_node_size + index_node = start['_M_node'][1 + int(idx) // self._bufsize] + return index_node[idx % self._bufsize] + class DequeEmptyWorker(DequeWorkerBase): def get_arg_types(self): @@ -190,6 +223,7 @@ class DequeEmptyWorker(DequeWorkerBase): return (obj['_M_impl']['_M_start']['_M_cur'] == obj['_M_impl']['_M_finish']['_M_cur']) + class DequeSizeWorker(DequeWorkerBase): def get_arg_types(self): return None @@ -200,6 +234,7 @@ class DequeSizeWorker(DequeWorkerBase): def __call__(self, obj): return self.size(obj) + class DequeFrontWorker(DequeWorkerBase): def get_arg_types(self): return None @@ -210,6 +245,7 @@ class DequeFrontWorker(DequeWorkerBase): def __call__(self, obj): return obj['_M_impl']['_M_start']['_M_cur'][0] + class DequeBackWorker(DequeWorkerBase): def get_arg_types(self): return None @@ -219,12 +255,13 @@ class DequeBackWorker(DequeWorkerBase): def __call__(self, obj): if (obj['_M_impl']['_M_finish']['_M_cur'] == - obj['_M_impl']['_M_finish']['_M_first']): + obj['_M_impl']['_M_finish']['_M_first']): prev_node = obj['_M_impl']['_M_finish']['_M_node'] - 1 return prev_node[0][self._bufsize - 1] else: return obj['_M_impl']['_M_finish']['_M_cur'][-1] + class DequeSubscriptWorker(DequeWorkerBase): def get_arg_types(self): return get_std_size_type() @@ -235,6 +272,7 @@ class DequeSubscriptWorker(DequeWorkerBase): def __call__(self, obj, subscript): return self.index(obj, subscript) + class DequeAtWorker(DequeWorkerBase): def get_arg_types(self): return get_std_size_type() @@ -248,7 +286,8 @@ class DequeAtWorker(DequeWorkerBase): raise IndexError('Deque index "%d" should not be >= %d.' % (int(index), deque_size)) else: - return self.index(obj, index) + return self.index(obj, index) + class DequeMethodsMatcher(gdb.xmethod.XMethodMatcher): def __init__(self): @@ -265,7 +304,7 @@ class DequeMethodsMatcher(gdb.xmethod.XMethodMatcher): self.methods = [self._method_dict[m] for m in self._method_dict] def match(self, class_type, method_name): - if not re.match('^std::(__\d+::)?deque<.*>$', class_type.tag): + if not is_specialization_of(class_type, 'deque'): return None method = self._method_dict.get(method_name) if method is None or not method.enabled: @@ -274,6 +313,7 @@ class DequeMethodsMatcher(gdb.xmethod.XMethodMatcher): # Xmethods for std::forward_list + class ForwardListWorkerBase(gdb.xmethod.XMethodMatcher): def __init__(self, val_type, node_type): self._val_type = val_type @@ -282,6 +322,7 @@ class ForwardListWorkerBase(gdb.xmethod.XMethodMatcher): def get_arg_types(self): return None + class ForwardListEmptyWorker(ForwardListWorkerBase): def get_result_type(self, obj): return get_bool_type() @@ -289,6 +330,7 @@ class ForwardListEmptyWorker(ForwardListWorkerBase): def __call__(self, obj): return obj['_M_impl']['_M_head']['_M_next'] == 0 + class ForwardListFrontWorker(ForwardListWorkerBase): def get_result_type(self, obj): return self._val_type @@ -298,6 +340,7 @@ class ForwardListFrontWorker(ForwardListWorkerBase): val_address = node['_M_storage']['_M_storage'].address return val_address.cast(self._val_type.pointer()).dereference() + class ForwardListMethodsMatcher(gdb.xmethod.XMethodMatcher): def __init__(self): matcher_name = matcher_name_prefix + 'forward_list' @@ -309,7 +352,7 @@ class ForwardListMethodsMatcher(gdb.xmethod.XMethodMatcher): self.methods = [self._method_dict[m] for m in self._method_dict] def match(self, class_type, method_name): - if not re.match('^std::(__\d+::)?forward_list<.*>$', class_type.tag): + if not is_specialization_of(class_type, 'forward_list'): return None method = self._method_dict.get(method_name) if method is None or not method.enabled: @@ -320,6 +363,7 @@ class ForwardListMethodsMatcher(gdb.xmethod.XMethodMatcher): # Xmethods for std::list + class ListWorkerBase(gdb.xmethod.XMethodWorker): def __init__(self, val_type, node_type): self._val_type = val_type @@ -337,6 +381,7 @@ class ListWorkerBase(gdb.xmethod.XMethodWorker): addr = node['_M_storage'].address return addr.cast(self._val_type.pointer()).dereference() + class ListEmptyWorker(ListWorkerBase): def get_result_type(self, obj): return get_bool_type() @@ -348,6 +393,7 @@ class ListEmptyWorker(ListWorkerBase): else: return False + class ListSizeWorker(ListWorkerBase): def get_result_type(self, obj): return get_std_size_type() @@ -361,6 +407,7 @@ class ListSizeWorker(ListWorkerBase): size += 1 return size + class ListFrontWorker(ListWorkerBase): def get_result_type(self, obj): return self._val_type @@ -369,6 +416,7 @@ class ListFrontWorker(ListWorkerBase): node = obj['_M_impl']['_M_node']['_M_next'].cast(self._node_type) return self.get_value_from_node(node) + class ListBackWorker(ListWorkerBase): def get_result_type(self, obj): return self._val_type @@ -377,6 +425,7 @@ class ListBackWorker(ListWorkerBase): prev_node = obj['_M_impl']['_M_node']['_M_prev'].cast(self._node_type) return self.get_value_from_node(prev_node) + class ListMethodsMatcher(gdb.xmethod.XMethodMatcher): def __init__(self): gdb.xmethod.XMethodMatcher.__init__(self, @@ -390,7 +439,7 @@ class ListMethodsMatcher(gdb.xmethod.XMethodMatcher): self.methods = [self._method_dict[m] for m in self._method_dict] def match(self, class_type, method_name): - if not re.match('^std::(__\d+::)?(__cxx11::)?list<.*>$', class_type.tag): + if not is_specialization_of(class_type, '(__cxx11::)?list'): return None method = self._method_dict.get(method_name) if method is None or not method.enabled: @@ -401,6 +450,7 @@ class ListMethodsMatcher(gdb.xmethod.XMethodMatcher): # Xmethods for std::vector + class VectorWorkerBase(gdb.xmethod.XMethodWorker): def __init__(self, val_type): self._val_type = val_type @@ -425,6 +475,7 @@ class VectorWorkerBase(gdb.xmethod.XMethodWorker): else: return obj['_M_impl']['_M_start'][index] + class VectorEmptyWorker(VectorWorkerBase): def get_arg_types(self): return None @@ -435,6 +486,7 @@ class VectorEmptyWorker(VectorWorkerBase): def __call__(self, obj): return int(self.size(obj)) == 0 + class VectorSizeWorker(VectorWorkerBase): def get_arg_types(self): return None @@ -445,6 +497,7 @@ class VectorSizeWorker(VectorWorkerBase): def __call__(self, obj): return self.size(obj) + class VectorFrontWorker(VectorWorkerBase): def get_arg_types(self): return None @@ -455,6 +508,7 @@ class VectorFrontWorker(VectorWorkerBase): def __call__(self, obj): return self.get(obj, 0) + class VectorBackWorker(VectorWorkerBase): def get_arg_types(self): return None @@ -465,6 +519,7 @@ class VectorBackWorker(VectorWorkerBase): def __call__(self, obj): return self.get(obj, int(self.size(obj)) - 1) + class VectorAtWorker(VectorWorkerBase): def get_arg_types(self): return get_std_size_type() @@ -479,6 +534,7 @@ class VectorAtWorker(VectorWorkerBase): ((int(index), size))) return self.get(obj, int(index)) + class VectorSubscriptWorker(VectorWorkerBase): def get_arg_types(self): return get_std_size_type() @@ -489,6 +545,7 @@ class VectorSubscriptWorker(VectorWorkerBase): def __call__(self, obj, subscript): return self.get(obj, int(subscript)) + class VectorMethodsMatcher(gdb.xmethod.XMethodMatcher): def __init__(self): gdb.xmethod.XMethodMatcher.__init__(self, @@ -505,7 +562,7 @@ class VectorMethodsMatcher(gdb.xmethod.XMethodMatcher): self.methods = [self._method_dict[m] for m in self._method_dict] def match(self, class_type, method_name): - if not re.match('^std::(__\d+::)?vector<.*>$', class_type.tag): + if not is_specialization_of(class_type, 'vector'): return None method = self._method_dict.get(method_name) if method is None or not method.enabled: @@ -514,6 +571,7 @@ class VectorMethodsMatcher(gdb.xmethod.XMethodMatcher): # Xmethods for associative containers + class AssociativeContainerWorkerBase(gdb.xmethod.XMethodWorker): def __init__(self, unordered): self._unordered = unordered @@ -527,6 +585,7 @@ class AssociativeContainerWorkerBase(gdb.xmethod.XMethodWorker): def get_arg_types(self): return None + class AssociativeContainerEmptyWorker(AssociativeContainerWorkerBase): def get_result_type(self, obj): return get_bool_type() @@ -534,6 +593,7 @@ class AssociativeContainerEmptyWorker(AssociativeContainerWorkerBase): def __call__(self, obj): return int(self.node_count(obj)) == 0 + class AssociativeContainerSizeWorker(AssociativeContainerWorkerBase): def get_result_type(self, obj): return get_std_size_type() @@ -541,6 +601,7 @@ class AssociativeContainerSizeWorker(AssociativeContainerWorkerBase): def __call__(self, obj): return self.node_count(obj) + class AssociativeContainerMethodsMatcher(gdb.xmethod.XMethodMatcher): def __init__(self, name): gdb.xmethod.XMethodMatcher.__init__(self, @@ -554,7 +615,7 @@ class AssociativeContainerMethodsMatcher(gdb.xmethod.XMethodMatcher): self.methods = [self._method_dict[m] for m in self._method_dict] def match(self, class_type, method_name): - if not re.match('^std::(__\d+::)?%s<.*>$' % self._name, class_type.tag): + if not is_specialization_of(class_type, self._name): return None method = self._method_dict.get(method_name) if method is None or not method.enabled: @@ -564,8 +625,11 @@ class AssociativeContainerMethodsMatcher(gdb.xmethod.XMethodMatcher): # Xmethods for std::unique_ptr + class UniquePtrGetWorker(gdb.xmethod.XMethodWorker): - "Implements std::unique_ptr::get() and std::unique_ptr::operator->()" + """ + Implement std::unique_ptr::get() and std::unique_ptr::operator->(). + """ def __init__(self, elem_type): self._is_array = elem_type.code == gdb.TYPE_CODE_ARRAY @@ -581,19 +645,31 @@ class UniquePtrGetWorker(gdb.xmethod.XMethodWorker): return self._elem_type.pointer() def _supports(self, method_name): - "operator-> is not supported for unique_ptr" + # operator-> is not supported for unique_ptr return method_name == 'get' or not self._is_array def __call__(self, obj): impl_type = obj.dereference().type.fields()[0].type.tag - if re.match('^std::(__\d+::)?__uniq_ptr_impl<.*>$', impl_type): # New implementation - return obj['_M_t']['_M_t']['_M_head_impl'] - elif re.match('^std::(__\d+::)?tuple<.*>$', impl_type): - return obj['_M_t']['_M_head_impl'] - return None + # Check for new implementations first: + if is_specialization_of(impl_type, '__uniq_ptr_(data|impl)'): + tuple_member = obj['_M_t']['_M_t'] + elif is_specialization_of(impl_type, 'tuple'): + tuple_member = obj['_M_t'] + else: + return None + tuple_impl_type = tuple_member.type.fields()[0].type # _Tuple_impl + tuple_head_type = tuple_impl_type.fields()[1].type # _Head_base + head_field = tuple_head_type.fields()[0] + if head_field.name == '_M_head_impl': + return tuple_member.cast(tuple_head_type)['_M_head_impl'] + elif head_field.is_base_class: + return tuple_member.cast(head_field.type) + else: + return None + class UniquePtrDerefWorker(UniquePtrGetWorker): - "Implements std::unique_ptr::operator*()" + """Implement std::unique_ptr::operator*().""" def __init__(self, elem_type): UniquePtrGetWorker.__init__(self, elem_type) @@ -602,14 +678,15 @@ class UniquePtrDerefWorker(UniquePtrGetWorker): return self._elem_type def _supports(self, method_name): - "operator* is not supported for unique_ptr" + # operator* is not supported for unique_ptr return not self._is_array def __call__(self, obj): return UniquePtrGetWorker.__call__(self, obj).dereference() + class UniquePtrSubscriptWorker(UniquePtrGetWorker): - "Implements std::unique_ptr::operator[](size_t)" + """Implement std::unique_ptr::operator[](size_t).""" def __init__(self, elem_type): UniquePtrGetWorker.__init__(self, elem_type) @@ -621,12 +698,13 @@ class UniquePtrSubscriptWorker(UniquePtrGetWorker): return self._elem_type def _supports(self, method_name): - "operator[] is only supported for unique_ptr" + # operator[] is only supported for unique_ptr return self._is_array def __call__(self, obj, index): return UniquePtrGetWorker.__call__(self, obj)[index] + class UniquePtrMethodsMatcher(gdb.xmethod.XMethodMatcher): def __init__(self): gdb.xmethod.XMethodMatcher.__init__(self, @@ -640,7 +718,7 @@ class UniquePtrMethodsMatcher(gdb.xmethod.XMethodMatcher): self.methods = [self._method_dict[m] for m in self._method_dict] def match(self, class_type, method_name): - if not re.match('^std::(__\d+::)?unique_ptr<.*>$', class_type.tag): + if not is_specialization_of(class_type, 'unique_ptr'): return None method = self._method_dict.get(method_name) if method is None or not method.enabled: @@ -652,8 +730,11 @@ class UniquePtrMethodsMatcher(gdb.xmethod.XMethodMatcher): # Xmethods for std::shared_ptr + class SharedPtrGetWorker(gdb.xmethod.XMethodWorker): - "Implements std::shared_ptr::get() and std::shared_ptr::operator->()" + """ + Implements std::shared_ptr::get() and std::shared_ptr::operator->(). + """ def __init__(self, elem_type): self._is_array = elem_type.code == gdb.TYPE_CODE_ARRAY @@ -669,14 +750,15 @@ class SharedPtrGetWorker(gdb.xmethod.XMethodWorker): return self._elem_type.pointer() def _supports(self, method_name): - "operator-> is not supported for shared_ptr" + # operator-> is not supported for shared_ptr return method_name == 'get' or not self._is_array def __call__(self, obj): return obj['_M_ptr'] + class SharedPtrDerefWorker(SharedPtrGetWorker): - "Implements std::shared_ptr::operator*()" + """Implement std::shared_ptr::operator*().""" def __init__(self, elem_type): SharedPtrGetWorker.__init__(self, elem_type) @@ -685,14 +767,15 @@ class SharedPtrDerefWorker(SharedPtrGetWorker): return self._elem_type def _supports(self, method_name): - "operator* is not supported for shared_ptr" + # operator* is not supported for shared_ptr return not self._is_array def __call__(self, obj): return SharedPtrGetWorker.__call__(self, obj).dereference() + class SharedPtrSubscriptWorker(SharedPtrGetWorker): - "Implements std::shared_ptr::operator[](size_t)" + """Implement std::shared_ptr::operator[](size_t).""" def __init__(self, elem_type): SharedPtrGetWorker.__init__(self, elem_type) @@ -704,22 +787,23 @@ class SharedPtrSubscriptWorker(SharedPtrGetWorker): return self._elem_type def _supports(self, method_name): - "operator[] is only supported for shared_ptr" + # operator[] is only supported for shared_ptr return self._is_array def __call__(self, obj, index): # Check bounds if _elem_type is an array of known bound - m = re.match('.*\[(\d+)]$', str(self._elem_type)) + m = re.match(r'.*\[(\d+)]$', str(self._elem_type)) if m and index >= int(m.group(1)): raise IndexError('shared_ptr<%s> index "%d" should not be >= %d.' % (self._elem_type, int(index), int(m.group(1)))) return SharedPtrGetWorker.__call__(self, obj)[index] + class SharedPtrUseCountWorker(gdb.xmethod.XMethodWorker): - "Implements std::shared_ptr::use_count()" + """Implement std::shared_ptr::use_count().""" def __init__(self, elem_type): - SharedPtrUseCountWorker.__init__(self, elem_type) + pass def get_arg_types(self): return None @@ -727,12 +811,16 @@ class SharedPtrUseCountWorker(gdb.xmethod.XMethodWorker): def get_result_type(self, obj): return gdb.lookup_type('long') + def _supports(self, method_name): + return True + def __call__(self, obj): refcounts = obj['_M_refcount']['_M_pi'] return refcounts['_M_use_count'] if refcounts else 0 + class SharedPtrUniqueWorker(SharedPtrUseCountWorker): - "Implements std::shared_ptr::unique()" + """Implement std::shared_ptr::unique().""" def __init__(self, elem_type): SharedPtrUseCountWorker.__init__(self, elem_type) @@ -743,6 +831,7 @@ class SharedPtrUniqueWorker(SharedPtrUseCountWorker): def __call__(self, obj): return SharedPtrUseCountWorker.__call__(self, obj) == 1 + class SharedPtrMethodsMatcher(gdb.xmethod.XMethodMatcher): def __init__(self): gdb.xmethod.XMethodMatcher.__init__(self, @@ -758,7 +847,7 @@ class SharedPtrMethodsMatcher(gdb.xmethod.XMethodMatcher): self.methods = [self._method_dict[m] for m in self._method_dict] def match(self, class_type, method_name): - if not re.match('^std::(__\d+::)?shared_ptr<.*>$', class_type.tag): + if not is_specialization_of(class_type, 'shared_ptr'): return None method = self._method_dict.get(method_name) if method is None or not method.enabled: @@ -768,6 +857,7 @@ class SharedPtrMethodsMatcher(gdb.xmethod.XMethodMatcher): return worker return None + def register_libstdcxx_xmethods(locus): gdb.xmethod.register_xmethod_matcher(locus, ArrayMethodsMatcher()) gdb.xmethod.register_xmethod_matcher(locus, ForwardListMethodsMatcher())