2012-07-20 380 views
22

我試圖在C++程序的backtrace中找到確切的調用行。現在我使用這些線(從回溯的手冊頁),以獲得跟蹤:來自addr2line的錯誤行號

void *bt_buffer[1000]; 
    char **bt_strings; 
    int bt_nptrs = backtrace(bt_buffer, 1000); 
    bt_strings = backtrace_symbols(bt_buffer, bt_nptrs); 

在bt_strings我覺得形式的線

./prog() [0x402e42] 

現在我把地址(十六進制字符串)並將其提供給addr2line。這有時會導致顯然錯誤的行號。互聯網搜索使我這個post,其中表明

readelf -wl ./prog 

指示了線真的是,或者說有多少行的符號已經移動到當前行。

編輯:這發生在我編譯-g -O0,即明確沒有優化。編譯器是gcc 4.6.3是否有另一個我錯過的編譯器標誌?

我的問題是以下幾點:我需要自動執行此操作。我需要我的程序創建一個回溯(完成),提取文件(完成)和行號(失敗)。

我當然可以調用readelf並解析輸出,但這不太合適,因爲根據發生的事情,輸出在符號和符號之間有所不同。有時,一個符號的地址是在同一行約行中的下一行偏移量的信息...

綜上所述:

有一種優雅的方式來獲得的確切行號函數在運行時從程序內部追蹤回調?

編輯:示例代碼:

#define UNW_LOCAL_ONLY 
#include <libunwind.h> 
#include <execinfo.h> 
#include <iostream> 
#include <stdlib.h> 

void show_backtrace() 
{ 
    // get current address 
    void* p = __builtin_return_address(0); 
    std::cout << std::hex << p << std::endl; 

    // get callee addresses 
    p = __builtin_return_address(1); 
    std::cout << std::hex << p << std::endl; 

    p = __builtin_return_address(2); 
    std::cout << std::hex << p << std::endl; 
} 

void show_backtrace2() 
{ 
    void *array[10]; 
    size_t size; 
    char **strings; 
    int i; 

    size = backtrace (array, 10); 
    strings = backtrace_symbols ((void *const *)array, size); 

    for (i = 0; i < size; i++) 
    { 
     std::cout << strings[i] << std::endl; 
    } 

    free (strings); 
} 

void show_backtrace3 (void) 
{ 
    char name[256]; 
    unw_cursor_t cursor; unw_context_t uc; 
    unw_word_t ip, sp, offp; 

    unw_getcontext (&uc); 
    unw_init_local (&cursor, &uc); 

    while (unw_step(&cursor) > 0) 
    { 
     char file[256]; 
     int line = 0; 

     name[0] = '\0'; 
     unw_get_proc_name (&cursor, name, 256, &offp); 
     unw_get_reg (&cursor, UNW_REG_IP, &ip); 
     unw_get_reg (&cursor, UNW_REG_SP, &sp); 

     std::cout << std:: hex << name << " ip = " << (long) ip 
       << " , sp = " << (long) sp << std::endl; 
    } 
} 

void dummy_function2() 
{ 
    show_backtrace(); 
    show_backtrace2(); 
    show_backtrace3(); 
} 

void dummy_function1() 
{ 
    dummy_function2(); 
} // line 73 

int main(int argc, char **argv) 
{ 
    dummy_function1(); 
    return 0; 
} 

編譯和運行:

g++ test_unwind.cc -g -O0 -lunwind && ./a.out 

輸出:

0x400edb 
0x400ef0 
0x400f06 
./a.out() [0x400cfb] 
./a.out() [0x400ee0] 
./a.out() [0x400ef0] 
./a.out() [0x400f06] 
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xed) [0x7f2f044ae76d] 
./a.out() [0x400b79] 
_Z15dummy_function2v ip = 400ee5 , sp = 7fffdb564580 
_Z15dummy_function1v ip = 400ef0 , sp = 7fffdb564590 
main ip = 400f06 , sp = 7fffdb5645a0 
__libc_start_main ip = 7f2f044ae76d , sp = 7fffdb5645c0 
_start ip = 400b79 , sp = 7fffdb564680 

測試例如0x40ef0與addr2line產量

/path/to/code/test_unwind.cc:73 

這是正確的文件,但錯誤的行號。在實際應用中,行號可能會因許多行而不同,向前和向後。

編輯:-S編譯顯示相關的部分是:

.LCFI34: 
    .cfi_def_cfa_register 6 
    .loc 2 72 0 
    call _Z15dummy_function2v 
    .loc 2 73 0 
    popq %rbp 

什麼是addr2line顯示,並且都爲返回地址,如圖所示call之後的行。我想獲得「入口」行,即之前顯示的內容!

+0

編譯時沒有進行優化是否會出錯? – Flexo 2012-07-20 12:39:25

+0

是的,忘了提及......我將編輯這個文章 – steffen 2012-07-20 12:40:23

+1

嘗試使用'gdb'執行並使用'l * 0x

'。它是否給出了正確的地址,還是它也給出了相同的'addr2line'? – Shahbaz 2012-07-20 12:49:30

回答

3

你當然可以做!我知道一個使用libunwind的示例實現。看到這個博客帖子:http://blog.bigpixel.ro/2010/09/stack-unwinding-stack-trace-with-gcc/

歸結到一點,這段代碼(文章逐字複製):

void show_backtrace (void) 
{ 
    char name[256]; 
    unw_cursor_t cursor; unw_context_t uc; 
    unw_word_t ip, sp, offp; 

    unw_getcontext(&uc); 
    unw_init_local(&cursor, &uc); 

    while (unw_step(&cursor) > 0) 
    { 
     char file[256]; 
     int line = 0; 

     name[0] = '\0'; 
     unw_get_proc_name(&cursor, name, 256, &offp); 
     unw_get_reg(&cursor, UNW_REG_IP, &ip); 
     unw_get_reg(&cursor, UNW_REG_SP, &sp); 

     //printf ("%s ip = %lx, sp = %lx\n", name, (long) ip, (long) sp); 
     getFileAndLine((long)ip, file, 256, &line); 
     printf("%s in file %s line %d\n", name, file, line); 
    } 
} 
+0

我很高興嘗試一到我的辦公室! – steffen 2012-07-24 07:38:47

+2

我試了一下,發現在'getFileAndLine'中他們也使用'popen'調用'addr2line'。 libunwind很有趣(爲+1),但具有與backtrace類似的功能。結果是相同的偏移行號。 – steffen 2012-07-24 13:34:44

+1

在你的問題中你說你用'-g'編譯,也許你應該用'-ggdb'代替? – Bart 2012-07-24 15:51:40

0

你試過

__LINE__ 

這是一個預處理符號,但你可以將它編譯進去。

+0

我曾想過'__FILE__'和'__LINE__'爲好,但我想要得到的foo'()'從'FOO()'調用的函數的信息。我不能改變'foo()'的調用方式。 – steffen 2012-07-29 11:19:56