嵌入式arm开发C语言调用栈回溯实战

嵌入式arm开发C语言调用栈回溯实战

参考: https://stackoverflow.com/questions/77005/how-to-automatically-generate-a-stacktrace-when-my-program-crashes

  • 代码

    #define _GNU_SOURCE
    #endif
    #ifndef __USE_GNU
    #define __USE_GNU
    #endif
    
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    /* This structure mirrors the one found in /usr/include/asm/ucontext.h (arm: arm-linux-gnueabihf/libc/usr/include/asm-generic/ucontext.h) */
    typedef struct _sig_ucontext {
           
      unsigned long     uc_flags;
      struct ucontext   *uc_link;
      stack_t           uc_stack;
      struct sigcontext uc_mcontext;
      sigset_t          uc_sigmask;
    } sig_ucontext_t;
    
    void crit_err_hdlr(int sig_num, siginfo_t * info, void * ucontext)
    {
           
      void *             array[50];
      void *             caller_address;
      char **            messages;
      int                size, i;
      sig_ucontext_t *   uc;
    
      uc = (sig_ucontext_t *)ucontext;
    
      /* Get the address at the time the signal was raised */
    #if defined(__i386__) // gcc specific
      caller_address = (void *) uc->uc_mcontext.eip; // EIP: x86 specific
    #elif defined(__x86_64__) // gcc specific
      caller_address = (void *) uc->uc_mcontext.rip; // RIP: x86_64 specific
    #else
      //#error Unsupported architecture. // TODO: Add support for other arch.
      caller_address = (void *) uc->uc_mcontext.arm_pc; // ARM 
    #endif
    
      fprintf(stderr, "signal %d (%s), address is %p from %p\n", 
        sig_num, strsignal(sig_num), info->si_addr, 
        (void *)caller_address);
    
      size = backtrace(array, 50);
    
      /* overwrite sigaction with caller's address */
      array[1] = caller_address;
    
      messages = backtrace_symbols(array, size);
    
      /* skip first stack frame (points here) */
      for (i = 1; i < size && messages != NULL; ++i)
      {
           
        fprintf(stderr, "[bt]: (%d) %s\n", i, messages[i]);
      }
    
      free(messages);
    
      exit(EXIT_FAILURE);
    }
    
    int crash()
    {
           
      char * p = NULL;
      *p = 0;
      return 0;
    }
    
    int foo4()
    {
           
      crash();
      return 0;
    }
    
    int foo3()
    {
           
      foo4();
      return 0;
    }
    
    int foo2()
    {
           
      foo3();
      return 0;
    }
    
    int foo1()
    {
           
      foo2();
      return 0;
    }
    
    int main(int argc, char ** argv)
    {
           
      struct sigaction sigact;
    
      sigact.sa_sigaction = crit_err_hdlr;
      sigact.sa_flags = SA_RESTART | SA_SIGINFO;
    
      if (sigaction(SIGSEGV, &sigact, (struct sigaction *)NULL) != 0)
      {
           
        fprintf(stderr, "error setting signal handler for %d (%s)\n", SIGSEGV, strsignal(SIGSEGV));
    
        exit(EXIT_FAILURE);
      }
    
      foo1();
    
      exit(EXIT_SUCCESS);
    }
    
  • 编译

    arm-linux-gnueabihf-gcc -o test -rdynamic -mapcs-frame -funwind-tables -ffunction-sections backtrace.c
    
  • 运行

    signal 11 (Segmentation fault), address is (nil) from 0x10a32
    [bt]: (1) ./Test(crash+0xd) [0x10a32]
    [bt]: (2) ./Test(crash+0xd) [0x10a32]
    [bt]: (3) ./Test(foo4+0x7) [0x10a4c]
    [bt]: (4) ./Test(foo3+0x7) [0x10a5c]
    [bt]: (5) ./Test(foo2+0x7) [0x10a6c]
    [bt]: (6) ./Test(foo1+0x7) [0x10a7c]
    [bt]: (7) ./Test(main+0x5d) [0x10ae2]
    [bt]: (8) /lib/libc.so.6(__libc_start_main+0x9b) [0xb6ec9334]
    
  • 回溯

    arm-linux-gnueabihf-addr2line -e ./test -C -f 0x10a4c
    arm-linux-gnueabihf-objdump -s -d ./test --start-address=0x10a00 --stop-address=0x10a80
    
  • 更多参考

  • 自己动手实现arm函数栈帧回溯【转】

    • 用的uclib版本较低,没有这些函数,但又需要,只能自己实现了(较高的版本应该有这些函数,换版本很麻烦),而且可以加深自己对这方面的理解
  • 利用backtrace和ucontex定位segment错误

    • 上面通过backtrace可以大体得到”segmentfault”错误时的函数调用栈,然而仅凭backtrace还是不能得到引起异常的指令地址(甚至连引起异常的函数也无法得到)。在Redis的源码中,看到了打印指令地址的方法。使用ucontext_t结构,打印出指令寄存器的内容。

你可能感兴趣的:(C&C++,Linux,知识&技巧,嵌入式,arm,c语言)