2011-08-03 148 views
67

特定於Linux的backtrace()backtrace_symbols()允許您生成程序的調用跟蹤。但是,它只打印功能地址,而不是我的程序的名稱。我怎樣才能讓他們打印函數名稱呢?我試着用-g以及-ggdb來編譯程序。測試用例下面只是打印此:如何使backtrace()/ backtrace_symbols()打印函數名?

 

    BACKTRACE ------------ 
    ./a.out() [0x8048616] 
    ./a.out() [0x8048623] 
    /lib/libc.so.6(__libc_start_main+0xf3) [0x4a937413] 
    ./a.out() [0x8048421] 
    ---------------------- 

我願意第一個2項也顯示函數名,foomain

代碼:

#include <execinfo.h> 
#include <string.h> 
#include <errno.h> 
#include <unistd.h> 
#include <stdlib.h> 

static void full_write(int fd, const char *buf, size_t len) 
{ 
     while (len > 0) { 
       ssize_t ret = write(fd, buf, len); 

       if ((ret == -1) && (errno != EINTR)) 
         break; 

       buf += (size_t) ret; 
       len -= (size_t) ret; 
     } 
} 

void print_backtrace(void) 
{ 
     static const char start[] = "BACKTRACE ------------\n"; 
     static const char end[] = "----------------------\n"; 

     void *bt[1024]; 
     int bt_size; 
     char **bt_syms; 
     int i; 

     bt_size = backtrace(bt, 1024); 
     bt_syms = backtrace_symbols(bt, bt_size); 
     full_write(STDERR_FILENO, start, strlen(start)); 
     for (i = 1; i < bt_size; i++) { 
       size_t len = strlen(bt_syms[i]); 
       full_write(STDERR_FILENO, bt_syms[i], len); 
       full_write(STDERR_FILENO, "\n", 1); 
     } 
     full_write(STDERR_FILENO, end, strlen(end)); 
    free(bt_syms); 
} 
void foo() 
{ 
    print_backtrace(); 
} 

int main() 
{ 
    foo(); 
    return 0; 
} 
+0

的可能重複[如何獲得更詳細的回溯(http://stackoverflow.com/questions/5945775/how-to-get-more-detailed-backtrace) – Nemo

+0

http://stackoverflow.com/questions/105659/how-can-one-grab-a-stack-trace-in-c –

回答

46

採取的符號從動態符號表中;您需要將-rdynamic選項設置爲gcc,這會使其將標記傳遞給鏈接器,以確保將全部符號放置在表中。

(見Link Options頁的GCC manual的,和/或Backtraces頁面的glibc manual的。)

+8

雖然這不適用於靜態符號。 @Nemo提到的'libunwind',適用於靜態函數。 –

25

使用addr2line command映射到源代碼文件名+行數的可執行地址。給出-f選項以獲取函數名稱。

或者,嘗試libunwind

+3

addr2行很好,因爲它包含輸出中的文件名和行號,但只要加載程序執行重定位就會失敗。 –

+0

...和ASLR重新定位比以往更常見。 –

+0

@ErwanLegrand:在ASLR之前,我認爲重定位是可預測的,即使對於共享對象(?)中的地址,addr2line也能可靠地工作。但是,在現代平臺上,您需要知道可重定位對象的實際加載地址,原則上這個操作。 – Nemo

9

伊恩蘭斯泰勒出色的Libbacktrace解決了這個問題。它處理堆棧展開並支持普通的ELF符號和DWARF調試符號。

Libbacktrace不需要導出所有符號,這將是醜陋的,而ASLR不會破壞它。

Libbacktrace最初是GCC發行版的一部分。現在,一個獨立的版本可以在Github上找到:

https://github.com/ianlancetaylor/libbacktrace

1

頂部的答案有一個錯誤 在ret == -1,並將errno是EINTER請您再次嘗試,但由於複製不算RET (不會讓一個帳戶只爲這一點,如果你不喜歡它堅韌)

static void full_write(int fd, const char *buf, size_t len) 
{ 
     while (len > 0) { 
       ssize_t ret = write(fd, buf, len); 

       if ((ret == -1) { 
         if (errno != EINTR)) 
           break; 
         //else 
         continue; 
       } 
       buf += (size_t) ret; 
       len -= (size_t) ret; 
     } 
}