File indexing completed on 2024-04-28 04:38:54

0001 /*
0002     SPDX-FileCopyrightText: 2017 Anton Anikin <anton.anikin@htower.ru>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "plugin.h"
0008 
0009 #include "config/globalconfigpage.h"
0010 #include "debug.h"
0011 #include "job.h"
0012 #include "utils.h"
0013 #include "visualizer.h"
0014 
0015 #include <config-kdevelop.h>
0016 #if HAVE_KSYSGUARD
0017 #include "dialogs/processselection.h"
0018 #include <QPointer>
0019 #endif
0020 
0021 #include <execute/iexecuteplugin.h>
0022 #include <interfaces/iplugincontroller.h>
0023 #include <interfaces/iuicontroller.h>
0024 #include <interfaces/launchconfigurationtype.h>
0025 #include <shell/core.h>
0026 #include <shell/launchconfiguration.h>
0027 #include <shell/runcontroller.h>
0028 #include <sublime/message.h>
0029 #include <util/executecompositejob.h>
0030 // KF
0031 #include <KActionCollection>
0032 #include <KPluginFactory>
0033 // Qt
0034 #include <QAction>
0035 #include <QApplication>
0036 #include <QFile>
0037 
0038 K_PLUGIN_FACTORY_WITH_JSON(HeaptrackFactory, "kdevheaptrack.json", registerPlugin<Heaptrack::Plugin>();)
0039 
0040 namespace {
0041 void postErrorMessage(const QString& messageText)
0042 {
0043     auto* const message = new Sublime::Message(messageText, Sublime::Message::Error);
0044     KDevelop::ICore::self()->uiController()->postMessage(message);
0045 }
0046 }
0047 
0048 namespace Heaptrack
0049 {
0050 
0051 Plugin::Plugin(QObject* parent, const QVariantList&)
0052     : IPlugin(QStringLiteral("kdevheaptrack"), parent)
0053 {
0054     setXMLFile(QStringLiteral("kdevheaptrack.rc"));
0055 
0056     m_launchAction = new QAction(
0057         QIcon::fromTheme(QStringLiteral("office-chart-area")),
0058         i18nc("@action", "Run Heaptrack Analysis"),
0059         this);
0060 
0061     connect(m_launchAction, &QAction::triggered, this, &Plugin::launchHeaptrack);
0062     actionCollection()->addAction(QStringLiteral("heaptrack_launch"), m_launchAction);
0063 
0064 #if HAVE_KSYSGUARD
0065     m_attachAction = new QAction(
0066         QIcon::fromTheme(QStringLiteral("office-chart-area")),
0067         i18nc("@action", "Attach to Process with Heaptrack"),
0068         this);
0069 
0070     connect(m_attachAction, &QAction::triggered, this, &Plugin::attachHeaptrack);
0071     actionCollection()->addAction(QStringLiteral("heaptrack_attach"), m_attachAction);
0072 #endif
0073 }
0074 
0075 Plugin::~Plugin()
0076 {
0077 }
0078 
0079 void Plugin::launchHeaptrack()
0080 {
0081     IExecutePlugin* executePlugin = nullptr;
0082 
0083     // First we should check that our "kdevexecute" plugin is loaded. This is needed since
0084     // current plugin controller logic allows us to unload this plugin with keeping dependent
0085     // plugins like Heaptrack in "loaded" state. This seems to be wrong behaviour but now we have
0086     // to do additional checks.
0087     // TODO fix plugin controller to avoid such inconsistent states.
0088     auto pluginController = core()->pluginController();
0089     if (auto plugin = pluginController->pluginForExtension(
0090         QStringLiteral("org.kdevelop.IExecutePlugin"), QStringLiteral("kdevexecute"))) {
0091         executePlugin = plugin->extension<IExecutePlugin>();
0092     } else {
0093         auto pluginInfo = pluginController->infoForPluginId(QStringLiteral("kdevexecute"));
0094         postErrorMessage(i18n("Unable to start Heaptrack analysis - \"%1\" plugin is not loaded.", pluginInfo.name()));
0095         return;
0096     }
0097 
0098     auto runController = KDevelop::Core::self()->runControllerInternal();
0099     auto defaultLaunch = runController->defaultLaunch();
0100     if (!defaultLaunch) {
0101         runController->showConfigurationDialog();
0102         defaultLaunch = runController->defaultLaunch();
0103         if (!defaultLaunch) {
0104             postErrorMessage(i18n("Configure a native application launch to perform Heaptrack analysis on."));
0105             return;
0106         }
0107     }
0108 
0109     if (!defaultLaunch->type()->launcherForId(QStringLiteral("nativeAppLauncher"))) {
0110         postErrorMessage(i18n("Heaptrack analysis can be started only for native applications."));
0111         return;
0112     }
0113 
0114     auto heaptrackJob = new Job(defaultLaunch, executePlugin);
0115     connect(heaptrackJob, &Job::finished, this, &Plugin::jobFinished);
0116 
0117     QList<KJob*> jobList;
0118     if (KJob* depJob = executePlugin->dependencyJob(defaultLaunch)) {
0119         jobList += depJob;
0120     }
0121     jobList += heaptrackJob;
0122 
0123     auto ecJob = new KDevelop::ExecuteCompositeJob(runController, jobList);
0124     ecJob->setObjectName(heaptrackJob->statusName());
0125     runController->registerJob(ecJob);
0126 
0127     m_launchAction->setEnabled(false);
0128 }
0129 
0130 void Plugin::attachHeaptrack()
0131 {
0132 #if HAVE_KSYSGUARD
0133     QPointer<KDevMI::ProcessSelectionDialog> dlg = new KDevMI::ProcessSelectionDialog(activeMainWindow());
0134     if (!dlg->exec() || !dlg->pidSelected()) {
0135         delete dlg;
0136         return;
0137     }
0138 
0139     auto heaptrackJob = new Job(dlg->pidSelected());
0140     delete dlg;
0141     connect(heaptrackJob, &Job::finished, this, &Plugin::jobFinished);
0142 
0143     heaptrackJob->setObjectName(heaptrackJob->statusName());
0144     core()->runController()->registerJob(heaptrackJob);
0145 
0146     m_launchAction->setEnabled(false);
0147 #endif
0148 }
0149 
0150 void Plugin::jobFinished(KJob* kjob)
0151 {
0152     auto job = static_cast<Job*>(kjob);
0153     Q_ASSERT(job);
0154 
0155     if (job->status() == KDevelop::OutputExecuteJob::JobStatus::JobSucceeded) {
0156         auto visualizer = new Visualizer(job->resultsFile(), this);
0157         visualizer->start();
0158     } else {
0159         QFile::remove(job->resultsFile());
0160     }
0161 
0162     m_launchAction->setEnabled(true);
0163 }
0164 
0165 int Plugin::configPages() const
0166 {
0167     return 1;
0168 }
0169 
0170 KDevelop::ConfigPage* Plugin::configPage(int number, QWidget* parent)
0171 {
0172     if (number) {
0173         return nullptr;
0174     }
0175 
0176     return new GlobalConfigPage(this, parent);
0177 }
0178 
0179 }
0180 
0181 #include "plugin.moc"
0182 #include "moc_plugin.cpp"