Linux下利用backtrace追踪函数调用堆栈以及定位段错误
一般察看函數運行時堆棧的方法是使用GDB(bt命令)之類的外部調試器,但是,有些時候為了分析程序的BUG,(主要針對長時間運行程序的分析),在程序出錯時打印出函數的調用堆棧是非常有用的。
在glibc頭文件"execinfo.h"中聲明了三個函數用于獲取當前線程的函數調用堆棧。
int backtrace(void **buffer,int size)
該函數用于獲取當前線程的調用堆棧,獲取的信息將會被存放在buffer中,它是一個指針列表。參數 size 用來指定buffer中可以保存多少個void* 元素。函數返回值是實際獲取的指針個數,最大不超過size大小。
在buffer中的指針實際是從堆棧中獲取的返回地址,每一個堆棧框架有一個返回地址。
注意:某些編譯器的優化選項對獲取正確的調用堆棧有干擾,另外內聯函數沒有堆棧框架;刪除框架指針也會導致無法正確解析堆棧內容。
char ** backtrace_symbols (void *const *buffer, int size)
backtrace_symbols將從backtrace函數獲取的信息轉化為一個字符串數組. 參數buffer應該是從backtrace函數獲取的指針數組,size是該數組中的元素個數(backtrace的返回值)。
函數返回值是一個指向字符串數組的指針,它的大小同buffer相同.每個字符串包含了一個相對于buffer中對應元素的可打印信息.它包括函數名,函數的偏移地址,和實際的返回地址。
現在,只有使用ELF二進制格式的程序才能獲取函數名稱和偏移地址.在其他系統,只有16進制的返回地址能被獲取.另外,你可能需要傳遞相應的符號給鏈接器,以能支持函數名功能(比如,在使用GNU ld鏈接器的系統中,你需要傳遞(-rdynamic), -rdynamic可用來通知鏈接器將所有符號添加到動態符號表中,如果你的鏈接器支持-rdynamic的話,建議將其加上!)
該函數的返回值是通過malloc函數申請的空間,因此調用者必須使用free函數來釋放指針。
注意:如果不能為字符串獲取足夠的空間函數的返回值將會為NULL。void backtrace_symbols_fd (void *const *buffer, int size, int fd) backtrace_symbols_fd與backtrace_symbols 函數具有相同的功能,不同的是它不會給調用者返回字符串數組,而是將結果寫入文件描述符為fd的文件中,每個函數對應一行.它不需要調用malloc函數,因此適用于有可能調用該函數會失敗的情況。
?
下面是glibc中的實例(稍有修改):
#include <execinfo.h> #include <stdio.h> #include <stdlib.h>/* Obtain a backtrace and print it to @code{stdout}. */ void print_trace (void) {void *array[10];int size;char **strings;int i;size = backtrace(array, 10);strings = backtrace_symbols(array, size);if(NULL == strings){perror("backtrace_synbols");exit(EXIT_FAILURE);}printf ("Obtained %zd stack frames.\n", size);for (i = 0; i < size; i++)printf ("%s\n", strings[i]);free (strings);strings = NULL; }/* A dummy function to make the backtrace more interesting. */ void dummy_function (void) {print_trace (); }int main (int argc, char *argv[]) {dummy_function ();return 0; }
輸出如下:
[root@vm userAppInit]# gcc test.c [root@vm userAppInit]# ./a.out Obtained 5 stack frames. ./a.out [0x80484dd] ./a.out [0x804856d] ./a.out [0x8048585] /lib/libc.so.6(__libc_start_main+0xdc) [0x616e9c] ./a.out [0x8048411]
只能看到地址,現在修改編譯參數:
[root@vm userAppInit]# gcc -rdynamic test.c [root@vm userAppInit]# ./a.out Obtained 5 stack frames. ./a.out(print_trace+0x19) [0x80486ad] ./a.out(dummy_function+0xb) [0x804873d] ./a.out(main+0x16) [0x8048755] /lib/libc.so.6(__libc_start_main+0xdc) [0x616e9c] ./a.out [0x80485e1]
現在可以看到函數名了,但是沒有行號,不過沒有關系addr2line提供了這個功能。
在調用 Addr2line 工具時,要使用 -e 選項來指定可執行映像是 a.out。通過使用 -f 選項,可以告訴工具輸出函數名。
然后我們試圖用addr2line來看地址對應的函數和行號:
[root@vm userAppInit]# addr2line 0x80486ad -e ./a.out f ??:0 ??:0失敗了,然后我們再次修改編譯參數:
[root@vm userAppInit]# gcc -g -rdynamic test.c [root@vm userAppInit]# ./a.out Obtained 5 stack frames. ./a.out(print_trace+0x19) [0x80486ad] ./a.out(dummy_function+0xb) [0x804873d] ./a.out(main+0x16) [0x8048755] /lib/libc.so.6(__libc_start_main+0xdc) [0x616e9c] ./a.out [0x80485e1] [root@vm userAppInit]# addr2line 0x80486ad -e ./a.out -f print_trace /home/D8135/SW_COMM/lib/SW_BPS/Terminal/D8135/CPU/userAppInit/test.c:12
這次成功了,多加了-g參數。
使用這個地址和 Addr2line 工具,就可以判斷出函數名(print_trace)、源文件(/home/D8135/SW_COMM/lib/SW_BPS/Terminal/D8135/CPU/userAppInit/test.c)以及它在源文件中的行號(12)。
backtrace和backtrace_symbols的具體說明可以參考
http://www.kernel.org/doc/man-pages/online/pages/man3/backtrace.3.html
from:
http://www.linuxidc.com/Linux/2012-11/73470.htm
http://blog.csdn.net/wind19/article/details/6105617
轉載于:https://www.cnblogs.com/hehehaha/p/6332384.html
總結
以上是生活随笔為你收集整理的Linux下利用backtrace追踪函数调用堆栈以及定位段错误的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SQLServer Profiler 跟
- 下一篇: linux 其他常用命令