flamegraph.cpp
stacksmodel.cpp
topproxy.cpp
+ callercalleemodel.cpp
${UIFILES}
)
--- /dev/null
+/*
+ * 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.
+ */
+
+#include "callercalleemodel.h"
+
+#include <KLocalizedString>
+
+#include <QTextStream>
+
+namespace{
+/// TODO: share code
+QString basename(const QString& path)
+{
+ int idx = path.lastIndexOf(QLatin1Char('/'));
+ return path.mid(idx + 1);
+}
+}
+
+CallerCalleeModel::CallerCalleeModel(QObject* parent)
+ : QAbstractTableModel(parent)
+{
+ qRegisterMetaType<CallerCalleeRows>();
+}
+
+CallerCalleeModel::~CallerCalleeModel() = default;
+
+QVariant CallerCalleeModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ if (orientation != Qt::Horizontal || section < 0 || section >= NUM_COLUMNS) {
+ return {};
+ }
+ if (role == Qt::InitialSortOrderRole) {
+ if (section == SelfAllocatedColumn || section == SelfAllocationsColumn
+ || section == SelfPeakColumn || section == SelfLeakedColumn
+ || section == SelfTemporaryColumn
+ || section == InclusiveAllocatedColumn || section == InclusiveAllocationsColumn
+ || section == InclusivePeakColumn || section == InclusiveLeakedColumn
+ || section == InclusiveTemporaryColumn)
+ {
+ return Qt::DescendingOrder;
+ }
+ }
+ if (role == Qt::DisplayRole) {
+ switch (static_cast<Columns>(section)) {
+ case FileColumn:
+ return i18n("File");
+ case LineColumn:
+ return i18n("Line");
+ case FunctionColumn:
+ return i18n("Function");
+ case ModuleColumn:
+ return i18n("Module");
+ case SelfAllocationsColumn:
+ return i18n("Allocations (Self)");
+ case SelfTemporaryColumn:
+ return i18n("Temporary (Self)");
+ case SelfPeakColumn:
+ return i18n("Peak (Self)");
+ case SelfLeakedColumn:
+ return i18n("Leaked (Self)");
+ case SelfAllocatedColumn:
+ return i18n("Allocated (Self)");
+ case InclusiveAllocationsColumn:
+ return i18n("Allocations (Incl.)");
+ case InclusiveTemporaryColumn:
+ return i18n("Temporary (Incl.)");
+ case InclusivePeakColumn:
+ return i18n("Peak (Incl.)");
+ case InclusiveLeakedColumn:
+ return i18n("Leaked (Incl.)");
+ case InclusiveAllocatedColumn:
+ return i18n("Allocated (Incl.)");
+ case LocationColumn:
+ return i18n("Location");
+ case NUM_COLUMNS:
+ break;
+ }
+ } else if (role == Qt::ToolTipRole) {
+ switch (static_cast<Columns>(section)) {
+ case FileColumn:
+ return i18n("<qt>The file where the allocation function was called from. "
+ "May be empty when debug information is missing.</qt>");
+ case LineColumn:
+ return i18n("<qt>The line number where the allocation function was called from. "
+ "May be empty when debug information is missing.</qt>");
+ case FunctionColumn:
+ return i18n("<qt>The parent function that called an allocation function. "
+ "May be unknown when debug information is missing.</qt>");
+ case ModuleColumn:
+ return i18n("<qt>The module, i.e. executable or shared library, from which an allocation function was called.</qt>");
+ case SelfAllocationsColumn:
+ return i18n("<qt>The number of times an allocation function was directly called from this location.</qt>");
+ case SelfTemporaryColumn:
+ return i18n("<qt>The number of direct temporary allocations. These allocations are directly followed by a free without any other allocations in-between.</qt>");
+ case SelfPeakColumn:
+ return i18n("<qt>The maximum heap memory in bytes consumed from allocations originating directly at this location. "
+ "This takes deallocations into account.</qt>");
+ case SelfLeakedColumn:
+ return i18n("<qt>The bytes allocated directly at this location that have not been deallocated.</qt>");
+ case SelfAllocatedColumn:
+ return i18n("<qt>The sum of all bytes directly allocated from this location, ignoring deallocations.</qt>");
+ case InclusiveAllocationsColumn:
+ return i18n("<qt>The inclusive number of times an allocation function was called from this location or any functions called from here.</qt>");
+ case InclusiveTemporaryColumn:
+ return i18n("<qt>The number of inclusive temporary allocations. These allocations are directly followed by a free without any other allocations in-between.</qt>");
+ case InclusivePeakColumn:
+ return i18n("<qt>The inclusive maximum heap memory in bytes consumed from allocations originating at this location or from functions called from here. "
+ "This takes deallocations into account.</qt>");
+ case InclusiveLeakedColumn:
+ return i18n("<qt>The bytes allocated at this location that have not been deallocated.</qt>");
+ case InclusiveAllocatedColumn:
+ return i18n("<qt>The inclusive sum of all bytes allocated from this location or functions called from here, ignoring deallocations.</qt>");
+ case LocationColumn:
+ return i18n("<qt>The location from which an allocation function was called. Function symbol and file information "
+ "may be unknown when debug information was missing when heaptrack was run.</qt>");
+ case NUM_COLUMNS:
+ break;
+ }
+ }
+ return {};
+
+}
+
+QVariant CallerCalleeModel::data(const QModelIndex& index, int role) const
+{
+ if (!hasIndex(index.row(), index.column(), index.parent())) {
+ return {};
+ }
+
+ const auto& row = (role == MaxCostRole) ? m_maxCost : m_rows.at(index.row());
+
+ if (role == Qt::DisplayRole || role == SortRole || role == MaxCostRole) {
+ switch (static_cast<Columns>(index.column())) {
+ case SelfAllocatedColumn:
+ if (role == SortRole || role == MaxCostRole) {
+ return static_cast<quint64>(row.selfCost.allocated);
+ } else {
+ return m_format.formatByteSize(row.selfCost.allocated);
+ }
+ case SelfAllocationsColumn:
+ return static_cast<quint64>(row.selfCost.allocations);
+ case SelfTemporaryColumn:
+ return static_cast<quint64>(row.selfCost.temporary);
+ case SelfPeakColumn:
+ if (role == SortRole || role == MaxCostRole) {
+ return static_cast<quint64>(row.selfCost.peak);
+ } else {
+ return m_format.formatByteSize(row.selfCost.peak);
+ }
+ case SelfLeakedColumn:
+ if (role == SortRole || role == MaxCostRole) {
+ return static_cast<quint64>(row.selfCost.leaked);
+ } else {
+ return m_format.formatByteSize(row.selfCost.leaked);
+ }
+ case InclusiveAllocatedColumn:
+ if (role == SortRole || role == MaxCostRole) {
+ return static_cast<quint64>(row.inclusiveCost.allocated);
+ } else {
+ return m_format.formatByteSize(row.inclusiveCost.allocated);
+ }
+ case InclusiveAllocationsColumn:
+ return static_cast<quint64>(row.inclusiveCost.allocations);
+ case InclusiveTemporaryColumn:
+ return static_cast<quint64>(row.inclusiveCost.temporary);
+ case InclusivePeakColumn:
+ if (role == SortRole || role == MaxCostRole) {
+ return static_cast<quint64>(row.inclusiveCost.peak);
+ } else {
+ return m_format.formatByteSize(row.inclusiveCost.peak);
+ }
+ case InclusiveLeakedColumn:
+ if (role == SortRole || role == MaxCostRole) {
+ return static_cast<quint64>(row.inclusiveCost.leaked);
+ } else {
+ return m_format.formatByteSize(row.inclusiveCost.leaked);
+ }
+ case FunctionColumn:
+ return row.location->function;
+ case ModuleColumn:
+ return row.location->module;
+ case FileColumn:
+ return row.location->file;
+ case LineColumn:
+ return row.location->line;
+ case LocationColumn:
+ if (row.location->file.isEmpty()) {
+ return i18n("%1 in ?? (%2)",
+ basename(row.location->function),
+ basename(row.location->module));
+ } else {
+ return i18n("%1 in %2:%3 (%4)", row.location->function,
+ basename(row.location->file), row.location->line,
+ basename(row.location->module));
+ }
+ case NUM_COLUMNS:
+ break;
+ }
+ } else if (role == Qt::ToolTipRole) {
+ QString tooltip;
+ QTextStream stream(&tooltip);
+ stream << "<qt><pre style='font-family:monospace;'>";
+ if (row.location->line > 0) {
+ stream << i18nc("1: function, 2: file, 3: line, 4: module", "%1\n at %2:%3\n in %4",
+ row.location->function,
+ row.location->file, row.location->line, row.location->module);
+ } else {
+ stream << i18nc("1: function, 2: module", "%1\n in %2",
+ row.location->function, row.location->module);
+ }
+ stream << '\n';
+ stream << i18n("inclusive: allocated %1 over %2 calls (%3 temporary, i.e. %4%), peak at %5, leaked %6",
+ m_format.formatByteSize(row.inclusiveCost.allocated), row.inclusiveCost.allocations, row.inclusiveCost.temporary,
+ round(float(row.inclusiveCost.temporary) * 100.f * 100.f / row.inclusiveCost.allocations) / 100.f,
+ m_format.formatByteSize(row.inclusiveCost.peak), m_format.formatByteSize(row.inclusiveCost.leaked));
+ stream << '\n';
+ stream << i18n("self: allocated %1 over %2 calls (%3 temporary, i.e. %4%), peak at %5, leaked %6",
+ m_format.formatByteSize(row.selfCost.allocated), row.selfCost.allocations, row.selfCost.temporary,
+ round(float(row.selfCost.temporary) * 100.f * 100.f / row.selfCost.allocations) / 100.f,
+ m_format.formatByteSize(row.selfCost.peak), m_format.formatByteSize(row.selfCost.leaked));
+ stream << '\n';
+ stream << "</pre></qt>";
+ return tooltip;
+ }
+ return {};
+}
+
+int CallerCalleeModel::columnCount(const QModelIndex& parent) const
+{
+ return parent.isValid() ? 0 : NUM_COLUMNS;
+}
+
+int CallerCalleeModel::rowCount(const QModelIndex& parent) const
+{
+ return parent.isValid() ? 0 : m_rows.size();
+}
+
+void CallerCalleeModel::resetData(const QVector<CallerCalleeData>& rows)
+{
+ beginResetModel();
+ m_rows = rows;
+ endResetModel();
+}
+
+void CallerCalleeModel::setSummary(const SummaryData& data)
+{
+ beginResetModel();
+ m_maxCost.inclusiveCost = data.cost;
+ m_maxCost.selfCost = data.cost;
+ endResetModel();
+}
--- /dev/null
+/*
+ * 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 CALLERCALLEEMODEL_H
+#define CALLERCALLEEMODEL_H
+
+#include <QVector>
+#include <QAbstractTableModel>
+
+#include <KFormat>
+
+#include "../allocationdata.h"
+#include "locationdata.h"
+#include "summarydata.h"
+
+struct CallerCalleeData
+{
+ AllocationData inclusiveCost;
+ AllocationData selfCost;
+ std::shared_ptr<LocationData> location;
+};
+Q_DECLARE_TYPEINFO(CallerCalleeData, Q_MOVABLE_TYPE);
+
+using CallerCalleeRows = QVector<CallerCalleeData>;
+Q_DECLARE_METATYPE(CallerCalleeRows);
+
+class CallerCalleeModel : public QAbstractTableModel
+{
+ Q_OBJECT
+public:
+ explicit CallerCalleeModel(QObject* parent = nullptr);
+ ~CallerCalleeModel();
+
+ enum Columns {
+ InclusiveAllocationsColumn,
+ InclusiveTemporaryColumn,
+ InclusivePeakColumn,
+ InclusiveLeakedColumn,
+ InclusiveAllocatedColumn,
+ SelfAllocationsColumn,
+ SelfTemporaryColumn,
+ SelfPeakColumn,
+ SelfLeakedColumn,
+ SelfAllocatedColumn,
+ FunctionColumn,
+ FileColumn,
+ LineColumn,
+ ModuleColumn,
+ LocationColumn,
+ NUM_COLUMNS
+ };
+
+ enum Roles {
+ SortRole = Qt::UserRole,
+ MaxCostRole = Qt::UserRole + 1
+ };
+
+ QVariant headerData(int section, Qt::Orientation orientation = Qt::Horizontal,
+ int role = Qt::DisplayRole) const override;
+ QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
+
+ int columnCount(const QModelIndex& parent = {}) const override;
+ int rowCount(const QModelIndex &parent = {}) const override;
+
+ void resetData(const QVector<CallerCalleeData>& rows);
+ void setSummary(const SummaryData& data);
+private:
+ QVector<CallerCalleeData> m_rows;
+ CallerCalleeData m_maxCost;
+ KFormat m_format;
+};
+
+#endif // CALLERCALLEEMODEL_H
--- /dev/null
+/*
+ * 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 LOCATIONDATA_H
+#define LOCATIONDATA_H
+
+#include <QString>
+
+#include <memory>
+
+struct LocationData
+{
+ QString function;
+ QString file;
+ QString module;
+ int line;
+
+ bool operator==(const LocationData& rhs) const
+ {
+ return function == rhs.function
+ && file == rhs.file
+ && module == rhs.module
+ && line == rhs.line;
+ }
+
+ bool operator<(const LocationData& rhs) const
+ {
+ int i = function.compare(rhs.function);
+ if (!i) {
+ i = file.compare(rhs.file);
+ }
+ if (!i) {
+ i = line < rhs.line ? -1 : (line > rhs.line);
+ }
+ if (!i) {
+ i = module.compare(rhs.module);
+ }
+ return i < 0;
+ }
+};
+Q_DECLARE_TYPEINFO(LocationData, Q_MOVABLE_TYPE);
+
+inline bool operator<(const std::shared_ptr<LocationData>& lhs, const LocationData& rhs)
+{
+ return *lhs < rhs;
+}
+
+#endif // LOCATIONDATA_H
#include "chartproxy.h"
#include "histogrammodel.h"
#include "stacksmodel.h"
+#include "callercalleemodel.h"
using namespace std;
auto bottomUpModel = new TreeModel(this);
auto topDownModel = new TreeModel(this);
+ auto callerCalleeModel = new CallerCalleeModel(this);
auto consumedModel = new ChartModel(ChartModel::Consumed, this);
m_ui->consumedTab->setModel(consumedModel);
auto sizeHistogramModel = new HistogramModel(this);
m_ui->sizesTab->setModel(sizeHistogramModel);
+ m_ui->tabWidget->setTabEnabled(m_ui->tabWidget->indexOf(m_ui->callerCalleeTab), false);
m_ui->tabWidget->setTabEnabled(m_ui->tabWidget->indexOf(m_ui->consumedTab), false);
m_ui->tabWidget->setTabEnabled(m_ui->tabWidget->indexOf(m_ui->allocationsTab), false);
m_ui->tabWidget->setTabEnabled(m_ui->tabWidget->indexOf(m_ui->allocatedTab), false);
statusBar()->addWidget(m_ui->loadingProgress);
m_ui->pages->setCurrentWidget(m_ui->resultsPage);
});
+ connect(m_parser, &Parser::callerCalleeDataAvailable,
+ this, [=] (const CallerCalleeRows& data) {
+ callerCalleeModel->resetData(data);
+ m_ui->tabWidget->setTabEnabled(m_ui->tabWidget->indexOf(m_ui->callerCalleeTab), true);
+ });
connect(m_parser, &Parser::topDownDataAvailable,
this, [=] (const TreeData& data) {
topDownModel->resetData(data);
m_ui->tabWidget->setTabEnabled(m_ui->tabWidget->indexOf(m_ui->sizesTab), true);
});
connect(m_parser, &Parser::summaryAvailable,
- this, [this, bottomUpModel, topDownModel] (const SummaryData& data) {
+ this, [=] (const SummaryData& data) {
bottomUpModel->setSummary(data);
topDownModel->setSummary(data);
+ callerCalleeModel->setSummary(data);
KFormat format;
QString textLeft;
QString textCenter;
});
m_ui->messages->hide();
- auto bottomUpProxy = new TreeProxy(bottomUpModel);
+ auto bottomUpProxy = new TreeProxy(TreeModel::FunctionColumn,
+ TreeModel::FileColumn,
+ TreeModel::ModuleColumn,
+ bottomUpModel);
bottomUpProxy->setSourceModel(bottomUpModel);
bottomUpProxy->setSortRole(TreeModel::SortRole);
m_ui->bottomUpResults->setModel(bottomUpProxy);
connect(m_ui->bottomUpFilterModule, &QLineEdit::textChanged,
bottomUpProxy, &TreeProxy::setModuleFilter);
- auto topDownProxy = new TreeProxy(topDownModel);
+ auto topDownProxy = new TreeProxy(TreeModel::FunctionColumn,
+ TreeModel::FileColumn,
+ TreeModel::ModuleColumn,
+ topDownModel);
topDownProxy->setSourceModel(topDownModel);
topDownProxy->setSortRole(TreeModel::SortRole);
m_ui->topDownResults->setModel(topDownProxy);
connect(m_ui->topDownFilterModule, &QLineEdit::textChanged,
bottomUpProxy, &TreeProxy::setModuleFilter);
+ auto callerCalleeProxy = new TreeProxy(CallerCalleeModel::FunctionColumn,
+ CallerCalleeModel::FileColumn,
+ CallerCalleeModel::ModuleColumn,
+ callerCalleeModel);
+ callerCalleeProxy->setSourceModel(callerCalleeModel);
+ callerCalleeProxy->setSortRole(CallerCalleeModel::SortRole);
+ m_ui->callerCalleeResults->setModel(callerCalleeProxy);
+ m_ui->callerCalleeResults->setItemDelegateForColumn(CallerCalleeModel::SelfPeakColumn, costDelegate);
+ m_ui->callerCalleeResults->setItemDelegateForColumn(CallerCalleeModel::SelfAllocatedColumn, costDelegate);
+ m_ui->callerCalleeResults->setItemDelegateForColumn(CallerCalleeModel::SelfLeakedColumn, costDelegate);
+ m_ui->callerCalleeResults->setItemDelegateForColumn(CallerCalleeModel::SelfAllocationsColumn, costDelegate);
+ m_ui->callerCalleeResults->setItemDelegateForColumn(CallerCalleeModel::SelfTemporaryColumn, costDelegate);
+ m_ui->callerCalleeResults->setItemDelegateForColumn(CallerCalleeModel::InclusivePeakColumn, costDelegate);
+ m_ui->callerCalleeResults->setItemDelegateForColumn(CallerCalleeModel::InclusiveAllocatedColumn, costDelegate);
+ m_ui->callerCalleeResults->setItemDelegateForColumn(CallerCalleeModel::InclusiveLeakedColumn, costDelegate);
+ m_ui->callerCalleeResults->setItemDelegateForColumn(CallerCalleeModel::InclusiveAllocationsColumn, costDelegate);
+ m_ui->callerCalleeResults->setItemDelegateForColumn(CallerCalleeModel::InclusiveTemporaryColumn, costDelegate);
+ m_ui->callerCalleeResults->hideColumn(CallerCalleeModel::FunctionColumn);
+ m_ui->callerCalleeResults->hideColumn(CallerCalleeModel::FileColumn);
+ m_ui->callerCalleeResults->hideColumn(CallerCalleeModel::LineColumn);
+ m_ui->callerCalleeResults->hideColumn(CallerCalleeModel::ModuleColumn);
+ connect(m_ui->callerCalleeFilterFunction, &QLineEdit::textChanged,
+ bottomUpProxy, &TreeProxy::setFunctionFilter);
+ connect(m_ui->callerCalleeFilterFile, &QLineEdit::textChanged,
+ bottomUpProxy, &TreeProxy::setFileFilter);
+ connect(m_ui->callerCalleeFilterModule, &QLineEdit::textChanged,
+ bottomUpProxy, &TreeProxy::setModuleFilter);
+
auto openFile = KStandardAction::open(this, SLOT(openFile()), this);
m_ui->openFile->setDefaultAction(openFile);
</item>
</layout>
</widget>
+ <widget class="QWidget" name="callerCalleeTab">
+ <attribute name="title">
+ <string>Caller / Callee</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_13">
+ <item>
+ <widget class="QWidget" name="widget_11" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_7">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QLineEdit" name="callerCalleeFilterFunction">
+ <property name="placeholderText">
+ <string>filter by function...</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="callerCalleeFilterFile">
+ <property name="placeholderText">
+ <string>filter by file...</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="callerCalleeFilterModule">
+ <property name="placeholderText">
+ <string>filter by module...</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QTreeView" name="callerCalleeResults">
+ <property name="rootIsDecorated">
+ <bool>false</bool>
+ </property>
+ <property name="uniformRowHeights">
+ <bool>true</bool>
+ </property>
+ <property name="sortingEnabled">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
<widget class="QWidget" name="topDownTab">
<attribute name="title">
<string>Top-Down</string>
}
}
-QVector<RowData> mergeAllocations(const ParserData& data)
+QPair<TreeData, CallerCalleeRows> mergeAllocations(const ParserData& data)
{
- QVector<RowData> topRows;
+ TreeData topRows;
+ CallerCalleeRows callerCalleeRows;
+ callerCalleeRows.resize(data.instructionPointers.size());
// merge allocations, leave parent pointers invalid (their location may change)
for (const auto& allocation : data.allocations) {
auto traceIndex = allocation.traceIndex;
const auto& trace = data.findTrace(traceIndex);
const auto& ip = data.findIp(trace.ipIndex);
auto location = data.stringCache.location(ip);
+
+ auto& callerCallee = callerCalleeRows[trace.ipIndex.index - 1];
+ callerCallee.inclusiveCost += allocation;
+ if (traceIndex == allocation.traceIndex) {
+ callerCallee.selfCost += allocation;
+ }
+ callerCallee.location = location;
+
auto it = lower_bound(rows->begin(), rows->end(), location);
if (it != rows->end() && it->location == location) {
it->cost += allocation;
}
// now set the parents, the data is constant from here on
setParents(topRows, nullptr);
- return topRows;
+ callerCalleeRows.erase(std::remove_if(callerCalleeRows.begin(), callerCalleeRows.end(), [] (const CallerCalleeData& data) {
+ return !data.location;
+ }), callerCalleeRows.end());
+ return qMakePair(topRows, callerCalleeRows);
}
RowData* findByLocation(const RowData& row, QVector<RowData>* data)
return nullptr;
}
-void buildTopDown(const QVector<RowData>& bottomUpData, QVector<RowData>* topDownData)
+void buildTopDown(const TreeData& bottomUpData, TreeData* topDownData)
{
foreach (const auto& row, bottomUpData) {
if (row.children.isEmpty()) {
emit progressMessageAvailable(i18n("merging allocations..."));
// merge allocations before modifying the data again
const auto mergedAllocations = mergeAllocations(*data);
- emit bottomUpDataAvailable(mergedAllocations);
+ emit bottomUpDataAvailable(mergedAllocations.first);
+ emit callerCalleeDataAvailable(mergedAllocations.second);
// also calculate the size histogram
emit progressMessageAvailable(i18n("building size histogram..."));
emit progressMessageAvailable(i18n("building charts..."));
auto parallel = new Collection;
*parallel << make_job([this, mergedAllocations, sizeHistogram]() {
- const auto topDownData = toTopDownData(mergedAllocations);
+ const auto topDownData = toTopDownData(mergedAllocations.first);
emit topDownDataAvailable(topDownData);
}) << make_job([this, data, stdPath]() {
// this mutates data, and thus anything running in parallel must
#include "treemodel.h"
#include "chartmodel.h"
#include "histogrammodel.h"
+#include "callercalleemodel.h"
class Parser : public QObject
{
void summaryAvailable(const SummaryData& summary);
void bottomUpDataAvailable(const TreeData& data);
void topDownDataAvailable(const TreeData& data);
+ void callerCalleeDataAvailable(const CallerCalleeRows& data);
void consumedChartDataAvailable(const ChartData& data);
void allocationsChartDataAvailable(const ChartData& data);
void allocatedChartDataAvailable(const ChartData& data);
--- /dev/null
+/*
+ * 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 SUMMARYDATA_H
+#define SUMMARYDATA_H
+
+#include <QString>
+#include <QMetaType>
+#include "../allocationdata.h"
+
+struct SummaryData
+{
+ QString debuggee;
+ AllocationData cost;
+ uint64_t totalTime;
+ uint64_t peakTime;
+ uint64_t peakRSS;
+ uint64_t totalSystemMemory;
+};
+Q_DECLARE_METATYPE(SummaryData);
+
+#endif // SUMMARYDATA_H
#include <KFormat>
-#include <memory>
-
+#include "locationdata.h"
+#include "summarydata.h"
#include "../allocationdata.h"
-struct SummaryData
-{
- QString debuggee;
- AllocationData cost;
- uint64_t totalTime;
- uint64_t peakTime;
- uint64_t peakRSS;
- uint64_t totalSystemMemory;
-};
-Q_DECLARE_METATYPE(SummaryData);
-
-struct LocationData
-{
- QString function;
- QString file;
- QString module;
- int line;
-
- bool operator==(const LocationData& rhs) const
- {
- return function == rhs.function
- && file == rhs.file
- && module == rhs.module
- && line == rhs.line;
- }
-
- bool operator<(const LocationData& rhs) const
- {
- int i = function.compare(rhs.function);
- if (!i) {
- i = file.compare(rhs.file);
- }
- if (!i) {
- i = line < rhs.line ? -1 : (line > rhs.line);
- }
- if (!i) {
- i = module.compare(rhs.module);
- }
- return i < 0;
- }
-};
-Q_DECLARE_TYPEINFO(LocationData, Q_MOVABLE_TYPE);
-
-inline bool operator<(const std::shared_ptr<LocationData>& lhs, const LocationData& rhs)
-{
- return *lhs < rhs;
-}
-
struct RowData
{
AllocationData cost;
#include "treeproxy.h"
-#include "treemodel.h"
-
-TreeProxy::TreeProxy(QObject* parent)
+TreeProxy::TreeProxy(int functionColumn, int fileColumn, int moduleColumn, QObject* parent)
: KRecursiveFilterProxyModel(parent)
+ , m_functionColumn(functionColumn)
+ , m_fileColumn(fileColumn)
+ , m_moduleColumn(moduleColumn)
{
}
return false;
}
if (!m_functionFilter.isEmpty()) {
- const auto& function = source->index(sourceRow, TreeModel::FunctionColumn, sourceParent).data().toString();
+ const auto& function = source->index(sourceRow, m_functionColumn, sourceParent).data().toString();
if (!function.contains(m_functionFilter, Qt::CaseInsensitive)) {
return false;
}
}
if (!m_fileFilter.isEmpty()) {
- const auto& file = source->index(sourceRow, TreeModel::FileColumn, sourceParent).data().toString();
+ const auto& file = source->index(sourceRow, m_fileColumn, sourceParent).data().toString();
if (!file.contains(m_fileFilter, Qt::CaseInsensitive)) {
return false;
}
}
if (!m_moduleFilter.isEmpty()) {
- const auto& module = source->index(sourceRow, TreeModel::ModuleColumn, sourceParent).data().toString();
+ const auto& module = source->index(sourceRow, m_moduleColumn, sourceParent).data().toString();
if (!module.contains(m_moduleFilter, Qt::CaseInsensitive)) {
return false;
}
{
Q_OBJECT
public:
- explicit TreeProxy(QObject* parent = nullptr);
+ explicit TreeProxy(int functionColumn, int fileColumn, int moduleColumn, QObject* parent = nullptr);
virtual ~TreeProxy();
public slots:
private:
bool acceptRow(int source_row, const QModelIndex& source_parent) const override;
+ int m_functionColumn;
+ int m_fileColumn;
+ int m_moduleColumn;
+
QString m_functionFilter;
QString m_fileFilter;
QString m_moduleFilter;