Reuse common AllocationData class, with operator+=, operator+.
authorMilian Wolff <mail@milianw.de>
Thu, 24 Mar 2016 17:08:07 +0000 (18:08 +0100)
committerMilian Wolff <mail@milianw.de>
Thu, 24 Mar 2016 17:22:33 +0000 (18:22 +0100)
Simplifies the code in some areas.

accumulatedtracedata.cpp
accumulatedtracedata.h
allocationdata.h [new file with mode: 0644]
gui/flamegraph.cpp
gui/mainwindow.cpp
gui/parser.cpp
gui/treemodel.cpp
gui/treemodel.h
heaptrack_print.cpp

index b150183..0c4bbc2 100644 (file)
@@ -155,12 +155,8 @@ bool AccumulatedTraceData::read(istream& in)
 
     const bool reparsing = totalTime != 0;
     m_maxAllocationTraceIndex.index = 0;
-    totalAllocated = 0;
-    totalAllocations = 0;
-    totalTemporary = 0;
-    peak = 0;
+    totalCost = {};
     peakTime = 0;
-    leaked = 0;
     systemInfo = {};
     peakRSS = 0;
     allocations.clear();
@@ -250,11 +246,11 @@ bool AccumulatedTraceData::read(istream& in)
                 allocation.peak = allocation.leaked;
             }
 
-            totalAllocated += info.size;
-            ++totalAllocations;
-            leaked += info.size;
-            if (leaked > peak) {
-                peak = leaked;
+            ++totalCost.allocations;
+            totalCost.allocated += info.size;
+            totalCost.leaked += info.size;
+            if (totalCost.leaked > totalCost.peak) {
+                totalCost.peak = totalCost.leaked;
                 peakTime = timeStamp;
             }
 
@@ -297,10 +293,10 @@ bool AccumulatedTraceData::read(istream& in)
             } else {
                 allocation.leaked -= info.size;
             }
-            leaked -= info.size;
+            totalCost.leaked -= info.size;
             if (temporary) {
                 ++allocation.temporary;
-                ++totalTemporary;
+                ++totalCost.temporary;
             }
         } else if (reader.mode() == 'a') {
             if (reparsing) {
@@ -332,8 +328,7 @@ bool AccumulatedTraceData::read(istream& in)
         } else if (reader.mode() == 'X') {
             handleDebuggee(reader.line().c_str() + 2);
         } else if (reader.mode() == 'A') {
-            leaked = 0;
-            peak = 0;
+            totalCost = {};
             fromAttached = true;
         } else if (reader.mode() == 'v') {
             reader >> fileVersion;
index 7f2b816..9f315f3 100644 (file)
@@ -31,6 +31,7 @@
 #include <boost/functional/hash.hpp>
 
 #include "indices.h"
+#include "allocationdata.h"
 
 struct InstructionPointer
 {
@@ -59,20 +60,6 @@ struct TraceNode
     TraceIndex parentIndex;
 };
 
-struct AllocationData
-{
-    // number of allocations
-    uint64_t allocations = 0;
-    // bytes allocated in total
-    uint64_t allocated = 0;
-    // amount of bytes leaked
-    uint64_t leaked = 0;
-    // largest amount of bytes allocated
-    uint64_t peak = 0;
-    // number of temporary allocations
-    uint64_t temporary = 0;
-};
-
 struct Allocation : public AllocationData
 {
     // backtrace entry point
@@ -124,11 +111,7 @@ struct AccumulatedTraceData
     bool fromAttached = false;
 
     std::vector<Allocation> allocations;
-    uint64_t totalAllocated = 0;
-    uint64_t totalAllocations = 0;
-    uint64_t totalTemporary = 0;
-    uint64_t peak = 0;
-    uint64_t leaked = 0;
+    AllocationData totalCost;
     uint64_t totalTime = 0;
     uint64_t peakTime = 0;
     uint64_t peakRSS = 0;
diff --git a/allocationdata.h b/allocationdata.h
new file mode 100644 (file)
index 0000000..4938af8
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2016 Milian Wolff <mail@milianw.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef ALLOCATIONDATA_H
+#define ALLOCATIONDATA_H
+
+#include <cstdint>
+
+struct AllocationData
+{
+    // number of allocations
+    uint64_t allocations = 0;
+    // number of temporary allocations
+    uint64_t temporary = 0;
+    // bytes allocated in total
+    uint64_t allocated = 0;
+    // amount of bytes leaked
+    uint64_t leaked = 0;
+    // largest amount of bytes allocated
+    uint64_t peak = 0;
+};
+
+inline AllocationData& operator+=(AllocationData& lhs, const AllocationData& rhs)
+{
+    lhs.allocations += rhs.allocations;
+    lhs.temporary += rhs.temporary;
+    lhs.peak += rhs.peak;
+    lhs.leaked += rhs.leaked;
+    lhs.allocated += rhs.allocated;
+    return lhs;
+}
+
+inline AllocationData operator+(AllocationData lhs, const AllocationData& rhs)
+{
+    return lhs += rhs;
+}
+
+#endif // ALLOCATIONDATA_H
index d1abc43..b8e62bf 100644 (file)
@@ -242,45 +242,45 @@ FrameGraphicsItem* findItemByFunction(const QList<QGraphicsItem*>& items, const
 /**
  * Convert the top-down graph into a tree of FrameGraphicsItem.
  */
-void toGraphicsItems(const QVector<RowData>& data, FrameGraphicsItem *parent, quint64 RowData::* member)
+void toGraphicsItems(const QVector<RowData>& data, FrameGraphicsItem *parent, uint64_t AllocationData::* member)
 {
     foreach (const auto& row, data) {
         auto item = findItemByFunction(parent->childItems(), row.location->function);
         if (!item) {
-            item = new FrameGraphicsItem(row.*member, row.location->function, parent);
+            item = new FrameGraphicsItem(row.cost.*member, row.location->function, parent);
             item->setPen(parent->pen());
             item->setBrush(brush());
         } else {
-            item->setCost(item->cost() + row.*member);
+            item->setCost(item->cost() + row.cost.*member);
         }
         toGraphicsItems(row.children, item, member);
     }
 }
 
-quint64 RowData::* memberForType(CostType type)
+uint64_t AllocationData::* memberForType(CostType type)
 {
     switch (type) {
     case Allocations:
-        return &RowData::allocations;
+        return &AllocationData::allocations;
     case Temporary:
-        return &RowData::temporary;
+        return &AllocationData::temporary;
     case Peak:
-        return &RowData::peak;
+        return &AllocationData::peak;
     case Leaked:
-        return &RowData::leaked;
+        return &AllocationData::leaked;
     case Allocated:
-        return &RowData::allocated;
+        return &AllocationData::allocated;
     }
     Q_UNREACHABLE();
 }
 
 FrameGraphicsItem* parseData(const QVector<RowData>& topDownData, CostType type)
 {
-    quint64 RowData::* member = memberForType(type);
+    uint64_t AllocationData::* member = memberForType(type);
 
     double totalCost = 0;
     foreach(const auto& frame, topDownData) {
-        totalCost += frame.*member;
+        totalCost += frame.cost.*member;
     }
 
     KColorScheme scheme(QPalette::Active);
index c6b5c52..e850faf 100644 (file)
@@ -170,20 +170,20 @@ MainWindow::MainWindow(QWidget* parent)
                     QTextStream stream(&textCenter);
                     stream << "<qt><dl>"
                            << i18n("<dt><b>calls to allocation functions</b>:</dt><dd>%1 (%2/s)</dd>",
-                                   data.allocations, quint64(data.allocations / totalTimeS))
+                                   data.cost.allocations, quint64(data.cost.allocations / totalTimeS))
                            << i18n("<dt><b>temporary allocations</b>:</dt><dd>%1 (%2%, %3/s)</dd>",
-                                   data.temporary, round(float(data.temporary) * 100.f * 100.f / data.allocations) / 100.f,
-                                   quint64(data.temporary / totalTimeS))
+                                   data.cost.temporary, round(float(data.cost.temporary) * 100.f * 100.f / data.cost.allocations) / 100.f,
+                                   quint64(data.cost.temporary / totalTimeS))
                            << i18n("<dt><b>bytes allocated in total</b> (ignoring deallocations):</dt><dd>%1 (%2/s)</dd>",
-                                   format.formatByteSize(data.allocated, 2), format.formatByteSize(data.allocated / totalTimeS))
+                                   format.formatByteSize(data.cost.allocated, 2), format.formatByteSize(data.cost.allocated / totalTimeS))
                            << "</dl></qt>";
                 }
                 {
                     QTextStream stream(&textRight);
                     stream << "<qt><dl>"
-                           << i18n("<dt><b>peak heap memory consumption</b>:</dt><dd>%1 after %2s</dd>", format.formatByteSize(data.peak), peakTimeS)
+                           << i18n("<dt><b>peak heap memory consumption</b>:</dt><dd>%1 after %2s</dd>", format.formatByteSize(data.cost.peak), peakTimeS)
                            << i18n("<dt><b>peak RSS</b> (including heaptrack overhead):</dt><dd>%1</dd>", format.formatByteSize(data.peakRSS))
-                           << i18n("<dt><b>total memory leaked</b>:</dt><dd>%1</dd>", format.formatByteSize(data.leaked))
+                           << i18n("<dt><b>total memory leaked</b>:</dt><dd>%1</dd>", format.formatByteSize(data.cost.leaked))
                            << "</dl></qt>";
                 }
 
index 4a1adc7..8c937a6 100644 (file)
@@ -190,7 +190,7 @@ struct ParserData final : public AccumulatedTraceData
         if (!buildCharts) {
             return;
         }
-        maxConsumedSinceLastTimeStamp = max(maxConsumedSinceLastTimeStamp, leaked);
+        maxConsumedSinceLastTimeStamp = max(maxConsumedSinceLastTimeStamp, totalCost.leaked);
         const uint64_t diffBetweenTimeStamps = totalTime / MAX_CHART_DATAPOINTS;
         if (newStamp != totalTime && newStamp - lastTimeStamp < diffBetweenTimeStamps) {
             return;
@@ -207,9 +207,9 @@ struct ParserData final : public AccumulatedTraceData
             return row;
         };
         auto consumed = createRow(newStamp, nowConsumed);
-        auto allocated = createRow(newStamp, totalAllocated);
-        auto allocs = createRow(newStamp, totalAllocations);
-        auto temporary = createRow(newStamp, totalTemporary);
+        auto allocated = createRow(newStamp, totalCost.allocated);
+        auto allocs = createRow(newStamp, totalCost.allocations);
+        auto temporary = createRow(newStamp, totalCost.temporary);
 
         // if the cost is non-zero and the ip corresponds to a hotspot function selected in the labels,
         // we add the cost to the rows column
@@ -240,7 +240,7 @@ struct ParserData final : public AccumulatedTraceData
 
     void handleAllocation(const AllocationInfo& info, const AllocationIndex index)
     {
-        maxConsumedSinceLastTimeStamp = max(maxConsumedSinceLastTimeStamp, leaked);
+        maxConsumedSinceLastTimeStamp = max(maxConsumedSinceLastTimeStamp, totalCost.leaked);
 
         if (index.index == allocationInfoCounter.size()) {
             allocationInfoCounter.push_back({info, 1});
@@ -313,14 +313,9 @@ QVector<RowData> mergeAllocations(const ParserData& data)
             auto location = data.stringCache.location(ip);
             auto it = lower_bound(rows->begin(), rows->end(), location);
             if (it != rows->end() && it->location == location) {
-                it->allocated += allocation.allocated;
-                it->allocations += allocation.allocations;
-                it->temporary += allocation.temporary;
-                it->leaked += allocation.leaked;
-                it->peak += allocation.peak;
+                it->cost += allocation;
             } else {
-                it = rows->insert(it, {allocation.allocations, allocation.peak, allocation.leaked, allocation.allocated, allocation.temporary,
-                                        location, nullptr, {}});
+                it = rows->insert(it, {allocation, location, nullptr, {}});
             }
             if (data.isStopIndex(ip.functionIndex)) {
                 break;
@@ -355,16 +350,12 @@ void buildTopDown(const QVector<RowData>& bottomUpData, QVector<RowData>* topDow
                 auto data = findByLocation(*node, stack);
                 if (!data) {
                     // create an empty top-down item for this bottom-up node
-                    *stack << RowData{0, 0, 0, 0, 0, node->location, nullptr, {}};
+                    *stack << RowData{{}, node->location, nullptr, {}};
                     data = &stack->back();
                 }
                 // always use the leaf node's cost and propagate that one up the chain
                 // otherwise we'd count the cost of some nodes multiple times
-                data->allocations += row.allocations;
-                data->peak += row.peak;
-                data->leaked += row.leaked;
-                data->allocated += row.allocated;
-                data->temporary += row.temporary;
+                data->cost += row.cost;
                 stack = &data->children;
                 node = node->parent;
             }
@@ -482,13 +473,9 @@ void Parser::parse(const QString& path)
 
         emit summaryAvailable({
             QString::fromStdString(data->debuggee),
+            data->totalCost,
             data->totalTime,
             data->peakTime,
-            data->peak,
-            data->leaked,
-            data->totalAllocations,
-            data->totalTemporary,
-            data->totalAllocated,
             data->peakRSS * data->systemInfo.pageSize,
             data->systemInfo.pages * data->systemInfo.pageSize
         });
index f0853bd..0d0a6d4 100644 (file)
@@ -150,25 +150,25 @@ QVariant TreeModel::data(const QModelIndex& index, int role) const
         switch (static_cast<Columns>(index.column())) {
         case AllocatedColumn:
             if (role == SortRole || role == MaxCostRole) {
-                return row->allocated;
+                return static_cast<quint64>(row->cost.allocated);
             } else {
-                return m_format.formatByteSize(row->allocated);
+                return m_format.formatByteSize(row->cost.allocated);
             }
         case AllocationsColumn:
-            return row->allocations;
+            return static_cast<quint64>(row->cost.allocations);
         case TemporaryColumn:
-            return row->temporary;
+            return static_cast<quint64>(row->cost.temporary);
         case PeakColumn:
             if (role == SortRole || role == MaxCostRole) {
-                return row->peak;
+                return static_cast<quint64>(row->cost.peak);
             } else {
-                return m_format.formatByteSize(row->peak);
+                return m_format.formatByteSize(row->cost.peak);
             }
         case LeakedColumn:
             if (role == SortRole || role == MaxCostRole) {
-                return row->leaked;
+                return static_cast<quint64>(row->cost.leaked);
             } else {
-                return m_format.formatByteSize(row->leaked);
+                return m_format.formatByteSize(row->cost.leaked);
             }
         case FunctionColumn:
             return row->location->function;
@@ -206,9 +206,9 @@ QVariant TreeModel::data(const QModelIndex& index, int role) const
         stream << '\n';
         KFormat format;
         stream << i18n("allocated %1 over %2 calls (%3 temporary, i.e. %4%), peak at %5, leaked %6",
-                       format.formatByteSize(row->allocated), row->allocations, row->temporary,
-                       round(float(row->temporary) * 100.f * 100.f / row->allocations) / 100.f,
-                       format.formatByteSize(row->peak), format.formatByteSize(row->leaked));
+                       format.formatByteSize(row->cost.allocated), row->cost.allocations, row->cost.temporary,
+                       round(float(row->cost.temporary) * 100.f * 100.f / row->cost.allocations) / 100.f,
+                       format.formatByteSize(row->cost.peak), format.formatByteSize(row->cost.leaked));
         stream << '\n';
         if (!row->children.isEmpty()) {
             auto child = row;
@@ -287,11 +287,7 @@ void TreeModel::resetData(const TreeData& data)
 void TreeModel::setSummary(const SummaryData& data)
 {
     beginResetModel();
-    m_maxCost.allocated = data.allocated;
-    m_maxCost.leaked = data.leaked;
-    m_maxCost.peak = data.peak;
-    m_maxCost.allocations = data.allocations;
-    m_maxCost.temporary = data.temporary;
+    m_maxCost.cost = data.cost;
     endResetModel();
 }
 
index f90873b..22e258f 100644 (file)
 
 #include <KFormat>
 
-#include <boost/functional/hash.hpp>
-
 #include <memory>
 
+#include "../allocationdata.h"
+
 struct SummaryData
 {
     QString debuggee;
+    AllocationData cost;
     uint64_t totalTime;
     uint64_t peakTime;
-    uint64_t peak;
-    uint64_t leaked;
-    uint64_t allocations;
-    uint64_t temporary;
-    uint64_t allocated;
     uint64_t peakRSS;
     uint64_t totalSystemMemory;
 };
@@ -83,11 +79,7 @@ inline bool operator<(const std::shared_ptr<LocationData>& lhs, const LocationDa
 
 struct RowData
 {
-    quint64 allocations;
-    quint64 peak;
-    quint64 leaked;
-    quint64 allocated;
-    quint64 temporary;
+    AllocationData cost;
     std::shared_ptr<LocationData> location;
     const RowData* parent;
     QVector<RowData> children;
index c559ae5..230812a 100644 (file)
@@ -323,7 +323,7 @@ struct Printer final : public AccumulatedTraceData
     void writeMassifSnapshot(size_t timeStamp, bool isLast)
     {
         if (!lastMassifPeak) {
-            lastMassifPeak = leaked;
+            lastMassifPeak = totalCost.leaked;
             massifAllocations = allocations;
         }
         massifOut
@@ -436,9 +436,9 @@ struct Printer final : public AccumulatedTraceData
             ++sizeHistogram[info.size];
         }
 
-        if (leaked > lastMassifPeak && massifOut.is_open()) {
+        if (totalCost.leaked > lastMassifPeak && massifOut.is_open()) {
             massifAllocations = allocations;
-            lastMassifPeak = leaked;
+            lastMassifPeak = totalCost.leaked;
         }
     }
 
@@ -661,15 +661,15 @@ int main(int argc, char** argv)
 
     const double totalTimeS = 0.001 * data.totalTime;
     cout << "total runtime: " << fixed << totalTimeS << "s.\n"
-         << "bytes allocated in total (ignoring deallocations): " << formatBytes(data.totalAllocated)
-            << " (" << formatBytes(data.totalAllocated / totalTimeS) << "/s)" << '\n'
-         << "calls to allocation functions: " << data.totalAllocations
-            << " (" << size_t(data.totalAllocations / totalTimeS) << "/s)\n"
-         << "temporary memory allocations: " << data.totalTemporary
-            << " (" << size_t(data.totalTemporary / totalTimeS) << "/s)\n"
-         << "peak heap memory consumption: " << formatBytes(data.peak) << '\n'
+         << "bytes allocated in total (ignoring deallocations): " << formatBytes(data.totalCost.allocated)
+            << " (" << formatBytes(data.totalCost.allocated / totalTimeS) << "/s)" << '\n'
+         << "calls to allocation functions: " << data.totalCost.allocations
+            << " (" << size_t(data.totalCost.allocations / totalTimeS) << "/s)\n"
+         << "temporary memory allocations: " << data.totalCost.temporary
+            << " (" << size_t(data.totalCost.temporary / totalTimeS) << "/s)\n"
+         << "peak heap memory consumption: " << formatBytes(data.totalCost.peak) << '\n'
          << "peak RSS (including heaptrack overhead): " << formatBytes(data.peakRSS * data.systemInfo.pageSize) << '\n'
-         << "total memory leaked: " << formatBytes(data.leaked) << '\n';
+         << "total memory leaked: " << formatBytes(data.totalCost.leaked) << '\n';
 
     if (!printHistogram.empty()) {
         ofstream histogram(printHistogram, ios_base::out);