From 57adcb38bd4aa405da2ef4e952f20bbccbde6617 Mon Sep 17 00:00:00 2001 From: Eduardo Bart Date: Wed, 21 Mar 2012 12:31:34 -0300 Subject: [PATCH] rework windows crash handler * use -Ofast -fomit-frame-pointer for release * new windows crash handler that can generate backtraces reports * fix crash after fatal errors --- src/framework/CMakeLists.txt | 66 +++---- src/framework/core/logger.cpp | 5 + src/framework/graphics/graphics.cpp | 1 - src/framework/platform/win32crashhandler.cpp | 180 +++++++++++++------ src/otclient/core/mapview.cpp | 1 - src/otclient/net/protocolcodes.h | 2 +- 6 files changed, 170 insertions(+), 85 deletions(-) diff --git a/src/framework/CMakeLists.txt b/src/framework/CMakeLists.txt index b401d9d8..8bed4704 100644 --- a/src/framework/CMakeLists.txt +++ b/src/framework/CMakeLists.txt @@ -7,7 +7,7 @@ ENDIF(${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 6) SET(CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake;${CMAKE_MODULE_PATH}") # framework options -OPTION(NO_CONSOLE "Disables console window on Windows platform" OFF) +OPTION(WINDOWS_CONSOLE "Enables console window on Windows platform" OFF) OPTION(CRASH_HANDLER "Generate crash reports" OFF) OPTION(USE_OPENGL_ES2 "Use OpenGL ES 2.0 (for mobiles devices)" OFF) @@ -35,27 +35,23 @@ FIND_PACKAGE(GMP REQUIRED) FIND_PACKAGE(ZLIB REQUIRED) # setup compiler options -IF(CMAKE_COMPILER_IS_GNUCXX) - SET(CXX_WARNS "-Wall -Wextra -Werror -Wno-unused-parameter -Wno-unused-but-set-variable") - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_WARNS} -std=gnu++0x -pipe") - SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${CXX_WARNS} -pipe") - SET(CMAKE_CXX_FLAGS_DEBUG "-O0 -g -ggdb") - SET(CMAKE_C_FLAGS_DEBUG "-O0 -g -ggdb") - SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O1 -g -ggdb") - SET(CMAKE_C_FLAGS_RELWITHDEBINFO "-O1 -g -ggdb") - SET(CMAKE_CXX_FLAGS_RELEASE "-O2") - SET(CMAKE_C_FLAGS_RELEASE "-O2") - #SET(CMAKE_CXX_FLAGS_PERFORMANCE "-Ofast -fomit-frame-pointer") - #SET(CMAKE_C_FLAGS_PERFORMANCE "-Ofast -fomit-frame-pointer") - SET(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -static-libgcc -static-libstdc++ -Wl,--as-needed") -ENDIF(CMAKE_COMPILER_IS_GNUCXX) +SET(CXX_WARNS "-Wall -Wextra -Werror -Wno-unused-parameter -Wno-unused-but-set-variable") +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_WARNS} -std=gnu++0x -pipe") +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${CXX_WARNS} -pipe") +SET(CMAKE_CXX_FLAGS_DEBUG "-O0 -ggdb") +SET(CMAKE_C_FLAGS_DEBUG "-O0 -ggdb") +SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O1 -ggdb") +SET(CMAKE_C_FLAGS_RELWITHDEBINFO "-O1 -ggdb") +SET(CMAKE_CXX_FLAGS_RELEASE "-Ofast -fomit-frame-pointer") +SET(CMAKE_C_FLAGS_RELEASE "-Ofast -fomit-frame-pointer") +SET(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -static-libgcc -static-libstdc++ -Wl,--as-needed") MESSAGE(STATUS "Build type: " ${CMAKE_BUILD_TYPE}) IF(USE_OPENGL_ES2) MESSAGE(STATUS "Renderer: OpenGL ES 2.0") -ELSE(USE_OPENGL_ES2) +ELSE() MESSAGE(STATUS "Renderer: OpenGL") -ENDIF(USE_OPENGL_ES2) +ENDIF() IF(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") ADD_DEFINITIONS(-DDEBUG) @@ -67,35 +63,43 @@ ENDIF() IF(CMAKE_BUILD_TYPE STREQUAL "Release") # NDEBUG disable asserts ADD_DEFINITIONS(-DNDEBUG) -ENDIF(CMAKE_BUILD_TYPE STREQUAL "Release") + # strip all debug information + SET(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -s") + IF(CRASH_HANDLER) + MESSAGE(SEND_ERROR "Crash handler cannot be enabled in release mode.") + ENDIF() +ENDIF() IF(CRASH_HANDLER) ADD_DEFINITIONS(-DCRASH_HANDLER) MESSAGE(STATUS "Crash handler: ON") -ELSE(CRASH_HANDLER) +ELSE() MESSAGE(STATUS "Crash handler: OFF") -ENDIF(CRASH_HANDLER) +ENDIF() IF(WIN32) + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mthreads") + ADD_DEFINITIONS(-D_WIN32_WINNT=0x0501) + SET(ADDITIONAL_LIBRARIES ws2_32 mswsock imagehlp) + SET(framework_SOURCES ${framework_SOURCES} ${CMAKE_CURRENT_LIST_DIR}/platform/win32window.cpp ${CMAKE_CURRENT_LIST_DIR}/platform/win32crashhandler.cpp) - SET(ADDITIONAL_LIBRARIES ws2_32 mswsock) - ADD_DEFINITIONS(-D_WIN32_WINNT=0x0501) - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mthreads") + IF(CRASH_HANDLER) + SET(ADDITIONAL_LIBRARIES ${ADDITIONAL_LIBRARIES} imagehlp) + ENDIF() - IF(NO_CONSOLE) - IF(CMAKE_COMPILER_IS_GNUCXX) - SET(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -mwindows") - ENDIF(CMAKE_COMPILER_IS_GNUCXX) - MESSAGE(STATUS "Disable windows console: ON") - ELSE(NO_CONSOLE) - MESSAGE(STATUS "Disable windows console: OFF") - ENDIF(NO_CONSOLE) + IF(WINDOWS_CONSOLE) + MESSAGE(STATUS "Windows console: ON") + ELSE() + SET(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -mwindows") + MESSAGE(STATUS "Windows console: OFF") + ENDIF() ELSE(WIN32) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") SET(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -rdynamic") + SET(ADDITIONAL_LIBRARIES X11 dl) SET(framework_SOURCES ${framework_SOURCES} ${CMAKE_CURRENT_LIST_DIR}/platform/x11window.cpp diff --git a/src/framework/core/logger.cpp b/src/framework/core/logger.cpp index f6101cc1..2513ced3 100644 --- a/src/framework/core/logger.cpp +++ b/src/framework/core/logger.cpp @@ -28,6 +28,10 @@ Logger g_logger; void Logger::log(Fw::LogLevel level, const std::string& message) { + static bool ignoreLogs = false; + if(ignoreLogs) + return; + const static std::string logPrefixes[] = { "", "", "WARNING: ", "ERROR: ", "FATAL ERROR: " }; std::string outmsg = logPrefixes[level] + message; @@ -41,6 +45,7 @@ void Logger::log(Fw::LogLevel level, const std::string& message) if(level == Fw::LogFatal) { g_window.displayFatalError(message); + ignoreLogs = true; exit(-1); } } diff --git a/src/framework/graphics/graphics.cpp b/src/framework/graphics/graphics.cpp index 999b53cc..a791af5e 100644 --- a/src/framework/graphics/graphics.cpp +++ b/src/framework/graphics/graphics.cpp @@ -43,7 +43,6 @@ void Graphics::init() !GLEW_ARB_fragment_program || !GLEW_ARB_fragment_shader || !GLEW_ARB_texture_non_power_of_two || !GLEW_ARB_multitexture) logFatal("Some OpenGL 2.0 extensions is not supported by your system graphics, please try updating your video drivers or buy a new hardware."); - m_useFBO = GLEW_ARB_framebuffer_object; m_useBilinearFiltering = true; m_generateMipmaps = true; diff --git a/src/framework/platform/win32crashhandler.cpp b/src/framework/platform/win32crashhandler.cpp index ad01bcff..a1fc0909 100644 --- a/src/framework/platform/win32crashhandler.cpp +++ b/src/framework/platform/win32crashhandler.cpp @@ -22,64 +22,142 @@ #include "crashhandler.h" #include -#include -#include #include -typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)( - HANDLE hProcess, - DWORD ProcessId, - HANDLE hFile, - MINIDUMP_TYPE DumpType, - PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, - PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, - PMINIDUMP_CALLBACK_INFORMATION CallbackParam); +#include +#include +#include -LONG WINAPI crashHandler(EXCEPTION_POINTERS* exceptionPointers) +const char *getExceptionName(DWORD exceptionCode) { - logError("Application crashed"); - HMODULE hDbgHelp = LoadLibraryA("DBGHELP.DLL"); - char fileName[128]; - - if(hDbgHelp) { - MINIDUMPWRITEDUMP minuDumpWriteDump = (MINIDUMPWRITEDUMP)GetProcAddress(hDbgHelp, "MiniDumpWriteDump"); - - SYSTEMTIME systemTime; - GetSystemTime(&systemTime); - snprintf(fileName, 128, "%s_%02u-%02u-%04u_%02u-%02u-%02u.mdmp", g_app->getName().c_str(), - systemTime.wDay, systemTime.wMonth, systemTime.wYear, - systemTime.wHour, systemTime.wMinute, systemTime.wSecond); - - HANDLE hFile = CreateFileA(fileName, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if(hFile) { - MINIDUMP_EXCEPTION_INFORMATION exceptionInformation; - exceptionInformation.ClientPointers = FALSE; - exceptionInformation.ExceptionPointers = exceptionPointers; - exceptionInformation.ThreadId = GetCurrentThreadId(); - - HANDLE hProcess = GetCurrentProcess(); - DWORD ProcessId = GetProcessId(hProcess); - MINIDUMP_TYPE flags = (MINIDUMP_TYPE)(MiniDumpNormal); - - BOOL dumpResult = minuDumpWriteDump(hProcess, ProcessId, hFile, flags, &exceptionInformation, NULL, NULL); - if(!dumpResult){ - logError("Cannot generate minidump: ", GetLastError()); - CloseHandle(hFile); - DeleteFileA(fileName); - return EXCEPTION_CONTINUE_SEARCH; - } else { - logInfo("Crash minidump genarated on file ", fileName); - } - } else { - logError("Cannot create dump file: ", GetLastError()); - } - } else { - logError("Cannot create dump file: dbghlp.dll not found"); + switch (exceptionCode) { + case EXCEPTION_ACCESS_VIOLATION: return "Access violation"; + case EXCEPTION_DATATYPE_MISALIGNMENT: return "Datatype misalignment"; + case EXCEPTION_BREAKPOINT: return "Breakpoint"; + case EXCEPTION_SINGLE_STEP: return "Single step"; + case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: return "Array bounds exceeded"; + case EXCEPTION_FLT_DENORMAL_OPERAND: return "Float denormal operand"; + case EXCEPTION_FLT_DIVIDE_BY_ZERO: return "Float divide by zero"; + case EXCEPTION_FLT_INEXACT_RESULT: return "Float inexact result"; + case EXCEPTION_FLT_INVALID_OPERATION: return "Float invalid operation"; + case EXCEPTION_FLT_OVERFLOW: return "Float overflow"; + case EXCEPTION_FLT_STACK_CHECK: return "Float stack check"; + case EXCEPTION_FLT_UNDERFLOW: return "Float underflow"; + case EXCEPTION_INT_DIVIDE_BY_ZERO: return "Integer divide by zero"; + case EXCEPTION_INT_OVERFLOW: return "Integer overflow"; + case EXCEPTION_PRIV_INSTRUCTION: return "Privileged instruction"; + case EXCEPTION_IN_PAGE_ERROR: return "In page error"; + case EXCEPTION_ILLEGAL_INSTRUCTION: return "Illegal instruction"; + case EXCEPTION_NONCONTINUABLE_EXCEPTION: return "Noncontinuable exception"; + case EXCEPTION_STACK_OVERFLOW: return "Stack overflow"; + case EXCEPTION_INVALID_DISPOSITION: return "Invalid disposition"; + case EXCEPTION_GUARD_PAGE: return "Guard page"; + case EXCEPTION_INVALID_HANDLE: return "Invalid handle"; } - return EXCEPTION_CONTINUE_SEARCH; + return "Unknown exception"; +} + +void Stacktrace(LPEXCEPTION_POINTERS e, std::stringstream& ss) +{ + PIMAGEHLP_SYMBOL pSym; + STACKFRAME sf; + HANDLE process, thread; + DWORD dwModBase, Disp; + BOOL more = FALSE; + int count = 0; + char modname[MAX_PATH]; + + pSym = (PIMAGEHLP_SYMBOL)GlobalAlloc(GMEM_FIXED, 16384); + + ZeroMemory(&sf, sizeof(sf)); + sf.AddrPC.Offset = e->ContextRecord->Eip; + sf.AddrStack.Offset = e->ContextRecord->Esp; + sf.AddrFrame.Offset = e->ContextRecord->Ebp; + sf.AddrPC.Mode = AddrModeFlat; + sf.AddrStack.Mode = AddrModeFlat; + sf.AddrFrame.Mode = AddrModeFlat; + + process = GetCurrentProcess(); + thread = GetCurrentThread(); + + while(1) { + more = StackWalk(IMAGE_FILE_MACHINE_I386, process, thread, &sf, e->ContextRecord, NULL, SymFunctionTableAccess, SymGetModuleBase, NULL); + if(!more || sf.AddrFrame.Offset == 0) + break; + + dwModBase = SymGetModuleBase(process, sf.AddrPC.Offset); + + if(dwModBase) + GetModuleFileName((HINSTANCE)dwModBase, modname, MAX_PATH); + else + strcpy(modname, "Unknown"); + + pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL); + pSym->MaxNameLength = MAX_PATH; + + if(SymGetSymFromAddr(process, sf.AddrPC.Offset, &Disp, pSym)) { + // this is the code path taken on VC if debugging syms are found. + ss << Fw::formatString(" (%d) %s(%s+%#0lx) [0x%08lX]\n", count, modname, pSym->Name, Disp, sf.AddrPC.Offset); + } else { + // this is the code path taken on MinGW, and VC if no debugging syms are found. + ss << Fw::formatString(" (%d) %s [0x%08lX]\n", count, modname, sf.AddrPC.Offset); + } + ++count; + } + GlobalFree(pSym); +} + +LONG CALLBACK ExceptionHandler(LPEXCEPTION_POINTERS e) +{ + char date[32]; + time_t tnow; + time(&tnow); + tm *ts = localtime(&tnow); + strftime(date, 32, "%b %d %Y %H:%M:%S", ts); + + // generate crash report + SymInitialize(GetCurrentProcess(), 0, TRUE); + std::stringstream ss; + ss << "== application crashed\n"; + ss << Fw::formatString("app name: %s\n", g_app->getName().c_str()); + ss << Fw::formatString("app version: %s\n", g_app->getVersion().c_str()); + ss << Fw::formatString("app build date: %s\n", g_app->getBuildDate().c_str()); + ss << Fw::formatString("crash date: %s\n", date); + ss << Fw::formatString("exception: %s (0x%08lx)\n", getExceptionName(e->ExceptionRecord->ExceptionCode), e->ExceptionRecord->ExceptionCode); + ss << Fw::formatString("exception address: 0x%08lx\n", (long unsigned int)e->ExceptionRecord->ExceptionAddress); + ss << Fw::formatString("backtrace:\n"); + Stacktrace(e, ss); + ss << "\n"; + SymCleanup(GetCurrentProcess()); + + // write stacktrace to crash.txt + char dir[MAX_PATH]; + GetCurrentDirectory(sizeof(dir) - 1, dir); + std::string fileName = Fw::formatString("%s\\crash_report.txt", dir); + + + std::ofstream fout(fileName.c_str(), std::ios_base::out | std::ios_base::ate); + fout << ss.str(); + fout.close(); + + // print in stdout + logInfo(ss.str()); + logInfo("Crash report saved to file ", fileName); + + // inform the user + std::string msg = Fw::formatString("The application has crashed.\n\n" + "A crash report has been written to:\n" + "%s", fileName.c_str()); + MessageBox(NULL, msg.c_str(), "Application crashed", 0); + + // this seems to silently close the application + return EXCEPTION_EXECUTE_HANDLER; + + // this triggers the microsoft "application has crashed" error dialog + //return EXCEPTION_CONTINUE_SEARCH; } void installCrashHandler() { - SetUnhandledExceptionFilter(crashHandler); + SetUnhandledExceptionFilter(ExceptionHandler); } diff --git a/src/otclient/core/mapview.cpp b/src/otclient/core/mapview.cpp index ee0c4e9d..91990366 100644 --- a/src/otclient/core/mapview.cpp +++ b/src/otclient/core/mapview.cpp @@ -415,7 +415,6 @@ void MapView::setVisibleDimension(const Size& visibleDimension) // found a valid size if(candidateDrawSize.width() <= framebufferSize.width() && candidateDrawSize.height() <= framebufferSize.height()) { tileSize = candidateTileSize; - dump << candidateDrawSize << m_framebuffer->getSize() << tileSize; break; } } diff --git a/src/otclient/net/protocolcodes.h b/src/otclient/net/protocolcodes.h index a0d9ce06..3b8f6d42 100644 --- a/src/otclient/net/protocolcodes.h +++ b/src/otclient/net/protocolcodes.h @@ -25,7 +25,7 @@ #include -#if PROTOCOL != 860 && PROTOCOL != 870 && PROTOCOL != 862 && PROTOCOL != 870 +#if PROTOCOL != 860 && PROTOCOL != 861 && PROTOCOL != 862 && PROTOCOL != 870 #error "the supplied protocol version is not supported" #endif