Members of the KDE Community are recommended to subscribe to the kde-community mailing list at https://mail.kde.org/mailman/listinfo/kde-community to allow them to participate in important discussions and receive other important announcements

Commit 20ab513d authored by Anton Anikin's avatar Anton Anikin

Initial version of Heaptrack analyzer plugin

Summary:
Heaptrack is a fast heap memory profiler that runs on Linux. It allows you to track all heap memory allocations at run-time. Afterwards, the accompanying GUI tool can be used to find optimization opportunities in your code by analyzing the recorded profiling data. It allows you to:

 * Inspect peak heap memory consumption
 * Find memory leaks
 * Count overall number of memory allocations and find temporary allocations
 * Find small allocations with large overhead

You can use heaptrack pretty much wherever you are using Linux – it has been successfully used on 32bit and 64bit flavors of x86 and ARM platforms, both on embedded projects as well as desktop applications.

https://www.kdab.com/heaptrack-v1-0-0-release/
https://github.com/KDE/heaptrack/blob/master/README.md

Test Plan: Tested with kdevelop/kdevplatform master branch and heaptrack v1.0

Reviewers: mwolff, kfunk

Reviewed By: kfunk

Subscribers: kfunk, kdevelop-devel

Differential Revision: https://phabricator.kde.org/D4910
parent 5c4b2e9a
add_subdirectory(cppcheck)
add_subdirectory(heaptrack)
add_definitions(-DTRANSLATION_DOMAIN=\"kdevheaptrack\")
set(kdevheaptrack_SRCS
debug.cpp
job.cpp
plugin.cpp
utils.cpp
visualizer.cpp
config/globalconfigpage.cpp
)
ki18n_wrap_ui(kdevheaptrack_UI_SRCS
config/globalconfigpage.ui
)
kconfig_add_kcfg_files(kdevheaptrack_CONFIG_SRCS
config/globalsettings.kcfgc
)
add_library(kdevheaptrack_config STATIC
${kdevheaptrack_CONFIG_SRCS}
)
target_link_libraries(kdevheaptrack_config
KDev::Shell
)
kdevplatform_add_plugin(kdevheaptrack
JSON kdevheaptrack.json
SOURCES ${kdevheaptrack_SRCS} ${kdevheaptrack_UI_SRCS}
)
target_link_libraries(kdevheaptrack
kdevheaptrack_config
KDev::Project
)
if(KF5SysGuard_FOUND)
target_link_libraries(kdevheaptrack
kdevdebuggercommon
KF5::ProcessUi
)
endif()
install(FILES kdevheaptrack.rc DESTINATION ${KXMLGUI_INSTALL_DIR}/kdevheaptrack)
/* This file is part of KDevelop
Copyright 2017 Anton Anikin <anton.anikin@htower.ru>
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 "globalconfigpage.h"
#include "ui_globalconfigpage.h"
#include "globalsettings.h"
namespace Heaptrack
{
GlobalConfigPage::GlobalConfigPage(KDevelop::IPlugin* plugin, QWidget* parent)
: ConfigPage(plugin, GlobalSettings::self(), parent)
{
Ui::GlobalConfigPage ui;
ui.setupUi(this);
}
KDevelop::ConfigPage::ConfigPageType GlobalConfigPage::configPageType() const
{
return KDevelop::ConfigPage::AnalyzerConfigPage;
}
QString GlobalConfigPage::name() const
{
return i18n("Heaptrack");
}
QString GlobalConfigPage::fullName() const
{
return i18n("Configure Heaptrack Settings");
}
QIcon GlobalConfigPage::icon() const
{
return QIcon::fromTheme(QStringLiteral("office-chart-area"));
}
}
/* This file is part of KDevelop
Copyright 2017 Anton Anikin <anton.anikin@htower.ru>
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 <interfaces/configpage.h>
namespace Heaptrack
{
class GlobalConfigPage: public KDevelop::ConfigPage
{
Q_OBJECT
public:
GlobalConfigPage(KDevelop::IPlugin* plugin, QWidget* parent);
~GlobalConfigPage() override = default;
KDevelop::ConfigPage::ConfigPageType configPageType() const override;
QString name() const override;
QString fullName() const override;
QIcon icon() const override;
};
}
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Heaptrack::GlobalConfigPage</class>
<widget class="QWidget" name="Heaptrack::GlobalConfigPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>450</width>
<height>213</height>
</rect>
</property>
<property name="windowTitle">
<string>Cppcheck Settings</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QGroupBox" name="pathsGroupBox">
<property name="title">
<string>Executables</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="heaptrackExecutableLabel">
<property name="text">
<string>Heaptrack:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>kcfg_heaptrackExecutable</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="KUrlRequester" name="kcfg_heaptrackExecutable"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="heaptrackGuiExecutableLabel">
<property name="text">
<string>Visualizer:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="KUrlRequester" name="kcfg_heaptrackGuiExecutable"/>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>68</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>KUrlRequester</class>
<extends>QWidget</extends>
<header>kurlrequester.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
<?xml version="1.0" encoding="UTF-8"?>
<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
http://www.kde.org/standards/kcfg/1.0/kcfg.xsd">
<include>"utils.h"</include>
<group name="Heaptrack">
<entry name="heaptrackExecutable" key="heaptrackExecutable" type="Path">
<default code="true">findExecutable(QStringLiteral("heaptrack"))</default>
</entry>
<entry name="heaptrackGuiExecutable" key="heaptrackGuiExecutable" type="Path">
<default code="true">findExecutable(QStringLiteral("heaptrack_gui"))</default>
</entry>
</group>
</kcfg>
File=globalsettings.kcfg
NameSpace=Heaptrack
ClassName=GlobalSettings
Singleton=true
/* This file is part of KDevelop
Copyright 2017 Anton Anikin <anton.anikin@htower.ru>
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 "debug.h"
Q_LOGGING_CATEGORY(KDEV_HEAPTRACK, "kdevelop.analyzers.heaptrack")
/* This file is part of KDevelop
Copyright 2017 Anton Anikin <anton.anikin@htower.ru>
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 <QLoggingCategory>
Q_DECLARE_LOGGING_CATEGORY(KDEV_HEAPTRACK)
/* This file is part of KDevelop
Copyright 2017 Anton Anikin <anton.anikin@htower.ru>
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 <execute/iexecuteplugin.h>
#include <interfaces/icore.h>
#include <interfaces/iplugincontroller.h>
#include <interfaces/iruncontroller.h>
#include <interfaces/iuicontroller.h>
#include <klocalizedstring.h>
#include <kmessagebox.h>
#include <util/environmentprofilelist.h>
#include <util/path.h>
#include <QFileInfo>
#include <QRegularExpression>
namespace Heaptrack
{
Job::Job(KDevelop::ILaunchConfiguration* launchConfig)
: m_pid(-1)
{
Q_ASSERT(launchConfig);
auto pluginController = KDevelop::ICore::self()->pluginController();
auto iface = pluginController->pluginForExtension(QStringLiteral("org.kdevelop.IExecutePlugin"))->extension<IExecutePlugin>();
Q_ASSERT(iface);
QString envProfile = iface->environmentProfileName(launchConfig);
if (envProfile.isEmpty()) {
envProfile = KDevelop::EnvironmentProfileList(KSharedConfig::openConfig()).defaultProfileName();
}
setEnvironmentProfile(envProfile);
QString errorString;
m_analyzedExecutable = iface->executable(launchConfig, errorString).toLocalFile();
if (!errorString.isEmpty()) {
setError(-1);
setErrorText(errorString);
}
QStringList analyzedExecutableArguments = iface->arguments(launchConfig, errorString);
if (!errorString.isEmpty()) {
setError(-1);
setErrorText(errorString);
}
QUrl workDir = iface->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()
: QString("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);
}
}
/* This file is part of KDevelop
Copyright 2017 Anton Anikin <anton.anikin@htower.ru>
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 <interfaces/istatus.h>
#include <outputview/outputexecutejob.h>
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);
explicit Job(long int pid);
~Job() override;
void start() override;
QString statusName() const override;
QString resultsFile() const;
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;
};
}
{
"GenericName": "Heaptrack Support",
"GenericName[x-test]": "xxHeaptrack Supportxx",
"KPlugin": {
"Authors": [
{
"Name": "Anton Anikin",
"Name[x-test]": "xxAnton Anikinxx"
}
],
"Category": "Analyzers",
"Icon": "office-chart-area",
"Id": "kdevheaptrack",
"License": "GPL",
"ServiceTypes": [
"KDevelop/Plugin"
],
"Name": "Heaptrack Support",
"Name[x-test]": "xxHeaptrack Supportxx",
"Description": "This plugin integrates Heaptrack to KDevelop",
"Description[x-test]": "xxThis plugin integrates Heaptrack to KDevelopkxx"
},
"X-KDevelop-Category": "Global",
"X-KDevelop-Mode": "GUI",
"X-KDevelop-IRequired": [
"org.kdevelop.IExecutePlugin"
]
}
<!DOCTYPE kpartgui>
<gui name="kdevheaptrack" library="kdevheaptrack" version="2" translationDomain="kdevheaptrack">
<MenuBar>
<Menu name="run">
<Menu name="debug_attach_menu">
<Action name="heaptrack_attach"/>
</Menu>
<Action name="heaptrack_launch" group="run_operations"/>
</Menu>
</MenuBar>
</gui>
/* This file is part of KDevelop
Copyright 2017 Anton Anikin <anton.anikin@htower.ru>
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 "config.h"
#if KF5SysGuard_FOUND
#include "debuggers/common/dialogs/processselection.h"
#endif
#include <execute/iexecuteplugin.h>
#include <interfaces/iplugincontroller.h>
#include <interfaces/iuicontroller.h>
#include <kactioncollection.h>
#include <kpluginfactory.h>
#include <shell/core.h>
#include <shell/launchconfiguration.h>
#include <shell/runcontroller.h>
#include <util/executecompositejob.h>
#include <QFile>
K_PLUGIN_FACTORY_WITH_JSON(HeaptrackFactory, "kdevheaptrack.json", registerPlugin<Heaptrack::Plugin>();)
namespace Heaptrack
{
Plugin::Plugin(QObject* parent, const QVariantList&)
: IPlugin(QStringLiteral("kdevheaptrack"), parent)
{
setComponentName(QStringLiteral("kdevheaptrack"), i18n("Heaptrack Analyzer"));
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();
Q_ASSERT(runController);
auto defaultLaunch = runController->defaultLaunch();
if (!defaultLaunch) {
return;
}
auto pluginController = core()->self()->pluginController();
auto iface = pluginController->pluginForExtension(QStringLiteral("org.kdevelop.IExecutePlugin"))->extension<IExecutePlugin>();
Q_ASSERT(iface);
auto heaptrackJob = new Job(defaultLaunch);
connect(heaptrackJob, &Job::finished, this, &Plugin::jobFinished);
QList<KJob*> jobList;
if (KJob* depJob = iface->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
KDevMI::ProcessSelectionDialog dlg(activeMainWindow());
if (!dlg.exec() || !dlg.pidSelected()) {