From ff408134547d690ac40e5186b0b68c5100e2a050 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20K=C3=BCgler?= Date: Wed, 4 Dec 2013 04:15:24 +0100 Subject: [PATCH] Crash recovery for plasma-shell We restart plasma-shell after crashes. When it crashes subsequently on startup, and more than two times in a row, we give up in order to not endlessly loop. Once the shell process stays alive for at least 15 seconds, we reset the crash counter, so a later crash, at runtime rather than on startup will still be recovered from. This logic is very similar as to how kwin handles it. DIGEST: --- src/shell/CMakeLists.txt | 2 ++ src/shell/main.cpp | 25 +++++++++++---------- src/shell/shellmanager.cpp | 45 ++++++++++++++++++++++++++++++++++++++ src/shell/shellmanager.h | 8 +++++++ 4 files changed, 68 insertions(+), 12 deletions(-) diff --git a/src/shell/CMakeLists.txt b/src/shell/CMakeLists.txt index 5c15ef48f..47d1eec20 100644 --- a/src/shell/CMakeLists.txt +++ b/src/shell/CMakeLists.txt @@ -15,6 +15,7 @@ find_package(Qt5Qml REQUIRED) find_package(Qt5Quick REQUIRED) find_package(Qt5Script REQUIRED) find_package(KCoreAddons REQUIRED) +find_package(KCrash REQUIRED) find_package(Solid REQUIRED) find_package(KActivities REQUIRED) @@ -64,6 +65,7 @@ target_link_libraries(plasma-shell KF5::KIOCore KF5::KWindowSystem KF5::KCoreAddons + KF5::KCrash Plasma PlasmaQuick Qt5::Script diff --git a/src/shell/main.cpp b/src/shell/main.cpp index 705b5cc56..529162468 100644 --- a/src/shell/main.cpp +++ b/src/shell/main.cpp @@ -36,17 +36,25 @@ int main(int argc, char** argv) QApplication app(argc, argv); app.setApplicationVersion(version); - QCommandLineOption dbg = QCommandLineOption(QStringList() << QStringLiteral("d") << QStringLiteral("qmljsdebugger"), - QStringLiteral("Enable QML Javascript debugger")); + QCommandLineOption dbg = QCommandLineOption(QStringList() << QStringLiteral("d") << + QStringLiteral("qmljsdebugger"), + QStringLiteral("Enable QML Javascript debugger")); - QCommandLineOption windowed = QCommandLineOption(QStringList() << QStringLiteral("w") << QStringLiteral("windowed"), - QStringLiteral("force a windowed view for desktop purposes")); + QCommandLineOption windowed = QCommandLineOption(QStringList() << QStringLiteral("w") << + QStringLiteral("windowed"), + QStringLiteral("Force a windowed view for testing purposes")); + + QCommandLineOption crashesOption(QStringLiteral("crashes"), + QStringLiteral("Recent number of crashes"), + QStringLiteral("n")); parser.addVersionOption(); parser.addHelpOption(); parser.setApplicationDescription(description); parser.addOption(dbg); parser.addOption(windowed); + parser.addOption(crashesOption); + parser.process(app); //enable the QML debugger only if --qmljsdebugger (or -d) is passed as a command line arg @@ -56,16 +64,9 @@ int main(int argc, char** argv) } Plasma::PluginLoader::setPluginLoader(new ShellPluginLoader); - // DesktopCorona *corona = new DesktopCorona(); - // corona->loadLayout(); - // if (corona->containments().isEmpty()) { - // corona->loadDefaultLayout(); - // } - // corona->processUpdateScripts(); - // corona->checkScreens(); + ShellManager::setCrashCount(parser.value(crashesOption).toInt()); ShellManager::s_forceWindowed = parser.isSet(windowed); - ShellManager::instance(); return app.exec(); diff --git a/src/shell/shellmanager.cpp b/src/shell/shellmanager.cpp index b57963eec..2d74adad8 100644 --- a/src/shell/shellmanager.cpp +++ b/src/shell/shellmanager.cpp @@ -26,18 +26,23 @@ #include #include +#include #include #include #include #include "shellcorona.h" +#include + static const QString s_shellsDir( QString(CMAKE_INSTALL_PREFIX) + "/" + DATA_INSTALL_DIR + "/" + "plasma/shells/"); static const QString s_shellLoaderPath = QString("/contents/loader.qml"); bool ShellManager::s_forceWindowed = false; +int ShellManager::crashes = 0; + // // ShellManager // @@ -60,6 +65,10 @@ public: ShellManager::ShellManager() : d(new Private()) { + // Using setCrashHandler, we end up in an infinite loop instead of quitting, + // use setEmergencySaveFunction instead to avoid this. + KCrash::setEmergencySaveFunction(ShellManager::crashHandler); + QTimer::singleShot(15 * 1000, this, SLOT(resetCrashCount())); connect( &d->shellUpdateDelay, &QTimer::timeout, @@ -203,3 +212,39 @@ ShellManager * ShellManager::instance() return &manager; } +void ShellManager::setCrashCount(int count) +{ + crashes = count; +} + +void ShellManager::resetCrashCount() +{ + crashes = 0; +} + +void ShellManager::crashHandler(int signal) +{ + /* plasma-shell restart logic as crash recovery + * + * We restart plasma-shell after crashes. When it crashes subsequently on startup, + * and more than two times in a row, we give up in order to not endlessly loop. + * Once the shell process stays alive for at least 15 seconds, we reset the crash + * counter, so a later crash, at runtime rather than on startup will still be + * recovered from. + * + * This logic is very similar as to how kwin handles it. + */ + crashes++; + fprintf(stderr, "Application::crashHandler() called with signal %d; recent crashes: %d\n", signal, crashes); + char cmd[1024]; + sprintf(cmd, "%s --crashes %d &", + QFile::encodeName(QCoreApplication::applicationFilePath()).constData(), crashes); + + if (crashes < 3) { + sleep(1); + system(cmd); + } else { + fprintf(stderr, "Too many crashes in short order, not restarting automatically.\n"); + } + +} diff --git a/src/shell/shellmanager.h b/src/shell/shellmanager.h index b8498d919..398bc7844 100644 --- a/src/shell/shellmanager.h +++ b/src/shell/shellmanager.h @@ -34,6 +34,8 @@ public: static bool s_forceWindowed; + static void setCrashCount(int count); + protected Q_SLOTS: void registerHandler(QObject * handler); void deregisterHandler(QObject * handler); @@ -45,11 +47,17 @@ public Q_SLOTS: Q_SIGNALS: void shellChanged(const QString & shell); +private Q_SLOTS: + void resetCrashCount(); + private: ShellManager(); class Private; const QScopedPointer d; + + static int crashes; + static void crashHandler(int signal); }; #endif /* SHELLMANAGER_H */