From: Milian Wolff Date: Thu, 24 Mar 2016 18:21:54 +0000 (+0100) Subject: Add caller/callee table view of data. X-Git-Tag: submit/tizen/20180620.112952^2~201 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=889831f44cd0a4a4143352d05754996baf0930c4;p=sdk%2Ftools%2Fheaptrack.git Add caller/callee table view of data. --- diff --git a/gui/CMakeLists.txt b/gui/CMakeLists.txt index 37c336f..47c60b7 100644 --- a/gui/CMakeLists.txt +++ b/gui/CMakeLists.txt @@ -27,6 +27,7 @@ add_executable(heaptrack_gui flamegraph.cpp stacksmodel.cpp topproxy.cpp + callercalleemodel.cpp ${UIFILES} ) diff --git a/gui/callercalleemodel.cpp b/gui/callercalleemodel.cpp new file mode 100644 index 0000000..3dc8f60 --- /dev/null +++ b/gui/callercalleemodel.cpp @@ -0,0 +1,267 @@ +/* + * Copyright 2016 Milian Wolff + * + * 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 + +#include + +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(); +} + +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(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(section)) { + case FileColumn: + return i18n("The file where the allocation function was called from. " + "May be empty when debug information is missing."); + case LineColumn: + return i18n("The line number where the allocation function was called from. " + "May be empty when debug information is missing."); + case FunctionColumn: + return i18n("The parent function that called an allocation function. " + "May be unknown when debug information is missing."); + case ModuleColumn: + return i18n("The module, i.e. executable or shared library, from which an allocation function was called."); + case SelfAllocationsColumn: + return i18n("The number of times an allocation function was directly called from this location."); + case SelfTemporaryColumn: + return i18n("The number of direct temporary allocations. These allocations are directly followed by a free without any other allocations in-between."); + case SelfPeakColumn: + return i18n("The maximum heap memory in bytes consumed from allocations originating directly at this location. " + "This takes deallocations into account."); + case SelfLeakedColumn: + return i18n("The bytes allocated directly at this location that have not been deallocated."); + case SelfAllocatedColumn: + return i18n("The sum of all bytes directly allocated from this location, ignoring deallocations."); + case InclusiveAllocationsColumn: + return i18n("The inclusive number of times an allocation function was called from this location or any functions called from here."); + case InclusiveTemporaryColumn: + return i18n("The number of inclusive temporary allocations. These allocations are directly followed by a free without any other allocations in-between."); + case InclusivePeakColumn: + return i18n("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."); + case InclusiveLeakedColumn: + return i18n("The bytes allocated at this location that have not been deallocated."); + case InclusiveAllocatedColumn: + return i18n("The inclusive sum of all bytes allocated from this location or functions called from here, ignoring deallocations."); + case LocationColumn: + return i18n("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."); + 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(index.column())) { + case SelfAllocatedColumn: + if (role == SortRole || role == MaxCostRole) { + return static_cast(row.selfCost.allocated); + } else { + return m_format.formatByteSize(row.selfCost.allocated); + } + case SelfAllocationsColumn: + return static_cast(row.selfCost.allocations); + case SelfTemporaryColumn: + return static_cast(row.selfCost.temporary); + case SelfPeakColumn: + if (role == SortRole || role == MaxCostRole) { + return static_cast(row.selfCost.peak); + } else { + return m_format.formatByteSize(row.selfCost.peak); + } + case SelfLeakedColumn: + if (role == SortRole || role == MaxCostRole) { + return static_cast(row.selfCost.leaked); + } else { + return m_format.formatByteSize(row.selfCost.leaked); + } + case InclusiveAllocatedColumn: + if (role == SortRole || role == MaxCostRole) { + return static_cast(row.inclusiveCost.allocated); + } else { + return m_format.formatByteSize(row.inclusiveCost.allocated); + } + case InclusiveAllocationsColumn: + return static_cast(row.inclusiveCost.allocations); + case InclusiveTemporaryColumn: + return static_cast(row.inclusiveCost.temporary); + case InclusivePeakColumn: + if (role == SortRole || role == MaxCostRole) { + return static_cast(row.inclusiveCost.peak); + } else { + return m_format.formatByteSize(row.inclusiveCost.peak); + } + case InclusiveLeakedColumn: + if (role == SortRole || role == MaxCostRole) { + return static_cast(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 << "
";
+        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 << "
"; + 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& rows) +{ + beginResetModel(); + m_rows = rows; + endResetModel(); +} + +void CallerCalleeModel::setSummary(const SummaryData& data) +{ + beginResetModel(); + m_maxCost.inclusiveCost = data.cost; + m_maxCost.selfCost = data.cost; + endResetModel(); +} diff --git a/gui/callercalleemodel.h b/gui/callercalleemodel.h new file mode 100644 index 0000000..993327a --- /dev/null +++ b/gui/callercalleemodel.h @@ -0,0 +1,89 @@ +/* + * Copyright 2016 Milian Wolff + * + * 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 +#include + +#include + +#include "../allocationdata.h" +#include "locationdata.h" +#include "summarydata.h" + +struct CallerCalleeData +{ + AllocationData inclusiveCost; + AllocationData selfCost; + std::shared_ptr location; +}; +Q_DECLARE_TYPEINFO(CallerCalleeData, Q_MOVABLE_TYPE); + +using CallerCalleeRows = QVector; +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& rows); + void setSummary(const SummaryData& data); +private: + QVector m_rows; + CallerCalleeData m_maxCost; + KFormat m_format; +}; + +#endif // CALLERCALLEEMODEL_H diff --git a/gui/locationdata.h b/gui/locationdata.h new file mode 100644 index 0000000..385e416 --- /dev/null +++ b/gui/locationdata.h @@ -0,0 +1,64 @@ +/* + * Copyright 2016 Milian Wolff + * + * 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 + +#include + +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& lhs, const LocationData& rhs) +{ + return *lhs < rhs; +} + +#endif // LOCATIONDATA_H diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index e850faf..22f799a 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -39,6 +39,7 @@ #include "chartproxy.h" #include "histogrammodel.h" #include "stacksmodel.h" +#include "callercalleemodel.h" using namespace std; @@ -86,6 +87,7 @@ MainWindow::MainWindow(QWidget* parent) 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); @@ -98,6 +100,7 @@ MainWindow::MainWindow(QWidget* parent) 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); @@ -115,6 +118,11 @@ MainWindow::MainWindow(QWidget* parent) 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); @@ -148,9 +156,10 @@ MainWindow::MainWindow(QWidget* parent) 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; @@ -208,7 +217,10 @@ MainWindow::MainWindow(QWidget* parent) }); 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); @@ -229,7 +241,10 @@ MainWindow::MainWindow(QWidget* parent) 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); @@ -249,6 +264,34 @@ MainWindow::MainWindow(QWidget* parent) 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); diff --git a/gui/mainwindow.ui b/gui/mainwindow.ui index b8910f0..fb00ff3 100644 --- a/gui/mainwindow.ui +++ b/gui/mainwindow.ui @@ -402,6 +402,68 @@ + + + Caller / Callee + + + + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + filter by function... + + + + + + + filter by file... + + + + + + + filter by module... + + + + + + + + + + false + + + true + + + true + + + + + Top-Down diff --git a/gui/parser.cpp b/gui/parser.cpp index 8c937a6..11b9a63 100644 --- a/gui/parser.cpp +++ b/gui/parser.cpp @@ -300,9 +300,11 @@ void setParents(QVector& children, const RowData* parent) } } -QVector mergeAllocations(const ParserData& data) +QPair mergeAllocations(const ParserData& data) { - QVector 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; @@ -311,6 +313,14 @@ QVector mergeAllocations(const ParserData& data) 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; @@ -326,7 +336,10 @@ QVector mergeAllocations(const ParserData& data) } // 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* data) @@ -339,7 +352,7 @@ RowData* findByLocation(const RowData& row, QVector* data) return nullptr; } -void buildTopDown(const QVector& bottomUpData, QVector* topDownData) +void buildTopDown(const TreeData& bottomUpData, TreeData* topDownData) { foreach (const auto& row, bottomUpData) { if (row.children.isEmpty()) { @@ -483,7 +496,8 @@ void Parser::parse(const QString& path) 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...")); @@ -494,7 +508,7 @@ void Parser::parse(const QString& path) 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 diff --git a/gui/parser.h b/gui/parser.h index 113eadf..eec9d29 100644 --- a/gui/parser.h +++ b/gui/parser.h @@ -25,6 +25,7 @@ #include "treemodel.h" #include "chartmodel.h" #include "histogrammodel.h" +#include "callercalleemodel.h" class Parser : public QObject { @@ -41,6 +42,7 @@ signals: 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); diff --git a/gui/summarydata.h b/gui/summarydata.h new file mode 100644 index 0000000..6f861c7 --- /dev/null +++ b/gui/summarydata.h @@ -0,0 +1,38 @@ +/* + * Copyright 2016 Milian Wolff + * + * 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 +#include +#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 diff --git a/gui/treemodel.h b/gui/treemodel.h index 22e258f..667c1b4 100644 --- a/gui/treemodel.h +++ b/gui/treemodel.h @@ -25,58 +25,10 @@ #include -#include - +#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& lhs, const LocationData& rhs) -{ - return *lhs < rhs; -} - struct RowData { AllocationData cost; diff --git a/gui/treeproxy.cpp b/gui/treeproxy.cpp index ea2faca..bc5baeb 100644 --- a/gui/treeproxy.cpp +++ b/gui/treeproxy.cpp @@ -19,10 +19,11 @@ #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) { } @@ -53,19 +54,19 @@ bool TreeProxy::acceptRow(int sourceRow, const QModelIndex& sourceParent) const 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; } diff --git a/gui/treeproxy.h b/gui/treeproxy.h index 646af5f..7154c90 100644 --- a/gui/treeproxy.h +++ b/gui/treeproxy.h @@ -26,7 +26,7 @@ class TreeProxy final : public KRecursiveFilterProxyModel { Q_OBJECT public: - explicit TreeProxy(QObject* parent = nullptr); + explicit TreeProxy(int functionColumn, int fileColumn, int moduleColumn, QObject* parent = nullptr); virtual ~TreeProxy(); public slots: @@ -37,6 +37,10 @@ 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;