• 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

     

  • 相关阅读:
    .NET 面试题汇总(带答案)
    C#声明一个100大小的数组 随机生成1-100之间不重复的数
    添加和读取Resources嵌入资源文件(例如.dll和.ssk文件)
    C#DataTable转List<T>互转
    “不允许使用邮箱名称。服务器响应为:”的错误解决办法
    微信多开防撤回(带提示)2.8.0.133补丁
    逆向某网站的登录接口生成元素加密
    C#中new的三种用法
    SQL Server查询第31到40条数据
    关于EF框架EntityState的几种状态
  • 原文地址:https://www.cnblogs.com/osyun/p/2644018.html
Copyright © 2020-2023  润新知