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

View File

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

View File

@ -29,6 +29,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "jthread/jmutex.h" #include "jthread/jmutex.h"
#include "jthread/jmutexautolock.h" #include "jthread/jmutexautolock.h"
#include "config.h" #include "config.h"
#ifdef _MSC_VER
#include <dbghelp.h>
#include "version.h"
#include "filesys.h"
#endif
/* /*
Debug output Debug output
*/ */
@ -57,7 +64,7 @@ void debugstreams_init(bool disable_stderr, const char *filename)
if(filename) if(filename)
g_debugstreams[1] = fopen(filename, "a"); g_debugstreams[1] = fopen(filename, "a");
if(g_debugstreams[1]) if(g_debugstreams[1])
{ {
fprintf(g_debugstreams[1], "\n\n-------------\n"); fprintf(g_debugstreams[1], "\n\n-------------\n");
@ -91,7 +98,7 @@ public:
//TODO: Is this slow? //TODO: Is this slow?
fflush(g_debugstreams[i]); fflush(g_debugstreams[i]);
} }
return c; return c;
} }
std::streamsize xsputn(const char *s, std::streamsize n) std::streamsize xsputn(const char *s, std::streamsize n)
@ -111,7 +118,7 @@ public:
return n; return n;
} }
private: private:
bool m_disable_stderr; bool m_disable_stderr;
}; };
@ -133,7 +140,7 @@ void assert_fail(const char *assertion, const char *file,
"%s:%u: %s: Assertion '%s' failed.\n", "%s:%u: %s: Assertion '%s' failed.\n",
(unsigned long)get_current_thread_id(), (unsigned long)get_current_thread_id(),
file, line, function, assertion); file, line, function, assertion);
debug_stacks_print(); debug_stacks_print();
if(g_debugstreams[1]) if(g_debugstreams[1])
@ -151,7 +158,7 @@ struct DebugStack
DebugStack(threadid_t id); DebugStack(threadid_t id);
void print(FILE *file, bool everything); void print(FILE *file, bool everything);
void print(std::ostream &os, bool everything); void print(std::ostream &os, bool everything);
threadid_t threadid; threadid_t threadid;
char stack[DEBUG_STACK_SIZE][DEBUG_STACK_TEXT_SIZE]; char stack[DEBUG_STACK_SIZE][DEBUG_STACK_TEXT_SIZE];
int stack_i; // Points to the lowest empty position int stack_i; // Points to the lowest empty position
@ -285,10 +292,10 @@ DebugStacker::DebugStacker(const char *text)
DebugStacker::~DebugStacker() DebugStacker::~DebugStacker()
{ {
JMutexAutoLock lock(g_debug_stacks_mutex); JMutexAutoLock lock(g_debug_stacks_mutex);
if(m_overflowed == true) if(m_overflowed == true)
return; return;
m_stack->stack_i--; m_stack->stack_i--;
if(m_stack->stack_i == 0) if(m_stack->stack_i == 0)
@ -301,35 +308,124 @@ DebugStacker::~DebugStacker()
} }
} }
#ifdef _MSC_VER #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"; switch (exception_code) {
if(u == EXCEPTION_ACCESS_VIOLATION) case EXCEPTION_ACCESS_VIOLATION:
{ return "Access violation";
PEXCEPTION_RECORD r = pExp->ExceptionRecord; case EXCEPTION_DATATYPE_MISALIGNMENT:
dstream<<"Access violation at "<<r->ExceptionAddress return "Misaligned data access";
<<" write?="<<r->ExceptionInformation[0] case EXCEPTION_BREAKPOINT:
<<" address="<<r->ExceptionInformation[1] return "Breakpoint reached";
<<std::endl; case EXCEPTION_SINGLE_STEP:
throw FatalSystemException return "Single debug step";
("Access violation"); case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
} return "Array access out of bounds";
if(u == EXCEPTION_STACK_OVERFLOW) case EXCEPTION_FLT_DENORMAL_OPERAND:
{ return "Denormal floating point operand";
throw FatalSystemException case EXCEPTION_FLT_DIVIDE_BY_ZERO:
("Stack overflow"); return "Floating point division by zero";
} case EXCEPTION_FLT_INEXACT_RESULT:
if(u == EXCEPTION_ILLEGAL_INSTRUCTION) return "Inaccurate floating point result";
{ case EXCEPTION_FLT_INVALID_OPERATION:
throw FatalSystemException return "Invalid floating point operation";
("Illegal instruction"); 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";
} }
#endif
long WINAPI Win32ExceptionHandler(struct _EXCEPTION_POINTERS *pExceptInfo)
{
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;
}
#endif #endif
void debug_set_exception_handler()
{
#ifdef _MSC_VER
SetUnhandledExceptionFilter(Win32ExceptionHandler);
#endif
}

View File

@ -95,6 +95,8 @@ __NORETURN extern void assert_fail(
#define assert(expr) ASSERT(expr) #define assert(expr) ASSERT(expr)
void debug_set_exception_handler();
/* /*
DebugStack DebugStack
*/ */
@ -118,13 +120,12 @@ private:
bool m_overflowed; bool m_overflowed;
}; };
#define DSTACK(msg)\ #define DSTACK(msg) \
DebugStacker __debug_stacker(msg); DebugStacker __debug_stacker(msg);
#define DSTACKF(...)\ #define DSTACKF(...) \
char __buf[DEBUG_STACK_TEXT_SIZE];\ char __buf[DEBUG_STACK_TEXT_SIZE]; \
snprintf(__buf,\ snprintf(__buf, DEBUG_STACK_TEXT_SIZE, __VA_ARGS__); \
DEBUG_STACK_TEXT_SIZE, __VA_ARGS__);\
DebugStacker __debug_stacker(__buf); DebugStacker __debug_stacker(__buf);
/* /*
@ -132,34 +133,13 @@ private:
*/ */
#if CATCH_UNHANDLED_EXCEPTIONS == 1 #if CATCH_UNHANDLED_EXCEPTIONS == 1
#define BEGIN_PORTABLE_DEBUG_EXCEPTION_HANDLER try{ #define BEGIN_DEBUG_EXCEPTION_HANDLER try {
#define END_PORTABLE_DEBUG_EXCEPTION_HANDLER(logstream)\ #define END_DEBUG_EXCEPTION_HANDLER(logstream) \
}catch(std::exception &e){\ } catch (std::exception &e) { \
logstream<<"ERROR: An unhandled exception occurred: "\ logstream << "ERROR: An unhandled exception occurred: " \
<<e.what()<<std::endl;\ << e.what() << std::endl; \
assert(0);\ 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 #else
// Dummy ones // Dummy ones
#define BEGIN_DEBUG_EXCEPTION_HANDLER #define BEGIN_DEBUG_EXCEPTION_HANDLER

View File

@ -110,12 +110,6 @@ public:
ServerError(const std::string &s): BaseException(s) {} 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 { class ClientStateError : public BaseException {
public: public:
ClientStateError(std::string s): BaseException(s) {} ClientStateError(std::string s): BaseException(s) {}

View File

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

View File

@ -419,6 +419,10 @@ inline const char * getPlatformName()
void setXorgClassHint(const video::SExposedVideoData &video_data, void setXorgClassHint(const video::SExposedVideoData &video_data,
const std::string &name); 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 } // namespace porting
#ifdef __ANDROID__ #ifdef __ANDROID__