信号处理函数中的异常

当程序因未处理C++异常而终止时,有时候可以使用调试器查找抛出异常的位置。但是如果异常是在信号处理函数中抛出的,这将会比平常困难的多。

本小节组要介绍使用gdb调试器在Linux系统上调试异常。

首先让我们看一个简单的示例,其中的一个普通函数(非信号处理函数)抛出异常:

// without_signal.cc
#include <gtkmm.h>

bool throwSomething()
{
  throw "Something";
  return true;
}

int main(int argc, char** argv)
{
  throwSomething();
  auto app = Gtk::Application::create("org.gtkmm.without_signal");
  return app->run();
}

这是gdb会话的摘要。只显示输出中最有趣的部分:

> 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
你可以看到在without_signal.cc的第六行抛出了异常(throw "Something";)。

现在,让我们看看从信号处理函数中抛出异常会发生什么。这是源代码:

// 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);
  auto app = Gtk::Application::create("org.gtkmm.with_signal");
  app->hold();
  return app->run();
}

这是gdb会话的摘录:

> 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
glibmm将捕获了异常,并调用g_error()结束了程序。其他的异常行为可能不一样,不过所有从信号处理函数中抛出的异常都会被glibmmgtkmm所捕获,并且gdb将无法看到异常被抛出的具体位置。

要查看哪里抛出了异常,你可以使用gdbcatch 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.

如果在感兴趣的未捕获异常之前有多个捕获异常,这个方法将会非常的枯燥。可以使用以下gdb命令自动化进行这个工作。

(gdb) catch throw
(gdb) commands
(gdb)   backtrace
(gdb)   continue
(gdb)   end
(gdb) set pagination off
(gdb) run
这些命令将在每次抛出时打印回溯信息并继续。通常最后一个或第一个throw是你所需要的。