public inbox for gcc-help@gcc.gnu.org
 help / color / mirror / Atom feed
* Help using the GDB C++ STL pretty-printers / xmethods
@ 2022-05-07  1:23 Paul Smith
  2022-05-07 11:19 ` Hannes Domani
  2022-05-07 15:25 ` Jonathan Wakely
  0 siblings, 2 replies; 21+ messages in thread
From: Paul Smith @ 2022-05-07  1:23 UTC (permalink / raw)
  To: gcc-help

Are there any docs or other information about how to use the GDB
pretty-printers for C++ STL that come with GCC?

I have them installed and they work for displaying data, but they don't
work for accessing data.

Just as one very simple example, is there a way to dereference a
unique_ptr?  I see xmethods defined but nothing I've tried works. 
Suppose I have:

  (gdb) p $mp->mgr
  $6 = std::unique_ptr<class Mgr> = {
    get() = 0x7f519a24e000
  }

(so you can see the pretty-printers are installed).  Now, how do I
extract out this object pointer so I can see what's in it?  These don't
work:

  (gdb) p *$mp->mgr
  One of the arguments you tried to pass to operator* could not be
  converted to what the function wants.

  (gdb) p $mp->mgr->initialized
  One of the arguments you tried to pass to operator-> could not be
  converted to what the function wants.

It used to work in GCC 10.2 / GDB 10.1 to access the pointer directly
if you knew, or deciphered, the internal structor of unique_ptr:

  (gdb) p $mp->mgr._M_t._M_t._M_head_impl

However, with GCC 11.3 / GDB 12.1 this no longer works: I get this
error:

  Request for member '_M_head_impl' is ambiguous in type
'std::tuple<Mgr*, std::default_delete<Mgr> >'.
  Candidates are:
    'std::default_delete<Mgr> std::_Head_base<1ul,
std::default_delete<Mgr>, true>::_M_head_impl' (std::tuple<Mgr*,
std::default_delete<Mgr> > -> std::_Tuple_impl<0ul, Mgr*,
std::default_delete<Mgr> > -> std::_Tuple_impl<1ul,
std::default_delete<Mgr> > -> std::_Head_base<1ul,
std::default_delete<Mgr>, true>)
    '<unnamed type> std::_Head_base<0ul, Mgr*, false>::_M_head_impl'
(std::tuple<Mgr*, std::default_delete<Mgr> > -> std::_Tuple_impl<0ul,
Mgr*, std::default_delete<Mgr> > -> std::_Head_base<0ul, Mgr*, false>)

I have found no way to resolve this ambiguity to GDB's satisfaction.

The only thing I've found that works is just to access the pointer
value directly by cutting and pasting it with a cast:

  (gdb) p *((Mgr*)0x7f519a24e000)
  $8 = {
    ...
    initialized = true
  }

  (gdb) p ((Mgr*)0x7f519a24e000)->initialized
  $9 = true

Is that really what we have to do?


Secondly, is there some interface that is defined by the libstdcxx.v6
Python macros for GDB that people who are doing their own python
scripting for their own C++ programs can take advantage of to avoid too
much groveling through the depths of the C++ STL implementation?

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Help using the GDB C++ STL pretty-printers / xmethods
  2022-05-07  1:23 Help using the GDB C++ STL pretty-printers / xmethods Paul Smith
@ 2022-05-07 11:19 ` Hannes Domani
  2022-05-07 15:07   ` Paul Smith
  2022-05-07 15:25 ` Jonathan Wakely
  1 sibling, 1 reply; 21+ messages in thread
From: Hannes Domani @ 2022-05-07 11:19 UTC (permalink / raw)
  To: gcc-help, Paul Smith

 Am Samstag, 7. Mai 2022, 03:23:46 MESZ hat Paul Smith <paul@mad-scientist.net> Folgendes geschrieben:

> Are there any docs or other information about how to use the GDB
> pretty-printers for C++ STL that come with GCC?
>
> I have them installed and they work for displaying data, but they don't
> work for accessing data.
>
> Just as one very simple example, is there a way to dereference a
> unique_ptr?  I see xmethods defined but nothing I've tried works.
> Suppose I have:
>
>   (gdb) p $mp->mgr
>   $6 = std::unique_ptr<class Mgr> = {
>     get() = 0x7f519a24e000
>   }
>
> (so you can see the pretty-printers are installed).  Now, how do I
> extract out this object pointer so I can see what's in it?  These don't
> work:
>
>   (gdb) p *$mp->mgr
>   One of the arguments you tried to pass to operator* could not be
>   converted to what the function wants.
>
>   (gdb) p $mp->mgr->initialized
>   One of the arguments you tried to pass to operator-> could not be
>   converted to what the function wants.
>
> It used to work in GCC 10.2 / GDB 10.1 to access the pointer directly
> if you knew, or deciphered, the internal structor of unique_ptr:
>
>   (gdb) p $mp->mgr._M_t._M_t._M_head_impl
>
> However, with GCC 11.3 / GDB 12.1 this no longer works: I get this
> error:
>
>   Request for member '_M_head_impl' is ambiguous in type
> 'std::tuple<Mgr*, std::default_delete<Mgr> >'.
>   Candidates are:
>     'std::default_delete<Mgr> std::_Head_base<1ul,
> std::default_delete<Mgr>, true>::_M_head_impl' (std::tuple<Mgr*,
> std::default_delete<Mgr> > -> std::_Tuple_impl<0ul, Mgr*,
> std::default_delete<Mgr> > -> std::_Tuple_impl<1ul,
> std::default_delete<Mgr> > -> std::_Head_base<1ul,
> std::default_delete<Mgr>, true>)
>     '<unnamed type> std::_Head_base<0ul, Mgr*, false>::_M_head_impl'
> (std::tuple<Mgr*, std::default_delete<Mgr> > -> std::_Tuple_impl<0ul,
> Mgr*, std::default_delete<Mgr> > -> std::_Head_base<0ul, Mgr*, false>)
>
> I have found no way to resolve this ambiguity to GDB's satisfaction.
>
> The only thing I've found that works is just to access the pointer
> value directly by cutting and pasting it with a cast:
>
>   (gdb) p *((Mgr*)0x7f519a24e000)
>   $8 = {
>     ...
>     initialized = true
>   }
>
>   (gdb) p ((Mgr*)0x7f519a24e000)->initialized
>   $9 = true
>
> Is that really what we have to do?

I'm assuming you installed the pretty printers with a call to
register_libstdcxx_printers() somewhere.
For access to C++ STL objects there is another set of gdb helpers,
called xmethods, which you need to install with a call to
register_libstdcxx_xmethods().


> Secondly, is there some interface that is defined by the libstdcxx.v6
> Python macros for GDB that people who are doing their own python
> scripting for their own C++ programs can take advantage of to avoid too
> much groveling through the depths of the C++ STL implementation?

Depends on what you want to do with it, but you can get access to the pretty
printers in gdb with a call to gdb.default_visualizer() [1].

[1] https://sourceware.org/gdb/onlinedocs/gdb/Pretty-Printing-API.html#index-gdb_002edefault_005fvisualizer


Hannes

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Help using the GDB C++ STL pretty-printers / xmethods
  2022-05-07 11:19 ` Hannes Domani
@ 2022-05-07 15:07   ` Paul Smith
  2022-05-07 15:35     ` Jonathan Wakely
  2022-05-07 15:44     ` Hannes Domani
  0 siblings, 2 replies; 21+ messages in thread
From: Paul Smith @ 2022-05-07 15:07 UTC (permalink / raw)
  To: Hannes Domani, gcc-help

On Sat, 2022-05-07 at 11:19 +0000, Hannes Domani wrote:
> > The only thing I've found that works is just to access the pointer
> > value directly by cutting and pasting it with a cast:
> > 
> >    (gdb) p *((Mgr*)0x7f519a24e000)
> >    $8 = {
> >      ...
> >      initialized = true
> >    }
> > 
> >    (gdb) p ((Mgr*)0x7f519a24e000)->initialized
> >    $9 = true
> > 
> > Is that really what we have to do?
> 
> I'm assuming you installed the pretty printers with a call to
> register_libstdcxx_printers() somewhere.
> For access to C++ STL objects there is another set of gdb helpers,
> called xmethods, which you need to install with a call to
> register_libstdcxx_xmethods().

Thanks for the reply.  I should have mentioned this; what I do is:

  python
  from libstdcxx.v6 import register_libstdcxx_printers
  register_libstdcxx_printers(None)
  end

That method loads both the pretty printers AND the xmethods:

  # Load the xmethods if GDB supports them.
  def gdb_has_xmethods():
      try:
          import gdb.xmethod
          return True
      except ImportError:
          return False

  def register_libstdcxx_printers(obj):
      # Load the pretty-printers.
      from .printers import register_libstdcxx_printers
      register_libstdcxx_printers(obj)

      if gdb_has_xmethods():
          from .xmethods import register_libstdcxx_xmethods
          register_libstdcxx_xmethods(obj)

Just to verify I've tried explicitly loading and calling
register_libstdcxx_xmethods() and I get an error saying they're already
loaded.

Just to clarify are you saying that one or both of the methods I've
tried (using * or -> operators) _should_ work, and that they do work
for you when you try them?

If so then I guess I'm doing something wrong and I will have to look
more deeply.

> > Secondly, is there some interface that is defined by the
> > libstdcxx.v6 Python macros for GDB that people who are doing their
> > own python scripting for their own C++ programs can take advantage
> > of to avoid too much groveling through the depths of the C++ STL
> > implementation?
> 
> Depends on what you want to do with it, but you can get access to the
> pretty printers in gdb with a call to gdb.default_visualizer() [1].

I'm not talking about printing things per se, I'm talking about writing
my own python macros that help me examine my own data structures, which
are built with STL types.

For example I have a complex structure that uses std::vector,
std::list, std:unordered_map, unique_ptr, etc. and I want to write my
own methods that examine these structures, either to print them in a
different way (not just the standard pretty-printer output) or
whatever.

So I have a Python variable containing a pointer to this object that
contains a unique_ptr, and I want to get a variable containing the
pointer contained in the unique_ptr.  How do I do that?  Is there some
Python function available in the macros that will do that for me?

As I said in my previous message, with GCC 11 it doesn't seem like I
can just get the _M_head_impl value any longer, as I get these
"ambiguous" failures.

Basically I can't use any of the new versions of GCC until I can figure
out how to debug them in a reasonable way.

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Help using the GDB C++ STL pretty-printers / xmethods
  2022-05-07  1:23 Help using the GDB C++ STL pretty-printers / xmethods Paul Smith
  2022-05-07 11:19 ` Hannes Domani
@ 2022-05-07 15:25 ` Jonathan Wakely
  1 sibling, 0 replies; 21+ messages in thread
From: Jonathan Wakely @ 2022-05-07 15:25 UTC (permalink / raw)
  To: Paul Smith; +Cc: gcc-help

On Sat, 7 May 2022 at 02:24, Paul Smith <paul@mad-scientist.net> wrote:
>
> Are there any docs or other information about how to use the GDB
> pretty-printers for C++ STL that come with GCC?
>
> I have them installed and they work for displaying data, but they don't
> work for accessing data.
>
> Just as one very simple example, is there a way to dereference a
> unique_ptr?  I see xmethods defined but nothing I've tried works.
> Suppose I have:
>
>   (gdb) p $mp->mgr
>   $6 = std::unique_ptr<class Mgr> = {
>     get() = 0x7f519a24e000
>   }
>
> (so you can see the pretty-printers are installed).  Now, how do I
> extract out this object pointer so I can see what's in it?  These don't
> work:
>
>   (gdb) p *$mp->mgr
>   One of the arguments you tried to pass to operator* could not be
>   converted to what the function wants.
>
>   (gdb) p $mp->mgr->initialized
>   One of the arguments you tried to pass to operator-> could not be
>   converted to what the function wants.
>
> It used to work in GCC 10.2 / GDB 10.1 to access the pointer directly
> if you knew, or deciphered, the internal structor of unique_ptr:
>
>   (gdb) p $mp->mgr._M_t._M_t._M_head_impl
>
> However, with GCC 11.3 / GDB 12.1 this no longer works: I get this
> error:
>
>   Request for member '_M_head_impl' is ambiguous in type
> 'std::tuple<Mgr*, std::default_delete<Mgr> >'.
>   Candidates are:
>     'std::default_delete<Mgr> std::_Head_base<1ul,
> std::default_delete<Mgr>, true>::_M_head_impl' (std::tuple<Mgr*,
> std::default_delete<Mgr> > -> std::_Tuple_impl<0ul, Mgr*,
> std::default_delete<Mgr> > -> std::_Tuple_impl<1ul,
> std::default_delete<Mgr> > -> std::_Head_base<1ul,
> std::default_delete<Mgr>, true>)
>     '<unnamed type> std::_Head_base<0ul, Mgr*, false>::_M_head_impl'
> (std::tuple<Mgr*, std::default_delete<Mgr> > -> std::_Tuple_impl<0ul,
> Mgr*, std::default_delete<Mgr> > -> std::_Head_base<0ul, Mgr*, false>)
>
> I have found no way to resolve this ambiguity to GDB's satisfaction.

The new GDB is correct, see
https://sourceware.org/bugzilla/show_bug.cgi?id=28480

To make it work you need to disambiguate the member access by
qualifying it with the class you want to access, but you shouldn't
need to so I'm not going to waste time figuring out the right voodoo.

>
> The only thing I've found that works is just to access the pointer
> value directly by cutting and pasting it with a cast:
>
>   (gdb) p *((Mgr*)0x7f519a24e000)
>   $8 = {
>     ...
>     initialized = true
>   }
>
>   (gdb) p ((Mgr*)0x7f519a24e000)->initialized
>   $9 = true
>
> Is that really what we have to do?

No, your Xmethods aren't working.

This works perfectly for me:

#include <memory>

struct Mgr
{
  bool initialized = true;
};

struct X
{
  std::unique_ptr<Mgr> mgr;
};

int main()
{
  X x;
  X* p = &x;
  x.mgr.reset(new Mgr);
  return 0; // line 18
}

(gdb) file a.out
Reading symbols from a.out...
(gdb) br 18
Breakpoint 1 at 0x401193: file up.C, line 18.
(gdb) r
Starting program: /tmp/a.out

Breakpoint 1, main () at up.C:18
18        return 0;
(gdb) set $mp = p
(gdb) p *$mp->mgr
$1 = {initialized = true}
(gdb) p $mp->mgr->initialized
$2 = true



> Secondly, is there some interface that is defined by the libstdcxx.v6
> Python macros for GDB that people who are doing their own python

(Aside: There are no macros involved in any of this)

> scripting for their own C++ programs can take advantage of to avoid too
> much groveling through the depths of the C++ STL implementation?

No. What sort of thing do you have in mind?

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Help using the GDB C++ STL pretty-printers / xmethods
  2022-05-07 15:07   ` Paul Smith
@ 2022-05-07 15:35     ` Jonathan Wakely
  2022-05-07 19:07       ` Paul Smith
  2022-05-07 15:44     ` Hannes Domani
  1 sibling, 1 reply; 21+ messages in thread
From: Jonathan Wakely @ 2022-05-07 15:35 UTC (permalink / raw)
  To: Paul Smith; +Cc: Hannes Domani, gcc-help

On Sat, 7 May 2022 at 16:08, Paul Smith <paul@mad-scientist.net> wrote:
>
> On Sat, 2022-05-07 at 11:19 +0000, Hannes Domani wrote:
> > > The only thing I've found that works is just to access the pointer
> > > value directly by cutting and pasting it with a cast:
> > >
> > >    (gdb) p *((Mgr*)0x7f519a24e000)
> > >    $8 = {
> > >      ...
> > >      initialized = true
> > >    }
> > >
> > >    (gdb) p ((Mgr*)0x7f519a24e000)->initialized
> > >    $9 = true
> > >
> > > Is that really what we have to do?
> >
> > I'm assuming you installed the pretty printers with a call to
> > register_libstdcxx_printers() somewhere.
> > For access to C++ STL objects there is another set of gdb helpers,
> > called xmethods, which you need to install with a call to
> > register_libstdcxx_xmethods().
>
> Thanks for the reply.  I should have mentioned this; what I do is:
>
>   python
>   from libstdcxx.v6 import register_libstdcxx_printers
>   register_libstdcxx_printers(None)
>   end

Why are you doing this by hand? That should not be necessary.


>
> That method loads both the pretty printers AND the xmethods:
>
>   # Load the xmethods if GDB supports them.
>   def gdb_has_xmethods():
>       try:
>           import gdb.xmethod
>           return True
>       except ImportError:
>           return False
>
>   def register_libstdcxx_printers(obj):
>       # Load the pretty-printers.
>       from .printers import register_libstdcxx_printers
>       register_libstdcxx_printers(obj)
>
>       if gdb_has_xmethods():
>           from .xmethods import register_libstdcxx_xmethods
>           register_libstdcxx_xmethods(obj)
>
> Just to verify I've tried explicitly loading and calling
> register_libstdcxx_xmethods() and I get an error saying they're already
> loaded.
>
> Just to clarify are you saying that one or both of the methods I've
> tried (using * or -> operators) _should_ work, and that they do work
> for you when you try them?

Yes.

>
> If so then I guess I'm doing something wrong and I will have to look
> more deeply.
>
> > > Secondly, is there some interface that is defined by the
> > > libstdcxx.v6 Python macros for GDB that people who are doing their
> > > own python scripting for their own C++ programs can take advantage
> > > of to avoid too much groveling through the depths of the C++ STL
> > > implementation?
> >
> > Depends on what you want to do with it, but you can get access to the
> > pretty printers in gdb with a call to gdb.default_visualizer() [1].
>
> I'm not talking about printing things per se, I'm talking about writing
> my own python macros that help me examine my own data structures, which
> are built with STL types.
>
> For example I have a complex structure that uses std::vector,
> std::list, std:unordered_map, unique_ptr, etc. and I want to write my
> own methods that examine these structures, either to print them in a
> different way (not just the standard pretty-printer output) or
> whatever.

That sounds useful, but it's unlikely anybody else is going to provide
it. If you want it, you get to build it :-)


>
> So I have a Python variable containing a pointer to this object that
> contains a unique_ptr, and I want to get a variable containing the
> pointer contained in the unique_ptr.  How do I do that?  Is there some
> Python function available in the macros that will do that for me?

Still no macros anywhere here :-)

>
> As I said in my previous message, with GCC 11 it doesn't seem like I
> can just get the _M_head_impl value any longer, as I get these
> "ambiguous" failures.
>
> Basically I can't use any of the new versions of GCC until I can figure
> out how to debug them in a reasonable way.

(gdb) p *static_cast<std::_Head_base<0, Mgr*,
false>&>(p->mgr._M_t._M_t)._M_head_impl
$10 = {initialized = true}

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Help using the GDB C++ STL pretty-printers / xmethods
  2022-05-07 15:07   ` Paul Smith
  2022-05-07 15:35     ` Jonathan Wakely
@ 2022-05-07 15:44     ` Hannes Domani
  1 sibling, 0 replies; 21+ messages in thread
From: Hannes Domani @ 2022-05-07 15:44 UTC (permalink / raw)
  To: gcc-help, Paul Smith

 Am Samstag, 7. Mai 2022, 17:06:49 MESZ hat Paul Smith <paul@mad-scientist.net> Folgendes geschrieben:

> On Sat, 2022-05-07 at 11:19 +0000, Hannes Domani wrote:
> > > The only thing I've found that works is just to access the pointer
> > > value directly by cutting and pasting it with a cast:
> > >
> > >    (gdb) p *((Mgr*)0x7f519a24e000)
> > >    $8 = {
> > >      ...
> > >      initialized = true
> > >    }
> > >
> > >    (gdb) p ((Mgr*)0x7f519a24e000)->initialized
> > >    $9 = true
> > >
> > > Is that really what we have to do?
> >
> > I'm assuming you installed the pretty printers with a call to
> > register_libstdcxx_printers() somewhere.
> > For access to C++ STL objects there is another set of gdb helpers,
> > called xmethods, which you need to install with a call to
> > register_libstdcxx_xmethods().
>
> Thanks for the reply.  I should have mentioned this; what I do is:
>
>   python
>   from libstdcxx.v6 import register_libstdcxx_printers
>   register_libstdcxx_printers(None)
>   end
>
> That method loads both the pretty printers AND the xmethods:
>
>   # Load the xmethods if GDB supports them.
>   def gdb_has_xmethods():
>       try:
>           import gdb.xmethod
>           return True
>       except ImportError:
>           return False
>
>   def register_libstdcxx_printers(obj):
>       # Load the pretty-printers.
>       from .printers import register_libstdcxx_printers
>       register_libstdcxx_printers(obj)
>
>       if gdb_has_xmethods():
>           from .xmethods import register_libstdcxx_xmethods
>           register_libstdcxx_xmethods(obj)
>
> Just to verify I've tried explicitly loading and calling
> register_libstdcxx_xmethods() and I get an error saying they're already
> loaded.
>
> Just to clarify are you saying that one or both of the methods I've
> tried (using * or -> operators) _should_ work, and that they do work
> for you when you try them?
>
> If so then I guess I'm doing something wrong and I will have to look
> more deeply.

Yes, this works for me.
But maybe you could show a small example program.

Do you see a list of available xmethods if you try `info xmethod` in gdb?



> > > Secondly, is there some interface that is defined by the
> > > libstdcxx.v6 Python macros for GDB that people who are doing their
> > > own python scripting for their own C++ programs can take advantage
> > > of to avoid too much groveling through the depths of the C++ STL
> > > implementation?
> >
> > Depends on what you want to do with it, but you can get access to the
> > pretty printers in gdb with a call to gdb.default_visualizer() [1].
>
>
> I'm not talking about printing things per se, I'm talking about writing
> my own python macros that help me examine my own data structures, which
> are built with STL types.
>
> For example I have a complex structure that uses std::vector,
> std::list, std:unordered_map, unique_ptr, etc. and I want to write my
> own methods that examine these structures, either to print them in a
> different way (not just the standard pretty-printer output) or
> whatever.
>
> So I have a Python variable containing a pointer to this object that
> contains a unique_ptr, and I want to get a variable containing the
> pointer contained in the unique_ptr.  How do I do that?  Is there some
> Python function available in the macros that will do that for me?
>
> As I said in my previous message, with GCC 11 it doesn't seem like I
> can just get the _M_head_impl value any longer, as I get these
> "ambiguous" failures.
>
> Basically I can't use any of the new versions of GCC until I can figure
> out how to debug them in a reasonable way.

With working xmethods, calling e.g. gdb.parse_and_eval("$mp->mgr->initialized") should give you the variable.
Again, a small reproducer would be helpful.


Hannes

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Help using the GDB C++ STL pretty-printers / xmethods
  2022-05-07 15:35     ` Jonathan Wakely
@ 2022-05-07 19:07       ` Paul Smith
  2022-05-07 19:51         ` Jonathan Wakely
  0 siblings, 1 reply; 21+ messages in thread
From: Paul Smith @ 2022-05-07 19:07 UTC (permalink / raw)
  To: Jonathan Wakely; +Cc: gcc-help

On Sat, 2022-05-07 at 16:35 +0100, Jonathan Wakely wrote:
> On Sat, 7 May 2022 at 16:08, Paul Smith <paul@mad-scientist.net>
> wrote:
> > Thanks for the reply.  I should have mentioned this; what I do is:
> > 
> >   python
> >   from libstdcxx.v6 import register_libstdcxx_printers
> >   register_libstdcxx_printers(None)
> >   end
> 
> Why are you doing this by hand? That should not be necessary.

Well I have this in my init file so I don't really enter it by hand. 
But are you implying that it should somehow happen without having to do
anything at all... how does that work?  Do you mean, that my
distribution should have set things up so that it happens
automatically?  Or that somehow GDB should "find" these macros on its
own?

Note I am building/installing my own GCC and GDB so maybe I just
haven't put everything in the right places.

> > Just to clarify are you saying that one or both of the methods I've
> > tried (using * or -> operators) _should_ work, and that they do
> > work for you when you try them?
> 
> Yes.

OK, I've discovered what's going on.  Apparently for some reason the
ability to show the values doesn't happen immediately.  When I attach
to a process and it's sitting in a sleep somewhere in the C runtime
like this:

  (gdb) bt
  #0  0x00007f804634e26f in clock_nanosleep () from /lib/x86_64-linux-
gnu/libc.so.6
  #1  0x00007f8046353ef7 in nanosleep () from /lib/x86_64-linux-
gnu/libc.so.6
  #2  0x000000000097a3c9 in SignalHandler::sleepUntilSignal
(this=<optimized out>, response=..., timeoutMs=<optimized out>) at
SignalHandler.cpp:205
  #3  0x0000000000960e64 in Engine::main (this=0x7f8045f0f000) at
Engine.cpp:1374
  #4  0x000000000098fbdf in Engine::runMain (this=0x7f8045f0f000) at
Engine.cpp:793
  #5  0x000000000095f907 in main (argc=<optimized out>, argv=<optimized
out>) at main.cpp:59

so my current frame is #0, then I run a python function I wrote that
shows a bunch of info about this process and sets a convenience
variable for the main pointer $mp, then I try to use it I get the
errors I showed earlier:

  (gdb) p $mp->mgr->initialized
  One of the arguments you tried to pass to operator-> could not be
  converted to what the function wants.

but, now if I change my frame (to one of my frames... it doesn't help
to go to frame #1 above) it works fine:

  (gdb) fr 2
  #2  0x000000000097a3c9 in SignalHandler::sleepUntilSignal
(this=<optimized out>, response=..., timeoutMs=<optimized out>) at
SignalHandler.cpp:205
  205         ::nanosleep(&interval, NULL);

  (gdb) p $mp->mgr->initialized
  $1 = true

Now I can go back down to frame 0 and it still works:

  (gdb) fr 0
  #0  0x00007f804634e26f in clock_nanosleep () from /lib/x86_64-linux-
gnu/libc.so.6

  (gdb) p $mp->mgr->initialized
  $2 = true

I can't find anything in the GDB docs about this but I guess it's a
question for them, as to what's happening here.

> > I'm not talking about printing things per se, I'm talking about
> > writing my own python macros that help me examine my own data
> > structures, which are built with STL types.
> > 
> > For example I have a complex structure that uses std::vector,
> > std::list, std:unordered_map, unique_ptr, etc. and I want to write
> > my own methods that examine these structures, either to print them
> > in a different way (not just the standard pretty-printer output) or
> > whatever.
> 
> That sounds useful, but it's unlikely anybody else is going to
> provide it. If you want it, you get to build it :-)

(sorry please replace "macro" with "Python function" everywhere in my
email... I will try to do better)

I'm pretty sure that's the same answer I got last time I asked :).

I just don't have the time or, probably, the knowledge of the STL
implementation I would need to do this well.  But I would be happy to
work with someone, including writing some code, designing some
features, etc., if anyone else were interested.

What I would really like to have are two things:

(a) iterator classes that I could instantiate from my python functions
when I need to iterate through an STL type.  So if in my class I have
"std::list<Foo> fooList" and in my python functions I have a python
variable "fooList" which refers to this object, I would like a way to
write something like:

    for elt in StdForwardIterator(fooList):
        <do something with elt>

Map iterators could return a tuple, etc.

I don't know if it makes more sense to create individual iterator
classes like above, or to implement Python classes that wrap types and
provides Pythonic access by implementing Python special methods (so
that an std::unordered_map object could be treated like a dict or
whatever).

(b) Ways to access the contents of containers like unique_ptr,
shared_ptr, etc. from python functions.  So if in my class I have
"std::unique_ptr<Foo> fooPtr" and in my python functions I have a
variable "fooPtr" which refers to this object, I would like a way to
retrieve a gdb.Value containing its pointer.

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Help using the GDB C++ STL pretty-printers / xmethods
  2022-05-07 19:07       ` Paul Smith
@ 2022-05-07 19:51         ` Jonathan Wakely
  2022-05-07 23:08           ` Paul Smith
  0 siblings, 1 reply; 21+ messages in thread
From: Jonathan Wakely @ 2022-05-07 19:51 UTC (permalink / raw)
  To: Paul Smith; +Cc: gcc-help

On Sat, 7 May 2022 at 20:07, Paul Smith <paul@mad-scientist.net> wrote:
>
> On Sat, 2022-05-07 at 16:35 +0100, Jonathan Wakely wrote:
> > On Sat, 7 May 2022 at 16:08, Paul Smith <paul@mad-scientist.net>
> > wrote:
> > > Thanks for the reply.  I should have mentioned this; what I do is:
> > >
> > >   python
> > >   from libstdcxx.v6 import register_libstdcxx_printers
> > >   register_libstdcxx_printers(None)
> > >   end
> >
> > Why are you doing this by hand? That should not be necessary.
>
> Well I have this in my init file so I don't really enter it by hand.
> But are you implying that it should somehow happen without having to do
> anything at all... how does that work?  Do you mean, that my
> distribution should have set things up so that it happens
> automatically?

Yes.

> Or that somehow GDB should "find" these macros on its
> own?
>
> Note I am building/installing my own GCC and GDB so maybe I just
> haven't put everything in the right places.

GCC's 'make install' should do everything needed. That installs
$prefix/lib64/libstdc++.so.6.0.30-gdb.py alongside the .so file, and
gdb auto-loads that when debugging a process linked to the
libstdc++.so.6.0.30 library. That python script imports the
register_libstdcxx_printers function and runs it.

Maybe you're only linking statically to libstdc++.a?

>
> > > Just to clarify are you saying that one or both of the methods I've
> > > tried (using * or -> operators) _should_ work, and that they do
> > > work for you when you try them?
> >
> > Yes.
>
> OK, I've discovered what's going on.  Apparently for some reason the
> ability to show the values doesn't happen immediately.  When I attach
> to a process and it's sitting in a sleep somewhere in the C runtime
> like this:
>
>   (gdb) bt
>   #0  0x00007f804634e26f in clock_nanosleep () from /lib/x86_64-linux-
> gnu/libc.so.6
>   #1  0x00007f8046353ef7 in nanosleep () from /lib/x86_64-linux-
> gnu/libc.so.6
>   #2  0x000000000097a3c9 in SignalHandler::sleepUntilSignal
> (this=<optimized out>, response=..., timeoutMs=<optimized out>) at
> SignalHandler.cpp:205
>   #3  0x0000000000960e64 in Engine::main (this=0x7f8045f0f000) at
> Engine.cpp:1374
>   #4  0x000000000098fbdf in Engine::runMain (this=0x7f8045f0f000) at
> Engine.cpp:793
>   #5  0x000000000095f907 in main (argc=<optimized out>, argv=<optimized
> out>) at main.cpp:59
>
> so my current frame is #0, then I run a python function I wrote that
> shows a bunch of info about this process and sets a convenience
> variable for the main pointer $mp, then I try to use it I get the
> errors I showed earlier:
>
>   (gdb) p $mp->mgr->initialized
>   One of the arguments you tried to pass to operator-> could not be
>   converted to what the function wants.
>
> but, now if I change my frame (to one of my frames... it doesn't help
> to go to frame #1 above) it works fine:
>
>   (gdb) fr 2
>   #2  0x000000000097a3c9 in SignalHandler::sleepUntilSignal
> (this=<optimized out>, response=..., timeoutMs=<optimized out>) at
> SignalHandler.cpp:205
>   205         ::nanosleep(&interval, NULL);
>
>   (gdb) p $mp->mgr->initialized
>   $1 = true
>
> Now I can go back down to frame 0 and it still works:
>
>   (gdb) fr 0
>   #0  0x00007f804634e26f in clock_nanosleep () from /lib/x86_64-linux-
> gnu/libc.so.6
>
>   (gdb) p $mp->mgr->initialized
>   $2 = true
>
> I can't find anything in the GDB docs about this but I guess it's a
> question for them, as to what's happening here.

Hmm, that's reminiscent of https://sourceware.org/bugzilla/show_bug.cgi?id=25234

> > > I'm not talking about printing things per se, I'm talking about
> > > writing my own python macros that help me examine my own data
> > > structures, which are built with STL types.
> > >
> > > For example I have a complex structure that uses std::vector,
> > > std::list, std:unordered_map, unique_ptr, etc. and I want to write
> > > my own methods that examine these structures, either to print them
> > > in a different way (not just the standard pretty-printer output) or
> > > whatever.
> >
> > That sounds useful, but it's unlikely anybody else is going to
> > provide it. If you want it, you get to build it :-)
>
> (sorry please replace "macro" with "Python function" everywhere in my
> email... I will try to do better)
>
> I'm pretty sure that's the same answer I got last time I asked :).
>
> I just don't have the time or, probably, the knowledge of the STL
> implementation I would need to do this well.  But I would be happy to
> work with someone, including writing some code, designing some
> features, etc., if anyone else were interested.
>
> What I would really like to have are two things:
>
> (a) iterator classes that I could instantiate from my python functions
> when I need to iterate through an STL type.  So if in my class I have
> "std::list<Foo> fooList" and in my python functions I have a python
> variable "fooList" which refers to this object, I would like a way to
> write something like:
>
>     for elt in StdForwardIterator(fooList):
>         <do something with elt>
>
> Map iterators could return a tuple, etc.
>
> I don't know if it makes more sense to create individual iterator
> classes like above, or to implement Python classes that wrap types and
> provides Pythonic access by implementing Python special methods (so
> that an std::unordered_map object could be treated like a dict or
> whatever).

All the logic to do that in Python is already present in the printers,
it's just that the iterators are GDB pretty-print iterators so have a
different API from general purpose Python iterables. They do exactly
what the printers want, which means the iterator returns ('[n]',
value) for each n in [0,cont.size()). And the StdMapPrinter._iter type
iterators over key,value,key,value,key,value because that's what a
printer with display_hint() = 'map' expects. For your purposes you
probably want (key,value),(key,value),(key,value). But starting from
the existing code and making it do that would take about five minutes
per container, I reckon. I don't think you need any knowledge of the
C++ implementation, because that's already been captured in Python
code.


> (b) Ways to access the contents of containers like unique_ptr,
> shared_ptr, etc. from python functions.  So if in my class I have
> "std::unique_ptr<Foo> fooPtr" and in my python functions I have a
> variable "fooPtr" which refers to this object, I would like a way to
> retrieve a gdb.Value containing its pointer.

The UniquePtrGetWorker Xmethod already does that. You should be able to just do:

 py ptr = gdb.parse_and_eval('uniqptr.get()')

Xmethods seems like the right way to do much of this, because they can
be used at the normal GDB prompt as well as within Python scripts
working in GDB. We could certainly add more Xmethods, but nobody is
asking for them or telling us which ones are missing. Adding things
nobody wants is a waste of time, there are dozens of other things we
can work on instead that people are actually asking for.

If we had a Python type to access a std::unique_ptr maybe it would
make sense to reuse that in both the printer and xmethods for
unique_ptr, and similarly for each other std type that was exposed via
some Python lookalike type. But somebody would have to do the work.

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Help using the GDB C++ STL pretty-printers / xmethods
  2022-05-07 19:51         ` Jonathan Wakely
@ 2022-05-07 23:08           ` Paul Smith
  2022-05-08  8:13             ` Jonathan Wakely
  0 siblings, 1 reply; 21+ messages in thread
From: Paul Smith @ 2022-05-07 23:08 UTC (permalink / raw)
  To: gcc-help

On Sat, 2022-05-07 at 20:51 +0100, Jonathan Wakely wrote:
> On Sat, 7 May 2022 at 20:07, Paul Smith <paul@mad-scientist.net>
> wrote:
> GCC's 'make install' should do everything needed. That installs
> $prefix/lib64/libstdc++.so.6.0.30-gdb.py alongside the .so file, and
> gdb auto-loads that when debugging a process linked to the
> libstdc++.so.6.0.30 library. That python script imports the
> register_libstdcxx_printers function and runs it.
> 
> Maybe you're only linking statically to libstdc++.a?

Ah.  Yes I'm linking statically.

> Hmm, that's reminiscent of
> https://sourceware.org/bugzilla/show_bug.cgi?id=25234

I checked and when I first attach I do see:

  (gdb) show lang
  The current source language is "auto; currently c".

and things don't work, then after I change to a C++ frame I see:

  (gdb) show lang
  The current source language is "auto; currently c++".

and things work.

I discovered that if I add:

  set language c++

to my init, that it all works properly.  For my purposes this is a
sufficient workaround.

It's a bit strange (confusing) that the C++ pretty-printers work
without having to do that, but the C++ xmethods do not.

Also just a data point, my previous GDB (10.2) didn't require this:
when I attach with that version, GDB chose the auto language as "c++"
immediately.  I suppose it's worth a bugzilla report.

> All the logic to do that in Python is already present in the
> printers,

I figured so I'd hoped there was something here already.  I get what
you're saying of course.  Maybe I'll find some time to dig into this...
at some point...

> > (b) Ways to access the contents of containers like unique_ptr,
> > shared_ptr, etc. from python functions.  So if in my class I have
> > "std::unique_ptr<Foo> fooPtr" and in my python functions I have a
> > variable "fooPtr" which refers to this object, I would like a way
> > to retrieve a gdb.Value containing its pointer.
> 
> The UniquePtrGetWorker Xmethod already does that. You should be able
> to just do:
> 
>  py ptr = gdb.parse_and_eval('uniqptr.get()')

xmethods don't help me (IIUC) because I'm in the middle of some Python
function and the value I want to retrieve is in a Python variable, not
in a GDB variable, so I can't easily access it with parse_and_eval().

For instance in my examples here I'd have a python method:

  def find_obj(val):
      if val['mgr']['initialized']:
          return val['mgr']
      return val['otherMgr']

or whatever, but of course I can't do this because val['mgr'] is a
std::unique_ptr and I don't know how to dig out the object it points
to.  The above doesn't need to work as-is: something like:

  def find_obj(val):
      mgr = StdUnique(val['mgr']).get()
      if mgr['initialized']:
          return mgr
      return val['otherMgr']

or whatever would be fine.

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Help using the GDB C++ STL pretty-printers / xmethods
  2022-05-07 23:08           ` Paul Smith
@ 2022-05-08  8:13             ` Jonathan Wakely
  2022-05-08  8:16               ` Jonathan Wakely
  0 siblings, 1 reply; 21+ messages in thread
From: Jonathan Wakely @ 2022-05-08  8:13 UTC (permalink / raw)
  To: Paul Smith; +Cc: gcc-help

On Sun, 8 May 2022, 00:09 Paul Smith, <paul@mad-scientist.net> wrote:

> On Sat, 2022-05-07 at 20:51 +0100, Jonathan Wakely wrote:
> > On Sat, 7 May 2022 at 20:07, Paul Smith <paul@mad-scientist.net>
> > wrote:
> > GCC's 'make install' should do everything needed. That installs
> > $prefix/lib64/libstdc++.so.6.0.30-gdb.py alongside the .so file, and
> > gdb auto-loads that when debugging a process linked to the
> > libstdc++.so.6.0.30 library. That python script imports the
> > register_libstdcxx_printers function and runs it.
> >
> > Maybe you're only linking statically to libstdc++.a?
>
> Ah.  Yes I'm linking statically.
>
> > Hmm, that's reminiscent of
> > https://sourceware.org/bugzilla/show_bug.cgi?id=25234
>
> I checked and when I first attach I do see:
>
>   (gdb) show lang
>   The current source language is "auto; currently c".
>
> and things don't work, then after I change to a C++ frame I see:
>
>   (gdb) show lang
>   The current source language is "auto; currently c++".
>
> and things work.
>
> I discovered that if I add:
>
>   set language c++
>
> to my init, that it all works properly.  For my purposes this is a
> sufficient workaround.
>
> It's a bit strange (confusing) that the C++ pretty-printers work
> without having to do that, but the C++ xmethods do not.
>
> Also just a data point, my previous GDB (10.2) didn't require this:
> when I attach with that version, GDB chose the auto language as "c++"
> immediately.  I suppose it's worth a bugzilla report.
>
> > All the logic to do that in Python is already present in the
> > printers,
>
> I figured so I'd hoped there was something here already.  I get what
> you're saying of course.  Maybe I'll find some time to dig into this...
> at some point...
>
> > > (b) Ways to access the contents of containers like unique_ptr,
> > > shared_ptr, etc. from python functions.  So if in my class I have
> > > "std::unique_ptr<Foo> fooPtr" and in my python functions I have a
> > > variable "fooPtr" which refers to this object, I would like a way
> > > to retrieve a gdb.Value containing its pointer.
> >
> > The UniquePtrGetWorker Xmethod already does that. You should be able
> > to just do:
> >
> >  py ptr = gdb.parse_and_eval('uniqptr.get()')
>
> xmethods don't help me (IIUC) because I'm in the middle of some Python
> function and the value I want to retrieve is in a Python variable, not
> in a GDB variable, so I can't easily access it with parse_and_eval().
>
> For instance in my examples here I'd have a python method:
>
>   def find_obj(val):
>       if val['mgr']['initialized']:
>           return val['mgr']
>       return val['otherMgr']
>
> or whatever, but of course I can't do this because val['mgr'] is a
> std::unique_ptr and I don't know how to dig out the object it points
> to.  The above doesn't need to work as-is: something like:
>
>   def find_obj(val):
>       mgr = StdUnique(val['mgr']).get()
>       if mgr['initialized']:
>           return mgr
>       return val['otherMgr']
>
> or whatever would be fine.
>


gdb.set_convenience_variable('mgr', val['mgr'])
init = gdb.parse_and_eval('$mgr->initialized')

This will use the xmethod to evaluate the expression.

>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Help using the GDB C++ STL pretty-printers / xmethods
  2022-05-08  8:13             ` Jonathan Wakely
@ 2022-05-08  8:16               ` Jonathan Wakely
  2022-05-08 14:09                 ` Paul Smith
  2022-05-08 19:44                 ` Paul Smith
  0 siblings, 2 replies; 21+ messages in thread
From: Jonathan Wakely @ 2022-05-08  8:16 UTC (permalink / raw)
  To: Paul Smith; +Cc: gcc-help

On Sun, 8 May 2022, 09:13 Jonathan Wakely, <jwakely.gcc@gmail.com> wrote:

>
>
> On Sun, 8 May 2022, 00:09 Paul Smith, <paul@mad-scientist.net> wrote:
>
>> On Sat, 2022-05-07 at 20:51 +0100, Jonathan Wakely wrote:
>> > On Sat, 7 May 2022 at 20:07, Paul Smith <paul@mad-scientist.net>
>> > wrote:
>> > GCC's 'make install' should do everything needed. That installs
>> > $prefix/lib64/libstdc++.so.6.0.30-gdb.py alongside the .so file, and
>> > gdb auto-loads that when debugging a process linked to the
>> > libstdc++.so.6.0.30 library. That python script imports the
>> > register_libstdcxx_printers function and runs it.
>> >
>> > Maybe you're only linking statically to libstdc++.a?
>>
>> Ah.  Yes I'm linking statically.
>>
>> > Hmm, that's reminiscent of
>> > https://sourceware.org/bugzilla/show_bug.cgi?id=25234
>>
>> I checked and when I first attach I do see:
>>
>>   (gdb) show lang
>>   The current source language is "auto; currently c".
>>
>> and things don't work, then after I change to a C++ frame I see:
>>
>>   (gdb) show lang
>>   The current source language is "auto; currently c++".
>>
>> and things work.
>>
>> I discovered that if I add:
>>
>>   set language c++
>>
>> to my init, that it all works properly.  For my purposes this is a
>> sufficient workaround.
>>
>> It's a bit strange (confusing) that the C++ pretty-printers work
>> without having to do that, but the C++ xmethods do not.
>>
>> Also just a data point, my previous GDB (10.2) didn't require this:
>> when I attach with that version, GDB chose the auto language as "c++"
>> immediately.  I suppose it's worth a bugzilla report.
>>
>> > All the logic to do that in Python is already present in the
>> > printers,
>>
>> I figured so I'd hoped there was something here already.  I get what
>> you're saying of course.  Maybe I'll find some time to dig into this...
>> at some point...
>>
>> > > (b) Ways to access the contents of containers like unique_ptr,
>> > > shared_ptr, etc. from python functions.  So if in my class I have
>> > > "std::unique_ptr<Foo> fooPtr" and in my python functions I have a
>> > > variable "fooPtr" which refers to this object, I would like a way
>> > > to retrieve a gdb.Value containing its pointer.
>> >
>> > The UniquePtrGetWorker Xmethod already does that. You should be able
>> > to just do:
>> >
>> >  py ptr = gdb.parse_and_eval('uniqptr.get()')
>>
>> xmethods don't help me (IIUC) because I'm in the middle of some Python
>> function and the value I want to retrieve is in a Python variable, not
>> in a GDB variable, so I can't easily access it with parse_and_eval().
>>
>> For instance in my examples here I'd have a python method:
>>
>>   def find_obj(val):
>>       if val['mgr']['initialized']:
>>           return val['mgr']
>>       return val['otherMgr']
>>
>> or whatever, but of course I can't do this because val['mgr'] is a
>> std::unique_ptr and I don't know how to dig out the object it points
>> to.  The above doesn't need to work as-is: something like:
>>
>>   def find_obj(val):
>>       mgr = StdUnique(val['mgr']).get()
>>       if mgr['initialized']:
>>           return mgr
>>       return val['otherMgr']
>>
>> or whatever would be fine.
>>
>
>
> gdb.set_convenience_variable('mgr', val['mgr'])
> init = gdb.parse_and_eval('$mgr->initialized')
>
> This will use the xmethod to evaluate the expression.
>

And then:

if init:
    return gdb.parse_and_eval('*$mgr')

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Help using the GDB C++ STL pretty-printers / xmethods
  2022-05-08  8:16               ` Jonathan Wakely
@ 2022-05-08 14:09                 ` Paul Smith
  2022-05-08 14:36                   ` Jonathan Wakely
  2022-05-08 19:44                 ` Paul Smith
  1 sibling, 1 reply; 21+ messages in thread
From: Paul Smith @ 2022-05-08 14:09 UTC (permalink / raw)
  To: Jonathan Wakely; +Cc: gcc-help

On Sun, 2022-05-08 at 09:16 +0100, Jonathan Wakely wrote:
> > > xmethods don't help me (IIUC) because I'm in the middle of some
> > > Python function and the value I want to retrieve is in a Python
> > > variable, not in a GDB variable, so I can't easily access it with
> > > parse_and_eval().
> > 
> > gdb.set_convenience_variable('mgr', val['mgr'])
> > init = gdb.parse_and_eval('$mgr->initialized')
> > 
> > This will use the xmethod to evaluate the expression.
> 
> And then:
> 
> if init:
>     return gdb.parse_and_eval('*$mgr')

Yes... this is why I qualified my statement with "easily" :).

Also if I'm doing this as I search through a 700,000 element container,
which is already not super-speedy, the performance is likely not going
to be great.

I imagine the cleanest solution is to have a Python API for STL that
works with and returns gdb.Values, then have the pretty-printers call
that API and format the result.  Then others can use that API as well.

I get that the current suite of Python functions are geared
specifically towards using STL objects directly from the GDB UI: both
pretty-printing and accessing them.  As someone who's writing my own
Python functions for a complex C++ program, however, an API as above
would be more helpful; we have lots of methods that grovel through
complex structures looking for specific elements, etc.

And, I understand the answer "patches welcome" :).  I'm just putting
this out there for people to consider.

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Help using the GDB C++ STL pretty-printers / xmethods
  2022-05-08 14:09                 ` Paul Smith
@ 2022-05-08 14:36                   ` Jonathan Wakely
  0 siblings, 0 replies; 21+ messages in thread
From: Jonathan Wakely @ 2022-05-08 14:36 UTC (permalink / raw)
  To: Paul Smith; +Cc: gcc-help

On Sun, 8 May 2022 at 15:09, Paul Smith <paul@mad-scientist.net> wrote:
>
> On Sun, 2022-05-08 at 09:16 +0100, Jonathan Wakely wrote:
> > > > xmethods don't help me (IIUC) because I'm in the middle of some
> > > > Python function and the value I want to retrieve is in a Python
> > > > variable, not in a GDB variable, so I can't easily access it with
> > > > parse_and_eval().
> > >
> > > gdb.set_convenience_variable('mgr', val['mgr'])
> > > init = gdb.parse_and_eval('$mgr->initialized')
> > >
> > > This will use the xmethod to evaluate the expression.
> >
> > And then:
> >
> > if init:
> >     return gdb.parse_and_eval('*$mgr')
>
> Yes... this is why I qualified my statement with "easily" :).
>
> Also if I'm doing this as I search through a 700,000 element container,
> which is already not super-speedy, the performance is likely not going
> to be great.
>
> I imagine the cleanest solution is to have a Python API for STL that
> works with and returns gdb.Values, then have the pretty-printers call
> that API and format the result.  Then others can use that API as well.
>
> I get that the current suite of Python functions are geared
> specifically towards using STL objects directly from the GDB UI: both
> pretty-printing and accessing them.  As someone who's writing my own
> Python functions for a complex C++ program, however, an API as above
> would be more helpful; we have lots of methods that grovel through
> complex structures looking for specific elements, etc.

Then you are the perfect person to scratch this itch :-)


>
> And, I understand the answer "patches welcome" :).  I'm just putting
> this out there for people to consider.

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Help using the GDB C++ STL pretty-printers / xmethods
  2022-05-08  8:16               ` Jonathan Wakely
  2022-05-08 14:09                 ` Paul Smith
@ 2022-05-08 19:44                 ` Paul Smith
  2022-05-08 20:26                   ` Paul Smith
  2022-05-09  9:32                   ` Jonathan Wakely
  1 sibling, 2 replies; 21+ messages in thread
From: Paul Smith @ 2022-05-08 19:44 UTC (permalink / raw)
  To: Jonathan Wakely; +Cc: gcc-help

On Sun, 2022-05-08 at 09:16 +0100, Jonathan Wakely wrote:
> > gdb.set_convenience_variable('mgr', val['mgr'])
> > init = gdb.parse_and_eval('$mgr->initialized')
> > 
> > This will use the xmethod to evaluate the expression.
> 
> And then:
> 
> if init:
>     return gdb.parse_and_eval('*$mgr')

Unfortunately, this doesn't work :(.  I can't do it from the GDB
command line or python (I have tried both with the same results). 
Something about convenience variables doesn't play well with xmethods 
(or maybe this xmethod implementation specifically?)

Very simple test program:

  /* /tmp/sleep.cpp */
  #include <unistd.h>
  #include <memory>

  class Foo { public: int val = 0; };

  std::unique_ptr<Foo> foo;

  int main()
  {
      foo = std::make_unique<Foo>();
      sleep(100000);
      return 0;
  }

Compile it & run it:

  g++ -ggdb3 -o /tmp/sleep /tmp/sleep.cpp
  /tmp/sleep &

Now attach with GDB (probably you can just run it in GDB but I did it
like this so I could detach/reattach):

  gdb -p <pid>

Switch to main, set an convenience variable to "foo", then try to use
xmethods on it and no joy:

  (gdb) fr 3
  11          sleep(100000);

  (gdb) p foo
  $1 = std::unique_ptr<Foo> = {
    get() = 0x105aeb0
  }
  (gdb) p foo->val
  $2 = 0

  (gdb) set $xx = foo
  (gdb) p $xx
  $3 = std::unique_ptr<Foo> = {
    get() = 0x105aeb0
  }

  (gdb) p $xx->val
  Attempt to take address of value not located in memory.

Same behavior with GDB 10.2 and 12.1.

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Help using the GDB C++ STL pretty-printers / xmethods
  2022-05-08 19:44                 ` Paul Smith
@ 2022-05-08 20:26                   ` Paul Smith
  2022-05-09 10:47                     ` Hannes Domani
  2022-05-09  9:32                   ` Jonathan Wakely
  1 sibling, 1 reply; 21+ messages in thread
From: Paul Smith @ 2022-05-08 20:26 UTC (permalink / raw)
  To: Jonathan Wakely; +Cc: gcc-help

On Sun, 2022-05-08 at 15:44 -0400, Paul Smith wrote:
> On Sun, 2022-05-08 at 09:16 +0100, Jonathan Wakely wrote:
> > > gdb.set_convenience_variable('mgr', val['mgr'])
> > > init = gdb.parse_and_eval('$mgr->initialized')
> > > 
> > > This will use the xmethod to evaluate the expression.
> > 
> > And then:
> > 
> > if init:
> >     return gdb.parse_and_eval('*$mgr')
> 
> Unfortunately, this doesn't work :(.  I can't do it from the GDB
> command line or python (I have tried both with the same results). 
> Something about convenience variables doesn't play well with xmethods
> (or maybe this xmethod implementation specifically?)

Hm.  I have done a fair amount of work writing GDB Python convenience
functions and commands, but I've only done a small amount of pretty-
printer work and no xmethod implementations.

But I don't understand this from the C++ STL xmethods.py:

  class UniquePtrGetWorker(gdb.xmethod.XMethodWorker):
    ...
      def __call__(self, obj):
          impl_type = obj.dereference().type.fields()[0].type.tag

Why are we using dereference() here?  Aren't we expecting to receive a
gdb.Value of type std::unique_ptr here, not _pointer to_
std::unique_ptr?

But, it definitely doesn't work to remove the dereference(), and also
the value we get normally IS a pointer; adding debugging above I see:

  type: 'std::unique_ptr<Foo, std::default_delete<Foo> > *'

I don't really grok xmethods so I'm not sure how calling "foo->val"
when "foo" is std::unique_ptr<> results in the __call__ method being
invoked with a gdb.Value of type std::unique_ptr<>*.

My suspicion (not based on anything) is that whatever the reason is, is
why I can't use these xmethods with a convenience variable.

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Help using the GDB C++ STL pretty-printers / xmethods
  2022-05-08 19:44                 ` Paul Smith
  2022-05-08 20:26                   ` Paul Smith
@ 2022-05-09  9:32                   ` Jonathan Wakely
  2022-05-09 11:23                     ` Jonathan Wakely
  1 sibling, 1 reply; 21+ messages in thread
From: Jonathan Wakely @ 2022-05-09  9:32 UTC (permalink / raw)
  To: Paul Smith; +Cc: gcc-help

On Sun, 8 May 2022 at 20:44, Paul Smith <paul@mad-scientist.net> wrote:
>
> On Sun, 2022-05-08 at 09:16 +0100, Jonathan Wakely wrote:
> > > gdb.set_convenience_variable('mgr', val['mgr'])
> > > init = gdb.parse_and_eval('$mgr->initialized')
> > >
> > > This will use the xmethod to evaluate the expression.
> >
> > And then:
> >
> > if init:
> >     return gdb.parse_and_eval('*$mgr')
>
> Unfortunately, this doesn't work :(.  I can't do it from the GDB
> command line or python (I have tried both with the same results).
> Something about convenience variables doesn't play well with xmethods
> (or maybe this xmethod implementation specifically?)

You're right, sorry. It doesn't work for me with a convenience variable.

But since what you want is something that works in arbitrary Python
code, not just within GDB, doesn't pybind11 already do everything you
want?
https://github.com/pybind/pybind11

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Help using the GDB C++ STL pretty-printers / xmethods
  2022-05-08 20:26                   ` Paul Smith
@ 2022-05-09 10:47                     ` Hannes Domani
  2022-05-09 10:52                       ` Hannes Domani
  0 siblings, 1 reply; 21+ messages in thread
From: Hannes Domani @ 2022-05-09 10:47 UTC (permalink / raw)
  To: Jonathan Wakely, Paul Smith; +Cc: gcc-help

 Am Sonntag, 8. Mai 2022, 22:26:37 MESZ hat Paul Smith <paul@mad-scientist.net> Folgendes geschrieben:

> On Sun, 2022-05-08 at 15:44 -0400, Paul Smith wrote:
> > On Sun, 2022-05-08 at 09:16 +0100, Jonathan Wakely wrote:
> > > > gdb.set_convenience_variable('mgr', val['mgr'])
> > > > init = gdb.parse_and_eval('$mgr->initialized')
> > > >
> > > > This will use the xmethod to evaluate the expression.
> > >
> > > And then:
> > >
> > > if init:
> > >     return gdb.parse_and_eval('*$mgr')
> >
> > Unfortunately, this doesn't work :(.  I can't do it from the GDB
> > command line or python (I have tried both with the same results).
> > Something about convenience variables doesn't play well with xmethods
> > (or maybe this xmethod implementation specifically?)
>
> Hm.  I have done a fair amount of work writing GDB Python convenience
> functions and commands, but I've only done a small amount of pretty-
> printer work and no xmethod implementations.
>
> But I don't understand this from the C++ STL xmethods.py:
>
>   class UniquePtrGetWorker(gdb.xmethod.XMethodWorker):
>     ...
>       def __call__(self, obj):
>           impl_type = obj.dereference().type.fields()[0].type.tag
>
> Why are we using dereference() here?  Aren't we expecting to receive a
> gdb.Value of type std::unique_ptr here, not _pointer to_
>
> std::unique_ptr?
>
>
> But, it definitely doesn't work to remove the dereference(), and also
> the value we get normally IS a pointer; adding debugging above I see:
>
>   type: 'std::unique_ptr<Foo, std::default_delete<Foo> > *'
>
> I don't really grok xmethods so I'm not sure how calling "foo->val"
> when "foo" is std::unique_ptr<> results in the __call__ method being
> invoked with a gdb.Value of type std::unique_ptr<>*.
>
> My suspicion (not based on anything) is that whatever the reason is, is
> why I can't use these xmethods with a convenience variable.

This is documented in the XMethod API, see XMethodWorker.__call__ in [1]:

The first element is always the this pointer value.


Why don't you just set the convenience variable to the address?:

(gdb) set $xx=&foo
(gdb) p (*$xx)->val
$7 = 0


Hannes

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Help using the GDB C++ STL pretty-printers / xmethods
  2022-05-09 10:47                     ` Hannes Domani
@ 2022-05-09 10:52                       ` Hannes Domani
  0 siblings, 0 replies; 21+ messages in thread
From: Hannes Domani @ 2022-05-09 10:52 UTC (permalink / raw)
  To: Jonathan Wakely, Paul Smith; +Cc: gcc-help

 Am Montag, 9. Mai 2022, 12:47:49 MESZ hat Hannes Domani <ssbssa@yahoo.de> Folgendes geschrieben:

> Am Sonntag, 8. Mai 2022, 22:26:37 MESZ hat Paul Smith <paul@mad-scientist.net> Folgendes geschrieben:
>
> > On Sun, 2022-05-08 at 15:44 -0400, Paul Smith wrote:
> > > On Sun, 2022-05-08 at 09:16 +0100, Jonathan Wakely wrote:
> > > > > gdb.set_convenience_variable('mgr', val['mgr'])
> > > > > init = gdb.parse_and_eval('$mgr->initialized')
> > > > >
> > > > > This will use the xmethod to evaluate the expression.
> > > >
> > > > And then:
> > > >
> > > > if init:
> > > >     return gdb.parse_and_eval('*$mgr')
> > >
> > > Unfortunately, this doesn't work :(.  I can't do it from the GDB
> > > command line or python (I have tried both with the same results).
> > > Something about convenience variables doesn't play well with xmethods
> > > (or maybe this xmethod implementation specifically?)
> >
> > Hm.  I have done a fair amount of work writing GDB Python convenience
> > functions and commands, but I've only done a small amount of pretty-
> > printer work and no xmethod implementations.
> >
> > But I don't understand this from the C++ STL xmethods.py:
> >
> >   class UniquePtrGetWorker(gdb.xmethod.XMethodWorker):
> >     ...
> >       def __call__(self, obj):
> >           impl_type = obj.dereference().type.fields()[0].type.tag
> >
> > Why are we using dereference() here?  Aren't we expecting to receive a
> > gdb.Value of type std::unique_ptr here, not _pointer to_
> >
> > std::unique_ptr?
> >
> >
> > But, it definitely doesn't work to remove the dereference(), and also
> > the value we get normally IS a pointer; adding debugging above I see:
> >
> >   type: 'std::unique_ptr<Foo, std::default_delete<Foo> > *'
> >
> > I don't really grok xmethods so I'm not sure how calling "foo->val"
> > when "foo" is std::unique_ptr<> results in the __call__ method being
> > invoked with a gdb.Value of type std::unique_ptr<>*.
> >
> > My suspicion (not based on anything) is that whatever the reason is, is
> > why I can't use these xmethods with a convenience variable.
>
>
> This is documented in the XMethod API, see XMethodWorker.__call__ in [1]:
>
> The first element is always the this pointer value.
>
>
> Why don't you just set the convenience variable to the address?:
>
> (gdb) set $xx=&foo
> (gdb) p (*$xx)->val
> $7 = 0


I forgot to add the link.

[1] https://sourceware.org/gdb/current/onlinedocs/gdb/Xmethod-API.html#index-XMethodWorker_002e_005f_005fcall_005f_005f

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Help using the GDB C++ STL pretty-printers / xmethods
  2022-05-09  9:32                   ` Jonathan Wakely
@ 2022-05-09 11:23                     ` Jonathan Wakely
  2022-05-09 14:05                       ` Paul Smith
  0 siblings, 1 reply; 21+ messages in thread
From: Jonathan Wakely @ 2022-05-09 11:23 UTC (permalink / raw)
  To: Paul Smith; +Cc: gcc-help

[-- Attachment #1: Type: text/plain, Size: 2796 bytes --]

On Mon, 9 May 2022 at 10:32, Jonathan Wakely <jwakely.gcc@gmail.com> wrote:
>
> On Sun, 8 May 2022 at 20:44, Paul Smith <paul@mad-scientist.net> wrote:
> >
> > On Sun, 2022-05-08 at 09:16 +0100, Jonathan Wakely wrote:
> > > > gdb.set_convenience_variable('mgr', val['mgr'])

This doesn't do what I thought it does, meaning that $mgr ends up
defined to void. I think you can't set a gdb.Value as the value of a
convenience variable.

> > > > init = gdb.parse_and_eval('$mgr->initialized')
> > > >
> > > > This will use the xmethod to evaluate the expression.
> > >
> > > And then:
> > >
> > > if init:
> > >     return gdb.parse_and_eval('*$mgr')
> >
> > Unfortunately, this doesn't work :(.  I can't do it from the GDB
> > command line or python (I have tried both with the same results).
> > Something about convenience variables doesn't play well with xmethods
> > (or maybe this xmethod implementation specifically?)
>
> You're right, sorry. It doesn't work for me with a convenience variable.

And even if I don't set the convenience variable to a gdb.Value, it
still doesn't work with the xmethod:

(gdb) set $mgr = x.mgr
(gdb) whatis $mgr
type = std::unique_ptr<Mgr>
(gdb) p $mgr
$2 = std::unique_ptr<Mgr> = {get() = 0x416eb0}
(gdb) p $mgr->initialized
Attempt to take address of value not located in memory.


>
> But since what you want is something that works in arbitrary Python
> code, not just within GDB, doesn't pybind11 already do everything you
> want?
> https://github.com/pybind/pybind11

If you don't want to (or can't) use pybind11 to create Python
bindings, and you don't want to use the GDB Python API, then you will
have a ton of work to do. The GDB Python API is what provides all the
tools for traversing C++ class hierarchies, examining template
arguments, casting values to related types etc.

The attached diff refactors the std::unique_ptr pretty printer to be
defined in terms of a new types.UniquePtr class that exposes the
std::unique_ptr API to the printer (and the start of a types.Tuple
class for std::tuple, but I didn't make the TuplePrinter use that yet,
and didn't change the xmethods yet). This was mostly just a bit of
copy & paste, and renaming some functions. The hardest part for me was
figuring out how Python module imports work, not refactoring the
actual code. But it all still relies on the GDB API. Doing it in pure
Python would be much more work.

This patch can't be committed though, because it breaks the libstdc++
testsuite. The gdb-test.exp procs expect to be able to source a single
Python script, which breaks if that script tries to import modules in
the same package (because there's no package when GDB loads the Python
file directly via "source blah/blah/printers.py"). That would need to
be fixed to go ahead with changes like this.

[-- Attachment #2: patch.txt --]
[-- Type: text/plain, Size: 10948 bytes --]

commit aedd1591cd8be6ec2bcf1d23d6495fbb7bbb2f9a
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Mon May 9 12:07:26 2022

    libstdc++: Create Python wrappers for std::tuple and std::unique_ptr
    
    libstdc++-v3/ChangeLog:
    
            * python/Makefile.am: Install new scripts.
            * python/Makefile.in: Regenerate.
            * python/libstdcxx/v6/printers.py:
            * python/libstdcxx/v6/types.py: New file.
            * python/libstdcxx/v6/util.py: New file.

diff --git a/libstdc++-v3/python/Makefile.am b/libstdc++-v3/python/Makefile.am
index f523d3a44dc..70b34f74b49 100644
--- a/libstdc++-v3/python/Makefile.am
+++ b/libstdc++-v3/python/Makefile.am
@@ -39,6 +39,8 @@ all-local: gdb.py
 
 nobase_python_DATA = \
     libstdcxx/v6/printers.py \
+    libstdcxx/v6/types.py \
+    libstdcxx/v6/util.py \
     libstdcxx/v6/xmethods.py \
     libstdcxx/v6/__init__.py \
     libstdcxx/__init__.py
diff --git a/libstdc++-v3/python/libstdcxx/v6/printers.py b/libstdc++-v3/python/libstdcxx/v6/printers.py
index 0bd793c0897..f71f12245be 100644
--- a/libstdc++-v3/python/libstdcxx/v6/printers.py
+++ b/libstdc++-v3/python/libstdcxx/v6/printers.py
@@ -19,6 +19,10 @@ import gdb
 import itertools
 import re
 import sys, os, errno
+from libstdcxx.v6 import types
+from libstdcxx.v6.util import _versioned_namespace
+from libstdcxx.v6.util import is_specialization_of
+from libstdcxx.v6.util import get_template_arg_list
 
 ### Python 2 + Python 3 compatibility code
 
@@ -100,8 +104,6 @@ def find_type(orig, name):
         else:
             raise ValueError("Cannot find type %s::%s" % (str(orig), name))
 
-_versioned_namespace = '__8::'
-
 def lookup_templ_spec(templ, *args):
     """
     Lookup template specialization templ<args...>
@@ -165,15 +167,6 @@ def is_member_of_namespace(typ, *namespaces):
             return True
     return False
 
-def is_specialization_of(x, template_name):
-    "Test if a type is a given template instantiation."
-    global _versioned_namespace
-    if type(x) is gdb.Type:
-        x = x.tag
-    if _versioned_namespace:
-        return re.match('^std::(%s)?%s<.*>$' % (_versioned_namespace, template_name), x) is not None
-    return re.match('^std::%s<.*>$' % template_name, x) is not None
-
 def strip_versioned_namespace(typename):
     global _versioned_namespace
     if _versioned_namespace:
@@ -191,17 +184,6 @@ def strip_inline_namespaces(type_str):
     type_str = type_str.replace(fs_ns+'v1::', fs_ns)
     return type_str
 
-def get_template_arg_list(type_obj):
-    "Return a type's template arguments as a list"
-    n = 0
-    template_args = []
-    while True:
-        try:
-            template_args.append(type_obj.template_argument(n))
-        except:
-            return template_args
-        n += 1
-
 class SmartPtrIterator(Iterator):
     "An iterator for smart pointer types with a single 'child' value"
 
@@ -252,55 +234,6 @@ class SharedPointerPrinter:
                 state = 'use count %d, weak count %d' % (usecount, weakcount - 1)
         return '%s<%s> (%s)' % (self.typename, str(targ), state)
 
-def _tuple_impl_get(val):
-    "Return the tuple element stored in a _Tuple_impl<N, T> base class."
-    bases = val.type.fields()
-    if not bases[-1].is_base_class:
-        raise ValueError("Unsupported implementation for std::tuple: %s" % str(val.type))
-    # Get the _Head_base<N, T> base class:
-    head_base = val.cast(bases[-1].type)
-    fields = head_base.type.fields()
-    if len(fields) == 0:
-        raise ValueError("Unsupported implementation for std::tuple: %s" % str(val.type))
-    if fields[0].name == '_M_head_impl':
-        # The tuple element is the _Head_base::_M_head_impl data member.
-        return head_base['_M_head_impl']
-    elif fields[0].is_base_class:
-        # The tuple element is an empty base class of _Head_base.
-        # Cast to that empty base class.
-        return head_base.cast(fields[0].type)
-    else:
-        raise ValueError("Unsupported implementation for std::tuple: %s" % str(val.type))
-
-def tuple_get(n, val):
-    "Return the result of std::get<n>(val) on a std::tuple"
-    tuple_size = len(get_template_arg_list(val.type))
-    if n > tuple_size:
-        raise ValueError("Out of range index for std::get<N> on std::tuple")
-    # Get the first _Tuple_impl<0, T...> base class:
-    node = val.cast(val.type.fields()[0].type)
-    while n > 0:
-        # Descend through the base classes until the Nth one.
-        node = node.cast(node.type.fields()[0].type)
-        n -= 1
-    return _tuple_impl_get(node)
-
-def unique_ptr_get(val):
-    "Return the result of val.get() on a std::unique_ptr"
-    # std::unique_ptr<T, D> contains a std::tuple<D::pointer, D>,
-    # either as a direct data member _M_t (the old implementation)
-    # or within a data member of type __uniq_ptr_data.
-    impl_type = val.type.fields()[0].type.strip_typedefs()
-    # Check for new implementations first:
-    if is_specialization_of(impl_type, '__uniq_ptr_data') \
-        or is_specialization_of(impl_type, '__uniq_ptr_impl'):
-        tuple_member = val['_M_t']['_M_t']
-    elif is_specialization_of(impl_type, 'tuple'):
-        tuple_member = val['_M_t']
-    else:
-        raise ValueError("Unsupported implementation for unique_ptr: %s" % str(impl_type))
-    return tuple_get(0, tuple_member)
-
 class UniquePointerPrinter:
     "Print a unique_ptr"
 
@@ -308,7 +241,7 @@ class UniquePointerPrinter:
         self.val = val
 
     def children (self):
-        return SmartPtrIterator(unique_ptr_get(self.val))
+        return SmartPtrIterator(types.UniquePtr(self.val).get())
 
     def to_string (self):
         return ('std::unique_ptr<%s>' % (str(self.val.type.template_argument(0))))
@@ -1413,7 +1346,7 @@ class StdPathPrinter:
     def __init__ (self, typename, val):
         self.val = val
         self.typename = typename
-        impl = unique_ptr_get(self.val['_M_cmpts']['_M_impl'])
+        impl = types.UniquePtr(self.val['_M_cmpts']['_M_impl']).get()
         self.type = impl.cast(gdb.lookup_type('uintptr_t')) & 3
         if self.type == 0:
             self.impl = impl
diff --git a/libstdc++-v3/python/libstdcxx/v6/types.py b/libstdc++-v3/python/libstdcxx/v6/types.py
new file mode 100644
index 00000000000..65feac0d600
--- /dev/null
+++ b/libstdc++-v3/python/libstdcxx/v6/types.py
@@ -0,0 +1,87 @@
+import gdb
+from libstdcxx.v6.util import is_specialization_of
+from libstdcxx.v6.util import get_template_arg_list
+
+class Tuple:
+    "Python wrapper for std::tuple<T...>"
+
+    def __init__(self, val):
+        self.val = val
+
+    def tuple_size(self):
+        "Return tuple_size_v<tuple<T...>>"
+        return len(get_template_arg_list(self.val.type))
+
+    def element_type(self, n):
+        "Return a gdb.Type for the tuple_element_t<n, tuple<T...>> type"
+        return self._nth_element(n).template_argument(1)
+
+    def get(self, n):
+        "Return the result of std::get<n> on a std::tuple"
+        return self._impl_get(self._nth_element(n))
+
+    def _nth_element(self, n):
+        "Return a gdb.Value for the _Tuple_impl<n, T> base class"
+        if n >= self.tuple_size():
+            raise ValueError("Out of range index for std::get<{}> on std::tuple".format(n))
+        # Get the first _Tuple_impl<0, T...> base class:
+        node = self.val.cast(self.val.type.fields()[0].type)
+        while n > 0:
+            # Descend through the base classes until the Nth one.
+            node = node.cast(node.type.fields()[0].type)
+            n -= 1
+        return node
+
+    def _impl_get(self, node):
+        "Return the tuple element stored in a _Tuple_impl<N, T> base class."
+        bases = node.type.fields()
+        if not bases[-1].is_base_class:
+            raise ValueError("Unsupported implementation for std::tuple: %s" % str(node.type))
+        # Get the _Head_base<N, T> base class:
+        head_base = node.cast(bases[-1].type)
+        fields = head_base.type.fields()
+        if len(fields) == 0:
+            raise ValueError("Unsupported implementation for std::tuple: %s" % str(node.type))
+        if fields[0].name == '_M_head_impl':
+            # The tuple element is the _Head_base::_M_head_impl data member.
+            return head_base['_M_head_impl']
+        elif fields[0].is_base_class:
+            # The tuple element is an empty base class of _Head_base.
+            # Cast to that empty base class.
+            return head_base.cast(fields[0].type)
+        else:
+            raise ValueError("Unsupported implementation for std::tuple: %s" % str(node.type))
+
+class UniquePtr:
+    "Python wrapper for std::unique_ptr<T, D>"
+
+    def __init__ (self, val):
+        self.val = val
+
+    #def pointer_type(self):
+    #    "Return a gdb.Type for pointer typedef"
+    #    return TODO
+
+    def element_type(self):
+        "Return a gdb.Type for the element_type typedef"
+        return self.val.type.template_argument(0)
+
+    def deleter_type(self):
+        "Return a gdb.Type for the deleter_type typedef"
+        return self.val.type.template_argument(1)
+
+    def get(self):
+        "Return a gdb.Value for the get() member function"
+        # std::unique_ptr<T, D> contains a std::tuple<D::pointer, D>,
+        # either as a direct data member _M_t (the old implementation)
+        # or within a data member of type __uniq_ptr_data.
+        impl_type = self.val.type.fields()[0].type.strip_typedefs()
+        # Check for new implementations first:
+        if is_specialization_of(impl_type, '__uniq_ptr_data') \
+            or is_specialization_of(impl_type, '__uniq_ptr_impl'):
+            tuple_member = self.val['_M_t']['_M_t']
+        elif is_specialization_of(impl_type, 'tuple'):
+            tuple_member = self.val['_M_t']
+        else:
+            raise ValueError("Unsupported implementation for unique_ptr: %s" % str(impl_type))
+        return Tuple(tuple_member).get(0)
diff --git a/libstdc++-v3/python/libstdcxx/v6/util.py b/libstdc++-v3/python/libstdcxx/v6/util.py
new file mode 100644
index 00000000000..82e9ce665e0
--- /dev/null
+++ b/libstdc++-v3/python/libstdcxx/v6/util.py
@@ -0,0 +1,24 @@
+import re
+import gdb
+
+_versioned_namespace = '__8::'
+
+def is_specialization_of(x, template_name):
+    "Test if a type is a given template instantiation."
+    global _versioned_namespace
+    if type(x) is gdb.Type:
+        x = x.tag
+    if _versioned_namespace:
+        return re.match('^std::(%s)?%s<.*>$' % (_versioned_namespace, template_name), x) is not None
+    return re.match('^std::%s<.*>$' % template_name, x) is not None
+
+def get_template_arg_list(type_obj):
+    "Return a type's template arguments as a list"
+    n = 0
+    template_args = []
+    while True:
+        try:
+            template_args.append(type_obj.template_argument(n))
+        except:
+            return template_args
+        n += 1

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Help using the GDB C++ STL pretty-printers / xmethods
  2022-05-09 11:23                     ` Jonathan Wakely
@ 2022-05-09 14:05                       ` Paul Smith
  2022-05-09 14:40                         ` Paul Smith
  0 siblings, 1 reply; 21+ messages in thread
From: Paul Smith @ 2022-05-09 14:05 UTC (permalink / raw)
  To: Jonathan Wakely; +Cc: gcc-help

[-- Attachment #1: Type: text/plain, Size: 1464 bytes --]

On Mon, 2022-05-09 at 12:23 +0100, Jonathan Wakely wrote:
> > But since what you want is something that works in arbitrary Python
> > code, not just within GDB, doesn't pybind11 already do everything
> > you
> > want?
> > https://github.com/pybind/pybind11
> 
> If you don't want to (or can't) use pybind11 to create Python
> bindings, and you don't want to use the GDB Python API, then you will
> have a ton of work to do. The GDB Python API is what provides all the
> tools for traversing C++ class hierarchies, examining template
> arguments, casting values to related types etc.

I think I've given the wrong impression: I definitely do not want to
avoid using the GDB Python API and I have no need for my python code to
work outside of GDB.

I was playing with this yesterday and came up with the attached
creating an "accessors.py" API.  It's certainly not fully-fleshed out
and is not well-tested; it doesn't try to address any container types.
More of a thought experiment.

The idea is that someone could import libstdcxx.v6.accessors and write
their own code using these Python types.  An alternative would of
course be to write a bunch of free methods, rather than create classes.

I went the other way and started with the xmethod implementation, so
mine doesn't do much with tuples.  I looked at printers.py a bit, and
considered moving the versioned namespace facilities to this new area
as well, but didn't go further.

[-- Attachment #2: accessors.py --]
[-- Type: text/x-python, Size: 4505 bytes --]

# Accessor methods for libstdc++.

# Copyright (C) 2022 Free Software Foundation, Inc.

# This program 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 of the License, or
# (at your option) any later version.
#
# This program 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 program.  If not, see <http://www.gnu.org/licenses/>.

import gdb
import re

try:
    from typing import Optional
except ImportError:
    pass


class AccessorBase(object):
    """Base class for accessors of C++ STL types."""

    _name = None    # type: str

    _typere = None  # type: str
    _typerx = None  # type: Optional[re.Match]

    _ptr = None     # type: Optional[gdb.Value]

    @classmethod
    def is_type(cls, otype):
        # type: (gdb.Type) -> None
        """Return true if the provided type is of this class."""
        if cls._typerx is None:
            cls._typerx = re.compile(cls._typere)
        return cls._typerx.match(otype.tag) is not None

    def __init__(self, ptr):
        # type: (gdb.Value) -> None
        if not self.is_type(ptr.type):
            raise ValueError("Value has type '%s', expected '%s'"
                             % (str(ptr.type), self._name))
        self._ptr = ptr

    def __str__(self):
        # type: () -> str
        return str(self._ptr)


class SmartPtr(AccessorBase):
    """Base accessor for C++ STL smart pointers."""

    def get(self):
        # type: () -> Optional[gdb.Value]
        """Return the pointer owned by this smart pointer, or None."""
        raise NotImplementedError("get() not implemented")


class UniquePtr(SmartPtr):
    """Accessor for std::unique_ptr<>."""

    _name = 'std::unique_ptr'
    _typere = r'^std::(?:__\d+::)?unique_ptr<.*>$'

    _newre = None  # type: Optional[re.Match]
    _oldre = None  # type: Optional[re.Match]

    def get(self):
        # type: () -> Optional[gdb.Value]
        """Return the pointer owned by std::unique_ptr<>, or None."""
        if self._ptr is None:
            return None
        impl_type = self._ptr.type.fields()[0].type.strip_typedefs()
        # Check for new implementations first:
        if UniquePtr._newre is None:
            UniquePtr._newre = re.compile(r'^std::(?:__\d+::)?__uniq_ptr_(data|impl)<.*>$')
        if UniquePtr._newre.match(impl_type):
            tuple_member = self._ptr['_M_t']['_M_t']
        else:
            if UniquePtr._oldre is None:
                UniquePtr._oldre = re.compile(r'^std::(?:__\d+::)?tuple<.*>$')
            if UniquePtr._oldre.match(impl_type):
                tuple_member = self._ptr['_M_t']
            else:
                raise ValueError("Unsupported implementation for unique_ptr: %s" % str(impl_type))
        tuple_impl_type = tuple_member.type.fields()[0].type  # _Tuple_impl
        tuple_head_type = tuple_impl_type.fields()[1].type    # _Head_base
        head_field = tuple_head_type.fields()[0]
        if head_field.name == '_M_head_impl':
            return tuple_member['_M_head_impl']
        if head_field.is_base_class:
            return tuple_member.cast(head_field.type)
        return None


class SharedPtr(SmartPtr):
    """Accessor for std::shared_ptr<>."""

    _name = 'std::{shared,weak}_ptr'
    _typere = r'^std::(?:__\d+::)?(?:shared|weak)_ptr<.*>$'

    def get(self):
        # type: () -> Optional[gdb.Value]
        """Return the pointer managed by std::shared_ptr<>, or None."""
        if self._ptr is None:
            return None
        return self._ptr['_M_ptr']

    def _get_refcounts(self):
        # type: () -> Optional[gdb.Value]
        """Return the refcount struct or None."""
        return self._ptr['_M_refcount']['_M_pi'] if self._ptr else None

    def use_count(self):
        # type: () -> int
        """Return the use count of the std::shared_ptr<>."""
        refcounts = self._get_refcounts()
        return refcounts['_M_use_count'] if refcounts else 0

    def weak_count(self):
        # type: () -> int
        """Return the weak count of the std::shared_ptr<>."""
        refcounts = self._get_refcounts()
        return refcounts['_M_weak_count'] if refcounts else 0

[-- Attachment #3: xmethods.diff --]
[-- Type: text/x-patch, Size: 2411 bytes --]

diff --git a/libstdc++-v3/python/libstdcxx/v6/xmethods.py b/libstdc++-v3/python/libstdcxx/v6/xmethods.py
index 130a658758b..c5e2e6f9363 100644
--- a/libstdc++-v3/python/libstdcxx/v6/xmethods.py
+++ b/libstdc++-v3/python/libstdcxx/v6/xmethods.py
@@ -19,6 +19,8 @@ import gdb
 import gdb.xmethod
 import re
 
+import accessors
+
 matcher_name_prefix = 'libstdc++::'
 
 def get_bool_type():
@@ -585,22 +587,9 @@ class UniquePtrGetWorker(gdb.xmethod.XMethodWorker):
         return method_name == 'get' or not self._is_array
 
     def __call__(self, obj):
-        impl_type = obj.dereference().type.fields()[0].type.tag
-        # Check for new implementations first:
-        if re.match('^std::(__\d+::)?__uniq_ptr_(data|impl)<.*>$', impl_type):
-            tuple_member = obj['_M_t']['_M_t']
-        elif re.match('^std::(__\d+::)?tuple<.*>$', impl_type):
-            tuple_member = obj['_M_t']
-        else:
-            return None
-        tuple_impl_type = tuple_member.type.fields()[0].type # _Tuple_impl
-        tuple_head_type = tuple_impl_type.fields()[1].type   # _Head_base
-        head_field = tuple_head_type.fields()[0]
-        if head_field.name == '_M_head_impl':
-            return tuple_member.cast(tuple_head_type)['_M_head_impl']
-        elif head_field.is_base_class:
-            return tuple_member.cast(head_field.type)
-        else:
+        try:
+            return accessors.UniquePtr(obj.dereference()).get()
+        except ValueError:
             return None
 
 class UniquePtrDerefWorker(UniquePtrGetWorker):
@@ -684,7 +673,10 @@ class SharedPtrGetWorker(gdb.xmethod.XMethodWorker):
         return method_name == 'get' or not self._is_array
 
     def __call__(self, obj):
-        return obj['_M_ptr']
+        try:
+            return accessors.SharedPtr(obj.dereference()).get()
+        except ValueError:
+            return None
 
 class SharedPtrDerefWorker(SharedPtrGetWorker):
     "Implements std::shared_ptr<T>::operator*()"
@@ -739,8 +731,7 @@ class SharedPtrUseCountWorker(gdb.xmethod.XMethodWorker):
         return gdb.lookup_type('long')
 
     def __call__(self, obj):
-        refcounts = obj['_M_refcount']['_M_pi']
-        return refcounts['_M_use_count'] if refcounts else 0
+        return accessors.SharedPtr(obj.dereference()).use_count()
 
 class SharedPtrUniqueWorker(SharedPtrUseCountWorker):
     "Implements std::shared_ptr<T>::unique()"

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Help using the GDB C++ STL pretty-printers / xmethods
  2022-05-09 14:05                       ` Paul Smith
@ 2022-05-09 14:40                         ` Paul Smith
  0 siblings, 0 replies; 21+ messages in thread
From: Paul Smith @ 2022-05-09 14:40 UTC (permalink / raw)
  To: Jonathan Wakely; +Cc: gcc-help

On Mon, 2022-05-09 at 10:05 -0400, Paul Smith wrote:
> I was playing with this yesterday and came up with the attached
> creating an "accessors.py" API.

The reason for the is_type() method in the base class was that I was
going to use it for match() in the XMethodMatcher class, to try to
collect all the type matching into one place (in the accessors).  But I
had to go do other things :).

^ permalink raw reply	[flat|nested] 21+ messages in thread

end of thread, other threads:[~2022-05-09 14:40 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-05-07  1:23 Help using the GDB C++ STL pretty-printers / xmethods Paul Smith
2022-05-07 11:19 ` Hannes Domani
2022-05-07 15:07   ` Paul Smith
2022-05-07 15:35     ` Jonathan Wakely
2022-05-07 19:07       ` Paul Smith
2022-05-07 19:51         ` Jonathan Wakely
2022-05-07 23:08           ` Paul Smith
2022-05-08  8:13             ` Jonathan Wakely
2022-05-08  8:16               ` Jonathan Wakely
2022-05-08 14:09                 ` Paul Smith
2022-05-08 14:36                   ` Jonathan Wakely
2022-05-08 19:44                 ` Paul Smith
2022-05-08 20:26                   ` Paul Smith
2022-05-09 10:47                     ` Hannes Domani
2022-05-09 10:52                       ` Hannes Domani
2022-05-09  9:32                   ` Jonathan Wakely
2022-05-09 11:23                     ` Jonathan Wakely
2022-05-09 14:05                       ` Paul Smith
2022-05-09 14:40                         ` Paul Smith
2022-05-07 15:44     ` Hannes Domani
2022-05-07 15:25 ` 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).