Windows下的异常处理和堆栈跟踪(MinGW/gcc)
Exception handling and stacktrace under Windows (MinGW/gcc)
我需要做一个Linux/GCC异常处理系统Windows/MinGW兼容。
注意:我需要从共享库中捕获和回溯异常。
这是我如何在Linux/GCC下实现它的…
头:#include <execinfo.h>
#include <signal.h>
static void handler(int sig)
{
// Catch exceptions
switch(sig)
{
case SIGABRT:
fputs("Caught SIGABRT: usually caused by an abort() or assert()n", stderr);
break;
case SIGFPE:
fputs("Caught SIGFPE: arithmetic exception, such as divide by zeron",
stderr);
break;
case SIGILL:
fputs("Caught SIGILL: illegal instructionn", stderr);
break;
case SIGINT:
fputs("Caught SIGINT: interactive attention signal, probably a ctrl+cn",
stderr);
break;
case SIGSEGV:
fputs("Caught SIGSEGV: segfaultn", stderr);
break;
case SIGTERM:
default:
fputs("Caught SIGTERM: a termination request was sent to the programn",
stderr);
break;
}
// Print stacktrace
void *array[10];
size_t size;
// get void*'s for all entries on the stack
size = backtrace(array, 10);
// Ctrl+C interrupt => No backtrace
if (sig != (int)SIGINT) {
// print out all the frames to stderr
fprintf(stderr, "Error: signal %d:n", sig);
backtrace_symbols_fd(array, size, 2);
}
exit(sig);
}
Cpp:
signal(SIGABRT, handler);
signal(SIGFPE, handler);
signal(SIGILL, handler);
signal(SIGINT, handler);
signal(SIGSEGV, handler);
signal(SIGTERM, handler);
以上所有内容在Linux下都可以正常工作。现在我想在我的库的Windows版本下有相同的行为…
下面是我捕获异常的方法:
#include <windows.h>
static LONG WINAPI windows_exception_handler(EXCEPTION_POINTERS * ExceptionInfo)
{
switch(ExceptionInfo->ExceptionRecord->ExceptionCode)
{
case EXCEPTION_ACCESS_VIOLATION:
fputs("Error: EXCEPTION_ACCESS_VIOLATIONn", stderr);
break;
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
fputs("Error: EXCEPTION_ARRAY_BOUNDS_EXCEEDEDn", stderr);
break;
case EXCEPTION_BREAKPOINT:
fputs("Error: EXCEPTION_BREAKPOINTn", stderr);
break;
case EXCEPTION_DATATYPE_MISALIGNMENT:
fputs("Error: EXCEPTION_DATATYPE_MISALIGNMENTn", stderr);
break;
case EXCEPTION_FLT_DENORMAL_OPERAND:
fputs("Error: EXCEPTION_FLT_DENORMAL_OPERANDn", stderr);
break;
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
fputs("Error: EXCEPTION_FLT_DIVIDE_BY_ZEROn", stderr);
break;
case EXCEPTION_FLT_INEXACT_RESULT:
fputs("Error: EXCEPTION_FLT_INEXACT_RESULTn", stderr);
break;
case EXCEPTION_FLT_INVALID_OPERATION:
fputs("Error: EXCEPTION_FLT_INVALID_OPERATIONn", stderr);
break;
case EXCEPTION_FLT_OVERFLOW:
fputs("Error: EXCEPTION_FLT_OVERFLOWn", stderr);
break;
case EXCEPTION_FLT_STACK_CHECK:
fputs("Error: EXCEPTION_FLT_STACK_CHECKn", stderr);
break;
case EXCEPTION_FLT_UNDERFLOW:
fputs("Error: EXCEPTION_FLT_UNDERFLOWn", stderr);
break;
case EXCEPTION_ILLEGAL_INSTRUCTION:
fputs("Error: EXCEPTION_ILLEGAL_INSTRUCTIONn", stderr);
break;
case EXCEPTION_IN_PAGE_ERROR:
fputs("Error: EXCEPTION_IN_PAGE_ERRORn", stderr);
break;
case EXCEPTION_INT_DIVIDE_BY_ZERO:
fputs("Error: EXCEPTION_INT_DIVIDE_BY_ZEROn", stderr);
break;
case EXCEPTION_INT_OVERFLOW:
fputs("Error: EXCEPTION_INT_OVERFLOWn", stderr);
break;
case EXCEPTION_INVALID_DISPOSITION:
fputs("Error: EXCEPTION_INVALID_DISPOSITIONn", stderr);
break;
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
fputs("Error: EXCEPTION_NONCONTINUABLE_EXCEPTIONn", stderr);
break;
case EXCEPTION_PRIV_INSTRUCTION:
fputs("Error: EXCEPTION_PRIV_INSTRUCTIONn", stderr);
break;
case EXCEPTION_SINGLE_STEP:
fputs("Error: EXCEPTION_SINGLE_STEPn", stderr);
break;
case EXCEPTION_STACK_OVERFLOW:
fputs("Error: EXCEPTION_STACK_OVERFLOWn", stderr);
break;
default:
fputs("Error: Unrecognized Exceptionn", stderr);
break;
}
fflush(stderr);
if (EXCEPTION_STACK_OVERFLOW != ExceptionInfo->ExceptionRecord->ExceptionCode)
{
// TODO : ...
//windows_print_stacktrace(ExceptionInfo->ContextRecord);
}
return EXCEPTION_EXECUTE_HANDLER;
}
下面是打印stacktrace的方法:
#include <windows.h>
#include <imagehlp.h>
void windows_print_stacktrace(CONTEXT* context)
{
SymInitialize(GetCurrentProcess(), 0, true);
STACKFRAME frame = { 0 };
/* setup initial stack frame */
frame.AddrPC.Offset = context->Eip;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrStack.Offset = context->Esp;
frame.AddrStack.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context->Ebp;
frame.AddrFrame.Mode = AddrModeFlat;
while (StackWalk(IMAGE_FILE_MACHINE_I386 ,
GetCurrentProcess(),
GetCurrentThread(),
&frame,
context,
0,
SymFunctionTableAccess,
SymGetModuleBase,
0 ) )
{
addr2line(global_program_name, (void*)frame.AddrPC.Offset);
}
SymCleanup( GetCurrentProcess() );
}
其中addr2line
为:
#include <stdlib.h>
#include <stdio.h>
/* Resolve symbol name and source location given the path to the executable
and an address */
int addr2line(char const * const program_name, void const * const addr)
{
char addr2line_cmd[512] = {0};
/* have addr2line map the address to the relent line in the code */
sprintf(addr2line_cmd,"addr2line -f -p -e %.256s %p", program_name, addr);
/* This will print a nicely formatted string specifying the
function and source line of the address */
return system(addr2line_cmd);
}
但是:
MinGW没有backtrace
,也没有backtrace_symbols
特征。上面需要知道global_program_name
,我没有,因为管理异常的代码位于主程序加载的dll
中。
So 问题:
是否有办法从dll动态地获得global_program_name
?如果没有,那么用MinGW打印堆栈跟踪的好方法是什么呢?
-g
编译器选项。使用它是否会影响性能(即使我将优化保持在最大-O3
) ?还是只影响共享库的大小?
谢谢你的帮助。
Windows平台可以通过全局__argv
变量获取程序名
#include <stdlib.h>
addr2line(__argv[0], addr);
虽然前面的答案有时可能有效,但在许多情况下它可能会失败。Argv[0]是一个命令行参数,可以在调用执行类型函数(包括windows变体)时传递。要可靠地获取可执行文件,请使用以下代码:
TCHAR szExeFileName[MAX_PATH];
GetModuleFileName(NULL, szExeFileName, MAX_PATH);
相关文章:
- 为什么与常规GCC不同,即使有"学究性错误",MinGW-GCC也能容忍丢失的返回类型
- 使用 MINGW gcc 编译时,不会为 std::string 调用重载的新运算符
- MinGW GCC通配符编译所有文件(Windows)
- C++编译错误,std 中的互斥锁不会在 MinGW (GCC 6.3.0) 中命名类型
- C++ MinGW GCC CodeBlocks静态链接
- 使用MinGW gcc/g++(nuwen发行版)编译的程序出现运行时错误
- Boost 1.49/1.50/1.51 ASIO with MinGW & GCC 4.7.0 中的转换错误
- 在MinGW / GCC中编译tvmet库时的"::drem has not been declared"
- 如何更新到 mingw-gcc 4.8.2
- MinGW gcc C编译器有效,但g++不能
- 新MinGW gcc什么都不做
- Eclipse中的MinGW GCC和G++错误
- MinGW GCC 4.9.1和浮点决定论
- Windows下的异常处理和堆栈跟踪(MinGW/gcc)
- 当其他编译器没有错误时,MinGW gcc给出错误
- 如何在Mingw Gcc中声明和链接RoInitialize,RoUninitialize,RoGetActivatio
- 在Windows 7下运行MinGW gcc编译器,不设置环境变量
- Eclipse CDT with MinGW GCC Make Error 127
- MinGW GCC.exe正在从路径中删除所有
- 如何查找已安装的MinGW GCC编译器的PATH变量