2011-05-10 99 views
18

我想在我的C++程序終止時打印回溯。功能打印回溯如下;如何獲得更詳細的回溯

void print_backtrace(void){ 

     void *tracePtrs[10]; 
     size_t count; 

     count = backtrace(tracePtrs, 10); 

     char** funcNames = backtrace_symbols(tracePtrs, count); 

     for (int i = 0; i < count; i++) 
      syslog(LOG_INFO,"%s\n", funcNames[i]); 

     free(funcNames); 

} 

它給出了一個輸出像;

desktop program: Received SIGSEGV signal, last error is : Success 
    desktop program: ./program() [0x422225] 
    desktop program: ./program() [0x422371] 
    desktop program: /lib/libc.so.6(+0x33af0) [0x7f0710f75af0] 
    desktop program: /lib/libc.so.6(+0x12a08e) [0x7f071106c08e] 
    desktop program: ./program() [0x428895] 
    desktop program: /lib/libc.so.6(__libc_start_main+0xfd) [0x7f0710f60c4d] 
    desktop program: ./program() [0x4082c9] 

有沒有辦法讓函數名和行更詳細的回溯,比如gdb輸出?

+0

您是否安裝了調試libc?如果您在命令行上將-g傳遞給GCC,IIRC Linux將爲此使用帶有調試符號的libc。 – 2011-05-10 05:58:56

+0

爲什麼不使用gdb,我可以問一下嗎?此外,GNU libc手冊的[Backtraces部分](http://www.gnu.org/s/hello/manual/libc/Backtraces.html)看起來很有用。 – 2011-05-10 06:53:55

回答

0

如果你想要一個非常詳細的回溯,你應該使用ptrace(2)跟蹤你想要的回溯過程。

你將能夠看到您的使用過程中的所有功能,但你需要一些基本的知識彙編

18

是 - 通過-rdynamic標誌鏈接。這將導致鏈接器在鏈接表中輸出代碼中所有非靜態函數的名稱,而不僅僅是導出的名稱。

您支付的價格是您的程序啓動時間稍長一點。對於小到中等程序你不會注意到它。你得到的是backtrace()能夠給你所有後面跟蹤中沒有靜態函數的名字。

但是當心 - :有你需要知道的幾個陷阱:

  1. backtrace_symbols通過malloc分配內存。如果由於malloc競技場腐敗(相當常見)而陷入SIGSEGV,那麼您會在這裏加倍錯誤,並且從未看到您的後退痕跡。

  2. 根據運行的平臺(例如x86),崩潰的確切函數的地址/函數名將被替換爲棧中的信號處理程序的返回地址。您需要從這些平臺的信號處理程序參數中獲取崩潰函數的正確EIP。

  3. syslog不是異步信號安全功能。發生碰撞時,它可能會在內部採取了鎖,如果鎖被採取了(因爲你在另一個呼叫日誌中部墜毀)如果您想了解所有的血淋淋的細節,你有一個死鎖

在OLS檢查出我的這段視頻給人講了:http://free-electrons.com/pub/video/2008/ols/ols2008-gilad-ben-yossef-fault-handlers.ogg

2
  1. 創建一條管道
  2. 叉()
  3. 使子進程中執行addr2line
  4. 在PA租過程中,從轉換回溯返回的地址()爲十六進制
  5. 寫的十六進制地址管道
  6. 讀回從addr2line和打印輸出/記錄它

既然你做這一切從信號處理程序中,請確保不要使用不是異步信號安全的功能。您可以看到異步信號安全的POSIX函數列表here

2

如果你的罰款通過Valgrind的運行時纔會得到適當的回溯,那麼這可能是一個選擇:

VALGRIND_PRINTF_BACKTRACE(格式,...):

它會給你回溯所有功能,包括靜態功能。

4

將地址輸入到addr2line,它會顯示文件名,行號和函數名稱。

1

更好的選擇,我發現是libbacktrace由伊恩·蘭斯·泰勒:

https://github.com/ianlancetaylor/libbacktrace

backtrace_symbols()不會只打印導出符號和它需要GNU庫不能少便攜。

addr2line不錯,因爲它包含文件名和行號。但是,只要加載器執行重定位,它就會失敗。現在ASLR很常見,它會經常失敗。

libunwind本身不允許打印文件名和行號。爲此,需要在ELF二進制文件內解析DWARF調試信息。不過,這可以使用libdwarf來完成。但是當libbacktrace爲你提供免費所需的一切時,爲什麼還要麻煩呢?

0

如果您不想採用「發出運行gdb的其他進程的信號」方法,我認爲gby提倡的方法,您還可以稍微更改代碼以在崩潰日誌文件中調用open()然後用open()返回的fd返回backtrace_symbols_fd() - 根據glibc手冊,這兩個函數都是異步信號安全的。當然,你仍然需要動態的。另外,從我所看到的情況來看,您仍然有時需要在backtrace *()函數無法解碼的某些地址上運行addr2line。

另請注意fork()不是異步信號安全:http://article.gmane.org/gmane.linux.man/1893/match=fork+async,至少在Linux上不是。正如有人已經指出的那樣,syslog()也不是。

相關問題