2011-08-04 55 views
10

我想在我的(C++)程序執行的某個位置獲得回溯。stacktrace和命名空間中的函數

因爲我正在使用backtrace和backtrace_symbols。沿着這條線:

std::string stacktrace(unsigned int frames_to_skip) 
{ 
    std::string str; 

    void* stack_addrs[50]; 
    int trace_size = backtrace(stack_addrs, 50); 
    char** stack_strings = backtrace_symbols(stack_addrs, trace_size); 

    str += "[bt] backtrace:\n"; 
    // skip frames_to_skip stack frames 
    for(int i = frames_to_skip; i < trace_size; ++i) 
    { 
     char tmp[4096]; 
     sprintf(tmp, "[bt] #%d %s\n", i-frames_to_skip, stack_strings[i]); 
     str += tmp; 
    } 

    free(stack_strings); 

    return str; 
} 

它的作品,但一些功能的名稱是缺少的。例如:

[bt] #0 /path/to/executable() [0x43e1b5] 
[bt] #1 /path/to/executable() [0x43e0cd] 
[bt] #2 /path/to/executable() [0x43df51] 
[bt] #3 /path/to/executable() [0x43dd44] 
[bt] #4 /path/to/executable() [0x43db50] 
[bt] #5 /path/to/executable() [0x43d847] 
[bt] #6 /path/to/executable() [0x43d216] 
[bt] #7 /path/to/executable() [0x43c1e1] 
[bt] #8 /path/to/executable() [0x43b293] 
[bt] #9 /path/to/executable(_Z29SomeRN5other8symbolE+0x2c) [0x43a6ca] 
[bt] #10 /path/to/executable(_Z11SomeIN5_8symbolEPFvRS1_EEvRKT_RKT0_+0x77) [0x441716] 
... 

功能0-8具有一個共同點:它們都在命名空間坐...
我試圖把功能9在匿名的命名空間(沒有任何其他修飾),並將其從所述在前看不見backtrace ...現在看起來像這樣:

[bt] #0 /path/to/executable() [0x43e1b5] 
[bt] #1 /path/to/executable() [0x43e0cd] 
[bt] #2 /path/to/executable() [0x43df51] 
[bt] #3 /path/to/executable() [0x43dd44] 
[bt] #4 /path/to/executable() [0x43db50] 
[bt] #5 /path/to/executable() [0x43d847] 
[bt] #6 /path/to/executable() [0x43d216] 
[bt] #7 /path/to/executable() [0x43c1e1] 
[bt] #8 /path/to/executable() [0x43b293] 
[bt] #9 /path/to/executable() [0x43a6ca] 
[bt] #10 /path/to/executable(_Z11SomeIN5_8symbolEPFvRS1_EEvRKT_RKT0_+0x77) [0x441716] 
... 

有沒有什麼辦法解決這個問題?

PS:版本的克++: 克++(GCC)4.6.0 20110530(紅帽4.6.0-9)固定回溯的最大深度代碼猴備註後

編輯
EDIT2添加的功能
EDIT3代碼編譯-O0 -g3與-rdynamic

回答

6

您的問題鏈接可能是你正在使用的功能的完整代碼。您的max_depthbacktrace(..)設置爲16。這可能太低。無論如何...

這篇博文在C++ stack traces with GCC解釋了你應該如何執行堆棧跟蹤。總之,

#include <execinfo.h> 
void print_trace(FILE *out, const char *file, int line) 
{ 
    const size_t max_depth = 100; 
    size_t stack_depth; 
    void *stack_addrs[max_depth]; 
    char **stack_strings; 

    stack_depth = backtrace(stack_addrs, max_depth); 
    stack_strings = backtrace_symbols(stack_addrs, stack_depth); 

    fprintf(out, "Call stack from %s:%d:\n", file, line); 

    for (size_t i = 1; i < stack_depth; i++) { 
     fprintf(out, " %s\n", stack_strings[i]); 
    } 
    free(stack_strings); // malloc()ed by backtrace_symbols 
    fflush(out); 
} 

GCC還提供了訪問C++名稱(DE)壓榨機。有一些 漂亮毛茸茸的詳細信息,以瞭解內存的所有權,和接口 與堆棧跟蹤輸出需要一些字符串解析,但它 歸結爲這種替換上述內環:

#include <cxxabi.h> 
... 
for (size_t i = 1; i < stack.depth; i++) { 
    size_t sz = 200; // just a guess, template names will go much wider 
    char *function = static_cast(malloc(sz)); 
    char *begin = 0, *end = 0; 
    // find the parentheses and address offset surrounding the mangled name 
    for (char *j = stack.strings[i]; *j; ++j) { 
     if (*j == '(') { 
      begin = j; 
     } 
     else if (*j == '+') { 
      end = j; 
     } 
    } 
    if (begin && end) { 
     *begin++ = ''; 
     *end = ''; 
     // found our mangled name, now in [begin, end) 

     int status; 
     char *ret = abi::__cxa_demangle(begin, function, &sz, &status); 
     if (ret) { 
      // return value may be a realloc() of the input 
      function = ret; 
     } 
     else { 
      // demangling failed, just pretend it's a C function with no args 
      std::strncpy(function, begin, sz); 
      std::strncat(function, "()", sz); 
      function[sz-1] = ''; 
     } 
     fprintf(out, " %s:%s\n", stack.strings[i], function); 
    } 
    else 
    { 
     // didn't find the mangled name, just print the whole line 
     fprintf(out, " %s\n", stack.strings[i]); 
    } 
    free(function); 
} 

該網站上有更多的信息(我不想逐字複製),但看着這段代碼,上面的網站應該讓你走上正軌。

+0

你好,謝謝你的回答。你對最大深度是正確的,回溯的結束丟失了,但這不是問題,因爲這些缺少的調用來自我正在使用的測試框架。儘管如此,問題依然存在,而且我的博客文章也是如此。然而,一個有趣的事實是,測試框架在命名空間中確實有函數,並且它們在堆棧跟蹤中是可見的,我的函數不是......我再次執行了測試(將函數放入命名空間中),並再次重複(函數在回溯中不再可見... – foke

1

backtrace列出了呼叫的幀,其對應於機器代碼指令call,不源電平的函數調用。

區別在於,對於內聯,優化編譯器通常可以避免在源代碼中爲每個邏輯函數調用使用call指令。

+0

在這種情況下,它們根本不會出現在bt中,無論如何這裏沒有優化,它是用'-O0 -g3'。另外,如果我在構造回溯的函數中斷開,並且在反彙編視圖(我正在使用eclipse)下去到一個沒有符號的地址下的地址,我會在一個'call'指令之後立即結束被正確顯示(又名顯示它將跳轉到的函數/方法的名稱)。 – foke