public inbox for libstdc++-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc r9-9168] libstdc++: improve how pretty printers find node types (PR 91997)
@ 2021-01-11 15:05 Jonathan Wakely
  0 siblings, 0 replies; only message in thread
From: Jonathan Wakely @ 2021-01-11 15:05 UTC (permalink / raw)
  To: gcc-cvs, libstdc++-cvs

https://gcc.gnu.org/g:b1dba8a228e7d9497d2ddbd012b4343f99b87823

commit r9-9168-gb1dba8a228e7d9497d2ddbd012b4343f99b87823
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Fri Nov 29 14:47:03 2019 +0000

    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.
    
    (cherry picked from commit 9d50a6a78509b42b3c2b2264da1a0d2c4b151d66)

Diff:
---
 libstdc++-v3/python/libstdcxx/v6/printers.py       | 138 +++++++++++++++------
 .../testsuite/libstdc++-prettyprinters/59161.cc    |   2 -
 .../testsuite/libstdc++-prettyprinters/91997.cc    |  53 ++++++++
 3 files changed, 151 insertions(+), 42 deletions(-)

diff --git a/libstdc++-v3/python/libstdcxx/v6/printers.py b/libstdc++-v3/python/libstdcxx/v6/printers.py
index 8f16b53c953..e648f0e9dda 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<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
@@ -242,40 +307,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"
@@ -302,9 +367,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:
@@ -320,8 +384,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:
@@ -574,12 +637,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']:
@@ -644,9 +703,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):
@@ -684,9 +741,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:
@@ -844,11 +899,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]
@@ -875,9 +930,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
@@ -892,7 +951,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)
@@ -918,7 +977,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)
@@ -989,8 +1048,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<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;
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
+// <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 } }


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2021-01-11 15:05 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-01-11 15:05 [gcc r9-9168] libstdc++: improve how pretty printers find node types (PR 91997) Jonathan Wakely

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).