堆栈信息的打印
目的
解决程序异常退出的排查问题
代码实现
void print_trace(int signum)
{
#define STACK_SIZE 16
void *array[STACK_SIZE];
size_t size;
char **strings;
size_t i;
size = backtrace(array, STACK_SIZE);
strings = backtrace_symbols(array, size);
printf("************** stack dump, signum:%d **************\\n", signum);
TERROR("************** stack dump, signum:%d **************\\n", signum);
// 从 1 开始遍历,第 0 层为当前栈,忽略
for(i = 1; i < size; i++)
{
printf("#%ld %s\\n", i, strings[i]);
TERROR("#%ld %s\\n", i, strings[i]);
}
signal(signum, SIG_DFL);
free(strings);
}
int sig_proc_reg()
{
signal(SIGSEGV, print_trace);
signal(SIGABRT, print_trace);
signal(SIGFPE, print_trace);
return 0;
}
- signal(SIGSEGV, print_trace): 注册SIGSEGV信号的重定向,当收到SIGSEGV信号时,跳转到print_trace函数
- backtrace 获取栈信息,返回值为一堆堆sp指针
- backtrace_symbols 将sp转为符号表
栈信息的分析
-
编译 要求在编译程序的时候带上-g -dynamic参数
-
栈信息
root@localhost:/share/debug# ./stack_dump ************** stack dump, signum:11 ************** #1 /lib/x86_64-linux-gnu/libc.so.6(+0x43090) [0x7fb1630e7090] #2 ./stack_dump(+0x987e) [0x55847cc0987e] #3 /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf3) [0x7fb1630c8083] #4 ./stack_dump(+0x975a) [0x55847cc0975a]
-
栈信息解析
#2 ./stack_dump(+0x987e) [0x55847cc0987e]
stack_dump : 程序名 0x987e:程序地址,可以通过addr2line找到对应的程序符号,如果程序没有带-g -dynamic,可能无法看到该信息 0x55847cc0987e:sp地址,通过该地址和/proc/self/maps可以计算出上面的程序地址,具体后面介绍
-
addr2line翻译 格式:addr2line -e {程序} -f -i {程序地址}
root@localhost:/share/debug# addr2line -e stack_dump -f -i 0x987e main /home/debug/workspace/NxBoxV30_BMC_X86_sys/source/02-apps/mgmt/mgmtmain.c:48
-
如何让程序自己翻译地址?
static void backtrace_symbols_detail(void *array[], int size) { int i; char cmd[CMD_SIZE] = "addr2line -C -f -e "; char* prog = cmd + strlen(cmd); int r = readlink("/proc/self/exe", prog, sizeof(cmd) - (prog-cmd)-1); if (r == -1) { fprintf(stderr, "backtrace_symbols_detail unsupported!\\n"); perror("readlink"); return; } prog[r] = '\\0'; FILE* fp = popen(cmd, "w"); if (!fp) { perror("popen"); return; } for (i = 1; i < size; ++i) {//from 1, ignore this file info fprintf(fp, "%p\\n", array[i]); } fclose(fp); }
- /proc/self/exe 获取到应用程序的程序路径
- addr2line -C -f -e : 支持持续输入程序地址,再翻译成符号表
root@localhost:/share/debug# addr2line -C -f -e stack_dump 0x987e main /home/debug/workspace/NxBoxV30_BMC_X86_sys/source/02-apps/mgmt/mgmtmain.c:48 0x987e main /home/debug/workspace/NxBoxV30_BMC_X86_sys/source/02-apps/mgmt/mgmtmain.c:48 0x9870 main /home/debug/workspace/NxBoxV30_BMC_X86_sys/source/02-apps/mgmt/mgmtmain.c:45
## 其他问题
-
如果程序在编译时没有带上-g -dynamic怎么办?
- 通过读取/proc/self/maps 文件,找到对应的段,将sp减掉段的起始地址,则找到程序地址
- maps文件, 如果异常的sp为7f65c020b111,可以看到7f65c020b111在下面的第一条记录里面,将7f65c020b111 - 7f65c020b000 = 0x111,得到0x111为程序地址,再用上面的addr2line翻译
7f65c020b000-7f65c4000000 ---p 00000000 00:00 0 7f65c4000000-7f65c4230000 rw-p 00000000 00:00 0 7f65c4230000-7f65c8000000 ---p 00000000 00:00 0 7f65c87f9000-7f65c87fa000 ---p 00000000 00:00 0 7f65c87fa000-7f65c8ffa000 rw-p 00000000 00:00 0 7f65c8ffa000-7f65c8ffb000 ---p 00000000 00:00 0 7f65c8ffb000-7f65c97fb000 rw-p 00000000 00:00 0 7f65c97fb000-7f65c97fc000 ---p 00000000 00:00 0 7f65c97fc000-7f65c9ffc000 rw-p 00000000 00:00 0 7f65c9ffc000-7f65c9ffd000 ---p 00000000 00:00 0 7f65c9ffd000-7f65ca7fd000 rw-p 00000000 00:00 0 7f65ca7fd000-7f65ca7fe000 ---p 00000000 00:00 0 7f65ca7fe000-7f65caffe000 rw-p 00000000 00:00 0 7f65caffe000-7f65cafff000 ---p 00000000 00:00 0 7f65cafff000-7f65cb7ff000 rw-p 00000000 00:00 0
- 如何定位Linux应用程序崩溃
-
backtrace相关的库