From 9d50a6a78509b42b3c2b2264da1a0d2c4b151d66 Mon Sep 17 00:00:00 2001 From: Jonathan Wakely Date: Fri, 29 Nov 2019 14:47:03 +0000 Subject: [PATCH] libstdc++:: improve how pretty printers find node types (PR 91997) This fixes two related problems. The iterators for node-based containers use nested typedefs such as std::list::iterator::_Node to denote their node types. As reported in https://bugzilla.redhat.com/show_bug.cgi?id=1053438 those typedefs are not always present in the debug info. That means the pretty printers cannot find them using gdb.lookup_type (via the find_type helper). Instead of looking up the nested typedefs this patch makes the printers look up the actual class templates directly. A related problem (and the original topic of PR 91997) is that GDB fails to find types via gdb.lookup_type when printing a backtrace from a non-C++ functiion: https://sourceware.org/bugzilla/show_bug.cgi?id=25234 That is also solved by not looking up the nested typedef. PR libstdc++/91997 * python/libstdcxx/v6/printers.py (find_type): Fail more gracefully if we run out of base classes to look at. (llokup_templ_spec, lookup_node_type): New utilities to find node types for node-based containers. (StdListPrinter.children, NodeIteratorPrinter.__init__) (NodeIteratorPrinter.to_string, StdSlistPrinter.children) (StdSlistIteratorPrinter.to_string, StdRbtreeIteratorPrinter.__init__) (StdMapPrinter.children, StdSetPrinter.children) (StdForwardListPrinter.children): Use lookup_node_type instead of find_type. (StdListIteratorPrinter.__init__, StdFwdListIteratorPrinter.__init__): Pass name of node type to NodeIteratorPrinter constructor. (Tr1HashtableIterator.__init__): Rename argument. (StdHashtableIterator.__init__): Likewise. Use lookup_templ_spec instead of find_type. * testsuite/libstdc++-prettyprinters/59161.cc: Remove workaround for _Node typedef not being present in debuginfo. * testsuite/libstdc++-prettyprinters/91997.cc: New test. From-SVN: r278846 --- libstdc++-v3/ChangeLog | 22 +++ libstdc++-v3/python/libstdcxx/v6/printers.py | 138 +++++++++++++----- .../libstdc++-prettyprinters/59161.cc | 2 - .../libstdc++-prettyprinters/91997.cc | 53 +++++++ 4 files changed, 173 insertions(+), 42 deletions(-) create mode 100644 libstdc++-v3/testsuite/libstdc++-prettyprinters/91997.cc diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index 23395b178d0..83deef2e9fb 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,25 @@ +2019-11-29 Jonathan Wakely + + PR libstdc++/91997 + * python/libstdcxx/v6/printers.py (find_type): Fail more gracefully + if we run out of base classes to look at. + (llokup_templ_spec, lookup_node_type): New utilities to find node + types for node-based containers. + (StdListPrinter.children, NodeIteratorPrinter.__init__) + (NodeIteratorPrinter.to_string, StdSlistPrinter.children) + (StdSlistIteratorPrinter.to_string, StdRbtreeIteratorPrinter.__init__) + (StdMapPrinter.children, StdSetPrinter.children) + (StdForwardListPrinter.children): Use lookup_node_type instead of + find_type. + (StdListIteratorPrinter.__init__, StdFwdListIteratorPrinter.__init__): + Pass name of node type to NodeIteratorPrinter constructor. + (Tr1HashtableIterator.__init__): Rename argument. + (StdHashtableIterator.__init__): Likewise. Use lookup_templ_spec + instead of find_type. + * testsuite/libstdc++-prettyprinters/59161.cc: Remove workaround for + _Node typedef not being present in debuginfo. + * testsuite/libstdc++-prettyprinters/91997.cc: New test. + 2019-11-26 François Dumont * include/debug/helper_functions.h (__valid_range_aux): Use C++98 diff --git a/libstdc++-v3/python/libstdcxx/v6/printers.py b/libstdc++-v3/python/libstdcxx/v6/printers.py index cd79a1fa6e6..869a8286675 100644 --- a/libstdc++-v3/python/libstdcxx/v6/printers.py +++ b/libstdc++-v3/python/libstdcxx/v6/printers.py @@ -94,13 +94,78 @@ 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 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 and _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. + e.g. if NODENAME is '_List_node' and CONTAINERTYPE is std::list + then return the type std::_List_node. + Returns None if not found. + """ + # If nodename is unqualified, assume it's in namespace std. + if '::' not in nodename: + nodename = 'std::' + nodename + 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 as e: + # 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) + return lookup_templ_spec(nodename, valtype) + 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 type(typ) is 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 if a type is a given template instantiation." global _versioned_namespace @@ -253,40 +318,40 @@ class StdListPrinter: self.val = val def children(self): - nodetype = find_type(self.val.type, '_Node') - nodetype = nodetype.strip_typedefs().pointer() + 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']: + 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): + 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() + 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" 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" 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" @@ -313,9 +378,8 @@ class StdSlistPrinter: 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: @@ -331,8 +395,7 @@ class StdSlistIteratorPrinter: def to_string(self): 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() + 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: @@ -583,12 +646,8 @@ class StdRbtreeIteratorPrinter: 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() + 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']: @@ -653,9 +712,7 @@ class StdMapPrinter: 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() + node = lookup_node_type('_Rb_tree_node', self.val.type).pointer() return self._iter (RbtreeIterator (self.val), node) def display_hint (self): @@ -693,9 +750,7 @@ class StdSetPrinter: 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() + node = lookup_node_type('_Rb_tree_node', self.val.type).pointer() return self._iter (RbtreeIterator (self.val), node) class StdBitsetPrinter: @@ -853,11 +908,11 @@ class StdStringPrinter: return 'string' class Tr1HashtableIterator(Iterator): - def __init__ (self, hash): - self.buckets = hash['_M_buckets'] + def __init__ (self, hashtable): + self.buckets = hashtable['_M_buckets'] self.bucket = 0 - self.bucket_count = hash['_M_bucket_count'] - self.node_type = find_type(hash.type, '_Node').pointer() + 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] @@ -884,9 +939,13 @@ class Tr1HashtableIterator(Iterator): 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 @@ -901,7 +960,7 @@ class StdHashtableIterator(Iterator): return valptr.dereference() class Tr1UnorderedSetPrinter: - "Print a tr1::unordered_set" + "Print a std::unordered_set or tr1::unordered_set" def __init__ (self, typename, val): self.typename = strip_versioned_namespace(typename) @@ -927,7 +986,7 @@ class Tr1UnorderedSetPrinter: return izip (counter, StdHashtableIterator (self.hashtable())) class Tr1UnorderedMapPrinter: - "Print a tr1::unordered_map" + "Print a std::unordered_map or tr1::unordered_map" def __init__ (self, typename, val): self.typename = strip_versioned_namespace(typename) @@ -998,8 +1057,7 @@ class StdForwardListPrinter: self.typename = strip_versioned_namespace(typename) def children(self): - nodetype = find_type(self.val.type, '_Node') - nodetype = nodetype.strip_typedefs().pointer() + 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): diff --git a/libstdc++-v3/testsuite/libstdc++-prettyprinters/59161.cc b/libstdc++-v3/testsuite/libstdc++-prettyprinters/59161.cc index 215899f3d2e..af629496b47 100644 --- a/libstdc++-v3/testsuite/libstdc++-prettyprinters/59161.cc +++ b/libstdc++-v3/testsuite/libstdc++-prettyprinters/59161.cc @@ -45,8 +45,6 @@ int main() std::list l; l.push_back(c); std::list::iterator liter = l.begin(); - // Need to ensure the list::iterator::_Node typedef is in the debuginfo: - int tmp __attribute__((unused)) = (*liter).ref; // { dg-final { regexp-test liter {ref = @0x.*} } } __gnu_cxx::slist sl; diff --git a/libstdc++-v3/testsuite/libstdc++-prettyprinters/91997.cc b/libstdc++-v3/testsuite/libstdc++-prettyprinters/91997.cc new file mode 100644 index 00000000000..393c5680e2e --- /dev/null +++ b/libstdc++-v3/testsuite/libstdc++-prettyprinters/91997.cc @@ -0,0 +1,53 @@ +// { dg-options "-std=gnu++17 -g -O0 -Wno-unused" } +// { dg-do run { target c++17 } } + +// Copyright (C) 2019 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// . + +#include +#include +#include +#include +#include +#include +#include + +int main() +{ + std::list list{"a"}; + std::list::iterator lit = list.begin(); + // { dg-final { note-test lit {"a"} } } + + std::forward_list flist{"b"}; + std::forward_list::iterator flit = flist.begin(); + // { dg-final { note-test flit {"b"} } } + + std::map m{ {1, 2} }; + auto mit = m.begin(); + // { dg-final { note-test mit {{first = 1, second = 2}} } } + + std::any a = m; + // { dg-final { note-test a {std::any containing std::map with 1 element = {[1] = 2}} } } + + std::set s{1, 2}; + auto sit = s.begin(); + // { dg-final { note-test sit {1} } } + + std::cout << "\n"; + return 0; // Mark SPOT +} +// { dg-final { gdb-test SPOT } } -- 2.30.2