diff --git a/plugins/heaptrack/job.cpp b/plugins/heaptrack/job.cpp index 6b4faedbba..61006a94be 100644 --- a/plugins/heaptrack/job.cpp +++ b/plugins/heaptrack/job.cpp @@ -1,150 +1,146 @@ /* This file is part of KDevelop Copyright 2017 Anton Anikin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "job.h" #include "debug.h" #include "globalsettings.h" #include "utils.h" #include #include -#include #include #include #include #include #include #include namespace Heaptrack { -Job::Job(KDevelop::ILaunchConfiguration* launchConfig) +Job::Job(KDevelop::ILaunchConfiguration* launchConfig, IExecutePlugin* executePlugin) : m_pid(-1) { Q_ASSERT(launchConfig); + Q_ASSERT(executePlugin); - auto pluginController = KDevelop::ICore::self()->pluginController(); - auto iface = pluginController->pluginForExtension(QStringLiteral("org.kdevelop.IExecutePlugin"))->extension(); - Q_ASSERT(iface); - - QString envProfile = iface->environmentProfileName(launchConfig); + QString envProfile = executePlugin->environmentProfileName(launchConfig); if (envProfile.isEmpty()) { envProfile = KDevelop::EnvironmentProfileList(KSharedConfig::openConfig()).defaultProfileName(); } setEnvironmentProfile(envProfile); QString errorString; - m_analyzedExecutable = iface->executable(launchConfig, errorString).toLocalFile(); + m_analyzedExecutable = executePlugin->executable(launchConfig, errorString).toLocalFile(); if (!errorString.isEmpty()) { setError(-1); setErrorText(errorString); } - QStringList analyzedExecutableArguments = iface->arguments(launchConfig, errorString); + QStringList analyzedExecutableArguments = executePlugin->arguments(launchConfig, errorString); if (!errorString.isEmpty()) { setError(-1); setErrorText(errorString); } - QUrl workDir = iface->workingDirectory(launchConfig); + QUrl workDir = executePlugin->workingDirectory(launchConfig); if (workDir.isEmpty() || !workDir.isValid()) { workDir = QUrl::fromLocalFile(QFileInfo(m_analyzedExecutable).absolutePath()); } setWorkingDirectory(workDir); *this << KDevelop::Path(GlobalSettings::heaptrackExecutable()).toLocalFile(); *this << m_analyzedExecutable; *this << analyzedExecutableArguments; setup(); } Job::Job(long int pid) : m_pid(pid) { *this << KDevelop::Path(GlobalSettings::heaptrackExecutable()).toLocalFile(); *this << QStringLiteral("-p"); *this << QString::number(m_pid); setup(); } void Job::setup() { setProperties(DisplayStdout); setProperties(DisplayStderr); setProperties(PostProcessOutput); setCapabilities(Killable); setStandardToolView(KDevelop::IOutputView::TestView); setBehaviours(KDevelop::IOutputView::AutoScroll); KDevelop::ICore::self()->uiController()->registerStatus(this); connect(this, &Job::finished, this, [this]() { emit hideProgress(this); }); } Job::~Job() { } QString Job::statusName() const { QString target = m_pid < 0 ? QFileInfo(m_analyzedExecutable).fileName() : QStringLiteral("PID: %1").arg(m_pid); return i18n("Heaptrack Analysis (%1)", target); } QString Job::resultsFile() const { return m_resultsFile; } void Job::start() { emit showProgress(this, 0, 0, 0); OutputExecuteJob::start(); } void Job::postProcessStdout(const QStringList& lines) { static const auto resultRegex = QRegularExpression(QStringLiteral("heaptrack output will be written to \\\"(.+)\\\"")); if (m_resultsFile.isEmpty()) { QRegularExpressionMatch match; for (const QString & line : lines) { match = resultRegex.match(line); if (match.hasMatch()) { m_resultsFile = match.captured(1); break; } } } OutputExecuteJob::postProcessStdout(lines); } } diff --git a/plugins/heaptrack/job.h b/plugins/heaptrack/job.h index edefd4588b..16ca362c8f 100644 --- a/plugins/heaptrack/job.h +++ b/plugins/heaptrack/job.h @@ -1,64 +1,62 @@ /* This file is part of KDevelop Copyright 2017 Anton Anikin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #pragma once #include #include -namespace KDevelop -{ - class ILaunchConfiguration; -} +class IExecutePlugin; +namespace KDevelop { class ILaunchConfiguration; } namespace Heaptrack { class Job : public KDevelop::OutputExecuteJob, public KDevelop::IStatus { Q_OBJECT Q_INTERFACES(KDevelop::IStatus) public: - explicit Job(KDevelop::ILaunchConfiguration* launchConfig); + Job(KDevelop::ILaunchConfiguration* launchConfig, IExecutePlugin* executePlugin); explicit Job(long int pid); ~Job() override; void start() override; QString statusName() const override; QString resultsFile() const; Q_SIGNALS: void clearMessage(KDevelop::IStatus*) override; void hideProgress(KDevelop::IStatus*) override; void showErrorMessage(const QString& message, int timeout = 0) override; void showMessage(KDevelop::IStatus*, const QString& message, int timeout = 0) override; void showProgress(KDevelop::IStatus*, int minimum, int maximum, int value) override; protected: void setup(); void postProcessStdout(const QStringList& lines) override; long int m_pid; QString m_analyzedExecutable; QString m_resultsFile; }; } diff --git a/plugins/heaptrack/kdevheaptrack.json b/plugins/heaptrack/kdevheaptrack.json index 0304f76b95..f0e6f23a1f 100644 --- a/plugins/heaptrack/kdevheaptrack.json +++ b/plugins/heaptrack/kdevheaptrack.json @@ -1,84 +1,84 @@ { "KPlugin": { "Authors": [ { "Name": "Anton Anikin", "Name[ca@valencia]": "Anton Anikin", "Name[ca]": "Anton Anikin", "Name[cs]": "Anton Anikin", "Name[de]": "Anton Anikin", "Name[en_GB]": "Anton Anikin", "Name[es]": "Anton Anikin", "Name[fr]": "Anton Anikin", "Name[gl]": "Anton Anikin", "Name[it]": "Anton Anikin", "Name[nl]": "Anton Anikin", "Name[nn]": "Anton Anikin", "Name[pl]": "Anton Anikin", "Name[pt]": "Anton Anikin", "Name[pt_BR]": "Anton Anikin", "Name[sk]": "Anton Anikin", "Name[sl]": "Anton Anikin", "Name[sv]": "Anton Anikin", "Name[tr]": "Anton Anikin", "Name[uk]": "Anton Anikin", "Name[x-test]": "xxAnton Anikinxx", "Name[zh_CN]": "Anton Anikin" } ], "Category": "Analyzers", "Description": "This plugin integrates Heaptrack to KDevelop", "Description[ca@valencia]": "Aquest connector integra el Heaptrack al KDevelop", "Description[ca]": "Aquest connector integra el Heaptrack al KDevelop", "Description[cs]": "Tento modul integruje Heaptrack do KDevelop", "Description[de]": "Dieses Modul integriert Heaptrack in KDevelop", "Description[en_GB]": "This plugin integrates Heaptrack to KDevelop", "Description[es]": "Este complemento integra Heaptrack en KDevelop", "Description[fr]": "Ce module externe intègre Heaptrack à KDevelop", "Description[gl]": "Este complemento integra Heaptrack con KDevelop.", "Description[it]": "Questa estensione integra Heaptrack in KDevelop", "Description[nl]": "Deze plugin integreert Heaptrack in KDevelop", "Description[pl]": "Wtyczka ta integruje Heaptrack z KDevelop", "Description[pt]": "Este 'plugin' integra o Heaptrack no KDevelop", "Description[pt_BR]": "Este plugin integra o Heaptrack ao KDevelop", "Description[sk]": "Tento plugin integruje Heaptrack pre KDevelop", "Description[sl]": "Ta vstavek v KDevelop vgradi Heaptrack", "Description[sv]": "Insticksprogrammet integrerar Heaptrack i KDevelop", "Description[tr]": "Bu eklenti Heaptrack uygulamasını KDevelop ile bütünleştirir", "Description[uk]": "За допомогою цього додатка можна інтегрувати Heaptrack до KDevelop", "Description[x-test]": "xxThis plugin integrates Heaptrack to KDevelopxx", "Description[zh_CN]": "此插件将 Heaptrack 整合到 KDevelop", "Icon": "office-chart-area", "Id": "kdevheaptrack", "License": "GPL", "Name": "Heaptrack Support", "Name[ca@valencia]": "Implementació del Heaptrack", "Name[ca]": "Implementació del Heaptrack", "Name[cs]": "Podpora Heaptracku", "Name[de]": "Heaptrack-Unterstützung", "Name[en_GB]": "Heaptrack Support", "Name[es]": "Implementación de Heaptrack", "Name[fr]": "Prise en charge Heaptrack", "Name[gl]": "Compatibilidade con Heaptrack.", "Name[it]": "Supporto per Heaptrack", "Name[nl]": "Ondersteuning van Heaptrack", "Name[pl]": "Obsługa Heaptrack", "Name[pt]": "Suporte para o Heaptrack", "Name[pt_BR]": "Suporte ao Heaptrack", "Name[sk]": "Podpora Heaptrack", "Name[sl]": "Podpora za Heaptrack", "Name[sv]": "Heaptrack-stöd", "Name[tr]": "Heaptrack Desteği", "Name[uk]": "Підтримка Heaptrack", "Name[x-test]": "xxHeaptrack Supportxx", "Name[zh_CN]": "Heaptrack 支持", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Category": "Global", "X-KDevelop-IRequired": [ - "org.kdevelop.IExecutePlugin" + "org.kdevelop.IExecutePlugin@kdevexecute" ], "X-KDevelop-Mode": "GUI" } diff --git a/plugins/heaptrack/plugin.cpp b/plugins/heaptrack/plugin.cpp index a21cdf1e01..1b08340fd5 100644 --- a/plugins/heaptrack/plugin.cpp +++ b/plugins/heaptrack/plugin.cpp @@ -1,163 +1,184 @@ /* This file is part of KDevelop Copyright 2017 Anton Anikin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "plugin.h" #include "config/globalconfigpage.h" #include "debug.h" #include "job.h" #include "utils.h" #include "visualizer.h" #include #if KF5SysGuard_FOUND #include "dialogs/processselection.h" #include #endif #include #include +#include #include #include #include #include #include +#include #include #include +#include #include K_PLUGIN_FACTORY_WITH_JSON(HeaptrackFactory, "kdevheaptrack.json", registerPlugin();) namespace Heaptrack { Plugin::Plugin(QObject* parent, const QVariantList&) : IPlugin(QStringLiteral("kdevheaptrack"), parent) { setXMLFile(QStringLiteral("kdevheaptrack.rc")); m_launchAction = new QAction( QIcon::fromTheme(QStringLiteral("office-chart-area")), i18n("Run Heaptrack Analysis"), this); connect(m_launchAction, &QAction::triggered, this, &Plugin::launchHeaptrack); actionCollection()->addAction(QStringLiteral("heaptrack_launch"), m_launchAction); #if KF5SysGuard_FOUND m_attachAction = new QAction( QIcon::fromTheme(QStringLiteral("office-chart-area")), i18n("Attach to Process with Heaptrack"), this); connect(m_attachAction, &QAction::triggered, this, &Plugin::attachHeaptrack); actionCollection()->addAction(QStringLiteral("heaptrack_attach"), m_attachAction); #endif } Plugin::~Plugin() { } void Plugin::launchHeaptrack() { - auto runController = KDevelop::Core::self()->runControllerInternal(); - if (runController->launchConfigurations().isEmpty()) { - runController->showConfigurationDialog(); + IExecutePlugin* executePlugin = nullptr; + + // First we should check that our "kdevexecute" plugin is loaded. This is needed since + // current plugin controller logic allows us to unload this plugin with keeping dependent + // plugins like Heaptrack in "loaded" state. This seems to be wrong behaviour but now we have + // to do additional checks. + // TODO fix plugin controller to avoid such inconsistent states. + auto pluginController = core()->pluginController(); + if (auto plugin = pluginController->pluginForExtension( + QStringLiteral("org.kdevelop.IExecutePlugin"), QStringLiteral("kdevexecute"))) { + executePlugin = plugin->extension(); + } else { + auto pluginInfo = pluginController->infoForPluginId(QStringLiteral("kdevexecute")); + KMessageBox::error( + qApp->activeWindow(), + i18n("Unable to start Heaptrack analysis - \"%1\" plugin is not loaded.", pluginInfo.name())); + return; } + auto runController = KDevelop::Core::self()->runControllerInternal(); auto defaultLaunch = runController->defaultLaunch(); if (!defaultLaunch) { - return; + runController->showConfigurationDialog(); } - auto pluginController = core()->self()->pluginController(); - auto iface = pluginController->pluginForExtension(QStringLiteral("org.kdevelop.IExecutePlugin"))->extension(); - Q_ASSERT(iface); + if (!defaultLaunch->type()->launcherForId(QStringLiteral("nativeAppLauncher"))) { + KMessageBox::error( + qApp->activeWindow(), + i18n("Heaptrack analysis can be started only for native applications.")); + return; + } - auto heaptrackJob = new Job(defaultLaunch); + auto heaptrackJob = new Job(defaultLaunch, executePlugin); connect(heaptrackJob, &Job::finished, this, &Plugin::jobFinished); QList jobList; - if (KJob* depJob = iface->dependencyJob(defaultLaunch)) { + if (KJob* depJob = executePlugin->dependencyJob(defaultLaunch)) { jobList += depJob; } jobList += heaptrackJob; auto ecJob = new KDevelop::ExecuteCompositeJob(runController, jobList); ecJob->setObjectName(heaptrackJob->statusName()); runController->registerJob(ecJob); m_launchAction->setEnabled(false); } void Plugin::attachHeaptrack() { #if KF5SysGuard_FOUND QPointer dlg = new KDevMI::ProcessSelectionDialog(activeMainWindow()); if (!dlg->exec() || !dlg->pidSelected()) { delete dlg; return; } auto heaptrackJob = new Job(dlg->pidSelected()); delete dlg; connect(heaptrackJob, &Job::finished, this, &Plugin::jobFinished); heaptrackJob->setObjectName(heaptrackJob->statusName()); core()->runController()->registerJob(heaptrackJob); m_launchAction->setEnabled(false); #endif } void Plugin::jobFinished(KJob* kjob) { auto job = static_cast(kjob); Q_ASSERT(job); if (job->status() == KDevelop::OutputExecuteJob::JobStatus::JobSucceeded) { auto visualizer = new Visualizer(job->resultsFile(), this); visualizer->start(); } else { QFile::remove(job->resultsFile()); } m_launchAction->setEnabled(true); } int Plugin::configPages() const { return 1; } KDevelop::ConfigPage* Plugin::configPage(int number, QWidget* parent) { if (number) { return nullptr; } return new GlobalConfigPage(this, parent); } } #include "plugin.moc"