C/C++列印堆棧資訊

來源:互聯網
上載者:User

標籤:lib   讀者   說明   option   沒有   完成   ram   apple   logs   

 前幾天幫同事跟蹤的一個程式莫名退出,沒有core dump(當然ulimit是開啟的)的問題。我們知道,正常情況下,如果程式因為某種異常條件退出的話,應該會產生core dump,而如果程式正常退出的話,應該是直接或者間接的調用了exit()相關的函數。基於這個事實,我想到了這樣一個辦法,在程式開始時,通過系統提供的atexit(),向系統註冊一個回呼函數,在程式調用exit()退出的時候,這個回呼函數就會被調用,然後我們在回呼函數中列印出當前的函數調用棧,由此便可以知道exit()是在哪裡調用,從而上述問題便迎刃而解了。上述方法用來解決類似問題是非常行之有效。在上面,我提到了在“回呼函數中列印出當前的函數調用棧”,相信細心的朋友應該注意到這個了,本文的主要內容就是詳細介紹,如何在程式中列印中當前的函數調用棧。
      我之前寫過一篇題目為《介紹幾個關於C/C++程式調試的函數》的文章,看到這裡,請讀者朋友先看一下前面這篇,因為本文是以前面這篇文章為基礎的。我正是用了backtrace()和backtrace_symbols()這兩個函數實現的,下面是一個簡單的例子,通過這個例子我們來介紹具體的方法:

12345678910111213141516171819202122232425262728293031323334353637383940414243
#include <execinfo .h>#include <stdio .h>#include <stdlib .h> void fun1();void fun2();void fun3(); void print_stacktrace(); int main(){    fun3();} void fun1(){    printf("stackstrace begin:\n");    print_stacktrace();} void fun2(){    fun1();} void fun3(){    fun2();} void print_stacktrace(){    int size = 16;    void * array[16];    int stack_num = backtrace(array, size);    char ** stacktrace = backtrace_symbols(array, stack_num);    for (int i = 0; i < stack_num; ++i)    {        printf("%s\n", stacktrace[i]);    }    free(stacktrace);}

(說明:下面的介紹採用的環境是ubuntu 11.04, x86_64, gcc-4.5.2)

  • 1. 通過下面的方式編譯運行:
12345678910
[email protected]:~/work/test$ gcc test.cc -o test1[email protected]:~/work/test$ ./test1stackstrace begin:./test1() [0x400645]./test1() [0x400607]./test1() [0x400612]./test1() [0x40061d]./test1() [0x4005ed]/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xff) [0x7f5c59a91eff]./test1() [0x400529]

      從上面的運行結果中,我們的確看到了函數的調用棧,但是都是16進位的地址,會有點小小的不爽。當然我們可以通過反組譯碼得到每個地址對應的函數,但這個還是有點麻煩了。不急,且聽我慢慢道來,看第2步。

  • 2. 通過下面的方式編譯運行:
12345678910
[email protected]:~/work/test$ gcc test.cc -rdynamic -o test2[email protected]:~/work/test$ ./test2stackstrace begin:./test2(_Z16print_stacktracev+0x26) [0x4008e5]./test2(_Z4fun1v+0x13) [0x4008a7]./test2(_Z4fun2v+0x9) [0x4008b2]./test2(_Z4fun3v+0x9) [0x4008bd]./test2(main+0x9) [0x40088d]/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xff) [0x7f9370186eff]./test2() [0x4007c9]

      這下終於可以看到函數的名字了,對比一下2和1的編譯過程,2比1多了一個-rdynamic的選項,讓我們來看看這個選項是幹什麼的(來自gcc mannual的說明):

12
-rdynamic           Pass the flag -export-dynamic to the ELF linker, on targets that support it. This instructs the linker to add all symbols, not only used ones, to the dynamic symbol table. This option is needed for some uses of "dlopen" or to allow obtaining backtraces from within a program.

      從上面的說明可以看出,它的主要作用是讓連結器把所有的符號都加入到動態符號表中,這下明白了吧。不過這裡還有一個問題,這裡的函數名都是mangle過的,需要demangle才能看到原始的函數。關於c++的mangle/demangle機制,不瞭解的朋友可以在搜尋引擎上搜一下,我這裡就不多就介紹了。這裡介紹如何用命令來demangle,通過c++filt命令便可以:

12
[email protected]:~/work/test$ c++filt < << "_Z16print_stacktracev"print_stacktrace()

      寫到這裡,大部分工作就ok了。不過不知道大家有沒有想過這樣一個問題,同一個函數可以在代碼中多個地方調用,如果我們只是知道函數,而不知道在哪裡調用的,有時候還是不夠方便,bingo,這個也是有辦法的,可以通過address2line命令來完成,我們用第2步中編譯出來的test2來做實驗(address2line的-f選項可以打出函數名, -C選項也可以demangle):

1234
[email protected]:~/work/test$ addr2line -a 0x4008a7 -e test2 -f0x00000000004008a7_Z4fun1v??:0

      Oh no,怎麼打出來的位置資訊是亂碼呢?不急,且看我們的第3步。

  • 3. 通過下面的方式編譯運行:
1234567891011121314
[email protected]:~/work/test$ gcc test.cc -rdynamic -g -o test3[email protected]:~/work/test$ ./test3stackstrace begin:./test3(_Z16print_stacktracev+0x26) [0x4008e5]./test3(_Z4fun1v+0x13) [0x4008a7]./test3(_Z4fun2v+0x9) [0x4008b2]./test3(_Z4fun3v+0x9) [0x4008bd]./test3(main+0x9) [0x40088d]/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xff) [0x7fa9558c1eff]./test3() [0x4007c9][email protected]:~/work/test$ addr2line -a 0x4008a7 -e test3 -f -C0x00000000004008a7fun1()/home/wuzesheng/work/test/test.cc:20

      看上面的結果,我們不僅得到了調用棧,而且可以得到每個函數的名字,以及被調用的位置,大功告成。在這裡需要說明一下的是,第3步比第2步多了一個-g選項,-g選項的主要作用是產生調試資訊,位置資訊就屬於調試資訊的範疇,經常用gdb的朋友相信不會對這個選項感到陌生。

好文要頂 關注我 收藏該文  

C/C++列印堆棧資訊

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.