打印堆栈是调试的常用方法,一般在系统异常时,我们可以将异常情况下的堆栈打印出来,这样十分方便错误查找。
先介绍下比较简单的backtrace方式,后面有机会可以尝试下core文件+gdb的方式。
backtrace打印堆栈方式
编译选项增加 -O0 -rdynamic -g -funwind-tables -ffunction-sections
CFLAGS ?= -mcpu=ck810 -static -mlittle-endian -Werror -O0 -rdynamic -g -funwind-tables -ffunction-sections
ASFLAGS += -O0
代码添加
#include <signal.h> /* for signal */ #include <execinfo.h> /* for backtrace() */ #define BACKTRACE_SIZE 32 void dump(void) { int j, nptrs; void *buffer[BACKTRACE_SIZE]; char **strings; printf("backtrace... "); nptrs = backtrace(buffer, BACKTRACE_SIZE); printf("backtrace() returned %d addresses ", nptrs); strings = backtrace_symbols(buffer, nptrs); if (strings == NULL) { perror("backtrace_symbols"); exit(EXIT_FAILURE); } for (j = 0; j < nptrs; j++) printf(" [%02d] %s ", j, strings[j]); free(strings); } void signal_handler(int signo) { #if 0 char buff[64] = {0x00}; sprintf(buff,"cat /proc/%d/maps", getpid()); system((const char*) buff); #endif printf(" =========>>>catch signal %d <<<========= ", signo); printf("Dump stack start... "); dump(); printf("Dump stack end... "); //exit(-1); signal(signo, SIG_DFL); /* 恢复信号默认处理 */ raise(signo); /* 重新发送信号 */ printf("app exit "); }
int main(void) { signal(SIGABRT, signal_handler); signal(SIGBUS, signal_handler); signal(SIGSEGV, signal_handler); }
执行结果:
=========>>>catch signal 11 <<<=========
Dump stack start...
backtrace...
backtrace() returned 7 addresses
[00] ./app-demo() [0xbf6e] // 最后一条指令
[01] ./app-demo() [0xc086] // 倒数第2调指令
[02] [0x2aac6000]
[03] /lib/libc.so.6(memcpy+0x30) [0x2ab516a0]
[04] ./app-demo() [0xbb62] // 出问题的地方
[05] ./app-demo() [0x9e92]
[06] /lib/libpthread.so.0(+0x61d0) [0x2aacd1d0]
Dump stack end...
app exit
Segmentation fault
文件分析:
在虚拟机上使用GNU工具中的addr2line工具获取源码位置
# addr2line -e app/app-demo 0xbc62
# /home/cql/smb/fuxi_h/demo-linux-wjc-20201211/app/main.c:538
附录:
SIGHUP 1 A 终端挂起或者控制进程终止
SIGINT 2 A 键盘中断(如break键被按下) 正常终止
SIGQUIT 3 C 键盘的退出键被按下
SIGILL 4 C 非法指令
SIGABRT 6 C 由abort(3)发出的退出指令,异常终止
SIGIO 23,29,22 A 某I/O操作现在可以进行了(4.2 BSD)
SIGKILL 9 AEF 杀死进程的终极方法
SIGSEGV 11 C 无效的内存引用
SIGPIPE 13 A 管道破裂: 写一个没有读端口的管道
SIGALRM 14 A 由alarm(2)发出的信号
SIGTERM 15 A 终止信号
SIGUSR1 30,10,16 A 用户自定义信号1 给用户做进程间通信,不像其他的处理方法被绑定了
SIGUSR2 31,12,17 A 用户自定义信号2
SIGCHLD 20,17,18 B 子进程结束信号
SIGCONT 19,18,25 进程继续(曾被停止的进程)
SIGSTOP 17,19,23 DEF 终止进程
SIGTSTP 18,20,24 D 控制终端(tty)上按下停止键
SIGTTIN 21,21,26 D 后台进程企图从控制终端读
SIGTTOU 22,22,27 D 后台进程企图从控制终端写