NO_K_LIB build mode (without KDE libraries) implemented
authorAlexey Chernobaev <achernobaev@dev.rtsoft.ru>
Thu, 15 Feb 2018 17:58:05 +0000 (20:58 +0300)
committerAlexey Chernobaev <achernobaev@dev.rtsoft.ru>
Thu, 15 Feb 2018 17:58:05 +0000 (20:58 +0300)
28 files changed:
src/analyze/gui/callercalleemodel.cpp
src/analyze/gui/callercalleemodel.h
src/analyze/gui/flamegraph.cpp
src/analyze/gui/gui.cpp
src/analyze/gui/gui_config.h [new file with mode: 0644]
src/analyze/gui/locationdata.h
src/analyze/gui/mainwindow.cpp
src/analyze/gui/mainwindow.h
src/analyze/gui/mainwindow.ui
src/analyze/gui/noklib.h [new file with mode: 0644]
src/analyze/gui/objecttreemodel.cpp
src/analyze/gui/objecttreemodel.h
src/analyze/gui/objecttreeproxy.cpp
src/analyze/gui/objecttreeproxy.h
src/analyze/gui/parser.cpp
src/analyze/gui/parser.h
src/analyze/gui/stacksmodel.cpp
src/analyze/gui/topproxy.cpp
src/analyze/gui/treemodel.cpp
src/analyze/gui/treemodel.h
src/analyze/gui/treeproxy.cpp
src/analyze/gui/treeproxy.h
src/analyze/gui/util.cpp
src/analyze/gui/util.h
src/heaptrack_gui.pro [new file with mode: 0644]
src/heaptrack_print.pro [new file with mode: 0644]
src/util/config.h [new file with mode: 0644]
src/util/libunwind_config.h [new file with mode: 0644]

index 5ce63d9..ee17565 100644 (file)
 
 #include "callercalleemodel.h"
 
+#ifdef NO_K_LIB
+#include "noklib.h"
+#else
 #include <KLocalizedString>
+#endif
+
+#include "util.h"
 
 #include <QTextStream>
 
@@ -183,7 +189,7 @@ QVariant CallerCalleeModel::data(const QModelIndex& index, int role) const
             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);
@@ -193,7 +199,7 @@ QVariant CallerCalleeModel::data(const QModelIndex& index, int role) const
             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);
@@ -201,13 +207,13 @@ QVariant CallerCalleeModel::data(const QModelIndex& index, int role) const
             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);
@@ -217,7 +223,7 @@ QVariant CallerCalleeModel::data(const QModelIndex& index, int role) const
             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);
@@ -225,7 +231,7 @@ QVariant CallerCalleeModel::data(const QModelIndex& index, int role) const
             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;
@@ -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 << "</pre></qt>";
         return tooltip;
index b824873..da5e1ba 100644 (file)
 #include <QAbstractTableModel>
 #include <QVector>
 
+#ifdef NO_K_LIB
+#include "noklib.h"
+#else
 #include <KFormat>
+#endif
 
 #include "../allocationdata.h"
 #include "locationdata.h"
@@ -89,7 +93,9 @@ public:
 private:
     QVector<CallerCalleeData> m_rows;
     CallerCalleeData m_maxCost;
+#ifndef NO_K_LIB
     KFormat m_format;
+#endif
 };
 
 #endif // CALLERCALLEEMODEL_H
index b680b49..d250c61 100644 (file)
 #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
 {
@@ -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<RowData>& 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<RowData>& 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<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)
@@ -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<CostType>()) {
         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);
 }
index 25c7b63..1fe223d 100644 (file)
 #include <QApplication>
 #include <QCommandLineParser>
 
+#ifdef NO_K_LIB
+#include "noklib.h"
+#else
 #include <KAboutData>
 #include <KLocalizedString>
+#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 (file)
index 0000000..c2ef130
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * 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
index 2a96b99..f974f6b 100644 (file)
 
 #include <boost/functional/hash.hpp>
 
+#ifdef NO_K_LIB
+#include "noklib.h"
+#else
 #include <KLocalizedString>
+#endif
 
 struct LocationData
 {
index b1a489e..0367bd5 100644 (file)
 
 #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>
@@ -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("<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>";
         }
 
@@ -334,8 +347,8 @@ MainWindow::MainWindow(QWidget* parent)
                            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)
@@ -347,20 +360,20 @@ MainWindow::MainWindow(QWidget* parent)
                 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
@@ -368,22 +381,22 @@ MainWindow::MainWindow(QWidget* parent)
                 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>";
             }
         }
@@ -392,13 +405,13 @@ MainWindow::MainWindow(QWidget* parent)
             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>";
         }
 
@@ -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)
index b0d9b8d..a6900e2 100644 (file)
@@ -21,7 +21,9 @@
 
 #include <QMainWindow>
 
+#ifndef NO_K_LIB
 #include <KSharedConfig>
+#endif
 
 namespace Ui {
 class MainWindow;
@@ -52,7 +54,9 @@ private:
 
     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;
index 4f289c8..bcce059 100644 (file)
      <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>&lt;qt&gt;&lt;p&gt;This field specifies the primary heaptrack data file. These files are called &lt;tt&gt;heaptrack.$APP.$PID.gz&lt;/tt&gt;. You can produce such a file by profiling your application, e.g. via:&lt;/p&gt;
 &lt;pre&gt;&lt;code&gt;heaptrack &amp;lt;yourapplication&amp;gt; ...&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;Or, alternatively, you can attach to a running process via&lt;/p&gt;
 &lt;pre&gt;&lt;code&gt;heaptrack --pid $(pidof &amp;lt;yourapplication&amp;gt;)&lt;/code&gt;&lt;/pre&gt;&lt;/qt&gt;</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>&lt;qt&gt;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.&lt;/qt&gt;</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>
diff --git a/src/analyze/gui/noklib.h b/src/analyze/gui/noklib.h
new file mode 100644 (file)
index 0000000..4393041
--- /dev/null
@@ -0,0 +1,91 @@
+#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
index cea03d1..b9d6a41 100644 (file)
 #include <QDebug>
 #include <QTextStream>
 
+#ifdef NO_K_LIB
+#include "noklib.h"
+#else
 #include <KFormat>
 #include <KLocalizedString>
+#endif
+
+#include "util.h"
 
 #include <cmath>
 
@@ -122,12 +128,12 @@ QVariant ObjectTreeModel::data(const QModelIndex& index, int role) const
             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:
index 2202da5..f011f9f 100644 (file)
 #include <QAbstractItemModel>
 #include <QVector>
 
+#ifndef NO_K_LIB
 #include <KFormat>
+#endif
+
 #include <string.h>
 
 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
index 53e32e0..a638e94 100644 (file)
 #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;
index 843fa74..8bb4911 100644 (file)
 #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:
@@ -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;
index f73fcb3..82bcb48 100644 (file)
 
 #include "parser.h"
 
+#ifdef NO_K_LIB
+#include "noklib.h"
+#else
 #include <KLocalizedString>
 #include <ThreadWeaver/ThreadWeaver>
+#endif
 
 #include <QDebug>
 
@@ -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<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
 }
index 7503b55..2554626 100644 (file)
@@ -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
index 9e29e16..2c01fdd 100644 (file)
 #include "stacksmodel.h"
 #include "treemodel.h"
 
+#ifdef NO_K_LIB
+#include "noklib.h"
+#else
 #include <KLocalizedString>
+#endif
 
 #include <QDebug>
 
index ad6cbb6..909729d 100644 (file)
@@ -18,8 +18,6 @@
 
 #include "topproxy.h"
 
-#include <KLocalizedString>
-
 namespace {
 TreeModel::Columns toSource(TopProxy::Type type)
 {
index 09b6b6c..c29737b 100644 (file)
 #include <QDebug>
 #include <QTextStream>
 
+#ifdef NO_K_LIB
+#include "noklib.h"
+#else
 #include <KFormat>
 #include <KLocalizedString>
+#endif
+
+#include "util.h"
 
 #include <cmath>
 
@@ -165,7 +171,7 @@ QVariant TreeModel::data(const QModelIndex& index, int role) const
             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));
@@ -180,7 +186,7 @@ QVariant TreeModel::data(const QModelIndex& index, int role) const
             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) {
@@ -191,7 +197,7 @@ QVariant TreeModel::data(const QModelIndex& index, int role) const
             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;
@@ -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);
index a8903f7..f46da65 100644 (file)
@@ -22,7 +22,9 @@
 #include <QAbstractItemModel>
 #include <QVector>
 
+#ifndef NO_K_LIB
 #include <KFormat>
+#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
index 93d96b7..db4a33b 100644 (file)
 #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
index 46e2247..219b0d4 100644 (file)
 #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:
@@ -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;
index 48c260d..3d9498c 100644 (file)
 
 #include <QString>
 
+#ifndef NO_K_LIB
+#include <KFormat>
+#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
+}
index f19e227..ad6165c 100644 (file)
@@ -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 (file)
index 0000000..a383fe7
--- /dev/null
@@ -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 (file)
index 0000000..b4ead91
--- /dev/null
@@ -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 (file)
index 0000000..aef8c2d
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * 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
diff --git a/src/util/libunwind_config.h b/src/util/libunwind_config.h
new file mode 100644 (file)
index 0000000..bcc01c1
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * 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
+