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

2fda6bf9384f9d4afd6d0ea436045453.png

一、前言

程序在执行过程中 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 sigactionaction;

sigemptyset(&action.sa_mask);

action.sa_sigaction = &sigHandler;

action.sa_flags = SA_SIGINFO;

for(const auto &sigPair : Signals)

{

if (sigaction(sigPair.first, &action,NULL) 

fprintf(stderr, "Error: sigaction failed! \n");

}

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

void sigHandler(intsignum, siginfo_t *info, void *ctx)

{

const size_t dump_size = 50;

void *array[dump_size];

intsize= backtrace(array, dump_size);

char**symbols = backtrace_symbols(array,size);

std::ostringstream oss;

for(inti = 0; i 

{

char*mangleName = 0;

char*offsetBegin = 0;

char*offsetEnd = 0;

for(char*p = symbols[i]; *p; ++p)

{

if ('('== *p)

{

mangleName = p;

}

elseif ('+'== *p)

{

offsetBegin = p;

}

elseif (')'== *p)

{

offsetEnd = p;

break;

}

}

if (mangleName && offsetBegin && offsetEnd && mangleName 

{

*mangleName++ = '\0';

*offsetBegin++ = '\0';

*offsetEnd++ = '\0';

intstatus;

char*realName = abi::__cxa_demangle(mangleName, 0, 0, &status);

if (0 == status)

oss <

else

oss <

oss <

free(realName);

}

else

{

oss <

}

}

free(symbols);

oss <

std::cout <

}

三、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_tindex= 0;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_tindex= 0;index

dump += getSymbolInfo(index, frameVector);

dump += "\n";

}

std::cout <

}

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

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

【编辑推荐】

【责任编辑:姜华 TEL:(010)68476606】

点赞 0

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值