原理:
1 、当程序出现异常时通常伴随着会收到一个由内核发过来的异常信号 (查看 kill -l),
- 如当对内存出现非法访问时将收到段错误信号SIGSEGV,然后才退出。
- 利用这一点,当我们在收到异常信号后将程序的调用栈进行输出
2、捕获系统异常信号输出程序的调用栈: 头文件<execinfo.h>提供了三个相关的函数
int backtrace(void **buffer, int size); // 用于获取堆栈的地址信息
char **backtrace_symbols(void *const *buffer, int size); //把堆栈地址翻译成我们易识别的字符串,其被存放在buffer中
void backtrace_symbols_fd(void *const *buffer, int size, int fd); //把字符串堆栈信息输出到文件中
注意: GCC编译链接的话,加上“-rdynamic”参数,这个参数的意思是告诉ELF连接器添加“-export-dynamic”标记,这样所有的符号信息symbols就会添加到动态符号表中,以便查看完整的堆栈信息。
static函数不会导出符号信息symbols,在backtrace中无效。
某些编译器的优化选项对获取正确的函数调用堆栈有干扰,内联函数没有堆栈框架,删除框架指针也会导致无法正确解析堆栈内容。
示例:
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <execinfo.h> //backtrace(), backtrace_symbols()
// 几个写文件函数
FILE *g_dump_file = NULL;
void dump_open()
{
char chPath[] = "dumpfile.log";
if (g_dump_file)
{
fclose(g_dump_file);
g_dump_file = NULL;
}
g_dump_file = fopen(chPath,"wb");
}
void dump_write(char *p)
{
if ((!g_dump_file) || (!p))
return;
fwrite(p,1,strlen(p),g_dump_file);
}
void dump_close()
{
if (!g_dump_file)
return;
fclose(g_dump_file);
g_dump_file = NULL;
}
// 信号处理二
void core_dump(int sig_no)
{
#define BACKTRACE_SIZE 20
void *array[BACKTRACE_SIZE] = {0};
//获取程序的调用栈
size_t size = backtrace (array, BACKTRACE_SIZE); // [a]
char **__backtrace= backtrace_symbols (array, size); // [b] 此处有 molloc
//下面 %zd 指 size_t 参考 http://www.cplusplus.com/reference/cstdio/printf/
char ch[128];
sprintf_s(ch,sizeof(ch),"stack size [%zd]\r\n", size);
dump_open(); // [1]
dump_write(ch); // [2]
printf(ch); // stdout
for (size_t i = 0; i < size; i++)
{
dump_write(__backtrace[i]); // [2]
dump_write("\r\n"); // [2]
printf(__backtrace[i]); // stdout
printf("\r\n");
}
dump_close(); // [3]
free(__backtrace);// [b]
exit(0);
}
// 信号处理函数一
void sigroutine(int sig_no)
{
switch (sig_no) {
case 1:
printf("Get a signal -- SIGHUP "); // 挂起
break;
case 2:
printf("Get a signal -- SIGINT "); // 对应为ctrl + C键
break;
case 3:
printf("Get a signal -- SIGQUIT ");
break;
}
}
// 注册信号处理函数
void InstallSign()
{
signal(SIGHUP, sigroutine);//控制进程终止
signal(SIGINT, sigroutine);//键盘中断
signal(SIGQUIT, sigroutine);//退出
signal(SIGILL, core_dump); //非法指令
signal(SIGSEGV, core_dump); //段错误
}
int main()
{
InstallSign(); //先注册信号处理函数
// ...
return 0;
}
/* 信号列表 kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
*/