android backtrace实现

  1. backtrace

    • 简介

      • 有的系统没有实现backtrace这个函数.
      • 常见的就有linux还有一些docker的库.
    • 判断是否支持backtrace

      #include<stdio.h>
      #include<dlfcn.h>
      int main()
      {
                
                
         void * bt = dlsym(NULL,"backtrace");
         if(NULL != bt)
         {
                
                
             printf("support\n");
         }
         else
         {
                
                
             printf("not support\n");
         }
         return 0;
      }
      
      
    • 输出

      [root@localhost bt_support]# gcc test.c -ldl -g
      [root@localhost bt_support]# ./a.out
      support
      
      
  2. 堆栈

    • 说明

      • 查看某一个时刻,函数调用栈.
      • 崩溃 的时候输出,可以精确的查看在什么地方出的问题.
    • 常见工具

      • pstack -p pid查看正在执行的进程某一时刻的堆栈.
      • gdb.
    • 案例

      (gdb) bt
      #0  show () at test.c:3
      #1  0x0000000000400501 in cool () at test.c:7
      #2  0x0000000000400511 in main () at test.c:12
      
      
    • 注意

      • 必须要有调试信息.
      • 否则结果是??:00这类无意义数据.
  3. 获取堆栈

    • 说明

      • 堆栈一般是代码地址.
      • 也可能是共享库的映射地址.
    • 获取地址

      #include <execinfo.h>
      void show()
      {
                
                
         void * s[30] = {
                
                0};
         backtrace(s,30);
         return;
      }
      
      void cool()
      {
                
                
      show();
      }
      
      int main()
      {
                
                
         cool();
      }
      
      

      输出

      (gdb) bt
      #0  show () at test.c:6
      #1  0x0000000000400587 in cool () at test.c:11
      #2  0x0000000000400597 in main () at test.c:16
      (gdb) x /3xg s
      0x7fffffffe320: 0x0000000000400576      0x0000000000400587
      0x7fffffffe330: 0x0000000000400597
      (gdb) p show
      $2 = {
                
                void ()} 0x40053d <show>
      
      • backtrace获取到的地址和gdb bt指令获取到的一样.

      addr2line

      [root@localhost bt_support]# addr2line -fap 0x0000000000400576 0x0000000000400587 0x0000000000400597
      0x0000000000400576: show at /root/cfile/bt_support/test.c:6
      0x0000000000400587: cool at /root/cfile/bt_support/test.c:12
      0x0000000000400597: main at /root/cfile/bt_support/test.c:17
      
      • 得到的结果基本相同.
  4. 动态库

    • 获取堆栈

      test.c

      #define _GNU_SOURCE
      #include <dlfcn.h>
      #include <execinfo.h>
      void show()
      {
                
                
         void * s[30] = {
                
                0};
         int n = backtrace(s,30);
         int i;
         Dl_info info;
         for(i = 0 ; i < n; i++)
         {
                
                
             dladdr(s[i],&info); // info.dli_saddr  失效时可以用s[i]获取函数栈的指针,dladdr 主要是获取 so 的挂载起始地址。
             i+=0;
         }
         return;
      }
      
      void cool()
      {
                
                
      show();
      }
      
      

      main.c

      extern void cool();
      int main()
      {
                
                
         cool();
      }
      
      

      编译

      gcc -fPIC -shared -o test.so test.c -g
      gcc ./test.so main.c -g -ldl
      

      调试输出

      (gdb) p info
      $1 = {
                
                dli_fname = 0x7ffff7ff9640 "./test.so", dli_fbase = 0x7ffff7bd9000,
       dli_sname = 0x7ffff7bd942d "show", dli_saddr = 0x7ffff7bd9785 <show>}
      
      (gdb) printf "%x\n",info.dli_saddr - info.dli_fbase
      785
      
      (gdb) !addr2line -e ./test.so 806
      /root/cfile/bt_support/test.c:21
      
      (gdb) bt
      #0  show () at test.c:10
      #1  0x00007ffff7bd9806 in cool () at test.c:20
      #2  0x000000000040063b in main () at main.c:4
      
      • addr信息需要借助dladdr.获取某个地址的信息.
  5. 实现backtrace

    • 参考链接

    • 应用场景

      • 有的会没有这个函数,比如android,docker.
    • 堆栈

      #include <unwind.h>
      
      struct BacktraceState
      {
                
                
         void** current;
         void** end;
      };
      
      static _Unwind_Reason_Code unwindCallback(struct _Unwind_Context* context, void* arg)
      {
                
                
         BacktraceState* state = static_cast<BacktraceState*>(arg);
         uintptr_t pc = _Unwind_GetIP(context);
         if (pc) {
                
                
             if (state->current == state->end) {
                
                
                 return _URC_END_OF_STACK;
             } else {
                
                
                 *state->current++ = reinterpret_cast<void*>(pc);
             }
         }
         return _URC_NO_REASON;
      }
      
      }
      
      size_t captureBacktrace(void** buffer, size_t max)
      {
                
                
         BacktraceState state = {
                
                buffer, buffer + max};
         _Unwind_Backtrace(unwindCallback, &state);
      
         return state.current - buffer;
      }
      
      • struct BacktraceState,头尾指针的数组.存放void*类型数据的指针数组.
      • _Unwind_Reason_Code 回调函数返回值.
      • struct _Unwind_Context* context回调函数第一参数.
      • void* arg用户用于存放数据的结构体,任意类型.
      • unwindCallback自定义的回调函数,传入堆栈信息content,和用户的容器指针.
      • _Unwind_GetIP获取栈指针.
      • _Unwind_Backtrace 实际函数,回调和容器.

猜你喜欢

转载自blog.csdn.net/rubikchen/article/details/120301446