diff --git a/components/crashcatcher/windows_crashcatcher.cpp b/components/crashcatcher/windows_crashcatcher.cpp index 39ac86d7b8..eea6ac40ad 100644 --- a/components/crashcatcher/windows_crashcatcher.cpp +++ b/components/crashcatcher/windows_crashcatcher.cpp @@ -144,6 +144,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 +197,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..26ebc0bad3 100644 --- a/components/crashcatcher/windows_crashmonitor.cpp +++ b/components/crashcatcher/windows_crashmonitor.cpp @@ -9,6 +9,8 @@ #include #include +#include + #include "windows_crashcatcher.hpp" #include "windows_crashmonitor.hpp" #include "windows_crashshm.hpp" @@ -28,6 +30,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 +83,44 @@ namespace Crash return code == STILL_ACTIVE; } + bool CrashMonitor::isAppFrozen() + { + 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) + { + // TODO: use https://devblogs.microsoft.com/oldnewthing/20111026-00/?p=9263 to monitor for the window being destroyed + } + 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,8 +129,16 @@ namespace Crash signalApp(); bool running = true; + bool frozen = false; while (isAppAlive() && running) { + if (isAppFrozen()) + { + frozen = true; + handleCrash(); + running = false; + break; + } if (waitApp()) { shmLock(); @@ -113,6 +162,13 @@ namespace Crash } } + if (frozen) + { + TerminateProcess(mAppProcessHandle, -1); + 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 (...) { diff --git a/components/crashcatcher/windows_crashmonitor.hpp b/components/crashcatcher/windows_crashmonitor.hpp index 678d38435c..01acd34bdd 100644 --- a/components/crashcatcher/windows_crashmonitor.hpp +++ b/components/crashcatcher/windows_crashmonitor.hpp @@ -21,6 +21,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; @@ -37,6 +39,8 @@ private: bool isAppAlive() const; + bool isAppFrozen(); + void shmLock(); void shmUnlock(); 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;