diff mbox

Making it easier to set breakpoints in gdb on pass->execute methods

Message ID 1406666669.2134.139.camel@surprise
State New
Headers show

Commit Message

David Malcolm July 29, 2014, 8:44 p.m. UTC
A complaint I heard at Cauldron with the C++ification of GCC passes is
that it's become much more difficult to set breakpoints on the execute
hooks of a pass, now that the passes are classes within anonymous
namespaces.

When this was first done, the execute methods were trivial
implementations that called into the existing named functions, which are
still easy to put a breakpoint on by name (assuming you know the name of
the function), but some of these have now been converted so that the
"execute" method is the body of the pass.

I did some experimentation, on this box with
gdb-7.6.50.20130731-19.fc20.x86_64 and gcc trunk r212913 (the latter
from a week ago).

You *can* set a breakpoint by name on such an execute method, but it's
tedious to type:
(gdb) break '(anonymous namespace)::pass_expand::execute'
Breakpoint 7 at 0x655220: file ../../src/gcc/cfgexpand.c, line 

...since tab-completion doesn't work well:

(gdb) break '(a<TAB>
does tab complete to:
(gdb) break '(anonymous namespace)::

but typing anything else then hitting tab returns back to:
(gdb) break '(anonymous namespace)::

Is anyone else seeing this?

I had a go at implementing a workaround, for the lack of tab completion
(and the general verbosity) using gdbhooks.py.  

Attached is a patch to gdbhooks.py which adds a new command
"break-on-pass" to gdb; in particular, it locates and parses passes.def,
so that it can tab-complete on pass classnames:

Example of use from the script: putting a breakpoint on "final", i.e.
classname "pass_final":

  (gdb) break-on-pass
Press <TAB>; it autocompletes to "pass_":
  (gdb) break-on-pass pass_
Press <TAB>:
  Display all 219 possibilities? (y or n)
Press "n"; then type "f":
  (gdb) break-on-pass pass_f
Press <TAB> to autocomplete to pass classnames beginning with "pass_f":
  pass_fast_rtl_dce              pass_fold_builtins
  pass_feedback_split_functions  pass_forwprop
  pass_final                     pass_fre
  pass_fixup_cfg                 pass_free_cfg
Type "in<TAB>" to complete to "pass_final":
  (gdb) break-on-pass pass_final
...and hit <RETURN>:
  Breakpoint 6 at 0x8396ba: file ../../src/gcc/final.c, line 4526.
...and we have a breakpoint set; continue execution:
  (gdb) cont
  Continuing.
  Breakpoint 6, (anonymous namespace)::pass_final::execute (this=0x17fb990) at ../../src/gcc/final.c:4526
  4526	  virtual unsigned int execute (function *) { return rest_of_handle_final (); }

This assumes you've suitably enabled gdbhooks.py, as documented at the
top of that file.

Thoughts?
Dave

gcc/
	* gdbhooks.py (find_gcc_source_dir): New helper function.
	(class PassNames): New class, locating and parsing passes.def.
	(class BreakOnPass): New command "break-on-pass".

Comments

David Malcolm July 29, 2014, 9:03 p.m. UTC | #1
On Tue, 2014-07-29 at 16:44 -0400, David Malcolm wrote:
> A complaint I heard at Cauldron with the C++ification of GCC passes is
> that it's become much more difficult to set breakpoints on the execute
> hooks of a pass, now that the passes are classes within anonymous
> namespaces.
> 
> When this was first done, the execute methods were trivial
> implementations that called into the existing named functions, which are
> still easy to put a breakpoint on by name (assuming you know the name of
> the function), but some of these have now been converted so that the
> "execute" method is the body of the pass.
> 
> I did some experimentation, on this box with
> gdb-7.6.50.20130731-19.fc20.x86_64 and gcc trunk r212913 (the latter
> from a week ago).
> 
> You *can* set a breakpoint by name on such an execute method, but it's
> tedious to type:
> (gdb) break '(anonymous namespace)::pass_expand::execute'
> Breakpoint 7 at 0x655220: file ../../src/gcc/cfgexpand.c, line 
> 
> ...since tab-completion doesn't work well:
> 
> (gdb) break '(a<TAB>
> does tab complete to:
> (gdb) break '(anonymous namespace)::
> 
> but typing anything else then hitting tab returns back to:
> (gdb) break '(anonymous namespace)::
> 
> Is anyone else seeing this?

FWIW, this appears to be filed against gdb as:
https://sourceware.org/bugzilla/show_bug.cgi?id=16874

[...snip discussion of workaround...]
Trevor Saunders July 29, 2014, 9:32 p.m. UTC | #2
On Tue, Jul 29, 2014 at 04:44:29PM -0400, David Malcolm wrote:
> A complaint I heard at Cauldron with the C++ification of GCC passes is
> that it's become much more difficult to set breakpoints on the execute
> hooks of a pass, now that the passes are classes within anonymous
> namespaces.

Well, its a big annoyance of anonymous namespaces in general.

> When this was first done, the execute methods were trivial
> implementations that called into the existing named functions, which are
> still easy to put a breakpoint on by name (assuming you know the name of
> the function), but some of these have now been converted so that the
> "execute" method is the body of the pass.

The same holds for the gate method, but those are probably less
important in general.

> I did some experimentation, on this box with
> gdb-7.6.50.20130731-19.fc20.x86_64 and gcc trunk r212913 (the latter
> from a week ago).

 I don't think anonymous namespaces ever worked well with gdb, and afaik
 it hasn't been fixed.

> Thoughts?

ugh, why can somebody just fix gdb is my first reaction, but if its
useful maybe its worth hacks.

Trev

> Dave
> 
> gcc/
> 	* gdbhooks.py (find_gcc_source_dir): New helper function.
> 	(class PassNames): New class, locating and parsing passes.def.
> 	(class BreakOnPass): New command "break-on-pass".
> 

> diff --git a/gcc/gdbhooks.py b/gcc/gdbhooks.py
> index 85608dc..aeb9199 100644
> --- a/gcc/gdbhooks.py
> +++ b/gcc/gdbhooks.py
> @@ -130,6 +130,7 @@ Instead (for now) you must access m_vecdata:
>    (gdb) p bb->preds->m_vecdata[1]
>    $21 = <edge 0x7ffff044d3b8 (4 -> 5)>
>  """
> +import os.path
>  import re
>  
>  import gdb
> @@ -476,4 +477,71 @@ gdb.printing.register_pretty_printer(
>      gdb.current_objfile(),
>      build_pretty_printer())
>  
> +def find_gcc_source_dir():
> +    # Use location of global "g" to locate the source tree
> +    sym_g = gdb.lookup_global_symbol('g')
> +    path = sym_g.symtab.filename # e.g. '../../src/gcc/context.h'
> +    srcdir = os.path.split(path)[0] # e.g. '../../src/gcc'
> +    return srcdir
> +
> +class PassNames:
> +    """Parse passes.def, gathering a list of pass class names"""
> +    def __init__(self):
> +        srcdir = find_gcc_source_dir()
> +        self.names = []
> +        with open(os.path.join(srcdir, 'passes.def')) as f:
> +            for line in f:
> +                m = re.match('\s*NEXT_PASS \((.+)\);', line)
> +                if m:
> +                    self.names.append(m.group(1))
> +PassNames()
> +
> +class BreakOnPass(gdb.Command):
> +    """
> +    A custom command for putting breakpoints on the execute hook of passes.
> +    This is largely a workaround for issues with tab-completion in gdb when
> +    setting breakpoints on methods on classes within anonymous namespaces.
> +
> +    Example of use: putting a breakpoint on "final"
> +      (gdb) break-on-pass
> +    Press <TAB>; it autocompletes to "pass_":
> +      (gdb) break-on-pass pass_
> +    Press <TAB>:
> +      Display all 219 possibilities? (y or n)
> +    Press "n"; then type "f":
> +      (gdb) break-on-pass pass_f
> +    Press <TAB> to autocomplete to pass classnames beginning with "pass_f":
> +      pass_fast_rtl_dce              pass_fold_builtins
> +      pass_feedback_split_functions  pass_forwprop
> +      pass_final                     pass_fre
> +      pass_fixup_cfg                 pass_free_cfg
> +    Type "in<TAB>" to complete to "pass_final":
> +      (gdb) break-on-pass pass_final
> +    ...and hit <RETURN>:
> +      Breakpoint 6 at 0x8396ba: file ../../src/gcc/final.c, line 4526.
> +    ...and we have a breakpoint set; continue execution:
> +      (gdb) cont
> +      Continuing.
> +      Breakpoint 6, (anonymous namespace)::pass_final::execute (this=0x17fb990) at ../../src/gcc/final.c:4526
> +      4526	  virtual unsigned int execute (function *) { return rest_of_handle_final (); }
> +    """
> +    def __init__(self):
> +        gdb.Command.__init__(self, 'break-on-pass', gdb.COMMAND_BREAKPOINTS)
> +        self.pass_names = None
> +
> +    def complete(self, text, word):
> +        # Lazily load pass names:
> +        if not self.pass_names:
> +            self.pass_names = PassNames()
> +
> +        return [name
> +                for name in sorted(self.pass_names.names)
> +                if name.startswith(text)]
> +
> +    def invoke(self, arg, from_tty):
> +        sym = '(anonymous namespace)::%s::execute' % arg
> +        breakpoint = gdb.Breakpoint(sym)
> +
> +BreakOnPass()
> +
>  print('Successfully loaded GDB hooks for GCC')
Richard Biener July 30, 2014, 8:07 a.m. UTC | #3
On Tue, Jul 29, 2014 at 10:44 PM, David Malcolm <dmalcolm@redhat.com> wrote:
> A complaint I heard at Cauldron with the C++ification of GCC passes is
> that it's become much more difficult to set breakpoints on the execute
> hooks of a pass, now that the passes are classes within anonymous
> namespaces.
>
> When this was first done, the execute methods were trivial
> implementations that called into the existing named functions, which are
> still easy to put a breakpoint on by name (assuming you know the name of
> the function), but some of these have now been converted so that the
> "execute" method is the body of the pass.
>
> I did some experimentation, on this box with
> gdb-7.6.50.20130731-19.fc20.x86_64 and gcc trunk r212913 (the latter
> from a week ago).
>
> You *can* set a breakpoint by name on such an execute method, but it's
> tedious to type:
> (gdb) break '(anonymous namespace)::pass_expand::execute'
> Breakpoint 7 at 0x655220: file ../../src/gcc/cfgexpand.c, line
>
> ...since tab-completion doesn't work well:
>
> (gdb) break '(a<TAB>
> does tab complete to:
> (gdb) break '(anonymous namespace)::
>
> but typing anything else then hitting tab returns back to:
> (gdb) break '(anonymous namespace)::
>
> Is anyone else seeing this?

Yes, I filed a gdb bug about this.

> I had a go at implementing a workaround, for the lack of tab completion
> (and the general verbosity) using gdbhooks.py.
>
> Attached is a patch to gdbhooks.py which adds a new command
> "break-on-pass" to gdb; in particular, it locates and parses passes.def,
> so that it can tab-complete on pass classnames:
>
> Example of use from the script: putting a breakpoint on "final", i.e.
> classname "pass_final":
>
>   (gdb) break-on-pass
> Press <TAB>; it autocompletes to "pass_":
>   (gdb) break-on-pass pass_
> Press <TAB>:
>   Display all 219 possibilities? (y or n)
> Press "n"; then type "f":
>   (gdb) break-on-pass pass_f
> Press <TAB> to autocomplete to pass classnames beginning with "pass_f":
>   pass_fast_rtl_dce              pass_fold_builtins
>   pass_feedback_split_functions  pass_forwprop
>   pass_final                     pass_fre
>   pass_fixup_cfg                 pass_free_cfg
> Type "in<TAB>" to complete to "pass_final":
>   (gdb) break-on-pass pass_final
> ...and hit <RETURN>:
>   Breakpoint 6 at 0x8396ba: file ../../src/gcc/final.c, line 4526.
> ...and we have a breakpoint set; continue execution:
>   (gdb) cont
>   Continuing.
>   Breakpoint 6, (anonymous namespace)::pass_final::execute (this=0x17fb990) at ../../src/gcc/final.c:4526
>   4526    virtual unsigned int execute (function *) { return rest_of_handle_final (); }
>
> This assumes you've suitably enabled gdbhooks.py, as documented at the
> top of that file.
>
> Thoughts?

Works for me though I'm not able to review python ;)

Richard.

> Dave
>
> gcc/
>         * gdbhooks.py (find_gcc_source_dir): New helper function.
>         (class PassNames): New class, locating and parsing passes.def.
>         (class BreakOnPass): New command "break-on-pass".
>
David Malcolm Aug. 5, 2014, 9:02 p.m. UTC | #4
On Wed, 2014-07-30 at 10:07 +0200, Richard Biener wrote:
> On Tue, Jul 29, 2014 at 10:44 PM, David Malcolm <dmalcolm@redhat.com> wrote:
> > A complaint I heard at Cauldron with the C++ification of GCC passes is
> > that it's become much more difficult to set breakpoints on the execute
> > hooks of a pass, now that the passes are classes within anonymous
> > namespaces.
> >
> > When this was first done, the execute methods were trivial
> > implementations that called into the existing named functions, which are
> > still easy to put a breakpoint on by name (assuming you know the name of
> > the function), but some of these have now been converted so that the
> > "execute" method is the body of the pass.
> >
> > I did some experimentation, on this box with
> > gdb-7.6.50.20130731-19.fc20.x86_64 and gcc trunk r212913 (the latter
> > from a week ago).
> >
> > You *can* set a breakpoint by name on such an execute method, but it's
> > tedious to type:
> > (gdb) break '(anonymous namespace)::pass_expand::execute'
> > Breakpoint 7 at 0x655220: file ../../src/gcc/cfgexpand.c, line
> >
> > ...since tab-completion doesn't work well:
> >
> > (gdb) break '(a<TAB>
> > does tab complete to:
> > (gdb) break '(anonymous namespace)::
> >
> > but typing anything else then hitting tab returns back to:
> > (gdb) break '(anonymous namespace)::
> >
> > Is anyone else seeing this?
> 
> Yes, I filed a gdb bug about this.
> 
> > I had a go at implementing a workaround, for the lack of tab completion
> > (and the general verbosity) using gdbhooks.py.
> >
> > Attached is a patch to gdbhooks.py which adds a new command
> > "break-on-pass" to gdb; in particular, it locates and parses passes.def,
> > so that it can tab-complete on pass classnames:
> >
> > Example of use from the script: putting a breakpoint on "final", i.e.
> > classname "pass_final":
> >
> >   (gdb) break-on-pass
> > Press <TAB>; it autocompletes to "pass_":
> >   (gdb) break-on-pass pass_
> > Press <TAB>:
> >   Display all 219 possibilities? (y or n)
> > Press "n"; then type "f":
> >   (gdb) break-on-pass pass_f
> > Press <TAB> to autocomplete to pass classnames beginning with "pass_f":
> >   pass_fast_rtl_dce              pass_fold_builtins
> >   pass_feedback_split_functions  pass_forwprop
> >   pass_final                     pass_fre
> >   pass_fixup_cfg                 pass_free_cfg
> > Type "in<TAB>" to complete to "pass_final":
> >   (gdb) break-on-pass pass_final
> > ...and hit <RETURN>:
> >   Breakpoint 6 at 0x8396ba: file ../../src/gcc/final.c, line 4526.
> > ...and we have a breakpoint set; continue execution:
> >   (gdb) cont
> >   Continuing.
> >   Breakpoint 6, (anonymous namespace)::pass_final::execute (this=0x17fb990) at ../../src/gcc/final.c:4526
> >   4526    virtual unsigned int execute (function *) { return rest_of_handle_final (); }
> >
> > This assumes you've suitably enabled gdbhooks.py, as documented at the
> > top of that file.
> >
> > Thoughts?
> 
> Works for me though I'm not able to review python ;)

I've gone ahead and committed it to trunk as r213646; hope this reduces
the pain somewhat.

> Richard.
> 
> > Dave
> >
> > gcc/
> >         * gdbhooks.py (find_gcc_source_dir): New helper function.
> >         (class PassNames): New class, locating and parsing passes.def.
> >         (class BreakOnPass): New command "break-on-pass".
> >
Trevor Saunders Aug. 5, 2014, 9:08 p.m. UTC | #5
On Tue, Aug 05, 2014 at 05:02:57PM -0400, David Malcolm wrote:
> On Wed, 2014-07-30 at 10:07 +0200, Richard Biener wrote:
> > On Tue, Jul 29, 2014 at 10:44 PM, David Malcolm <dmalcolm@redhat.com> wrote:
> > > A complaint I heard at Cauldron with the C++ification of GCC passes is
> > > that it's become much more difficult to set breakpoints on the execute
> > > hooks of a pass, now that the passes are classes within anonymous
> > > namespaces.
> > >
> > > When this was first done, the execute methods were trivial
> > > implementations that called into the existing named functions, which are
> > > still easy to put a breakpoint on by name (assuming you know the name of
> > > the function), but some of these have now been converted so that the
> > > "execute" method is the body of the pass.
> > >
> > > I did some experimentation, on this box with
> > > gdb-7.6.50.20130731-19.fc20.x86_64 and gcc trunk r212913 (the latter
> > > from a week ago).
> > >
> > > You *can* set a breakpoint by name on such an execute method, but it's
> > > tedious to type:
> > > (gdb) break '(anonymous namespace)::pass_expand::execute'
> > > Breakpoint 7 at 0x655220: file ../../src/gcc/cfgexpand.c, line
> > >
> > > ...since tab-completion doesn't work well:
> > >
> > > (gdb) break '(a<TAB>
> > > does tab complete to:
> > > (gdb) break '(anonymous namespace)::
> > >
> > > but typing anything else then hitting tab returns back to:
> > > (gdb) break '(anonymous namespace)::
> > >
> > > Is anyone else seeing this?
> > 
> > Yes, I filed a gdb bug about this.
> > 
> > > I had a go at implementing a workaround, for the lack of tab completion
> > > (and the general verbosity) using gdbhooks.py.
> > >
> > > Attached is a patch to gdbhooks.py which adds a new command
> > > "break-on-pass" to gdb; in particular, it locates and parses passes.def,
> > > so that it can tab-complete on pass classnames:
> > >
> > > Example of use from the script: putting a breakpoint on "final", i.e.
> > > classname "pass_final":
> > >
> > >   (gdb) break-on-pass
> > > Press <TAB>; it autocompletes to "pass_":
> > >   (gdb) break-on-pass pass_
> > > Press <TAB>:
> > >   Display all 219 possibilities? (y or n)
> > > Press "n"; then type "f":
> > >   (gdb) break-on-pass pass_f
> > > Press <TAB> to autocomplete to pass classnames beginning with "pass_f":
> > >   pass_fast_rtl_dce              pass_fold_builtins
> > >   pass_feedback_split_functions  pass_forwprop
> > >   pass_final                     pass_fre
> > >   pass_fixup_cfg                 pass_free_cfg
> > > Type "in<TAB>" to complete to "pass_final":
> > >   (gdb) break-on-pass pass_final
> > > ...and hit <RETURN>:
> > >   Breakpoint 6 at 0x8396ba: file ../../src/gcc/final.c, line 4526.
> > > ...and we have a breakpoint set; continue execution:
> > >   (gdb) cont
> > >   Continuing.
> > >   Breakpoint 6, (anonymous namespace)::pass_final::execute (this=0x17fb990) at ../../src/gcc/final.c:4526
> > >   4526    virtual unsigned int execute (function *) { return rest_of_handle_final (); }
> > >
> > > This assumes you've suitably enabled gdbhooks.py, as documented at the
> > > top of that file.
> > >
> > > Thoughts?
> > 
> > Works for me though I'm not able to review python ;)
> 
> I've gone ahead and committed it to trunk as r213646; hope this reduces
> the pain somewhat.

So, I was thinking about this more at some point.  iirc the reason to
put the classes in the anon namespace was to get stuff devirtualized.
However I think we can just use __final if building with gcc to
acomplish the same thing, and then remove the namespace {}s.  It needs
to be tested, but if it works it would be even better :-)

Trev

> 
> > Richard.
> > 
> > > Dave
> > >
> > > gcc/
> > >         * gdbhooks.py (find_gcc_source_dir): New helper function.
> > >         (class PassNames): New class, locating and parsing passes.def.
> > >         (class BreakOnPass): New command "break-on-pass".
> > >
> 
>
diff mbox

Patch

diff --git a/gcc/gdbhooks.py b/gcc/gdbhooks.py
index 85608dc..aeb9199 100644
--- a/gcc/gdbhooks.py
+++ b/gcc/gdbhooks.py
@@ -130,6 +130,7 @@  Instead (for now) you must access m_vecdata:
   (gdb) p bb->preds->m_vecdata[1]
   $21 = <edge 0x7ffff044d3b8 (4 -> 5)>
 """
+import os.path
 import re
 
 import gdb
@@ -476,4 +477,71 @@  gdb.printing.register_pretty_printer(
     gdb.current_objfile(),
     build_pretty_printer())
 
+def find_gcc_source_dir():
+    # Use location of global "g" to locate the source tree
+    sym_g = gdb.lookup_global_symbol('g')
+    path = sym_g.symtab.filename # e.g. '../../src/gcc/context.h'
+    srcdir = os.path.split(path)[0] # e.g. '../../src/gcc'
+    return srcdir
+
+class PassNames:
+    """Parse passes.def, gathering a list of pass class names"""
+    def __init__(self):
+        srcdir = find_gcc_source_dir()
+        self.names = []
+        with open(os.path.join(srcdir, 'passes.def')) as f:
+            for line in f:
+                m = re.match('\s*NEXT_PASS \((.+)\);', line)
+                if m:
+                    self.names.append(m.group(1))
+PassNames()
+
+class BreakOnPass(gdb.Command):
+    """
+    A custom command for putting breakpoints on the execute hook of passes.
+    This is largely a workaround for issues with tab-completion in gdb when
+    setting breakpoints on methods on classes within anonymous namespaces.
+
+    Example of use: putting a breakpoint on "final"
+      (gdb) break-on-pass
+    Press <TAB>; it autocompletes to "pass_":
+      (gdb) break-on-pass pass_
+    Press <TAB>:
+      Display all 219 possibilities? (y or n)
+    Press "n"; then type "f":
+      (gdb) break-on-pass pass_f
+    Press <TAB> to autocomplete to pass classnames beginning with "pass_f":
+      pass_fast_rtl_dce              pass_fold_builtins
+      pass_feedback_split_functions  pass_forwprop
+      pass_final                     pass_fre
+      pass_fixup_cfg                 pass_free_cfg
+    Type "in<TAB>" to complete to "pass_final":
+      (gdb) break-on-pass pass_final
+    ...and hit <RETURN>:
+      Breakpoint 6 at 0x8396ba: file ../../src/gcc/final.c, line 4526.
+    ...and we have a breakpoint set; continue execution:
+      (gdb) cont
+      Continuing.
+      Breakpoint 6, (anonymous namespace)::pass_final::execute (this=0x17fb990) at ../../src/gcc/final.c:4526
+      4526	  virtual unsigned int execute (function *) { return rest_of_handle_final (); }
+    """
+    def __init__(self):
+        gdb.Command.__init__(self, 'break-on-pass', gdb.COMMAND_BREAKPOINTS)
+        self.pass_names = None
+
+    def complete(self, text, word):
+        # Lazily load pass names:
+        if not self.pass_names:
+            self.pass_names = PassNames()
+
+        return [name
+                for name in sorted(self.pass_names.names)
+                if name.startswith(text)]
+
+    def invoke(self, arg, from_tty):
+        sym = '(anonymous namespace)::%s::execute' % arg
+        breakpoint = gdb.Breakpoint(sym)
+
+BreakOnPass()
+
 print('Successfully loaded GDB hooks for GCC')