Linux+Windows:程序崩溃时,在C++代码中,如何获取函数调用栈信息

一、前言

程序在执行过程中 crash 是非常严重的问题,一般都应该在测试阶段排除掉这些问题,但是总会有漏网之鱼被带到 release 阶段。

为南谯等地区用户提供了全套网页设计制作服务,及南谯网站建设行业解决方案。主营业务为网站设计制作、做网站、南谯网站设计,以传统方式定制建设网站,并提供域名空间备案等一条龙服务,秉承以专业、用心的态度为用户提供真诚的服务。我们深信只要达到每一位用户的要求,就会得到认可,从而选择与我们长期合作。这样,我们也可以走得更远!

因此,程序的日志系统需要侦测这种情况,在代码崩溃的时候获取函数调用栈信息,为 debug 提供有效的信息。

这篇文章的理论知识很少,直接分享 2 段代码:在 Linux 和 Windows 这 2 个平台上,如何用C++ 来捕获函数调用栈里的信息。

二、Linux 平台

1. 注册异常信号的处理函数

需要处理哪些异常信号

 
 
 
 
  1. #include 
  2. #include 
  3. #include 
  4. const std::map Signals = {
  5.     {SIGINT, "SIGINT"},    
  6.     {SIGABRT, "SIGABRT"}, 
  7.     {SIGFPE, "SIGFPE"},   
  8.     {SIGILL, "SIGILL"},  
  9.     {SIGSEGV, "SIGSEGV"}
  10.     // 可以添加其他信号
  11. };

注册信号处理函数

 
 
 
 
  1. struct sigaction action;
  2. sigemptyset(&action.sa_mask);
  3. action.sa_sigaction = &sigHandler;
  4. action.sa_flags = SA_SIGINFO; 
  5.  for (const auto &sigPair : Signals)
  6.  {
  7.     if (sigaction(sigPair.first, &action, NULL) < 0)
  8.         fprintf(stderr, "Error: sigaction failed! \n");
  9.  }

2. 捕获异常,获取函数调用栈信息

 
 
 
 
  1. void sigHandler(int signum, siginfo_t *info, void *ctx)
  2. {
  3.     const size_t dump_size = 50;
  4.     void *array[dump_size];
  5.     int size = backtrace(array, dump_size);
  6.     char **symbols = backtrace_symbols(array, size);
  7.     std::ostringstream oss;
  8.     for (int i = 0; i < size; ++i)
  9.     {
  10.         char *mangleName = 0;
  11.         char *offsetBegin = 0;
  12.         char *offsetEnd = 0;
  13.         for (char *p = symbols[i]; *p; ++p)
  14.         {
  15.             if ('(' == *p)
  16.             {   
  17.                  mangleName = p;
  18.             }   
  19.             else if ('+' == *p)
  20.             {
  21.                 offsetBegin = p;
  22.             }
  23.             else if (')' == *p)
  24.             {
  25.                 offsetEnd = p;
  26.                 break;
  27.             }
  28.         }
  29.         if (mangleName && offsetBegin && offsetEnd && mangleName < offsetBegin)
  30.         {
  31.             *mangleName++ = '\0';
  32.             *offsetBegin++ = '\0';
  33.             *offsetEnd++ = '\0';
  34.             
  35.             int status;
  36.             char *realName = abi::__cxa_demangle(mangleName, 0, 0, &status);
  37.             if (0 == status)
  38.                 oss << "\tstack dump [" << i << "]  " << symbols[i] << " : " << realName << "+";
  39.             else
  40.                 oss << "\tstack dump [" << i << "]  " << symbols[i] << mangleName << "+";
  41.             oss << offsetBegin << offsetEnd << std::endl;
  42.             free(realName);
  43.         }
  44.         else
  45.         {
  46.             oss << "\tstack dump [" << i << "]  " << symbols[i] << std::endl;
  47.         }
  48.     }
  49.     free(symbols);
  50.     oss << std::endl;
  51.     std::cout << oss.str(); // 打印函数调用栈信息
  52. }

三、Windwos 平台

在 Windows 平台下的代码实现,参考了国外某个老兄的代码,如下:

1. 设置异常处理函数

 
 
 
 
  1. #include 
  2. #include 
  3. SetUnhandledExceptionFilter(exceptionHandler);

2. 捕获异常,获取函数调用栈信息

 
 
 
 
  1. void exceptionHandler(LPEXCEPTION_POINTERS info)
  2. {
  3.     CONTEXT *context = info->ContextRecord;
  4.     std::shared_ptr RaiiSysCleaner(nullptr, [&](void *) {
  5.       SymCleanup(GetCurrentProcess());
  6.     });
  7.   const size_t dumpSize = 64;
  8.   std::vector frameVector(dumpSize);
  9.   DWORD machine_type = 0;
  10.   STACKFRAME64 frame = {};
  11.   frame.AddrPC.Mode = AddrModeFlat;
  12.   frame.AddrFrame.Mode = AddrModeFlat;
  13.   frame.AddrStack.Mode = AddrModeFlat;
  14. #ifdef _M_IX86
  15.   frame.AddrPC.Offset = context->Eip;
  16.   frame.AddrFrame.Offset = context->Ebp;
  17.   frame.AddrStack.Offset = context->Esp;
  18.   machine_type = IMAGE_FILE_MACHINE_I386;
  19. #elif _M_X64
  20.   frame.AddrPC.Offset = context->Rip;
  21.   frame.AddrFrame.Offset = context->Rbp;
  22.   frame.AddrStack.Offset = context->Rsp;
  23.   machine_type = IMAGE_FILE_MACHINE_AMD64;
  24. #elif _M_IA64
  25.   frame.AddrPC.Offset = context->StIIP;
  26.   frame.AddrFrame.Offset = context->IntSp;
  27.   frame.AddrStack.Offset = context->IntSp;
  28.   machine_type = IMAGE_FILE_MACHINE_IA64;
  29.   frame.AddrBStore.Offset = context.RsBSP;
  30.   frame.AddrBStore.Mode = AddrModeFlat;
  31. #else
  32.   frame.AddrPC.Offset = context->Eip;
  33.   frame.AddrFrame.Offset = context->Ebp;
  34.   frame.AddrStack.Offset = context->Esp;
  35.   machine_type = IMAGE_FILE_MACHINE_I386;
  36. #endif
  37.   for (size_t index = 0; index < frameVector.size(); ++index)
  38.   {
  39.     if (StackWalk64(machine_type,
  40.            GetCurrentProcess(),
  41.            GetCurrentThread(),
  42.            &frame,
  43.            context,
  44.            NULL,
  45.            SymFunctionTableAccess64,
  46.            SymGetModuleBase64,
  47.            NULL)) {
  48.       frameVector[index] = frame.AddrPC.Offset;
  49.     } else {
  50.       break;
  51.     }
  52.   }
  53.   std::string dump;
  54.   const size_t kSize = frameVector.size();
  55.   for (size_t index = 0; index < kSize && frameVector[index]; ++index) {
  56.     dump += getSymbolInfo(index, frameVector);
  57.     dump += "\n";
  58.   }
  59. std::cout << dump; 
  60. }

主要是利用了 StackWalk64 这个函数,从地址转换为函数名称。

利用以上几个神器,基本上可以获取到程序崩溃时的函数调用栈信息,定位问题,有如神助!

本文名称:Linux+Windows:程序崩溃时,在C++代码中,如何获取函数调用栈信息
网页网址:http://www.shufengxianlan.com/qtweb/news32/90632.html

网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联