程序异常追栈方法

堆栈信息的打印

目的

解决程序异常退出的排查问题

代码实现

 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;
 }
  1. signal(SIGSEGV, print_trace): 注册SIGSEGV信号的重定向,当收到SIGSEGV信号时,跳转到print_trace函数
  2. backtrace 获取栈信息,返回值为一堆堆sp指针
  3. backtrace_symbols 将sp转为符号表

栈信息的分析

  1. 编译 要求在编译程序的时候带上-g -dynamic参数

  2. 栈信息

     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]
    
  3. 栈信息解析

    #2  ./stack_dump(+0x987e) [0x55847cc0987e]
    

    stack_dump : 程序名 0x987e:程序地址,可以通过addr2line找到对应的程序符号,如果程序没有带-g -dynamic,可能无法看到该信息 0x55847cc0987e:sp地址,通过该地址和/proc/self/maps可以计算出上面的程序地址,具体后面介绍

  4. 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
    
  5. 如何让程序自己翻译地址?

     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 ## 其他问题
  6. 如果程序在编译时没有带上-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应用程序崩溃
  7. backtrace相关的库

  • 20
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值