2011-11-26 2 views
3

프로그램에서 예외 정보를 추적하는 동안 문제가 발생했습니다. C++에서 백 트레이스 행 번호 오류를 수정하는 방법

나는 아래의 기능 사용 :

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

내가 맥 OSX 10.7에서 GCC/LLVM-GCC를 사용, 감사합니다 :) –

+0

귀하의'callstack' 변수가 그래서 이것은 64 비트 시스템에서 작동하지 않습니다 잘못된 유형입니다. 'int' 대신'void *'를 사용하십시오. – 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; 
} 
관련 문제