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