Мне нужно сделать систему обработки исключений Linux/GCC совместимой с Windows/MinGW.Обработка исключений и stacktrace под Windows (MinGW/gcc)
Примечание: Мне нужно перехватывать исключения и исключать их из внутренней библиотеки.
Вот как я реализовал его под 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 zero\n",
stderr);
break;
case SIGILL:
fputs("Caught SIGILL: illegal instruction\n", stderr);
break;
case SIGINT:
fputs("Caught SIGINT: interactive attention signal, probably a ctrl+c\n",
stderr);
break;
case SIGSEGV:
fputs("Caught SIGSEGV: segfault\n", stderr);
break;
case SIGTERM:
default:
fputs("Caught SIGTERM: a termination request was sent to the program\n",
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_VIOLATION\n", stderr);
break;
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
fputs("Error: EXCEPTION_ARRAY_BOUNDS_EXCEEDED\n", stderr);
break;
case EXCEPTION_BREAKPOINT:
fputs("Error: EXCEPTION_BREAKPOINT\n", stderr);
break;
case EXCEPTION_DATATYPE_MISALIGNMENT:
fputs("Error: EXCEPTION_DATATYPE_MISALIGNMENT\n", stderr);
break;
case EXCEPTION_FLT_DENORMAL_OPERAND:
fputs("Error: EXCEPTION_FLT_DENORMAL_OPERAND\n", stderr);
break;
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
fputs("Error: EXCEPTION_FLT_DIVIDE_BY_ZERO\n", stderr);
break;
case EXCEPTION_FLT_INEXACT_RESULT:
fputs("Error: EXCEPTION_FLT_INEXACT_RESULT\n", stderr);
break;
case EXCEPTION_FLT_INVALID_OPERATION:
fputs("Error: EXCEPTION_FLT_INVALID_OPERATION\n", stderr);
break;
case EXCEPTION_FLT_OVERFLOW:
fputs("Error: EXCEPTION_FLT_OVERFLOW\n", stderr);
break;
case EXCEPTION_FLT_STACK_CHECK:
fputs("Error: EXCEPTION_FLT_STACK_CHECK\n", stderr);
break;
case EXCEPTION_FLT_UNDERFLOW:
fputs("Error: EXCEPTION_FLT_UNDERFLOW\n", stderr);
break;
case EXCEPTION_ILLEGAL_INSTRUCTION:
fputs("Error: EXCEPTION_ILLEGAL_INSTRUCTION\n", stderr);
break;
case EXCEPTION_IN_PAGE_ERROR:
fputs("Error: EXCEPTION_IN_PAGE_ERROR\n", stderr);
break;
case EXCEPTION_INT_DIVIDE_BY_ZERO:
fputs("Error: EXCEPTION_INT_DIVIDE_BY_ZERO\n", stderr);
break;
case EXCEPTION_INT_OVERFLOW:
fputs("Error: EXCEPTION_INT_OVERFLOW\n", stderr);
break;
case EXCEPTION_INVALID_DISPOSITION:
fputs("Error: EXCEPTION_INVALID_DISPOSITION\n", stderr);
break;
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
fputs("Error: EXCEPTION_NONCONTINUABLE_EXCEPTION\n", stderr);
break;
case EXCEPTION_PRIV_INSTRUCTION:
fputs("Error: EXCEPTION_PRIV_INSTRUCTION\n", stderr);
break;
case EXCEPTION_SINGLE_STEP:
fputs("Error: EXCEPTION_SINGLE_STEP\n", stderr);
break;
case EXCEPTION_STACK_OVERFLOW:
fputs("Error: EXCEPTION_STACK_OVERFLOW\n", stderr);
break;
default:
fputs("Error: Unrecognized Exception\n", 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
основных загрузках программы.
Так Вопрос:
Есть ли способ, чтобы получить global_program_name
динамически из библиотеки DLL? А если нет, то это будет хороший подход, чтобы получить стек, напечатанный с помощью MinGW?
Nota Bene: Еще один подзапрос меня дразнит в этот момент. Чтобы получить хорошие стеки, мне нужно включить опцию компилятора -g
. Получаю ли я производительность, используя его (даже если я буду поддерживать максимальную оптимизацию -O3
)? Или я просто влияю на размер моей общей библиотеки?
Спасибо за любую помощь по этому вопросу.
Вы можете включить все [код для стека блуждания] (http://stackoverflow.com/a/6207030/179910) вместо того, чтобы запустить внешний процесс значительная его часть. –
Ну, моя цель здесь заключалась в том, чтобы сохранить код отслеживания стека вне досягаемости конечного пользователя. Пользователь связывает DLL и делает свою собственную программу с использованием встроенных функций. В этом контексте stacktrace используется в основном для исключения исключений (от меня) из dll. Я должен был сказать это сначала в моем вопросе. Или, может быть, я не понял код, на который вы указали? –
Другими словами, пользователь dll не должен иметь дело с каким-либо дополнительным кодом в своей основной программе ... –