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

为南谯等地区用户提供了全套网页设计制作服务,及南谯网站建设行业解决方案。主营业务为网站设计制作、做网站、南谯网站设计,以传统方式定制建设网站,并提供域名空间备案等一条龙服务,秉承以专业、用心的态度为用户提供真诚的服务。我们深信只要达到每一位用户的要求,就会得到认可,从而选择与我们长期合作。这样,我们也可以走得更远!
因此,程序的日志系统需要侦测这种情况,在代码崩溃的时候获取函数调用栈信息,为 debug 提供有效的信息。
这篇文章的理论知识很少,直接分享 2 段代码:在 Linux 和 Windows 这 2 个平台上,如何用C++ 来捕获函数调用栈里的信息。
需要处理哪些异常信号
- #include
 - #include
 - #include
 - const std::map
 Signals = { - {SIGINT, "SIGINT"},
 - {SIGABRT, "SIGABRT"},
 - {SIGFPE, "SIGFPE"},
 - {SIGILL, "SIGILL"},
 - {SIGSEGV, "SIGSEGV"}
 - // 可以添加其他信号
 - };
 
注册信号处理函数
- struct sigaction action;
 - sigemptyset(&action.sa_mask);
 - action.sa_sigaction = &sigHandler;
 - action.sa_flags = SA_SIGINFO;
 - for (const auto &sigPair : Signals)
 - {
 - if (sigaction(sigPair.first, &action, NULL) < 0)
 - fprintf(stderr, "Error: sigaction failed! \n");
 - }
 
- void sigHandler(int signum, siginfo_t *info, void *ctx)
 - {
 - const size_t dump_size = 50;
 - void *array[dump_size];
 - int size = backtrace(array, dump_size);
 - char **symbols = backtrace_symbols(array, size);
 - std::ostringstream oss;
 - for (int i = 0; i < size; ++i)
 - {
 - char *mangleName = 0;
 - char *offsetBegin = 0;
 - char *offsetEnd = 0;
 - for (char *p = symbols[i]; *p; ++p)
 - {
 - if ('(' == *p)
 - {
 - mangleName = p;
 - }
 - else if ('+' == *p)
 - {
 - offsetBegin = p;
 - }
 - else if (')' == *p)
 - {
 - offsetEnd = p;
 - break;
 - }
 - }
 - if (mangleName && offsetBegin && offsetEnd && mangleName < offsetBegin)
 - {
 - *mangleName++ = '\0';
 - *offsetBegin++ = '\0';
 - *offsetEnd++ = '\0';
 - int status;
 - char *realName = abi::__cxa_demangle(mangleName, 0, 0, &status);
 - if (0 == status)
 - oss << "\tstack dump [" << i << "] " << symbols[i] << " : " << realName << "+";
 - else
 - oss << "\tstack dump [" << i << "] " << symbols[i] << mangleName << "+";
 - oss << offsetBegin << offsetEnd << std::endl;
 - free(realName);
 - }
 - else
 - {
 - oss << "\tstack dump [" << i << "] " << symbols[i] << std::endl;
 - }
 - }
 - free(symbols);
 - oss << std::endl;
 - std::cout << oss.str(); // 打印函数调用栈信息
 - }
 
在 Windows 平台下的代码实现,参考了国外某个老兄的代码,如下:
- #include
 - #include
 - SetUnhandledExceptionFilter(exceptionHandler);
 
- void exceptionHandler(LPEXCEPTION_POINTERS info)
 - {
 - CONTEXT *context = info->ContextRecord;
 - std::shared_ptr
 RaiiSysCleaner(nullptr, [&](void *) { - SymCleanup(GetCurrentProcess());
 - });
 - const size_t dumpSize = 64;
 - std::vector
 frameVector(dumpSize); - DWORD machine_type = 0;
 - STACKFRAME64 frame = {};
 - frame.AddrPC.Mode = AddrModeFlat;
 - frame.AddrFrame.Mode = AddrModeFlat;
 - frame.AddrStack.Mode = AddrModeFlat;
 - #ifdef _M_IX86
 - frame.AddrPC.Offset = context->Eip;
 - frame.AddrFrame.Offset = context->Ebp;
 - frame.AddrStack.Offset = context->Esp;
 - machine_type = IMAGE_FILE_MACHINE_I386;
 - #elif _M_X64
 - frame.AddrPC.Offset = context->Rip;
 - frame.AddrFrame.Offset = context->Rbp;
 - frame.AddrStack.Offset = context->Rsp;
 - machine_type = IMAGE_FILE_MACHINE_AMD64;
 - #elif _M_IA64
 - frame.AddrPC.Offset = context->StIIP;
 - frame.AddrFrame.Offset = context->IntSp;
 - frame.AddrStack.Offset = context->IntSp;
 - machine_type = IMAGE_FILE_MACHINE_IA64;
 - frame.AddrBStore.Offset = context.RsBSP;
 - frame.AddrBStore.Mode = AddrModeFlat;
 - #else
 - frame.AddrPC.Offset = context->Eip;
 - frame.AddrFrame.Offset = context->Ebp;
 - frame.AddrStack.Offset = context->Esp;
 - machine_type = IMAGE_FILE_MACHINE_I386;
 - #endif
 - for (size_t index = 0; index < frameVector.size(); ++index)
 - {
 - if (StackWalk64(machine_type,
 - GetCurrentProcess(),
 - GetCurrentThread(),
 - &frame,
 - context,
 - NULL,
 - SymFunctionTableAccess64,
 - SymGetModuleBase64,
 - NULL)) {
 - frameVector[index] = frame.AddrPC.Offset;
 - } else {
 - break;
 - }
 - }
 - std::string dump;
 - const size_t kSize = frameVector.size();
 - for (size_t index = 0; index < kSize && frameVector[index]; ++index) {
 - dump += getSymbolInfo(index, frameVector);
 - dump += "\n";
 - }
 - std::cout << dump;
 - }
 
主要是利用了 StackWalk64 这个函数,从地址转换为函数名称。
利用以上几个神器,基本上可以获取到程序崩溃时的函数调用栈信息,定位问题,有如神助!
Copyright © 2009-2022 www.wtcwzsj.com 青羊区广皓图文设计工作室(个体工商户) 版权所有 蜀ICP备19037934号