如何在系统崩溃时从C++中获取函数调用栈信息?

Crq
Crq
Crq
1156
文章
0
评论
2021年4月30日23:06:26
评论
613 2062字阅读6分52秒
摘要

这篇文章主要讲述在 Linux 和 Windows 这 2 个平台上,如何用C++ 来捕获函数调用栈里的信息。

一、前言

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

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

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

二、Linux 平台

1. 注册异常信号的处理函数
需要处理哪些异常信号

#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)

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
三、Windwos 平台

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

1. 设置异常处理函数
#include 
#include 
SetUnhandledExceptionFilter(exceptionHandler);
2. 捕获异常,获取函数调用栈信息
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 

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

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

weinxin
我的微信
这是我的微信扫一扫
Crq
  • 本文由 发表于 2021年4月30日23:06:26
  • 转载请注明:https://www.cncrq.com/9142.html
U盘安装Ubuntu Linux物理机 Linux教程

U盘安装Ubuntu Linux物理机

Linux系统越来越受欢迎,想要学习linux的前提是必须要有一个linux系统。虚拟机的安装方法就在《linux就该这么学》第零章,这里和大家分享一个物理机安装的方法。
挑选合适自己的一门编程语言 Linux教程

挑选合适自己的一门编程语言

想学编程的原因有很多,你也许是想要做一个程序,又或者你只是想投身于这个行业,所以,在选择你的第一门编程语言之前,问问你自己:你想要在哪里运行程序?你想要程序来完成什么工作?
CentOS 7 怎样安装或升级最新的内核? Linux教程

CentOS 7 怎样安装或升级最新的内核?

虽然有些人使用 Linux 来表示整个操作系统,但要注意的是,严格地来说,Linux 只是个内核。另一方面,发行版是一个完整功能的系统,它建立在内核之上,具有各种各样的应用程序工具...
震惊!SpaceVim 原来这么玩! Linux教程

震惊!SpaceVim 原来这么玩!

SpaceVim是一个社区驱动的模块化 vim/neovim 配置集合,其中包含了多种功能模块,并且针对 neovim 做了功能优化。SpaceVim有多种功能模块可供选择,用户只...
匿名

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: