klayout 有个很酷的功能:程序崩溃时会有对话框弹出显示错误所在行号和程序的函数调用堆栈,这对解决bug非常有帮助。
下面就分析下这个功能的实现机制。
首先,klayout在程序启动时,加了崩溃处理的Handler.
//laySignalHandler.cc
void install_signal_handlers ()
{
struct sigaction act;
act.sa_sigaction = signal_handler;
sigemptyset (&act.sa_mask);
act.sa_flags = SA_SIGINFO;
#if !defined(__APPLE__)
act.sa_restorer = 0;
#endif
sigaction (SIGSEGV, &act, NULL);
sigaction (SIGILL, &act, NULL);
sigaction (SIGFPE, &act, NULL);
sigaction (SIGABRT, &act, NULL);
sigaction (SIGBUS, &act, NULL);
}
之后,在程序崩溃时,会运行设置的signal_handler函数。
void signal_handler (int signo, siginfo_t *si, void *)
{
// InformationBox::Instance()->hide();
void *array [100];
bool can_resume = (signo != SIGILL);
size_t nptrs = backtrace (array, sizeof (array) / sizeof (array[0]));
std::string text;
text += tl::sprintf ("Signal number: %d\n", signo);
text += tl::sprintf ("Address: 0x%lx\n", (unsigned long) si->si_addr);
text += std::string ("Program Version: ") +
lay::Version::name () + " " +
lay::Version::version () + " (" + lay::Version::subversion () + ")\n";
std::auto_ptr<CrashMessage> msg;
bool has_gui = s_sh_has_gui && lay::ApplicationBase::instance () && lay::ApplicationBase::instance ()->has_gui ();
if (has_gui) {
msg.reset (new CrashMessage (0, false, tl::to_qstring (text) + QObject::tr ("\nCollecting backtrace ..")));
msg->show ();
lay::ApplicationBase::instance ()->qapp_gui ()->setOverrideCursor (Qt::WaitCursor);
}
text += std::string ("\nBacktrace:\n");
#if 0
// the approach with backtrace_symbols - this does not resolve shared object symbols
char **symbols = backtrace_symbols (array, nptrs);
if (symbols == NULL) {
text += "-- Unable to obtain stack trace --\n";
} else {
for (size_t i = 2; i < nptrs; i++) {
text += std::string (symbols [i]) + "\n";
}
}
free(symbols);
#else
// the more elaborate approach using the addr2line external tool to obtain debug information
// (if available)
const char *addr2line_call = "addr2line -C -s -f -e '%s' 0x%lx";
通过调用命令行程序 addr2line 显示错误堆栈。
关于addr2line的详细介绍,可参考 addr2line探秘