我們知道,GDB的backtrace命令可以查看堆棧信息。但很多時候,GDB根本用不上。比如說,在線上環境中可能沒有GDB,即使有,也不太可能讓我們直接在上面調試。如果能讓程序自己輸出調用棧,那是最好不過了。本文介紹和調用椎棧相關的幾個函數。
| NAME |
以上內容源自這幾個函數的man手冊。
先簡單介紹一下這幾個函數的功能:
l backtrace:獲取當前的調用棧信息,結果存儲在buffer中,返回值為棧的深度,參數size限制棧的最大深度,即最大取size步的棧信息。
l backtrace_symbols:把backtrace獲取的棧信息轉化為字符串,以字符指針數組的形式返回,參數size限定轉換的深度,一般用backtrace調用的返回值。
l backtrace_symbols_fd:它的功能和backtrace_symbols差不多,只不過它不把轉換結果返回給調用方,而是寫入fd指定的文件描述符。
Man手冊里,給出了一個簡單的實例,我們看一下:
| #include<execinfo.h> #include<stdio.h> #include<stdlib.h> #include<unistd.h> void myfunc3(void) { int j, nptrs; #define SIZE 100 void *buffer[100]; char **strings; nptrs = backtrace(buffer, 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); }
staticvoid /* "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); } |
編譯:
| # cc prog.c -o prog |
運行:
| # ./prog 0 |
這樣,是輸出了調用棧,不過只是以十六進制輸出函數地址而已,可讀性很差。仔細看下man手冊,原來很簡單,編譯時加上個參數:
重新編譯:
| # cc -rdynamic prog.c -o prog |
通過gcc手冊,我們可以也解下參數的說明:
| -rdynamic |
再執行:
| # ./prog 0 backtrace() returned 6 addresses |
這回,可以看到函數名了。是不是很酷呢?把它封裝到你的調試代碼中吧。
