diff options
author | Luboš Luňák <l.lunak@collabora.com> | 2020-02-04 17:28:10 +0100 |
---|---|---|
committer | Luboš Luňák <l.lunak@collabora.com> | 2020-02-07 09:11:32 +0100 |
commit | 397ad713cf9bc951b4882ca4b6baeb57541e318c (patch) | |
tree | 2096facd2ea8432972d6354f960c528fa0f48a6a /vcl | |
parent | bfb08ca517b6854b5c78b0b29c7a4d6dce473adb (diff) |
merge OpenGLZone and OpenCLZone into one generic code
To be used also by Skia code to detect problems with Vulkan drivers.
The watchdog does not handle OpenCL because neither did the original
code, but also because OpenCLZone::hardDisable() called from it
would deadlock on SolarMutex for some reason.
Change-Id: I483d8cb0b11a4e1e65c564f4e4c29ab68843ff71
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/88008
Tested-by: Jenkins
Reviewed-by: Luboš Luňák <l.lunak@collabora.com>
Diffstat (limited to 'vcl')
-rw-r--r-- | vcl/Library_vcl.mk | 1 | ||||
-rw-r--r-- | vcl/README.vars | 2 | ||||
-rw-r--r-- | vcl/inc/opengl/watchdog.hxx | 67 | ||||
-rw-r--r-- | vcl/inc/opengl/zone.hxx | 39 | ||||
-rw-r--r-- | vcl/inc/watchdog.hxx | 32 | ||||
-rw-r--r-- | vcl/source/app/svmain.cxx | 8 | ||||
-rw-r--r-- | vcl/source/app/watchdog.cxx | 144 | ||||
-rw-r--r-- | vcl/source/opengl/OpenGLHelper.cxx | 160 |
8 files changed, 235 insertions, 218 deletions
diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk index 3a7122f15b11..16be92968a92 100644 --- a/vcl/Library_vcl.mk +++ b/vcl/Library_vcl.mk @@ -399,6 +399,7 @@ $(eval $(call gb_Library_add_exception_objects,vcl,\ vcl/source/app/unohelp2 \ vcl/source/app/unohelp \ vcl/source/app/vclevent \ + vcl/source/app/watchdog \ vcl/source/app/weldutils \ vcl/source/app/winscheduler \ vcl/source/components/dtranscomp \ diff --git a/vcl/README.vars b/vcl/README.vars index 538c6975f26d..495c9679e401 100644 --- a/vcl/README.vars +++ b/vcl/README.vars @@ -36,7 +36,7 @@ OpenGL SAL_FORCEGL - force enable OpenGL SAL_GL_NO_SWAP - disable buffer swapping if set (should show nothing) SAL_GL_SLEEP_ON_SWAP - sleep for half a second on each swap-buffers. -SAL_DISABLE_GL_WATCHDOG - don't start the thread that watches for broken GL drivers +SAL_DISABLE_WATCHDOG - don't start the thread that watches for broken GL/Vulkan/OpenCL drivers Skia ---- diff --git a/vcl/inc/opengl/watchdog.hxx b/vcl/inc/opengl/watchdog.hxx deleted file mode 100644 index a675894c4ff3..000000000000 --- a/vcl/inc/opengl/watchdog.hxx +++ /dev/null @@ -1,67 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * This file is part of the LibreOffice project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -#ifndef INCLUDED_VCL_INC_OPENGL_WATCHDOG_H -#define INCLUDED_VCL_INC_OPENGL_WATCHDOG_H - -#include <sal/config.h> -#include <salhelper/thread.hxx> -#include <atomic> -#include <vector> - -struct WatchdogTimingsValues -{ - /// delays to take various actions in 1/4 of a second increments. - int mnDisableEntries; - int const mnAbortAfter; -}; - -enum class WatchdogTimingMode -{ - NORMAL, - SHADER_COMPILE -}; - -class WatchdogTimings -{ -private: - std::vector<WatchdogTimingsValues> maTimingValues; - std::atomic<bool> mbRelaxed; - -public: - WatchdogTimings(); - - void setRelax(bool bRelaxed) - { - mbRelaxed = bRelaxed; - } - - WatchdogTimingsValues const & getWatchdogTimingsValues(WatchdogTimingMode eMode) - { - size_t index = (eMode == WatchdogTimingMode::SHADER_COMPILE) ? 1 : 0; - index = mbRelaxed ? index + 2 : index; - - return maTimingValues[index]; - } -}; - -class OpenGLWatchdogThread : private salhelper::Thread -{ - OpenGLWatchdogThread(); - virtual void execute() override; -public: - using salhelper::Thread::acquire; - using salhelper::Thread::release; - static void start(); - static void stop(); -}; - -#endif // INCLUDED_VCL_INC_OPENGL_WATCHDOG_H - -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/inc/opengl/zone.hxx b/vcl/inc/opengl/zone.hxx index 90b7f48fd5a8..621ee4289b27 100644 --- a/vcl/inc/opengl/zone.hxx +++ b/vcl/inc/opengl/zone.hxx @@ -13,48 +13,19 @@ #include <sal/config.h> #include <sal/types.h> #include <vcl/dllapi.h> - -#include <atomic> -#include <csignal> -#include <type_traits> - -class OpenGLWatchdogThread; +#include <comphelper/crashzone.hxx> /** * We want to be able to detect if a given crash came * from the OpenGL code, so use this helper to track that. */ -class VCL_DLLPUBLIC OpenGLZone { - friend class OpenGLWatchdogThread; - friend class OpenGLSalGraphicsImpl; - - // gnEnterCount and gnLeaveCount are accessed both from multiple threads (cf. - // OpenGLWatchdogThread::execute; so need to be of atomic type) and from signal handlers (cf. - // VCLExceptionSignal_impl; so need to be of lock-free atomic type). sig_atomic_t is chosen as - // the underlying type under the assumption that it is most likely to lead to an atomic type - // that is actually lock-free. However, gnEnterCount and gnLeaveCount are both monotonically - // increasing, so will eventually overflow, so the underlying type better be unsigned, which - // sig_atomic_t is not guaranteed to be: -#if !defined ARM32 || (defined ARM32 && defined __ARM_PCS_VFP) - using AtomicCounter = std::atomic<std::make_unsigned_t<std::sig_atomic_t>>; - static_assert(AtomicCounter::is_always_lock_free); -#else - using AtomicCounter = volatile std::make_unsigned_t<std::sig_atomic_t>; -#endif - - /// how many times have we entered a GL zone - static AtomicCounter gnEnterCount; - /// how many times have we left a new GL zone - static AtomicCounter gnLeaveCount; - - static void enter() { gnEnterCount++; } - static void leave() { gnLeaveCount++; } +class VCL_DLLPUBLIC OpenGLZone : public CrashZone< OpenGLZone > { public: - OpenGLZone() { enter(); } - ~OpenGLZone() { leave(); } - static bool isInZone() { return gnEnterCount != gnLeaveCount; } static void hardDisable(); static void relaxWatchdogTimings(); + static const CrashWatchdogTimingsValues& getCrashWatchdogTimingsValues(); + static void checkDebug( int nUnchanged, const CrashWatchdogTimingsValues& aTimingValues ); + static const char* name() { return "OpenGL"; } }; /// Create this to not only enter the zone, but set VCL context. diff --git a/vcl/inc/watchdog.hxx b/vcl/inc/watchdog.hxx new file mode 100644 index 000000000000..ee357fb2dc89 --- /dev/null +++ b/vcl/inc/watchdog.hxx @@ -0,0 +1,32 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef INCLUDED_VCL_INC_WATCHDOG_H +#define INCLUDED_VCL_INC_WATCHDOG_H + +#include <sal/config.h> +#include <salhelper/thread.hxx> +#include <atomic> +#include <vector> + +class WatchdogThread : private salhelper::Thread +{ + WatchdogThread(); + virtual void execute() override; + +public: + using salhelper::Thread::acquire; + using salhelper::Thread::release; + static void start(); + static void stop(); +}; + +#endif // INCLUDED_VCL_INC_WATCHDOG_H + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/app/svmain.cxx b/vcl/source/app/svmain.cxx index b8c97d08d9c8..207a6ff4eaad 100644 --- a/vcl/source/app/svmain.cxx +++ b/vcl/source/app/svmain.cxx @@ -88,7 +88,7 @@ #include <opencl/OpenCLZone.hxx> #include <opengl/zone.hxx> -#include <opengl/watchdog.hxx> +#include <watchdog.hxx> #include <basegfx/utils/systemdependentdata.hxx> #include <tools/diagnose_ex.h> @@ -128,7 +128,7 @@ static oslSignalAction VCLExceptionSignal_impl( void* /*pData*/, oslSignalInfo* { OpenCLZone::hardDisable(); #ifdef _WIN32 - if (OpenCLZone::isInInitialTest()) + if (OpenCLInitialZone::isInZone()) TerminateProcess(GetCurrentProcess(), EXITHELPER_NORMAL_RESTART); #endif } @@ -212,9 +212,7 @@ int ImplSVMain() pSVData->mxAccessBridge.clear(); } -#if HAVE_FEATURE_OPENGL - OpenGLWatchdogThread::stop(); -#endif + WatchdogThread::stop(); DeInitVCL(); return nReturn; diff --git a/vcl/source/app/watchdog.cxx b/vcl/source/app/watchdog.cxx new file mode 100644 index 000000000000..c6c43e7e013b --- /dev/null +++ b/vcl/source/app/watchdog.cxx @@ -0,0 +1,144 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <watchdog.hxx> + +#include <config_features.h> + +#include <osl/conditn.hxx> +#include <rtl/ref.hxx> +#include <rtl/string.hxx> +#include <sal/log.hxx> +#include <opengl/zone.hxx> + +#include <stdlib.h> + +namespace +{ +static volatile bool gbWatchdogFiring = false; +static osl::Condition* gpWatchdogExit = nullptr; +static rtl::Reference<WatchdogThread> gxWatchdog; + +template <typename Zone> struct WatchdogHelper +{ + static inline sal_uInt64 nLastEnters = 0; + static inline int nUnchanged = 0; // how many unchanged nEnters + static inline bool bFired = false; + static inline bool bAbortFired = false; + static void setLastEnters() { nLastEnters = Zone::enterCount(); } + static void check() + { + if (Zone::isInZone()) + { + const CrashWatchdogTimingsValues& aTimingValues = Zone::getCrashWatchdogTimingsValues(); + + if (nLastEnters == Zone::enterCount()) + nUnchanged++; + else + nUnchanged = 0; + Zone::checkDebug(nUnchanged, aTimingValues); + + // Not making progress + if (nUnchanged >= aTimingValues.mnDisableEntries) + { + if (!bFired) + { + gbWatchdogFiring = true; + SAL_WARN("vcl.watchdog", + OStringLiteral("Watchdog triggered: hard disable ") + Zone::name()); + Zone::hardDisable(); + gbWatchdogFiring = false; + } + bFired = true; + + // we can hang using VCL in the abort handling -> be impatient + if (bAbortFired) + { + SAL_WARN("vcl.watchdog", + OStringLiteral("Watchdog gave up: hard exiting ") + Zone::name()); + _Exit(1); + } + } + + // Not making even more progress + if (nUnchanged >= aTimingValues.mnAbortAfter) + { + if (!bAbortFired) + { + SAL_WARN("vcl.watchdog", + OStringLiteral("Watchdog gave up: aborting ") + Zone::name()); + gbWatchdogFiring = true; + std::abort(); + } + // coverity[dead_error_line] - we might have caught SIGABRT and failed to exit yet + bAbortFired = true; + } + } + else + { + nUnchanged = 0; + } + } +}; + +} // namespace + +WatchdogThread::WatchdogThread() + : salhelper::Thread("Crash Watchdog") +{ +} + +void WatchdogThread::execute() +{ + TimeValue aQuarterSecond(0, 1000 * 1000 * 1000 * 0.25); + do + { +#if HAVE_FEATURE_OPENGL + WatchdogHelper<OpenGLZone>::setLastEnters(); +#endif + + gpWatchdogExit->wait(&aQuarterSecond); + +#if HAVE_FEATURE_OPENGL + WatchdogHelper<OpenGLZone>::check(); +#endif + + } while (!gpWatchdogExit->check()); +} + +void WatchdogThread::start() +{ + if (gxWatchdog != nullptr) + return; // already running + if (getenv("SAL_DISABLE_WATCHDOG")) + return; + gpWatchdogExit = new osl::Condition(); + gxWatchdog.set(new WatchdogThread()); + gxWatchdog->launch(); +} + +void WatchdogThread::stop() +{ + if (gbWatchdogFiring) + return; // in watchdog thread + + if (gpWatchdogExit) + gpWatchdogExit->set(); + + if (gxWatchdog.is()) + { + gxWatchdog->join(); + gxWatchdog.clear(); + } + + delete gpWatchdogExit; + gpWatchdogExit = nullptr; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/opengl/OpenGLHelper.cxx b/vcl/source/opengl/OpenGLHelper.cxx index 1c9fa66436c8..081bf240cc8b 100644 --- a/vcl/source/opengl/OpenGLHelper.cxx +++ b/vcl/source/opengl/OpenGLHelper.cxx @@ -29,12 +29,11 @@ #include <unordered_map> #include <opengl/zone.hxx> -#include <opengl/watchdog.hxx> -#include <osl/conditn.hxx> #include <vcl/opengl/OpenGLWrapper.hxx> #include <vcl/opengl/OpenGLContext.hxx> #include <desktop/crashreport.hxx> #include <bitmapwriteaccess.hxx> +#include <watchdog.hxx> #if defined UNX && !defined MACOSX && !defined IOS && !defined ANDROID && !defined HAIKU #include <opengl/x11/X11DeviceInfo.hxx> @@ -45,8 +44,6 @@ #include "GLMHelper.hxx" static bool volatile gbInShaderCompile = false; -OpenGLZone::AtomicCounter OpenGLZone::gnEnterCount = 0; -OpenGLZone::AtomicCounter OpenGLZone::gnLeaveCount = 0; namespace { @@ -793,121 +790,50 @@ bool OpenGLHelper::supportsVCLOpenGL() return !bDisableGL && !bBlacklisted; } -namespace { - static volatile bool gbWatchdogFiring = false; - static osl::Condition* gpWatchdogExit = nullptr; - static WatchdogTimings gWatchdogTimings; - static rtl::Reference<OpenGLWatchdogThread> gxWatchdog; -} - -WatchdogTimings::WatchdogTimings() - : maTimingValues{ - {{6, 20} /* 1.5s, 5s */, {20, 120} /* 5s, 30s */, - {60, 240} /* 15s, 60s */, {60, 240} /* 15s, 60s */} - } - , mbRelaxed(false) +namespace { -} -OpenGLWatchdogThread::OpenGLWatchdogThread() - : salhelper::Thread("OpenGL Watchdog") +enum class CrashWatchdogTimingMode { -} + NORMAL, + SHADER_COMPILE +}; -void OpenGLWatchdogThread::execute() +class CrashWatchdogTimings { - int nUnchanged = 0; // how many unchanged nEnters - TimeValue aQuarterSecond(0, 1000*1000*1000*0.25); - bool bAbortFired = false; +private: + std::vector<CrashWatchdogTimingsValues> maTimingValues; + std::atomic<bool> mbRelaxed; - do { - sal_uInt64 nLastEnters = OpenGLZone::gnEnterCount; +public: + CrashWatchdogTimings(); - gpWatchdogExit->wait(&aQuarterSecond); + void setRelax(bool bRelaxed) + { + mbRelaxed = bRelaxed; + } - if (OpenGLZone::isInZone()) - { - // The shader compiler can take a long time, first time. - WatchdogTimingMode eMode = gbInShaderCompile ? WatchdogTimingMode::SHADER_COMPILE : WatchdogTimingMode::NORMAL; - WatchdogTimingsValues aTimingValues = gWatchdogTimings.getWatchdogTimingsValues(eMode); + CrashWatchdogTimingsValues const & getWatchdogTimingsValues(CrashWatchdogTimingMode eMode) + { + size_t index = (eMode == CrashWatchdogTimingMode::SHADER_COMPILE) ? 1 : 0; + index = mbRelaxed ? index + 2 : index; - if (nLastEnters == OpenGLZone::gnEnterCount) - nUnchanged++; - else - nUnchanged = 0; - SAL_INFO("vcl.opengl", "GL watchdog - unchanged " << - nUnchanged << " enter count " << - OpenGLZone::gnEnterCount << " type " << - (eMode == WatchdogTimingMode::SHADER_COMPILE ? "in shader" : "normal gl") << - "breakpoints mid: " << aTimingValues.mnDisableEntries << - " max " << aTimingValues.mnAbortAfter); - - // Not making progress - if (nUnchanged >= aTimingValues.mnDisableEntries) - { - static bool bFired = false; - if (!bFired) - { - gbWatchdogFiring = true; - SAL_WARN("vcl.opengl", "Watchdog triggered: hard disable GL"); - OpenGLZone::hardDisable(); - gbWatchdogFiring = false; - } - bFired = true; - - // we can hang using VCL in the abort handling -> be impatient - if (bAbortFired) - { - SAL_WARN("vcl.opengl", "Watchdog gave up: hard exiting"); - _exit(1); - } - } + return maTimingValues[index]; + } +}; - // Not making even more progress - if (nUnchanged >= aTimingValues.mnAbortAfter) - { - if (!bAbortFired) - { - SAL_WARN("vcl.opengl", "Watchdog gave up: aborting"); - gbWatchdogFiring = true; - std::abort(); - } - // coverity[dead_error_line] - we might have caught SIGABRT and failed to exit yet - bAbortFired = true; - } - } - else - { - nUnchanged = 0; - } - } while (!gpWatchdogExit->check()); -} +static CrashWatchdogTimings gWatchdogTimings; -void OpenGLWatchdogThread::start() +CrashWatchdogTimings::CrashWatchdogTimings() + : maTimingValues{ + {{6, 20} /* 1.5s, 5s */, {20, 120} /* 5s, 30s */, + {60, 240} /* 15s, 60s */, {60, 240} /* 15s, 60s */} + } + , mbRelaxed(false) { - assert (gxWatchdog == nullptr); - gpWatchdogExit = new osl::Condition(); - gxWatchdog.set(new OpenGLWatchdogThread()); - gxWatchdog->launch(); } -void OpenGLWatchdogThread::stop() -{ - if (gbWatchdogFiring) - return; // in watchdog thread - - if (gpWatchdogExit) - gpWatchdogExit->set(); - - if (gxWatchdog.is()) - { - gxWatchdog->join(); - gxWatchdog.clear(); - } - - delete gpWatchdogExit; - gpWatchdogExit = nullptr; -} +} // namespace /** * Called from a signal handler or watchdog thread if we get @@ -932,8 +858,6 @@ void OpenGLZone::hardDisable() css::configuration::theDefaultProvider::get( comphelper::getProcessComponentContext()), css::uno::UNO_QUERY_THROW)->flush(); - - OpenGLWatchdogThread::stop(); } } @@ -942,6 +866,22 @@ void OpenGLZone::relaxWatchdogTimings() gWatchdogTimings.setRelax(true); } +void OpenGLZone::checkDebug( int nUnchanged, const CrashWatchdogTimingsValues& aTimingValues ) +{ + SAL_INFO("vcl.watchdog", "GL watchdog - unchanged " + << nUnchanged << " enter count " << enterCount() << " type " + << (gbInShaderCompile ? "in shader" : "normal gl") + << " breakpoints mid: " << aTimingValues.mnDisableEntries + << " max " << aTimingValues.mnAbortAfter); +} + +const CrashWatchdogTimingsValues& OpenGLZone::getCrashWatchdogTimingsValues() +{ + // The shader compiler can take a long time, first time. + CrashWatchdogTimingMode eMode = gbInShaderCompile ? CrashWatchdogTimingMode::SHADER_COMPILE : CrashWatchdogTimingMode::NORMAL; + return gWatchdogTimings.getWatchdogTimingsValues(eMode); +} + OpenGLVCLContextZone::OpenGLVCLContextZone() { OpenGLContext::makeVCLCurrent(); @@ -1019,10 +959,8 @@ bool OpenGLHelper::isVCLOpenGLEnabled() } if (bRet) - { - if (!getenv("SAL_DISABLE_GL_WATCHDOG")) - OpenGLWatchdogThread::start(); - } + WatchdogThread::start(); + CrashReporter::addKeyValue("UseOpenGL", OUString::boolean(bRet), CrashReporter::Write); return bRet; |