我正在尝试使用信号处理程序捕获错误,然后打印堆栈跟踪信息以添加到日志文件(或控制台)以获取崩溃报告并在非开发计算机上调试我的应用程序。我的问题是偶尔我没有获得完整的堆栈帧回溯。在许多情况下,它似乎挂起并且没有完成或退出。有时它才会成功退出。
这是我的代码:
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <execinfo.h>
typedef struct { char name[10]; int id; char description[40]; } signal_def;
signal_def signal_data[] =
{
{ "SIGHUP", SIGHUP, "Hangup (POSIX)" },
{ "SIGINT", SIGINT, "Interrupt (ANSI)" },
{ "SIGQUIT", SIGQUIT, "Quit (POSIX)" },
{ "SIGILL", SIGILL, "Illegal instruction (ANSI)" },
{ "SIGTRAP", SIGTRAP, "Trace trap (POSIX)" },
{ "SIGABRT", SIGABRT, "Abort (ANSI)" },
{ "SIGIOT", SIGIOT, "IOT trap (4.2 BSD)" },
{ "SIGBUS", SIGBUS, "BUS error (4.2 BSD)" },
{ "SIGFPE", SIGFPE, "Floating-point exception (ANSI)" },
{ "SIGKILL", SIGKILL, "Kill, unblockable (POSIX)" },
{ "SIGUSR1", SIGUSR1, "User-defined signal 1 (POSIX)" },
{ "SIGSEGV", SIGSEGV, "Segmentation violation (ANSI)" },
{ "SIGUSR2", SIGUSR2, "User-defined signal 2 (POSIX)" },
{ "SIGPIPE", SIGPIPE, "Broken pipe (POSIX)" },
{ "SIGALRM", SIGALRM, "Alarm clock (POSIX)" },
{ "SIGTERM", SIGTERM, "Termination (ANSI)" },
//{ "SIGSTKFLT", SIGSTKFLT, "Stack fault" },
{ "SIGCHLD", SIGCHLD, "Child status has changed (POSIX)" },
//{ "SIGCLD", SIGCLD, "Same as SIGCHLD (System V)" },
{ "SIGCONT", SIGCONT, "Continue (POSIX)" },
{ "SIGSTOP", SIGSTOP, "Stop, unblockable (POSIX)" },
{ "SIGTSTP", SIGTSTP, "Keyboard stop (POSIX)" },
{ "SIGTTIN", SIGTTIN, "Background read from tty (POSIX)" },
{ "SIGTTOU", SIGTTOU, "Background write to tty (POSIX)" },
{ "SIGURG", SIGURG, "Urgent condition on socket (4.2 BSD)" },
{ "SIGXCPU", SIGXCPU, "CPU limit exceeded (4.2 BSD)" },
{ "SIGXFSZ", SIGXFSZ, "File size limit exceeded (4.2 BSD)" },
{ "SIGVTALRM", SIGVTALRM, "Virtual alarm clock (4.2 BSD)" },
{ "SIGPROF", SIGPROF, "Profiling alarm clock (4.2 BSD)" },
{ "SIGWINCH", SIGWINCH, "Window size change (4.3 BSD, Sun)" },
{ "SIGIO", SIGIO, "I/O now possible (4.2 BSD)" },
//{ "SIGPOLL", SIGPOLL, "Pollable event occurred (System V)" },
//{ "SIGPWR", SIGPWR, "Power failure restart (System V)" },
{ "SIGSYS", SIGSYS, "Bad system call" },
};
void bt_sighandler(int sig, siginfo_t *info, void *secret) {
signal_def *sigd = NULL;
for (int i = 0; i < sizeof(signal_data) / sizeof(signal_def); ++i) {
if (sig == signal_data[i].id) {
sigd = &signal_data[i];
break;
}
}
//ucontext_t* uc = (ucontext_t*) secret;
//void *pnt = (void*) uc->uc_mcontext.gregs[REG_RIP] ;
void *trace[16];
int trace_size = backtrace(trace, 16);
/* overwrite sigaction with caller's address */
//trace[1] = pnt;
if (sigd) {
fprintf(stderr, "SigHandler(0x%02X)[%d]:%s[%s]", sig, trace_size,
sigd->name, sigd->description);
} else {
fprintf(stderr, "SigHandler(0x%02X)[%d]", sig, trace_size);
}
backtrace_symbols_fd(trace, trace_size, fileno(stderr));
exit(1);
}
#endif
int main(int argc, char* argv[]) {
struct sigaction sa;
sa.sa_sigaction = bt_sighandler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sigaction(SIGINT, &sa, NULL);
sigaction(SIGSEGV, &sa, NULL);
sigaction(SIGBUS, &sa, NULL);
sigaction(SIGILL, &sa, NULL);
sigaction(SIGFPE, &sa, NULL);
sigaction(SIGUSR1, &sa, NULL);
sigaction(SIGUSR2, &sa, NULL);
signal(SIGPIPE, SIG_IGN);
//Produce a fault
return 0;
}
您将在我的示例代码中注意到,负责用调用者地址覆盖sigaction的部分已被注释掉。这是因为我不确定如何为Mac编译它。
以下是控制台输出示例: console output http://www.minesclubtennis.com/images/stackoverflow/fatalconsoleoutputhang.png
你会注意到它只打印了前3帧,然后在没有退出的情况下挂起,即使找到9帧也应该打印出来。
所以我从Activity Monitor应用程序中做了一个“Sample Process”,发现执行backtrace_symbols_fd函数的线程卡在了strlen上。截图: sample process output http://www.minesclubtennis.com/images/stackoverflow/sampleprocessoutputhang.png
为什么要挂?这是我自己的代码中的错误还是Apple backtrace中的错误?我被告知,信号处理程序可以做的事情有限,但我在sigaction man page上没有看到任何表明我做错的事情。
答案 0 :(得分:3)
您需要更仔细地阅读sigaction手册页!未在信号安全功能列表中列出的任何内容都在信号处理程序中禁止。 backtrace_symbols_fd()不在该列表中。你不能在信号处理程序中使用它。
如果您想确切了解原因,请访问Apple的开源网站并下载Libc代码。您的捕获说明了问题所在。如果你看一下“stdio / vprintf-fbsd.c”,你会看到__vfprintf()有这个评论:
/*
* Non-MT-safe version
*/
很多printf样式函数最终都在这里(snprintf是我们如何到达这里)。如果您的应用程序在printf样式函数中崩溃并且信号处理程序尝试重新输入,那么您所看到的意外行为是......预期的。
或者即使你的应用程序在printf样式函数中没有崩溃,但是当崩溃时某些其他线程恰好在printf样式函数中,你可能会看到这种行为。