Create minidump on fatal Win32 exceptions
authorkwolekr <kwolekr@minetest.net>
Sun, 1 Feb 2015 08:08:04 +0000 (03:08 -0500)
committerkwolekr <kwolekr@minetest.net>
Mon, 2 Feb 2015 06:39:17 +0000 (01:39 -0500)
Remove software exception translator function, simplifying exception handler
macros.  FatalSystemExceptions are left unhandled.

src/CMakeLists.txt
src/debug.cpp
src/debug.h
src/exceptions.h
src/main.cpp
src/porting.h

index 848eb8d3eaa0de56d10b0276516d97751513c3dd..93083f3694586747e80a1a8c3d9fba5597c3f704 100644 (file)
@@ -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
index 3b2fb641a132eb76b34449b64f29ac536c561c28..bd970a8e4b953de49265dad7d5f7e99b75a51497 100644 (file)
@@ -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
 */
@@ -57,7 +64,7 @@ void debugstreams_init(bool disable_stderr, const char *filename)
 
        if(filename)
                g_debugstreams[1] = fopen(filename, "a");
-               
+
        if(g_debugstreams[1])
        {
                fprintf(g_debugstreams[1], "\n\n-------------\n");
@@ -91,7 +98,7 @@ public:
                        //TODO: Is this slow?
                        fflush(g_debugstreams[i]);
                }
-               
+
                return c;
        }
        std::streamsize xsputn(const char *s, std::streamsize n)
@@ -111,7 +118,7 @@ public:
 
                return n;
        }
-       
+
 private:
        bool m_disable_stderr;
 };
@@ -133,7 +140,7 @@ void assert_fail(const char *assertion, const char *file,
                        "%s:%u: %s: Assertion '%s' failed.\n",
                        (unsigned long)get_current_thread_id(),
                        file, line, function, assertion);
-       
+
        debug_stacks_print();
 
        if(g_debugstreams[1])
@@ -151,7 +158,7 @@ struct DebugStack
        DebugStack(threadid_t id);
        void print(FILE *file, bool everything);
        void print(std::ostream &os, bool everything);
-       
+
        threadid_t threadid;
        char stack[DEBUG_STACK_SIZE][DEBUG_STACK_TEXT_SIZE];
        int stack_i; // Points to the lowest empty position
@@ -285,10 +292,10 @@ DebugStacker::DebugStacker(const char *text)
 DebugStacker::~DebugStacker()
 {
        JMutexAutoLock lock(g_debug_stacks_mutex);
-       
+
        if(m_overflowed == true)
                return;
-       
+
        m_stack->stack_i--;
 
        if(m_stack->stack_i == 0)
@@ -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)
-       {
-               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");
-       }
-       if(u == EXCEPTION_STACK_OVERFLOW)
-       {
-               throw FatalSystemException
-               ("Stack overflow");
-       }
-       if(u == EXCEPTION_ILLEGAL_INSTRUCTION)
-       {
-               throw FatalSystemException
-               ("Illegal instruction");
+       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";
 }
-#endif
-#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
+
+void debug_set_exception_handler()
+{
+#ifdef _MSC_VER
+       SetUnhandledExceptionFilter(Win32ExceptionHandler);
+#endif
+}
 
index ba2e8704ebd1cc7c892dcec67618c6009f75035a..1027fde695b89703b84ef6a6e167874e0d69f465 100644 (file)
@@ -95,6 +95,8 @@ __NORETURN extern void assert_fail(
 
 #define assert(expr) ASSERT(expr)
 
+void debug_set_exception_handler();
+
 /*
        DebugStack
 */
@@ -118,13 +120,12 @@ private:
        bool m_overflowed;
 };
 
-#define DSTACK(msg)\
+#define DSTACK(msg) \
        DebugStacker __debug_stacker(msg);
 
-#define DSTACKF(...)\
-       char __buf[DEBUG_STACK_TEXT_SIZE];\
-       snprintf(__buf,\
-                       DEBUG_STACK_TEXT_SIZE, __VA_ARGS__);\
+#define DSTACKF(...) \
+       char __buf[DEBUG_STACK_TEXT_SIZE];                   \
+       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)\
-               }catch(std::exception &e){\
-                       logstream<<"ERROR: An unhandled exception occurred: "\
-                                       <<e.what()<<std::endl;\
-                       assert(0);\
+       #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
index abd8c68ae31ee9b0917eb8fd529e6b826a425f16..5b716c2ca862691fdf195b946e43fded2dddf62e 100644 (file)
@@ -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) {}
index 173050fed3c88e2ebeec49cac08418904d2fab2e..e7108a3e8e5b0e66e97e541d2ad3b9ab1ffdd76b 100644 (file)
@@ -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);
 
index e6574494a28753d4dda16efe759af0349f512809..8387453e8de589920ea336d21eee5ce3a66176f2 100644 (file)
@@ -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__