Skip to content

Commit

Permalink
diff view for top down and bottom up
Browse files Browse the repository at this point in the history
  • Loading branch information
lievenhey committed Dec 7, 2022
1 parent 216ce9e commit 1cf5b84
Show file tree
Hide file tree
Showing 9 changed files with 158 additions and 62 deletions.
3 changes: 2 additions & 1 deletion src/models/costdelegate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ void CostDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option,
}

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

auto rect = option.rect;
rect.setWidth(rect.width() * fraction);
Expand Down
43 changes: 39 additions & 4 deletions src/models/costproxy.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,39 @@ class CostProxy : public QSortFilterProxyModel
}
};

namespace CostProxyUtil {
inline int cost(const BottomUpModel* model, int column, int nodeid)
{
return model->results().costs.cost(column, nodeid);
}
inline int cost(const TopDownModel* model, int column, int nodeid)
{
const auto inclusiveTypes = model->results().inclusiveCosts.numTypes();
if (column >= inclusiveTypes) {
return model->results().selfCosts.cost(column - inclusiveTypes, nodeid);
}
return model->results().inclusiveCosts.cost(column, nodeid);
}

inline int totalCost(const BottomUpModel* model, int column)
{
return model->results().costs.totalCost(column);
}
inline int totalCost(const TopDownModel* model, int column)
{
const auto inclusiveTypes = model->results().inclusiveCosts.numTypes();
if (column >= inclusiveTypes) {
return model->results().selfCosts.totalCost(column - inclusiveTypes);
}
return model->results().inclusiveCosts.totalCost(column);
}
}

// TODO dedicated cost role
// The DiffCostProxy does all the heavy lifting of diffing
// its gets it data from a Model with baseline cost and the file to diff cost in alternating columns
// this proxy return for every even column the base cost and for every uneven the calculated diff cost
// this simplifies the other models, since we don't need to add this logic there
template<typename Model>
class DiffCostProxy : public CostProxy<Model>
{
Expand All @@ -62,23 +95,25 @@ class DiffCostProxy : public CostProxy<Model>
const auto baseColumn = (index.column() - Model::NUM_BASE_COLUMNS) / 2;
const auto column = baseColumn + (index.column() - Model::NUM_BASE_COLUMNS) % 2;

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

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

if (column == baseColumn) {
if (role == Model::TotalCostRole) {
return totalCost(column);
} else if (role == Model::SortRole) {
return cost(column) / totalCost(column);
return cost(column);
} else if (role == Qt::DisplayRole) {
return Util::formatCostRelative(cost(column), totalCost(column), true);
}
} else {
if (role == Model::TotalCostRole) {
return cost(baseColumn);
} else if (role == Model::SortRole) {
return cost(column) / cost(baseColumn);
if (cost(baseColumn) == 0)
return 0;
return cost(column);
} else if (role == Qt::DisplayRole) {
return Util::formatCostRelative(cost(column), cost(baseColumn), true);
}
Expand Down
58 changes: 51 additions & 7 deletions src/models/data.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -281,13 +281,14 @@ void buildPerLibrary(const TopDown* node, PerLibraryResults& results, QHash<QStr
}
}

void diffBottomUpResults(const BottomUp& a, const BottomUp* b, BottomUp* result_node, const Costs& costs_a,
const Costs& costs_b, Costs* costs_result)
template<typename ResultType, bool addResultNode = true>
void diffResults(const ResultType& a, const ResultType* b, ResultType* result_node, const Costs& costs_a,
const Costs& costs_b, Costs* costs_result)
{
for (const auto& node : a.children) {
const auto sibling = b->entryForSymbol(node.symbol);
if (sibling) {
BottomUp diffed;
ResultType diffed;
diffed.id = node.id;
diffed.symbol = node.symbol;

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

result_node->children.push_back(diffed);
diffBottomUpResults(node, sibling, &result_node->children.back(), costs_a, costs_b, costs_result);
if (addResultNode) {
result_node->children.push_back(diffed);
}
diffResults(node, sibling, &result_node->children.back(), costs_a, costs_b, costs_result);
}
}
}
Expand All @@ -309,7 +312,7 @@ QString Data::prettifySymbol(const QString& name)
return result == name ? name : result;
}

TopDownResults TopDownResults::fromBottomUp(const BottomUpResults& bottomUpData, bool skipFirstLevel)
TopDownResults Data::TopDownResults::fromBottomUp(const BottomUpResults& bottomUpData, bool skipFirstLevel)
{
TopDownResults results;
results.selfCosts.initializeCostsFrom(bottomUpData.costs);
Expand Down Expand Up @@ -443,9 +446,50 @@ Data::BottomUpResults BottomUpResults::diffBottomUpResults(const Data::BottomUpR
results.costs.addTotalCost(costBType, b.costs.totalCost(0));
}

::diffBottomUpResults(a.root, &b.root, &results.root, a.costs, b.costs, &results.costs);
diffResults(a.root, &b.root, &results.root, a.costs, b.costs, &results.costs);

BottomUp::initializeParents(&results.root);

return results;
}

TopDownResults TopDownResults::diffTopDownResults(const TopDownResults& a, const TopDownResults& b)
{
if (a.selfCosts.numTypes() != b.selfCosts.numTypes()) {
return {};
}

TopDownResults results;

for (int i = 0; i < a.selfCosts.numTypes(); i++) {
// only diff same type of costs
if (a.selfCosts.typeName(i) != b.selfCosts.typeName(i)) {
return {};
}

results.selfCosts.addType(2 * i, QLatin1String("baseline %1").arg(a.selfCosts.typeName(i)),
a.selfCosts.unit(i));
results.selfCosts.addTotalCost(2 * i, a.selfCosts.totalCost(i));

results.inclusiveCosts.addType(2 * i, QLatin1String("baseline %1").arg(a.inclusiveCosts.typeName(i)),
a.inclusiveCosts.unit(i));
results.inclusiveCosts.addTotalCost(2 * i, a.inclusiveCosts.totalCost(i));

const auto costBType = 2 * i + 1;
results.selfCosts.addType(costBType, QLatin1String("ratio of %1").arg(b.selfCosts.typeName(i)),
Costs::Unit::Unknown);
results.selfCosts.addTotalCost(costBType, b.selfCosts.totalCost(0));

results.inclusiveCosts.addType(costBType, QLatin1String("ratio of %1").arg(b.inclusiveCosts.typeName(i)),
Costs::Unit::Unknown);
results.inclusiveCosts.addTotalCost(costBType, b.inclusiveCosts.totalCost(0));
}

diffResults(a.root, &b.root, &results.root, a.selfCosts, b.selfCosts, &results.selfCosts);
diffResults<TopDown, false>(a.root, &b.root, &results.root, a.inclusiveCosts, b.inclusiveCosts,
&results.inclusiveCosts);

Data::TopDown::initializeParents(&results.root);

return results;
}
4 changes: 3 additions & 1 deletion src/models/data.h
Original file line number Diff line number Diff line change
Expand Up @@ -526,7 +526,9 @@ struct TopDownResults
TopDown root;
Costs selfCosts;
Costs inclusiveCosts;
static TopDownResults fromBottomUp(const Data::BottomUpResults& bottomUpData, bool skipFirstLevel);
static TopDownResults fromBottomUp(const Data::BottomUpResults& bottomUpData, bool skipFirestLevel);

static TopDownResults diffTopDownResults(const Data::TopDownResults& a, const Data::TopDownResults& b);
};

struct PerLibrary : SymbolTree<PerLibrary>
Expand Down
35 changes: 16 additions & 19 deletions src/resultsbottomuppage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,24 +85,21 @@ void ResultsBottomUpPage::setBottomUpResults(const Data::BottomUpResults& result
m_model->setData(results);
ResultsUtil::hideEmptyColumns(results.costs, ui->bottomUpTreeView, BottomUpModel::NUM_BASE_COLUMNS);

{
auto stackCollapsed =
m_exportMenu->addMenu(QIcon::fromTheme(QStringLiteral("text-plain")), tr("Stack Collapsed"));
stackCollapsed->setToolTip(tr("Export data in textual form compatible with <tt>flamegraph.pl</tt>."));
for (int i = 0; i < results.costs.numTypes(); ++i) {
const auto costName = results.costs.typeName(i);
stackCollapsed->addAction(costName, [this, i, costName]() {
const auto fileName = QFileDialog::getSaveFileName(this, tr("Export %1 Data").arg(costName));
if (fileName.isEmpty())
return;
QFile file(fileName);
if (!file.open(QIODevice::Text | QIODevice::WriteOnly)) {
QMessageBox::warning(this, tr("Failed to export data"),
tr("Failed to export stack collapsed data:\n%1").arg(file.errorString()));
return;
}
stackCollapsedExport(file, i, m_model->results());
});
}
auto stackCollapsed = m_exportMenu->addMenu(QIcon::fromTheme(QStringLiteral("text-plain")), tr("Stack Collapsed"));
stackCollapsed->setToolTip(tr("Export data in textual form compatible with <tt>flamegraph.pl</tt>."));
for (int i = 0; i < results.costs.numTypes(); ++i) {
const auto costName = results.costs.typeName(i);
stackCollapsed->addAction(costName, [this, i, costName]() {
const auto fileName = QFileDialog::getSaveFileName(this, tr("Export %1 Data").arg(costName));
if (fileName.isEmpty())
return;
QFile file(fileName);
if (!file.open(QIODevice::Text | QIODevice::WriteOnly)) {
QMessageBox::warning(this, tr("Failed to export data"),
tr("Failed to export stack collapsed data:\n%1").arg(file.errorString()));
return;
}
stackCollapsedExport(file, i, m_model->results());
});
}
}
2 changes: 1 addition & 1 deletion src/resultsbottomuppage.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class ResultsBottomUpPage;

namespace Data {
struct Symbol;
class BottomUpResults;
struct BottomUpResults;
}

class QTreeView;
Expand Down
9 changes: 8 additions & 1 deletion src/resultspagediff.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,16 @@ ResultsPageDiff::ResultsPageDiff(QWidget* parent)
}

connect(this, &ResultsPageDiff::parsingFinished, this, [this] {
const auto bottomUpData =
auto bottomUpData =
Data::BottomUpResults::diffBottomUpResults(m_fileA->bottomUpResults(), m_fileB->bottomUpResults());
m_resultsBottomUpPage->setBottomUpResults(bottomUpData);

auto skipFirstLevel = Settings::instance()->costAggregation() == Settings::CostAggregation::BySymbol;

auto topDownData = Data::TopDownResults::diffTopDownResults(
Data::TopDownResults::fromBottomUp(m_fileA->bottomUpResults(), skipFirstLevel),
Data::TopDownResults::fromBottomUp(m_fileB->bottomUpResults(), skipFirstLevel));
m_resultsTopDownPage->setTopDownResults(topDownData);
});

{
Expand Down
60 changes: 32 additions & 28 deletions src/resultstopdownpage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "resultstopdownpage.h"
#include "ui_resultstopdownpage.h"

#include "data.h"
#include "parsers/perf/perfparser.h"
#include "resultsutil.h"

Expand All @@ -18,38 +19,16 @@
ResultsTopDownPage::ResultsTopDownPage(FilterAndZoomStack* filterStack, PerfParser* parser,
CostContextMenu* contextMenu, QWidget* parent)
: QWidget(parent)
, m_model(new TopDownModel(this))
, ui(new Ui::ResultsTopDownPage)
{
ui->setupUi(this);

auto topDownCostModel = new TopDownModel(this);
ResultsUtil::setupTreeView(ui->topDownTreeView, contextMenu, ui->topDownSearch, topDownCostModel);
ResultsUtil::setupCostDelegate(topDownCostModel, ui->topDownTreeView);
ResultsUtil::setupContextMenu(ui->topDownTreeView, contextMenu, topDownCostModel, filterStack, this);

connect(parser, &PerfParser::topDownDataAvailable, this,
[this, topDownCostModel](const Data::TopDownResults& data) {
topDownCostModel->setData(data);
ResultsUtil::hideEmptyColumns(data.inclusiveCosts, ui->topDownTreeView, TopDownModel::NUM_BASE_COLUMNS);

ResultsUtil::hideEmptyColumns(data.selfCosts, ui->topDownTreeView,
TopDownModel::NUM_BASE_COLUMNS + data.inclusiveCosts.numTypes());
ResultsUtil::hideTracepointColumns(data.selfCosts, ui->topDownTreeView,
TopDownModel::NUM_BASE_COLUMNS + data.inclusiveCosts.numTypes());

// hide self cost columns for sched:sched_switch and off-CPU
// quasi all rows will have a cost of 0%, and only the leaves will show
// a non-zero value that is equal to the inclusive cost then
const auto costs = data.inclusiveCosts.numTypes();
const auto schedSwitchName = QLatin1String("sched:sched_switch");
const auto offCpuName = PerfParser::tr("off-CPU Time");
for (int i = 0; i < costs; ++i) {
const auto typeName = data.inclusiveCosts.typeName(i);
if (typeName == schedSwitchName || typeName == offCpuName) {
ui->topDownTreeView->hideColumn(topDownCostModel->selfCostColumn(i));
}
}
});
ResultsUtil::setupTreeViewDiff(ui->topDownTreeView, contextMenu, ui->topDownSearch, m_model);
ResultsUtil::setupCostDelegate(m_model, ui->topDownTreeView);
ResultsUtil::setupContextMenu(ui->topDownTreeView, contextMenu, m_model, filterStack, this);

connect(parser, &PerfParser::topDownDataAvailable, this, &ResultsTopDownPage::setTopDownResults);

ResultsUtil::setupResultsAggregation(ui->costAggregationComboBox);
}
Expand All @@ -60,3 +39,28 @@ void ResultsTopDownPage::clear()
{
ui->topDownSearch->setText({});
}

void ResultsTopDownPage::setTopDownResults(const Data::TopDownResults& data)
{
m_model->setData(data);
ResultsUtil::hideEmptyColumns(data.inclusiveCosts, ui->topDownTreeView, TopDownModel::NUM_BASE_COLUMNS);

ResultsUtil::hideEmptyColumns(data.selfCosts, ui->topDownTreeView,
TopDownModel::NUM_BASE_COLUMNS + data.inclusiveCosts.numTypes());
ResultsUtil::hideTracepointColumns(data.selfCosts, ui->topDownTreeView,
TopDownModel::NUM_BASE_COLUMNS + data.inclusiveCosts.numTypes());

// hide self cost columns for sched:sched_switch and off-CPU
// quasi all rows will have a cost of 0%, and only the leaves will show
// a non-zero value that is equal to the inclusive cost then
const auto costs = data.inclusiveCosts.numTypes();
const auto schedSwitchName = QLatin1String("sched:sched_switch");
const auto offCpuName = PerfParser::tr("off-CPU Time");
for (int i = 0; i < costs; ++i) {
const auto typeName = data.inclusiveCosts.typeName(i);
// use contains to also work in diff view
if (typeName.contains(schedSwitchName) || typeName.contains(offCpuName)) {
ui->topDownTreeView->hideColumn(m_model->selfCostColumn(i));
}
}
}
6 changes: 6 additions & 0 deletions src/resultstopdownpage.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@ class ResultsTopDownPage;

namespace Data {
struct Symbol;
struct TopDownResults;
}

class QTreeView;

class PerfParser;
class FilterAndZoomStack;
class CostContextMenu;
class TopDownModel;

class ResultsTopDownPage : public QWidget
{
Expand All @@ -34,12 +36,16 @@ class ResultsTopDownPage : public QWidget

void clear();

public slots:
void setTopDownResults(const Data::TopDownResults& data);

signals:
void jumpToCallerCallee(const Data::Symbol& symbol);
void openEditor(const Data::Symbol& symbol);
void selectSymbol(const Data::Symbol& symbol);
void jumpToDisassembly(const Data::Symbol& symbol);

private:
TopDownModel* m_model = nullptr;
QScopedPointer<Ui::ResultsTopDownPage> ui;
};

0 comments on commit 1cf5b84

Please sign in to comment.