diff --git a/src/app/app.pro b/src/app/app.pro index b86678ae169..2fa34de3e36 100644 --- a/src/app/app.pro +++ b/src/app/app.pro @@ -8,6 +8,8 @@ DESTDIR = $$IDE_APP_PATH VERSION = $$QTCREATOR_VERSION QT -= testlib +DEFINES += IDE_LIBEXEC_PATH=\\\"$$IDE_LIBEXEC_PATH\\\" + HEADERS += ../tools/qtcreatorcrashhandler/crashhandlersetup.h SOURCES += main.cpp ../tools/qtcreatorcrashhandler/crashhandlersetup.cpp diff --git a/src/app/app.qbs b/src/app/app.qbs index 2eac52ac64a..4a00abe5861 100644 --- a/src/app/app.qbs +++ b/src/app/app.qbs @@ -23,6 +23,7 @@ QtcProduct { installDir: qtc.ide_bin_path property bool qtcRunnable: true + cpp.defines: base.concat(['IDE_LIBEXEC_PATH="' + qtc.ide_libexec_path + '"']) cpp.rpaths: qbs.targetOS.contains("macos") ? ["@executable_path/../Frameworks"] : ["$ORIGIN/../" + qtc.libDirName + "/qtcreator"] cpp.includePaths: [ diff --git a/src/app/main.cpp b/src/app/main.cpp index c364b4d4b7b..5600be6e975 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -327,7 +327,8 @@ int main(int argc, char **argv) const int threadCount = QThreadPool::globalInstance()->maxThreadCount(); QThreadPool::globalInstance()->setMaxThreadCount(qMax(4, 2 * threadCount)); - CrashHandlerSetup setupCrashHandler; // Display a backtrace once a serious signal is delivered. + // Display a backtrace once a serious signal is delivered. + CrashHandlerSetup setupCrashHandler(IDE_LIBEXEC_PATH); #ifdef ENABLE_QT_BREAKPAD QtSystemExceptionHandler systemExceptionHandler; diff --git a/src/tools/qtcreatorcrashhandler/crashhandler.cpp b/src/tools/qtcreatorcrashhandler/crashhandler.cpp index a11c29c95f1..bb61e876e85 100644 --- a/src/tools/qtcreatorcrashhandler/crashhandler.cpp +++ b/src/tools/qtcreatorcrashhandler/crashhandler.cpp @@ -85,10 +85,13 @@ public: class CrashHandlerPrivate { public: - CrashHandlerPrivate(pid_t pid, const QString &signalName, CrashHandler *crashHandler) + CrashHandlerPrivate(pid_t pid, + const QString &signalName, + const QString &appName, + CrashHandler *crashHandler) : pid(pid), creatorInPath(Utils::Environment::systemEnvironment().searchInPath(QLatin1String(QtCreatorExecutable))), - dialog(crashHandler, signalName) {} + dialog(crashHandler, signalName, appName) {} const pid_t pid; const Utils::FileName creatorInPath; // Backup debugger. @@ -100,8 +103,12 @@ public: QStringList restartAppEnvironment; }; -CrashHandler::CrashHandler(pid_t pid, const QString &signalName, QObject *parent) - : QObject(parent), d(new CrashHandlerPrivate(pid, signalName, this)) +CrashHandler::CrashHandler(pid_t pid, + const QString &signalName, + const QString &appName, + RestartCapability restartCap, + QObject *parent) + : QObject(parent), d(new CrashHandlerPrivate(pid, signalName, appName, this)) { connect(&d->backtraceCollector, &BacktraceCollector::error, this, &CrashHandler::onError); connect(&d->backtraceCollector, &BacktraceCollector::backtraceChunk, @@ -112,7 +119,7 @@ CrashHandler::CrashHandler(pid_t pid, const QString &signalName, QObject *parent d->dialog.appendDebugInfo(collectKernelVersionInfo()); d->dialog.appendDebugInfo(collectLinuxDistributionInfo()); - if (!collectRestartAppData()) { + if (restartCap == DisableRestart || !collectRestartAppData()) { d->dialog.disableRestartAppCheckBox(); if (d->creatorInPath.isEmpty()) d->dialog.disableDebugAppButton(); diff --git a/src/tools/qtcreatorcrashhandler/crashhandler.h b/src/tools/qtcreatorcrashhandler/crashhandler.h index 31a50528e26..22042ab197c 100644 --- a/src/tools/qtcreatorcrashhandler/crashhandler.h +++ b/src/tools/qtcreatorcrashhandler/crashhandler.h @@ -38,7 +38,13 @@ class CrashHandler : public QObject { Q_OBJECT public: - explicit CrashHandler(pid_t pid, const QString &signalName, QObject *parent = 0); + enum RestartCapability { EnableRestart, DisableRestart }; + + explicit CrashHandler(pid_t pid, + const QString &signalName, + const QString &appName, + RestartCapability restartCap = EnableRestart, + QObject *parent = 0); ~CrashHandler(); void run(); diff --git a/src/tools/qtcreatorcrashhandler/crashhandlerdialog.cpp b/src/tools/qtcreatorcrashhandler/crashhandlerdialog.cpp index a1361bde2f5..524a1f5177d 100644 --- a/src/tools/qtcreatorcrashhandler/crashhandlerdialog.cpp +++ b/src/tools/qtcreatorcrashhandler/crashhandlerdialog.cpp @@ -39,7 +39,9 @@ static const char SettingsApplication[] = "QtCreator"; static const char SettingsKeySkipWarningAbortingBacktrace[] = "CrashHandler/SkipWarningAbortingBacktrace"; -CrashHandlerDialog::CrashHandlerDialog(CrashHandler *handler, const QString &signalName, +CrashHandlerDialog::CrashHandlerDialog(CrashHandler *handler, + const QString &signalName, + const QString &appName, QWidget *parent) : QDialog(parent), m_crashHandler(handler), @@ -51,6 +53,7 @@ CrashHandlerDialog::CrashHandlerDialog(CrashHandler *handler, const QString &sig m_ui->debugInfoEdit->setReadOnly(true); m_ui->progressBar->setMinimum(0); m_ui->progressBar->setMaximum(0); + m_ui->restartAppCheckBox->setText(tr("&Restart %1 on close").arg(appName)); const QStyle * const style = QApplication::style(); m_ui->closeButton->setIcon(style->standardIcon(QStyle::SP_DialogCloseButton)); @@ -67,7 +70,7 @@ CrashHandlerDialog::CrashHandlerDialog(CrashHandler *handler, const QString &sig m_crashHandler, &CrashHandler::debugApplication); connect(m_ui->closeButton, &QAbstractButton::clicked, this, &CrashHandlerDialog::close); - setApplicationInfo(signalName); + setApplicationInfo(signalName, appName); } CrashHandlerDialog::~CrashHandlerDialog() @@ -120,10 +123,9 @@ void CrashHandlerDialog::disableDebugAppButton() m_ui->debugAppButton->setDisabled(true); } -void CrashHandlerDialog::setApplicationInfo(const QString &signalName) +void CrashHandlerDialog::setApplicationInfo(const QString &signalName, const QString &appName) { - const QString ideName = QLatin1String("Qt Creator"); - const QString title = tr("%1 has closed unexpectedly (Signal \"%2\")").arg(ideName, signalName); + const QString title = tr("%1 has closed unexpectedly (Signal \"%2\")").arg(appName, signalName); const QString introLabelContents = tr( "

%1.

" "

Please file a bug report with the debug information provided below.

") @@ -137,7 +139,7 @@ void CrashHandlerDialog::setApplicationInfo(const QString &signalName) #endif const QString versionInformation = tr( "%1 %2%3, based on Qt %4 (%5 bit)\n") - .arg(ideName, QLatin1String(Core::Constants::IDE_VERSION_LONG), revision, + .arg(appName, QLatin1String(Core::Constants::IDE_VERSION_LONG), revision, QLatin1String(QT_VERSION_STR), QString::number(QSysInfo::WordSize)); m_ui->debugInfoEdit->append(versionInformation); diff --git a/src/tools/qtcreatorcrashhandler/crashhandlerdialog.h b/src/tools/qtcreatorcrashhandler/crashhandlerdialog.h index c222e9c9b5f..006ee0d9588 100644 --- a/src/tools/qtcreatorcrashhandler/crashhandlerdialog.h +++ b/src/tools/qtcreatorcrashhandler/crashhandlerdialog.h @@ -39,12 +39,14 @@ class CrashHandlerDialog : public QDialog Q_OBJECT public: - explicit CrashHandlerDialog(CrashHandler *handler, const QString &signalName, + explicit CrashHandlerDialog(CrashHandler *handler, + const QString &signalName, + const QString &appName, QWidget *parent = 0); ~CrashHandlerDialog(); public: - void setApplicationInfo(const QString &signalName); + void setApplicationInfo(const QString &signalName, const QString &appName); void appendDebugInfo(const QString &chunk); void selectLineWithContents(const QString &text); void setToFinalState(); diff --git a/src/tools/qtcreatorcrashhandler/crashhandlersetup.cpp b/src/tools/qtcreatorcrashhandler/crashhandlersetup.cpp index d62ffb337ca..e8778000701 100644 --- a/src/tools/qtcreatorcrashhandler/crashhandlersetup.cpp +++ b/src/tools/qtcreatorcrashhandler/crashhandlersetup.cpp @@ -57,8 +57,10 @@ #include #endif -static const char *crashHandlerPathC; -static void *signalHandlerStack; +static const char *appNameC = nullptr; +static const char *disableRestartOptionC = nullptr; +static const char *crashHandlerPathC = nullptr; +static void *signalHandlerStack = nullptr; extern "C" void signalHandler(int signal) { @@ -72,7 +74,12 @@ extern "C" void signalHandler(int signal) case -1: // error break; case 0: // child - execl(crashHandlerPathC, crashHandlerPathC, strsignal(signal), (char *) 0); + if (disableRestartOptionC) { + execl(crashHandlerPathC, crashHandlerPathC, strsignal(signal), appNameC, + disableRestartOptionC, (char *) 0); + } else { + execl(crashHandlerPathC, crashHandlerPathC, strsignal(signal), appNameC, (char *) 0); + } _exit(EXIT_FAILURE); default: // parent prctl(PR_SET_PTRACER, pid, 0, 0, 0); @@ -83,14 +90,23 @@ extern "C" void signalHandler(int signal) } #endif // BUILD_CRASH_HANDLER -CrashHandlerSetup::CrashHandlerSetup() +CrashHandlerSetup::CrashHandlerSetup(const QString &appName, + RestartCapability restartCap, + const QString &executableDirPath) { #ifdef BUILD_CRASH_HANDLER if (qgetenv("QTC_USE_CRASH_HANDLER").isEmpty()) return; - const QString crashHandlerPath = qApp->applicationDirPath() - + QLatin1String("/qtcreator_crash_handler"); + appNameC = qstrdup(qPrintable(appName)); + + if (restartCap == DisableRestart) + disableRestartOptionC = "--disable-restart"; + + const QString execDirPath = executableDirPath.isEmpty() + ? qApp->applicationDirPath() + : executableDirPath; + const QString crashHandlerPath = execDirPath + QLatin1String("/qtcreator_crash_handler"); crashHandlerPathC = qstrdup(qPrintable(crashHandlerPath)); // Setup an alternative stack for the signal handler. This way we are able to handle SIGSEGV @@ -137,6 +153,7 @@ CrashHandlerSetup::~CrashHandlerSetup() { #ifdef BUILD_CRASH_HANDLER delete[] crashHandlerPathC; + delete[] appNameC; free(signalHandlerStack); #endif } diff --git a/src/tools/qtcreatorcrashhandler/crashhandlersetup.h b/src/tools/qtcreatorcrashhandler/crashhandlersetup.h index 4236bb534d1..19e4d308d52 100644 --- a/src/tools/qtcreatorcrashhandler/crashhandlersetup.h +++ b/src/tools/qtcreatorcrashhandler/crashhandlersetup.h @@ -25,9 +25,15 @@ #pragma once +#include + class CrashHandlerSetup { public: - CrashHandlerSetup(); + enum RestartCapability { EnableRestart, DisableRestart }; + + CrashHandlerSetup(const QString &appName, + RestartCapability restartCap = EnableRestart, + const QString &executableDirPath = QString()); ~CrashHandlerSetup(); }; diff --git a/src/tools/qtcreatorcrashhandler/main.cpp b/src/tools/qtcreatorcrashhandler/main.cpp index ed0b901de29..700e796ab3c 100644 --- a/src/tools/qtcreatorcrashhandler/main.cpp +++ b/src/tools/qtcreatorcrashhandler/main.cpp @@ -27,6 +27,7 @@ #include "utils.h" #include +#include #include #include #include @@ -37,27 +38,52 @@ #include #include -// Called by signal handler of qtcreator. -// Usage: $0 +static void printErrorAndExit() +{ + QTextStream err(stderr); + err << QString::fromLatin1("This crash handler will be called by Qt Creator itself. " + "Do not call this manually.\n"); + exit(EXIT_FAILURE); +} + +static bool hasProcessOriginFromQtCreatorBuildDir(Q_PID pid) +{ + const QString executable = QFile::symLinkTarget(QString::fromLatin1("/proc/%1/exe") + .arg(QString::number(pid))); + return executable.contains("qtcreator"); +} + +// Called by signal handler of crashing application. int main(int argc, char *argv[]) { QApplication app(argc, argv); app.setApplicationName(QLatin1String(APPLICATION_NAME)); app.setWindowIcon(QApplication::style()->standardIcon(QStyle::SP_MessageBoxCritical)); + // Parse arguments. + QCommandLineParser parser; + parser.addPositionalArgument("signal-name", QString()); + parser.addPositionalArgument("app-name", QString()); + const QCommandLineOption disableRestartOption("disable-restart"); + parser.addOption(disableRestartOption); + parser.process(app); + // Check usage. + const QStringList positionalArguments = parser.positionalArguments(); + if (positionalArguments.size() != 2) + printErrorAndExit(); + Q_PID parentPid = getppid(); - QString parentExecutable = QFile::symLinkTarget(QString::fromLatin1("/proc/%1/exe") - .arg(QString::number(parentPid))); - if (argc > 2 || !parentExecutable.contains(QLatin1String("qtcreator"))) { - QTextStream err(stderr); - err << QString::fromLatin1("This crash handler will be called by Qt Creator itself. " - "Do not call this manually.\n"); - return EXIT_FAILURE; - } + if (!hasProcessOriginFromQtCreatorBuildDir(parentPid)) + printErrorAndExit(); // Run. - CrashHandler *crashHandler = new CrashHandler(parentPid, app.arguments().at(1)); + const QString signalName = parser.positionalArguments().at(0); + const QString appName = parser.positionalArguments().at(1); + CrashHandler::RestartCapability restartCap = CrashHandler::EnableRestart; + if (parser.isSet(disableRestartOption)) + restartCap = CrashHandler::DisableRestart; + CrashHandler *crashHandler = new CrashHandler(parentPid, signalName, appName, restartCap); crashHandler->run(); return app.exec(); diff --git a/src/tools/qtcreatorcrashhandler/qtcreatorcrashhandler.pro b/src/tools/qtcreatorcrashhandler/qtcreatorcrashhandler.pro index ca7c9208ac7..8090a3f3b47 100644 --- a/src/tools/qtcreatorcrashhandler/qtcreatorcrashhandler.pro +++ b/src/tools/qtcreatorcrashhandler/qtcreatorcrashhandler.pro @@ -2,7 +2,6 @@ QTC_LIB_DEPENDS += utils include(../../qtcreatortool.pri) -DESTDIR = $$IDE_BIN_PATH TARGET = qtcreator_crash_handler SOURCES += \ diff --git a/src/tools/qtcreatorcrashhandler/qtcreatorcrashhandler.qbs b/src/tools/qtcreatorcrashhandler/qtcreatorcrashhandler.qbs index 6b75aa13458..4fdc5788705 100644 --- a/src/tools/qtcreatorcrashhandler/qtcreatorcrashhandler.qbs +++ b/src/tools/qtcreatorcrashhandler/qtcreatorcrashhandler.qbs @@ -3,7 +3,6 @@ import qbs 1.0 QtcTool { name: "qtcreator_crash_handler" condition: qbs.targetOS.contains("linux") && qbs.buildVariant == "debug" - installDir: qtc.ide_bin_path Depends { name: "Utils" } Depends { name: "Qt.widgets" }