diff options
author | psi29a <psi29a@gmail.com> | 2022-01-06 08:47:23 +0000 |
---|---|---|
committer | psi29a <psi29a@gmail.com> | 2022-01-06 08:47:23 +0000 |
commit | 555e0b1f531c05d064bc1d9a2ae0bbb5e09bbea4 (patch) | |
tree | 49430d956ef9206983ad10d7a43a7f87c2b18649 | |
parent | b56406b0addb124f9e369198a54f8510cd146a2f (diff) | |
parent | fa05b0b96cbdaae575c5b5f65696965139d63042 (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.cpp | 6 | ||||
-rw-r--r-- | components/crashcatcher/windows_crashmonitor.cpp | 130 | ||||
-rw-r--r-- | components/crashcatcher/windows_crashmonitor.hpp | 19 | ||||
-rw-r--r-- | components/crashcatcher/windows_crashshm.hpp | 1 |
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; |