add exception handler for windows/linux platforms that will generate backtrace reports when the application crashes

This commit is contained in:
Eduardo Bart 2011-10-31 14:57:14 -02:00
parent ae6cd41819
commit e9d69b7980
4 changed files with 161 additions and 30 deletions

View File

@ -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)

View File

@ -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;

View File

@ -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;

View File

@ -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();
}