Create minidump on fatal Win32 exceptions

Remove software exception translator function, simplifying exception handler
macros.  FatalSystemExceptions are left unhandled.
This commit is contained in:
kwolekr 2015-02-01 03:08:04 -05:00
parent 48fa893abd
commit 43f102271d
6 changed files with 149 additions and 72 deletions

@ -128,6 +128,7 @@ add_definitions ( -DUSE_CMAKE_CONFIG_H )
if(WIN32)
# Windows
if(MSVC) # MSVC Specifics
set(PLATFORM_LIBS dbghelp.lib ${PLATFORM_LIBS})
# Surpress some useless warnings
add_definitions ( /D "_CRT_SECURE_NO_DEPRECATE" /W1 )
else() # Probably MinGW = GCC
@ -630,11 +631,11 @@ if(MSVC)
#set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/LTCG /NODEFAULTLIB:\"libcmtd.lib\" /NODEFAULTLIB:\"libcmt.lib\"")
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/LTCG")
set(CMAKE_CXX_FLAGS_SEMIDEBUG "/MDd /Zi /Ob0 /O1 /RTC1 /Wall")
set(CMAKE_CXX_FLAGS_SEMIDEBUG "/MDd /Zi /Ob0 /O1 /RTC1")
# Debug build doesn't catch exceptions by itself
# Add some optimizations because otherwise it's VERY slow
set(CMAKE_CXX_FLAGS_DEBUG "/MDd /Zi /Ob0 /Od /RTC1 /Wall")
set(CMAKE_CXX_FLAGS_DEBUG "/MDd /Zi /Ob0 /Od /RTC1")
# Flags for C files (sqlite)
# /MT = Link statically with standard library stuff

@ -29,6 +29,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "jthread/jmutex.h"
#include "jthread/jmutexautolock.h"
#include "config.h"
#ifdef _MSC_VER
#include <dbghelp.h>
#include "version.h"
#include "filesys.h"
#endif
/*
Debug output
*/
@ -301,35 +308,124 @@ DebugStacker::~DebugStacker()
}
}
#ifdef _MSC_VER
#if CATCH_UNHANDLED_EXCEPTIONS == 1
void se_trans_func(unsigned int u, EXCEPTION_POINTERS* pExp)
const char *Win32ExceptionCodeToString(DWORD exception_code)
{
dstream<<"In trans_func.\n";
if(u == EXCEPTION_ACCESS_VIOLATION)
switch (exception_code) {
case EXCEPTION_ACCESS_VIOLATION:
return "Access violation";
case EXCEPTION_DATATYPE_MISALIGNMENT:
return "Misaligned data access";
case EXCEPTION_BREAKPOINT:
return "Breakpoint reached";
case EXCEPTION_SINGLE_STEP:
return "Single debug step";
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
return "Array access out of bounds";
case EXCEPTION_FLT_DENORMAL_OPERAND:
return "Denormal floating point operand";
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
return "Floating point division by zero";
case EXCEPTION_FLT_INEXACT_RESULT:
return "Inaccurate floating point result";
case EXCEPTION_FLT_INVALID_OPERATION:
return "Invalid floating point operation";
case EXCEPTION_FLT_OVERFLOW:
return "Floating point exponent overflow";
case EXCEPTION_FLT_STACK_CHECK:
return "Floating point stack overflow or underflow";
case EXCEPTION_FLT_UNDERFLOW:
return "Floating point exponent underflow";
case EXCEPTION_INT_DIVIDE_BY_ZERO:
return "Integer division by zero";
case EXCEPTION_INT_OVERFLOW:
return "Integer overflow";
case EXCEPTION_PRIV_INSTRUCTION:
return "Privileged instruction executed";
case EXCEPTION_IN_PAGE_ERROR:
return "Could not access or load page";
case EXCEPTION_ILLEGAL_INSTRUCTION:
return "Illegal instruction encountered";
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
return "Attempted to continue after fatal exception";
case EXCEPTION_STACK_OVERFLOW:
return "Stack overflow";
case EXCEPTION_INVALID_DISPOSITION:
return "Invalid disposition returned to the exception dispatcher";
case EXCEPTION_GUARD_PAGE:
return "Attempted guard page access";
case EXCEPTION_INVALID_HANDLE:
return "Invalid handle";
}
return "Unknown exception";
}
long WINAPI Win32ExceptionHandler(struct _EXCEPTION_POINTERS *pExceptInfo)
{
PEXCEPTION_RECORD r = pExp->ExceptionRecord;
dstream<<"Access violation at "<<r->ExceptionAddress
<<" write?="<<r->ExceptionInformation[0]
<<" address="<<r->ExceptionInformation[1]
<<std::endl;
throw FatalSystemException
("Access violation");
char buf[512];
MINIDUMP_EXCEPTION_INFORMATION mdei;
MINIDUMP_USER_STREAM_INFORMATION mdusi;
MINIDUMP_USER_STREAM mdus;
bool minidump_created = false;
std::string version_str("Minetest ");
std::string dumpfile = porting::path_user + DIR_DELIM "minetest.dmp";
HANDLE hFile = CreateFileA(dumpfile.c_str(), GENERIC_WRITE,
FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
goto minidump_failed;
if (SetEndOfFile(hFile) == FALSE)
goto minidump_failed;
mdei.ClientPointers = NULL;
mdei.ExceptionPointers = pExceptInfo;
mdei.ThreadId = GetCurrentThreadId();
version_str += minetest_version_hash;
mdus.Type = CommentStreamA;
mdus.BufferSize = version_str.size();
mdus.Buffer = (PVOID)version_str.c_str();
mdusi.UserStreamArray = &mdus;
mdusi.UserStreamCount = 1;
if (MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile,
MiniDumpNormal, &mdei, &mdusi, NULL) == FALSE)
goto minidump_failed;
minidump_created = true;
minidump_failed:
CloseHandle(hFile);
DWORD excode = pExceptInfo->ExceptionRecord->ExceptionCode;
_snprintf(buf, sizeof(buf),
" >> === FATAL ERROR ===\n"
" >> %s (Exception 0x%08X) at 0x%p\n",
Win32ExceptionCodeToString(excode), excode,
pExceptInfo->ExceptionRecord->ExceptionAddress);
dstream << buf;
if (minidump_created)
dstream << " >> Saved dump to " << dumpfile << std::endl;
else
dstream << " >> Failed to save dump" << std::endl;
return EXCEPTION_EXECUTE_HANDLER;
}
if(u == EXCEPTION_STACK_OVERFLOW)
{
throw FatalSystemException
("Stack overflow");
}
if(u == EXCEPTION_ILLEGAL_INSTRUCTION)
{
throw FatalSystemException
("Illegal instruction");
}
}
#endif
#endif
void debug_set_exception_handler()
{
#ifdef _MSC_VER
SetUnhandledExceptionFilter(Win32ExceptionHandler);
#endif
}

@ -95,6 +95,8 @@ __NORETURN extern void assert_fail(
#define assert(expr) ASSERT(expr)
void debug_set_exception_handler();
/*
DebugStack
*/
@ -123,8 +125,7 @@ private:
#define DSTACKF(...) \
char __buf[DEBUG_STACK_TEXT_SIZE]; \
snprintf(__buf,\
DEBUG_STACK_TEXT_SIZE, __VA_ARGS__);\
snprintf(__buf, DEBUG_STACK_TEXT_SIZE, __VA_ARGS__); \
DebugStacker __debug_stacker(__buf);
/*
@ -132,34 +133,13 @@ private:
*/
#if CATCH_UNHANDLED_EXCEPTIONS == 1
#define BEGIN_PORTABLE_DEBUG_EXCEPTION_HANDLER try{
#define END_PORTABLE_DEBUG_EXCEPTION_HANDLER(logstream)\
#define BEGIN_DEBUG_EXCEPTION_HANDLER try {
#define END_DEBUG_EXCEPTION_HANDLER(logstream) \
} catch (std::exception &e) { \
logstream << "ERROR: An unhandled exception occurred: " \
<< e.what() << std::endl; \
assert(0); \
}
#ifdef _WIN32 // Windows
#ifdef _MSC_VER // MSVC
void se_trans_func(unsigned int, EXCEPTION_POINTERS*);
#define BEGIN_DEBUG_EXCEPTION_HANDLER \
BEGIN_PORTABLE_DEBUG_EXCEPTION_HANDLER\
_set_se_translator(se_trans_func);
#define END_DEBUG_EXCEPTION_HANDLER(logstream) \
END_PORTABLE_DEBUG_EXCEPTION_HANDLER(logstream)
#else // Probably mingw
#define BEGIN_DEBUG_EXCEPTION_HANDLER\
BEGIN_PORTABLE_DEBUG_EXCEPTION_HANDLER
#define END_DEBUG_EXCEPTION_HANDLER(logstream)\
END_PORTABLE_DEBUG_EXCEPTION_HANDLER(logstream)
#endif
#else // Posix
#define BEGIN_DEBUG_EXCEPTION_HANDLER\
BEGIN_PORTABLE_DEBUG_EXCEPTION_HANDLER
#define END_DEBUG_EXCEPTION_HANDLER(logstream)\
END_PORTABLE_DEBUG_EXCEPTION_HANDLER(logstream)
#endif
#else
// Dummy ones
#define BEGIN_DEBUG_EXCEPTION_HANDLER

@ -110,12 +110,6 @@ public:
ServerError(const std::string &s): BaseException(s) {}
};
// Only used on Windows (SEH)
class FatalSystemException : public BaseException {
public:
FatalSystemException(const std::string &s): BaseException(s) {}
};
class ClientStateError : public BaseException {
public:
ClientStateError(std::string s): BaseException(s) {}

@ -780,6 +780,8 @@ int main(int argc, char *argv[])
{
int retval;
debug_set_exception_handler();
log_add_output_maxlev(&main_stderr_log_out, LMT_ACTION);
log_add_output_all_levs(&main_dstream_no_stderr_log_out);

@ -419,6 +419,10 @@ inline const char * getPlatformName()
void setXorgClassHint(const video::SExposedVideoData &video_data,
const std::string &name);
// This only needs to be called at the start of execution, since all future
// threads in the process inherit this exception handler
void setWin32ExceptionHandler();
} // namespace porting
#ifdef __ANDROID__