diff options
Diffstat (limited to 'vcl/source/app/watchdog.cxx')
-rw-r--r-- | vcl/source/app/watchdog.cxx | 168 |
1 files changed, 168 insertions, 0 deletions
diff --git a/vcl/source/app/watchdog.cxx b/vcl/source/app/watchdog.cxx new file mode 100644 index 0000000000..dc2153e639 --- /dev/null +++ b/vcl/source/app/watchdog.cxx @@ -0,0 +1,168 @@ +/* -*- 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 <comphelper/debuggerinfo.hxx> +#include <opengl/zone.hxx> +#include <skia/zone.hxx> + +#include <stdlib.h> + +#if defined HAVE_VALGRIND_HEADERS +#include <valgrind/memcheck.h> +#endif + +#include <atomic> + +namespace +{ +std::atomic<bool> gbWatchdogFiring = false; +osl::Condition* gpWatchdogExit = nullptr; +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", "Watchdog triggered: hard disable " << Zone::name()); +#ifdef DBG_UTIL + std::abort(); +#else + Zone::hardDisable(); + gbWatchdogFiring = false; +#endif + } + bFired = true; + + // we can hang using VCL in the abort handling -> be impatient + if (bAbortFired) + { + SAL_WARN("vcl.watchdog", "Watchdog gave up: hard exiting " << Zone::name()); + _Exit(1); + } + } + + // Not making even more progress + if (nUnchanged >= aTimingValues.mnAbortAfter) + { + if (!bAbortFired) + { + SAL_WARN("vcl.watchdog", "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 +#if HAVE_FEATURE_SKIA + WatchdogHelper<SkiaZone>::setLastEnters(); +#endif + + gpWatchdogExit->wait(&aQuarterSecond); + +#if defined HAVE_VALGRIND_HEADERS + if (RUNNING_ON_VALGRIND) + continue; +#endif +#if defined DBG_UTIL + if (comphelper::isDebuggerAttached()) + continue; +#endif + +#if HAVE_FEATURE_OPENGL + WatchdogHelper<OpenGLZone>::check(); +#endif +#if HAVE_FEATURE_SKIA + WatchdogHelper<SkiaZone>::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: */ |