Exceptions in signal handlers

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.