add exception handler for windows/linux platforms that will generate backtrace reports when the application crashes
This commit is contained in:
		
							parent
							
								
									ae6cd41819
								
							
						
					
					
						commit
						e9d69b7980
					
				|  | @ -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) | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -25,7 +25,6 @@ | |||
| 
 | ||||
| #include <windows.h> | ||||
| #include <dir.h> | ||||
| 
 | ||||
| #include <physfs.h> | ||||
| 
 | ||||
| LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); | ||||
|  | @ -47,15 +46,76 @@ struct Win32PlatformPrivate { | |||
| 
 | ||||
| Platform g_platform; | ||||
| 
 | ||||
| #ifdef HANDLE_EXCEPTIONS | ||||
| #include <dbghelp.h> | ||||
| 
 | ||||
| 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; | ||||
|  |  | |||
|  | @ -70,8 +70,102 @@ struct X11PlatformPrivate { | |||
| 
 | ||||
| Platform g_platform; | ||||
| 
 | ||||
| #ifdef HANDLE_EXCEPTIONS | ||||
| 
 | ||||
| #define MAX_BACKTRACE_DEPTH 128 | ||||
| #define DEMANGLE_BACKTRACE_SYMBOLS | ||||
| 
 | ||||
| #include <csignal> | ||||
| 
 | ||||
| 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<void*> 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(); | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Eduardo Bart
						Eduardo Bart