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(USE_PCH "Use precompiled header" ON)
|
||||||
OPTION(NO_CONSOLE "Disable console window on Windows" OFF)
|
OPTION(NO_CONSOLE "Disable console window on Windows" OFF)
|
||||||
|
OPTION(HANDLE_EXCEPTIONS "Generate crash reports" OFF)
|
||||||
|
|
||||||
# find needed packages
|
# find needed packages
|
||||||
SET(Boost_USE_STATIC_LIBS ON)
|
SET(Boost_USE_STATIC_LIBS ON)
|
||||||
|
@ -136,6 +137,10 @@ SET(SOURCES
|
||||||
src/framework/ui/uilayout.cpp
|
src/framework/ui/uilayout.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
IF(HANDLE_EXCEPTIONS)
|
||||||
|
ADD_DEFINITIONS(-DHANDLE_EXCEPTIONS)
|
||||||
|
ENDIF(HANDLE_EXCEPTIONS)
|
||||||
|
|
||||||
IF(WIN32)
|
IF(WIN32)
|
||||||
SET(SOURCES ${SOURCES} src/framework/platform/win32platform.cpp)
|
SET(SOURCES ${SOURCES} src/framework/platform/win32platform.cpp)
|
||||||
SET(ADDITIONAL_LIBRARIES ws2_32)
|
SET(ADDITIONAL_LIBRARIES ws2_32)
|
||||||
|
|
|
@ -81,7 +81,6 @@ public:
|
||||||
|
|
||||||
/// Get the app user directory, the place to save files configurations files
|
/// Get the app user directory, the place to save files configurations files
|
||||||
std::string getAppUserDir();
|
std::string getAppUserDir();
|
||||||
std::string generateBacktrace(int maxLevel = 100);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int m_lastTicks;
|
int m_lastTicks;
|
||||||
|
|
|
@ -25,7 +25,6 @@
|
||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <dir.h>
|
#include <dir.h>
|
||||||
|
|
||||||
#include <physfs.h>
|
#include <physfs.h>
|
||||||
|
|
||||||
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
||||||
|
@ -47,15 +46,76 @@ struct Win32PlatformPrivate {
|
||||||
|
|
||||||
Platform g_platform;
|
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)
|
void Platform::init(PlatformListener* platformListener, const char *appName)
|
||||||
{
|
{
|
||||||
|
// install crash handler
|
||||||
|
#ifdef HANDLE_EXCEPTIONS
|
||||||
|
SetUnhandledExceptionFilter(crashHandler);
|
||||||
|
#endif
|
||||||
|
|
||||||
// seend random numbers
|
// seend random numbers
|
||||||
std::srand(std::time(NULL));
|
std::srand(std::time(NULL));
|
||||||
|
|
||||||
win32.appName = appName;
|
win32.appName = appName;
|
||||||
win32.instance = GetModuleHandle(NULL);
|
win32.instance = GetModuleHandle(NULL);
|
||||||
win32.listener = platformListener;
|
win32.listener = platformListener;
|
||||||
|
|
||||||
win32.keyMap[VK_ESCAPE] = Fw::KeyEscape;
|
win32.keyMap[VK_ESCAPE] = Fw::KeyEscape;
|
||||||
win32.keyMap[VK_TAB] = Fw::KeyTab;
|
win32.keyMap[VK_TAB] = Fw::KeyTab;
|
||||||
win32.keyMap[VK_RETURN] = Fw::KeyReturn;
|
win32.keyMap[VK_RETURN] = Fw::KeyReturn;
|
||||||
|
|
|
@ -70,8 +70,102 @@ struct X11PlatformPrivate {
|
||||||
|
|
||||||
Platform g_platform;
|
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)
|
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
|
// seend random numbers
|
||||||
srand(time(NULL));
|
srand(time(NULL));
|
||||||
|
|
||||||
|
@ -868,30 +962,3 @@ std::string Platform::getAppUserDir()
|
||||||
logError("Couldn't create directory for saving configuration file. (",sdir.str(),")");
|
logError("Couldn't create directory for saving configuration file. (",sdir.str(),")");
|
||||||
return 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