android backtrace实现

狄睿
2023-12-01

前景

backtrace 文档

说明

通过数组获取调用栈; 一般获取的是内存地址;

借助dladdr获取地址信息, 计算可执行文件中的偏移地址;

实现

有的没有实现backtrace但是大多都支持unwind; 利用unwind实现类似;

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

查看堆栈

pstack

gdb

void show() {
    while(1);
}
int main() {
    show();
}

执行输出

ch@ch-ubuntu:~/ch/cfile/test$ gcc test.c -g
ch@ch-ubuntu:~/ch/cfile/test$ ./a.out &
[2] 42592
ch@ch-ubuntu:~/ch/cfile/test$ sudo gdb -q -ex "bt" -ex "q" -p 42592 <<< "Y"
Attaching to process 42592
Reading symbols from /home/ch/ch/cfile/test/a.out...
Reading symbols from /lib/x86_64-linux-gnu/libc.so.6...
Reading symbols from /usr/lib/debug/.build-id/d1/704d25fbbb72fa95d517b883131828c0883fe9.debug...
Reading symbols from /lib64/ld-linux-x86-64.so.2...
Reading symbols from /usr/lib/debug/.build-id/29/2e105c0bb3ee8e8f5b917f8af764373d206659.debug...
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
0x0000558c38dab132 in show () at test.c:2
2           while(1);
#0  0x0000558c38dab132 in show () at test.c:2
#1  0x0000558c38dab146 in main () at test.c:5
A debugging session is active.

        Inferior 1 [process 42592] will be detached.

Quit anyway? (y or n) [answered Y; input not from terminal]
Detaching from program: /home/ch/ch/cfile/test/a.out, process 42592
[Inferior 1 (process 42592) detached]

backtrace获取

获取地址

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

void cool()
{
show();
}

int main()
{
   cool();
}

gdb查看

ch@ch-ubuntu:~/ch/cfile/test$ gdb ./a.out -q
Reading symbols from ./a.out...
(gdb) b 6
Breakpoint 1 at 0x11b2: file test.c, line 6.
(gdb) list 6
1       #include <execinfo.h>
2       void show()
3       {
4          void * s[30] = {0};
5          backtrace(s,30);
6          return;
7       }
8
9       void cool()
10      {
(gdb) r
Starting program: /home/ch/ch/cfile/test/a.out 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Breakpoint 1, show () at test.c:6
6          return;
(gdb) x /3xg s
0x7fffffffdf70: 0x00005555555551b2      0x00005555555551db
0x7fffffffdf80: 0x00005555555551f0
(gdb) bt
#0  show () at test.c:6
#1  0x00005555555551db in cool () at test.c:11
#2  0x00005555555551f0 in main () at test.c:16
(gdb) p show
$1 = {void ()} 0x555555555169 <show>

addr2line解析

ch@ch-ubuntu:~/ch/cfile/test$ addr2line -fapCe a.out 0x11b2  0x11db 11f0
0x00000000000011b2: show at /home/ch/ch/cfile/test/test.c:6
0x00000000000011db: cool at /home/ch/ch/cfile/test/test.c:12
0x00000000000011f0: main at /home/ch/ch/cfile/test/test.c:16

获取文件偏移量

#define _GNU_SOURCE
#include <dlfcn.h>
#include <execinfo.h>
#include <stdio.h>
void show()
{
   void * s[30] = {0};
   int n = backtrace(s,30);
   int i;
   Dl_info info;
   for(i = 0 ; i < n; i++)
   {
        // 获取提供地址信息, 地址没有对应symbol, dli_saddr可能会为0;
        // 这里直接用输入地址作为比较;
        dladdr(s[i],&info);
        printf("%p %p %p\n", info.dli_fbase, s[i], (void*)((char*)s[i] - (char*)info.dli_fbase));
   }
}

void cool()
{
show();
}

int main() {
    cool();
}

执行和解析

ch@ch-ubuntu:~/ch/cfile/test$ ./a.out 
0x56344a851000 0x56344a8521f2 0x11f2
0x56344a851000 0x56344a8522b3 0x12b3
0x56344a851000 0x56344a8522c8 0x12c8
0x7f56fcc00000 0x7f56fcc23510 0x23510
0x7f56fcc00000 0x7f56fcc235c9 0x235c9
0x56344a851000 0x56344a8520e5 0x10e5
ch@ch-ubuntu:~/ch/cfile/test$ addr2line -fapCe a.out 0x11f2  0x12b3 0x12c8 0x23510 0x235c9 0x10e5
0x00000000000011f2: show at /home/ch/ch/cfile/test/test.c:8
0x00000000000012b3: cool at /home/ch/ch/cfile/test/test.c:21
0x00000000000012c8: main at /home/ch/ch/cfile/test/test.c:24
0x0000000000023510: ?? ??:0
0x00000000000235c9: ?? ??:0
0x00000000000010e5: _start at ??:?

自定义

主要是有些环境没有实现android,docker

参考链接 https://stackoverflow.com/questions/8115192/android-ndk-getting-the-backtrace

案例

#define _GNU_SOURCE
#include <dlfcn.h>
#include <unwind.h>


typedef unsigned int uint;

struct BacktraceState
{
   void** current;
   void** end;
};

static _Unwind_Reason_Code unwind_backtrace(struct _Unwind_Context* context, void* arg)
{
   struct  BacktraceState* state = (struct BacktraceState*)(arg);
   _Unwind_Word pc = _Unwind_GetIP(context);
   if (pc) {
       if (state->current == state->end) {
           return _URC_END_OF_STACK;
       } else {
           *state->current++ = (void*)(pc);
       }
   }
   return _URC_NO_REASON;
}

uint backtrace(void** buffer, uint max)
{
   struct BacktraceState state = {buffer, buffer + max};
   _Unwind_Backtrace(unwind_backtrace, &state);
   return state.current - buffer;
}

void backtrace2offset(void** buffer, uint buffer_len) {
    int i = 0;
    Dl_info info;
    for(i = 0; i < buffer_len; i++) {
        dladdr(buffer[i],&info);
        buffer[i] = (void*)((char*)buffer[i] - (char*)info.dli_fbase);
    }
}

#include<stdio.h>
#include<string.h>
void show() {
    void *s[30] = {0}, *offset[30]={0};
    int n = backtrace(s, sizeof(s)/sizeof(*s));
    memcpy(offset, s, sizeof(offset));
    backtrace2offset(offset, n);
    int i = 0;
    for(; i<n ; i++) printf("%p ", offset[i]);
    printf("\n");
}

int main() {
    show();
}

执行输出

ch@ch-ubuntu:~/ch/cfile/test$ gcc test.c -g
ch@ch-ubuntu:~/ch/cfile/test$ ./a.out 
0x12ad 0x13df 0x15f1 0x23510 0x235c9 0x1125 
ch@ch-ubuntu:~/ch/cfile/test$ addr2line -fapCe a.out 0x12ad 0x13df 0x15f1 0x23510 0x235c9 0x1125
0x00000000000012ad: backtrace at /home/ch/ch/cfile/test/test.c:32
0x00000000000013df: show at /home/ch/ch/cfile/test/test.c:48
0x00000000000015f1: main at /home/ch/ch/cfile/test/test.c:57
0x0000000000023510: ?? ??:0
0x00000000000235c9: ?? ??:0
0x0000000000001125: _start at ??:?

说明

  • struct BacktraceState,头尾指针的数组.存放 void* 类型数据的指针数组.
  • _Unwind_Reason_Code 回调函数返回值.
  • struct _Unwind_Context* context 回调函数第一参数.
  • void* arg 用户用于存放数据的结构体,任意类型.
  • unwindCallback 自定义的回调函数,传入堆栈信息 content ,和用户的容器指针.
  • _Unwind_GetIP 获取栈指针.
  • _Unwind_Backtrace 实际函数,回调和容器.

https://github.com/llvm-mirror/libunwind/blob/master/include/unwind.h

 类似资料: