Skip to content

Commit

Permalink
Add Windows crash handling
Browse files Browse the repository at this point in the history
  • Loading branch information
stschake committed Feb 14, 2017
1 parent 45d9d22 commit 4ae75bf
Show file tree
Hide file tree
Showing 2 changed files with 147 additions and 1 deletion.
140 changes: 140 additions & 0 deletions src/Core/WindowsCrashHandler.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
// Only for Windows 64 Bit and MSVC
#if defined(_MSC_VER) && defined(_WIN64)

#include <fstream>
#include <cstdint>
#include <chrono>
#include <ctime>
#include <iomanip>
#include <string>
#include <sstream>

#include <windows.h>
#include <dbghelp.h>
#pragma comment(lib, "dbghelp.lib")

static std::string getCrashFileName()
{
using namespace std::chrono;
auto now = system_clock::now();
auto nowTimeT = system_clock::to_time_t(now);
std::stringstream ret;
ret << "crash_" << std::put_time(std::localtime(&nowTimeT), "%H%M_%d%m%y");
return ret.str();
}

static LONG WINAPI ExceptionHandler(LPEXCEPTION_POINTERS exception)
{
auto crashFileName = getCrashFileName();
HANDLE process = GetCurrentProcess();

auto dumpFileName = crashFileName + ".dmp";
HANDLE dumpFile = CreateFileA(dumpFileName.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (dumpFile != INVALID_HANDLE_VALUE)
{
MINIDUMP_EXCEPTION_INFORMATION minidumpException = {0};
minidumpException.ThreadId = GetCurrentThreadId();
minidumpException.ExceptionPointers = exception;
minidumpException.ClientPointers = false;

MiniDumpWriteDump(process, GetCurrentProcessId(), dumpFile,
(MINIDUMP_TYPE)(MiniDumpNormal | MiniDumpFilterMemory | MiniDumpFilterModulePaths | MiniDumpWithoutOptionalData),
&minidumpException, NULL, NULL);

FlushFileBuffers(dumpFile);
CloseHandle(dumpFile);
}

SymInitialize(process, NULL, TRUE);
SymSetOptions(SYMOPT_LOAD_LINES);

PCONTEXT context = exception->ContextRecord;
STACKFRAME64 curStackFrame = {0};
curStackFrame.AddrPC.Offset = context->Rip;
curStackFrame.AddrPC.Mode = AddrModeFlat;
curStackFrame.AddrFrame.Offset = context->Rbp;
curStackFrame.AddrFrame.Mode = AddrModeFlat;
curStackFrame.AddrStack.Offset = context->Rsp;
curStackFrame.AddrStack.Mode = AddrModeFlat;

auto crashLogName = crashFileName + ".log";
std::ofstream logFile(crashLogName, std::ofstream::trunc);
logFile << "Unhandled exception 0x" << std::hex << exception->ExceptionRecord->ExceptionCode << std::dec << std::endl;
logFile << "RAX=" << std::hex << context->Rax << " RCX=" << context->Rcx << " RDX=" << context->Rdx << " RBX=" << context->Rbx << std::endl;
logFile << "RSP=" << context->Rsp << " RBP=" << context->Rbp << " RSI=" << context->Rsi << " RDI=" << context->Rdi << std::endl;
logFile << "R8=" << context->R8 << " R9=" << context->R9 << " R10=" << context->R10 << " R11=" << context->R11 << std::endl;
logFile << "R12=" << context->R12 << " R13=" << context->R13 << " R14=" << context->R14 << " R15=" << context->R15 << std::dec << std::endl;
logFile << std::endl;

while (StackWalk64(IMAGE_FILE_MACHINE_AMD64, process, GetCurrentThread(),
&curStackFrame, context, NULL, SymFunctionTableAccess, SymGetModuleBase, NULL))
{
DWORD64 module = SymGetModuleBase64(process, curStackFrame.AddrPC.Offset);
char moduleBuffer[MAX_PATH] = {0};
const char* moduleName = "[unknown module]";
int fullLength = 0;
if (module != 0 && (fullLength = GetModuleFileNameA((HMODULE) module, moduleBuffer, MAX_PATH)) != 0) {
moduleName = moduleBuffer;
// we're only interested in the name, not the full path
const char* shortName = strrchr(moduleName, '\\');
if (shortName != NULL && shortName < (moduleBuffer+fullLength-1))
moduleName = shortName + 1;
}

char symbolBuffer[sizeof(SYMBOL_INFO) + 512];
PSYMBOL_INFO symbol = (PSYMBOL_INFO) symbolBuffer;
DWORD64 symbolOffset = 0;
const char* symbolName = NULL;
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
symbol->MaxNameLen = sizeof(symbolBuffer) - sizeof(SYMBOL_INFO);
if (SymFromAddr(process, curStackFrame.AddrPC.Offset, NULL, symbol)) {
symbolName = symbol->Name;
symbolOffset = curStackFrame.AddrPC.Offset - symbol->Address;
}

IMAGEHLP_LINE64 line;
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
const char* fileName = NULL;
int lineNumber = 0;
DWORD lineOffset = 0;
if (SymGetLineFromAddr64(process, curStackFrame.AddrPC.Offset, &lineOffset, &line))
{
fileName = line.FileName;
lineNumber = line.LineNumber;
// only include text after "\src\" if applicable
const char* shortFileName = strstr(fileName, "\\src\\");
if (shortFileName != NULL)
fileName = shortFileName+5;
}

if (symbolName == NULL)
logFile << moduleName << "@0x" << std::hex << module << "!+0x" << (curStackFrame.AddrPC.Offset-module) << std::dec;
else
logFile << moduleName << "!" << symbolName << "+0x" << std::hex << symbolOffset << std::dec;

if (fileName != NULL)
logFile << " " << fileName << ":" << lineNumber;
logFile << std::endl;
}

logFile.close();
SymCleanup(process);
return EXCEPTION_EXECUTE_HANDLER;
}

class WindowsCrashHandler
{
public:
WindowsCrashHandler() {
SetUnhandledExceptionFilter(ExceptionHandler);
}

~WindowsCrashHandler() {
SetUnhandledExceptionFilter(NULL);
}
};

// static object constructors don't run super early, but still before main
static WindowsCrashHandler __windowsCrashHandler;

#endif
8 changes: 7 additions & 1 deletion src/src.pro
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ lessThan(QT_MAJOR_VERSION, 5) {
## QT5 modules we use
QT += widgets concurrent serialport multimedia multimediawidgets

## Always add debug information
CONFIG += force_debug_info

## If building with QT5 there is experimental suport for building
## with WebEngine now that WebKit is deprecated in QT 5.6
## It brings in a LOT of dependencies !
Expand Down Expand Up @@ -98,7 +101,6 @@ DEFINES += GC_HAVE_SOAP
# via file extensions .lib or .a in src.pro unless the section is
# platform specific. Instead we use directives -Ldir and -llib
win32 {

#QWT is configured to build 2 libs (release/debug) on win32 (see qwtbuild.pri)
CONFIG(release, debug|release){
LIBS += -L$${PWD}/../qwt/lib -lqwt
Expand Down Expand Up @@ -830,6 +832,10 @@ SOURCES += Train/AddDeviceWizard.cpp Train/CalibrationData.cpp Train/Computraine
Train/VideoLayoutParser.cpp Train/VideoSyncFile.cpp Train/WorkoutPlotWindow.cpp Train/WebPageWindow.cpp \
Train/WorkoutWidget.cpp Train/WorkoutWidgetItems.cpp Train/WorkoutWindow.cpp Train/WorkoutWizard.cpp Train/ZwoParser.cpp

## Crash Handling
win32-msvc* {
SOURCES += Core/WindowsCrashHandler.cpp
}

###======================================
### PENDING SOURCE FILES [not active yet]
Expand Down

0 comments on commit 4ae75bf

Please sign in to comment.