Skip to content

Instantly share code, notes, and snippets.

@rioki
Created April 4, 2023 07:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rioki/1ffb0d24d00e46f4894ee16c947f771f to your computer and use it in GitHub Desktop.
Save rioki/1ffb0d24d00e46f4894ee16c947f771f to your computer and use it in GitHub Desktop.
CrashWatchdog
// This program is free software. It comes without any warranty, to
// the extent permitted by applicable law. You can redistribute it
// and/or modify it under the terms of the Do What The Fuck You Want
// To Public License, Version 2, as published by Sam Hocevar. See
// http://www.wtfpl.net/ for more details.
#include "CrashWatchdog.h"
#include <stdexcept>
#include <array>
#include <string>
#include <string_view>
#include <eh.h>
#include <dbghelp.h>
namespace dbg
{
// Wrapper around CreateFile / CloseHandle API
class Win32File
{
public:
Win32File(const std::filesystem::path& fileName)
{
handle = CreateFileW(fileName.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (handle == INVALID_HANDLE_VALUE)
{
throw std::runtime_error("Failed to open file for writing");
}
}
~Win32File()
{
CloseHandle(handle);
}
operator HANDLE ()
{
return handle;
}
private:
HANDLE handle = NULL;
Win32File(const Win32File&) = delete;
Win32File& operator = (const Win32File&) = delete;
};
std::wstring GetTempPathEx()
{
std::array<wchar_t, MAX_PATH> buff;
auto r = GetTempPathW(static_cast<DWORD>(buff.size()), buff.data());
if (r == 0)
{
throw std::runtime_error("Failed to get %TEMP% path.");
}
return std::wstring(buff.data());
}
std::wstring GetTempFileNameEx(const std::wstring_view pathName, const std::wstring_view prefix)
{
std::array<wchar_t, MAX_PATH> buff;
auto r = GetTempFileNameW(pathName.data(), prefix.data(), 0, buff.data());
if (r == 0)
{
throw std::runtime_error("Failed to get temp file name.");
}
return std::wstring(buff.data());
}
std::wstring GetDumpFileName()
{
return GetTempFileNameEx(GetTempPathEx(), L"cfc");
}
MINIDUMP_EXCEPTION_INFORMATION GetExceptionInfo(EXCEPTION_POINTERS* exceptionPointers)
{
auto exceptionInfo = MINIDUMP_EXCEPTION_INFORMATION{};
exceptionInfo.ThreadId = GetCurrentThreadId();
exceptionInfo.ExceptionPointers = exceptionPointers;
exceptionInfo.ClientPointers = TRUE;
return exceptionInfo;
}
std::wstring WriteCrashDump(EXCEPTION_POINTERS* exceptionPointers = nullptr)
{
try
{
auto dumpFile = GetDumpFileName();
auto dumpFileHandle = Win32File(dumpFile);
auto process = GetCurrentProcess();
auto processId = GetCurrentProcessId();
auto exceptionInfo = GetExceptionInfo(exceptionPointers);
auto result = MiniDumpWriteDump(process, processId, dumpFileHandle, MiniDumpNormal, &exceptionInfo, 0, 0);
if (result == FALSE)
{
throw std::runtime_error("MiniDumpWriteDump failed.");
}
return dumpFile;
}
catch (const std::exception& ex)
{
MessageBoxA(NULL, ex.what(), "Failed to write Crash Dump", MB_ICONERROR|MB_OK);
return L"Failed";
}
}
LONG __stdcall HandleUnhendledExceptionFilter(EXCEPTION_POINTERS* pExPtrs)
{
auto file = WriteCrashDump(pExPtrs);
auto msg = std::wstring(L"Process Crashed.\nCrash Dump written to: ") + file;
MessageBoxW(NULL, msg.data(), L"Unexpected Error", MB_ICONERROR | MB_OK);
exit(EXIT_FAILURE);
}
CrashWatchdog::CrashWatchdog()
{
m_oldUnhandledExceptionFilter = SetUnhandledExceptionFilter(HandleUnhendledExceptionFilter);
}
CrashWatchdog::~CrashWatchdog()
{
SetUnhandledExceptionFilter(m_oldUnhandledExceptionFilter);
}
}
// This program is free software. It comes without any warranty, to
// the extent permitted by applicable law. You can redistribute it
// and/or modify it under the terms of the Do What The Fuck You Want
// To Public License, Version 2, as published by Sam Hocevar. See
// http://www.wtfpl.net/ for more details.
#pragma once
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <windows.h>
namespace dbg
{
//! Utility that overrides crash handling functions and pumps stack at the appropriate time.
class CrashWatchdog
{
public:
//! Install crash handlers
CrashWatchdog();
//! Reset state
~CrashWatchdog();
private:
LPTOP_LEVEL_EXCEPTION_FILTER m_oldUnhandledExceptionFilter = nullptr;
CrashWatchdog(const CrashWatchdog&) = delete;
CrashWatchdog& operator = (const CrashWatchdog&) = delete;
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment