mirror of https://github.com/OpenMW/openmw.git
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
186 lines
5.5 KiB
C++
186 lines
5.5 KiB
C++
4 years ago
|
#undef WIN32_LEAN_AND_MEAN
|
||
|
#define WIN32_LEAN_AND_MEAN
|
||
|
#include <Windows.h>
|
||
|
#include <Psapi.h>
|
||
|
|
||
|
#include <DbgHelp.h>
|
||
|
|
||
|
#include <iostream>
|
||
|
#include <memory>
|
||
|
#include <sstream>
|
||
|
|
||
|
#include "windows_crashcatcher.hpp"
|
||
|
#include "windows_crashmonitor.hpp"
|
||
|
#include "windows_crashshm.hpp"
|
||
|
#include <components/debug/debuglog.hpp>
|
||
|
|
||
|
namespace Crash
|
||
|
{
|
||
|
|
||
|
CrashMonitor::CrashMonitor(HANDLE shmHandle)
|
||
|
: mShmHandle(shmHandle)
|
||
|
{
|
||
|
mShm = reinterpret_cast<CrashSHM*>(MapViewOfFile(mShmHandle, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(CrashSHM)));
|
||
|
if (mShm == nullptr)
|
||
|
throw std::runtime_error("Failed to map crash monitor shared memory");
|
||
|
|
||
|
// accessing SHM without lock is OK here, the parent waits for a signal before continuing
|
||
|
|
||
|
mShmMutex = mShm->mStartup.mShmMutex;
|
||
|
mAppProcessHandle = mShm->mStartup.mAppProcessHandle;
|
||
|
mSignalAppEvent = mShm->mStartup.mSignalApp;
|
||
|
mSignalMonitorEvent = mShm->mStartup.mSignalMonitor;
|
||
|
}
|
||
|
|
||
|
CrashMonitor::~CrashMonitor()
|
||
|
{
|
||
|
if (mShm)
|
||
|
UnmapViewOfFile(mShm);
|
||
|
|
||
|
// the handles received from the app are duplicates, we must close them
|
||
|
|
||
|
if (mShmHandle)
|
||
|
CloseHandle(mShmHandle);
|
||
|
|
||
|
if (mShmMutex)
|
||
|
CloseHandle(mShmMutex);
|
||
|
|
||
|
if (mSignalAppEvent)
|
||
|
CloseHandle(mSignalAppEvent);
|
||
|
|
||
|
if (mSignalMonitorEvent)
|
||
|
CloseHandle(mSignalMonitorEvent);
|
||
|
}
|
||
|
|
||
|
void CrashMonitor::shmLock()
|
||
|
{
|
||
|
if (WaitForSingleObject(mShmMutex, CrashCatcherTimeout) != WAIT_OBJECT_0)
|
||
|
throw std::runtime_error("SHM monitor lock timed out");
|
||
|
}
|
||
|
|
||
|
void CrashMonitor::shmUnlock()
|
||
|
{
|
||
|
ReleaseMutex(mShmMutex);
|
||
|
}
|
||
|
|
||
|
void CrashMonitor::signalApp() const
|
||
|
{
|
||
|
SetEvent(mSignalAppEvent);
|
||
|
}
|
||
|
|
||
|
bool CrashMonitor::waitApp() const
|
||
|
{
|
||
|
return WaitForSingleObject(mSignalMonitorEvent, CrashCatcherTimeout) == WAIT_OBJECT_0;
|
||
|
}
|
||
|
|
||
|
bool CrashMonitor::isAppAlive() const
|
||
|
{
|
||
|
DWORD code = 0;
|
||
|
GetExitCodeProcess(mAppProcessHandle, &code);
|
||
|
return code == STILL_ACTIVE;
|
||
|
}
|
||
|
|
||
|
void CrashMonitor::run()
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
// app waits for monitor start up, let it continue
|
||
|
signalApp();
|
||
|
|
||
|
bool running = true;
|
||
|
while (isAppAlive() && running)
|
||
|
{
|
||
|
if (waitApp())
|
||
|
{
|
||
|
shmLock();
|
||
|
|
||
|
switch (mShm->mEvent)
|
||
|
{
|
||
|
case CrashSHM::Event::None:
|
||
|
break;
|
||
|
case CrashSHM::Event::Crashed:
|
||
|
handleCrash();
|
||
|
running = false;
|
||
|
break;
|
||
|
case CrashSHM::Event::Shutdown:
|
||
|
running = false;
|
||
|
break;
|
||
|
case CrashSHM::Event::Startup:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
shmUnlock();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
catch (...)
|
||
|
{
|
||
|
Log(Debug::Error) << "Exception in crash monitor, exiting";
|
||
|
}
|
||
|
signalApp();
|
||
|
}
|
||
|
|
||
|
std::wstring utf8ToUtf16(const std::string& utf8)
|
||
|
{
|
||
|
const int nLenWide = MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), utf8.size(), nullptr, 0);
|
||
|
|
||
|
std::wstring utf16;
|
||
|
utf16.resize(nLenWide);
|
||
|
if (MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), utf8.size(), utf16.data(), nLenWide) != nLenWide)
|
||
|
return {};
|
||
|
|
||
|
return utf16;
|
||
|
}
|
||
|
|
||
|
void CrashMonitor::handleCrash()
|
||
|
{
|
||
|
DWORD processId = GetProcessId(mAppProcessHandle);
|
||
|
|
||
|
try
|
||
|
{
|
||
|
HMODULE dbghelp = LoadLibraryA("dbghelp.dll");
|
||
|
if (dbghelp == NULL)
|
||
|
return;
|
||
|
|
||
|
using MiniDumpWirteDumpFn = BOOL (WINAPI*)(
|
||
|
HANDLE hProcess, DWORD ProcessId, HANDLE hFile, MINIDUMP_TYPE DumpType, PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
|
||
|
PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, PMINIDUMP_CALLBACK_INFORMATION CallbackParam
|
||
|
);
|
||
|
|
||
|
MiniDumpWirteDumpFn miniDumpWriteDump = (MiniDumpWirteDumpFn)GetProcAddress(dbghelp, "MiniDumpWriteDump");
|
||
|
if (miniDumpWriteDump == NULL)
|
||
|
return;
|
||
|
|
||
|
const std::wstring utf16Path = utf8ToUtf16(mShm->mStartup.mLogFilePath);
|
||
|
if (utf16Path.empty())
|
||
|
return;
|
||
|
|
||
|
HANDLE hCrashLog = CreateFileW(utf16Path.c_str(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||
|
if (hCrashLog == NULL || hCrashLog == INVALID_HANDLE_VALUE)
|
||
|
return;
|
||
|
if (auto err = GetLastError(); err != ERROR_ALREADY_EXISTS && err != 0)
|
||
|
return;
|
||
|
|
||
|
EXCEPTION_POINTERS exp;
|
||
|
exp.ContextRecord = &mShm->mCrashed.mContext;
|
||
|
exp.ExceptionRecord = &mShm->mCrashed.mExceptionRecord;
|
||
|
MINIDUMP_EXCEPTION_INFORMATION infos = {};
|
||
|
infos.ThreadId = mShm->mCrashed.mThreadId;
|
||
|
infos.ExceptionPointers = &exp;
|
||
|
infos.ClientPointers = FALSE;
|
||
|
MINIDUMP_TYPE type = (MINIDUMP_TYPE)(MiniDumpWithDataSegs | MiniDumpWithHandleData);
|
||
|
miniDumpWriteDump(mAppProcessHandle, processId, hCrashLog, type, &infos, 0, 0);
|
||
|
}
|
||
|
catch (const std::exception&e)
|
||
|
{
|
||
|
Log(Debug::Error) << "CrashMonitor: " << e.what();
|
||
|
}
|
||
|
catch (...)
|
||
|
{
|
||
|
Log(Debug::Error) << "CrashMonitor: unknown exception";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} // namespace Crash
|