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

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM