1 2 3 4 5 6 7 8 9 10 | wuzesheng@ubuntu:~/work/test$ gcc test.cc -o test1 wuzesheng@ubuntu:~/work/test$ ./test1 stackstrace 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] |
從上面的運(yùn)行結(jié)果中,我們的確看到了函數(shù)的調(diào)用棧,但是都是16進(jìn)制的地址,會有點(diǎn)小小的不爽。當(dāng)然我們可以通過反匯編得到每個地址對應(yīng)的函數(shù),但這個還是有點(diǎn)麻煩了。不急,且聽我慢慢道來,看第2步。
1 2 3 4 5 6 7 8 9 10 | wuzesheng@ubuntu:~/work/test$ gcc test.cc -rdynamic -o test2 wuzesheng@ubuntu:~/work/test$ ./test2 stackstrace 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] |
這下終于可以看到函數(shù)的名字了,對比一下2和1的編譯過程,2比1多了一個-rdynamic的選項(xiàng),讓我們來看看這個選項(xiàng)是干什么的(來自gcc mannual的說明):
1 2 | -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. |
從上面的說明可以看出,它的主要作用是讓鏈接器把所有的符號都加入到動態(tài)符號表中,這下明白了吧。不過這里還有一個問題,這里的函數(shù)名 都是mangle過的,需要demangle才能看到原始的函數(shù)。關(guān)于c++的mangle/demangle機(jī)制,不了解的朋友可以在搜索引擎上搜一 下,我這里就不多就介紹了。這里介紹如何用命令來demangle,通過c++filt命令便可以:
1 2 | wuzesheng@ubuntu:~/work/test$ c++filt < << "_Z16print_stacktracev" print_stacktrace() |
寫到這里,大部分工作就ok了。不過不知道大家有沒有想過這樣一個問題,同一個函數(shù)可以在代碼中多個地方調(diào)用,如果我們只是知道函數(shù), 而不知道在哪里調(diào)用的,有時候還是不夠方便,bingo,這個也是有辦法的,可以通過address2line命令來完成,我們用第2步中編譯出來的 test2來做實(shí)驗(yàn)(address2line的-f選項(xiàng)可以打出函數(shù)名, -C選項(xiàng)也可以demangle):
1 2 3 4 | wuzesheng@ubuntu:~/work/test$ addr2line -a 0x4008a7 -e test2 -f 0x00000000004008a7 _Z4fun1v ??:0 |
Oh no,怎么打出來的位置信息是亂碼呢?不急,且看我們的第3步。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | wuzesheng@ubuntu:~/work/test$ gcc test.cc -rdynamic -g -o test3 wuzesheng@ubuntu:~/work/test$ ./test3 stackstrace 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] wuzesheng@ubuntu:~/work/test$ addr2line -a 0x4008a7 -e test3 -f -C 0x00000000004008a7 fun1() /home/wuzesheng/work/test/test.cc:20 |
看上面的結(jié)果,我們不僅得到了調(diào)用棧,而且可以得到每個函數(shù)的名字,以及被調(diào)用的位置,大功告成。在這里需要說明一下的是,第3步比第
2步多了一個-g選項(xiàng),-g選項(xiàng)的主要作用是生成調(diào)試信息,位置信息就屬于調(diào)試信息的范疇,經(jīng)常用gdb的朋友相信不會對這個選項(xiàng)感到陌生。