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);
+ }
}
}
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())));
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
if (parent.isValid()) {
return 0;
} else {
- return m_data.size();
+ return m_data.rows.size();
}
}
#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
{
{
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)
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));
});
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;
}
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;
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);