Skip to content

Commit f282a0f

Browse files
committed
diff view for top down and bottom up
1 parent 95684db commit f282a0f

9 files changed

Lines changed: 152 additions & 62 deletions

src/models/costdelegate.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ void CostDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option,
3131
}
3232

3333
const auto totalCost = index.data(m_totalCostRole).toULongLong();
34-
const auto fraction = std::abs(float(cost) / totalCost);
34+
// TODO C++17: std::clamp
35+
const auto fraction = std::max(0.f, std::min(1.f, std::abs(float(cost) / totalCost)));
3536

3637
auto rect = option.rect;
3738
rect.setWidth(rect.width() * fraction);

src/models/costproxy.h

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,35 @@ class CostProxy : public QSortFilterProxyModel
4242
}
4343
};
4444

45+
namespace CostProxyUtil {
46+
inline int cost(const BottomUpModel* model, int column, int nodeid)
47+
{
48+
return model->results().costs.cost(column, nodeid);
49+
}
50+
inline int cost(const TopDownModel* model, int column, int nodeid)
51+
{
52+
const auto inclusiveTypes = model->results().inclusiveCosts.numTypes();
53+
if (column >= inclusiveTypes) {
54+
return model->results().selfCosts.cost(column - inclusiveTypes, nodeid);
55+
}
56+
return model->results().inclusiveCosts.cost(column, nodeid);
57+
}
58+
59+
inline int totalCost(const BottomUpModel* model, int column)
60+
{
61+
return model->results().costs.totalCost(column);
62+
}
63+
inline int totalCost(const TopDownModel* model, int column)
64+
{
65+
const auto inclusiveTypes = model->results().inclusiveCosts.numTypes();
66+
if (column >= inclusiveTypes) {
67+
return model->results().selfCosts.totalCost(column - inclusiveTypes);
68+
}
69+
return model->results().inclusiveCosts.totalCost(column);
70+
}
71+
}
72+
73+
// TODO dedicated cost role
4574
template<typename Model>
4675
class DiffCostProxy : public CostProxy<Model>
4776
{
@@ -62,23 +91,25 @@ class DiffCostProxy : public CostProxy<Model>
6291
const auto baseColumn = (index.column() - Model::NUM_BASE_COLUMNS) / 2;
6392
const auto column = baseColumn + (index.column() - Model::NUM_BASE_COLUMNS) % 2;
6493

65-
auto cost = [model, node](int column) -> float { return model->results().costs.cost(column, node->id); };
94+
auto cost = [model, node](int column) { return CostProxyUtil::cost(model, column, node->id); };
6695

67-
auto totalCost = [model](int column) -> float { return model->results().costs.totalCost(column); };
96+
auto totalCost = [model](int column) { return CostProxyUtil::totalCost(model, column); };
6897

6998
if (column == baseColumn) {
7099
if (role == Model::TotalCostRole) {
71100
return totalCost(column);
72101
} else if (role == Model::SortRole) {
73-
return cost(column) / totalCost(column);
102+
return cost(column);
74103
} else if (role == Qt::DisplayRole) {
75104
return Util::formatCostRelative(cost(column), totalCost(column), true);
76105
}
77106
} else {
78107
if (role == Model::TotalCostRole) {
79108
return cost(baseColumn);
80109
} else if (role == Model::SortRole) {
81-
return cost(column) / cost(baseColumn);
110+
if (cost(baseColumn) == 0)
111+
return 0;
112+
return cost(column);
82113
} else if (role == Qt::DisplayRole) {
83114
return Util::formatCostRelative(cost(column), cost(baseColumn), true);
84115
}

src/models/data.cpp

Lines changed: 51 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -281,13 +281,14 @@ void buildPerLibrary(const TopDown* node, PerLibraryResults& results, QHash<QStr
281281
}
282282
}
283283

284-
void diffBottomUpResults(const BottomUp& a, const BottomUp* b, BottomUp* result_node, const Costs& costs_a,
285-
const Costs& costs_b, Costs* costs_result)
284+
template<typename ResultType, bool addResultNode = true>
285+
void diffResults(const ResultType& a, const ResultType* b, ResultType* result_node, const Costs& costs_a,
286+
const Costs& costs_b, Costs* costs_result)
286287
{
287288
for (const auto& node : a.children) {
288289
const auto sibling = b->entryForSymbol(node.symbol);
289290
if (sibling) {
290-
BottomUp diffed;
291+
ResultType diffed;
291292
diffed.id = node.id;
292293
diffed.symbol = node.symbol;
293294

@@ -296,8 +297,10 @@ void diffBottomUpResults(const BottomUp& a, const BottomUp* b, BottomUp* result_
296297
costs_result->add(2 * i + 1, diffed.id, costs_b.cost(i, sibling->id));
297298
}
298299

299-
result_node->children.push_back(diffed);
300-
diffBottomUpResults(node, sibling, &result_node->children.back(), costs_a, costs_b, costs_result);
300+
if (addResultNode) {
301+
result_node->children.push_back(diffed);
302+
}
303+
diffResults(node, sibling, &result_node->children.back(), costs_a, costs_b, costs_result);
301304
}
302305
}
303306
}
@@ -309,7 +312,7 @@ QString Data::prettifySymbol(const QString& name)
309312
return result == name ? name : result;
310313
}
311314

312-
TopDownResults TopDownResults::fromBottomUp(const BottomUpResults& bottomUpData, bool skipFirstLevel)
315+
TopDownResults Data::TopDownResults::fromBottomUp(const BottomUpResults& bottomUpData, bool skipFirstLevel)
313316
{
314317
TopDownResults results;
315318
results.selfCosts.initializeCostsFrom(bottomUpData.costs);
@@ -435,9 +438,50 @@ Data::BottomUpResults BottomUpResults::diffBottomUpResults(const Data::BottomUpR
435438
results.costs.addTotalCost(costBType, b.costs.totalCost(0));
436439
}
437440

438-
::diffBottomUpResults(a.root, &b.root, &results.root, a.costs, b.costs, &results.costs);
441+
diffResults(a.root, &b.root, &results.root, a.costs, b.costs, &results.costs);
439442

440443
BottomUp::initializeParents(&results.root);
441444

442445
return results;
443446
}
447+
448+
TopDownResults TopDownResults::diffTopDownResults(const TopDownResults& a, const TopDownResults& b)
449+
{
450+
if (a.selfCosts.numTypes() != b.selfCosts.numTypes()) {
451+
return {};
452+
}
453+
454+
TopDownResults results;
455+
456+
for (int i = 0; i < a.selfCosts.numTypes(); i++) {
457+
// only diff same type of costs
458+
if (a.selfCosts.typeName(i) != b.selfCosts.typeName(i)) {
459+
return {};
460+
}
461+
462+
results.selfCosts.addType(2 * i, QLatin1String("baseline %1").arg(a.selfCosts.typeName(i)),
463+
a.selfCosts.unit(i));
464+
results.selfCosts.addTotalCost(2 * i, a.selfCosts.totalCost(i));
465+
466+
results.inclusiveCosts.addType(2 * i, QLatin1String("baseline %1").arg(a.inclusiveCosts.typeName(i)),
467+
a.inclusiveCosts.unit(i));
468+
results.inclusiveCosts.addTotalCost(2 * i, a.inclusiveCosts.totalCost(i));
469+
470+
const auto costBType = 2 * i + 1;
471+
results.selfCosts.addType(costBType, QLatin1String("ratio of %1").arg(b.selfCosts.typeName(i)),
472+
Costs::Unit::Unknown);
473+
results.selfCosts.addTotalCost(costBType, b.selfCosts.totalCost(0));
474+
475+
results.inclusiveCosts.addType(costBType, QLatin1String("ratio of %1").arg(b.inclusiveCosts.typeName(i)),
476+
Costs::Unit::Unknown);
477+
results.inclusiveCosts.addTotalCost(costBType, b.inclusiveCosts.totalCost(0));
478+
}
479+
480+
diffResults(a.root, &b.root, &results.root, a.selfCosts, b.selfCosts, &results.selfCosts);
481+
diffResults<TopDown, false>(a.root, &b.root, &results.root, a.inclusiveCosts, b.inclusiveCosts,
482+
&results.inclusiveCosts);
483+
484+
Data::TopDown::initializeParents(&results.root);
485+
486+
return results;
487+
}

src/models/data.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -472,7 +472,9 @@ struct TopDownResults
472472
TopDown root;
473473
Costs selfCosts;
474474
Costs inclusiveCosts;
475-
static TopDownResults fromBottomUp(const Data::BottomUpResults& bottomUpData, bool skipFirstLevel);
475+
static TopDownResults fromBottomUp(const Data::BottomUpResults& bottomUpData, bool skipFirestLevel);
476+
477+
static TopDownResults diffTopDownResults(const Data::TopDownResults& a, const Data::TopDownResults& b);
476478
};
477479

478480
struct PerLibrary : SymbolTree<PerLibrary>

src/resultsbottomuppage.cpp

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -85,24 +85,21 @@ void ResultsBottomUpPage::setBottomUpResults(const Data::BottomUpResults& result
8585
m_model->setData(results);
8686
ResultsUtil::hideEmptyColumns(results.costs, ui->bottomUpTreeView, BottomUpModel::NUM_BASE_COLUMNS);
8787

88-
{
89-
auto stackCollapsed =
90-
m_exportMenu->addMenu(QIcon::fromTheme(QStringLiteral("text-plain")), tr("Stack Collapsed"));
91-
stackCollapsed->setToolTip(tr("Export data in textual form compatible with <tt>flamegraph.pl</tt>."));
92-
for (int i = 0; i < results.costs.numTypes(); ++i) {
93-
const auto costName = results.costs.typeName(i);
94-
stackCollapsed->addAction(costName, [this, i, costName]() {
95-
const auto fileName = QFileDialog::getSaveFileName(this, tr("Export %1 Data").arg(costName));
96-
if (fileName.isEmpty())
97-
return;
98-
QFile file(fileName);
99-
if (!file.open(QIODevice::Text | QIODevice::WriteOnly)) {
100-
QMessageBox::warning(this, tr("Failed to export data"),
101-
tr("Failed to export stack collapsed data:\n%1").arg(file.errorString()));
102-
return;
103-
}
104-
stackCollapsedExport(file, i, m_model->results());
105-
});
106-
}
88+
auto stackCollapsed = m_exportMenu->addMenu(QIcon::fromTheme(QStringLiteral("text-plain")), tr("Stack Collapsed"));
89+
stackCollapsed->setToolTip(tr("Export data in textual form compatible with <tt>flamegraph.pl</tt>."));
90+
for (int i = 0; i < results.costs.numTypes(); ++i) {
91+
const auto costName = results.costs.typeName(i);
92+
stackCollapsed->addAction(costName, [this, i, costName]() {
93+
const auto fileName = QFileDialog::getSaveFileName(this, tr("Export %1 Data").arg(costName));
94+
if (fileName.isEmpty())
95+
return;
96+
QFile file(fileName);
97+
if (!file.open(QIODevice::Text | QIODevice::WriteOnly)) {
98+
QMessageBox::warning(this, tr("Failed to export data"),
99+
tr("Failed to export stack collapsed data:\n%1").arg(file.errorString()));
100+
return;
101+
}
102+
stackCollapsedExport(file, i, m_model->results());
103+
});
107104
}
108105
}

src/resultsbottomuppage.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class ResultsBottomUpPage;
1818

1919
namespace Data {
2020
struct Symbol;
21-
class BottomUpResults;
21+
struct BottomUpResults;
2222
}
2323

2424
class QTreeView;

src/resultspagediff.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,9 +145,14 @@ ResultsPageDiff::ResultsPageDiff(QWidget* parent)
145145
}
146146

147147
connect(this, &ResultsPageDiff::parsingFinished, this, [this] {
148-
const auto bottomUpData =
148+
auto bottomUpData =
149149
Data::BottomUpResults::diffBottomUpResults(m_fileA->bottomUpResults(), m_fileB->bottomUpResults());
150150
m_resultsBottomUpPage->setBottomUpResults(bottomUpData);
151+
152+
auto topDownData =
153+
Data::TopDownResults::diffTopDownResults(Data::TopDownResults::fromBottomUp(m_fileA->bottomUpResults()),
154+
Data::TopDownResults::fromBottomUp(m_fileB->bottomUpResults()));
155+
m_resultsTopDownPage->setTopDownResults(topDownData);
151156
});
152157

153158
{

src/resultstopdownpage.cpp

Lines changed: 32 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "resultstopdownpage.h"
1010
#include "ui_resultstopdownpage.h"
1111

12+
#include "data.h"
1213
#include "parsers/perf/perfparser.h"
1314
#include "resultsutil.h"
1415

@@ -18,38 +19,16 @@
1819
ResultsTopDownPage::ResultsTopDownPage(FilterAndZoomStack* filterStack, PerfParser* parser,
1920
CostContextMenu* contextMenu, QWidget* parent)
2021
: QWidget(parent)
22+
, m_model(new TopDownModel(this))
2123
, ui(new Ui::ResultsTopDownPage)
2224
{
2325
ui->setupUi(this);
2426

25-
auto topDownCostModel = new TopDownModel(this);
26-
ResultsUtil::setupTreeView(ui->topDownTreeView, contextMenu, ui->topDownSearch, topDownCostModel);
27-
ResultsUtil::setupCostDelegate(topDownCostModel, ui->topDownTreeView);
28-
ResultsUtil::setupContextMenu(ui->topDownTreeView, contextMenu, topDownCostModel, filterStack, this);
29-
30-
connect(parser, &PerfParser::topDownDataAvailable, this,
31-
[this, topDownCostModel](const Data::TopDownResults& data) {
32-
topDownCostModel->setData(data);
33-
ResultsUtil::hideEmptyColumns(data.inclusiveCosts, ui->topDownTreeView, TopDownModel::NUM_BASE_COLUMNS);
34-
35-
ResultsUtil::hideEmptyColumns(data.selfCosts, ui->topDownTreeView,
36-
TopDownModel::NUM_BASE_COLUMNS + data.inclusiveCosts.numTypes());
37-
ResultsUtil::hideTracepointColumns(data.selfCosts, ui->topDownTreeView,
38-
TopDownModel::NUM_BASE_COLUMNS + data.inclusiveCosts.numTypes());
39-
40-
// hide self cost columns for sched:sched_switch and off-CPU
41-
// quasi all rows will have a cost of 0%, and only the leaves will show
42-
// a non-zero value that is equal to the inclusive cost then
43-
const auto costs = data.inclusiveCosts.numTypes();
44-
const auto schedSwitchName = QLatin1String("sched:sched_switch");
45-
const auto offCpuName = PerfParser::tr("off-CPU Time");
46-
for (int i = 0; i < costs; ++i) {
47-
const auto typeName = data.inclusiveCosts.typeName(i);
48-
if (typeName == schedSwitchName || typeName == offCpuName) {
49-
ui->topDownTreeView->hideColumn(topDownCostModel->selfCostColumn(i));
50-
}
51-
}
52-
});
27+
ResultsUtil::setupTreeViewDiff(ui->topDownTreeView, contextMenu, ui->topDownSearch, m_model);
28+
ResultsUtil::setupCostDelegate(m_model, ui->topDownTreeView);
29+
ResultsUtil::setupContextMenu(ui->topDownTreeView, contextMenu, m_model, filterStack, this);
30+
31+
connect(parser, &PerfParser::topDownDataAvailable, this, &ResultsTopDownPage::setTopDownResults);
5332

5433
ResultsUtil::setupResultsAggregation(ui->costAggregationComboBox);
5534
}
@@ -60,3 +39,28 @@ void ResultsTopDownPage::clear()
6039
{
6140
ui->topDownSearch->setText({});
6241
}
42+
43+
void ResultsTopDownPage::setTopDownResults(const Data::TopDownResults& data)
44+
{
45+
m_model->setData(data);
46+
ResultsUtil::hideEmptyColumns(data.inclusiveCosts, ui->topDownTreeView, TopDownModel::NUM_BASE_COLUMNS);
47+
48+
ResultsUtil::hideEmptyColumns(data.selfCosts, ui->topDownTreeView,
49+
TopDownModel::NUM_BASE_COLUMNS + data.inclusiveCosts.numTypes());
50+
ResultsUtil::hideTracepointColumns(data.selfCosts, ui->topDownTreeView,
51+
TopDownModel::NUM_BASE_COLUMNS + data.inclusiveCosts.numTypes());
52+
53+
// hide self cost columns for sched:sched_switch and off-CPU
54+
// quasi all rows will have a cost of 0%, and only the leaves will show
55+
// a non-zero value that is equal to the inclusive cost then
56+
const auto costs = data.inclusiveCosts.numTypes();
57+
const auto schedSwitchName = QLatin1String("sched:sched_switch");
58+
const auto offCpuName = PerfParser::tr("off-CPU Time");
59+
for (int i = 0; i < costs; ++i) {
60+
const auto typeName = data.inclusiveCosts.typeName(i);
61+
// use contains to also work in diff view
62+
if (typeName.contains(schedSwitchName) || typeName.contains(offCpuName)) {
63+
ui->topDownTreeView->hideColumn(m_model->selfCostColumn(i));
64+
}
65+
}
66+
}

src/resultstopdownpage.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,15 @@ class ResultsTopDownPage;
1616

1717
namespace Data {
1818
struct Symbol;
19+
struct TopDownResults;
1920
}
2021

2122
class QTreeView;
2223

2324
class PerfParser;
2425
class FilterAndZoomStack;
2526
class CostContextMenu;
27+
class TopDownModel;
2628

2729
class ResultsTopDownPage : public QWidget
2830
{
@@ -34,12 +36,16 @@ class ResultsTopDownPage : public QWidget
3436

3537
void clear();
3638

39+
public slots:
40+
void setTopDownResults(const Data::TopDownResults& data);
41+
3742
signals:
3843
void jumpToCallerCallee(const Data::Symbol& symbol);
3944
void openEditor(const Data::Symbol& symbol);
4045
void selectSymbol(const Data::Symbol& symbol);
4146
void jumpToDisassembly(const Data::Symbol& symbol);
4247

4348
private:
49+
TopDownModel* m_model = nullptr;
4450
QScopedPointer<Ui::ResultsTopDownPage> ui;
4551
};

0 commit comments

Comments
 (0)