1. 程式人生 > >函式呼叫堆疊追蹤(1)

函式呼叫堆疊追蹤(1)

對於一個比較大的工程,函式的呼叫關係錯綜複雜,這時候僅僅靠IDE靜態檢視函式的呼叫關係已經遠遠不夠,所以為了更好地理解程式碼,還是有必要去掌握一些方法和工具來跟蹤函式的呼叫堆疊。

一、設斷點

eclipse裡可以設定斷點檢視函式的堆疊呼叫情況,這裡比較方便的是可以直接定位到上級函式檢視變數的值

clip_image002[4]

二、使用backtrace

backtrace功能可以跟蹤函式的呼叫堆疊,這個需要glibc庫的支援,所以一般情況下是在linux平臺使用。當然使用msys2cygwin等工具在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在工程選項裡設定如下:

clip_image004[4]

執行結果如下:

           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來獲取對應的符號資訊,對應以上的例子程式procmyfunc2static函式,backtrace取到的地址是0x8048871,使用addr2line的轉換如下:

addr2line -e proc 0x8048871 –f

最後的結果應該是函式名、檔名和行號:

myfun2
./prog.c:37