Gdb support for exceptions (Re: using backtrace() in exception throwing?)

Tom Tromey tromey at redhat.com
Thu Mar 14 07:39:42 PDT 2013


Tom> I think it isn't possible in general.  When an exception is thrown, I
Tom> think all that can really be determined is the next catch point.

Michael> This would be perfect. Explicit rethrows are relatively rare in
Michael> our code, however it is completely normal to have code that we
Michael> simply don't understand and/or follow - whereby we do:

Unfortunately, it is even worse than I made it out to be.  The "next
catch point" also includes the spots where unwinding pauses to invoke
destructors.  So, at the lowest level you can't easily associate a
"throw" with a "catch" that you would see in the source.  You can only
see to spot that calls the next destructor.

If you're interested you can see this in action by breaking at the
"libgcc:unwind" probe point (or equivalently _Unwind_DebugHook but then
you have to do argument decoding by hand), then "disassem $_probe_arg1".
That will show you the assembly for where you're about to unwind to.

("catch catch" and "catch throw" hook into somewhat higher-level
exception functions in libstdc++, which is why they don't stop at
destructors; but this code doesn't know about the details and just
defers to the lower-level unwinder.)

I thought a bit about whether we could fix the lower levels to expose
the information we'd like, but I couldn't think of a good way.

Tom> catch catch [REGEXP]
Tom> catch throw [REGEXP]
Tom> catch rethrow [REGEXP]

Michael> This is really nice; the ability to hide many of the "two
Michael> dozen+" expected exceptions in some way be really
Michael> useful. Unfortunately these are often of quite generic types
Michael> :-)

Yeah, that makes it harder.

If they carry any identifying markers, you can use two of the features
in tandem to filter.  For example:

    catch throw TheExceptionType if $_exception.field == 23

This would filter by type and then do some additional checking of the
identity of the exception.

If they don't carry identifying markers -- if it were me, I guess I
would add something to make debugging simpler.

Michael> Tor suggested on IRC some way of ignoring specific exception throwing
Michael> sites which tend to creep into the code over time and need tackling.

Michael> Being able to say: catch throw ignore - which would ignore the last
Michael> thrown exception site would be really lovely ;-)

This sort of thing is reasonably easy to do in Python.

Fedora has had a $_caller_is function in its gdb for a while (not sure
why we haven't upstreamed this yet, probably just nobody got around to
it yet).  This function was actually my motivation for getting into gdb
hacking and working on Python in gdb -- I wanted to be able to filter
breakpoints according to caller, without a lot of hassle.

I've appended it for convenience.  I'll file a bug to remind us to
upstream this.

You would use it like:

  catch throw if !$_caller_is('some_thrower_to_ignore')

You could wrap this up in a Python command to add "&& !$_caller_is..."
to existing breakpoint conditions, or a LibreOffice-specific "catch
throw"-like command which pre-ignores uninteresting call sites, or etc.

Michael> Is any of that useful ? - really looking forward to getting a
Michael> new libstdc++ etc. with your fixes in a few months :-)

An optimist, I see :)
Actually, I assume all the patches will go in reasonably soon, but
there's a pretty long lag until the new stuff is deployed all over,
unless you're willing to build your gcc.

Please feel free to CC me on gdb questions and suggestions.
I'm very interested in your feedback.

Tom


# Caller-is functions.

# Copyright (C) 2008 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

class CallerIs (gdb.Function):
    """Return True if the calling function's name is equal to a string.
This function takes one or two arguments.
The first argument is the name of a function; if the calling function's
name is equal to this argument, this function returns True.
The optional second argument tells this function how many stack frames
to traverse to find the calling function.  The default is 1."""

    def __init__ (self):
        super (CallerIs, self).__init__ ("caller_is")

    def invoke (self, name, nframes = 1):
        frame = gdb.selected_frame ()
        while nframes > 0:
            frame = frame.older ()
            nframes = nframes - 1
        return frame.name () == name.string ()

class CallerMatches (gdb.Function):
    """Return True if the calling function's name matches a string.
This function takes one or two arguments.
The first argument is a regular expression; if the calling function's
name is matched by this argument, this function returns True.
The optional second argument tells this function how many stack frames
to traverse to find the calling function.  The default is 1."""

    def __init__ (self):
        super (CallerMatches, self).__init__ ("caller_matches")

    def invoke (self, name, nframes = 1):
        frame = gdb.selected_frame ()
        while nframes > 0:
            frame = frame.older ()
            nframes = nframes - 1
        return re.match (name.string (), frame.name ()) is not None

CallerIs()
CallerMatches()


More information about the LibreOffice mailing list