>>>>> "Michael" == Michael Meeks <michael.me...@suse.com> writes:
Michael> The basic debugging experience in these "an exception broke Michael> something" flows is that we get an exception thrown that Michael> ultimately ends up in a pathalogical situation - an abort, or Michael> some similar horrible badness. At that point the most Michael> interesting thing is not the catcher - which usually ends up Michael> being utterly random - but the last guy that threw the Michael> exception. So then as Lubos says comes the knotty job of trying Michael> to put a breakpoint on the -one- exception that ends up being Michael> caught where we are now [ and that of course requires Michael> re-running, and inevitably we throw dozens of exceptions in the Michael> normal case ]. Thanks. This kind of discussion is very helpful to me. This problem is a bit tricky. The various low-level exception-related functions, like __cxa_throw, treat the exception object as a "void *". However, the value of this seems to change depending on the "throw" point. It's clear that this can't always be the argument to throw, due to scalar and object throws. So I wonder what exactly it refers to. I'll have to dig a bit deeper to see how all this code really works. Anyway, this makes tracking backward from std::terminate to the original throw point more difficult. It helps a bit to install the libstdc++ debuginfo. Then at least you can dig into some of the details. However, due to optimization, even with the improvements in newer version of gcc, this turns out to be less than perfect. I tried this out to see what it was like. It is kind of awful! At least, I had to dig around through several frames of libstdc++ to find the object that lead to std::terminate being called: terminate called after throwing an instance of 'char const*' Program received signal SIGABRT, Aborted. 0x0000003be3036285 in __GI_raise (sig=6) at ../nptl/sysdeps/unix/sysv/linux/raise.c:64 64 return INLINE_SYSCALL (tgkill, 3, pid, selftid, sig); (gdb) up #1 0x0000003be3037b9b in __GI_abort () at abort.c:91 91 raise (SIGABRT); (gdb) up #2 0x0000003be80bbc5d in __gnu_cxx::__verbose_terminate_handler () at ../../../../libstdc++-v3/libsupc++/vterminate.cc:95 95 abort(); (gdb) up #3 0x0000003be80b9e16 in __cxxabiv1::__terminate (handler=<optimized out>) at ../../../../libstdc++-v3/libsupc++/eh_terminate.cc:40 40 handler (); (gdb) up #4 0x0000003be80b9e43 in std::terminate () at ../../../../libstdc++-v3/libsupc++/eh_terminate.cc:50 50 __terminate (__terminate_handler); (gdb) up #5 0x0000003be80b9f3e in __cxxabiv1::__cxa_throw (obj=0x601090, tinfo=<optimized out>, dest=<optimized out>) at ../../../../libstdc++-v3/libsupc++/eh_throw.cc:83 83 std::terminate (); At this point I can do: (gdb) catch throw Catchpoint 1 (throw) (gdb) cond 1 obj == 0x601090 (gdb) r The program being debugged has been started already. Start it from the beginning? (y or n) y [Inferior 25299 exited] Starting program: /tmp/r warning: failed to reevaluate condition for breakpoint 1: No symbol "obj" in current context. warning: failed to reevaluate condition for breakpoint 1: No symbol "obj" in current context. warning: failed to reevaluate condition for breakpoint 1: No symbol "obj" in current context. Catchpoint 1 (exception thrown), __cxxabiv1::__cxa_throw (obj=0x601090, tinfo= 0x600a60, dest=0) at ../../../../libstdc++-v3/libsupc++/eh_throw.cc:70 70 header->exc.unexpectedHandler = __unexpected_handler; Ignore the warnings; I'm not sure what they are about, but I will file a bug. ... but all this still fails if you insert a "manual re-throw" like "throw x;" into the call chain. At that point it gets really messy :( Michael> Really nice ! though of course - having a full stack trace Michael> would make that very substantially more useful. This is reasonably easy to implement. It may be expensive. I've appended a version that does this... well, it lists file name and line number for all the frames. If you want to get a really full stack trace, capturing the arguments and locals, then you would have to do more work. Michael> Even better than this would (perhaps) be a "break inside thrower that Michael> is caught here" type breakpoint - that we could invoke to land us in Michael> whatever code is going to throw as it does that [ and before it started Michael> all the magic cleanup / unwinding work ]. That is - assuming that it's Michael> possible for the code to know (at that point) where it will ultimately Michael> end up (? ;-) I think it isn't possible in general. When an exception is thrown, I think all that can really be determined is the next catch point. What this means is that if you have a series of throws and re-throws, winding up at some "catch", then the best you could do is stop at the re-throw that leads to that catch. Does that make sense? Like: void doit() { throw "hi"; } void dd2() { try { doit(); } catch (const char *x) { throw x; } } int main() { try { dd2(); } catch (const char *x) { // The problem spot. } } Here, suppose the comment marks the catch you are concerned with. You want to find the throw that leads to this point. The original throw in "doit" can only see as far as the catchpoint in "dd2". That is because arbitrary code can be run there -- for example it may swallow the exception and no more throwing is even done. So this hypothetical breakpoint would only trigger at the re-throw in dd2. But, that isn't what you want. And from there it is even hard to track backwards, you have to "catch catch" and filter for the particular one you want. I'm curious what types of exceptions are actually thrown in LibreOffice. Does it throw -- scalars? Objects? Just pointers ("Java style")? A few improvements come to mind. I'd like to hear your take on these, or any other ideas you've got. It seems like it would be nice if gdb exposed some kind of convenience variable so that "catch catch" and "catch throw" could be conditional on the thrown object without needing the libstdc++ debuginfo. This may require some libstdc++ change, perhaps a probe point. It would be nice to solve the problem above, of following an exception back to its ultimate origination point. It seems like this would be useful even if it were not 100% reliable, in the sense that it is ok to have a few extra breakpoints -- filtering 90% of uninteresting exceptions is better than filtering 0% of them. If we had the convenience variable mentioned above, and if LibreOffice has a relatively simple "exception identity" measure (e.g., if you only throw pointers, you can just compare them with ==), then it could perhaps be done by: break at the losing catch, make a conditional "catch throw", then re-run. Tom import gdb last_sals = None throw_bp = None class ThrowTracker(gdb.Breakpoint): def __init__(self): gdb.Breakpoint.__init__(self, '__cxa_throw') def stop(self): global last_sals frame = gdb.newest_frame().older() last_sals = [] while frame is not None: last_sals.append(frame.find_sal()) frame = frame.older() return False class TrackThrows(gdb.Command): def __init__(self): gdb.Command.__init__(self, 'track-throws', gdb.COMMAND_BREAKPOINTS) def invoke(self, arg, from_tty): global throw_bp if throw_bp is None or not throw_bp.is_valid(): # Still no good way to create a pending breakpoint from # Python. save = gdb.parameter('breakpoint pending') gdb.execute('set breakpoint pending on', to_string = True) throw_bp = ThrowTracker() if save is None: arg = 'auto' elif save: arg = 'on' else: arg = 'off' gdb.execute('set breakpoint pending %s' % arg, to_string = True) class InfoThrow(gdb.Command): def __init__(self): gdb.Command.__init__(self, 'info last-throw', gdb.COMMAND_BREAKPOINTS) def invoke(self, arg, from_tty): global last_sals if last_sals is not None: first = True for sal in last_sals: filename = sal.symtab.filename line = sal.line if first: print "Last exception thrown at file %s, line %d" % (filename, line) first = False else: print "From %s, %d" % (filename, line) else: print "No previous exception seen" TrackThrows() InfoThrow() _______________________________________________ LibreOffice mailing list LibreOffice@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/libreoffice