2011-11-26 55 views
3

我想了解一些關於程序捕獲異常的信息時遇到了問題。如何修復C++中的backtrace行號錯誤

我用下面的功能:

extern "C" void log_backtrace() 
{ 
    // Dump the callstack 
    int callstack[128]; 
    int frames = backtrace((void**) callstack, 128); 
    char** strs = backtrace_symbols((void**) callstack, frames); 

    for (int i = 1; i < frames; ++i) 
    { 
     char functionSymbol[64*1024]; 
     char moduleName [64*1024]; 
     int offset  = 0; 
     sscanf(strs[i], "%*d %s %*s %s %*s %d", &moduleName, &functionSymbol, &offset); 
     int addr = callstack[i]; 
     int validCppName; 
     char* functionName = abi::__cxa_demangle(functionSymbol, NULL, 0, 
              &validCppName); 
     if (validCppName == 0) 
      printf( "\t%8.8x — %s + %d\t\t(%s)\n", addr, functionName, offset, moduleName); 
     else 
      printf( "\t%8.8x — %s + %d\t\t(%s)\n", addr, functionSymbol, offset, moduleName); 
     if (functionName) 
      free(functionName); 
    } 
    free(strs); 
} 

和輸出是這樣的:

20:48:44 [ERROR]tcp_client::connect() failed. error:Connection refused 
00000001 — latte::Log::out_error(std::string const&) + 151  (valhalla) 
001a6637 — latte::tcp_client::connect(boost::asio::ip::basic_endpoint<boost::asio::ip::tcp> const&) + 307  (valhalla) 
00000001 — valhalla::hall::start() + 388  (valhalla) 
00204803 — main + 388  (valhalla) 
00000001 — start + 52  (valhalla) 
00143ae4 — 0x0 + 1  (???) 

所有的信息(命名空間,類名和方法名)都不錯。但唯一的問題是行號錯誤。

如何修復回溯行號?

+0

我用GCC/LLVM-GCC中的Mac OSX 10.7,謝謝:) –

+0

你'callstack'變量是錯誤的類型,因此這將不會在64位系統上運行。使用'void *'而不是'int'。 – SoapBox

+0

非常感謝你:) –

回答

11

它們不是行號,而是函數開始的偏移量。有一個名爲addr2line的工具,它附帶binutils,可以將地址轉換爲給定調試符號的行號。你可以在程序中調用它(pipe() + fork() + exec()),或者查看它用來做這件事的庫。

在我的Linux系統上,addr2line在內部使用libbfd。儘管從我看過的內容來看,它的記錄並不是很好,但通過addr2line源代碼可以很容易地看到它。

+0

謝謝,我會試試:) –

1

我有這個相同的問題,並尋找解決方案,但我沒有找到任何東西。你已經在你的帖子中標記了OSX,所以我認爲這是你希望這個工作的平臺?那麼OSX沒有addr2line,至少它不在OSX版本10.8/10.9中。我放在一起下面的代碼(使用來自不同來源的片段),它產生回溯看起來像這樣的(它包括頂部的兩幀這是異常處理程序,如果你願意,你可以跳過此):

Caught SIGBUG: Bus error (bad memory access) 
0 MyGame          0x342878 ExceptionHandler::PrintStackTrace() (in MyGame) (MacOSXEngine.cpp:89) 
1 MyGame          0x342c4e ExceptionHandler::Handler(int, __siginfo*, void*) (in MyGame) (MacOSXEngine.cpp:232) 
2 libsystem_platform.dylib     0x92beedeb 0x92beedeb 
3 ???          0xffffffff 0 + 4294967295 
4 MyGame          0x48ae93 GlfwGraphicsSystem::Initialise(PiEngine::EmulationMode::T, PiGraphics::Orientation::T) (in MyGame) (GlfwGraphicsSystem.cpp:29) 
5 MyGame          0x343f1f MacOSXEngine::Initialise() (in MyGame) (MacOSXEngine.cpp:581) 
6 MyGame          0x342f8f main (in MyGame) (MacOSXEngine.cpp:304) 
7 MyGame          0x3445 start (in MyGame) + 53 

它只爲執行模塊中的幀生成函數+文件+行號。該代碼僅適用於OSX,但可以適用於其他平臺。將會出現沒有涉及的角落案件,但希望這是一個很好的起點。該代碼是:

namespace ExceptionHandler 
{ 
    char m_ExeFilename[ PATH_MAX ]; 

    // Execute cmd store stdout into buf (up to bufSize). 
    int Execute(const char * cmd, char * buf, size_t bufSize) 
    { 
     char filename[ 512 ]; 
     sprintf(filename, "%d.tmp", rand()); 

     if (FILE * file = fopen(filename, "w")) 
     { 
      if (FILE * ptr = popen(cmd, "r")) 
      { 
       while (fgets(buf, bufSize, ptr) != NULL) 
       { 
        fprintf(file, "%s", buf); 
       } 
       pclose(ptr); 
      } 
      fclose(file); 

      unlink(filename); 

      return 0; 
     } 

     return -1; 
    } 

    // Resolve symbol name and source location given the path to the executable and an address 
    int Addr2Line(char const * const program_name, void const * const addr, char * buff, size_t buffSize) 
    { 
     char addr2line_cmd[512] = {0}; 
     sprintf(addr2line_cmd, "atos -d -o %.256s %p", program_name, addr); 
     return Execute(addr2line_cmd, buff, buffSize); 
    } 

    // Check if file exists. 
    bool FileExists(const char * filename) 
    { 
     if (FILE * fh = fopen(filename, "r")) 
     { 
      fclose(fh); 
      return true; 
     } 

     return false; 
    } 

    // Print stack trace. 
    void PrintStackTrace() 
    { 
     int trace_size = 0; 
     char ** messages = (char **)NULL; 

     static const size_t kMaxStackFrames = 64; 
     static void * stack_traces[ kMaxStackFrames ]; 
     trace_size = backtrace(stack_traces, kMaxStackFrames); 
     messages = backtrace_symbols(stack_traces, trace_size); 

     for (int i = 0; i < trace_size; ++i) 
     { 
      int stackLevel; 
      char filename[ 512 ]; 
      uintptr_t address; 
      char symbol[ 512 ]; 
      uintptr_t symbolOffset; 
      uintptr_t functionOffset; 
      bool symbolOffsetValid = false; 
      bool somethingValid = true; 

      if (sscanf(messages[ i ], "%d%*[ \t]%s%*[ \t]%" SCNxPTR "%*[ \t]%" SCNxPTR "%*[ \t]+%*[ \t]%" SCNuPTR, &stackLevel, filename, &address, &symbolOffset, &functionOffset) == 5) 
      { 
       symbolOffsetValid = true; 
      } 
      else if (sscanf(messages[ i ], "%d%*[ \t]%s%*[ \t]%" SCNxPTR "%*[ \t]%s%*[ \t]+%*[ \t]%" SCNuPTR, &stackLevel, filename, &address, symbol, &functionOffset) == 5) 
      { 
      } 
      else 
      { 
       somethingValid = false; 
      } 

      const size_t BUFF_SIZE = 4096; 
      char buff[ BUFF_SIZE ] = { '\0' }; 

      if (somethingValid) 
      { 
       if (symbolOffsetValid && symbolOffset == 0) 
       { 
        fprintf(stderr, "%3d %-32s %#16" PRIxPTR " %#" PRIxPTR " + %" PRIuPTR "\n", stackLevel, filename, address, symbolOffset, functionOffset); 
       } 
       else if (FileExists(m_ExeFilename) && Addr2Line(m_ExeFilename, stack_traces[ i ], buff, BUFF_SIZE) == 0) 
       { 
        fprintf(stderr, "%3d %-32s %#16" PRIxPTR " %s", stackLevel, filename, address, buff); 
       } 
       else 
       { 
        fprintf(stderr, "%3d %-32s %#16" PRIxPTR " %#" PRIxPTR " + %" PRIuPTR "\n", stackLevel, filename, address, symbolOffset, functionOffset); 
       } 
      } 
      else 
      { 
       fprintf(stderr, "%s\n", messages[ i ]); 
      } 
     } 
     if (messages) 
     { 
      free(messages); 
     } 
    } 

    void Handler(int sig, siginfo_t * siginfo, void * context) 
    { 
     switch(sig) 
     { 
      case SIGSEGV: 
       fputs("Caught SIGSEGV: Segmentation Fault\n", stderr); 
       break; 

      case SIGBUS: 
       fputs("Caught SIGBUG: Bus error (bad memory access)\n", stderr); 
       break; 

      case SIGINT: 
       fputs("Caught SIGINT: Interactive attention signal, (usually ctrl+c)\n", stderr); 
       break; 

      case SIGFPE: 
       switch(siginfo->si_code) 
       { 
        case FPE_INTDIV: 
         fputs("Caught SIGFPE: (integer divide by zero)\n", stderr); 
         break; 
        case FPE_INTOVF: 
         fputs("Caught SIGFPE: (integer overflow)\n", stderr); 
         break; 
        case FPE_FLTDIV: 
         fputs("Caught SIGFPE: (floating-point divide by zero)\n", stderr); 
         break; 
        case FPE_FLTOVF: 
         fputs("Caught SIGFPE: (floating-point overflow)\n", stderr); 
         break; 
        case FPE_FLTUND: 
         fputs("Caught SIGFPE: (floating-point underflow)\n", stderr); 
         break; 
        case FPE_FLTRES: 
         fputs("Caught SIGFPE: (floating-point inexact result)\n", stderr); 
         break; 
        case FPE_FLTINV: 
         fputs("Caught SIGFPE: (floating-point invalid operation)\n", stderr); 
         break; 
        case FPE_FLTSUB: 
         fputs("Caught SIGFPE: (subscript out of range)\n", stderr); 
         break; 
        default: 
         fputs("Caught SIGFPE: Arithmetic Exception\n", stderr); 
         break; 
       } 
       break; 

      case SIGILL: 
       switch(siginfo->si_code) 
       { 
        case ILL_ILLOPC: 
         fputs("Caught SIGILL: (illegal opcode)\n", stderr); 
         break; 
        case ILL_ILLOPN: 
         fputs("Caught SIGILL: (illegal operand)\n", stderr); 
         break; 
        case ILL_ILLADR: 
         fputs("Caught SIGILL: (illegal addressing mode)\n", stderr); 
         break; 
        case ILL_ILLTRP: 
         fputs("Caught SIGILL: (illegal trap)\n", stderr); 
         break; 
        case ILL_PRVOPC: 
         fputs("Caught SIGILL: (privileged opcode)\n", stderr); 
         break; 
        case ILL_PRVREG: 
         fputs("Caught SIGILL: (privileged register)\n", stderr); 
         break; 
        case ILL_COPROC: 
         fputs("Caught SIGILL: (coprocessor error)\n", stderr); 
         break; 
        case ILL_BADSTK: 
         fputs("Caught SIGILL: (internal stack error)\n", stderr); 
         break; 
        default: 
         fputs("Caught SIGILL: Illegal Instruction\n", stderr); 
         break; 
       } 
       break; 

      case SIGTERM: 
       fputs("Caught SIGTERM: a termination request was sent to the program\n", stderr); 
       break; 
      case SIGABRT: 
       fputs("Caught SIGABRT: usually caused by an abort() or assert()\n", stderr); 
       break; 
      default: 
       break; 
     } 
     PrintStackTrace(); 
     fflush(stderr); 
     fflush(stdout); 

     _exit(1); 
    } 

    bool Initialise(const char * argv) 
    { 
     char path[ PATH_MAX ]; 
     uint32_t size = sizeof(path); 
     if (_NSGetExecutablePath(path, &size) == 0) 
     { 
      if (! realpath(path, m_ExeFilename)) 
      { 
       strcpy(m_ExeFilename, path); 
      } 
     } 
     else 
     { 
      strcpy(m_ExeFilename, argv ? argv : ""); 
     } 

     struct sigaction sig_action = {}; 
     sig_action.sa_sigaction = Handler; 
     sigemptyset(&sig_action.sa_mask); 
     sig_action.sa_flags = SA_SIGINFO; 

     int toCatch[ ] = { 
      SIGSEGV, 
      SIGBUS, 
      SIGFPE, 
      SIGINT, 
      SIGILL, 
      SIGTERM, 
      SIGABRT 
     }; 

     bool okay = true; 
     for (size_t toCatchIx = 0; toCatchIx < PiArraySize(toCatch); ++toCatchIx) 
     { 
      okay &= sigaction(toCatch[ toCatchIx ], &sig_action, NULL) == 0; 
     } 

     return okay; 
    } 
} 

int main(int argc, char ** argv) 
{ 
    argc = argc; 
    argv = argv; 

    ExceptionHandler::Initialise(argc > 0 ? argv[ 0 ] : NULL); 

    // Do something 

    return 0; 
}