Linux内核调试方法总结之backtrace

backtrace

【用途】用户态或者内核态程序异常退出时回溯堆栈信息

【原理】通过对当前堆栈的分析,回溯上层函数在当前栈中的帧地址,直至顶层函数。帧地址是指在栈中存在局部变量、上一级函数返回地址、寄存器值的内存空间。由于不同处理器堆栈实现不同(向上增长和向下增长),此功能的具体实现是编译器内建的__buildin_frame_address及__buildin_return_address函数。如果编译器不支持此函数,也可以自己实现该函数。

【接口说明】具体说明可以参考man backtrace帮助文档

execinfo.h

int backtrace (void **buffer, int size)
The backtrace function obtains a backtrace for the current thread, as a list of pointers, and places the information into buffer.

char ** backtrace_symbols (void *const *buffer, int size)

The backtrace_symbols function translates the information obtained from the backtrace function into an array of strings. The argument buffer should be a pointer to an array of addresses obtained via the backtrace function, and size is the number of entries in that array (the return value of backtrace).

void backtrace_symbols_fd(void *const *buffer, int size, int fd)

【实例】

#include <signal.h>

#include <stdio.h>

#include <stdlib.h>

#include <execinfo.h>

#include <fcntl.h>

#include <string.h>

#include <unistd.h>

#include <sys/types.h>

#include <sys/stat.h>

 

#define PRINT_DEBUG

#define MAX_BACKTRACE_LEVEL 10

#define BACKTRACE_LOG_NAME "backtrace.log"

 

static void show_reason(int sig, siginfo_t *info, void *secret){

    void *array[MAX_BACKTRACE_LEVEL];

    size_t size;

#ifdef PRINT_DEBUG

    char **strings;

    size_t i;

    size = backtrace(array, MAX_BACKTRACE_LEVEL);

    strings = backtrace_symbols(array, size);

    printf("Obtain %zd stack frames.\n", size);

    for(i = 0; i < size; i++)

    printf("%s\n", strings[i]);

    free(strings);

#else

    int fd = open(BACKSTRACE_LOG_NAME, O_CREAT | O_WRONLY);

    size = backtrace(array, MAX_BACKTRACE_LEVEL);

    backtrace_symbols_fd(array, size, fd);

    close(fd);

#endif

    exit(0);

}

 

void die() {

    char *str1;

    char *str2;

    char *str3;

    char *str4 = NULL;

    strcpy(str4, "ab");

}

 

void let_it_die() {

    die();

}

 

int main(int argc, char **argv){

    struct sigaction act;

    act.sa_sigaction = show_reason;

    sigemptyset(&act.sa_mask);

    act.sa_flags = SA_RESTART | SA_SIGINFO;

 

    sigaction(SIGSEGV, &act, NULL);

    sigaction(SIGUSR1, &act, NULL);

    sigaction(SIGFPE, &act, NULL);

  sigaction(SIGILL, &act, NULL);

    sigaction(SIGBUS, &act, NULL);

    sigaction(SIGABRT, &act, NULL);

    sigaction(SIGSYS, &act, NULL);

 

    let_it_die();

 

   return  0;

}

                                       

【调试】

1) 编译

huawei@WUH1000002965:~/test$ gcc backtrace_test.c -o backtrace_test -g –rdynamic

(注:-rdynamic,这个option是传递给linker的,linker会将symbol放到.dydym table中,这样backtrace_symbols才能获取到地址对应的symbol。所以即使是使用了-g来编译程序,如果不使用-rdynamic的话,backtrace_symbols也找不到地址对应的symbol。这是backtrace系列函数的一个缺陷)

2) 运行

huawei@WUH1000002965:~/test$ ./backtrace_test

Obtain 7 stack frames.

./backtrace_test() [0x40096e]

/lib/x86_64-linux-gnu/libc.so.6(+0x364a0) [0x7f2ff171c4a0]

./backtrace_test(die+0x18) [0x400a03]

./backtrace_test(let_it_die+0xe) [0x400a1d]

./backtrace_test(main+0xf6) [0x400b15]

/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xed) [0x7f2ff170776d]

./backtrace_test() [0x400889]

posted on 2016-04-23 14:04  者旨於陽  阅读(4008)  评论(0编辑  收藏  举报

导航