From cc7be5724ad9e25c03710a17ce50e09088497709 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Mon, 21 Sep 2020 12:58:35 +0300 Subject: [PATCH] Add Crashpad to Qt Creator and Qt Design Studio Fixes: QDS-2748 Change-Id: I87e25682f066d167eebfd7b78c46c166e5062e11 Reviewed-by: Mahmoud Badri --- .../qmlpuppet/qml2puppet/qml2puppetmain.cpp | 49 ++++++++++ src/app/CMakeLists.txt | 51 ++++++++++ src/app/main.cpp | 62 +++++++++++- src/plugins/coreplugin/CMakeLists.txt | 11 +++ src/plugins/coreplugin/coreplugin.cpp | 61 ++++++++++++ src/plugins/coreplugin/coreplugin.h | 2 + src/plugins/coreplugin/icore.cpp | 8 ++ src/plugins/coreplugin/icore.h | 1 + src/plugins/coreplugin/systemsettings.cpp | 97 +++++++++++++++++-- src/plugins/coreplugin/systemsettings.ui | 52 ++++++++++ .../qml/splashscreen/Welcome_splash.qml | 50 +++++++++- .../studiowelcome/qml/splashscreen/main.qml | 8 +- .../studiowelcome/studiowelcomeplugin.cpp | 34 ++++++- .../studiowelcome/studiowelcomeplugin.h | 4 + src/tools/qml2puppet/CMakeLists.txt | 22 +++++ 15 files changed, 493 insertions(+), 19 deletions(-) diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/qml2puppetmain.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/qml2puppetmain.cpp index cd2877a4f52..e690adf75ae 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/qml2puppetmain.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/qml2puppetmain.cpp @@ -44,6 +44,13 @@ #include #endif +#if defined(ENABLE_CRASHPAD) && defined(Q_OS_WIN) +#define NOMINMAX +#include "client/crashpad_client.h" +#include "client/crash_report_database.h" +#include "client/settings.h" +#endif + #ifdef Q_OS_WIN #include #endif @@ -98,6 +105,44 @@ void myMessageOutput(QtMsgType type, const QMessageLogContext &context, const QS } #endif +#if defined(ENABLE_CRASHPAD) && defined(Q_OS_WIN) +bool startCrashpad() +{ + using namespace crashpad; + + // Cache directory that will store crashpad information and minidumps + base::FilePath database(L"crashpad_reports"); + base::FilePath handler(L"crashpad_handler.exe"); + + // URL used to submit minidumps to + std::string url(CRASHPAD_BACKEND_URL); + + // Optional annotations passed via --annotations to the handler + std::map annotations; + annotations["qt-version"] = QT_VERSION_STR; + + // Optional arguments to pass to the handler + std::vector arguments; + + CrashpadClient *client = new CrashpadClient(); + bool success = client->StartHandler( + handler, + database, + database, + url, + annotations, + arguments, + /* restartable */ true, + /* asynchronous_start */ true + ); + // TODO: research using this method, should avoid creating a separate CrashpadClient for the + // puppet (needed only on windows according to docs). +// client->SetHandlerIPCPipe(L"\\\\.\\pipe\\qml2puppet"); + + return success; +} +#endif + int internalMain(QGuiApplication *application) { QCoreApplication::setOrganizationName("QtProject"); @@ -180,6 +225,10 @@ int internalMain(QGuiApplication *application) QtSystemExceptionHandler systemExceptionHandler(libexecPath); #endif +#if defined(ENABLE_CRASHPAD) && defined(Q_OS_WIN) + startCrashpad(); +#endif + new QmlDesigner::Qt5NodeInstanceClientProxy(application); #if defined(Q_OS_WIN) && defined(QT_NO_DEBUG) diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index 0e52bde5dd3..41c56f03caa 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -79,3 +79,54 @@ if (APPLE) DESTINATION ${IDE_DATA_PATH} ) endif() + +# Crashpad +set(CRASHPAD_BACKEND_URL "" CACHE STRING "Crashpad backend URL") +set(CRASHPAD_SRC "" CACHE STRING "Path to Crashpad source code") +set(CRASHPAD_BUILD "" CACHE STRING "Path to Crashpad build folder") + +if (CRASHPAD_BACKEND_URL + AND CRASHPAD_SRC + AND EXISTS "${CRASHPAD_SRC}" + AND CRASHPAD_BUILD + AND EXISTS "${CRASHPAD_BUILD}" + AND (WIN32 OR APPLE)) # LINUX isn't supported for now + target_compile_definitions(qtcreator PRIVATE + CRASHPAD_BACKEND_URL="${CRASHPAD_BACKEND_URL}" + ENABLE_CRASHPAD + WIN32_LEAN_AND_MEAN) # It comes usually with precompiled header, but at the installer they are disabled + target_include_directories(qtcreator PRIVATE + "${CRASHPAD_SRC}" + "${CRASHPAD_SRC}/third_party/mini_chromium/mini_chromium") + + if (WIN32) + target_link_libraries(qtcreator PUBLIC ${CRASHPAD_BUILD}/obj/third_party/mini_chromium/mini_chromium/base/base.lib + ${CRASHPAD_BUILD}/obj/util/util.lib + ${CRASHPAD_BUILD}/obj/client/client.lib + advapi32) + install(FILES ${CRASHPAD_BUILD}/crashpad_handler.exe DESTINATION "${IDE_LIBEXEC_PATH}") + + elseif(APPLE) + find_library(FWbsm bsm) + target_link_libraries(qtcreator PUBLIC ${CRASHPAD_BUILD}/obj/third_party/mini_chromium/mini_chromium/base/libbase.a + ${CRASHPAD_BUILD}/obj/util/libutil.a + ${CRASHPAD_BUILD}/obj/client/libclient.a + ${CRASHPAD_BUILD}/obj/out/Default/gen/util/mach/mig_output.child_portServer.o + ${CRASHPAD_BUILD}/obj/out/Default/gen/util/mach/mig_output.child_portUser.o + ${CRASHPAD_BUILD}/obj/out/Default/gen/util/mach/mig_output.excServer.o + ${CRASHPAD_BUILD}/obj/out/Default/gen/util/mach/mig_output.excUser.o + ${CRASHPAD_BUILD}/obj/out/Default/gen/util/mach/mig_output.mach_excServer.o + ${CRASHPAD_BUILD}/obj/out/Default/gen/util/mach/mig_output.mach_excUser.o + ${CRASHPAD_BUILD}/obj/out/Default/gen/util/mach/mig_output.notifyServer.o + ${CRASHPAD_BUILD}/obj/out/Default/gen/util/mach/mig_output.notifyUser.o + ${FWbsm} ${FWAppKit} ${FWIOKit} ${FWSecurity}) + install(FILES ${CRASHPAD_BUILD}/crashpad_handler DESTINATION "${IDE_LIBEXEC_PATH}") + + elseif(UNIX) + # TODO: Crashpad is not well supported on linux currently + target_link_libraries(qtcreator PUBLIC ${CRASHPAD_BUILD}/obj/third_party/mini_chromium/mini_chromium/base/libbase.a + ${CRASHPAD_BUILD}/obj/util/libutil.a + ${CRASHPAD_BUILD}/obj/client/libclient.a) + install(FILES ${CRASHPAD_BUILD}/crashpad_handler DESTINATION "${IDE_LIBEXEC_PATH}") + endif() +endif() diff --git a/src/app/main.cpp b/src/app/main.cpp index 52c176c0572..3448ace8240 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -69,6 +69,13 @@ #include #endif +#ifdef ENABLE_CRASHPAD +#define NOMINMAX +#include "client/crashpad_client.h" +#include "client/crash_report_database.h" +#include "client/settings.h" +#endif + #ifdef Q_OS_LINUX #include #endif @@ -455,6 +462,54 @@ QStringList lastSessionArgument() return hasProjectExplorer ? QStringList({"-lastsession"}) : QStringList(); } +#ifdef ENABLE_CRASHPAD +bool startCrashpad(const QString &libexecPath, bool crashReportingEnabled) +{ + using namespace crashpad; + + // Cache directory that will store crashpad information and minidumps + QString databasePath = QDir::cleanPath(libexecPath + "/crashpad_reports"); + QString handlerPath = QDir::cleanPath(libexecPath + "/crashpad_handler"); +#ifdef Q_OS_WIN + handlerPath += ".exe"; + base::FilePath database(databasePath.toStdWString()); + base::FilePath handler(handlerPath.toStdWString()); +#elif defined(Q_OS_MACOS) || defined(Q_OS_LINUX) + base::FilePath database(databasePath.toStdString()); + base::FilePath handler(handlerPath.toStdString()); +#endif + + std::unique_ptr db = CrashReportDatabase::Initialize(database); + if (db && db->GetSettings()) + db->GetSettings()->SetUploadsEnabled(crashReportingEnabled); + + // URL used to submit minidumps to + std::string url(CRASHPAD_BACKEND_URL); + + // Optional annotations passed via --annotations to the handler + std::map annotations; + annotations["app-version"] = Core::Constants::IDE_VERSION_DISPLAY; + annotations["qt-version"] = QT_VERSION_STR; + + // Optional arguments to pass to the handler + std::vector arguments; + + CrashpadClient *client = new CrashpadClient(); + bool success = client->StartHandler( + handler, + database, + database, + url, + annotations, + arguments, + /* restartable */ true, + /* asynchronous_start */ true + ); + + return success; +} +#endif + int main(int argc, char **argv) { Restarter restarter(argc, argv); @@ -493,7 +548,7 @@ int main(int argc, char **argv) Utils::TemporaryDirectory::setMasterTemporaryDirectory(QDir::tempPath() + "/" + Core::Constants::IDE_CASED_ID + "-XXXXXX"); -#ifdef Q_OS_MAC +#ifdef Q_OS_MACOS // increase the number of file that can be opened in Qt Creator. struct rlimit rl; getrlimit(RLIMIT_NOFILE, &rl); @@ -562,6 +617,11 @@ int main(int argc, char **argv) CrashHandlerSetup::EnableRestart, libexecPath); #endif +#ifdef ENABLE_CRASHPAD + bool crashReportingEnabled = settings->value("CrashReportingEnabled", false).toBool(); + startCrashpad(libexecPath, crashReportingEnabled); +#endif + #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) app.setAttribute(Qt::AA_UseHighDpiPixmaps); app.setAttribute(Qt::AA_DisableWindowContextHelpButton); diff --git a/src/plugins/coreplugin/CMakeLists.txt b/src/plugins/coreplugin/CMakeLists.txt index d5e02881f4a..12e38d45a65 100644 --- a/src/plugins/coreplugin/CMakeLists.txt +++ b/src/plugins/coreplugin/CMakeLists.txt @@ -185,6 +185,17 @@ extend_qtc_plugin(Core SOURCES progressmanager/progressmanager_x11.cpp ) +if (CRASHPAD_BACKEND_URL + AND CRASHPAD_SRC + AND EXISTS "${CRASHPAD_SRC}" + AND CRASHPAD_BUILD + AND EXISTS "${CRASHPAD_BUILD}" + AND (WIN32 OR APPLE)) # LINUX isn't supported for now + extend_qtc_plugin(Core + DEFINES ENABLE_CRASHPAD + ) +endif() + if ((NOT WIN32) AND (NOT APPLE)) # install logo foreach(size 16 24 32 48 64 128 256 512) diff --git a/src/plugins/coreplugin/coreplugin.cpp b/src/plugins/coreplugin/coreplugin.cpp index 1a40a1d21d3..59b008855e4 100644 --- a/src/plugins/coreplugin/coreplugin.cpp +++ b/src/plugins/coreplugin/coreplugin.cpp @@ -49,6 +49,7 @@ #include #include #include +#include #include #include #include @@ -62,6 +63,7 @@ #include #include #include +#include #include #include #include @@ -75,6 +77,7 @@ using namespace Utils; static CorePlugin *m_instance = nullptr; +const char kWarnCrashReportingSetting[] = "WarnCrashReporting"; const char kEnvironmentChanges[] = "Core/EnvironmentChanges"; void CorePlugin::setupSystemEnvironment() @@ -234,6 +237,11 @@ bool CorePlugin::initialize(const QStringList &arguments, QString *errorMessage) Utils::PathChooser::setAboutToShowContextMenuHandler(&CorePlugin::addToPathChooserContextMenu); +#ifdef ENABLE_CRASHPAD + connect(ICore::instance(), &ICore::coreOpened, this, &CorePlugin::warnAboutCrashReporing, + Qt::QueuedConnection); +#endif + return true; } @@ -379,6 +387,59 @@ void CorePlugin::checkSettings() showMsgBox(errorMsg, QMessageBox::Critical); } +void CorePlugin::warnAboutCrashReporing() +{ + if (!ICore::infoBar()->canInfoBeAdded(kWarnCrashReportingSetting)) + return; + + QString warnStr = ICore::settings()->value("CrashReportingEnabled", false).toBool() + ? tr("%1 collects crash reports for the sole purpose of fixing bugs. " + "To disable this feature go to %2.") + : tr("%1 can collect crash reports for the sole purpose of fixing bugs. " + "to enable this feature go to %2."); + + if (Utils::HostOsInfo::isMacHost()) { + warnStr = warnStr.arg(Core::Constants::IDE_DISPLAY_NAME) + .arg(Core::Constants::IDE_DISPLAY_NAME + tr(" > Preferences > Environment > System")); + } else { + warnStr = warnStr.arg(Core::Constants::IDE_DISPLAY_NAME) + .arg(tr("Tools > Options > Environment > System")); + } + + Utils::InfoBarEntry info(kWarnCrashReportingSetting, warnStr, + Utils::InfoBarEntry::GlobalSuppression::Enabled); + info.setCustomButtonInfo(tr("Configure..."), [] { + ICore::infoBar()->removeInfo(kWarnCrashReportingSetting); + ICore::infoBar()->globallySuppressInfo(kWarnCrashReportingSetting); + ICore::showOptionsDialog(Core::Constants::SETTINGS_ID_SYSTEM); + }); + + info.setDetailsWidgetCreator([]() -> QWidget * { + auto label = new QLabel; + label->setWordWrap(true); + label->setOpenExternalLinks(true); + label->setText(msgCrashpadInformation()); + label->setContentsMargins(0, 0, 0, 8); + return label; + }); + ICore::infoBar()->addInfo(info); +} + +// static +QString CorePlugin::msgCrashpadInformation() +{ + return tr("%1 uses Google Crashpad for collecting crashes and sending them to our backend " + "for processing. Crashpad may capture arbitrary contents from crashed process’ " + "memory, including user sensitive information, URLs, and whatever other content " + "users have trusted %1 with. The collected crash reports are however only used " + "for the sole purpose of fixing bugs.").arg(Core::Constants::IDE_DISPLAY_NAME) + + "

" + tr("More information:") + + "
" + tr("Crashpad Overview") + "" + "
" + tr("%1 security policy").arg("Sentry.io") + + ""; +} + ExtensionSystem::IPlugin::ShutdownFlag CorePlugin::aboutToShutdown() { Find::aboutToShutdown(); diff --git a/src/plugins/coreplugin/coreplugin.h b/src/plugins/coreplugin/coreplugin.h index 8d65f81ba7d..29fe6491a88 100644 --- a/src/plugins/coreplugin/coreplugin.h +++ b/src/plugins/coreplugin/coreplugin.h @@ -68,6 +68,7 @@ public: static Utils::Environment startupSystemEnvironment(); static Utils::EnvironmentItems environmentChanges(); static void setEnvironmentChanges(const Utils::EnvironmentItems &changes); + static QString msgCrashpadInformation(); public slots: void fileOpenRequest(const QString&); @@ -89,6 +90,7 @@ private: static void addToPathChooserContextMenu(Utils::PathChooser *pathChooser, QMenu *menu); static void setupSystemEnvironment(); void checkSettings(); + void warnAboutCrashReporing(); MainWindow *m_mainWindow = nullptr; EditMode *m_editMode = nullptr; diff --git a/src/plugins/coreplugin/icore.cpp b/src/plugins/coreplugin/icore.cpp index af41bfd6a62..dca6bde7beb 100644 --- a/src/plugins/coreplugin/icore.cpp +++ b/src/plugins/coreplugin/icore.cpp @@ -482,6 +482,14 @@ QString ICore::libexecPath() return QDir::cleanPath(QApplication::applicationDirPath() + '/' + RELATIVE_LIBEXEC_PATH); } +QString ICore::crashReportsPath() +{ + if (Utils::HostOsInfo::isMacHost()) + return libexecPath() + "/crashpad_reports/completed"; + else + return libexecPath() + "/crashpad_reports/reports"; +} + static QString clangIncludePath(const QString &clangVersion) { return "/lib/clang/" + clangVersion + "/include"; diff --git a/src/plugins/coreplugin/icore.h b/src/plugins/coreplugin/icore.h index 1bed598d369..7d8adbfa682 100644 --- a/src/plugins/coreplugin/icore.h +++ b/src/plugins/coreplugin/icore.h @@ -99,6 +99,7 @@ public: static QString cacheResourcePath(); static QString installerResourcePath(); static QString libexecPath(); + static QString crashReportsPath(); static QString versionString(); diff --git a/src/plugins/coreplugin/systemsettings.cpp b/src/plugins/coreplugin/systemsettings.cpp index a3586393e36..4318cece91c 100644 --- a/src/plugins/coreplugin/systemsettings.cpp +++ b/src/plugins/coreplugin/systemsettings.cpp @@ -27,6 +27,7 @@ #include "coreconstants.h" #include "coreplugin.h" #include "editormanager/editormanager_p.h" +#include "dialogs/restartdialog.h" #include "fileutils.h" #include "icore.h" #include "iversioncontrol.h" @@ -35,6 +36,7 @@ #include "vcsmanager.h" #include +#include #include #include #include @@ -50,9 +52,28 @@ using namespace Utils; +static const char crashReportingEnabledKey[] = "CrashReportingEnabled"; +static const char showCrashButtonKey[] = "ShowCrashButton"; + namespace Core { namespace Internal { +// TODO: move to somewhere in Utils +static QString formatSize(qint64 size) +{ + QStringList units {QObject::tr("Bytes"), QObject::tr("KB"), QObject::tr("MB"), + QObject::tr("GB"), QObject::tr("TB")}; + double outputSize = size; + int i; + for (i = 0; i < units.size() - 1; ++i) { + if (outputSize < 1024) + break; + outputSize /= 1024; + } + return i == 0 ? QString("%0 %1").arg(outputSize).arg(units[i]) // Bytes + : QString("%0 %1").arg(outputSize, 0, 'f', 2).arg(units[i]); // KB, MB, GB, TB +} + class SystemSettingsWidget : public IOptionsPageWidget { Q_DECLARE_TR_FUNCTIONS(Core::Internal::SystemSettingsWidget) @@ -113,6 +134,46 @@ public: m_ui.maxRecentFilesSpinBox->setMinimum(1); m_ui.maxRecentFilesSpinBox->setMaximum(99); m_ui.maxRecentFilesSpinBox->setValue(EditorManagerPrivate::maxRecentFiles()); +#ifdef ENABLE_CRASHPAD + if (ICore::settings()->value(showCrashButtonKey).toBool()) { + auto crashButton = new QPushButton("CRASH!!!"); + crashButton->show(); + connect(crashButton, &QPushButton::clicked, []() { + // do a real crash + volatile int* a = reinterpret_cast(NULL); *a = 1; + }); + } + + m_ui.enableCrashReportingCheckBox->setChecked( + ICore::settings()->value(crashReportingEnabledKey).toBool()); + connect(m_ui.helpCrashReportingButton, &QAbstractButton::clicked, this, [this] { + showHelpDialog(tr("Crash Reporting"), CorePlugin::msgCrashpadInformation()); + }); + connect(m_ui.enableCrashReportingCheckBox, + QOverload::of(&QCheckBox::stateChanged), this, [this] { + const QString restartText = tr("The change will take effect after restart."); + Core::RestartDialog restartDialog(Core::ICore::dialogParent(), restartText); + restartDialog.exec(); + if (restartDialog.result() == QDialog::Accepted) + apply(); + }); + + updateClearCrashWidgets(); + connect(m_ui.clearCrashReportsButton, &QPushButton::clicked, this, [&] { + QDir crashReportsDir(ICore::crashReportsPath()); + crashReportsDir.setFilter(QDir::Files); + const QStringList crashFiles = crashReportsDir.entryList(); + for (QString file : crashFiles) + crashReportsDir.remove(file); + updateClearCrashWidgets(); + }); +#else + m_ui.enableCrashReportingCheckBox->setVisible(false); + m_ui.helpCrashReportingButton->setVisible(false); + m_ui.clearCrashReportsButton->setVisible(false); + m_ui.crashReportsSizeText->setVisible(false); +#endif + m_ui.askBeforeExitCheckBox->setChecked( static_cast(ICore::mainWindow())->askConfirmationBeforeExit()); @@ -182,8 +243,9 @@ private: void updateTerminalUi(const Utils::TerminalCommand &term); void updatePath(); void updateEnvironmentChangesLabel(); + void updateClearCrashWidgets(); - void variableHelpDialogCreator(const QString &helpText); + void showHelpDialog(const QString &title, const QString &helpText); Ui::SystemSettings m_ui; QPointer m_dialog; EnvironmentItems m_environmentChanges; @@ -211,6 +273,11 @@ void SystemSettingsWidget::apply() m_ui.warnBeforeOpeningBigFiles->isChecked()); EditorManagerPrivate::setBigFileSizeLimit(m_ui.bigFilesLimitSpinBox->value()); EditorManagerPrivate::setMaxRecentFiles(m_ui.maxRecentFilesSpinBox->value()); +#ifdef ENABLE_CRASHPAD + ICore::settings()->setValue(crashReportingEnabledKey, + m_ui.enableCrashReportingCheckBox->isChecked()); +#endif + static_cast(ICore::mainWindow())->setAskConfirmationBeforeExit( m_ui.askBeforeExitCheckBox->isChecked()); @@ -263,9 +330,12 @@ void SystemSettingsWidget::updateEnvironmentChangesLabel() : shortSummary); } -void SystemSettingsWidget::variableHelpDialogCreator(const QString &helpText) +void SystemSettingsWidget::showHelpDialog(const QString &title, const QString &helpText) { if (m_dialog) { + if (m_dialog->windowTitle() != title) + m_dialog->setText(helpText); + if (m_dialog->text() != helpText) m_dialog->setText(helpText); @@ -273,20 +343,31 @@ void SystemSettingsWidget::variableHelpDialogCreator(const QString &helpText) ICore::raiseWindow(m_dialog); return; } - QMessageBox *mb = new QMessageBox(QMessageBox::Information, - tr("Variables"), - helpText, - QMessageBox::Close, - this); + auto mb = new QMessageBox(QMessageBox::Information, title, helpText, QMessageBox::Close, this); mb->setWindowModality(Qt::NonModal); m_dialog = mb; mb->show(); } +#ifdef ENABLE_CRASHPAD +void SystemSettingsWidget::updateClearCrashWidgets() +{ + QDir crashReportsDir(ICore::crashReportsPath()); + crashReportsDir.setFilter(QDir::Files); + qint64 size = 0; + const QStringList crashFiles = crashReportsDir.entryList(); + for (QString file : crashFiles) + size += QFileInfo(crashReportsDir, file).size(); + + m_ui.clearCrashReportsButton->setEnabled(!crashFiles.isEmpty()); + m_ui.crashReportsSizeText->setText(formatSize(size)); +} +#endif + void SystemSettingsWidget::showHelpForFileBrowser() { if (HostOsInfo::isAnyUnixHost() && !HostOsInfo::isMacHost()) - variableHelpDialogCreator(UnixUtils::fileBrowserHelpText()); + showHelpDialog(tr("Variables"), UnixUtils::fileBrowserHelpText()); } SystemSettings::SystemSettings() diff --git a/src/plugins/coreplugin/systemsettings.ui b/src/plugins/coreplugin/systemsettings.ui index 3cb48bd9354..68f0d4065dc 100644 --- a/src/plugins/coreplugin/systemsettings.ui +++ b/src/plugins/coreplugin/systemsettings.ui @@ -309,6 +309,24 @@ + + + + + + Clear local crash reports + + + + + + + + + + + + @@ -455,6 +473,40 @@ + + + + + + Allow crashes to be automatically reported. Collected reports are used for the sole purpose of fixing bugs. + + + Enable crash reporting + + + + + + + ? + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + diff --git a/src/plugins/studiowelcome/qml/splashscreen/Welcome_splash.qml b/src/plugins/studiowelcome/qml/splashscreen/Welcome_splash.qml index a41f6003237..57c4cb5da3e 100644 --- a/src/plugins/studiowelcome/qml/splashscreen/Welcome_splash.qml +++ b/src/plugins/studiowelcome/qml/splashscreen/Welcome_splash.qml @@ -38,10 +38,31 @@ Image { signal goNext signal closeClicked + signal configureClicked property alias doNotShowAgain: do_not_show_checkBox.checked property bool loadingPlugins: true + // called from C++ + function onPluginInitialized(crashReportingEnabled: bool, crashReportingOn: bool) + { + loadingPlugins = false + + if (crashReportingEnabled) { + var configureButton = "" + + qsTr("[Configure]") + ""; + var settingPath = Qt.platform.os === "osx" + ? qsTr("Qt Creator > Preferences > Environment > System") + : qsTr("Tools > Options > Environment > System") + var strOn = qsTr("Qt Design Studio collects crash reports for the sole purpose of fixing bugs. " + + "You can disable this feature under %1. %2").arg(settingPath).arg(configureButton) + var strOff = qsTr("Qt Design Studio can collect crash reports for the sole purpose of fixing bugs. " + + "You can enable this feature under %1. %2").arg(settingPath).arg(configureButton) + + crash_reporting_text.text = crashReportingOn ? strOn : strOff; + } + } + Image { id: logo x: 14 @@ -104,7 +125,7 @@ Image { Text { id: marketing_1 x: 16 - y: 302 + y: 252 width: 355 height: 31 color: "#ffffff" @@ -118,7 +139,7 @@ Image { Text { id: marketing_2 x: 16 - y: 323 + y: 273 width: 311 height: 31 color: "#ffffff" @@ -132,7 +153,7 @@ Image { Text { id: marketing_3 x: 16 - y: 344 + y: 294 width: 311 height: 31 color: "#ffffff" @@ -143,6 +164,26 @@ Image { font.wordSpacing: 0 } + Text { + id: crash_reporting_text + color: "#ffffff" + textFormat: Text.RichText + x: 16 + y: 330 + width: 311 + wrapMode: Text.WordWrap + font.family: StudioFonts.titilliumWeb_light + font.pixelSize: 12 + font.wordSpacing: 0 + onLinkActivated: welcome_splash.configureClicked() + + MouseArea { // show hand cursor on link hover + anchors.fill: parent + acceptedButtons: Qt.NoButton // don't eat clicks on the Text + cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor + } + } + Dof_Effect { id: dof_Effect x: 358 @@ -192,10 +233,9 @@ Image { scale: 0.5 } - RowLayout { x: 16 - y: 254 + y: 330 visible: welcome_splash.loadingPlugins diff --git a/src/plugins/studiowelcome/qml/splashscreen/main.qml b/src/plugins/studiowelcome/qml/splashscreen/main.qml index be887047840..674bb47c9fa 100644 --- a/src/plugins/studiowelcome/qml/splashscreen/main.qml +++ b/src/plugins/studiowelcome/qml/splashscreen/main.qml @@ -32,9 +32,14 @@ Item { signal closeClicked signal checkBoxToggled + signal configureClicked property alias doNotShowAgain: welcome_splash.doNotShowAgain - property alias loadingPlugins: welcome_splash.loadingPlugins + + function onPluginInitialized(crashReportingEnabled: bool, crashReportingOn: bool) + { + welcome_splash.onPluginInitialized(crashReportingEnabled, crashReportingOn); + } Welcome_splash { id: welcome_splash @@ -42,5 +47,6 @@ Item { y: 0 antialiasing: true onCloseClicked: root.closeClicked() + onConfigureClicked: root.configureClicked() } } diff --git a/src/plugins/studiowelcome/studiowelcomeplugin.cpp b/src/plugins/studiowelcome/studiowelcomeplugin.cpp index 3fc5a850290..e80075acaeb 100644 --- a/src/plugins/studiowelcome/studiowelcomeplugin.cpp +++ b/src/plugins/studiowelcome/studiowelcomeplugin.cpp @@ -36,12 +36,12 @@ #include #include -#include #include #include #include #include +#include #include #include @@ -264,6 +264,18 @@ void StudioWelcomePlugin::closeSplashScreen() } } +void StudioWelcomePlugin::showSystemSettings() +{ + Core::ICore::infoBar()->removeInfo("WarnCrashReporting"); + Core::ICore::infoBar()->globallySuppressInfo("WarnCrashReporting"); + + // pause remove splash timer while settings dialog is open otherwise splash crashes upon removal + int splashAutoCloseRemainingTime = m_removeSplashTimer.remainingTime(); // milliseconds + m_removeSplashTimer.stop(); + Core::ICore::showOptionsDialog(Core::Constants::SETTINGS_ID_SYSTEM); + m_removeSplashTimer.start(splashAutoCloseRemainingTime); +} + StudioWelcomePlugin::~StudioWelcomePlugin() { delete m_welcomeMode; @@ -284,6 +296,9 @@ bool StudioWelcomePlugin::initialize(const QStringList &arguments, QString *erro QFont systemFont("Titillium Web", QApplication::font().pointSize()); QApplication::setFont(systemFont); + m_removeSplashTimer.setSingleShot(true); + m_removeSplashTimer.setInterval(15000); + connect(&m_removeSplashTimer, &QTimer::timeout, this, [this] { closeSplashScreen(); }); return true; } @@ -292,7 +307,7 @@ void StudioWelcomePlugin::extensionsInitialized() Core::ModeManager::activateMode(m_welcomeMode->id()); if (Utils::CheckableMessageBox::shouldAskAgain(Core::ICore::settings(), DO_NOT_SHOW_SPLASHSCREEN_AGAIN_KEY)) { - connect(Core::ICore::instance(), &Core::ICore::coreOpened, this, [this] (){ + connect(Core::ICore::instance(), &Core::ICore::coreOpened, this, [this] { s_view = new QQuickWidget(Core::ICore::dialogParent()); s_view->setResizeMode(QQuickWidget::SizeRootObjectToView); s_view->setWindowFlag(Qt::SplashScreen, true); @@ -313,11 +328,12 @@ void StudioWelcomePlugin::extensionsInitialized() return); connect(s_view->rootObject(), SIGNAL(closeClicked()), this, SLOT(closeSplashScreen())); + connect(s_view->rootObject(), SIGNAL(configureClicked()), this, SLOT(showSystemSettings())); s_view->show(); s_view->raise(); - QTimer::singleShot(15000, [this](){ closeSplashScreen(); }); + m_removeSplashTimer.start(); }); } } @@ -329,7 +345,17 @@ bool StudioWelcomePlugin::delayedInitialize() QTC_ASSERT(s_view->rootObject() , return true); - s_view->rootObject()->setProperty("loadingPlugins", false); +#ifdef ENABLE_CRASHPAD + const bool crashReportingEnabled = true; + const bool crashReportingOn = Core::ICore::settings()->value("CrashReportingEnabled", false).toBool(); +#else + const bool crashReportingEnabled = false; + const bool crashReportingOn = false; +#endif + + QMetaObject::invokeMethod(s_view->rootObject(), "onPluginInitialized", + Q_ARG(bool, crashReportingEnabled), Q_ARG(bool, crashReportingOn)); + return false; } diff --git a/src/plugins/studiowelcome/studiowelcomeplugin.h b/src/plugins/studiowelcome/studiowelcomeplugin.h index 6c8aa34b9c4..ddc86807b9b 100644 --- a/src/plugins/studiowelcome/studiowelcomeplugin.h +++ b/src/plugins/studiowelcome/studiowelcomeplugin.h @@ -27,6 +27,8 @@ #include +#include + namespace StudioWelcome { namespace Internal { @@ -37,6 +39,7 @@ class StudioWelcomePlugin final : public ExtensionSystem::IPlugin public slots: void closeSplashScreen(); + void showSystemSettings(); public: ~StudioWelcomePlugin() final; @@ -47,6 +50,7 @@ public: private: class WelcomeMode *m_welcomeMode = nullptr; + QTimer m_removeSplashTimer; }; } // namespace Internal diff --git a/src/tools/qml2puppet/CMakeLists.txt b/src/tools/qml2puppet/CMakeLists.txt index 1f59c5322a0..fab8774380a 100644 --- a/src/tools/qml2puppet/CMakeLists.txt +++ b/src/tools/qml2puppet/CMakeLists.txt @@ -207,3 +207,25 @@ extend_qtc_executable(qml2puppet DEPENDS QtCreator::multilanguage-support FEATURE_INFO "multilanguage-support in qml2puppet" ) + +# Crashpad +# only windows requires separate crashpad client per process until client->SetHandlerIPCPipe() +# is implemented (check the TODO inside startCrashpad()) +if (CRASHPAD_BACKEND_URL + AND CRASHPAD_SRC + AND EXISTS "${CRASHPAD_SRC}" + AND CRASHPAD_BUILD + AND EXISTS "${CRASHPAD_BUILD}" + AND WIN32) + target_compile_definitions(qml2puppet PRIVATE + CRASHPAD_BACKEND_URL="${CRASHPAD_BACKEND_URL}" + ENABLE_CRASHPAD) + target_include_directories(qml2puppet PRIVATE + "${CRASHPAD_SRC}" + "${CRASHPAD_SRC}/third_party/mini_chromium/mini_chromium") + + target_link_libraries(qml2puppet PUBLIC ${CRASHPAD_BUILD}/obj/third_party/mini_chromium/mini_chromium/base/base.lib + ${CRASHPAD_BUILD}/obj/util/util.lib + ${CRASHPAD_BUILD}/obj/client/client.lib + advapi32) +endif()