summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpsi29a <psi29a@gmail.com>2022-01-06 08:47:23 +0000
committerpsi29a <psi29a@gmail.com>2022-01-06 08:47:23 +0000
commit555e0b1f531c05d064bc1d9a2ae0bbb5e09bbea4 (patch)
tree49430d956ef9206983ad10d7a43a7f87c2b18649
parentb56406b0addb124f9e369198a54f8510cd146a2f (diff)
parentfa05b0b96cbdaae575c5b5f65696965139d63042 (diff)
Merge branch 'brainfreeze' into 'master'
Attempt to catch freezes on Windows See merge request OpenMW/openmw!1493
-rw-r--r--components/crashcatcher/windows_crashcatcher.cpp6
-rw-r--r--components/crashcatcher/windows_crashmonitor.cpp130
-rw-r--r--components/crashcatcher/windows_crashmonitor.hpp19
-rw-r--r--components/crashcatcher/windows_crashshm.hpp1
4 files changed, 150 insertions, 6 deletions
diff --git a/components/crashcatcher/windows_crashcatcher.cpp b/components/crashcatcher/windows_crashcatcher.cpp
index 39ac86d7b8..e9eb60e845 100644
--- a/components/crashcatcher/windows_crashcatcher.cpp
+++ b/components/crashcatcher/windows_crashcatcher.cpp
@@ -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);
}
diff --git a/components/crashcatcher/windows_crashmonitor.cpp b/components/crashcatcher/windows_crashmonitor.cpp
index 8976deb2ea..50d3fc08a1 100644
--- a/components/crashcatcher/windows_crashmonitor.cpp
+++ b/components/crashcatcher/windows_crashmonitor.cpp
@@ -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
diff --git a/components/crashcatcher/windows_crashmonitor.hpp b/components/crashcatcher/windows_crashmonitor.hpp
index 678d38435c..4028362836 100644
--- a/components/crashcatcher/windows_crashmonitor.hpp
+++ b/components/crashcatcher/windows_crashmonitor.hpp
@@ -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
diff --git a/components/crashcatcher/windows_crashshm.hpp b/components/crashcatcher/windows_crashshm.hpp
index 47929a45fe..a474600f94 100644
--- a/components/crashcatcher/windows_crashshm.hpp
+++ b/components/crashcatcher/windows_crashshm.hpp
@@ -26,6 +26,7 @@ namespace Crash
struct Startup
{
HANDLE mAppProcessHandle;
+ DWORD mAppMainThreadId;
HANDLE mSignalApp;
HANDLE mSignalMonitor;
HANDLE mShmMutex;