From patchwork Fri Nov 29 14:46:55 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jonathan Wakely X-Patchwork-Id: 1202510 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=209.132.180.131; helo=sourceware.org; envelope-from=gcc-patches-return-514872-incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.b="YqiD0o5t"; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="DS3f77PS"; dkim-atps=neutral Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 47PcmJ6NhJz9sPj for ; Sat, 30 Nov 2019 01:47:11 +1100 (AEDT) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:date :from:to:subject:message-id:mime-version:content-type; q=dns; s= default; b=Xn++EMj4rOdsmCbEycS8EBTqbmBdVsrQyP6Fysi0y49tptM0BJzsD HEwE8CS4NCHLwM9oAU3gwE3cIYCQ9qdY0/hKwcbrRlYcrjcV9Bh0BhaEPTTxatjU 0YaKwZsSRirYRuddMaqNIjgI9pwlV7bnGhKIizshC4CZizFa0uZPZw= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:date :from:to:subject:message-id:mime-version:content-type; s= default; bh=psQv9JgirxfEUIv30HlW8/TQzUQ=; b=YqiD0o5tPMcYnMTr9N3L aq/sMofK81VHJIE5nr6flmHoe4iedRfI4sfVO4+zaHgTGo17qpNe2/rIHBF03t9i vBKfgWwiYaz3LxBvfF+qOrWb9vsTRMrzlioomK+rRod6aokJ/tCEc2ExKcrLDfrX JafLwTtUHaPZhQjJkyv0bd4= Received: (qmail 30209 invoked by alias); 29 Nov 2019 14:47:04 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 30156 invoked by uid 89); 29 Nov 2019 14:47:03 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-17.2 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_SHORT autolearn=ham version=3.3.1 spammy=mit, sk:get_val, sk:bugzill, UD:list.begin X-HELO: us-smtp-delivery-1.mimecast.com Received: from us-smtp-1.mimecast.com (HELO us-smtp-delivery-1.mimecast.com) (207.211.31.81) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Fri, 29 Nov 2019 14:47:01 +0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1575038819; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type; bh=rRs/76MAjf08N7xYCXrc+BKdntgosbw/AaafC+cu5oM=; b=DS3f77PSQlILA8rN6aUZoPkFtbR5CTomcHpKKqQ1Cn/I5LSb1UgH02QHN/YMPq6fZ/jMLU tf/awkWjePXP0YCqnNamTze/35j50iHWv1tI9TCWFI8XYq91rGBKB11KPzocx4e2xd9j4H iK4FRatuJ/YwqbKk6y+AqsAoYhbW+nI= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-67-w_p70ZsjPTaoM6W79Gp3GA-1; Fri, 29 Nov 2019 09:46:57 -0500 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 66A1C10054E3; Fri, 29 Nov 2019 14:46:56 +0000 (UTC) Received: from localhost (unknown [10.33.36.157]) by smtp.corp.redhat.com (Postfix) with ESMTP id E0A1219C58; Fri, 29 Nov 2019 14:46:55 +0000 (UTC) Date: Fri, 29 Nov 2019 14:46:55 +0000 From: Jonathan Wakely To: libstdc++@gcc.gnu.org, gcc-patches@gcc.gnu.org Subject: [PATCH] libstdc++:: improve how pretty printers find node types (PR 91997) Message-ID: <20191129144655.GA747760@redhat.com> MIME-Version: 1.0 X-Clacks-Overhead: GNU Terry Pratchett User-Agent: Mutt/1.12.1 (2019-06-15) X-Mimecast-Spam-Score: 0 Content-Disposition: inline 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. Tested powerpc64le-linux, committed to trunk. I plan to backport this to the release branches too. commit 85e0abed67eaf9e10382d8688dfa3260d11c1b7a Author: Jonathan Wakely Date: Fri Nov 29 13:13:56 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::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. 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 } }