Bug 91997 - pretty printers: The __node_type type alias in _Hashtable is not available
Summary: pretty printers: The __node_type type alias in _Hashtable is not available
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: libstdc++ (show other bugs)
Version: unknown
: P3 normal
Target Milestone: 9.4
Assignee: Jonathan Wakely
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2019-10-04 20:35 UTC by Rafael Avila de Espindola
Modified: 2021-01-11 17:56 UTC (History)
2 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2019-11-28 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Rafael Avila de Espindola 2019-10-04 20:35:30 UTC
At least when compiling with optimizations with

gcc (GCC) 8.3.1 20190223 (Red Hat 8.3.1-2)

the debug info doesn't include the type alias

 using __node_type = __detail::_Hash_node<_Value, __hash_cached::value>

Which causes the pretty printer to fail with

-----------------------------------------------------
Traceback (most recent call last):
  File "/home/espindola/scylla/gdb-printers/libstdcxx/v6/printers.py", line 966, in children
    data = self.flatten (imap (self.format_one, StdHashtableIterator (self.hashtable())))
  File "/home/espindola/scylla/gdb-printers/libstdcxx/v6/printers.py", line 889, in __init__
    self.node_type = find_type(hash.type, '__node_type').pointer()
  File "/home/espindola/scylla/gdb-printers/libstdcxx/v6/printers.py", line 97, in find_type
    field = typ.fields()[0]
IndexError: list index out of range
-----------------------------------------------------

To work around that the pretty printer should reconstruct the type.
The best I was able to come up with was 

-        self.node_type = find_type(hash.type, '__node_type').pointer()
+        pair_name = hash.type.template_argument(1).name
+        traits_type = hash.type.template_argument(9)
+        cached = str(traits_type.template_argument(0)).lower()
+        node_name = '::std::__detail::_Hash_node<%s,%s>' % (pair_name, cached)
+        self.node_type = gdb.lookup_type(node_name).pointer()
Comment 1 Jonathan Wakely 2019-11-28 09:24:03 UTC
From https://bugzilla.redhat.com/show_bug.cgi?id=1053438 this also happens with std::list

#include <stdio.h>
#include <list>
#include <string>
int main() {
  std::list<std::string> list;
  list.push_back("a");
  std::list<std::string>::iterator it=list.begin();
  return 0;
}


$ gdb -q -ex "br 8" -ex r -ex "p it"  a.out
Reading symbols from a.out...
Breakpoint 1 at 0x401237: file 91997.cc, line 8.
Starting program: /tmp/a.out 

Breakpoint 1, main () at 91997.cc:8
8         return 0;
Python Exception <class 'ValueError'> Cannot find type std::__cxx11::list<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >::iterator::_Node: 
$1 = 
(gdb)
Comment 2 Jonathan Wakely 2019-11-28 14:50:10 UTC
Rafael, I'm unable to reproduce this with unordered containers. Do you have a testcase?
Comment 3 Rafael Avila de Espindola 2019-11-28 18:52:55 UTC
(In reply to Jonathan Wakely from comment #2)
> Rafael, I'm unable to reproduce this with unordered containers. Do you have
> a testcase?

I was able to reproduce it with 2 files:

$ cat test.cc
#include <unordered_map>
void foo(std::unordered_map<int, int> &map);
int main() {
  std::unordered_map<int, int> map;
  map[42] = 1;
  foo(map);
  return 0;
}
$ cat test2.cc
#include <unordered_map>
#include <stdio.h>
void foo(std::unordered_map<int, int> &map) {
  auto it = map.begin();
  printf("%d\n", *it);
}
$ g++ test.cc test2.cc -o t -g
$ /usr/bin/gdb -q -ex "b printf" -ex r -ex bt ./t
Reading symbols from ./t...
Breakpoint 1 at 0x204b10
Starting program: /home/espindola/scylla/t
warning: Loadable section ".note.gnu.property" outside of ELF segments
warning: Loadable section ".note.gnu.property" outside of ELF segments
warning: Loadable section ".note.gnu.property" outside of ELF segments

Breakpoint 1, __printf (format=0x20081b "%d\n") at printf.c:28
28      {
#0  __printf (format=0x20081b "%d\n") at printf.c:28
#1  0x000000000020494a in foo (Traceback (most recent call last):
  File "/lib64/../share/gcc-9/python/libstdcxx/v6/printers.py", line 957, in children
    data = self.flatten (imap (self.format_one, StdHashtableIterator (self.hashtable())))
  File "/lib64/../share/gcc-9/python/libstdcxx/v6/printers.py", line 880, in __init__
    self.node_type = find_type(hash.type, '__node_type').pointer()
  File "/lib64/../share/gcc-9/python/libstdcxx/v6/printers.py", line 97, in find_type
    field = typ.fields()[0]
IndexError: list index out of range
map=
std::unordered_map with 1 element) at test2.cc:5
#2  0x0000000000203017 in main () at test.cc:6
Comment 4 Jonathan Wakely 2019-11-28 20:18:49 UTC
Thanks, I can confirm that error. Oddly, it works fine when printing the variable within its own stack frame:

$ gdb -q -ex "br printf" -ex r -ex up -ex bt -ex down -ex bt -ex cont -ex q  map
Reading symbols from map...
Breakpoint 1 at 0x401030
Starting program: /tmp/map 

Breakpoint 1, __printf (format=0x403007 "%d\n") at printf.c:28
28      {
#1  0x0000000000401272 in foo (map=std::unordered_map with 1 element = {...}) at map.cc:16
16        __builtin_printf("%d\n", *it);
#0  __printf (format=0x403007 "%d\n") at printf.c:28
#1  0x0000000000401272 in foo (map=std::unordered_map with 1 element = {...}) at map.cc:16
#2  0x0000000000401203 in main () at map.cc:9
#0  __printf (format=0x403007 "%d\n") at printf.c:28
28      {
#0  __printf (format=0x403007 "%d\n") at printf.c:28
#1  0x0000000000401272 in foo (Python Exception <class 'gdb.error'> No type named std::__detail::_Hash_node<struct std::pair<int const, int>, false>.: 
map=std::unordered_map with 1 element) at map.cc:16
#2  0x0000000000401203 in main () at map.cc:9
Continuing.
42
[Inferior 1 (process 679052) exited normally]

It fails when it's not at the top of the stack.
Comment 5 Jonathan Wakely 2019-11-28 20:19:47 UTC
(In reply to Jonathan Wakely from comment #4)
> #1  0x0000000000401272 in foo (Python Exception <class 'gdb.error'> No type
> named std::__detail::_Hash_node<struct std::pair<int const, int>, false>.: 

N.B. That's a different error because I'm testing a fix. Apparently it doesn't fix it.
Comment 6 Jonathan Wakely 2019-11-29 12:32:56 UTC
The problem is not missing debuginfo, it's a GDB bug:
https://sourceware.org/bugzilla/show_bug.cgi?id=25234

I have a workaround for libstdc++ though.
Comment 7 Jonathan Wakely 2019-11-29 14:47:35 UTC
Author: redi
Date: Fri Nov 29 14:47:03 2019
New Revision: 278846

URL: https://gcc.gnu.org/viewcvs?rev=278846&root=gcc&view=rev
Log:
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.

Added:
    trunk/libstdc++-v3/testsuite/libstdc++-prettyprinters/91997.cc
Modified:
    trunk/libstdc++-v3/ChangeLog
    trunk/libstdc++-v3/python/libstdcxx/v6/printers.py
    trunk/libstdc++-v3/testsuite/libstdc++-prettyprinters/59161.cc
Comment 8 Jonathan Wakely 2019-11-29 14:54:52 UTC
Fixed on trunk, backports to follow.
Comment 9 Jakub Jelinek 2020-03-04 09:34:22 UTC
GCC 8.4.0 has been released, adjusting target milestone.
Comment 10 GCC Commits 2021-01-11 15:05:22 UTC
The releases/gcc-9 branch has been updated by Jonathan Wakely <redi@gcc.gnu.org>:

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)
Comment 11 Jonathan Wakely 2021-01-11 17:56:00 UTC
Fixed for 9.4, but I do not plan to backport it to gcc-8.