#include "callercalleemodel.h"
+#ifdef NO_K_LIB
+#include "noklib.h"
+#else
#include <KLocalizedString>
+#endif
+
+#include "util.h"
#include <QTextStream>
if (role == SortRole || role == MaxCostRole) {
return static_cast<qint64>(row.selfCost.allocated);
} else {
- return m_format.formatByteSize(row.selfCost.allocated, 1, KFormat::JEDECBinaryDialect);
+ return Util::formatByteSize(row.selfCost.allocated, 1);
}
case SelfAllocationsColumn:
return static_cast<qint64>(row.selfCost.allocations);
if (role == SortRole || role == MaxCostRole) {
return static_cast<qint64>(row.selfCost.peak);
} else {
- return m_format.formatByteSize(row.selfCost.peak, 1, KFormat::JEDECBinaryDialect);
+ return Util::formatByteSize(row.selfCost.peak, 1);
}
case SelfPeakInstancesColumn:
return static_cast<qint64>(row.selfCost.peak_instances);
if (role == SortRole || role == MaxCostRole) {
return static_cast<qint64>(row.selfCost.leaked);
} else {
- return m_format.formatByteSize(row.selfCost.leaked, 1, KFormat::JEDECBinaryDialect);
+ return Util::formatByteSize(row.selfCost.leaked, 1);
}
case InclusiveAllocatedColumn:
if (role == SortRole || role == MaxCostRole) {
return static_cast<qint64>(row.inclusiveCost.allocated);
} else {
- return m_format.formatByteSize(row.inclusiveCost.allocated, 1, KFormat::JEDECBinaryDialect);
+ return Util::formatByteSize(row.inclusiveCost.allocated, 1);
}
case InclusiveAllocationsColumn:
return static_cast<qint64>(row.inclusiveCost.allocations);
if (role == SortRole || role == MaxCostRole) {
return static_cast<qint64>(row.inclusiveCost.peak);
} else {
- return m_format.formatByteSize(row.inclusiveCost.peak, 1, KFormat::JEDECBinaryDialect);
+ return Util::formatByteSize(row.inclusiveCost.peak, 1);
}
case InclusivePeakInstancesColumn:
return static_cast<qint64>(row.inclusiveCost.peak_instances);
if (role == SortRole || role == MaxCostRole) {
return static_cast<qint64>(row.inclusiveCost.leaked);
} else {
- return m_format.formatByteSize(row.inclusiveCost.leaked, 1, KFormat::JEDECBinaryDialect);
+ return Util::formatByteSize(row.inclusiveCost.leaked, 1);
}
case FunctionColumn:
return row.location->function;
stream << '\n';
stream << i18n("inclusive: allocated %1 over %2 calls (%3 temporary, i.e. "
"%4%), peak at %5, leaked %6",
- m_format.formatByteSize(row.inclusiveCost.allocated, 1, KFormat::JEDECBinaryDialect),
+ Util::formatByteSize(row.inclusiveCost.allocated, 1),
row.inclusiveCost.allocations,
row.inclusiveCost.temporary, round(float(row.inclusiveCost.temporary) * 100.f * 100.f
/ std::max(int64_t(1), row.inclusiveCost.allocations))
/ 100.f,
- m_format.formatByteSize(row.inclusiveCost.peak, 1, KFormat::JEDECBinaryDialect),
- m_format.formatByteSize(row.inclusiveCost.leaked, 1, KFormat::JEDECBinaryDialect));
+ Util::formatByteSize(row.inclusiveCost.peak, 1),
+ Util::formatByteSize(row.inclusiveCost.leaked, 1));
stream << '\n';
stream << i18n(
"self: allocated %1 over %2 calls (%3 temporary, i.e. %4%), "
"peak at %5, leaked %6",
- m_format.formatByteSize(row.selfCost.allocated, 1, KFormat::JEDECBinaryDialect),
+ Util::formatByteSize(row.selfCost.allocated, 1),
row.selfCost.allocations, row.selfCost.temporary,
round(float(row.selfCost.temporary) * 100.f * 100.f / std::max(int64_t(1), row.selfCost.allocations))
/ 100.f,
- m_format.formatByteSize(row.selfCost.peak, 1, KFormat::JEDECBinaryDialect),
- m_format.formatByteSize(row.selfCost.leaked, 1, KFormat::JEDECBinaryDialect));
+ Util::formatByteSize(row.selfCost.peak, 1),
+ Util::formatByteSize(row.selfCost.leaked, 1));
stream << '\n';
stream << "</pre></qt>";
return tooltip;
#include <QAbstractTableModel>
#include <QVector>
+#ifdef NO_K_LIB
+#include "noklib.h"
+#else
#include <KFormat>
+#endif
#include "../allocationdata.h"
#include "locationdata.h"
private:
QVector<CallerCalleeData> m_rows;
CallerCalleeData m_maxCost;
+#ifndef NO_K_LIB
KFormat m_format;
+#endif
};
#endif // CALLERCALLEEMODEL_H
#include <QWheelEvent>
#include <QLineEdit>
+#ifdef NO_K_LIB
+#include "noklib.h"
+#else
#include <KColorScheme>
#include <KLocalizedString>
#include <KStandardAction>
#include <ThreadWeaver/ThreadWeaver>
+#endif
+
+#include "util.h"
enum CostType
{
// we build the tooltip text on demand, which is much faster than doing that
// for potentially thousands of items when we load the data
QString tooltip;
- KFormat format;
qint64 totalCost = 0;
{
auto item = this;
i18nc("%1: peak consumption in bytes, %2: relative number, %3: "
"function label",
"%1 (%2%) contribution to peak consumption in %3 and below.",
- format.formatByteSize(m_cost, 1, KFormat::JEDECBinaryDialect), fraction, function);
+ Util::formatByteSize(m_cost, 1), fraction, function);
break;
case PeakInstances:
tooltip =
break;
case Leaked:
tooltip = i18nc("%1: leaked bytes, %2: relative number, %3: function label", "%1 (%2%) leaked in %3 and below.",
- format.formatByteSize(m_cost, 1, KFormat::JEDECBinaryDialect), fraction, function);
+ Util::formatByteSize(m_cost, 1), fraction, function);
break;
case Allocated:
tooltip = i18nc("%1: allocated bytes, %2: relative number, %3: function label",
"%1 (%2%) allocated in %3 and below.",
- format.formatByteSize(m_cost, 1, KFormat::JEDECBinaryDialect), fraction, function);
+ Util::formatByteSize(m_cost, 1), fraction, function);
break;
}
totalCost += frame.cost.*member;
}
+#ifdef NO_K_LIB
+ QPalette pal;
+ const QPen pen(pal.color(QPalette::Active, QPalette::Foreground));
+#else
KColorScheme scheme(QPalette::Active);
const QPen pen(scheme.foreground().color());
+#endif
- KFormat format;
QString label;
switch (type) {
case Allocations:
label = i18n("%1 temporary allocations in total", totalCost);
break;
case Peak:
- label = i18n("%1 contribution to peak consumption", format.formatByteSize(totalCost, 1, KFormat::JEDECBinaryDialect));
+ label = i18n("%1 contribution to peak consumption", Util::formatByteSize(totalCost, 1));
break;
case PeakInstances:
label = i18n("%1 contribution to peak number of instances", totalCost);
break;
case Leaked:
- label = i18n("%1 leaked in total", format.formatByteSize(totalCost, 1, KFormat::JEDECBinaryDialect));
+ label = i18n("%1 leaked in total", Util::formatByteSize(totalCost, 1));
break;
case Allocated:
- label = i18n("%1 allocated in total", format.formatByteSize(totalCost, 1, KFormat::JEDECBinaryDialect));
+ label = i18n("%1 allocated in total", Util::formatByteSize(totalCost, 1));
break;
}
auto rootItem = new FrameGraphicsItem(totalCost, type, label, AllocationData::CoreCLRType::nonCoreCLR);
+#ifdef NO_K_LIB
+ rootItem->setBrush(pal.color(QPalette::Active, QPalette::Background));
+#else
rootItem->setBrush(scheme.background());
+#endif
rootItem->setPen(pen);
toGraphicsItems(topDownData, rootItem, member, totalCost * costThreshold / 100, collapseRecursion);
return rootItem;
layout()->addWidget(m_displayLabel);
layout()->addWidget(m_searchResultsLabel);
+#ifdef NO_K_LIB
+ // TODO!! implement back and forward
+#else
m_backAction = KStandardAction::back(this, SLOT(navigateBack()), this);
addAction(m_backAction);
m_forwardAction = KStandardAction::forward(this, SLOT(navigateForward()), this);
addAction(m_forwardAction);
+#endif
m_resetAction = new QAction(QIcon::fromTheme(QStringLiteral("go-first")), i18n("Reset View"), this);
m_resetAction->setShortcut(Qt::Key_Escape);
connect(m_resetAction, &QAction::triggered, this, [this]() {
setData(nullptr);
m_buildingScene = true;
- using namespace ThreadWeaver;
auto data = m_showBottomUpData ? m_bottomUpData : m_topDownData;
bool collapseRecursion = m_collapseRecursion;
auto source = m_costSource->currentData().value<CostType>();
auto threshold = m_costThreshold;
+#ifdef NO_K_LIB
+ setData(parseData(data, source, threshold, collapseRecursion));
+#else
+ using namespace ThreadWeaver;
stream() << make_job([data, source, threshold, collapseRecursion, this]() {
auto parsedData = parseData(data, source, threshold, collapseRecursion);
QMetaObject::invokeMethod(this, "setData", Qt::QueuedConnection, Q_ARG(FrameGraphicsItem*, parsedData));
});
+#endif
}
void FlameGraph::setTooltipItem(const FrameGraphicsItem* item)
m_searchResultsLabel->hide();
} else {
QString label;
- KFormat format;
const auto costFraction = fraction(match.directCost, m_rootItem->cost());
switch (m_costSource->currentData().value<CostType>()) {
case Allocations:
case Leaked:
case Allocated:
label = i18n("%1 (%2% of total of %3) matched by search.",
- format.formatByteSize(match.directCost, 1, KFormat::JEDECBinaryDialect), costFraction,
- format.formatByteSize(m_rootItem->cost(), 1, KFormat::JEDECBinaryDialect));
+ Util::formatByteSize(match.directCost, 1), costFraction,
+ Util::formatByteSize(m_rootItem->cost(), 1));
break;
}
m_searchResultsLabel->setText(label);
void FlameGraph::updateNavigationActions()
{
- m_backAction->setEnabled(m_selectedItem > 0);
- m_forwardAction->setEnabled(m_selectedItem + 1 < m_selectionHistory.size());
+ if (m_backAction) {
+ m_backAction->setEnabled(m_selectedItem > 0);
+ }
+ if (m_forwardAction) {
+ m_forwardAction->setEnabled(m_selectedItem + 1 < m_selectionHistory.size());
+ }
m_resetAction->setEnabled(m_selectedItem > 0);
}
#include <QApplication>
#include <QCommandLineParser>
+#ifdef NO_K_LIB
+#include "noklib.h"
+#else
#include <KAboutData>
#include <KLocalizedString>
+#endif
#include "../accumulatedtracedata.h"
#include "../allocationdata.h"
{
QApplication app(argc, argv);
+#ifndef NO_K_LIB
KLocalizedString::setApplicationDomain("heaptrack");
KAboutData aboutData(QStringLiteral("heaptrack_gui"), i18n("Heaptrack GUI"), QStringLiteral("0.1"),
aboutData.setOrganizationDomain("kde.org");
KAboutData::setApplicationData(aboutData);
+#endif
app.setWindowIcon(QIcon::fromTheme(QStringLiteral("office-chart-area")));
QCommandLineParser parser;
parser.addVersionOption();
parser.addHelpOption();
+#ifndef NO_K_LIB
aboutData.setupCommandLine(&parser);
+#endif
QCommandLineOption diffOption{{QStringLiteral("d"), QStringLiteral("diff")},
i18n("Base profile data to compare other files to."),
parser.addOption(showCoreCLRPartOption);
parser.process(app);
+#ifndef NO_K_LIB
aboutData.processCommandLine(&parser);
+#endif
bool isShowMalloc = parser.isSet(showMallocOption);
bool isShowManaged = parser.isSet(showManagedOption);
--- /dev/null
+/*
+ * Copyright 2017 Milian Wolff <mail@milianw.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef HEAPTRACK_GUI_CONFIG_H
+#define HEAPTRACK_GUI_CONFIG_H
+
+#ifndef NO_K_LIB
+#define KChart_FOUND 1
+#endif
+
+#endif // HEAPTRACK_CONFIG_H
#include <boost/functional/hash.hpp>
+#ifdef NO_K_LIB
+#include "noklib.h"
+#else
#include <KLocalizedString>
+#endif
struct LocationData
{
#include <cmath>
+#ifdef NO_K_LIB
+#include "noklib.h"
+#include <QAbstractButton>
+#else
#include <KConfigGroup>
#include <KLocalizedString>
#include <KRecursiveFilterProxyModel>
#include <KStandardAction>
+#endif
+
+#include "util.h"
#include <QAction>
#include <QDebug>
: QMainWindow(parent)
, m_ui(new Ui::MainWindow)
, m_parser(new Parser(this))
+#ifndef NO_K_LIB // TODO!! find a replacement for KSharedConfig
, m_config(KSharedConfig::openConfig(QStringLiteral("heaptrack_gui")))
+#endif
{
m_ui->setupUi(this);
+#ifndef NO_K_LIB // TODO!! find a replacement for KSharedConfig
auto group = m_config->group(Config::Groups::MainWindow);
auto state = group.readEntry(Config::Entries::State, QByteArray());
restoreState(state, MAINWINDOW_VERSION);
+#endif
m_ui->pages->setCurrentWidget(m_ui->openPage);
// TODO: proper progress report
m_ui->tabWidget->setTabEnabled(m_ui->tabWidget->indexOf(m_ui->bottomUpTab), true);
});
connect(m_parser, &Parser::objectTreeBottomUpDataAvailable, this, [=](const ObjectTreeData& data) {
- int maxGC = 0;
+ quint32 maxGC = 0;
for (const ObjectRowData& row: data) {
if (maxGC < row.gcNum)
maxGC = row.gcNum;
}
- for (int gc = 0; gc < maxGC; gc++) {
+ for (quint32 gc = 0; gc < maxGC; gc++) {
m_ui->filterGC->addItem(QString::number(gc+1));
}
objectTreeModel->resetData(data);
bottomUpModelFilterOutLeaves->setSummary(data);
topDownModel->setSummary(data);
callerCalleeModel->setSummary(data);
+#ifndef NO_K_LIB
KFormat format;
+#endif
QString textLeft;
QString textCenter;
QString textRight;
// xgettext:no-c-format
<< i18n("<dt><b>total runtime</b>:</dt><dd>%1s</dd>", totalTimeS)
<< i18n("<dt><b>total system memory</b>:</dt><dd>%1</dd>",
- format.formatByteSize(data.totalSystemMemory, 1, KFormat::JEDECBinaryDialect))
+ Util::formatByteSize(data.totalSystemMemory, 1))
<< "</dl></qt>";
}
qint64(data.cost.temporary / totalTimeS))
<< i18n("<dt><b>bytes allocated in total</b> (ignoring "
"deallocations):</dt><dd>%1 (%2/s)</dd>",
- format.formatByteSize(data.cost.allocated, 2, KFormat::JEDECBinaryDialect),
- format.formatByteSize(data.cost.allocated / totalTimeS, 1, KFormat::JEDECBinaryDialect))
+ Util::formatByteSize(data.cost.allocated, 2),
+ Util::formatByteSize(data.cost.allocated / totalTimeS, 1))
<< "</dl></qt>";
}
if (AccumulatedTraceData::isShowCoreCLRPartOption)
stream << "<qt><dl>" << i18n("<dt><b>peak heap memory consumption</b>:</dt><dd>%1 "
"after %2s</dd>"
"</dt><dd>%3 (CoreCLR), %4 (non-CoreCLR), %5 (unknown)</dd>",
- format.formatByteSize(data.cost.peak, 1, KFormat::JEDECBinaryDialect),
+ Util::formatByteSize(data.cost.peak, 1),
peakTimeS,
- format.formatByteSize(data.CoreCLRPart.peak, 1, KFormat::JEDECBinaryDialect),
- format.formatByteSize(data.nonCoreCLRPart.peak, 1, KFormat::JEDECBinaryDialect),
- format.formatByteSize(data.unknownPart.peak, 1, KFormat::JEDECBinaryDialect))
+ Util::formatByteSize(data.CoreCLRPart.peak, 1),
+ Util::formatByteSize(data.nonCoreCLRPart.peak, 1),
+ Util::formatByteSize(data.unknownPart.peak, 1))
<< i18n("<dt><b>peak RSS</b> (including heaptrack "
"overhead):</dt><dd>%1</dd>",
- format.formatByteSize(data.peakRSS, 1, KFormat::JEDECBinaryDialect))
+ Util::formatByteSize(data.peakRSS, 1))
<< i18n("<dt><b>total memory leaked</b>:</dt><dd>%1</dd>"
"</dt><dd>%2 (CoreCLR), %3 (non-CoreCLR), %4 (unknown)</dd>",
- format.formatByteSize(data.cost.leaked, 1, KFormat::JEDECBinaryDialect),
- format.formatByteSize(data.CoreCLRPart.leaked, 1, KFormat::JEDECBinaryDialect),
- format.formatByteSize(data.nonCoreCLRPart.leaked, 1, KFormat::JEDECBinaryDialect),
- format.formatByteSize(data.unknownPart.leaked, 1, KFormat::JEDECBinaryDialect))
+ Util::formatByteSize(data.cost.leaked, 1),
+ Util::formatByteSize(data.CoreCLRPart.leaked, 1),
+ Util::formatByteSize(data.nonCoreCLRPart.leaked, 1),
+ Util::formatByteSize(data.unknownPart.leaked, 1))
<< "</dl></qt>";
}
else
stream << "<qt><dl>" << i18n("<dt><b>peak heap memory consumption</b>:</dt><dd>%1 "
"after %2s</dd>"
"</dt><dd>%3 (CoreCLR), %4 (non-CoreCLR), %5 (sbrk heap), %6 (unknown)</dd>",
- format.formatByteSize(data.cost.peak, 1, KFormat::JEDECBinaryDialect),
+ Util::formatByteSize(data.cost.peak, 1),
peakTimeS,
- format.formatByteSize(data.CoreCLRPart.peak, 1, KFormat::JEDECBinaryDialect),
- format.formatByteSize(data.nonCoreCLRPart.peak, 1, KFormat::JEDECBinaryDialect),
- format.formatByteSize(data.untrackedPart.peak, 1, KFormat::JEDECBinaryDialect),
- format.formatByteSize(data.unknownPart.peak, 1, KFormat::JEDECBinaryDialect))
+ Util::formatByteSize(data.CoreCLRPart.peak, 1),
+ Util::formatByteSize(data.nonCoreCLRPart.peak, 1),
+ Util::formatByteSize(data.untrackedPart.peak, 1),
+ Util::formatByteSize(data.unknownPart.peak, 1))
<< i18n("<dt><b>peak RSS</b> (including heaptrack "
"overhead):</dt><dd>%1</dd>",
- format.formatByteSize(data.peakRSS, 1, KFormat::JEDECBinaryDialect))
+ Util::formatByteSize(data.peakRSS, 1))
<< i18n("<dt><b>total memory leaked</b>:</dt><dd>%1</dd>"
"</dt><dd>%2 (CoreCLR), %3 (non-CoreCLR), %4 (sbrk heap), %5 (unknown)</dd>",
- format.formatByteSize(data.cost.leaked, 1, KFormat::JEDECBinaryDialect),
- format.formatByteSize(data.CoreCLRPart.leaked, 1, KFormat::JEDECBinaryDialect),
- format.formatByteSize(data.nonCoreCLRPart.leaked, 1, KFormat::JEDECBinaryDialect),
- format.formatByteSize(data.untrackedPart.leaked, 1, KFormat::JEDECBinaryDialect),
- format.formatByteSize(data.unknownPart.leaked, 1, KFormat::JEDECBinaryDialect))
+ Util::formatByteSize(data.cost.leaked, 1),
+ Util::formatByteSize(data.CoreCLRPart.leaked, 1),
+ Util::formatByteSize(data.nonCoreCLRPart.leaked, 1),
+ Util::formatByteSize(data.untrackedPart.leaked, 1),
+ Util::formatByteSize(data.unknownPart.leaked, 1))
<< "</dl></qt>";
}
}
QTextStream stream(&textRight);
stream << "<qt><dl>" << i18n("<dt><b>peak heap memory consumption</b>:</dt><dd>%1 "
"after %2s</dd>",
- format.formatByteSize(data.cost.peak, 1, KFormat::JEDECBinaryDialect),
+ Util::formatByteSize(data.cost.peak, 1),
peakTimeS)
<< i18n("<dt><b>peak RSS</b> (including heaptrack "
"overhead):</dt><dd>%1</dd>",
- format.formatByteSize(data.peakRSS, 1, KFormat::JEDECBinaryDialect))
+ Util::formatByteSize(data.peakRSS, 1))
<< i18n("<dt><b>total memory leaked</b>:</dt><dd>%1</dd>",
- format.formatByteSize(data.cost.leaked, 1, KFormat::JEDECBinaryDialect))
+ Util::formatByteSize(data.cost.leaked, 1))
<< "</dl></qt>";
}
}
return false;
};
-
+#ifndef NO_K_LIB
auto validateInput = [this, validateInputFile]() {
m_ui->messages->hide();
m_ui->buttonBox->setEnabled(validateInputFile(m_ui->openFile->url().toLocalFile(), false)
const auto base = m_ui->compareTo->url().toLocalFile();
loadFile(path, base);
});
+#else
+ m_ui->buttonBox->setEnabled(true);
+
+ auto validateInputAndLoadFile = [this, validateInputFile]() {
+ const auto path = m_ui->openFile->text();
+ if (!validateInputFile(path, false)) {
+ return;
+ }
+ Q_ASSERT(!path.isEmpty());
+ const auto base = m_ui->compareTo->text();
+ if (!validateInputFile(base, true)) {
+ return;
+ }
+ loadFile(path, base);
+ };
+
+ connect(m_ui->buttonBox, &QDialogButtonBox::clicked, this, validateInputAndLoadFile);
+#endif
setupStacks();
setWindowTitle(i18n("Heaptrack"));
// closing the current file shows the stack page to open a new one
+#ifdef NO_K_LIB
+ m_openAction = new QAction(i18n("&Open..."), this);
+ connect(m_openAction, &QAction::triggered, this, &MainWindow::closeFile);
+ m_openAction->setEnabled(false);
+ m_openNewAction = new QAction(i18n("&New"), this);
+ connect(m_openNewAction, &QAction::triggered, this, &MainWindow::openNewFile);
+ m_closeAction = new QAction(i18n("&Close"), this);
+ connect(m_closeAction, &QAction::triggered, this, &MainWindow::close);
+ m_quitAction = new QAction(i18n("&Quit"), this);
+ connect(m_quitAction, &QAction::triggered, qApp, &QApplication::quit);
+#else
m_openAction = KStandardAction::open(this, SLOT(closeFile()), this);
m_openAction->setEnabled(false);
- m_ui->menu_File->addAction(m_openAction);
m_openNewAction = KStandardAction::openNew(this, SLOT(openNewFile()), this);
- m_ui->menu_File->addAction(m_openNewAction);
m_closeAction = KStandardAction::close(this, SLOT(close()), this);
- m_ui->menu_File->addAction(m_closeAction);
m_quitAction = KStandardAction::quit(qApp, SLOT(quit()), this);
+#endif
+ m_ui->menu_File->addAction(m_openAction);
+ m_ui->menu_File->addAction(m_openNewAction);
+ m_ui->menu_File->addAction(m_closeAction);
m_ui->menu_File->addAction(m_quitAction);
}
MainWindow::~MainWindow()
{
+#ifndef NO_K_LIB // TODO!! find a replacement for KSharedConfig
auto state = saveState(MAINWINDOW_VERSION);
auto group = m_config->group(Config::Groups::MainWindow);
group.writeEntry(Config::Entries::State, state);
+#endif
}
void MainWindow::loadFile(const QString& file, const QString& diffBase)
#include <QMainWindow>
+#ifndef NO_K_LIB
#include <KSharedConfig>
+#endif
namespace Ui {
class MainWindow;
QScopedPointer<Ui::MainWindow> m_ui;
Parser* m_parser;
+#ifndef NO_K_LIB // TODO!! find a replacement for KSharedConfig
KSharedConfig::Ptr m_config;
+#endif
bool m_diffMode = false;
QAction* m_openAction = nullptr;
<number>0</number>
</property>
<item>
- <widget class="KMessageWidget" name="messages">
- <property name="messageType">
- <enum>KMessageWidget::Information</enum>
- </property>
- <property name="icon">
- <iconset theme="dialog-error">
- <normaloff>.</normaloff>.</iconset>
- </property>
+ <widget class="QLabel" name="messages">
</widget>
</item>
<item>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="1" column="1">
- <widget class="KUrlRequester" name="openFile">
+ <widget class="QLineEdit" name="openFile">
<property name="toolTip">
<string><qt><p>This field specifies the primary heaptrack data file. These files are called <tt>heaptrack.$APP.$PID.gz</tt>. You can produce such a file by profiling your application, e.g. via:</p>
<pre><code>heaptrack &lt;yourapplication&gt; ...</code></pre>
<p>Or, alternatively, you can attach to a running process via</p>
<pre><code>heaptrack --pid $(pidof &lt;yourapplication&gt;)</code></pre></qt></string>
</property>
- <property name="filter">
- <string>heaptrack.*.*.gz</string>
- </property>
- <property name="placeholderText">
- <string>path/to/heaptrack.$APP.$PID.gz</string>
- </property>
- <property name="fileDialogModality">
- <enum>Qt::WindowModal</enum>
+ <property name="text">
+ <string>heaptrack.*.*.gz (PLEASE ENTER path/to/heaptrack.$APP.$PID.gz)</string>
</property>
</widget>
</item>
<item row="2" column="1">
- <widget class="KUrlRequester" name="compareTo">
+ <widget class="QLineEdit" name="compareTo">
<property name="toolTip">
<string><qt>You can optionally specify a second heaptrack data file to compare to. If set, this file will be used as a base and its cost gets subtracted from the primary data costs.</qt></string>
</property>
- <property name="filter">
- <string>heaptrack.*.*.gz</string>
- </property>
- <property name="placeholderText">
- <string>path/to/heaptrack.$APP.$PID.gz</string>
- </property>
- <property name="fileDialogModality">
- <enum>Qt::WindowModal</enum>
+ <property name="text">
+ <string>heaptrack.*.*.gz (PLEASE ENTER path/to/heaptrack.$APP.$PID.gz) [OPTIONAL]</string>
</property>
</widget>
</item>
</widget>
<customwidgets>
<customwidget>
- <class>KMessageWidget</class>
- <extends>QFrame</extends>
- <header>kmessagewidget.h</header>
- </customwidget>
- <customwidget>
- <class>KUrlRequester</class>
- <extends>QWidget</extends>
- <header>kurlrequester.h</header>
- </customwidget>
- <customwidget>
<class>FlameGraph</class>
<extends>QWidget</extends>
<header>flamegraph.h</header>
--- /dev/null
+#ifndef NOKLIB_H
+#define NOKLIB_H
+
+#include <QtCore/QString>
+
+inline QString i18n(const char *text)
+{
+ return text;
+}
+
+template <typename A1>
+inline QString i18n(const char *text, const A1 &a1)
+{
+ return QString(text).arg(a1);
+}
+
+template <typename A1, typename A2>
+inline QString i18n(const char *text, const A1 &a1, const A2 &a2)
+{
+ return QString(text).arg(a1).arg(a2);
+}
+
+template <typename A1, typename A2, typename A3>
+inline QString i18n(const char *text, const A1 &a1, const A2 &a2, const A3 &a3)
+{
+ return QString(text).arg(a1).arg(a2).arg(a3);
+}
+
+template <typename A1, typename A2, typename A3, typename A4>
+inline QString i18n(const char *text, const A1 &a1, const A2 &a2, const A3 &a3, const A4 &a4)
+{
+ return QString(text).arg(a1).arg(a2).arg(a3).arg(a4);
+}
+
+template <typename A1, typename A2, typename A3, typename A4, typename A5>
+inline QString i18n(const char *text, const A1 &a1, const A2 &a2, const A3 &a3, const A4 &a4, const A5 &a5)
+{
+ return QString(text).arg(a1).arg(a2).arg(a3).arg(a4).arg(a5);
+}
+
+template <typename A1, typename A2, typename A3, typename A4, typename A5, typename A6>
+inline QString i18n(const char *text, const A1 &a1, const A2 &a2, const A3 &a3, const A4 &a4, const A5 &a5, const A6 &a6)
+{
+ return QString(text).arg(a1).arg(a2).arg(a3).arg(a4).arg(a5).arg(a6);
+}
+
+template <typename A1>
+inline QString i18nc(const char */*comment*/, const char *text, const A1 &a1)
+{
+ return QString(text).arg(a1);
+}
+
+template <typename A1, typename A2>
+inline QString i18nc(const char */*comment*/, const char *text, const A1 &a1, const A2 &a2)
+{
+ return QString(text).arg(a1).arg(a2);
+}
+
+template <typename A1, typename A2, typename A3>
+inline QString i18nc(const char */*comment*/, const char *text, const A1 &a1, const A2 &a2, const A3 &a3)
+{
+ return QString(text).arg(a1).arg(a2).arg(a3);
+}
+
+template <typename A1, typename A2, typename A3, typename A4>
+inline QString i18nc(const char */*comment*/, const char *text, const A1 &a1, const A2 &a2, const A3 &a3, const A4 &a4)
+{
+ return QString(text).arg(a1).arg(a2).arg(a3).arg(a4);
+}
+
+template <typename A1, typename A2, typename A3, typename A4, typename A5>
+inline QString i18nc(const char */*comment*/, const char *text, const A1 &a1, const A2 &a2, const A3 &a3, const A4 &a4,
+ const A5 &a5)
+{
+ return QString(text).arg(a1).arg(a2).arg(a3).arg(a4).arg(a5);
+}
+
+template <typename A1, typename A2, typename A3, typename A4, typename A5, typename A6>
+inline QString i18nc(const char */*comment*/, const char *text, const A1 &a1, const A2 &a2, const A3 &a3, const A4 &a4,
+ const A5 &a5, const A6 &a6)
+{
+ return QString(text).arg(a1).arg(a2).arg(a3).arg(a4).arg(a5).arg(a6);
+}
+
+template <typename A1>
+inline QString i18np(const char */*singular*/, const char *plural, const A1 &a1)
+{
+ return QString(plural).arg(a1);
+}
+
+#endif // NOKLIB_H
#include <QDebug>
#include <QTextStream>
+#ifdef NO_K_LIB
+#include "noklib.h"
+#else
#include <KFormat>
#include <KLocalizedString>
+#endif
+
+#include "util.h"
#include <cmath>
if (role == SortRole) {
return static_cast<qint64>(row->allocated);
}
- return m_format.formatByteSize(row->allocated, 1, KFormat::JEDECBinaryDialect);
+ return Util::formatByteSize(row->allocated, 1);
case ReferencedSizeColumn:
if (role == SortRole) {
return static_cast<qint64>(row->referenced);
}
- return m_format.formatByteSize(row->referenced, 1, KFormat::JEDECBinaryDialect);
+ return Util::formatByteSize(row->referenced, 1);
case GCNumColumn:
return static_cast<quint64>(row->gcNum);
case NUM_COLUMNS:
#include <QAbstractItemModel>
#include <QVector>
+#ifndef NO_K_LIB
#include <KFormat>
+#endif
+
#include <string.h>
struct ObjectRowData
int rowOf(const ObjectRowData* row) const;
ObjectTreeData m_data;
+#ifndef NO_K_LIB
KFormat m_format;
+#endif
};
#endif // OBJECTTREEMODEL_H
#include "objecttreeproxy.h"
ObjectTreeProxy::ObjectTreeProxy(int nameColumn, int gcColumn, QObject* parent)
+#ifdef NO_K_LIB
+ : QSortFilterProxyModel(parent)
+#else
: KRecursiveFilterProxyModel(parent)
+#endif
, m_nameColumn(nameColumn)
, m_gcColumn(gcColumn)
{
+#if QT_VERSION >= 0x050A00
+ setRecursiveFilteringEnabled(true);
+#else
+ #pragma message("Qt 5.10+ is required, otherwise text filtering in GUI will not work as expected")
+#endif
}
ObjectTreeProxy::~ObjectTreeProxy() = default;
#ifndef OBJECTTREEPROXY_H
#define OBJECTTREEPROXY_H
+#ifdef NO_K_LIB
+#include <QSortFilterProxyModel>
+#else
#include <KRecursiveFilterProxyModel>
-
-class ObjectTreeProxy final : public KRecursiveFilterProxyModel
+#endif
+
+class ObjectTreeProxy final : public
+#ifdef NO_K_LIB
+ QSortFilterProxyModel
+#else
+ KRecursiveFilterProxyModel
+#endif
{
Q_OBJECT
public:
void setGCFilter(int gcFilter);
private:
+#ifdef NO_K_LIB
+ bool acceptRow(int source_row, const QModelIndex& source_parent) const;
+#else
bool acceptRow(int source_row, const QModelIndex& source_parent) const override;
+#endif
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
int m_nameColumn;
#include "parser.h"
+#ifdef NO_K_LIB
+#include "noklib.h"
+#else
#include <KLocalizedString>
#include <ThreadWeaver/ThreadWeaver>
+#endif
#include <QDebug>
void Parser::parse(const QString& path, const QString& diffBase)
{
- using namespace ThreadWeaver;
- stream() << make_job([this, path, diffBase]() {
- const auto stdPath = path.toStdString();
- auto data = make_shared<ParserData>();
- emit progressMessageAvailable(i18n("parsing data..."));
-
- if (!diffBase.isEmpty()) {
- ParserData diffData;
- auto readBase =
- async(launch::async, [&diffData, diffBase]() { return diffData.read(diffBase.toStdString()); });
- if (!data->read(stdPath)) {
- emit failedToOpen(path);
- return;
- }
- if (!readBase.get()) {
- emit failedToOpen(diffBase);
- return;
- }
- data->diff(diffData);
- data->stringCache.diffMode = true;
- } else if (!data->read(stdPath)) {
+#ifdef NO_K_LIB
+ parseJob(path, diffBase);
+#else
+ ThreadWeaver::stream() << ThreadWeaver::make_job([this, path, diffBase]() {
+ parseJob(path, diffBase);
+ });
+#endif
+}
+
+void Parser::parseJob(const QString& path, const QString& diffBase)
+{
+ const auto stdPath = path.toStdString();
+ auto data = make_shared<ParserData>();
+ emit progressMessageAvailable(i18n("parsing data..."));
+
+ if (!diffBase.isEmpty()) {
+ ParserData diffData;
+ auto readBase =
+ async(launch::async, [&diffData, diffBase]() { return diffData.read(diffBase.toStdString()); });
+ if (!data->read(stdPath)) {
emit failedToOpen(path);
return;
}
+ if (!readBase.get()) {
+ emit failedToOpen(diffBase);
+ return;
+ }
+ data->diff(diffData);
+ data->stringCache.diffMode = true;
+ } else if (!data->read(stdPath)) {
+ emit failedToOpen(path);
+ return;
+ }
- data->updateStringCache();
+ data->updateStringCache();
- AllocationData::Stats *partCoreclr;
- AllocationData::Stats *partNonCoreclr;
- AllocationData::Stats *partUntracked;
- AllocationData::Stats *partUnknown;
+ AllocationData::Stats *partCoreclr;
+ AllocationData::Stats *partNonCoreclr;
+ AllocationData::Stats *partUntracked;
+ AllocationData::Stats *partUnknown;
- if (AllocationData::display == AllocationData::DisplayId::malloc)
- {
- partCoreclr = &data->partCoreclr;
- partNonCoreclr = &data->partNonCoreclr;
- partUntracked = &data->partUntracked;
- partUnknown = &data->partUnknown;
- }
- else
- {
- partCoreclr = &data->partCoreclrMMAP;
- partNonCoreclr = &data->partNonCoreclrMMAP;
- partUntracked = &data->partUntrackedMMAP;
- partUnknown = &data->partUnknownMMAP;
- }
+ if (AllocationData::display == AllocationData::DisplayId::malloc)
+ {
+ partCoreclr = &data->partCoreclr;
+ partNonCoreclr = &data->partNonCoreclr;
+ partUntracked = &data->partUntracked;
+ partUnknown = &data->partUnknown;
+ }
+ else
+ {
+ partCoreclr = &data->partCoreclrMMAP;
+ partNonCoreclr = &data->partNonCoreclrMMAP;
+ partUntracked = &data->partUntrackedMMAP;
+ partUnknown = &data->partUnknownMMAP;
+ }
- emit summaryAvailable({QString::fromStdString(data->debuggee), *data->totalCost.getDisplay(), data->totalTime, data->getPeakTime(),
- data->peakRSS * 1024,
- data->systemInfo.pages * data->systemInfo.pageSize, data->fromAttached,
- *partCoreclr, *partNonCoreclr, *partUntracked, *partUnknown});
+ emit summaryAvailable({QString::fromStdString(data->debuggee), *data->totalCost.getDisplay(), data->totalTime, data->getPeakTime(),
+ data->peakRSS * 1024,
+ data->systemInfo.pages * data->systemInfo.pageSize, data->fromAttached,
+ *partCoreclr, *partNonCoreclr, *partUntracked, *partUnknown});
- emit progressMessageAvailable(i18n("merging allocations..."));
- // merge allocations before modifying the data again
- const auto mergedAllocations = mergeAllocations(*data, true);
- emit bottomUpDataAvailable(mergedAllocations);
+ emit progressMessageAvailable(i18n("merging allocations..."));
+ // merge allocations before modifying the data again
+ const auto mergedAllocations = mergeAllocations(*data, true);
+ emit bottomUpDataAvailable(mergedAllocations);
- if (!AccumulatedTraceData::isHideUnmanagedStackParts) {
- emit bottomUpFilterOutLeavesDataAvailable(mergeAllocations(*data, false));
- } else {
- emit bottomUpFilterOutLeavesDataAvailable(mergedAllocations);
- }
-
- if (AllocationData::display == AllocationData::DisplayId::managed) {
- const auto objectTreeBottomUpData = buildObjectTree(*data);
- emit objectTreeBottomUpDataAvailable(objectTreeBottomUpData);
- }
-
- // also calculate the size histogram
- emit progressMessageAvailable(i18n("building size histogram..."));
- const auto sizeHistogram = buildSizeHistogram(*data);
- emit sizeHistogramDataAvailable(sizeHistogram);
- // now data can be modified again for the chart data evaluation
-
- const auto diffMode = data->stringCache.diffMode;
- emit progressMessageAvailable(i18n("building charts..."));
- auto parallel = new Collection;
- *parallel << make_job([this, mergedAllocations]() {
- const auto topDownData = toTopDownData(mergedAllocations);
- emit topDownDataAvailable(topDownData);
- }) << make_job([this, mergedAllocations, diffMode]() {
- const auto callerCalleeData = toCallerCalleeData(mergedAllocations, diffMode);
- emit callerCalleeDataAvailable(callerCalleeData);
- });
- if (!data->stringCache.diffMode) {
- // only build charts when we are not diffing
- *parallel << make_job([this, data, stdPath]() {
- // this mutates data, and thus anything running in parallel must
- // not access data
- data->prepareBuildCharts();
- data->read(stdPath);
- emit consumedChartDataAvailable(data->consumedChartData);
- emit instancesChartDataAvailable(data->instancesChartData);
- emit allocationsChartDataAvailable(data->allocationsChartData);
- emit allocatedChartDataAvailable(data->allocatedChartData);
- emit temporaryChartDataAvailable(data->temporaryChartData);
- });
- }
-
- auto sequential = new Sequence;
- *sequential << parallel << make_job([this]() { emit finished(); });
-
- stream() << sequential;
- });
+ if (!AccumulatedTraceData::isHideUnmanagedStackParts) {
+ emit bottomUpFilterOutLeavesDataAvailable(mergeAllocations(*data, false));
+ } else {
+ emit bottomUpFilterOutLeavesDataAvailable(mergedAllocations);
+ }
+
+ if (AllocationData::display == AllocationData::DisplayId::managed) {
+ const auto objectTreeBottomUpData = buildObjectTree(*data);
+ emit objectTreeBottomUpDataAvailable(objectTreeBottomUpData);
+ }
+
+ // also calculate the size histogram
+ emit progressMessageAvailable(i18n("building size histogram..."));
+ const auto sizeHistogram = buildSizeHistogram(*data);
+ emit sizeHistogramDataAvailable(sizeHistogram);
+ // now data can be modified again for the chart data evaluation
+
+ const auto diffMode = data->stringCache.diffMode;
+ emit progressMessageAvailable(i18n("building charts..."));
+#ifndef NO_K_LIB
+ using namespace ThreadWeaver;
+ auto parallel = new Collection;
+ *parallel << make_job([this, mergedAllocations]()
+#endif
+ {
+ const auto topDownData = toTopDownData(mergedAllocations);
+ emit topDownDataAvailable(topDownData);
+ }
+#ifndef NO_K_LIB
+ ) << make_job([this, mergedAllocations, diffMode]()
+#endif
+ {
+ const auto callerCalleeData = toCallerCalleeData(mergedAllocations, diffMode);
+ emit callerCalleeDataAvailable(callerCalleeData);
+ }
+#ifndef NO_K_LIB
+ );
+#endif
+ if (!data->stringCache.diffMode) {
+ // only build charts when we are not diffing
+#ifndef NO_K_LIB
+ *parallel << make_job([this, data, stdPath]()
+#endif
+ {
+ // this mutates data, and thus anything running in parallel must
+ // not access data
+ data->prepareBuildCharts();
+ data->read(stdPath);
+ emit consumedChartDataAvailable(data->consumedChartData);
+ emit instancesChartDataAvailable(data->instancesChartData);
+ emit allocationsChartDataAvailable(data->allocationsChartData);
+ emit allocatedChartDataAvailable(data->allocatedChartData);
+ emit temporaryChartDataAvailable(data->temporaryChartData);
+ }
+#ifndef NO_K_LIB
+ );
+#endif
+ }
+
+#ifdef NO_K_LIB
+ emit finished();
+#else
+ auto sequential = new Sequence;
+ *sequential << parallel << make_job([this]() { emit finished(); });
+
+ stream() << sequential;
+#endif
}
void objectTreeBottomUpDataAvailable(const ObjectTreeData& data);
void finished();
void failedToOpen(const QString& path);
+
+private:
+ void parseJob(const QString& path, const QString& diffBase);
};
#endif // PARSER_H
#include "stacksmodel.h"
#include "treemodel.h"
+#ifdef NO_K_LIB
+#include "noklib.h"
+#else
#include <KLocalizedString>
+#endif
#include <QDebug>
#include "topproxy.h"
-#include <KLocalizedString>
-
namespace {
TreeModel::Columns toSource(TopProxy::Type type)
{
#include <QDebug>
#include <QTextStream>
+#ifdef NO_K_LIB
+#include "noklib.h"
+#else
#include <KFormat>
#include <KLocalizedString>
+#endif
+
+#include "util.h"
#include <cmath>
if (role == SortRole || role == MaxCostRole) {
return static_cast<qint64>(abs(row->cost.allocated));
}
- return m_format.formatByteSize(row->cost.allocated, 1, KFormat::JEDECBinaryDialect);
+ return Util::formatByteSize(row->cost.allocated, 1);
case AllocationsColumn:
if (role == SortRole || role == MaxCostRole) {
return static_cast<qint64>(abs(row->cost.allocations));
if (role == SortRole || role == MaxCostRole) {
return static_cast<qint64>(abs(row->cost.peak));
} else {
- return m_format.formatByteSize(row->cost.peak, 1, KFormat::JEDECBinaryDialect);
+ return Util::formatByteSize(row->cost.peak, 1);
}
case PeakInstancesColumn:
if (role == SortRole || role == MaxCostRole) {
if (role == SortRole || role == MaxCostRole) {
return static_cast<qint64>(abs(row->cost.leaked));
} else {
- return m_format.formatByteSize(row->cost.leaked, 1, KFormat::JEDECBinaryDialect);
+ return Util::formatByteSize(row->cost.leaked, 1);
}
case FunctionColumn:
return row->location->function;
}
stream << '\n';
stream << '\n';
+#ifndef NO_K_LIB
KFormat format;
+#endif
const auto allocatedFraction =
QString::number(double(row->cost.allocated) * 100. / m_maxCost.cost.allocated, 'g', 3);
const auto peakFraction = QString::number(double(row->cost.peak) * 100. / m_maxCost.cost.peak, 'g', 3);
const auto temporaryFractionTotal =
QString::number(double(row->cost.temporary) * 100. / m_maxCost.cost.temporary, 'g', 3);
stream << i18n("allocated: %1 (%2% of total)\n",
- format.formatByteSize(row->cost.allocated, 1, KFormat::JEDECBinaryDialect), allocatedFraction);
+ Util::formatByteSize(row->cost.allocated, 1), allocatedFraction);
stream << i18n("peak contribution: %1 (%2% of total)\n",
- format.formatByteSize(row->cost.peak, 1, KFormat::JEDECBinaryDialect), peakFraction);
+ Util::formatByteSize(row->cost.peak, 1), peakFraction);
stream << i18n("leaked: %1 (%2% of total)\n",
- format.formatByteSize(row->cost.leaked, 1, KFormat::JEDECBinaryDialect), leakedFraction);
+ Util::formatByteSize(row->cost.leaked, 1), leakedFraction);
stream << i18n("allocations: %1 (%2% of total)\n", row->cost.allocations, allocationsFraction);
stream << i18n("temporary: %1 (%2% of allocations, %3% of total)\n", row->cost.temporary, temporaryFraction,
temporaryFractionTotal);
#include <QAbstractItemModel>
#include <QVector>
+#ifndef NO_K_LIB
#include <KFormat>
+#endif
#include "../allocationdata.h"
#include "locationdata.h"
TreeData m_data;
RowData m_maxCost;
// TODO: update via global event filter when the locale changes (changeEvent)
+#ifndef NO_K_LIB
KFormat m_format;
+#endif
};
#endif // TREEMODEL_H
#include "treeproxy.h"
TreeProxy::TreeProxy(int functionColumn, int fileColumn, int moduleColumn, QObject* parent)
+#ifdef NO_K_LIB
+ : QSortFilterProxyModel(parent)
+#else
: KRecursiveFilterProxyModel(parent)
+#endif
, m_functionColumn(functionColumn)
, m_fileColumn(fileColumn)
, m_moduleColumn(moduleColumn)
{
+#if QT_VERSION >= 0x050A00
+ setRecursiveFilteringEnabled(true);
+#else
+ #pragma message("Qt 5.10+ is required, otherwise text filtering in GUI will not work as expected")
+#endif
}
TreeProxy::~TreeProxy() = default;
}
return true;
}
+
+#ifdef NO_K_LIB
+bool TreeProxy::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
+{
+ return acceptRow(sourceRow, sourceParent);
+}
+#endif
#ifndef TREEPROXY_H
#define TREEPROXY_H
+#ifdef NO_K_LIB
+#include <QSortFilterProxyModel>
+#else
#include <KRecursiveFilterProxyModel>
-
-class TreeProxy final : public KRecursiveFilterProxyModel
+#endif
+
+class TreeProxy final : public
+#ifdef NO_K_LIB
+ QSortFilterProxyModel
+#else
+ KRecursiveFilterProxyModel
+#endif
{
Q_OBJECT
public:
void setModuleFilter(const QString& moduleFilter);
private:
+#ifdef NO_K_LIB
+ bool acceptRow(int source_row, const QModelIndex& source_parent) const;
+ bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
+#else
bool acceptRow(int source_row, const QModelIndex& source_parent) const override;
+#endif
int m_functionColumn;
int m_fileColumn;
#include <QString>
+#ifndef NO_K_LIB
+#include <KFormat>
+#endif
+
QString Util::formatTime(qint64 ms)
{
if (ms > 60000) {
return QString::number(double(ms) / 1000, 'g', 3) + QLatin1Char('s');
}
}
+
+QString Util::formatByteSize(int64_t size, int precision)
+{
+#ifndef NO_K_LIB
+ static KFormat format;
+ return format.formatByteSize(size, precision, KFormat::JEDECBinaryDialect);
+#else
+ int32_t divider = 0;
+ QString suffix;
+ if (size < 1024)
+ {
+ suffix = "B";
+ }
+ else
+ {
+ const int32_t LimitMB = 1024 * 1024;
+ if (size < LimitMB)
+ {
+ divider = 1024;
+ suffix = "KB";
+ }
+ else
+ {
+ const int32_t LimitGB = LimitMB * 1024;
+ if (size < LimitGB)
+ {
+ divider = LimitMB;
+ suffix = "MB";
+ }
+ else
+ {
+ divider = LimitGB;
+ suffix = "GB";
+ }
+ }
+ }
+ QString result;
+ if (divider > 1)
+ {
+ result = QString::number(size / (double)divider, 'f', precision);
+ }
+ else
+ {
+ result = QString::number(size);
+ }
+ result += ' ';
+ result += suffix;
+ return result;
+#endif
+}
QString formatTime(qint64 ms);
+QString formatByteSize(int64_t size, int precision = 1);
+
}
#endif // UTIL_H
--- /dev/null
+QT += core gui
+
+greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
+
+TARGET = heaptrack_gui_qt
+TEMPLATE = app
+
+DEFINES += QT_DEPRECATED_WARNINGS
+
+#CONFIG += console c++11
+
+INCLUDEPATH += $$PWD/analyze/gui
+
+win32 {
+ CONFIG += NO_K_LIB
+ DEFINES += NO_K_LIB
+ INCLUDEPATH += $$(BOOST_LIB)
+ LIBS += -L$$(BOOST_LIB)/stage/lib
+}
+
+unix {
+# QMAKE_CXXFLAGS += -std=c++0x
+# QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-parameter # disable 'unused parameter' warning
+ LIBS += -lboost_program_options -lboost_iostreams -lpthread
+}
+
+#Test only!
+#DEFINES *= NO_K_LIB
+#CONFIG *= NO_K_LIB
+
+SOURCES += \
+ analyze/accumulatedtracedata.cpp \
+ analyze/gui/gui.cpp \
+ analyze/gui/callercalleemodel.cpp \
+ analyze/gui/costdelegate.cpp \
+ analyze/gui/flamegraph.cpp \
+ analyze/gui/mainwindow.cpp \
+ analyze/gui/objecttreemodel.cpp \
+ analyze/gui/objecttreeproxy.cpp \
+ analyze/gui/parser.cpp \
+ analyze/gui/stacksmodel.cpp \
+ analyze/gui/topproxy.cpp \
+ analyze/gui/treemodel.cpp \
+ analyze/gui/treeproxy.cpp \
+ analyze/gui/util.cpp
+
+HEADERS += \
+ analyze/accumulatedtracedata.h \
+ analyze/gui/callercalleemodel.h \
+ analyze/gui/costdelegate.h \
+ analyze/gui/flamegraph.h \
+ analyze/gui/gui_config.h \
+ analyze/gui/locationdata.h \
+ analyze/gui/mainwindow.h \
+ analyze/gui/objecttreemodel.h \
+ analyze/gui/objecttreeproxy.h \
+ analyze/gui/parser.h \
+ analyze/gui/stacksmodel.h \
+ analyze/gui/summarydata.h \
+ analyze/gui/topproxy.h \
+ analyze/gui/treemodel.h \
+ analyze/gui/treeproxy.h \
+ analyze/gui/util.h \
+ util/config.h
+
+FORMS += \
+ analyze/gui/mainwindow.ui
+
+!NO_K_LIB {
+ QT += KCoreAddons # for KFormat
+ QT += KI18n
+
+ QT += KIOCore
+ QT += KIOFileWidgets
+
+ QT += KConfigWidgets
+ QT += KIOWidgets
+
+ QT += KItemModels
+ QT += KChart
+
+ QT += ThreadWeaver
+
+ SOURCES += \
+ analyze/gui/chartmodel.cpp \
+ analyze/gui/chartproxy.cpp \
+ analyze/gui/chartwidget.cpp \
+ analyze/gui/histogrammodel.cpp \
+ analyze/gui/histogramwidget.cpp
+
+ HEADERS += \
+ analyze/gui/chartmodel.h \
+ analyze/gui/chartproxy.h \
+ analyze/gui/chartwidget.h \
+ analyze/gui/histogrammodel.h \
+ analyze/gui/histogramwidget.h
+}
+
+NO_K_LIB {
+ HEADERS += \
+ analyze/gui/noklib.h
+}
--- /dev/null
+TEMPLATE = app
+CONFIG += console c++11
+CONFIG -= app_bundle
+CONFIG -= qt
+
+#INCLUDEPATH += $$PWD/src
+
+LIBS += -lboost_program_options -lboost_iostreams -lpthread
+
+SOURCES += \
+ analyze/print/heaptrack_print.cpp \
+ analyze/accumulatedtracedata.cpp
+
+HEADERS += \
+ analyze/accumulatedtracedata.h
+ util/config.h
--- /dev/null
+/*
+ * Copyright 2014-2017 Milian Wolff <mail@milianw.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef HEAPTRACK_CONFIG_H
+#define HEAPTRACK_CONFIG_H
+
+#define HEAPTRACK_VERSION_STRING "1.1.0"
+#define HEAPTRACK_VERSION_MAJOR 1
+#define HEAPTRACK_VERSION_MINOR 1
+#define HEAPTRACK_VERSION_PATCH 0
+#define HEAPTRACK_VERSION ((HEAPTRACK_VERSION_MAJOR<<16)|(HEAPTRACK_VERSION_MINOR<<8)|(HEAPTRACK_VERSION_PATCH))
+
+#define HEAPTRACK_FILE_FORMAT_VERSION 2
+
+#define HEAPTRACK_DEBUG_BUILD 1
+
+// cfree() does not exist in glibc 2.26+.
+// See: https://bugs.kde.org/show_bug.cgi?id=383889
+#define HAVE_CFREE 0
+
+#endif // HEAPTRACK_CONFIG_H
--- /dev/null
+/*
+ * Copyright 2017 Milian Wolff <mail@milianw.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef LIBUNWIND_CONFIG_H
+#define LIBUNWIND_CONFIG_H
+
+#define LIBUNWIND_HAS_UNW_BACKTRACE 1
+
+#define LIBUNWIND_HAS_UNW_BACKTRACE_SKIP 0
+
+#endif // LIBUNWIND_CONFIG_H
+