peak = 0;
leaked = 0;
allocations.clear();
- sizeHistogram.clear();
uint64_t lastAllocationPtr = 0;
uint fileVersion = 0;
}
} else if (reader.mode() == '+') {
BigAllocationInfo info;
+ AllocationIndex allocationIndex;
if (fileVersion >= 0x010000) {
- uint32_t allocationInfoIndex = 0;
- if (!(reader >> allocationInfoIndex)) {
+ if (!(reader >> allocationIndex.index)) {
cerr << "failed to parse line: " << reader.line() << endl;
continue;
}
- info = allocationInfos[allocationInfoIndex];
+ info = allocationInfos[allocationIndex.index];
} else {
+ // TODO: allocationInfoIndex
uint64_t ptr = 0;
if (!(reader >> info.size) || !(reader >> info.traceIndex) || !(reader >> ptr)) {
cerr << "failed to parse line: " << reader.line() << endl;
peak = leaked;
}
- if (printHistogram) {
- ++sizeHistogram[info.size];
- }
-
- handleAllocation();
+ handleAllocation(info, allocationIndex);
} else if (reader.mode() == '-') {
BigAllocationInfo info;
bool temporary = false;
virtual ~AccumulatedTraceData() = default;
virtual void handleTimeStamp(uint64_t oldStamp, uint64_t newStamp) = 0;
- virtual void handleAllocation() = 0;
+ virtual void handleAllocation(const BigAllocationInfo& info, const AllocationIndex index) = 0;
virtual void handleDebuggee(const char* command) = 0;
const std::string& stringify(const StringIndex stringId) const;
bool read(std::istream& in);
bool shortenTemplates = false;
- bool printHistogram = false;
bool fromAttached = false;
std::vector<Allocation> allocations;
- std::map<uint64_t, uint64_t> sizeHistogram;
uint64_t totalAllocated = 0;
uint64_t totalAllocations = 0;
uint64_t totalTemporary = 0;
chartwidget.cpp
chartmodel.cpp
chartproxy.cpp
+ histogramwidget.cpp
+ histogrammodel.cpp
modeltest.cpp
parser.cpp
flamegraph.cpp
const QString customizedLabel(const QString& label) const override
{
- // TODO: change distance between labels to 1024 and simply use prettyCost() here
KFormat format(QLocale::system());
return format.formatByteSize(label.toDouble(), 1, KFormat::MetricBinaryDialect);
}
--- /dev/null
+/*
+ * Copyright 2015 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.
+ */
+
+#include "histogrammodel.h"
+
+#include <KChartGlobal>
+
+#include <KFormat>
+#include <KLocalizedString>
+
+#include <QColor>
+#include <QBrush>
+#include <QPen>
+
+#include <limits>
+
+namespace {
+QColor colorForColumn(int column, int columnCount)
+{
+ return QColor::fromHsv((double(column) / columnCount) * 255, 255, 255);
+}
+}
+
+HistogramModel::HistogramModel(QObject* parent)
+ : QAbstractTableModel(parent)
+{
+ qRegisterMetaType<HistogramData>("HistogramData");
+}
+
+HistogramModel::~HistogramModel() = default;
+
+QVariant HistogramModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ if (orientation == Qt::Vertical && role == Qt::DisplayRole && section >= 0 && section < m_data.size()) {
+ return m_data.at(section).sizeLabel;
+ }
+ return {};
+}
+
+QVariant HistogramModel::data(const QModelIndex& index, int role) const
+{
+ if (!hasIndex(index.row(), index.column(), index.parent())) {
+ return {};
+ }
+ if ( role == KChart::DatasetBrushRole ) {
+ return QVariant::fromValue(QBrush(colorForColumn(index.column(), columnCount())));
+ } else if ( role == KChart::DatasetPenRole ) {
+ return QVariant::fromValue(QPen(Qt::black));
+ }
+
+ if (role != Qt::DisplayRole && role != Qt::ToolTipRole) {
+ return {};
+ }
+
+ const auto& row = m_data.at(index.row());
+ const auto& column = row.columns[index.column()];
+ if (role == Qt::ToolTipRole) {
+ if (index.column() == 0) {
+ return i18n("%1 allocations in total", column.allocations);
+ }
+ if (!column.location->file.isEmpty()) {
+ return i18n("%1 allocations from %2 at %3:%4 in %5", column.allocations,
+ column.location->function, column.location->file, column.location->line,
+ column.location->module);
+ }
+ return i18n("%1 allocations from %2 in %3", column.allocations,
+ column.location->function, column.location->module);
+ }
+ return column.allocations;
+}
+
+int HistogramModel::columnCount(const QModelIndex& parent) const
+{
+ if (parent.isValid()) {
+ return 0;
+ }
+ return HistogramRow::NUM_COLUMNS;
+}
+
+int HistogramModel::rowCount(const QModelIndex& parent) const
+{
+ if (parent.isValid()) {
+ return 0;
+ }
+ return m_data.size();
+}
+
+void HistogramModel::resetData(const HistogramData& data)
+{
+ beginResetModel();
+ m_data = data;
+ endResetModel();
+}
--- /dev/null
+/*
+ * Copyright 2015 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 HISTOGRAMMODEL_H
+#define HISTOGRAMMODEL_H
+
+#include <QAbstractTableModel>
+
+#include "treemodel.h"
+
+struct HistogramColumn
+{
+ quint64 allocations;
+ std::shared_ptr<LocationData> location;
+};
+Q_DECLARE_TYPEINFO(HistogramColumn, Q_MOVABLE_TYPE);
+
+struct HistogramRow
+{
+ HistogramRow()
+ {
+ columns.fill({0, {}});
+ }
+ enum {
+ NUM_COLUMNS = 10 + 1
+ };
+ QString sizeLabel;
+ quint64 size = 0;
+ std::array<HistogramColumn, NUM_COLUMNS> columns;
+};
+Q_DECLARE_TYPEINFO(HistogramRow, Q_MOVABLE_TYPE);
+Q_DECLARE_METATYPE(HistogramRow);
+
+using HistogramData = QVector<HistogramRow>;
+
+class HistogramModel : public QAbstractTableModel
+{
+ Q_OBJECT
+public:
+ explicit HistogramModel(QObject* parent = nullptr);
+ ~HistogramModel() override;
+
+ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
+ QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
+ int rowCount(const QModelIndex& parent = {}) const override;
+ int columnCount(const QModelIndex& parent = {}) const override;
+
+ void resetData(const HistogramData& data);
+
+private:
+ HistogramData m_data;
+};
+
+#endif // HISTOGRAMMODEL_H
--- /dev/null
+/*
+ * Copyright 2015 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.
+ */
+
+#include "histogramwidget.h"
+
+#include <QVBoxLayout>
+#include <QSortFilterProxyModel>
+
+#include <KChartChart>
+#include <KChartBarDiagram>
+
+#include <KChartGridAttributes>
+#include <KChartHeaderFooter>
+#include <KChartCartesianCoordinatePlane>
+#include <KChartLegend>
+#include <KChartDataValueAttributes>
+#include <KChartBackgroundAttributes>
+#include <KChartFrameAttributes.h>
+
+#include <KLocalizedString>
+#include <KColorScheme>
+#include <KFormat>
+
+#include "histogrammodel.h"
+
+using namespace KChart;
+
+namespace {
+class SizeAxis : public CartesianAxis
+{
+ Q_OBJECT
+public:
+ explicit SizeAxis(AbstractCartesianDiagram* diagram = nullptr)
+ : CartesianAxis(diagram)
+ {}
+
+ const QString customizedLabel(const QString& label) const override
+ {
+ KFormat format(QLocale::system());
+ return format.formatByteSize(label.toDouble(), 1, KFormat::MetricBinaryDialect);
+ }
+};
+
+class HistogramProxy : public QSortFilterProxyModel
+{
+ Q_OBJECT
+public:
+ explicit HistogramProxy(bool showTotal, QObject* parent = nullptr)
+ : QSortFilterProxyModel(parent)
+ , m_showTotal(showTotal)
+ {
+ }
+ virtual ~HistogramProxy() = default;
+
+protected:
+ bool filterAcceptsColumn(int sourceColumn, const QModelIndex& /*sourceParent*/) const override
+ {
+ if (m_showTotal) {
+ return sourceColumn == 0;
+ } else {
+ return sourceColumn != 0;
+ }
+ }
+
+private:
+ bool m_showTotal;
+};
+
+}
+
+HistogramWidget::HistogramWidget(QWidget* parent)
+ : QWidget(parent)
+ , m_chart(new KChart::Chart(this))
+ , m_total(new BarDiagram(this))
+ , m_detailed(new BarDiagram(this))
+{
+ auto layout = new QVBoxLayout(this);
+ layout->addWidget(m_chart);
+ setLayout(layout);
+
+ auto* coordinatePlane = dynamic_cast<CartesianCoordinatePlane*>(m_chart->coordinatePlane());
+ Q_ASSERT(coordinatePlane);
+
+ {
+ m_total->setAntiAliasing(true);
+
+ KColorScheme scheme(QPalette::Active, KColorScheme::Window);
+ QPen foreground(scheme.foreground().color());
+ auto bottomAxis = new CartesianAxis(m_total);
+ auto axisTextAttributes = bottomAxis->textAttributes();
+ axisTextAttributes.setPen(foreground);
+ bottomAxis->setTextAttributes(axisTextAttributes);
+ auto axisTitleTextAttributes = bottomAxis->titleTextAttributes();
+ axisTitleTextAttributes.setPen(foreground);
+ bottomAxis->setTitleTextAttributes(axisTitleTextAttributes);
+ bottomAxis->setPosition(KChart::CartesianAxis::Bottom);
+ bottomAxis->setTitleText(i18n("Requested Allocation Size"));
+ m_total->addAxis(bottomAxis);
+
+ auto* rightAxis = new CartesianAxis(m_total);
+ rightAxis->setTextAttributes(axisTextAttributes);
+ rightAxis->setTitleTextAttributes(axisTitleTextAttributes);
+ rightAxis->setTitleText(i18n("Number of Allocations"));
+ rightAxis->setPosition(CartesianAxis::Right);
+ m_total->addAxis(rightAxis);
+
+ coordinatePlane->addDiagram(m_total);
+
+ m_total->setType(BarDiagram::Normal);
+ }
+
+ {
+ m_detailed->setAntiAliasing(true);
+
+ coordinatePlane->addDiagram(m_detailed);
+
+ m_detailed->setType(BarDiagram::Stacked);
+ }
+}
+
+HistogramWidget::~HistogramWidget() = default;
+
+void HistogramWidget::setModel(QAbstractItemModel* model)
+{
+ {
+ auto proxy = new HistogramProxy(true, this);
+ proxy->setSourceModel(model);
+ m_total->setModel(proxy);
+ }
+ {
+ auto proxy = new HistogramProxy(false, this);
+ proxy->setSourceModel(model);
+ m_detailed->setModel(proxy);
+ }
+}
+
+#include "histogramwidget.moc"
--- /dev/null
+/*
+ * Copyright 2015 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 HISTOGRAMWIDGET_H
+#define HISTOGRAMWIDGET_H
+
+#include <QWidget>
+
+namespace KChart {
+class Chart;
+class BarDiagram;
+}
+
+class QAbstractItemModel;
+
+class HistogramWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ explicit HistogramWidget(QWidget* parent = nullptr);
+ virtual ~HistogramWidget();
+
+ void setModel(QAbstractItemModel* model);
+
+private:
+ KChart::Chart* m_chart;
+ KChart::BarDiagram* m_total;
+ KChart::BarDiagram* m_detailed;
+};
+
+#endif // HISTOGRAMWIDGET_H
+
#include "parser.h"
#include "chartmodel.h"
#include "chartproxy.h"
+#include "histogrammodel.h"
using namespace std;
m_ui->allocatedTab->setModel(allocatedModel);
auto temporaryModel = new ChartModel(ChartModel::Temporary, this);
m_ui->temporaryTab->setModel(temporaryModel);
+ auto sizeHistogramModel = new HistogramModel(this);
+ m_ui->sizesTab->setModel(sizeHistogramModel);
connect(m_parser, &Parser::bottomUpDataAvailable,
m_bottomUpModel, &TreeModel::resetData);
allocationsModel, &ChartModel::resetData);
connect(m_parser, &Parser::temporaryChartDataAvailable,
temporaryModel, &ChartModel::resetData);
+ connect(m_parser, &Parser::sizeHistogramDataAvailable,
+ sizeHistogramModel, &HistogramModel::resetData);
connect(m_parser, &Parser::summaryAvailable,
m_ui->summary, &QLabel::setText);
connect(m_parser, &Parser::topDownDataAvailable,
<string>Allocated</string>
</attribute>
</widget>
+ <widget class="HistogramWidget" name="sizesTab">
+ <attribute name="title">
+ <string>Sizes</string>
+ </attribute>
+ </widget>
<widget class="FlameGraph" name="flameGraphTab">
<attribute name="title">
<string>Flame Graph</string>
<header>flamegraph.h</header>
<container>1</container>
</customwidget>
+ <customwidget>
+ <class>HistogramWidget</class>
+ <extends>QWidget</extends>
+ <header>histogramwidget.h</header>
+ <container>1</container>
+ </customwidget>
</customwidgets>
<resources/>
<connections/>
#include "../accumulatedtracedata.h"
#include <vector>
+#include <tuple>
using namespace std;
temporaryChartData.rows << temporary;
}
- void handleAllocation()
+ void handleAllocation(const BigAllocationInfo& info, const AllocationIndex index)
{
maxConsumedSinceLastTimeStamp = max(maxConsumedSinceLastTimeStamp, leaked);
+
+ if (index.index == allocationInfoCounter.size()) {
+ allocationInfoCounter.push_back({info, 1});
+ } else {
+ ++allocationInfoCounter[index.index].allocations;
+ }
}
void handleDebuggee(const char* command)
string debuggee;
+ struct CountedAllocationInfo
+ {
+ BigAllocationInfo info;
+ uint64_t allocations;
+ bool operator<(const CountedAllocationInfo& rhs) const
+ {
+ return make_tuple(info.size, allocations) < make_tuple(rhs.info.size, rhs.allocations);
+ }
+ };
+ vector<CountedAllocationInfo> allocationInfoCounter;
+
ChartData consumedChartData;
ChartData allocationsChartData;
ChartData allocatedChartData;
return topRows;
}
+struct MergedHistogramColumnData
+{
+ std::shared_ptr<LocationData> location;
+ uint64_t allocations;
+ bool operator<(const std::shared_ptr<LocationData>& rhs) const
+ {
+ return location < rhs;
+ }
+};
+
+HistogramData buildSizeHistogram(ParserData& data)
+{
+ HistogramData ret;
+ if (data.allocationInfoCounter.empty()) {
+ return ret;
+ }
+ sort(data.allocationInfoCounter.begin(), data.allocationInfoCounter.end());
+ const auto totalLabel = i18n("total");
+ HistogramRow row;
+ const pair<uint64_t, QString> buckets[] = {
+ {8, i18n("0B to 8B")},
+ {16, i18n("9B to 16B")},
+ {32, i18n("17B to 32B")},
+ {64, i18n("33B to 64B")},
+ {128, i18n("65B to 128B")},
+ {256, i18n("129B to 256B")},
+ {512, i18n("257B to 512B")},
+ {1024, i18n("512B to 1KB")},
+ {numeric_limits<uint64_t>::max(), i18n("more than 1KB")}
+ };
+ uint bucketIndex = 0;
+ row.size = buckets[bucketIndex].first;
+ row.sizeLabel = buckets[bucketIndex].second;
+ vector<MergedHistogramColumnData> columnData;
+ columnData.reserve(128);
+ auto insertColumns = [&] () {
+ sort(columnData.begin(), columnData.end(), [] (const MergedHistogramColumnData& lhs, const MergedHistogramColumnData& rhs) {
+ return lhs.allocations > rhs.allocations;
+ });
+ // -1 to account for total row
+ for (size_t i = 0; i < size_t(HistogramRow::NUM_COLUMNS - 1); ++i) {
+ const auto& column = columnData[i];
+ row.columns[i + 1] = {column.allocations, column.location};
+ }
+ };
+ for (const auto& info : data.allocationInfoCounter) {
+ if (info.info.size > row.size) {
+ insertColumns();
+ columnData.clear();
+ ret << row;
+ ++bucketIndex;
+ row.size = buckets[bucketIndex].first;
+ row.sizeLabel = buckets[bucketIndex].second;
+ row.columns[0] = {info.allocations, {}};
+ } else {
+ row.columns[0].allocations += info.allocations;
+ }
+ const auto ipIndex = data.findTrace(info.info.traceIndex).ipIndex;
+ const auto ip = data.findIp(ipIndex);
+ const auto location = data.stringCache.location(ip);
+ auto it = lower_bound(columnData.begin(), columnData.end(), location);
+ if (it == columnData.end() || it->location != location) {
+ columnData.insert(it, {location, info.allocations});
+ } else {
+ it->allocations += info.allocations;
+ }
+ }
+ insertColumns();
+ ret << row;
+ return ret;
+}
+
}
Parser::Parser(QObject* parent)
// merge allocations before modifying the data again
const auto mergedAllocations = mergeAllocations(*data);
+ // also calculate the size histogram
+ const auto sizeHistogram = buildSizeHistogram(*data);
// now data can be modified again for the chart data evaluation
auto parallel = new Collection;
- *parallel << make_job([this, mergedAllocations]() {
+ *parallel << make_job([this, mergedAllocations, sizeHistogram]() {
emit bottomUpDataAvailable(mergedAllocations);
+ emit sizeHistogramDataAvailable(sizeHistogram);
const auto topDownData = toTopDownData(mergedAllocations);
emit topDownDataAvailable(topDownData);
}) << make_job([this, data, stdPath]() {
#include "treemodel.h"
#include "chartmodel.h"
+#include "histogrammodel.h"
class Parser : public QObject
{
void allocationsChartDataAvailable(const ChartData& data);
void allocatedChartDataAvailable(const ChartData& data);
void temporaryChartDataAvailable(const ChartData& data);
+ void sizeHistogramDataAvailable(const HistogramData& data);
void finished();
};
}
}
- void handleAllocation() override
+ void handleAllocation(const BigAllocationInfo& info, const AllocationIndex /*index*/) override
{
+ if (printHistogram) {
+ ++sizeHistogram[info.size];
+ }
+
if (leaked > lastMassifPeak && massifOut.is_open()) {
massifAllocations = allocations;
lastMassifPeak = leaked;
}
}
+ bool printHistogram = false;
bool mergeBacktraces = true;
vector<MergedAllocation> mergedAllocations;
+ std::map<uint64_t, uint64_t> sizeHistogram;
+
uint64_t massifSnapshotId = 0;
uint64_t lastMassifPeak = 0;
vector<Allocation> massifAllocations;