函式呼叫堆疊追蹤(1)
阿新 • • 發佈:2019-01-04
對於一個比較大的工程,函式的呼叫關係錯綜複雜,這時候僅僅靠IDE靜態檢視函式的呼叫關係已經遠遠不夠,所以為了更好地理解程式碼,還是有必要去掌握一些方法和工具來跟蹤函式的呼叫堆疊。
一、設斷點
在eclipse裡可以設定斷點檢視函式的堆疊呼叫情況,這裡比較方便的是可以直接定位到上級函式檢視變數的值。
二、使用backtrace
backtrace功能可以跟蹤函式的呼叫堆疊,這個需要glibc庫的支援,所以一般情況下是在linux平臺使用。當然使用msys2、cygwin等工具在win32平臺下模擬linux環境也是可以的,就是需要折騰一下。
要獲取函式的呼叫堆疊資訊,主要通過以下3 個函式來實現:
#include <execinfo.h>
int backtrace(void **buffer, int size);
char **backtrace_symbols(void *const *buffer, int size);
void backtrace_symbols_fd(void *const *buffer, int size, int fd);
backtrace只是獲得了函式的堆疊地址,儲存到buffer指標數組裡,每個陣列元素儲存一個堆疊地址,size為堆疊的最大深度。
backtrace_symbols 把堆疊地址轉換為函式符號儲存到一個二維指標裡並返回,static函式不能匯出。
backtrace_symbols_fd的作用和backtrace_symbols類似,只是把函式符號匯出的檔案。
具體的例子可以參考:
程式如下:
#include <execinfo.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #define BT_BUF_SIZE 100 void myfunc3(void) { int j, nptrs; void *buffer[BT_BUF_SIZE]; char **strings; nptrs = backtrace(buffer, BT_BUF_SIZE); printf("backtrace() returned %d addresses\n", nptrs); /* The call backtrace_symbols_fd(buffer, nptrs, STDOUT_FILENO) would produce similar output to the following: */ strings = backtrace_symbols(buffer, nptrs); if (strings == NULL) { perror("backtrace_symbols"); exit(EXIT_FAILURE); } for (j = 0; j < nptrs; j++) printf("%s\n", strings[j]); free(strings); } static void /* "static" means don't export the symbol... */ myfunc2(void) { myfunc3(); } void myfunc(int ncalls) { if (ncalls > 1) myfunc(ncalls - 1); else myfunc2(); } int main(int argc, char *argv[]) { if (argc != 2) { fprintf(stderr, "%s num-calls\n", argv[0]); exit(EXIT_FAILURE); } myfunc(atoi(argv[1])); exit(EXIT_SUCCESS); }
注意編譯的時候要加上-rdynamic選項,eclipse在工程選項裡設定如下:
執行結果如下:
backtrace() returned 8 addresses
./prog(myfunc3+0x5c) [0x80487f0]
./prog [0x8048871]
./prog(myfunc+0x21) [0x8048894]
./prog(myfunc+0x1a) [0x804888d]
./prog(myfunc+0x1a) [0x804888d]
./prog(main+0x65) [0x80488fb]
/lib/libc.so.6(__libc_start_main+0xdc) [0xb7e38f9c]
./prog [0x8048711]
對於static函式,可以通過addr2line來獲取對應的符號資訊,對應以上的例子程式proc,myfunc2是static函式,backtrace取到的地址是0x8048871,使用addr2line的轉換如下:
addr2line -e proc 0x8048871 –f
最後的結果應該是函式名、檔名和行號:
myfun2
./prog.c:37