代码之家  ›  专栏  ›  技术社区  ›  J.Panek

addr2line返回??:0即使使用-g3编译,gdb backtrace也可以工作

  •  0
  • J.Panek  · 技术社区  · 8 年前

    对于一个更大的项目,我启动了一个小项目来编程一个模块,这个模块在stderr上打印堆栈跟踪。我可以捕捉异常(信号)并看到堆栈,但是,当我使用addr2line时,我的函数只是打印??:0。我读到你需要用-g编译你的程序来获得调试信息。目前,我总是使用-g3编译,所以应该包括所有调试信息(对吧?)。由于gdb也需要调试信息,我测试了我的小程序,我可以正确地回溯我的信号。函数名,行号,一切正常。我也知道不同的include不会有输出,因为它们不是用-g标志编译的。

    这是我的代码:

    导致灾难和异常处理的源代码(set_signal_handler):

    #include <stdio.h>
    #include <signal.h>
    #include <assert.h>
    #include <stdbool.h>
    #include "Backtrace.h"
    
    int
    divide_by_zero();
    void
    cause_segfault();
    void
    stack_overflow();
    void
    infinite_loop();
    void
    illegal_instruction();
    void
    cause_calamity();
    
    
    int main(int argc, char * argv[])
    {
        (void) argc;
    
        set_signal_handler();
    
        cause_calamity();
    
        puts("OMG! Nothing bad happend!");
        return 0;
    }
    
    void cause_calamity()
    {
        /* uncomment one of the following error conditions to cause a calamity of
         your choosing! */
    
    //   (void)divide_by_zero();
         cause_segfault();
    //   assert(false);
    //   infinite_loop();
    //   illegal_instruction();
    //   stack_overflow();
    }
    
    int divide_by_zero()
    {
        int a = 1;
        int b = 0;
        return a / b;
    }
    
    void cause_segfault()
    {
        int * p = (int*) 0x12345678;
        *p = 0;
    }
    
    void
    stack_overflow();
    void stack_overflow()
    {
        int foo[1000]; //allocate something big on the stack
        (void) foo;
        stack_overflow();
    }
    
    /* break out with ctrl+c to test SIGINT handling */
    void infinite_loop()
    {
        while (1)
        {
        };
    }
    
    void illegal_instruction()
    {
        raise(SIGILL);
    }
    

    异常处理和打印堆栈跟踪的源代码:

    #include <stdio.h>
    #include <signal.h>
    #include <stdlib.h>
    #include <err.h>
    #include <inttypes.h>
    #include <execinfo.h>
    #include "backtrace.h"
    
    #define MAX_STACK_FRAMES 64
    
    static void *stack_traces[MAX_STACK_FRAMES];
    
    
    int addr2line(void const * const addr)
    {
      char addr2line_cmd[512] = {0};
    
      /* have addr2line map the address to the relent line in the code */
        sprintf(addr2line_cmd,"addr2line -f -e StackTrace %p", addr);
    
      /* This will print a nicely formatted string specifying the
         function and source line of the address */
      return system(addr2line_cmd);
    }
    
    void posix_print_stack_trace()
    {
      int i, trace_size = 0;
      char **messages = (char **)NULL;
    
      trace_size = backtrace(stack_traces, MAX_STACK_FRAMES);
      messages = backtrace_symbols(stack_traces, trace_size);
    
      /* skip the first couple stack frames (as they are this function and
         our handler) and also skip the last frame as it's (always?) junk. */
      // for (i = 3; i < (trace_size - 1); ++i)
    
      for (i = 0; i < trace_size; ++i) //just for testing
      {
        if (addr2line(stack_traces[i]) != 0)
        {
            printf("  error determining line # for: %s\n", messages[i]);
        }
      }
      if (messages) { free(messages); }
    }
    
    void posix_signal_handler(int sig, siginfo_t *siginfo, void *context)
    {
      (void)context;
      switch(sig)
      {
        case SIGSEGV:
          fputs("Caught SIGSEGV: Segmentation Fault\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;
      }
       posix_print_stack_trace();
      _Exit(1);
    }
    
    static uint8_t alternate_stack[SIGSTKSZ];
    void set_signal_handler()
    {
      /* setup alternate stack */
      {
        stack_t ss = {};
        /* malloc is usually used here, I'm not 100% sure my static allocation
           is valid but it seems to work just fine. */
        ss.ss_sp = (void*)alternate_stack;
        ss.ss_size = SIGSTKSZ;
        ss.ss_flags = 0;
    
        if (sigaltstack(&ss, NULL) != 0) { err(1, "sigaltstack"); }
      }
    
      /* register our signal handlers */
      {
        struct sigaction sig_action = {};
        sig_action.sa_sigaction = posix_signal_handler;
        sigemptyset(&sig_action.sa_mask);
    
            sig_action.sa_flags = SA_SIGINFO | SA_ONSTACK;
    
        if (sigaction(SIGSEGV, &sig_action, NULL) != 0) { err(1, "sigaction"); }
        if (sigaction(SIGFPE,  &sig_action, NULL) != 0) { err(1, "sigaction"); }
        if (sigaction(SIGINT,  &sig_action, NULL) != 0) { err(1, "sigaction"); }
        if (sigaction(SIGILL,  &sig_action, NULL) != 0) { err(1, "sigaction"); }
        if (sigaction(SIGTERM, &sig_action, NULL) != 0) { err(1, "sigaction"); }
        if (sigaction(SIGABRT, &sig_action, NULL) != 0) { err(1, "sigaction"); }
      }
    }
    

    这是调用程序时控制台中的输出:

    Caught SIGSEGV: Segmentation Fault
    ??
    ??:0
    ??
    ??:0
    ??
    ??:0
    ??
    ??:0
    ??
    ??:0
    ??
    ??:0
    ??
    ??:0
    ??
    ??:0
    

    我在这个项目中使用Eclipse和自生成的makefile(hello world示例中的新c代码项目)。这个问题是否与eclipse和makefile有关?还是密码有问题?

    这是编译器输出:

    make all 
    Building file: ../src/Backtrace.c
    Invoking: GCC C Compiler
    gcc -O0 -g3 -Wall -c -fmessage-length=0 -MMD -MP -MF"src/Backtrace.d" -MT"src/Backtrace.o" -o "src/Backtrace.o" "../src/Backtrace.c"
    Finished building: ../src/Backtrace.c
    
    Building file: ../src/StackTrace.c
    Invoking: GCC C Compiler
    gcc -O0 -g3 -Wall -c -fmessage-length=0 -MMD -MP -MF"src/StackTrace.d" -MT"src/StackTrace.o" -o "src/StackTrace.o" "../src/StackTrace.c"
    Finished building: ../src/StackTrace.c
    
    Building target: StackTrace
    Invoking: GCC C Linker
    gcc  -o "StackTrace"  ./src/Backtrace.o ./src/StackTrace.o   
    Finished building target: StackTrace
    

    我正在使用Ubuntu 18.04。如有任何帮助,我们将不胜感激。

    1 回复  |  直到 8 年前
        1
  •  2
  •   user3629249    8 年前

    使用的地址不正确。

    例如,当消息[0]包含:

    0x5555557599c0 "/home/richard/Documents/forum/untitled(+0x1058) [0x555555555058]"
    

    然后将参数传递给 addr2line() 应该是:

    -a -f --exe=/home/richard/Documents/forum/untitled +0x1058
    

    那么输出将是:

    0x0000000000001058
    posix_signal_handler
    /home/richard/Documents/forum/untitled.c:209
    

    既然您有了正确的参数信息,就可以对参数进行一些试验。