Keep the chart data associated with a constant column.
authorMilian Wolff <mail@milianw.de>
Sat, 19 Sep 2015 06:47:21 +0000 (08:47 +0200)
committerMilian Wolff <mail@milianw.de>
Mon, 21 Sep 2015 02:18:06 +0000 (04:18 +0200)
This is neccessary to have the association with a color and
later a legend label make sense.

What's left to be done now is adding some sorting on top to
have the most costly column at the bottom of the model.

gui/chartmodel.cpp
gui/chartmodel.h
gui/parser.cpp

index 543dad6..5a2286a 100644 (file)
@@ -35,21 +35,26 @@ QColor colorForColumn(int column, int columnCount)
     return QColor::fromHsv((1. - double(column + 1) / columnCount) * 255, 255, 255);
 }
 
-QVector<ChartRow> columnValue(const ChartRows& data, int column)
+quint64 indexValue(const ChartRows& data, int column, int index)
 {
     if (column == ChartModel::LeakedColumn) {
-        return data.leaked;
+        return data.leaked.value(index);
     } else if (column == ChartModel::AllocationsColumn) {
-        return data.allocations;
+        return data.allocations.value(index);
     } else {
-        return data.allocated;
+        return data.allocated.value(index);
     }
 }
 
-ChartRow indexValue(const ChartRows& data, int idx, int column)
+QString indexLabel(const ChartData& data, int column, int index)
 {
-    const auto& values = columnValue(data, column);
-    return idx < values.size() ? values[idx] : ChartRow();
+    if (column == ChartModel::LeakedColumn) {
+        return data.leakedLabels.value(index);
+    } else if (column == ChartModel::AllocationsColumn) {
+        return data.allocationsLabels.value(index);
+    } else {
+        return data.allocatedLabels.value(index);
+    }
 }
 }
 
@@ -108,8 +113,6 @@ QVariant ChartModel::data(const QModelIndex& index, int role) const
         return QVariant::fromValue(attributes);
     }
 
-    const auto idx = index.column() / 4;
-    const auto column = index.column() % 4;
 
     if ( role == KChart::DatasetPenRole ) {
         return QVariant::fromValue(QPen(colorForColumn(index.column(), columnCount())));
@@ -121,25 +124,28 @@ QVariant ChartModel::data(const QModelIndex& index, int role) const
         return {};
     }
 
-    const auto& data = m_data.at(index.row());
+    const auto& data = m_data.rows.at(index.row());
+    const auto column = index.column() % NUM_COLUMNS;
+    const auto idx = index.column() / NUM_COLUMNS;
 
     if (column == TimeStampColumn) {
         return data.timeStamp;
     }
 
-    const auto& chartRow = indexValue(data, idx, column);
+    const auto cost = indexValue(data, column, idx);
 
     if ( role == Qt::ToolTipRole ) {
         // TODO: use correct label for column, format cost and time properly in a human readable way
-        return i18n("%1: %2 at %3", chartRow.function, chartRow.cost, data.timeStamp);
+        return i18n("%1: %2 at %3", indexLabel(m_data, column, idx), cost, data.timeStamp);
     }
 
-    return chartRow.cost;
+    return cost;
 }
 
 int ChartModel::columnCount(const QModelIndex& /*parent*/) const
 {
-    return m_data.isEmpty() ? 0 : NUM_COLUMNS * m_data.last().allocated.size();
+    // the three cost columns + timestamp
+    return m_data.allocatedLabels.size() + m_data.allocationsLabels.size() + m_data.leakedLabels.size() + 1;
 }
 
 int ChartModel::rowCount(const QModelIndex& parent) const
@@ -147,7 +153,7 @@ int ChartModel::rowCount(const QModelIndex& parent) const
     if (parent.isValid()) {
         return 0;
     } else {
-        return m_data.size();
+        return m_data.rows.size();
     }
 }
 
index b0f992e..0aa05ca 100644 (file)
 #include <QAbstractTableModel>
 #include <QVector>
 
-struct ChartRow
-{
-    QString function;
-    quint64 cost;
-};
-Q_DECLARE_TYPEINFO(ChartRow, Q_MOVABLE_TYPE);
-
 struct ChartRows
 {
-    quint64 timeStamp;
-    QVector<ChartRow> leaked;
-    QVector<ChartRow> allocations;
-    QVector<ChartRow> allocated;
+    quint64 timeStamp = 0;
+    QHash<int, quint64> leaked;
+    QHash<int, quint64> allocations;
+    QHash<int, quint64> allocated;
 };
 Q_DECLARE_TYPEINFO(ChartRows, Q_MOVABLE_TYPE);
 
-using ChartData = QVector<ChartRows>;
+struct ChartData
+{
+    QVector<ChartRows> rows;
+    QHash<int, QString> leakedLabels;
+    QHash<int, QString> allocationsLabels;
+    QHash<int, QString> allocatedLabels;
+};
 Q_DECLARE_METATYPE(ChartData)
+Q_DECLARE_TYPEINFO(ChartData, Q_MOVABLE_TYPE);
 
 class ChartModel : public QAbstractTableModel
 {
index d448370..a06b876 100644 (file)
@@ -105,7 +105,11 @@ struct ParserData final : public AccumulatedTraceData
 {
     ParserData()
     {
-        chartData.push_back({0, {{i18n("total"), 0}}, {{i18n("total"), 0}}, {{i18n("total"), 0}}});
+        chartData.rows.push_back({});
+        // index 0 indicates the total row
+        chartLeakedLabelIds.insert(i18n("total"), 0);
+        chartAllocatedLabelIds.insert(i18n("total"), 0);
+        chartAllocationsLabelIds.insert(i18n("total"), 0);
     }
 
     void handleTimeStamp(uint64_t /*newStamp*/, uint64_t oldStamp)
@@ -114,10 +118,12 @@ struct ParserData final : public AccumulatedTraceData
         maxLeakedSinceLastTimeStamp = max(maxLeakedSinceLastTimeStamp, leaked);
         ChartRows data;
         data.timeStamp = oldStamp;
-        data.leaked.push_back({i18n("total"), maxLeakedSinceLastTimeStamp});
-        data.allocations.push_back({i18n("total"), totalAllocations});
-        data.allocated.push_back({i18n("total"), totalAllocated});
+        // total data
+        data.leaked.insert(0, maxLeakedSinceLastTimeStamp);
+        data.allocations.insert(0, totalAllocations);
+        data.allocated.insert(0, totalAllocated);
 
+        // merge data for top 10 functions in this timestamp
         vector<ChartMergeData> mergedData;
         for (const auto& allocation : allocations) {
             const auto function = stringCache.func(findIp(findTrace(allocation.traceIndex).ipIndex));
@@ -137,29 +143,44 @@ struct ParserData final : public AccumulatedTraceData
         });
         for (size_t i = 0; i < min(size_t(10), mergedData.size()); ++i) {
             const auto& alloc = mergedData[i];
-            if (!alloc.leaked)
+            if (!alloc.leaked) {
                 break;
-            data.leaked.push_back({alloc.function, alloc.leaked});
+            }
+            auto& id = chartLeakedLabelIds[alloc.function];
+            if (!id) {
+                id = chartLeakedLabelIds.size() - 1;
+            }
+            data.leaked.insert(id, alloc.leaked);
         }
         sort(mergedData.begin(), mergedData.end(), [] (const ChartMergeData& left, const ChartMergeData& right) {
             return left.allocations > right.allocations;
         });
         for (size_t i = 0; i < min(size_t(10), mergedData.size()); ++i) {
             const auto& alloc = mergedData[i];
-            if (!alloc.allocations)
+            if (!alloc.allocations) {
                 break;
-            data.allocations.push_back({alloc.function, alloc.allocations});
+            }
+            auto& id = chartAllocationsLabelIds[alloc.function];
+            if (!id) {
+                id = chartAllocationsLabelIds.size() - 1;
+            }
+            data.allocations.insert(id, alloc.allocations);
         }
         sort(mergedData.begin(), mergedData.end(), [] (const ChartMergeData& left, const ChartMergeData& right) {
             return left.allocated > right.allocated;
         });
         for (size_t i = 0; i < min(size_t(10), mergedData.size()); ++i) {
             const auto& alloc = mergedData[i];
-            if (!alloc.allocated)
+            if (!alloc.allocated) {
                 break;
-            data.allocated.push_back({alloc.function, alloc.allocated});
+            }
+            auto& id = chartAllocatedLabelIds[alloc.function];
+            if (!id) {
+                id = chartAllocatedLabelIds.size() - 1;
+            }
+            data.allocated.insert(id, alloc.allocated);
         }
-        chartData.push_back(data);
+        chartData.rows.push_back(data);
         maxLeakedSinceLastTimeStamp = 0;
     }
 
@@ -175,8 +196,10 @@ struct ParserData final : public AccumulatedTraceData
 
     string debuggee;
 
-    // FIXME: keep order of entries constant, add zero elements where required
     ChartData chartData;
+    QHash<QString, int> chartLeakedLabelIds;
+    QHash<QString, int> chartAllocationsLabelIds;
+    QHash<QString, int> chartAllocatedLabelIds;
     uint64_t maxLeakedSinceLastTimeStamp = 0;
 
     StringCache stringCache;
@@ -307,6 +330,19 @@ void Parser::parse(const QString& path)
     stream() << make_job([=]() {
         ParserData data;
         data.read(path.toStdString());
+        // TODO: deduplicate
+        data.chartData.leakedLabels.reserve(data.chartLeakedLabelIds.size());
+        for (auto it = data.chartLeakedLabelIds.constBegin(); it != data.chartLeakedLabelIds.constEnd(); ++it) {
+            data.chartData.leakedLabels.insert(it.value(), it.key());
+        }
+        data.chartData.allocatedLabels.reserve(data.chartAllocatedLabelIds.size());
+        for (auto it = data.chartAllocatedLabelIds.constBegin(); it != data.chartAllocatedLabelIds.constEnd(); ++it) {
+            data.chartData.allocatedLabels.insert(it.value(), it.key());
+        }
+        data.chartData.allocationsLabels.reserve(data.chartAllocationsLabelIds.size());
+        for (auto it = data.chartAllocationsLabelIds.constBegin(); it != data.chartAllocationsLabelIds.constEnd(); ++it) {
+            data.chartData.allocationsLabels.insert(it.value(), it.key());
+        }
         emit summaryAvailable(generateSummary(data));
         const auto mergedAllocations = mergeAllocations(data);
         emit bottomUpDataAvailable(mergedAllocations);