获取栈信息

■ API
    // 获取将backstrace信息,将地址存到buffer中。
    // 参数size指定buffer的最大值,返回值则是backstrace的实际大小
    int backtrace(void **buffer, int size)
   
    // 根据buffer指定的地址,返回符号信息。参数size指定返回符号信息的大小
    char ** backtrace_symbols(void *const *buffer, int size)
   
    // 类似backtrace_symbols()函数,但是不需要malloc空间来存放符号信息,
    // 而是将结果写到文件描述符fd所代表的文件中
    void backtrace_symbols_fd(void *const *buffer, int size, int fd)
   
    注意:

  . 使用函数backtrace_symbols()或者backtrace_symbols_fd()时,需要用-rdynamic编译才能得到正确的符号名,否则只能得到偏移地址。

  . 为了可以判断地址对应的代码行号,需要编译追加-g选项。

■ 实例

  ① 代码  

 1 //gcc test.c -g -rdynamic -o test
 2 #include <execinfo.h> 
 3 #include <stdio.h> 
 4 #include <stdlib.h> 
 5 #include <fcntl.h>
 6 
 7 #define PRINT_DEBUG
 8 
 9 /* Obtain a backtrace and print it to stdout. */ 
10 void print_trace(void) 
11 { 
12      void *array[10]; 
13      size_t size; 
14      char **strings; 
15      size_t i;
16 
17      size = backtrace(array, 10); 
18 #ifdef PRINT_DEBUG
19     strings = backtrace_symbols(array, size); 
20      printf("Obtained %zd stack frames.\n", size); 
21      for(i = 0; i < size; i++) 
22          printf("%s\n", strings[i]); 
23      free(strings); 
24 #else
25     int fd = open("err.log", O_CREAT | O_WRONLY);
26     backtrace_symbols_fd(array, size, fd);
27     close(fd);
28 #endif
29 
30 }
31 
32 /* A dummy function to make the backtrace more interesting. */ 
33 void dummy_function(void) 
34 { 
35     print_trace(); 
36 } 
37 
38 int main(void) 
39 { 
40      dummy_function(); 
41      return 0; 
42 }

     ② 结果

   root@ubuntu:/home/linux/develop/backtrace# gcc test.c -g -rdynamic -o test
   root@ubuntu:/home/linux/develop/backtrace# ./test
   Obtained 4 stack frames.
   ./test(print_trace+0x19) [0x80486ad]
   ./test(dummy_function+0xb) [0x8048719]
   ./test(main+0xb) [0x8048726]
   /lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3) [0x7fb113]
   root@ubuntu:/home/linux/develop/backtrace# addr2line 0x80486ad -f -e test
   print_trace
   /home/linux/develop/backtrace/test.c:17
   root@ubuntu:/home/linux/develop/backtrace# addr2line 0x8048719 -f -e test
   dummy_function
   /home/linux/develop/backtrace/test.c:36
   root@ubuntu:/home/linux/develop/backtrace# addr2line 0x8048726 -f -e test
   main
   /home/linux/develop/backtrace/test.c:41    

■ 应用

  reset时,获取进程的栈信息

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <execinfo.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>

#define PRINT_DEBUG

static void print_reason(int sig)
{
    void *array[10];
    size_t size;
    size = backtrace(array, 10);
#ifdef PRINT_DEBUG
    char **strings;
    int i;
    strings = backtrace_symbols(array, size);
    printf("Obtained %d stack frames.\n", size);
    for (i = 0; i < size; i++)
        printf("%s\n", strings[i]);
    free(strings);

    char cmd[256] = "addr2line -C -f -e ";
    char* prog = cmd + strlen(cmd);
    readlink("/proc/self/exe", prog, sizeof(cmd) - strlen(cmd) - 1);// 获取进程的完整路径

    FILE* fp = popen(cmd, "w");
    if (fp != NULL)
    {
        for (i = 0; i < size; ++i)
        {
            fprintf(fp, "%p\n", array[i]);
        }
        pclose(fp);
    }
#else
    int fd = open("err.log", O_CREAT | O_WRONLY);
    backtrace_symbols_fd(array, size, fd);
    close(fd);
#endif
    exit(0);
}
void die()
{
    char *test1;
    char *test2;
    char *test3;
    char *test4 = NULL;
    strcpy(test4, "ab");
}
void test1()
{
    die();
}
int main(int argc, char **argv)
{
    struct sigaction myAction;
    myAction.sa_handler = print_reason;
    sigemptyset(&myAction.sa_mask);
    myAction.sa_flags = SA_RESTART | SA_SIGINFO;
    sigaction(SIGSEGV, &myAction, NULL); // 无效内存引用
    sigaction(SIGABRT, &myAction, NULL); // 异常终止
    test1();
}

 

posted @ 2013-09-08 11:03  renhl  阅读(378)  评论(0编辑  收藏  举报