diff --git a/CMakeLists.txt b/CMakeLists.txt index 93d5c976..c7e2b8ec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,7 @@ SET(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake;${CMAKE_MODULE_PATH}") OPTION(USE_PCH "Use precompiled header" ON) OPTION(NO_CONSOLE "Disable console window on Windows" OFF) +OPTION(HANDLE_EXCEPTIONS "Generate crash reports" OFF) # find needed packages SET(Boost_USE_STATIC_LIBS ON) @@ -136,6 +137,10 @@ SET(SOURCES src/framework/ui/uilayout.cpp ) +IF(HANDLE_EXCEPTIONS) + ADD_DEFINITIONS(-DHANDLE_EXCEPTIONS) +ENDIF(HANDLE_EXCEPTIONS) + IF(WIN32) SET(SOURCES ${SOURCES} src/framework/platform/win32platform.cpp) SET(ADDITIONAL_LIBRARIES ws2_32) diff --git a/src/framework/platform/platform.h b/src/framework/platform/platform.h index 153cf5fb..1127c823 100644 --- a/src/framework/platform/platform.h +++ b/src/framework/platform/platform.h @@ -81,7 +81,6 @@ public: /// Get the app user directory, the place to save files configurations files std::string getAppUserDir(); - std::string generateBacktrace(int maxLevel = 100); private: int m_lastTicks; diff --git a/src/framework/platform/win32platform.cpp b/src/framework/platform/win32platform.cpp index 766901c8..fb92f6b4 100644 --- a/src/framework/platform/win32platform.cpp +++ b/src/framework/platform/win32platform.cpp @@ -25,7 +25,6 @@ #include #include - #include LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); @@ -47,15 +46,76 @@ struct Win32PlatformPrivate { Platform g_platform; +#ifdef HANDLE_EXCEPTIONS +#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); + +LONG WINAPI crashHandler(EXCEPTION_POINTERS* exceptionPointers) +{ + 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", win32.appName.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"); + } + return EXCEPTION_CONTINUE_SEARCH; +} +#endif + void Platform::init(PlatformListener* platformListener, const char *appName) { + // install crash handler +#ifdef HANDLE_EXCEPTIONS + SetUnhandledExceptionFilter(crashHandler); +#endif + // seend random numbers std::srand(std::time(NULL)); win32.appName = appName; win32.instance = GetModuleHandle(NULL); win32.listener = platformListener; - win32.keyMap[VK_ESCAPE] = Fw::KeyEscape; win32.keyMap[VK_TAB] = Fw::KeyTab; win32.keyMap[VK_RETURN] = Fw::KeyReturn; diff --git a/src/framework/platform/x11platform.cpp b/src/framework/platform/x11platform.cpp index c5dbbcf8..de87d4cd 100644 --- a/src/framework/platform/x11platform.cpp +++ b/src/framework/platform/x11platform.cpp @@ -70,8 +70,102 @@ struct X11PlatformPrivate { Platform g_platform; +#ifdef HANDLE_EXCEPTIONS + +#define MAX_BACKTRACE_DEPTH 128 +#define DEMANGLE_BACKTRACE_SYMBOLS + +#include + +void crashHandler(int signum, siginfo_t* info, void* secret) +{ + logError("Application crashed"); + + ucontext_t context = *(ucontext_t*)secret; + time_t tnow; + char fileName[128]; + time(&tnow); + tm *ts = localtime(&tnow); + strftime(fileName, 128, (x11.appName + "-crash_-%d-%m-%Y_%H:%M:%S.txt").c_str(), ts); + + std::stringstream ss; + ss.flags(std::ios::hex | std::ios::showbase); +#if __WORDSIZE == 32 + ss << + ss << " at eip = " << context.uc_mcontext.gregs[REG_EIP] << std::endl; + ss << " eax = " << context.uc_mcontext.gregs[REG_EAX] << std::endl; + ss << " ebx = " << context.uc_mcontext.gregs[REG_EBX] << std::endl; + ss << " ecx = " << context.uc_mcontext.gregs[REG_ECX] << std::endl; + ss << " edx = " << context.uc_mcontext.gregs[REG_EDX] << std::endl; + ss << " esi = " << context.uc_mcontext.gregs[REG_ESI] << std::endl; + ss << " edi = " << context.uc_mcontext.gregs[REG_EDI] << std::endl; + ss << " ebp = " << context.uc_mcontext.gregs[REG_EBP] << std::endl; + ss << " esp = " << context.uc_mcontext.gregs[REG_ESP] << std::endl; + ss << " efl = " << context.uc_mcontext.gregs[REG_EFL] << std::endl; +#else // 64-bit + ss << " at rip = " << context.uc_mcontext.gregs[REG_RIP] << std::endl; + ss << " rax = " << context.uc_mcontext.gregs[REG_RAX] << std::endl; + ss << " rbx = " << context.uc_mcontext.gregs[REG_RBX] << std::endl; + ss << " rcx = " << context.uc_mcontext.gregs[REG_RCX] << std::endl; + ss << " rdx = " << context.uc_mcontext.gregs[REG_RDX] << std::endl; + ss << " rsi = " << context.uc_mcontext.gregs[REG_RSI] << std::endl; + ss << " rdi = " << context.uc_mcontext.gregs[REG_RDI] << std::endl; + ss << " rbp = " << context.uc_mcontext.gregs[REG_RBP] << std::endl; + ss << " rsp = " << context.uc_mcontext.gregs[REG_RSP] << std::endl; + ss << " efl = " << context.uc_mcontext.gregs[REG_EFL] << std::endl; +#endif + ss << std::endl; + ss.flags(std::ios::dec); + ss << " backtrace:" << std::endl; + + void* buffer[MAX_BACKTRACE_DEPTH]; + int numLevels = backtrace(buffer, MAX_BACKTRACE_DEPTH); + char **tracebackBuffer = backtrace_symbols(buffer, numLevels); + if(tracebackBuffer) { + for(int i = 2; i < numLevels; i++) { + std::string line = tracebackBuffer[i]; + if(line.find("__libc_start_main") != std::string::npos) + break; +#ifdef DEMANGLE_BACKTRACE_SYMBOLS + std::size_t demanglePos = line.find("(_Z"); + if(demanglePos != std::string::npos) { + demanglePos++; + int len = std::min(line.find_first_of("+", demanglePos), line.find_first_of(")", demanglePos)) - demanglePos; + std::string funcName = line.substr(demanglePos, len); + line.replace(demanglePos, len, Fw::demangleName(funcName.c_str())); + } +#endif + ss << " " << i-1 << ": " << line << std::endl; + } + free(tracebackBuffer); + } + + logInfo(ss.str()); + + std::ofstream out(fileName); + out << ss.str(); + out.close(); + logInfo("Crash report saved to file ", fileName); + + signal(SIGILL, SIG_DFL); + signal(SIGSEGV, SIG_DFL); + signal(SIGFPE, SIG_DFL); +} +#endif + void Platform::init(PlatformListener* platformListener, const char *appName) { +#ifdef HANDLE_EXCEPTIONS + struct sigaction sa; + sa.sa_sigaction = &crashHandler; + sigemptyset (&sa.sa_mask); + sa.sa_flags = SA_RESTART | SA_SIGINFO; + + sigaction(SIGILL, &sa, NULL); // illegal instruction + sigaction(SIGSEGV, &sa, NULL); // segmentation fault + sigaction(SIGFPE, &sa, NULL); // floating-point exception +#endif + // seend random numbers srand(time(NULL)); @@ -868,30 +962,3 @@ std::string Platform::getAppUserDir() logError("Couldn't create directory for saving configuration file. (",sdir.str(),")"); return sdir.str(); } - -std::string Platform::generateBacktrace(int maxLevel) -{ - std::stringstream ss; - std::vector buffer(maxLevel); - int numLevels = backtrace(&buffer[0], maxLevel); - char **tracebackBuffer = backtrace_symbols(&buffer[0], numLevels); - if(tracebackBuffer) { - for(int i = 1; i < numLevels; i++) { - std::string line = tracebackBuffer[i]; - if(line.find("__libc_start_main") != std::string::npos) - break; - std::size_t demanglePos = line.find("(_Z"); - if(demanglePos != std::string::npos) { - demanglePos++; - int len = std::min(line.find_first_of("+", demanglePos), line.find_first_of(")", demanglePos)) - demanglePos; - std::string funcName = line.substr(demanglePos, len); - line.replace(demanglePos, len, Fw::demangleName(funcName.c_str())); - } - if(i > 1) - ss << "\n"; - ss << i << ": " << line; - } - free(tracebackBuffer); - } - return ss.str(); -}