From: Alexey Chernobaev Date: Thu, 15 Feb 2018 17:58:05 +0000 (+0300) Subject: NO_K_LIB build mode (without KDE libraries) implemented X-Git-Tag: submit/tizen/20180620.112952^2^2~79 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=351308bc69b699bd923e44490bd29a1fc5f248b2;p=sdk%2Ftools%2Fheaptrack.git NO_K_LIB build mode (without KDE libraries) implemented --- diff --git a/src/analyze/gui/callercalleemodel.cpp b/src/analyze/gui/callercalleemodel.cpp index 5ce63d9..ee17565 100644 --- a/src/analyze/gui/callercalleemodel.cpp +++ b/src/analyze/gui/callercalleemodel.cpp @@ -18,7 +18,13 @@ #include "callercalleemodel.h" +#ifdef NO_K_LIB +#include "noklib.h" +#else #include +#endif + +#include "util.h" #include @@ -183,7 +189,7 @@ QVariant CallerCalleeModel::data(const QModelIndex& index, int role) const if (role == SortRole || role == MaxCostRole) { return static_cast(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(row.selfCost.allocations); @@ -193,7 +199,7 @@ QVariant CallerCalleeModel::data(const QModelIndex& index, int role) const if (role == SortRole || role == MaxCostRole) { return static_cast(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(row.selfCost.peak_instances); @@ -201,13 +207,13 @@ QVariant CallerCalleeModel::data(const QModelIndex& index, int role) const if (role == SortRole || role == MaxCostRole) { return static_cast(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(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(row.inclusiveCost.allocations); @@ -217,7 +223,7 @@ QVariant CallerCalleeModel::data(const QModelIndex& index, int role) const if (role == SortRole || role == MaxCostRole) { return static_cast(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(row.inclusiveCost.peak_instances); @@ -225,7 +231,7 @@ QVariant CallerCalleeModel::data(const QModelIndex& index, int role) const if (role == SortRole || role == MaxCostRole) { return static_cast(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; @@ -260,23 +266,23 @@ QVariant CallerCalleeModel::data(const QModelIndex& index, int role) const 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 << ""; return tooltip; diff --git a/src/analyze/gui/callercalleemodel.h b/src/analyze/gui/callercalleemodel.h index b824873..da5e1ba 100644 --- a/src/analyze/gui/callercalleemodel.h +++ b/src/analyze/gui/callercalleemodel.h @@ -22,7 +22,11 @@ #include #include +#ifdef NO_K_LIB +#include "noklib.h" +#else #include +#endif #include "../allocationdata.h" #include "locationdata.h" @@ -89,7 +93,9 @@ public: private: QVector m_rows; CallerCalleeData m_maxCost; +#ifndef NO_K_LIB KFormat m_format; +#endif }; #endif // CALLERCALLEEMODEL_H diff --git a/src/analyze/gui/flamegraph.cpp b/src/analyze/gui/flamegraph.cpp index b680b49..d250c61 100644 --- a/src/analyze/gui/flamegraph.cpp +++ b/src/analyze/gui/flamegraph.cpp @@ -37,10 +37,16 @@ #include #include +#ifdef NO_K_LIB +#include "noklib.h" +#else #include #include #include #include +#endif + +#include "util.h" enum CostType { @@ -201,7 +207,6 @@ QString FrameGraphicsItem::description() const // 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; @@ -231,7 +236,7 @@ QString FrameGraphicsItem::description() const 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 = @@ -242,12 +247,12 @@ QString FrameGraphicsItem::description() const 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; } @@ -448,10 +453,14 @@ FrameGraphicsItem* parseData(const QVector& topDownData, CostType type, 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: @@ -461,20 +470,24 @@ FrameGraphicsItem* parseData(const QVector& topDownData, CostType type, 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; @@ -635,10 +648,14 @@ FlameGraph::FlameGraph(QWidget* parent, Qt::WindowFlags flags) 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]() { @@ -725,15 +742,19 @@ void FlameGraph::showData() 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(); 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) @@ -839,7 +860,6 @@ void FlameGraph::setSearchValue(const QString& value) m_searchResultsLabel->hide(); } else { QString label; - KFormat format; const auto costFraction = fraction(match.directCost, m_rootItem->cost()); switch (m_costSource->currentData().value()) { case Allocations: @@ -852,8 +872,8 @@ void FlameGraph::setSearchValue(const QString& value) 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); @@ -877,7 +897,11 @@ void FlameGraph::navigateForward() 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); } diff --git a/src/analyze/gui/gui.cpp b/src/analyze/gui/gui.cpp index 25c7b63..1fe223d 100644 --- a/src/analyze/gui/gui.cpp +++ b/src/analyze/gui/gui.cpp @@ -19,8 +19,12 @@ #include #include +#ifdef NO_K_LIB +#include "noklib.h" +#else #include #include +#endif #include "../accumulatedtracedata.h" #include "../allocationdata.h" @@ -30,6 +34,7 @@ int main(int argc, char** argv) { QApplication app(argc, argv); +#ifndef NO_K_LIB KLocalizedString::setApplicationDomain("heaptrack"); KAboutData aboutData(QStringLiteral("heaptrack_gui"), i18n("Heaptrack GUI"), QStringLiteral("0.1"), @@ -43,12 +48,15 @@ int main(int argc, char** argv) 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."), @@ -75,7 +83,9 @@ int main(int argc, char** argv) parser.addOption(showCoreCLRPartOption); parser.process(app); +#ifndef NO_K_LIB aboutData.processCommandLine(&parser); +#endif bool isShowMalloc = parser.isSet(showMallocOption); bool isShowManaged = parser.isSet(showManagedOption); diff --git a/src/analyze/gui/gui_config.h b/src/analyze/gui/gui_config.h new file mode 100644 index 0000000..c2ef130 --- /dev/null +++ b/src/analyze/gui/gui_config.h @@ -0,0 +1,26 @@ +/* + * Copyright 2017 Milian Wolff + * + * 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 diff --git a/src/analyze/gui/locationdata.h b/src/analyze/gui/locationdata.h index 2a96b99..f974f6b 100644 --- a/src/analyze/gui/locationdata.h +++ b/src/analyze/gui/locationdata.h @@ -25,7 +25,11 @@ #include +#ifdef NO_K_LIB +#include "noklib.h" +#else #include +#endif struct LocationData { diff --git a/src/analyze/gui/mainwindow.cpp b/src/analyze/gui/mainwindow.cpp index b1a489e..0367bd5 100644 --- a/src/analyze/gui/mainwindow.cpp +++ b/src/analyze/gui/mainwindow.cpp @@ -22,10 +22,17 @@ #include +#ifdef NO_K_LIB +#include "noklib.h" +#include +#else #include #include #include #include +#endif + +#include "util.h" #include #include @@ -226,13 +233,17 @@ MainWindow::MainWindow(QWidget* parent) : 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 @@ -264,12 +275,12 @@ MainWindow::MainWindow(QWidget* parent) 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); @@ -297,7 +308,9 @@ MainWindow::MainWindow(QWidget* parent) bottomUpModelFilterOutLeaves->setSummary(data); topDownModel->setSummary(data); callerCalleeModel->setSummary(data); +#ifndef NO_K_LIB KFormat format; +#endif QString textLeft; QString textCenter; QString textRight; @@ -316,7 +329,7 @@ MainWindow::MainWindow(QWidget* parent) // xgettext:no-c-format << i18n("
total runtime:
%1s
", totalTimeS) << i18n("
total system memory:
%1
", - format.formatByteSize(data.totalSystemMemory, 1, KFormat::JEDECBinaryDialect)) + Util::formatByteSize(data.totalSystemMemory, 1)) << ""; } @@ -334,8 +347,8 @@ MainWindow::MainWindow(QWidget* parent) qint64(data.cost.temporary / totalTimeS)) << i18n("
bytes allocated in total (ignoring " "deallocations):
%1 (%2/s)
", - 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)) << ""; } if (AccumulatedTraceData::isShowCoreCLRPartOption) @@ -347,20 +360,20 @@ MainWindow::MainWindow(QWidget* parent) stream << "
" << i18n("
peak heap memory consumption:
%1 " "after %2s
" "
%3 (CoreCLR), %4 (non-CoreCLR), %5 (unknown)
", - 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("
peak RSS (including heaptrack " "overhead):
%1
", - format.formatByteSize(data.peakRSS, 1, KFormat::JEDECBinaryDialect)) + Util::formatByteSize(data.peakRSS, 1)) << i18n("
total memory leaked:
%1
" "
%2 (CoreCLR), %3 (non-CoreCLR), %4 (unknown)
", - 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)) << "
"; } else @@ -368,22 +381,22 @@ MainWindow::MainWindow(QWidget* parent) stream << "
" << i18n("
peak heap memory consumption:
%1 " "after %2s
" "
%3 (CoreCLR), %4 (non-CoreCLR), %5 (sbrk heap), %6 (unknown)
", - 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("
peak RSS (including heaptrack " "overhead):
%1
", - format.formatByteSize(data.peakRSS, 1, KFormat::JEDECBinaryDialect)) + Util::formatByteSize(data.peakRSS, 1)) << i18n("
total memory leaked:
%1
" "
%2 (CoreCLR), %3 (non-CoreCLR), %4 (sbrk heap), %5 (unknown)
", - 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)) << "
"; } } @@ -392,13 +405,13 @@ MainWindow::MainWindow(QWidget* parent) QTextStream stream(&textRight); stream << "
" << i18n("
peak heap memory consumption:
%1 " "after %2s
", - format.formatByteSize(data.cost.peak, 1, KFormat::JEDECBinaryDialect), + Util::formatByteSize(data.cost.peak, 1), peakTimeS) << i18n("
peak RSS (including heaptrack " "overhead):
%1
", - format.formatByteSize(data.peakRSS, 1, KFormat::JEDECBinaryDialect)) + Util::formatByteSize(data.peakRSS, 1)) << i18n("
total memory leaked:
%1
", - format.formatByteSize(data.cost.leaked, 1, KFormat::JEDECBinaryDialect)) + Util::formatByteSize(data.cost.leaked, 1)) << "
"; } @@ -492,7 +505,7 @@ MainWindow::MainWindow(QWidget* parent) } 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) @@ -507,6 +520,24 @@ MainWindow::MainWindow(QWidget* parent) 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(); @@ -545,22 +576,36 @@ MainWindow::MainWindow(QWidget* parent) 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) diff --git a/src/analyze/gui/mainwindow.h b/src/analyze/gui/mainwindow.h index b0d9b8d..a6900e2 100644 --- a/src/analyze/gui/mainwindow.h +++ b/src/analyze/gui/mainwindow.h @@ -21,7 +21,9 @@ #include +#ifndef NO_K_LIB #include +#endif namespace Ui { class MainWindow; @@ -52,7 +54,9 @@ private: QScopedPointer 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; diff --git a/src/analyze/gui/mainwindow.ui b/src/analyze/gui/mainwindow.ui index 4f289c8..bcce059 100644 --- a/src/analyze/gui/mainwindow.ui +++ b/src/analyze/gui/mainwindow.ui @@ -28,14 +28,7 @@ 0 - - - KMessageWidget::Information - - - - .. - + @@ -62,37 +55,25 @@ - + <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> - - heaptrack.*.*.gz - - - path/to/heaptrack.$APP.$PID.gz - - - Qt::WindowModal + + heaptrack.*.*.gz (PLEASE ENTER path/to/heaptrack.$APP.$PID.gz) - + <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> - - heaptrack.*.*.gz - - - path/to/heaptrack.$APP.$PID.gz - - - Qt::WindowModal + + heaptrack.*.*.gz (PLEASE ENTER path/to/heaptrack.$APP.$PID.gz) [OPTIONAL] @@ -867,16 +848,6 @@ - - KMessageWidget - QFrame -
kmessagewidget.h
-
- - KUrlRequester - QWidget -
kurlrequester.h
-
FlameGraph QWidget diff --git a/src/analyze/gui/noklib.h b/src/analyze/gui/noklib.h new file mode 100644 index 0000000..4393041 --- /dev/null +++ b/src/analyze/gui/noklib.h @@ -0,0 +1,91 @@ +#ifndef NOKLIB_H +#define NOKLIB_H + +#include + +inline QString i18n(const char *text) +{ + return text; +} + +template +inline QString i18n(const char *text, const A1 &a1) +{ + return QString(text).arg(a1); +} + +template +inline QString i18n(const char *text, const A1 &a1, const A2 &a2) +{ + return QString(text).arg(a1).arg(a2); +} + +template +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 +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 +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 +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 +inline QString i18nc(const char */*comment*/, const char *text, const A1 &a1) +{ + return QString(text).arg(a1); +} + +template +inline QString i18nc(const char */*comment*/, const char *text, const A1 &a1, const A2 &a2) +{ + return QString(text).arg(a1).arg(a2); +} + +template +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 +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 +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 +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 +inline QString i18np(const char */*singular*/, const char *plural, const A1 &a1) +{ + return QString(plural).arg(a1); +} + +#endif // NOKLIB_H diff --git a/src/analyze/gui/objecttreemodel.cpp b/src/analyze/gui/objecttreemodel.cpp index cea03d1..b9d6a41 100644 --- a/src/analyze/gui/objecttreemodel.cpp +++ b/src/analyze/gui/objecttreemodel.cpp @@ -21,8 +21,14 @@ #include #include +#ifdef NO_K_LIB +#include "noklib.h" +#else #include #include +#endif + +#include "util.h" #include @@ -122,12 +128,12 @@ QVariant ObjectTreeModel::data(const QModelIndex& index, int role) const if (role == SortRole) { return static_cast(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(row->referenced); } - return m_format.formatByteSize(row->referenced, 1, KFormat::JEDECBinaryDialect); + return Util::formatByteSize(row->referenced, 1); case GCNumColumn: return static_cast(row->gcNum); case NUM_COLUMNS: diff --git a/src/analyze/gui/objecttreemodel.h b/src/analyze/gui/objecttreemodel.h index 2202da5..f011f9f 100644 --- a/src/analyze/gui/objecttreemodel.h +++ b/src/analyze/gui/objecttreemodel.h @@ -22,7 +22,10 @@ #include #include +#ifndef NO_K_LIB #include +#endif + #include struct ObjectRowData @@ -96,7 +99,9 @@ private: int rowOf(const ObjectRowData* row) const; ObjectTreeData m_data; +#ifndef NO_K_LIB KFormat m_format; +#endif }; #endif // OBJECTTREEMODEL_H diff --git a/src/analyze/gui/objecttreeproxy.cpp b/src/analyze/gui/objecttreeproxy.cpp index 53e32e0..a638e94 100644 --- a/src/analyze/gui/objecttreeproxy.cpp +++ b/src/analyze/gui/objecttreeproxy.cpp @@ -19,10 +19,19 @@ #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; diff --git a/src/analyze/gui/objecttreeproxy.h b/src/analyze/gui/objecttreeproxy.h index 843fa74..8bb4911 100644 --- a/src/analyze/gui/objecttreeproxy.h +++ b/src/analyze/gui/objecttreeproxy.h @@ -19,9 +19,18 @@ #ifndef OBJECTTREEPROXY_H #define OBJECTTREEPROXY_H +#ifdef NO_K_LIB +#include +#else #include - -class ObjectTreeProxy final : public KRecursiveFilterProxyModel +#endif + +class ObjectTreeProxy final : public +#ifdef NO_K_LIB + QSortFilterProxyModel +#else + KRecursiveFilterProxyModel +#endif { Q_OBJECT public: @@ -33,7 +42,11 @@ public slots: 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; diff --git a/src/analyze/gui/parser.cpp b/src/analyze/gui/parser.cpp index f73fcb3..82bcb48 100644 --- a/src/analyze/gui/parser.cpp +++ b/src/analyze/gui/parser.cpp @@ -18,8 +18,12 @@ #include "parser.h" +#ifdef NO_K_LIB +#include "noklib.h" +#else #include #include +#endif #include @@ -851,108 +855,137 @@ Parser::~Parser() = default; 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(); - 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(); + 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 } diff --git a/src/analyze/gui/parser.h b/src/analyze/gui/parser.h index 7503b55..2554626 100644 --- a/src/analyze/gui/parser.h +++ b/src/analyze/gui/parser.h @@ -54,6 +54,9 @@ signals: void objectTreeBottomUpDataAvailable(const ObjectTreeData& data); void finished(); void failedToOpen(const QString& path); + +private: + void parseJob(const QString& path, const QString& diffBase); }; #endif // PARSER_H diff --git a/src/analyze/gui/stacksmodel.cpp b/src/analyze/gui/stacksmodel.cpp index 9e29e16..2c01fdd 100644 --- a/src/analyze/gui/stacksmodel.cpp +++ b/src/analyze/gui/stacksmodel.cpp @@ -19,7 +19,11 @@ #include "stacksmodel.h" #include "treemodel.h" +#ifdef NO_K_LIB +#include "noklib.h" +#else #include +#endif #include diff --git a/src/analyze/gui/topproxy.cpp b/src/analyze/gui/topproxy.cpp index ad6cbb6..909729d 100644 --- a/src/analyze/gui/topproxy.cpp +++ b/src/analyze/gui/topproxy.cpp @@ -18,8 +18,6 @@ #include "topproxy.h" -#include - namespace { TreeModel::Columns toSource(TopProxy::Type type) { diff --git a/src/analyze/gui/treemodel.cpp b/src/analyze/gui/treemodel.cpp index 09b6b6c..c29737b 100644 --- a/src/analyze/gui/treemodel.cpp +++ b/src/analyze/gui/treemodel.cpp @@ -21,8 +21,14 @@ #include #include +#ifdef NO_K_LIB +#include "noklib.h" +#else #include #include +#endif + +#include "util.h" #include @@ -165,7 +171,7 @@ QVariant TreeModel::data(const QModelIndex& index, int role) const if (role == SortRole || role == MaxCostRole) { return static_cast(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(abs(row->cost.allocations)); @@ -180,7 +186,7 @@ QVariant TreeModel::data(const QModelIndex& index, int role) const if (role == SortRole || role == MaxCostRole) { return static_cast(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) { @@ -191,7 +197,7 @@ QVariant TreeModel::data(const QModelIndex& index, int role) const if (role == SortRole || role == MaxCostRole) { return static_cast(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; @@ -225,7 +231,9 @@ QVariant TreeModel::data(const QModelIndex& index, int role) const } 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); @@ -237,11 +245,11 @@ QVariant TreeModel::data(const QModelIndex& index, int role) const 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); diff --git a/src/analyze/gui/treemodel.h b/src/analyze/gui/treemodel.h index a8903f7..f46da65 100644 --- a/src/analyze/gui/treemodel.h +++ b/src/analyze/gui/treemodel.h @@ -22,7 +22,9 @@ #include #include +#ifndef NO_K_LIB #include +#endif #include "../allocationdata.h" #include "locationdata.h" @@ -96,7 +98,9 @@ private: 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 diff --git a/src/analyze/gui/treeproxy.cpp b/src/analyze/gui/treeproxy.cpp index 93d96b7..db4a33b 100644 --- a/src/analyze/gui/treeproxy.cpp +++ b/src/analyze/gui/treeproxy.cpp @@ -19,11 +19,20 @@ #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; @@ -72,3 +81,10 @@ bool TreeProxy::acceptRow(int sourceRow, const QModelIndex& sourceParent) const } return true; } + +#ifdef NO_K_LIB +bool TreeProxy::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const +{ + return acceptRow(sourceRow, sourceParent); +} +#endif diff --git a/src/analyze/gui/treeproxy.h b/src/analyze/gui/treeproxy.h index 46e2247..219b0d4 100644 --- a/src/analyze/gui/treeproxy.h +++ b/src/analyze/gui/treeproxy.h @@ -19,9 +19,18 @@ #ifndef TREEPROXY_H #define TREEPROXY_H +#ifdef NO_K_LIB +#include +#else #include - -class TreeProxy final : public KRecursiveFilterProxyModel +#endif + +class TreeProxy final : public +#ifdef NO_K_LIB + QSortFilterProxyModel +#else + KRecursiveFilterProxyModel +#endif { Q_OBJECT public: @@ -34,7 +43,12 @@ public slots: 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; diff --git a/src/analyze/gui/util.cpp b/src/analyze/gui/util.cpp index 48c260d..3d9498c 100644 --- a/src/analyze/gui/util.cpp +++ b/src/analyze/gui/util.cpp @@ -20,6 +20,10 @@ #include +#ifndef NO_K_LIB +#include +#endif + QString Util::formatTime(qint64 ms) { if (ms > 60000) { @@ -30,3 +34,53 @@ QString Util::formatTime(qint64 ms) 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 +} diff --git a/src/analyze/gui/util.h b/src/analyze/gui/util.h index f19e227..ad6165c 100644 --- a/src/analyze/gui/util.h +++ b/src/analyze/gui/util.h @@ -27,6 +27,8 @@ namespace Util { QString formatTime(qint64 ms); +QString formatByteSize(int64_t size, int precision = 1); + } #endif // UTIL_H diff --git a/src/heaptrack_gui.pro b/src/heaptrack_gui.pro new file mode 100644 index 0000000..a383fe7 --- /dev/null +++ b/src/heaptrack_gui.pro @@ -0,0 +1,102 @@ +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 +} diff --git a/src/heaptrack_print.pro b/src/heaptrack_print.pro new file mode 100644 index 0000000..b4ead91 --- /dev/null +++ b/src/heaptrack_print.pro @@ -0,0 +1,16 @@ +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 diff --git a/src/util/config.h b/src/util/config.h new file mode 100644 index 0000000..aef8c2d --- /dev/null +++ b/src/util/config.h @@ -0,0 +1,36 @@ +/* + * Copyright 2014-2017 Milian Wolff + * + * 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 diff --git a/src/util/libunwind_config.h b/src/util/libunwind_config.h new file mode 100644 index 0000000..bcc01c1 --- /dev/null +++ b/src/util/libunwind_config.h @@ -0,0 +1,27 @@ +/* + * Copyright 2017 Milian Wolff + * + * 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 +