mirror of
https://github.com/OpenMW/openmw.git
synced 2025-02-21 05:09:43 +00:00
Merge branch 'brainfreeze' into 'master'
Attempt to catch freezes on Windows See merge request OpenMW/openmw!1493
This commit is contained in:
commit
555e0b1f53
4 changed files with 150 additions and 6 deletions
|
@ -1,10 +1,11 @@
|
|||
#include "windows_crashcatcher.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <cwchar>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
|
||||
#include "windows_crashcatcher.hpp"
|
||||
#include "windows_crashmonitor.hpp"
|
||||
#include "windows_crashshm.hpp"
|
||||
#include <SDL_messagebox.h>
|
||||
|
@ -144,6 +145,7 @@ namespace Crash
|
|||
mShm->mEvent = CrashSHM::Event::Startup;
|
||||
mShm->mStartup.mShmMutex = duplicateHandle(mShmMutex);
|
||||
mShm->mStartup.mAppProcessHandle = duplicateHandle(GetCurrentProcess());
|
||||
mShm->mStartup.mAppMainThreadId = GetThreadId(GetCurrentThread());
|
||||
mShm->mStartup.mSignalApp = duplicateHandle(mSignalAppEvent);
|
||||
mShm->mStartup.mSignalMonitor = duplicateHandle(mSignalMonitorEvent);
|
||||
|
||||
|
@ -196,7 +198,7 @@ namespace Crash
|
|||
// must remain until monitor has finished
|
||||
waitMonitor();
|
||||
|
||||
std::string message = "OpenMW has encountered a fatal error.\nCrash log saved to '" + std::string(mShm->mStartup.mLogFilePath) + "'.\n Please report this to https://gitlab.com/OpenMW/openmw/issues !";
|
||||
std::string message = "OpenMW has encountered a fatal error.\nCrash log saved to '" + std::string(mShm->mStartup.mLogFilePath) + "'.\nPlease report this to https://gitlab.com/OpenMW/openmw/issues !";
|
||||
SDL_ShowSimpleMessageBox(0, "Fatal Error", message.c_str(), nullptr);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#include "windows_crashmonitor.hpp"
|
||||
|
||||
#undef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <Windows.h>
|
||||
|
@ -9,13 +11,15 @@
|
|||
#include <memory>
|
||||
#include <sstream>
|
||||
|
||||
#include <SDL_messagebox.h>
|
||||
|
||||
#include "windows_crashcatcher.hpp"
|
||||
#include "windows_crashmonitor.hpp"
|
||||
#include "windows_crashshm.hpp"
|
||||
#include <components/debug/debuglog.hpp>
|
||||
|
||||
namespace Crash
|
||||
{
|
||||
std::unordered_map<HWINEVENTHOOK, CrashMonitor*> CrashMonitor::smEventHookOwners{};
|
||||
|
||||
CrashMonitor::CrashMonitor(HANDLE shmHandle)
|
||||
: mShmHandle(shmHandle)
|
||||
|
@ -28,6 +32,7 @@ namespace Crash
|
|||
|
||||
mShmMutex = mShm->mStartup.mShmMutex;
|
||||
mAppProcessHandle = mShm->mStartup.mAppProcessHandle;
|
||||
mAppMainThreadId = mShm->mStartup.mAppMainThreadId;
|
||||
mSignalAppEvent = mShm->mStartup.mSignalApp;
|
||||
mSignalMonitorEvent = mShm->mStartup.mSignalMonitor;
|
||||
}
|
||||
|
@ -80,6 +85,61 @@ namespace Crash
|
|||
return code == STILL_ACTIVE;
|
||||
}
|
||||
|
||||
bool CrashMonitor::isAppFrozen()
|
||||
{
|
||||
MSG message;
|
||||
// Allow the event hook callback to run
|
||||
PeekMessage(&message, nullptr, 0, 0, PM_NOREMOVE);
|
||||
|
||||
if (!mAppWindowHandle)
|
||||
{
|
||||
EnumWindows([](HWND handle, LPARAM param) -> BOOL {
|
||||
CrashMonitor& crashMonitor = *(CrashMonitor*)param;
|
||||
DWORD processId;
|
||||
if (GetWindowThreadProcessId(handle, &processId) == crashMonitor.mAppMainThreadId && processId == GetProcessId(crashMonitor.mAppProcessHandle))
|
||||
{
|
||||
if (GetWindow(handle, GW_OWNER) == 0)
|
||||
{
|
||||
crashMonitor.mAppWindowHandle = handle;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}, (LPARAM)this);
|
||||
if (mAppWindowHandle)
|
||||
{
|
||||
DWORD processId;
|
||||
GetWindowThreadProcessId(mAppWindowHandle, &processId);
|
||||
HWINEVENTHOOK eventHookHandle = SetWinEventHook(EVENT_OBJECT_DESTROY, EVENT_OBJECT_DESTROY, nullptr,
|
||||
[](HWINEVENTHOOK hWinEventHook, DWORD event, HWND windowHandle, LONG objectId, LONG childId, DWORD eventThread, DWORD eventTime)
|
||||
{
|
||||
CrashMonitor& crashMonitor = *smEventHookOwners[hWinEventHook];
|
||||
if (event == EVENT_OBJECT_DESTROY && windowHandle == crashMonitor.mAppWindowHandle && objectId == OBJID_WINDOW && childId == INDEXID_CONTAINER)
|
||||
{
|
||||
crashMonitor.mAppWindowHandle = nullptr;
|
||||
smEventHookOwners.erase(hWinEventHook);
|
||||
UnhookWinEvent(hWinEventHook);
|
||||
}
|
||||
}, processId, mAppMainThreadId, WINEVENT_OUTOFCONTEXT);
|
||||
smEventHookOwners[eventHookHandle] = this;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
if (IsHungAppWindow)
|
||||
return IsHungAppWindow(mAppWindowHandle);
|
||||
else
|
||||
{
|
||||
BOOL debuggerPresent;
|
||||
|
||||
if (CheckRemoteDebuggerPresent(mAppProcessHandle, &debuggerPresent) && debuggerPresent)
|
||||
return false;
|
||||
if (SendMessageTimeoutA(mAppWindowHandle, WM_NULL, 0, 0, 0, 5000, nullptr) == 0)
|
||||
return GetLastError() == ERROR_TIMEOUT;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CrashMonitor::run()
|
||||
{
|
||||
try
|
||||
|
@ -88,9 +148,24 @@ namespace Crash
|
|||
signalApp();
|
||||
|
||||
bool running = true;
|
||||
while (isAppAlive() && running)
|
||||
bool frozen = false;
|
||||
while (isAppAlive() && running && !mFreezeAbort)
|
||||
{
|
||||
if (waitApp())
|
||||
if (isAppFrozen())
|
||||
{
|
||||
if (!frozen)
|
||||
{
|
||||
showFreezeMessageBox();
|
||||
frozen = true;
|
||||
}
|
||||
}
|
||||
else if (frozen)
|
||||
{
|
||||
hideFreezeMessageBox();
|
||||
frozen = false;
|
||||
}
|
||||
|
||||
if (!mFreezeAbort && waitApp())
|
||||
{
|
||||
shmLock();
|
||||
|
||||
|
@ -113,6 +188,16 @@ namespace Crash
|
|||
}
|
||||
}
|
||||
|
||||
if (frozen)
|
||||
hideFreezeMessageBox();
|
||||
|
||||
if (mFreezeAbort)
|
||||
{
|
||||
TerminateProcess(mAppProcessHandle, 0xDEAD);
|
||||
std::string message = "OpenMW appears to have frozen.\nCrash log saved to '" + std::string(mShm->mStartup.mLogFilePath) + "'.\nPlease report this to https://gitlab.com/OpenMW/openmw/issues !";
|
||||
SDL_ShowSimpleMessageBox(0, "Fatal Error", message.c_str(), nullptr);
|
||||
}
|
||||
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
|
@ -185,4 +270,43 @@ namespace Crash
|
|||
}
|
||||
}
|
||||
|
||||
void CrashMonitor::showFreezeMessageBox()
|
||||
{
|
||||
std::thread messageBoxThread([&]() {
|
||||
SDL_MessageBoxButtonData button = { SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, 0, "Abort" };
|
||||
SDL_MessageBoxData messageBoxData = {
|
||||
SDL_MESSAGEBOX_ERROR,
|
||||
nullptr,
|
||||
"OpenMW appears to have frozen",
|
||||
"OpenMW appears to have frozen. Press Abort to terminate it and generate a crash dump.\nIf OpenMW hasn't actually frozen, this message box will disappear a within a few seconds of it becoming responsive.",
|
||||
1,
|
||||
&button,
|
||||
nullptr
|
||||
};
|
||||
|
||||
int buttonId;
|
||||
if (SDL_ShowMessageBox(&messageBoxData, &buttonId) == 0 && buttonId == 0)
|
||||
mFreezeAbort = true;
|
||||
});
|
||||
|
||||
mFreezeMessageBoxThreadId = GetThreadId(messageBoxThread.native_handle());
|
||||
messageBoxThread.detach();
|
||||
}
|
||||
|
||||
void CrashMonitor::hideFreezeMessageBox()
|
||||
{
|
||||
if (!mFreezeMessageBoxThreadId)
|
||||
return;
|
||||
|
||||
EnumWindows([](HWND handle, LPARAM param) -> BOOL {
|
||||
CrashMonitor& crashMonitor = *(CrashMonitor*)param;
|
||||
DWORD processId;
|
||||
if (GetWindowThreadProcessId(handle, &processId) == crashMonitor.mFreezeMessageBoxThreadId && processId == GetCurrentProcessId())
|
||||
PostMessage(handle, WM_CLOSE, 0, 0);
|
||||
return true;
|
||||
}, (LPARAM)this);
|
||||
|
||||
mFreezeMessageBoxThreadId = 0;
|
||||
}
|
||||
|
||||
} // namespace Crash
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
#ifndef WINDOWS_CRASHMONITOR_HPP
|
||||
#define WINDOWS_CRASHMONITOR_HPP
|
||||
|
||||
#include <windef.h>
|
||||
#undef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <Windows.h>
|
||||
|
||||
#include <atomic>
|
||||
|
||||
namespace Crash
|
||||
{
|
||||
|
@ -21,6 +25,8 @@ public:
|
|||
private:
|
||||
|
||||
HANDLE mAppProcessHandle = nullptr;
|
||||
DWORD mAppMainThreadId = 0;
|
||||
HWND mAppWindowHandle = nullptr;
|
||||
|
||||
// triggered when the monitor process wants to wake the parent process (received via SHM)
|
||||
HANDLE mSignalAppEvent = nullptr;
|
||||
|
@ -31,17 +37,28 @@ private:
|
|||
HANDLE mShmHandle = nullptr;
|
||||
HANDLE mShmMutex = nullptr;
|
||||
|
||||
DWORD mFreezeMessageBoxThreadId = 0;
|
||||
std::atomic_bool mFreezeAbort;
|
||||
|
||||
static std::unordered_map<HWINEVENTHOOK, CrashMonitor*> smEventHookOwners;
|
||||
|
||||
void signalApp() const;
|
||||
|
||||
bool waitApp() const;
|
||||
|
||||
bool isAppAlive() const;
|
||||
|
||||
bool isAppFrozen();
|
||||
|
||||
void shmLock();
|
||||
|
||||
void shmUnlock();
|
||||
|
||||
void handleCrash();
|
||||
|
||||
void showFreezeMessageBox();
|
||||
|
||||
void hideFreezeMessageBox();
|
||||
};
|
||||
|
||||
} // namespace Crash
|
||||
|
|
|
@ -26,6 +26,7 @@ namespace Crash
|
|||
struct Startup
|
||||
{
|
||||
HANDLE mAppProcessHandle;
|
||||
DWORD mAppMainThreadId;
|
||||
HANDLE mSignalApp;
|
||||
HANDLE mSignalMonitor;
|
||||
HANDLE mShmMutex;
|
||||
|
|
Loading…
Reference in a new issue