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:
This commit is contained in:
Sebastian Kügler 2013-12-04 04:15:24 +01:00
parent 5540fdeaa5
commit ff40813454
4 changed files with 68 additions and 12 deletions

View File

@ -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

View File

@ -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();

View File

@ -26,18 +26,23 @@
#include <QList>
#include <QTimer>
#include <qplatformdefs.h>
#include <QQmlEngine>
#include <QQmlComponent>
#include <config-prefix.h>
#include "shellcorona.h"
#include <kcrash.h>
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");
}
}

View File

@ -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<Private> d;
static int crashes;
static void crashHandler(int signal);
};
#endif /* SHELLMANAGER_H */