diff options
-rw-r--r-- | src/CMakeLists.txt | 5 | ||||
-rw-r--r-- | src/debug.cpp | 160 | ||||
-rw-r--r-- | src/debug.h | 44 | ||||
-rw-r--r-- | src/exceptions.h | 6 | ||||
-rw-r--r-- | src/main.cpp | 2 | ||||
-rw-r--r-- | src/porting.h | 4 |
6 files changed, 149 insertions, 72 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 848eb8d3e..93083f369 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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 diff --git a/src/debug.cpp b/src/debug.cpp index 3b2fb641a..bd970a8e4 100644 --- a/src/debug.cpp +++ b/src/debug.cpp @@ -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 +} diff --git a/src/debug.h b/src/debug.h index ba2e8704e..1027fde69 100644 --- a/src/debug.h +++ b/src/debug.h @@ -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 diff --git a/src/exceptions.h b/src/exceptions.h index abd8c68ae..5b716c2ca 100644 --- a/src/exceptions.h +++ b/src/exceptions.h @@ -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) {} diff --git a/src/main.cpp b/src/main.cpp index 173050fed..e7108a3e8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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); diff --git a/src/porting.h b/src/porting.h index e6574494a..8387453e8 100644 --- a/src/porting.h +++ b/src/porting.h @@ -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__ |