CrashHandler: Save minidump on Windows
This commit is contained in:
parent
f943aa0489
commit
8582e2770d
|
@ -5,9 +5,11 @@
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include "thirdparty/StackWalker.h"
|
|
||||||
#include "windows_headers.h"
|
#include "windows_headers.h"
|
||||||
|
|
||||||
|
#include "thirdparty/StackWalker.h"
|
||||||
|
#include <DbgHelp.h>
|
||||||
|
|
||||||
namespace CrashHandler {
|
namespace CrashHandler {
|
||||||
|
|
||||||
class CrashHandlerStackWalker : public StackWalker
|
class CrashHandlerStackWalker : public StackWalker
|
||||||
|
@ -45,33 +47,68 @@ void CrashHandlerStackWalker::OnOutput(LPCSTR szText)
|
||||||
OutputDebugStringA(szText);
|
OutputDebugStringA(szText);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool WriteMinidump(HMODULE hDbgHelp, HANDLE hFile, HANDLE hProcess, DWORD process_id, DWORD thread_id,
|
||||||
|
PEXCEPTION_POINTERS exception, MINIDUMP_TYPE type)
|
||||||
|
{
|
||||||
|
using PFNMINIDUMPWRITEDUMP =
|
||||||
|
BOOL(WINAPI*)(HANDLE hProcess, DWORD ProcessId, HANDLE hFile, MINIDUMP_TYPE DumpType,
|
||||||
|
PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
|
||||||
|
PMINIDUMP_CALLBACK_INFORMATION CallbackParam);
|
||||||
|
|
||||||
|
PFNMINIDUMPWRITEDUMP minidump_write_dump =
|
||||||
|
reinterpret_cast<PFNMINIDUMPWRITEDUMP>(GetProcAddress(hDbgHelp, "MiniDumpWriteDump"));
|
||||||
|
if (!minidump_write_dump)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
MINIDUMP_EXCEPTION_INFORMATION mei;
|
||||||
|
PMINIDUMP_EXCEPTION_INFORMATION mei_ptr = nullptr;
|
||||||
|
if (exception)
|
||||||
|
{
|
||||||
|
mei.ThreadId = thread_id;
|
||||||
|
mei.ExceptionPointers = exception;
|
||||||
|
mei.ClientPointers = FALSE;
|
||||||
|
mei_ptr = &mei;
|
||||||
|
}
|
||||||
|
|
||||||
|
return minidump_write_dump(hProcess, process_id, hFile, type, mei_ptr, nullptr, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
static std::wstring s_write_directory;
|
static std::wstring s_write_directory;
|
||||||
static PVOID s_veh_handle;
|
static PVOID s_veh_handle = nullptr;
|
||||||
|
static bool s_in_crash_handler = false;
|
||||||
|
|
||||||
static LONG ExceptionHandler(PEXCEPTION_POINTERS exi)
|
static LONG ExceptionHandler(PEXCEPTION_POINTERS exi)
|
||||||
{
|
{
|
||||||
|
if (s_in_crash_handler)
|
||||||
|
return EXCEPTION_CONTINUE_SEARCH;
|
||||||
|
|
||||||
switch (exi->ExceptionRecord->ExceptionCode)
|
switch (exi->ExceptionRecord->ExceptionCode)
|
||||||
{
|
{
|
||||||
case EXCEPTION_ACCESS_VIOLATION:
|
case EXCEPTION_ACCESS_VIOLATION:
|
||||||
case EXCEPTION_BREAKPOINT:
|
case EXCEPTION_BREAKPOINT:
|
||||||
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
|
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
|
||||||
case EXCEPTION_INT_DIVIDE_BY_ZERO:
|
case EXCEPTION_INT_DIVIDE_BY_ZERO:
|
||||||
case EXCEPTION_INT_OVERFLOW:
|
case EXCEPTION_INT_OVERFLOW:
|
||||||
case EXCEPTION_PRIV_INSTRUCTION:
|
case EXCEPTION_PRIV_INSTRUCTION:
|
||||||
case EXCEPTION_ILLEGAL_INSTRUCTION:
|
case EXCEPTION_ILLEGAL_INSTRUCTION:
|
||||||
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
|
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
|
||||||
case EXCEPTION_STACK_OVERFLOW:
|
case EXCEPTION_STACK_OVERFLOW:
|
||||||
case EXCEPTION_GUARD_PAGE:
|
case EXCEPTION_GUARD_PAGE:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return EXCEPTION_CONTINUE_SEARCH;
|
return EXCEPTION_CONTINUE_SEARCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the debugger is attached, let it take care of it.
|
// if the debugger is attached, let it take care of it.
|
||||||
if (IsDebuggerPresent())
|
if (IsDebuggerPresent())
|
||||||
return EXCEPTION_CONTINUE_SEARCH;
|
return EXCEPTION_CONTINUE_SEARCH;
|
||||||
|
|
||||||
|
s_in_crash_handler = true;
|
||||||
|
|
||||||
|
// we definitely need dbg helper - maintain an extra reference here
|
||||||
|
HMODULE hDbgHelp = StackWalker::LoadDbgHelpLibrary();
|
||||||
|
|
||||||
wchar_t filename[1024] = {};
|
wchar_t filename[1024] = {};
|
||||||
if (!s_write_directory.empty())
|
if (!s_write_directory.empty())
|
||||||
{
|
{
|
||||||
|
@ -94,8 +131,42 @@ static LONG ExceptionHandler(PEXCEPTION_POINTERS exi)
|
||||||
WriteFile(hFile, line, static_cast<DWORD>(std::strlen(line)), &written, nullptr);
|
WriteFile(hFile, line, static_cast<DWORD>(std::strlen(line)), &written, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!s_write_directory.empty())
|
||||||
|
{
|
||||||
|
wcsncpy_s(filename, countof(filename), s_write_directory.c_str(), _TRUNCATE);
|
||||||
|
wcsncat_s(filename, countof(filename), L"\\crash.dmp", _TRUNCATE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
wcsncat_s(filename, countof(filename), L"crash.dmp", _TRUNCATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
const MINIDUMP_TYPE minidump_type =
|
||||||
|
static_cast<MINIDUMP_TYPE>(MiniDumpNormal | MiniDumpWithHandleData | MiniDumpWithProcessThreadData |
|
||||||
|
MiniDumpWithThreadInfo | MiniDumpWithIndirectlyReferencedMemory);
|
||||||
|
HANDLE hMinidumpFile = CreateFileW(filename, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, 0, nullptr);
|
||||||
|
if (!hMinidumpFile || !WriteMinidump(hDbgHelp, hMinidumpFile, GetCurrentProcess(), GetCurrentProcessId(),
|
||||||
|
GetCurrentThreadId(), exi, minidump_type))
|
||||||
|
{
|
||||||
|
static const char error_message[] = "Failed to write minidump file.\n";
|
||||||
|
if (hFile)
|
||||||
|
{
|
||||||
|
DWORD written;
|
||||||
|
WriteFile(hFile, error_message, sizeof(error_message) - 1, &written, nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hMinidumpFile)
|
||||||
|
CloseHandle(hMinidumpFile);
|
||||||
|
|
||||||
CrashHandlerStackWalker sw(hFile);
|
CrashHandlerStackWalker sw(hFile);
|
||||||
sw.ShowCallstack(GetCurrentThread(), exi->ContextRecord);
|
sw.ShowCallstack(GetCurrentThread(), exi->ContextRecord);
|
||||||
|
|
||||||
|
if (hFile)
|
||||||
|
CloseHandle(hFile);
|
||||||
|
|
||||||
|
if (hDbgHelp)
|
||||||
|
FreeLibrary(hDbgHelp);
|
||||||
|
|
||||||
return EXCEPTION_CONTINUE_SEARCH;
|
return EXCEPTION_CONTINUE_SEARCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -246,6 +246,81 @@ static void MyStrCpy(char* szDest, size_t nMaxDestSize, const char* szSrc)
|
||||||
// Normally it should be enough to use 'CONTEXT_FULL' (better would be 'CONTEXT_ALL')
|
// Normally it should be enough to use 'CONTEXT_FULL' (better would be 'CONTEXT_ALL')
|
||||||
#define USED_CONTEXT_FLAGS CONTEXT_FULL
|
#define USED_CONTEXT_FLAGS CONTEXT_FULL
|
||||||
|
|
||||||
|
HMODULE StackWalker::LoadDbgHelpLibrary()
|
||||||
|
{
|
||||||
|
HMODULE hModule = NULL;
|
||||||
|
|
||||||
|
// Dynamically load the Entry-Points for dbghelp.dll:
|
||||||
|
// First try to load the newest one from
|
||||||
|
TCHAR szTemp[4096];
|
||||||
|
// But before we do this, we first check if the ".local" file exists
|
||||||
|
if (GetModuleFileName(NULL, szTemp, 4096) > 0)
|
||||||
|
{
|
||||||
|
_tcscat_s(szTemp, _T(".local"));
|
||||||
|
if (GetFileAttributes(szTemp) == INVALID_FILE_ATTRIBUTES)
|
||||||
|
{
|
||||||
|
// ".local" file does not exist, so we can try to load the dbghelp.dll from the "Debugging Tools for Windows"
|
||||||
|
// Ok, first try the new path according to the architecture:
|
||||||
|
#ifdef _M_IX86
|
||||||
|
if ((hModule == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0))
|
||||||
|
{
|
||||||
|
_tcscat_s(szTemp, _T("\\Debugging Tools for Windows (x86)\\dbghelp.dll"));
|
||||||
|
// now check if the file exists:
|
||||||
|
if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES)
|
||||||
|
{
|
||||||
|
hModule = LoadLibrary(szTemp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#elif _M_X64
|
||||||
|
if ((hModule == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0))
|
||||||
|
{
|
||||||
|
_tcscat_s(szTemp, _T("\\Debugging Tools for Windows (x64)\\dbghelp.dll"));
|
||||||
|
// now check if the file exists:
|
||||||
|
if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES)
|
||||||
|
{
|
||||||
|
hModule = LoadLibrary(szTemp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#elif _M_IA64
|
||||||
|
if ((hModule == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0))
|
||||||
|
{
|
||||||
|
_tcscat_s(szTemp, _T("\\Debugging Tools for Windows (ia64)\\dbghelp.dll"));
|
||||||
|
// now check if the file exists:
|
||||||
|
if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES)
|
||||||
|
{
|
||||||
|
hModule = LoadLibrary(szTemp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
// If still not found, try the old directories...
|
||||||
|
if ((hModule == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0))
|
||||||
|
{
|
||||||
|
_tcscat_s(szTemp, _T("\\Debugging Tools for Windows\\dbghelp.dll"));
|
||||||
|
// now check if the file exists:
|
||||||
|
if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES)
|
||||||
|
{
|
||||||
|
hModule = LoadLibrary(szTemp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if defined _M_X64 || defined _M_IA64
|
||||||
|
// Still not found? Then try to load the (old) 64-Bit version:
|
||||||
|
if ((hModule == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0))
|
||||||
|
{
|
||||||
|
_tcscat_s(szTemp, _T("\\Debugging Tools for Windows 64-Bit\\dbghelp.dll"));
|
||||||
|
if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES)
|
||||||
|
{
|
||||||
|
hModule = LoadLibrary(szTemp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hModule == NULL) // if not already loaded, try to load a default-one
|
||||||
|
hModule = LoadLibrary(_T("dbghelp.dll"));
|
||||||
|
|
||||||
|
return hModule;
|
||||||
|
}
|
||||||
|
|
||||||
class StackWalkerInternal
|
class StackWalkerInternal
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -285,73 +360,8 @@ public:
|
||||||
{
|
{
|
||||||
if (m_parent == NULL)
|
if (m_parent == NULL)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
// Dynamically load the Entry-Points for dbghelp.dll:
|
|
||||||
// First try to load the newest one from
|
m_hDbhHelp = StackWalker::LoadDbgHelpLibrary();
|
||||||
TCHAR szTemp[4096];
|
|
||||||
// But before we do this, we first check if the ".local" file exists
|
|
||||||
if (GetModuleFileName(NULL, szTemp, 4096) > 0)
|
|
||||||
{
|
|
||||||
_tcscat_s(szTemp, _T(".local"));
|
|
||||||
if (GetFileAttributes(szTemp) == INVALID_FILE_ATTRIBUTES)
|
|
||||||
{
|
|
||||||
// ".local" file does not exist, so we can try to load the dbghelp.dll from the "Debugging Tools for Windows"
|
|
||||||
// Ok, first try the new path according to the architecture:
|
|
||||||
#ifdef _M_IX86
|
|
||||||
if ((m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0))
|
|
||||||
{
|
|
||||||
_tcscat_s(szTemp, _T("\\Debugging Tools for Windows (x86)\\dbghelp.dll"));
|
|
||||||
// now check if the file exists:
|
|
||||||
if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES)
|
|
||||||
{
|
|
||||||
m_hDbhHelp = LoadLibrary(szTemp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#elif _M_X64
|
|
||||||
if ((m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0))
|
|
||||||
{
|
|
||||||
_tcscat_s(szTemp, _T("\\Debugging Tools for Windows (x64)\\dbghelp.dll"));
|
|
||||||
// now check if the file exists:
|
|
||||||
if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES)
|
|
||||||
{
|
|
||||||
m_hDbhHelp = LoadLibrary(szTemp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#elif _M_IA64
|
|
||||||
if ((m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0))
|
|
||||||
{
|
|
||||||
_tcscat_s(szTemp, _T("\\Debugging Tools for Windows (ia64)\\dbghelp.dll"));
|
|
||||||
// now check if the file exists:
|
|
||||||
if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES)
|
|
||||||
{
|
|
||||||
m_hDbhHelp = LoadLibrary(szTemp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
// If still not found, try the old directories...
|
|
||||||
if ((m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0))
|
|
||||||
{
|
|
||||||
_tcscat_s(szTemp, _T("\\Debugging Tools for Windows\\dbghelp.dll"));
|
|
||||||
// now check if the file exists:
|
|
||||||
if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES)
|
|
||||||
{
|
|
||||||
m_hDbhHelp = LoadLibrary(szTemp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#if defined _M_X64 || defined _M_IA64
|
|
||||||
// Still not found? Then try to load the (old) 64-Bit version:
|
|
||||||
if ((m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0))
|
|
||||||
{
|
|
||||||
_tcscat_s(szTemp, _T("\\Debugging Tools for Windows 64-Bit\\dbghelp.dll"));
|
|
||||||
if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES)
|
|
||||||
{
|
|
||||||
m_hDbhHelp = LoadLibrary(szTemp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (m_hDbhHelp == NULL) // if not already loaded, try to load a default-one
|
|
||||||
m_hDbhHelp = LoadLibrary(_T("dbghelp.dll"));
|
|
||||||
if (m_hDbhHelp == NULL)
|
if (m_hDbhHelp == NULL)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
pSI = (tSI)GetProcAddress(m_hDbhHelp, "SymInitialize");
|
pSI = (tSI)GetProcAddress(m_hDbhHelp, "SymInitialize");
|
||||||
|
|
|
@ -102,6 +102,8 @@ public:
|
||||||
StackWalker(DWORD dwProcessId, HANDLE hProcess);
|
StackWalker(DWORD dwProcessId, HANDLE hProcess);
|
||||||
virtual ~StackWalker();
|
virtual ~StackWalker();
|
||||||
|
|
||||||
|
static HMODULE LoadDbgHelpLibrary();
|
||||||
|
|
||||||
typedef BOOL(__stdcall* PReadProcessMemoryRoutine)(
|
typedef BOOL(__stdcall* PReadProcessMemoryRoutine)(
|
||||||
HANDLE hProcess,
|
HANDLE hProcess,
|
||||||
DWORD64 qwBaseAddress,
|
DWORD64 qwBaseAddress,
|
||||||
|
|
Loading…
Reference in New Issue