From 95f5d82d37405bde429133d9348a680dc7682e1a Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 8 Mar 2016 23:45:51 -0800 Subject: [PATCH] Add a watchdog thread to trigger a crash on detecting a deadlock --- interface/src/Application.cpp | 48 +++++++++++++++++++++++++++++++++++ interface/src/Application.h | 1 + interface/src/Menu.cpp | 2 ++ interface/src/Menu.h | 1 + 4 files changed, 52 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 660e597752..0d6a36f583 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -235,6 +235,42 @@ const QHash Application::_acceptedExtensi { AVA_JSON_EXTENSION, &Application::askToWearAvatarAttachmentUrl } }; +class DeadlockWatchdogThread : public QThread { +public: + static const unsigned long HEARTBEAT_CHECK_INTERVAL_SECS = 1; + static const unsigned long HEARTBEAT_UPDATE_INTERVAL_SECS = 1; + static const unsigned long MAX_HEARTBEAT_AGE_USECS = 10 * USECS_PER_SECOND; + + // Set the heartbeat on launch + DeadlockWatchdogThread() { + QTimer* heartbeatTimer = new QTimer(); + connect(heartbeatTimer, &QTimer::timeout, [this] { + _heartbeat = usecTimestampNow(); + }); + heartbeatTimer->start(HEARTBEAT_UPDATE_INTERVAL_SECS * MSECS_PER_SECOND); + } + + void deadlockDetectionCrash() { + uint32_t* crashTrigger = nullptr; + *crashTrigger = 0xDEAD10CC; + } + + void run() override { + while (!qApp->isAboutToQuit()) { + QThread::sleep(HEARTBEAT_UPDATE_INTERVAL_SECS); + auto now = usecTimestampNow(); + auto lastHeartbeatAge = now - _heartbeat; + if (lastHeartbeatAge > MAX_HEARTBEAT_AGE_USECS) { + deadlockDetectionCrash(); + } + } + } + + static std::atomic _heartbeat; +}; + +std::atomic DeadlockWatchdogThread::_heartbeat; + #ifdef Q_OS_WIN class MyNativeEventFilter : public QAbstractNativeEventFilter { public: @@ -457,6 +493,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : auto nodeList = DependencyManager::get(); + // Set up a watchdog thread to intentionally crash the application on deadlocks + (new DeadlockWatchdogThread())->start(); + qCDebug(interfaceapp) << "[VERSION] Build sequence:" << qPrintable(applicationVersion()); _bookmarks = new Bookmarks(); // Before setting up the menu @@ -4960,6 +4999,15 @@ void Application::crashApplication() { Q_UNUSED(value); } +void Application::deadlockApplication() { + qCDebug(interfaceapp) << "Intentionally deadlocked Interface"; + // Using a loop that will *technically* eventually exit (in ~600 billion years) + // to avoid compiler warnings about a loop that will never exit + for (uint64_t i = 1; i != 0; ++i) { + QThread::sleep(1); + } +} + void Application::setActiveDisplayPlugin(const QString& pluginName) { auto menu = Menu::getInstance(); foreach(DisplayPluginPointer displayPlugin, PluginManager::getInstance()->getDisplayPlugins()) { diff --git a/interface/src/Application.h b/interface/src/Application.h index 3a727db533..c93b7431f3 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -276,6 +276,7 @@ public slots: void reloadResourceCaches(); void crashApplication(); + void deadlockApplication(); void rotationModeChanged(); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index d605516380..164f94a094 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -590,6 +590,8 @@ Menu::Menu() { addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::DisplayCrashOptions, 0, true); // Developer > Crash Application addActionToQMenuAndActionHash(developerMenu, MenuOption::CrashInterface, 0, qApp, SLOT(crashApplication())); + // Developer > Deadlock Application + addActionToQMenuAndActionHash(developerMenu, MenuOption::DeadlockInterface, 0, qApp, SLOT(deadlockApplication())); // Developer > Log... addActionToQMenuAndActionHash(developerMenu, MenuOption::Log, Qt::CTRL | Qt::SHIFT | Qt::Key_L, diff --git a/interface/src/Menu.h b/interface/src/Menu.h index fb00416af0..6d5fd45b66 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -66,6 +66,7 @@ namespace MenuOption { const QString CopyPath = "Copy Path to Clipboard"; const QString CoupleEyelids = "Couple Eyelids"; const QString CrashInterface = "Crash Interface"; + const QString DeadlockInterface = "Deadlock Interface"; const QString DecreaseAvatarSize = "Decrease Avatar Size"; const QString DeleteBookmark = "Delete Bookmark..."; const QString DisableActivityLogger = "Disable Activity Logger";