Show temporary allocations in heaptrack_gui.
authorMilian Wolff <mail@milianw.de>
Thu, 10 Dec 2015 14:56:48 +0000 (15:56 +0100)
committerMilian Wolff <mail@milianw.de>
Thu, 10 Dec 2015 14:57:38 +0000 (15:57 +0100)
gui/chartmodel.cpp
gui/chartmodel.h
gui/mainwindow.cpp
gui/mainwindow.ui
gui/parser.cpp
gui/parser.h
gui/treemodel.cpp
gui/treemodel.h

index 0925995..f40ed88 100644 (file)
@@ -70,6 +70,8 @@ QVariant ChartModel::headerData(int section, Qt::Orientation orientation, int ro
                 return i18n("Memory Allocations");
             case Consumed:
                 return i18n("Memory Consumed");
+            case Temporary:
+                return i18n("Temporary Allocations");
             }
         }
     }
@@ -120,7 +122,7 @@ QVariant ChartModel::data(const QModelIndex& index, int role) const
     if (role == Qt::ToolTipRole) {
         const QString time = QString::number(double(data.timeStamp) / 1000, 'g', 3) + QLatin1Char('s');
         const auto label = m_data.labels.value(column);
-        if (m_type == Allocations) {
+        if (m_type == Allocations || m_type == Temporary) {
             return i18n("%1: %2 at %3", label, cost, time);
         } else {
             KFormat format;
index 77404da..2e299d6 100644 (file)
@@ -54,6 +54,7 @@ public:
         Consumed,
         Allocations,
         Allocated,
+        Temporary,
     };
     explicit ChartModel(Type type, QObject* parent = nullptr);
     virtual ~ChartModel();
index 5860abb..3b8b2ef 100644 (file)
@@ -55,6 +55,8 @@ MainWindow::MainWindow(QWidget* parent)
     m_ui->allocationsTab->setModel(allocationsModel);
     auto allocatedModel = new ChartModel(ChartModel::Allocated, this);
     m_ui->allocatedTab->setModel(allocatedModel);
+    auto temporaryModel = new ChartModel(ChartModel::Temporary, this);
+    m_ui->temporaryTab->setModel(temporaryModel);
 
     connect(m_parser, &Parser::bottomUpDataAvailable,
             m_bottomUpModel, &TreeModel::resetData);
@@ -66,6 +68,8 @@ MainWindow::MainWindow(QWidget* parent)
             allocatedModel, &ChartModel::resetData);
     connect(m_parser, &Parser::allocationsChartDataAvailable,
             allocationsModel, &ChartModel::resetData);
+    connect(m_parser, &Parser::temporaryChartDataAvailable,
+            temporaryModel, &ChartModel::resetData);
     connect(m_parser, &Parser::summaryAvailable,
             m_ui->summary, &QLabel::setText);
     connect(m_parser, &Parser::topDownDataAvailable,
index 8187984..cbbae1d 100644 (file)
             <string>Allocations</string>
            </attribute>
           </widget>
+          <widget class="ChartWidget" name="temporaryTab">
+           <attribute name="title">
+            <string>Temporary Allocations</string>
+           </attribute>
+          </widget>
           <widget class="ChartWidget" name="allocatedTab">
            <attribute name="title">
             <string>Allocated</string>
index 88b5b71..12fc978 100644 (file)
@@ -109,6 +109,7 @@ struct ChartMergeData
     quint64 consumed;
     quint64 allocations;
     quint64 allocated;
+    quint64 temporary;
     bool operator<(const IpIndex rhs) const
     {
         return ip < rhs;
@@ -133,14 +134,17 @@ struct ParserData final : public AccumulatedTraceData
         consumedChartData.rows.reserve(MAX_CHART_DATAPOINTS);
         allocatedChartData.rows.reserve(MAX_CHART_DATAPOINTS);
         allocationsChartData.rows.reserve(MAX_CHART_DATAPOINTS);
+        temporaryChartData.rows.reserve(MAX_CHART_DATAPOINTS);
         // start off with null data at the origin
         consumedChartData.rows.push_back({});
         allocatedChartData.rows.push_back({});
         allocationsChartData.rows.push_back({});
+        temporaryChartData.rows.push_back({});
         // index 0 indicates the total row
         consumedChartData.labels[0] = i18n("total");
         allocatedChartData.labels[0] = i18n("total");
         allocationsChartData.labels[0] = i18n("total");
+        temporaryChartData.labels[0] = i18n("total");
 
         buildCharts = true;
         maxConsumedSinceLastTimeStamp = 0;
@@ -153,11 +157,12 @@ struct ParserData final : public AccumulatedTraceData
             const auto ip = findTrace(alloc.traceIndex).ipIndex;
             auto it = lower_bound(merged.begin(), merged.end(), ip);
             if (it == merged.end() || it->ip != ip) {
-                it = merged.insert(it, {ip, 0, 0, 0});
+                it = merged.insert(it, {ip, 0, 0, 0, 0});
             }
             it->consumed += alloc.peak; // we want to track the top peaks in the chart
             it->allocated += alloc.allocated;
             it->allocations += alloc.allocations;
+            it->temporary += alloc.temporary;
         }
         // find the top hot spots for the individual data members and remember their IP and store the label
         auto findTopChartEntries = [&] (quint64 ChartMergeData::* member, int LabelIds::* label, ChartData* data) {
@@ -178,6 +183,7 @@ struct ParserData final : public AccumulatedTraceData
         findTopChartEntries(&ChartMergeData::consumed, &LabelIds::consumed, &consumedChartData);
         findTopChartEntries(&ChartMergeData::allocated, &LabelIds::allocated, &allocatedChartData);
         findTopChartEntries(&ChartMergeData::allocations, &LabelIds::allocations, &allocationsChartData);
+        findTopChartEntries(&ChartMergeData::temporary, &LabelIds::temporary, &temporaryChartData);
     }
 
     void handleTimeStamp(uint64_t /*oldStamp*/, uint64_t newStamp)
@@ -204,6 +210,7 @@ struct ParserData final : public AccumulatedTraceData
         auto consumed = createRow(newStamp, nowConsumed);
         auto allocated = createRow(newStamp, totalAllocated);
         auto allocs = createRow(newStamp, totalAllocations);
+        auto temporary = createRow(newStamp, totalTemporary);
 
         // 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
@@ -223,11 +230,13 @@ struct ParserData final : public AccumulatedTraceData
             addDataToRow(alloc.leaked, labelIds.consumed, &consumed);
             addDataToRow(alloc.allocated, labelIds.allocated, &allocated);
             addDataToRow(alloc.allocations, labelIds.allocations, &allocs);
+            addDataToRow(alloc.temporary, labelIds.temporary, &temporary);
         }
         // add the rows for this time stamp
         consumedChartData.rows << consumed;
         allocatedChartData.rows << allocated;
         allocationsChartData.rows << allocs;
+        temporaryChartData.rows << temporary;
     }
 
     void handleAllocation()
@@ -245,6 +254,7 @@ struct ParserData final : public AccumulatedTraceData
     ChartData consumedChartData;
     ChartData allocationsChartData;
     ChartData allocatedChartData;
+    ChartData temporaryChartData;
     // here we store the indices into ChartRows::cost for those IpIndices that
     // are within the top hotspots. This way, we can do one hash lookup in the
     // handleTimeStamp function instead of three when we'd store this data
@@ -254,6 +264,7 @@ struct ParserData final : public AccumulatedTraceData
         int consumed = -1;
         int allocations = -1;
         int allocated = -1;
+        int temporary = -1;
     };
     QHash<IpIndex, LabelIds> labelIds;
     uint64_t maxConsumedSinceLastTimeStamp = 0;
@@ -278,6 +289,9 @@ QString generateSummary(const ParserData& data)
                    format.formatByteSize(data.totalAllocated, 2), format.formatByteSize(data.totalAllocated / totalTimeS)) << "<br/>"
            << i18n("<strong>calls to allocation functions</strong>: %1 (%2/s)",
                    data.totalAllocations, quint64(data.totalAllocations / totalTimeS)) << "<br/>"
+           << i18n("<strong>temporary allocations</strong>: %1 (%2%, %3/s)",
+                   data.totalTemporary, round(float(data.totalTemporary) * 100.f * 100.f / data.totalAllocations) / 100.f,
+                   quint64(data.totalTemporary / totalTimeS)) << "<br/>"
            << i18n("<strong>peak heap memory consumption</strong>: %1", format.formatByteSize(data.peak)) << "<br/>"
            << i18n("<strong>total memory leaked</strong>: %1", format.formatByteSize(data.leaked)) << "<br/>";
     stream << "</qt>";
@@ -307,10 +321,11 @@ QVector<RowData> mergeAllocations(const ParserData& data)
             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;
             } else {
-                it = rows->insert(it, {allocation.allocations, allocation.peak, allocation.leaked, allocation.allocated,
+                it = rows->insert(it, {allocation.allocations, allocation.peak, allocation.leaked, allocation.allocated, allocation.temporary,
                                         location, nullptr, {}});
             }
             if (data.isStopIndex(ip.functionIndex)) {
@@ -346,7 +361,7 @@ 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, node->location, nullptr, {}};
+                    *stack << RowData{0, 0, 0, 0, 0, node->location, nullptr, {}};
                     data = &stack->back();
                 }
                 // always use the leaf node's cost and propagate that one up the chain
@@ -355,6 +370,7 @@ void buildTopDown(const QVector<RowData>& bottomUpData, QVector<RowData>* topDow
                 data->peak += row.peak;
                 data->leaked += row.leaked;
                 data->allocated += row.allocated;
+                data->temporary += row.temporary;
                 stack = &data->children;
                 node = node->parent;
             }
@@ -410,6 +426,7 @@ void Parser::parse(const QString& path)
             emit consumedChartDataAvailable(data->consumedChartData);
             emit allocationsChartDataAvailable(data->allocationsChartData);
             emit allocatedChartDataAvailable(data->allocatedChartData);
+            emit temporaryChartDataAvailable(data->temporaryChartData);
         });
 
         auto sequential = new Sequence;
index 7a57772..88438cd 100644 (file)
@@ -42,6 +42,7 @@ signals:
     void consumedChartDataAvailable(const ChartData& data);
     void allocationsChartDataAvailable(const ChartData& data);
     void allocatedChartDataAvailable(const ChartData& data);
+    void temporaryChartDataAvailable(const ChartData& data);
     void finished();
 };
 
index b397784..c0d73e2 100644 (file)
@@ -25,6 +25,8 @@
 #include <KFormat>
 #include <KLocalizedString>
 
+#include <cmath>
+
 namespace {
 
 int indexOf(const RowData* row, const TreeData& siblings)
@@ -69,6 +71,8 @@ QVariant TreeModel::headerData(int section, Qt::Orientation orientation, int rol
                 return i18n("Module");
             case AllocationsColumn:
                 return i18n("Allocations [-]");
+            case TemporaryColumn:
+                return i18n("Temporary Allocations [-]");
             case PeakColumn:
                 return i18n("Peak [B]");
             case LeakedColumn:
@@ -95,6 +99,8 @@ QVariant TreeModel::headerData(int section, Qt::Orientation orientation, int rol
                 return i18n("<qt>The module, i.e. executable or shared library, from which an allocation function was called.</qt>");
             case AllocationsColumn:
                 return i18n("<qt>The number of times an allocation function was called from this location.</qt>");
+            case TemporaryColumn:
+                return i18n("<qt>The number of temporary allocations. These allocations are directly followed by a free without any other allocations in-between.</qt>");
             case PeakColumn:
                 return i18n("<qt>The maximum heap memory in bytes consumed from allocations originating at this location. "
                             "This takes deallocations into account.</qt>");
@@ -124,6 +130,8 @@ QVariant TreeModel::data(const QModelIndex& index, int role) const
             return row->allocated;
         case AllocationsColumn:
             return row->allocations;
+        case TemporaryColumn:
+            return row->temporary;
         case PeakColumn:
             return row->peak;
         case LeakedColumn:
@@ -156,8 +164,9 @@ QVariant TreeModel::data(const QModelIndex& index, int role) const
                         row->location->function, row->location->file, row->location->line, row->location->module);
         stream << '\n';
         KFormat format;
-        stream << i18n("allocated %1 over %2 calls, peak at %3, leaked %4",
-                       format.formatByteSize(row->allocated), row->allocations,
+        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));
         stream << '\n';
         if (!row->children.isEmpty()) {
index 00de25f..b8b08e1 100644 (file)
@@ -70,6 +70,7 @@ struct RowData
     quint64 peak;
     quint64 leaked;
     quint64 allocated;
+    quint64 temporary;
     std::shared_ptr<LocationData> location;
     const RowData* parent;
     QVector<RowData> children;
@@ -92,6 +93,7 @@ public:
 
     enum Columns {
         AllocationsColumn,
+        TemporaryColumn,
         PeakColumn,
         LeakedColumn,
         AllocatedColumn,