一、前言
程序在执行过程中 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