When a program is aborted because of an unhandled C++ exception, it's sometimes possible to use a debugger to find the location where the exception was thrown. This is more difficult than usual if the exception was thrown from a signal handler.
This section describes primarily what you can expect on a Linux system, when you use the gdb debugger.
First, let's look at a simple example where an exception is thrown from a normal function (no signal handler).
// without_signal.cc #include <gtkmm.h> bool throwSomething() { throw "Something"; return true; } int main(int argc, char** argv) { throwSomething(); Glib::RefPtr<Gtk::Application> app = Gtk::Application::create(argc, argv, "org.gtkmm.without_signal"); return app->run(); }
Here is an excerpt from a gdb session. Only the most interesting parts of the output are shown.
> gdb without_signal (gdb) run terminate called after throwing an instance of 'char const*' Program received signal SIGABRT, Aborted. (gdb) backtrace #7 0x08048864 in throwSomething () at without_signal.cc:6 #8 0x0804887d in main (argc=1, argv=0xbfffecd4) at without_signal.cc:12
You can see that the exception was thrown from without_signal.cc
,
line 6 (throw "Something";
).
Now let's see what happens when an exception is thrown from a signal handler. Here's the source code.
// with_signal.cc #include <gtkmm.h> bool throwSomething() { throw "Something"; return true; } int main(int argc, char** argv) { Glib::signal_timeout().connect(sigc::ptr_fun(throwSomething), 500); Glib::RefPtr<Gtk::Application> app = Gtk::Application::create(argc, argv, "org.gtkmm.with_signal"); app->hold(); return app->run(); }
And here's an excerpt from a gdb session.
> gdb with_signal (gdb) run (with_signal:2703): glibmm-ERROR **: unhandled exception (type unknown) in signal handler Program received signal SIGTRAP, Trace/breakpoint trap. (gdb) backtrace #2 0x0063c6ab in glibmm_unexpected_exception () at exceptionhandler.cc:77 #3 Glib::exception_handlers_invoke () at exceptionhandler.cc:150 #4 0x0063d370 in glibmm_source_callback (data=0x804d620) at main.cc:212 #13 0x002e1b31 in Gtk::Application::run (this=0x804f300) at application.cc:178 #14 0x08048ccc in main (argc=1, argv=0xbfffecd4) at with_signal.cc:16
The exception is caught in glibmm, and the program
ends with a call to g_error()
. Other exceptions may result
in different behaviour, but in any case the exception from a signal handler is
caught in glibmm or gtkmm, and
gdb can't see where it was thrown.
To see where the exception is thrown, you can use the gdb
command catch throw
.
> gdb with_signal (gdb) catch throw Catchpoint 1 (throw) (gdb) run Catchpoint 1 (exception thrown), 0x00714ff0 in __cxa_throw () (gdb) backtrace #0 0x00714ff0 in __cxa_throw () from /usr/lib/i386-linux-gnu/libstdc++.so.6 #1 0x08048bd4 in throwSomething () at with_signal.cc:6 (gdb) continue Continuing. (with_signal:2375): glibmm-ERROR ** unhandled exception (type unknown) in signal handler Program received signal SIGTRAP, Trace/breakpoint trap.
If there are many caught exceptions before the interesting uncaught one, this method can be tedious. It can be automated with the following gdb commands.
(gdb) catch throw (gdb) commands (gdb) backtrace (gdb) continue (gdb) end (gdb) set pagination off (gdb) run
These commands will print a backtrace from each throw
and continue.
The backtrace from the last (or possibly the last but one) throw
before the program stops, is the interesting one.