简体   繁体   中英

Writing an efficient backtrace function

I came across below code for walking backtrace

struct stack_frame {
  struct stack_frame *prev;
    void *return_addr;
} __attribute__((packed));
typedef struct stack_frame stack_frame;

__attribute__((noinline, noclone))
void backtrace_from_fp(void **buf, int size)
{
    int i;
    stack_frame *fp;

    __asm__("movl %%ebp, %[fp]" :  /* output */ [fp] "=r" (fp));

    for(i = 0; i < size && fp != NULL; fp = fp->prev, i++)
        buf[i] = fp->return_addr;
}

the reason behind looking for this code is we are using a 3rd party malloc hook hence don't want to use backtrace which again allocates memory. Above doesn't work for x86_64 and I modified asm statement to

    __asm__("movl %%rbp, %[fp]" :  /* output */ [fp] "=r" (fp));

I get crash

(gdb) bt
#0  backtrace_from_fp (size=10, buf=<optimized out>) at src/tcmalloc.cc:1910
#1  tc_malloc (size=<optimized out>) at src/tcmalloc.cc:1920
#2  0x00007f5023ade58d in __fopen_internal () from /lib64/libc.so.6
#3  0x00007f501e687956 in selinuxfs_exists () from /lib64/libselinux.so.1
#4  0x00007f501e67fc28 in init_lib () from /lib64/libselinux.so.1
#5  0x00007f5029a32503 in _dl_init_internal () from /lib64/ld-linux-x86-64.so.2
#6  0x00007f5029a241aa in _dl_start_user () from /lib64/ld-linux-x86-64.so.2
#7  0x0000000000000001 in ?? ()
#8  0x00007fff22cb8e24 in ?? ()
#9  0x0000000000000000 in ?? ()
(gdb)
(gdb) p $rbp
$2 = (void *) 0x7f501e695f37
(gdb) p (stack_frame *)$rbp
$3 = (stack_frame *) 0x7f501e695f37
(gdb) p *$3
$4 = {prev = 0x69662f636f72702f, return_addr = 0x6d6574737973656c}
(gdb) x /1xw 0x69662f636f72702f
0x69662f636f72702f:     Cannot access memory at address 0x69662f636f72702f
(gdb) fr
#0  backtrace_from_fp (size=10, buf=<optimized out>) at src/tcmalloc.cc:1910
1910    in src/tcmalloc.cc
(gdb)

Am I missing something ?. Any help on how can I reconstruct the same via code ?.

Am I missing something ?

The code you referenced assumes the compiled code is using frame pointer register chain.

This was the default on (32-bit) i*86 up until about 5-7 years ago, and has not been the default on x86_64 since ~forever.

The code will most likely work fine in non-optimized builds, but will fail miserably with optimization on both 32-bit and 64-bit x86 platforms using non-ancient versions of the compiler.

If you can rebuild all code (including libc ) with -fno-omit-frame-pointer , then this code will work most of the time (but not all the time, because libc may have hand-coded assembly, and that assembly will not have frame pointer chain).

One solution is to use libunwind . Unfortunately, using it from inside malloc can still run into a problem , if you (or any libraries you use) also use dlopen .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM