程序崩溃时,如何获取函数调用栈信息 太过爱你忘了你带给我的痛 2022-10-05 07:55 185阅读 0赞 ![671e1b1be6bc82acf632605ce807c5d3.png][] * 一、前言 * 二、Linux 平台 * 三、Windwos 平台 ## 一、前言 ## 程序在执行过程中 crash 是非常严重的问题,一般都应该在测试阶段排除掉这些问题,但是总会有漏网之鱼被带到 release 阶段。 因此,程序的日志系统需要侦测这种情况,在代码崩溃的时候获取函数调用栈信息,为 debug 提供有效的信息。 这篇文章的理论知识很少,直接分享 2 段代码:在 Linux 和 Windows 这 2 个平台上,如何用 C++ 来捕获函数调用栈里的信息。 ## 二、Linux 平台 ## #### 1. 注册异常信号的处理函数 #### 需要处理哪些异常信号 #include <execinfo.h>#include <cxxabi.h>#include <signal.h> const std::map<int, std::string> 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"); } #### 2. 捕获异常,获取函数调用栈信息 #### 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(); // 打印函数调用栈信息} ## 三、Windwos 平台 ## 在 Windows 平台下的代码实现,参考了国外某个老兄的代码,如下: #### 1. 设置异常处理函数 #### #include <windows.h>#include <dbghelp.h> SetUnhandledExceptionFilter(exceptionHandler); #### 2. 捕获异常,获取函数调用栈信息 #### void exceptionHandler(LPEXCEPTION_POINTERS info){ CONTEXT *context = info->ContextRecord; std::shared_ptr<void> RaiiSysCleaner(nullptr, [&](void *) { SymCleanup(GetCurrentProcess()); }); const size_t dumpSize = 64; std::vector<uint64_t> 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 这个函数,从地址转换为函数名称。 利用以上几个神器,基本上可以获取到程序崩溃时的函数调用栈信息,定位问题,有如神助! ![968a0ed6372bc3aeeb432df0b72a8a80.png][] [671e1b1be6bc82acf632605ce807c5d3.png]: /images/20221005/8c58612ba111482f941b42a931adcf91.png [968a0ed6372bc3aeeb432df0b72a8a80.png]: /images/20221005/1625ddc139824d9fba58700d97fe3842.png
相关 Java API调用错误导致程序崩溃案例 在编程中,API(应用程序接口)调用错误可能会导致程序运行时出现问题,甚至引发系统崩溃。以下是一个具体的案例: **案例描述:** 开发一个基于Java的Web应用,该应用需 约定不等于承诺〃/ 2024年09月10日 02:27/ 0 赞/ 11 阅读
相关 Java反射机制:如何在运行时获取类的信息并动态调用方法? Java的反射机制允许我们在运行时获取类的信息,并动态地调用方法。以下是详细步骤: 1. 获取类信息: - 类名:`Class<?> className = Class 迈不过友情╰/ 2024年09月04日 07:48/ 0 赞/ 22 阅读
相关 函数调用栈(Call Stack) 首先,引用《代码揭秘——从C/C++的角度探秘计算机系统》中6.3.1小节的部分内容作为本文的开始,文章后续部分会对相关内容进行穿插引用,并辅以必要的解释说明。 ![wate 旧城等待,/ 2022年10月10日 11:19/ 0 赞/ 149 阅读
相关 程序崩溃时,如何获取函数调用栈信息 ![671e1b1be6bc82acf632605ce807c5d3.png][] 一、前言 二、Linux 平台 三、Windwos 平台 一、前言 太过爱你忘了你带给我的痛/ 2022年10月05日 07:55/ 0 赞/ 186 阅读
相关 Linux下的C++程序崩溃时打印崩溃信息 概述 在某些极端情况下,原本正常执行的程序发生了崩溃。这时候想通过调试是很难发现出错的地方的,所以在崩溃时打印出错点的调用堆栈是十分有必要的。 使用的命令:`catch 逃离我推掉我的手/ 2022年08月22日 15:12/ 0 赞/ 547 阅读
相关 java栈 函数如何调用 1、当前正在执行的函数所对应的帧就是当前的帧(位于栈顶),它保存当前函数的局部变量、中间运算结果等数据 2、当函数返回时,栈帧从java栈中被弹出。 java 方法有两种返回 野性酷女/ 2022年07月16日 08:22/ 0 赞/ 229 阅读
相关 记录程序崩溃时的调用堆栈 最近有个用户遇到程序Crash问题,但我们的机器都不能重现,于是在网上搜了一把,发现有个MSJExceptionHandler类还比较好用,故整理了一下供大家参考。 这个类的 r囧r小猫/ 2022年06月18日 03:53/ 0 赞/ 291 阅读
相关 python 打印函数调用栈 python开发中有时我们想知道函数的调用路径,这时可以写一个简单的函数来实现: import sys def TraceStack(): 痛定思痛。/ 2022年06月05日 02:56/ 0 赞/ 408 阅读
相关 获取栈内的异常信息 [2019独角兽企业重金招聘Python工程师标准>>> ][2019_Python_] ![hot3.png][] / 获取栈内的异常信息 浅浅的花香味﹌/ 2022年01月13日 15:45/ 0 赞/ 218 阅读
还没有评论,来说两句吧...