Linux程序异常退出打印调用堆栈

    1. 一.用途:
      主要用于程序异常退出时寻找错误原因
      二.功能:
      回溯堆栈,简单的说就是可以列出当前函数调用关系
      三.原理:
      1. 通过对当前堆栈的分析,找到其上层函数在栈中的帧地址,再分析上层函数的堆栈,再找再上层的帧地址……一直找到最顶层为止,帧地址指的是一块:在栈上存放局部变量,上层返回地址,及寄存器值的空间。
      2. 由于不同处理器堆栈方式不同,此功能的具体实现是编译器的内建函数__buildin_frame_address及__buildin_return_address中,它涉及工具glibc和gcc, 如果编译器不支持此函数,也可自己实现此函数,举例中有arm上的实现
      四.方法:
      在程序中加入backtrace及相关函数调用
      五.举例:
      1. 一般backtrace的实现
      i. 程序
      #include <signal.h>
      #include <stdio.h>
      #include <stdlib.h>
      #include <execinfo.h>
      #include <sys/types.h>
      #include <sys/stat.h>
      #include <fcntl.h>
      #include <string.h>
      #include <unistd.h>
      #define PRINT_DEBUG
      static void print_reason(int sig, siginfo_t * info, void *secret)
      {
      void *array[10];
      size_t size;
      #ifdef PRINT_DEBUG
      char **strings;
      size_t i;
      size = backtrace(array, 10);
      strings = backtrace_symbols(array, size);
      printf("Obtained %zd stack frames.\n", size);
      for (i = 0; i < size; i++)
      printf("%s\n", strings[i]);
      free(strings);
      #else
      int fd = open("err.log", O_CREAT | O_WRONLY);
      size = backtrace(array, 10);
      backtrace_symbols_fd(array, size, fd);
      close(fd);
      #endif
      exit(0);
      }
      void die()
      {
      char *test1;
      char *test2;
      char *test3;
      char *test4 = NULL;
      strcpy(test4, "ab");
      }
      void test1()
      {
      die();
      }
      int main(int argc, char **argv)
      {
      struct sigaction myAction;
      myAction.sa_sigaction = print_reason;
      sigemptyset(&myAction.sa_mask);
      myAction.sa_flags = SA_RESTART | SA_SIGINFO;
      sigaction(SIGSEGV, &myAction, NULL);
      sigaction(SIGUSR1, &myAction, NULL);
      sigaction(SIGFPE, &myAction, NULL);
      sigaction(SIGILL, &myAction, NULL);
      sigaction(SIGBUS, &myAction, NULL);
      sigaction(SIGABRT, &myAction, NULL);
      sigaction(SIGSYS, &myAction, NULL);
      test1();
      }
      ii. 编译参数
      gcc main.c -o test -g -rdynamic
      2. 根据不同的处理器自已实现backtrace
      i. arm的backtrace函数实现
      static int backtrace_xy(void **BUFFER, int SIZE)
      {
      volatile int n = 0;
      volatile int *p;
      volatile int *q;
      volatile int ebp1;
      volatile int eip1;
      volatile int i = 0;
      p = &n;
      ebp1 = p[4];
      eip1 = p[6];
      fprintf(stderr, "======================= backtrace_xy addr: 0x%0x, param1: 0x%0x, param2: 0x%0x\n",
      backtrace_xy, &BUFFER, &SIZE);
      fprintf(stderr, "n addr is 0x%0x\n", &n);
      fprintf(stderr, "p addr is 0x%0x\n", &p);
      for (i = 0; i &lt; SIZE; i++)
      {
      fprintf(stderr, "ebp1 is 0x%0x, eip1 is 0x%0x\n", ebp1, eip1);
      BUFFER[i] = (void *)eip1;
      p = (int*)ebp1;
      q = p - 5;
      eip1 = q[5];
      ebp1 = q[2];
      if (ebp1 == 0 || eip1 == 0)
      break;
      }
      fprintf(stderr, "total level: %d\n", i);
      return i;
      }
      六.举例2:
      /*main.c*/
      #include "sigsegv.h"
      #include &lt;string.h>
      int die() {
        char *err = NULL;
        strcpy(err, "gonner");
        return 0;
      }
      int main() {
        return die();
      }
      /*sigsegv.c*/
      #define _GNU_SOURCE
      #include <memory.h>
      #include <stdlib.h>
      #include <stdio.h>
      #include <signal.h>
      #include <ucontext.h>
      #include <dlfcn.h>
      #include <execinfo.h>
      #define NO_CPP_DEMANGLE
      #ifndef NO_CPP_DEMANGLE
      #include <cxxabi.h>
      #endif
      #if defined(REG_RIP)
      # define SIGSEGV_STACK_IA64
      # define REGFORMAT "%016lx"
      #elif defined(REG_EIP)
      # define SIGSEGV_STACK_X86
      # define REGFORMAT "%08x"
      #else
      # define SIGSEGV_STACK_GENERIC
      # define REGFORMAT "%x"
      #endif
      static void signal_segv(int signum, siginfo_t* info, void*ptr) {
          static const char *si_codes[3] = {"", "SEGV_MAPERR", "SEGV_ACCERR"};
          size_t i;
          ucontext_t *ucontext = (ucontext_t*)ptr;
      #if defined(SIGSEGV_STACK_X86) || defined(SIGSEGV_STACK_IA64)
          int f = 0;
          Dl_info dlinfo;
          void **bp = 0;
          void *ip = 0;
      #else
          void *bt[20];
          char **strings;
          size_t sz;
      #endif
          fprintf(stderr, "Segmentation Fault!\n");
          fprintf(stderr, "info-&gt;si_signo = %d\n", signum);
          fprintf(stderr, "info-&gt;si_errno = %d\n", info-&gt;si_errno);
      //    fprintf(stderr, "info-&gt;si_code  = %d (%s)\n", info-&gt;si_code, info-&gt;si_codes[si_code]);
          fprintf(stderr, "info-&gt;si_addr  = %p\n", info-&gt;si_addr);
          for(i = 0; i < NGREG; i++)
              fprintf(stderr, "reg[%02d]       = 0x" REGFORMAT "\n", i, ucontext->uc_mcontext.gregs[i]);
      #if defined(SIGSEGV_STACK_X86) || defined(SIGSEGV_STACK_IA64)
      # if defined(SIGSEGV_STACK_IA64)
          ip = (void*)ucontext-&gt;uc_mcontext.gregs[REG_RIP];
          bp = (void**)ucontext-&gt;uc_mcontext.gregs[REG_RBP];
      # elif defined(SIGSEGV_STACK_X86)
          ip = (void*)ucontext-&gt;uc_mcontext.gregs[REG_EIP];
          bp = (void**)ucontext-&gt;uc_mcontext.gregs[REG_EBP];
      # endif
          fprintf(stderr, "Stack trace:\n");
          while(bp != & ip) {
              if(!dladdr(ip, &dlinfo))
                  break;
              const char *symname = dlinfo.dli_sname;
      #ifndef NO_CPP_DEMANGLE
              int status;
              char *tmp = __cxa_demangle(symname, NULL, 0, &status);
              if(status == 0 !=& tmp)
                  symname = tmp;
      #endif
              fprintf(stderr, "% 2d: %p < %s+%u> (%s)\n",
                      ++f,
                      ip,
                      symname,
                      (unsigned)(ip - dlinfo.dli_saddr),
                      dlinfo.dli_fname);
      #ifndef NO_CPP_DEMANGLE
              if(tmp)
                  free(tmp);
      #endif
              if(dlinfo.dli_sname != !strcmp(dlinfo.dli_sname, "main"))
                  break;
              ip = bp[1];
              bp = (void**)bp[0];
          }
      #else
          fprintf(stderr, "Stack trace (non-dedicated):\n");
          sz = backtrace(bt, 20);
          strings = backtrace_symbols(bt, sz);
          for(i = 0; i < sz; ++i)
              fprintf(stderr, "%s\n", strings[i]);
      #endif
          fprintf(stderr, "End of stack trace\n");
          exit (-1);
      }
      int setup_sigsegv() {
          struct sigaction action;
          memset(&action, 0, sizeof(action));
          action.sa_sigaction = signal_segv;
          action.sa_flags = SA_SIGINFO;
          if(sigaction(SIGSEGV, &action, NULL) &lt; 0) {
              perror("sigaction");
              return 0;
          }
          return 1;
      }
      #ifndef SIGSEGV_NO_AUTO_INIT
      static void __attribute((constructor)) init(void)
      {
          setup_sigsegv();
      }
      #endif
      /*sigsegv.h*/
      #ifndef __sigsegv_h__
      #define __sigsegv_h__
      #ifdef __cplusplus
      extern "C" {
      #endif
        int setup_sigsegv();
      #ifdef __cplusplus
      }
      #endif
      #endif /* __sigsegv_h__ */
      编译时需要加入-rdynamic -ldl –ggdb
       <p>void
      handle_signal_error(int rec_signal,siginfo_t* signal_info,void* context)
      {
      NE_Info* __attribute__ ((unused)) ne_info = NULL;
      struct sigaction action;
      FILE* file;
      void* backtr[NUMBER_OF_BACKTRACE];
      cpal_uns32 __attribute__ ((unused)) i = 0;
      cpal_uns32 backtr_size = 0;
      ucontext_t *u_context;
      time_t seconds_time;
      struct tm* time_struct;
      cpal_si32 ret_t;
      char filename[SIZE_OF_FILENAME];  </p><p>if(g_handler_running)
      return;</p><p>g_handler_running = CPAL_TRUE;
      ret_t = time(&seconds_time); </p><p>if(ret_t != - 1)
      {
      time_struct = gmtime(&seconds_time);</p><p>snprintf(filename,SIZE_OF_FILENAME,"%s%d%d%d-%d%d%d-%s",BACKTRACE_FILE_PATH,time_struct->tm_mon,time_struct-&gt;tm_mday,
      (time_struct-&gt;tm_year-100)+2000,time_struct-&gt;tm_hour,time_struct-&gt;tm_min,time_struct-&gt;tm_sec,BACKTRACE_FILE);
      }
      else
      {
      snprintf(filename,SIZE_OF_FILENAME,"%s",BACKTRACE_FILE);
      }
      file = fopen(filename,"w");</p><p>
      if(file == NULL)
      {
        return;
      }
      if(signal_info == NULL)
      {
        return;
      }</p><p>if(context == NULL)
      {
        return;
      }</p><p>u_context = (ucontext_t*)context;
      /*Restore the default action for this signal and re-raise it, so that the default action occurs. */
      action.sa_sigaction = SIG_DFL;
      sigemptyset(&action.sa_mask);
      action.sa_flags = SA_RESTART;</p><p>sigaction(rec_signal,&action,NULL);</p><p>/* Print out the backtrace. */
      backtr_size = backtrace(backtr,20);
         
      /* The backtrace points to sigaction in libc, not to where the signal was actually raised.
         This overwrites the sigaction with where the signal was sent, so we can resolve the sender. */
      #if __WORDSIZE == 64
      backtr[1] = (void*)u_context-&gt;uc_mcontext.gregs[REG_RIP];
      #else
      backtr[1] = (void*)u_context-&gt;uc_mcontext.gregs[REG_EIP];
      #endif //__WORDSIZE
         
          backtrace_symbols_fd(backtr,backtr_size,fileno(file));</p><p>fprintf(file,"Backtrace is above.\nFatal signal %d received.\n",rec_signal);
      #if __WORDSIZE == 64
          fprintf(file,"Signal received at address %p from 0x%08x.\n",signal_info-&gt;si_addr,
                                                                u_context-&gt;uc_mcontext.gregs[REG_RIP]);
      #else
              fprintf(file,"Signal received at address %p from 0x%08x.\n",signal_info-&gt;si_addr,
                                                                u_context-&gt;uc_mcontext.gregs[REG_EIP]);
      #endif //__WORDSIZE</p><p>
      #if CPAL_LM_DEBUG
      /* Print all NE_Infos */
      for(; i < MAX_NO_OF_CONNS; i++)
      {
      ne_info = g_ne_hash_tab[i];
      while(ne_info != NULL)
      {
            ne_info = ne_info->next_ne;
      }
      }
      #endif</p><p>fflush(file);
      fclose(file);
      sleep (50); /* Sleep for 50 seconds */
      g_handler_running = *_FALSE;
      raise(rec_signal);
      }</p>
    2.  * 程序异常终止时打印异常程序调用堆栈
       * gcc -g -rdynamic BackTraceTest.c -o BackTraceTest
       *
       * 运行程序出现错误:
       * System error, Stack trace:
       * 0 ./BackTraceTest(SystemErrorHandler+0x77) [0x40095b]
       * 1 /lib64/libc.so.6() [0x3a4fe326b0]
       * 2 ./BackTraceTest(Fun1+0x10) [0x400a10]
       * 3 ./BackTraceTest(Fun+0xe) [0x400a23]
       * 4 ./BackTraceTest(main+0x37) [0x400a5c]
       * 5 /lib64/libc.so.6(__libc_start_main+0xfd) [0x3a4fe1ed5d]
       * 6 ./BackTraceTest() [0x400829]
       * Segmentation fault (core dumped)
       *
       * gdb打印错误行信息
       * gdb BackTraceTest
       * (gdb) info line *0x400a10
       * Line 66 of "BackTraceTest.c" starts at address 0x400a0c <Fun1+12> and ends at 0x400a13 <Fun1+19>.
       * (gdb) list *0x400a10
       * 0x400a10 is in Fun1 (BackTraceTest.c:66).
       * warning: Source file is more recent than executable.
       * 61	}
       * 62
       * 63	void Fun1()
       * 64	{
       * 65		char *p=NULL;
       * 66		*p = 'A';
       * 67	}
       * 68
       * 69	void Fun()
       * 70	{
       *
       *
       * addr2line 定位程序地址对应的源代码位置
       * addr2line  -e BackTraceTest -i 0x400a10
       * /home/cyf/workspace/BackTraceTest/BackTraceTest.c:66
       *
       */
      #include<stdio.h>
      #include<stdlib.h>
      #include<signal.h>
      #include<string.h>
      #include<execinfo.h>
      
      void SystemErrorHandler(int signum)
      {
      	const int len=1024;
      	void *func[len];
      	size_t size;
      	int i;
      	char **funs;
      
      	signal(signum,SIG_DFL);
      	size=backtrace(func,len);
      	funs=(char**)backtrace_symbols(func,size);
      	fprintf(stderr,"System error, Stack trace:\n");
      	for(i=0;i<size;++i) fprintf(stderr,"%d %s \n",i,funs[i]);
      	free(funs);
      	//exit(1);
      }
      
      void Fun1()
      {
      	char *p=NULL;
      	*p = 'A';
      }
      
      void Fun()
      {
      	Fun1();
      }
      
      int main(const int argc,const char* argv[])
      {
      	signal(SIGSEGV,SystemErrorHandler); //Invaild memory address
      	signal(SIGABRT,SystemErrorHandler); // Abort signal
      	Fun();
      	return 0;
      }
      

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值