Linux系統應用程序發生異常時,系統一般會發出相應的信號,因此在收到信號後打印調用棧即可實現相應功能。
引起程序退出的信號主要有:
signal(SIGABRT, dump_backtrace);
signal(SIGBUS, dump_backtrace);
signal(SIGFPE, dump_backtrace);
signal(SIGILL, dump_backtrace);
signal(SIGSEGV, dump_backtrace);
- 打印調用棧可以使用gcc自帶的 backtrace 函數:
void dump_backtrace(int sig)
{
int tracefd = 0;
void *array[50];
size_t size;
tracefd = open(BACKTRACE_FILE, O_CREAT | O_APPEND | O_WRONLY, 0666);
if (tracefd < 0) {
fprintf(stderr, "unable to open backtrace file '%s': %s\n", BACKTRACE_FILE, strerror(errno));
exit(0);
}
size = backtrace(array, 50);
backtrace_symbols_fd(array, size, tracefd);
close(tracefd);
exit(0);
}
- 或者使用libunwind等第三方庫實現:
static void dump_backtrace(int sig) {
unw_cursor_t cursor;
unw_context_t context;
unw_word_t ip;
unw_word_t offset;
int tracefd = 0;
ssize_t r;
char fname[128] = {0};
char stac[128 * 3] = {0};
int len = 0;
tracefd = open(BACKTRACE_FILE, O_CREAT | O_APPEND | O_WRONLY, 0666);
if (tracefd < 0) {
fprintf(stderr, "unable to open backtrace file '%s': %s\n", BACKTRACE_FILE, strerror(errno));
exit(0);
}
unw_getcontext(&context);
unw_init_local(&cursor, &context);
while (unw_step(&cursor) > 0) {
memset(fname, 0, sizeof(fname));
unw_get_reg(&cursor, UNW_REG_IP, &ip);
unw_get_proc_name(&cursor, fname, sizeof(fname), &offset);
len = snprintf(stac, sizeof(stac), "0x%016lx: %s+0x%lx\n", ip, fname, offset);
r = write(tracefd, stac, len);
if (r < 0) {
close(tracefd);
exit(0);
}
}
close(tracefd);
exit(0);
}