forked from qt-creator/qt-creator
Guard against crashing plugins
If a plugin crashes, ask the user if that plugin should be disabled. For this track which plugin currently is changing its state. Remove that information if the state change was successful. At startup check if there is a plugin for which we wrote, but not removed that information. This is especially interesting if the user installed 3rdparty plugins. Change-Id: I5729aa5c786b653d5bd53304f4fbeaca35ec9e71 Reviewed-by: Leena Miettinen <riitta-leena.miettinen@qt.io> Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
This commit is contained in:
@@ -680,6 +680,7 @@ int main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PluginManager::checkForProblematicPlugins();
|
||||||
PluginManager::loadPlugins();
|
PluginManager::loadPlugins();
|
||||||
if (coreplugin->hasError()) {
|
if (coreplugin->hasError()) {
|
||||||
displayError(msgCoreLoadFailure(coreplugin->errorString()));
|
displayError(msgCoreLoadFailure(coreplugin->errorString()));
|
||||||
|
|||||||
@@ -31,14 +31,18 @@
|
|||||||
#include "iplugin.h"
|
#include "iplugin.h"
|
||||||
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
|
#include <QCryptographicHash>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QEventLoop>
|
#include <QEventLoop>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
#include <QGuiApplication>
|
||||||
#include <QLibrary>
|
#include <QLibrary>
|
||||||
#include <QLibraryInfo>
|
#include <QLibraryInfo>
|
||||||
|
#include <QMessageBox>
|
||||||
#include <QMetaProperty>
|
#include <QMetaProperty>
|
||||||
|
#include <QPushButton>
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
#include <QSysInfo>
|
#include <QSysInfo>
|
||||||
#include <QTextStream>
|
#include <QTextStream>
|
||||||
@@ -1353,6 +1357,94 @@ bool PluginManagerPrivate::loadQueue(PluginSpec *spec,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class LockFile
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static QString filePath(PluginManagerPrivate *pm)
|
||||||
|
{
|
||||||
|
return QFileInfo(pm->settings->fileName()).absolutePath() + '/'
|
||||||
|
+ QCoreApplication::applicationName() + '.'
|
||||||
|
+ QCryptographicHash::hash(QCoreApplication::applicationDirPath().toUtf8(),
|
||||||
|
QCryptographicHash::Sha1)
|
||||||
|
.left(8)
|
||||||
|
.toHex()
|
||||||
|
+ ".lock";
|
||||||
|
}
|
||||||
|
|
||||||
|
static Utils::optional<QString> lockedPluginName(PluginManagerPrivate *pm)
|
||||||
|
{
|
||||||
|
const QString lockFilePath = LockFile::filePath(pm);
|
||||||
|
if (QFile::exists(lockFilePath)) {
|
||||||
|
QFile f(lockFilePath);
|
||||||
|
f.open(QIODevice::ReadOnly);
|
||||||
|
const auto pluginName = QString::fromUtf8(f.readLine()).trimmed();
|
||||||
|
f.close();
|
||||||
|
return pluginName;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
LockFile(PluginManagerPrivate *pm, PluginSpec *spec)
|
||||||
|
: m_filePath(filePath(pm))
|
||||||
|
{
|
||||||
|
QFile f(m_filePath);
|
||||||
|
f.open(QIODevice::WriteOnly);
|
||||||
|
f.write(spec->name().toUtf8());
|
||||||
|
f.write("\n");
|
||||||
|
f.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
~LockFile() { QFile::remove(m_filePath); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString m_filePath;
|
||||||
|
};
|
||||||
|
|
||||||
|
void PluginManagerPrivate::checkForProblematicPlugins()
|
||||||
|
{
|
||||||
|
const Utils::optional<QString> pluginName = LockFile::lockedPluginName(this);
|
||||||
|
if (pluginName) {
|
||||||
|
PluginSpec *spec = pluginByName(*pluginName);
|
||||||
|
if (spec && !spec->isRequired()) {
|
||||||
|
const QSet<PluginSpec *> dependents = PluginManager::pluginsRequiringPlugin(spec);
|
||||||
|
auto dependentsNames = Utils::transform<QStringList>(dependents, &PluginSpec::name);
|
||||||
|
std::sort(dependentsNames.begin(), dependentsNames.end());
|
||||||
|
const QString dependentsList = dependentsNames.join(", ");
|
||||||
|
const QString pluginsMenu = HostOsInfo::isMacHost()
|
||||||
|
? tr("%1 > About Plugins")
|
||||||
|
.arg(QGuiApplication::applicationDisplayName())
|
||||||
|
: tr("Help > About Plugins");
|
||||||
|
const QString otherPluginsText = tr("The following plugins depend on "
|
||||||
|
"%1 and are also disabled: %2.\n\n")
|
||||||
|
.arg(spec->name(), dependentsList);
|
||||||
|
const QString detailsText = (dependents.isEmpty() ? QString() : otherPluginsText)
|
||||||
|
+ tr("Disable plugins permanently in %1.").arg(pluginsMenu);
|
||||||
|
const QString text = tr("It looks like %1 closed because of a problem with the \"%2\" "
|
||||||
|
"plugin. Temporarily disable the plugin?")
|
||||||
|
.arg(QGuiApplication::applicationDisplayName(), spec->name());
|
||||||
|
QMessageBox dialog;
|
||||||
|
dialog.setIcon(QMessageBox::Question);
|
||||||
|
dialog.setText(text);
|
||||||
|
dialog.setDetailedText(detailsText);
|
||||||
|
QPushButton *disableButton = dialog.addButton(tr("Disable Plugin"),
|
||||||
|
QMessageBox::AcceptRole);
|
||||||
|
dialog.addButton(tr("Continue"), QMessageBox::RejectRole);
|
||||||
|
dialog.exec();
|
||||||
|
if (dialog.clickedButton() == disableButton) {
|
||||||
|
spec->d->setForceDisabled(true);
|
||||||
|
for (PluginSpec *other : dependents)
|
||||||
|
other->d->setForceDisabled(true);
|
||||||
|
enableDependenciesIndirectly();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PluginManager::checkForProblematicPlugins()
|
||||||
|
{
|
||||||
|
d->checkForProblematicPlugins();
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\internal
|
\internal
|
||||||
*/
|
*/
|
||||||
@@ -1365,6 +1457,8 @@ void PluginManagerPrivate::loadPlugin(PluginSpec *spec, PluginSpec::State destSt
|
|||||||
if (!spec->isEffectivelyEnabled() && destState == PluginSpec::Loaded)
|
if (!spec->isEffectivelyEnabled() && destState == PluginSpec::Loaded)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
LockFile f(this, spec);
|
||||||
|
|
||||||
switch (destState) {
|
switch (destState) {
|
||||||
case PluginSpec::Running:
|
case PluginSpec::Running:
|
||||||
profilingReport(">initializeExtensions", spec);
|
profilingReport(">initializeExtensions", spec);
|
||||||
|
|||||||
@@ -96,6 +96,7 @@ public:
|
|||||||
static const QStringList allErrors();
|
static const QStringList allErrors();
|
||||||
static QSet<PluginSpec *> pluginsRequiringPlugin(PluginSpec *spec);
|
static QSet<PluginSpec *> pluginsRequiringPlugin(PluginSpec *spec);
|
||||||
static QSet<PluginSpec *> pluginsRequiredByPlugin(PluginSpec *spec);
|
static QSet<PluginSpec *> pluginsRequiredByPlugin(PluginSpec *spec);
|
||||||
|
static void checkForProblematicPlugins();
|
||||||
|
|
||||||
// Settings
|
// Settings
|
||||||
static void setSettings(QSettings *settings);
|
static void setSettings(QSettings *settings);
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ public:
|
|||||||
void removeObject(QObject *obj);
|
void removeObject(QObject *obj);
|
||||||
|
|
||||||
// Plugin operations
|
// Plugin operations
|
||||||
|
void checkForProblematicPlugins();
|
||||||
void loadPlugins();
|
void loadPlugins();
|
||||||
void shutdown();
|
void shutdown();
|
||||||
void setPluginPaths(const QStringList &paths);
|
void setPluginPaths(const QStringList &paths);
|
||||||
|
|||||||
Reference in New Issue
Block a user