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<T>::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
This commit is contained in:
parent
9909a05940
commit
9d50a6a785
|
@ -1,3 +1,25 @@
|
|||
2019-11-29 Jonathan Wakely <jwakely@redhat.com>
|
||||
|
||||
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 <fdumont@gcc.gnu.org>
|
||||
|
||||
* include/debug/helper_functions.h (__valid_range_aux): Use C++98
|
||||
|
|
|
@ -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<args...>
|
||||
"""
|
||||
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<int>
|
||||
then return the type std::_List_node<int>.
|
||||
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):
|
||||
|
|
|
@ -45,8 +45,6 @@ int main()
|
|||
std::list<C> l;
|
||||
l.push_back(c);
|
||||
std::list<C>::iterator liter = l.begin();
|
||||
// Need to ensure the list<C>::iterator::_Node typedef is in the debuginfo:
|
||||
int tmp __attribute__((unused)) = (*liter).ref;
|
||||
// { dg-final { regexp-test liter {ref = @0x.*} } }
|
||||
|
||||
__gnu_cxx::slist<C> sl;
|
||||
|
|
|
@ -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
|
||||
// <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include <forward_list>
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <any>
|
||||
#include <iostream>
|
||||
|
||||
int main()
|
||||
{
|
||||
std::list<std::string> list{"a"};
|
||||
std::list<std::string>::iterator lit = list.begin();
|
||||
// { dg-final { note-test lit {"a"} } }
|
||||
|
||||
std::forward_list<std::string> flist{"b"};
|
||||
std::forward_list<std::string>::iterator flit = flist.begin();
|
||||
// { dg-final { note-test flit {"b"} } }
|
||||
|
||||
std::map<int, int> 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<int> 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 } }
|
Loading…
Reference in New Issue