c++运用backtrace追踪函数调用的堆栈

一般察看函数运行时堆栈的方法是使用GDB之类的外部调试器,但是,有些时候为了分析程序的BUG,(主要针对长时间运行程序的分析),在程序出错时打印出函数的调用堆栈是非常有用的。

在头文件"execinfo.h"中声明了三个函数用于获取当前线程的函数调用堆栈

Function: int backtrace(void **buffer,int size)

该函数用与获取当前线程的调用堆栈,获取的信息将会被存放在buffer中,它是一个指针列表。参数 size 用来指定buffer中可以保存多少个void* 元素。函数返回值是实际获取的指针个数,最大不超过size大小在buffer中的指针实际是从堆栈中获取的返回地址,每一个堆栈框架有一个返回地址,注意某些编译器的优化选项对获取正确的调用堆栈有干扰,另外内联函数没有堆栈框架;删除框架指针也会使无法正确解析堆栈内容。

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

backtrace_symbols
将从backtrace函数获取的信息转化为一个字符串数组. 参数buffer应该是从backtrace函数获取的数组指针,size是该数组中的元素个数(backtrace的返回值)。函数返回值是一个指向字符串数组的指针,它的大小同buffer相同.每个字符串包含了一个相对于buffer中对应元素的可打印信息.它包括函数名,函数的偏移地址,和实际的返回地址。
现在,只有使用ELF二进制格式的程序和苦衷才能获取函数名称和偏移地址.在其他系统,只有16进制的返回地址能被获取.另外,你可能需要传递相应的标志给链接器,以能支持函数名功能(比如,在使用GNU ld的系统中,你需要传递(-rdynamic))。
该函数的返回值是通过malloc函数申请的空间,因此调用这必须使用free函数来释放指针
.
注意:如果不能为字符串获取足够的空间函数的返回值将会为
NULL

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

backtrace_symbols_fd与backtrace_symbols 函数具有相同的功能,不同的是它不会给调用者返回字符串数组,而是将结果写入文件描述符为fd的文件中,每个函数对应一行.它不需要调用malloc函数,因此适用于有可能调用该函数会失败的情况。

 

下面是一个使用backtrace捕获异常并打印函数调用堆栈的例子:

 

#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[64] = "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();

}

 

 

我本机测试打印出的信息如下:

Obtained 7 stack frames.

/root/workspace/test/Debug/test(__gxx_personality_v0+0x12d) [0x80486c1]

[0x71b440]

/root/workspace/test/Debug/test(__gxx_personality_v0+0x2ac) [0x8048840]

/root/workspace/test/Debug/test(__gxx_personality_v0+0x2c0) [0x8048854]

/root/workspace/test/Debug/test(__gxx_personality_v0+0x339) [0x80488cd]

/lib/libc.so.6(__libc_start_main+0xdc) [0xbf3e9c]

/root/workspace/test/Debug/test(__gxx_personality_v0+0x5d) [0x80485f1]

print_reason

/root/workspace/test/Debug/../main.cpp:15

??

??:0

die()

/root/workspace/test/Debug/../main.cpp:51

test1()

/root/workspace/test/Debug/../main.cpp:56

main

/root/workspace/test/Debug/../main.cpp:65

??

??:0

_start

??:0

 

 

你可能感兴趣的:(Trac)