Skip to content

Commit

Permalink
add diff view between two files
Browse files Browse the repository at this point in the history
closes #37
  • Loading branch information
lievenhey committed Nov 5, 2021
1 parent 9a136cc commit 50d15b4
Show file tree
Hide file tree
Showing 17 changed files with 570 additions and 8 deletions.
2 changes: 2 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ set(hotspot_SRCS
perfoutputwidget.cpp
perfoutputwidgettext.cpp
perfoutputwidgetkonsole.cpp
diffviewwidget.cpp

# ui files:
mainwindow.ui
Expand All @@ -65,6 +66,7 @@ set(hotspot_SRCS
settingsdialog.ui
flamegraphsettings.ui
debuginfoddialog.ui
resultsdiffpage.ui

# resources:
resources.qrc
Expand Down
132 changes: 132 additions & 0 deletions src/diffviewwidget.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/*
diffviewwidget.cpp
This file is part of Hotspot, the Qt GUI for performance analysis.
Copyright (C) 2021 Klarälvdalens Datakonsult AB, a KDAB Group company, [email protected]
Author: Lieven Hey <[email protected]>
Licensees holding valid commercial KDAB Hotspot licenses may use this file in
accordance with Hotspot Commercial License Agreement provided with the Software.
Contact [email protected] if any conditions of this licensing are not clear to you.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU 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, see <http://www.gnu.org/licenses/>.
*/

#include "diffviewwidget.h"

#include <QLabel>
#include <QProgressBar>

#include "dockwidgetsetup.h"
#include "filterandzoomstack.h"
#include "models/treemodel.h"
#include "parsers/perf/perfparser.h"
#include "resultsutil.h"
#include "settings.h"
#include "timelinewidget.h"

#include "ui_resultsdiffpage.h"

DiffViewWidget::DiffViewWidget(QWidget* parent)
: QWidget(parent)
, ui(new Ui::ResultsDiffPage)
, m_parserA(new PerfParser(this))
, m_parserB(new PerfParser(this))
, m_model(new DiffViewModel(this))
{
ui->setupUi(this);
;
ui->diffTreeView->setModel(m_model);
ui->diffTreeView->sortByColumn(DiffViewModel::InitialSortColumn, Qt::DescendingOrder);
ResultsUtil::setupCostDelegate<DiffViewModel>(m_model, ui->diffTreeView);

auto repositionFilterBusyIndicator = [this] {
auto geometry = m_filterBusyIndicator->geometry();
geometry.setWidth(width() / 2);
geometry.moveCenter(rect().center());
m_filterBusyIndicator->setGeometry(geometry);
};

connect(m_parserA, &PerfParser::parsingStarted, this, [this, repositionFilterBusyIndicator] {
repositionFilterBusyIndicator();
m_filterBusyIndicator->setVisible(true);
});

connect(m_parserB, &PerfParser::parsingStarted, this, [this, repositionFilterBusyIndicator] {
repositionFilterBusyIndicator();
m_filterBusyIndicator->setVisible(true);
});

connect(m_parserA, &PerfParser::parsingFinished, this, [this] {
m_aFinished = true;
if (m_bFinished) {
m_filterBusyIndicator->setVisible(false);
}
});

connect(m_parserB, &PerfParser::parsingFinished, this, [this] {
m_bFinished = true;
if (m_aFinished) {
m_filterBusyIndicator->setVisible(false);
}
});

connect(m_parserA, &PerfParser::topDownDataAvailable, this, [this](const Data::TopDownResults& results) {
m_resultsA = results;

const auto data = Data::DiffViewResults::fromTopDown(m_resultsA, m_resultsB);

if (data.costsA.numTypes() > 0 && data.costsB.numTypes() > 0) {
m_model->setData(data);
}
});

connect(m_parserB, &PerfParser::topDownDataAvailable, this, [this](const Data::TopDownResults& results) {
m_resultsB = results;

const auto data = Data::DiffViewResults::fromTopDown(m_resultsA, m_resultsB);

if (data.costsA.numTypes() > 0 && data.costsB.numTypes() > 0) {
m_model->setData(data);
}
});

{
m_filterBusyIndicator = new QWidget(this);
m_filterBusyIndicator->setMinimumHeight(100);
m_filterBusyIndicator->setVisible(false);
m_filterBusyIndicator->setToolTip(tr("Filtering in progress, please wait..."));
auto layout = new QVBoxLayout(m_filterBusyIndicator);
layout->setAlignment(Qt::AlignCenter);
auto progressBar = new QProgressBar(m_filterBusyIndicator);
layout->addWidget(progressBar);
progressBar->setMaximum(0);
auto label = new QLabel(m_filterBusyIndicator->toolTip(), m_filterBusyIndicator);
label->setAlignment(Qt::AlignCenter);
layout->addWidget(label);
}
}

DiffViewWidget::~DiffViewWidget() = default;

void DiffViewWidget::open(const QString& a, const QString& b)
{
auto settings = Settings::instance();
m_parserA->startParseFile(a, settings->sysroot(), settings->kallsyms(), settings->debugPaths(),
settings->extraLibPaths(), settings->appPath(), settings->arch());
m_parserB->startParseFile(b, settings->sysroot(), settings->kallsyms(), settings->debugPaths(),
settings->extraLibPaths(), settings->appPath(), settings->arch());
}
73 changes: 73 additions & 0 deletions src/diffviewwidget.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
diffviewwidget.h
This file is part of Hotspot, the Qt GUI for performance analysis.
Copyright (C) 2021 Klarälvdalens Datakonsult AB, a KDAB Group company, [email protected]
Author: Lieven Hey <[email protected]>
Licensees holding valid commercial KDAB Hotspot licenses may use this file in
accordance with Hotspot Commercial License Agreement provided with the Software.
Contact [email protected] if any conditions of this licensing are not clear to you.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU 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, see <http://www.gnu.org/licenses/>.
*/

#pragma once

#include "data.h"
#include <QWidget>

class PerfParser;
class DiffViewModel;
class TimeLineWidget;
class FilterAndZoomStack;

namespace KDDockWidgets {
class MainWindow;
class DockWidget;
}

namespace Ui {
class ResultsDiffPage;
}

class DiffViewWidget : public QWidget
{
Q_OBJECT
public:
explicit DiffViewWidget(QWidget* parent = nullptr);
~DiffViewWidget();

public slots:
void open(const QString& a, const QString& b);

private:
QScopedPointer<Ui::ResultsDiffPage> ui;
PerfParser* m_parserA;
PerfParser* m_parserB;
DiffViewModel* m_model;
KDDockWidgets::MainWindow* m_contents;
KDDockWidgets::DockWidget* m_timelineDockA;
KDDockWidgets::DockWidget* m_timelineDockB;
TimeLineWidget* m_timelineA;
TimeLineWidget* m_timelineB;
QWidget* m_filterBusyIndicator = nullptr;
Data::TopDownResults m_resultsA;
Data::TopDownResults m_resultsB;

bool m_aFinished = false;
bool m_bFinished = false;
};
16 changes: 15 additions & 1 deletion src/mainwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,13 @@
*/

#include "mainwindow.h"
#include "diffviewwidget.h"
#include "recordpage.h"
#include "resultspage.h"
#include "settings.h"
#include "settingsdialog.h"
#include "startpage.h"
#include "ui_mainwindow.h"
#include "settingsdialog.h"
#include "ui_settingsdialog.h"

#include <QApplication>
Expand Down Expand Up @@ -115,12 +116,14 @@ MainWindow::MainWindow(QWidget* parent)
, m_recordPage(new RecordPage(this))
, m_resultsPage(new ResultsPage(m_parser, this))
, m_settingsDialog(new SettingsDialog(this))
, m_diffView(new DiffViewWidget(this))
{
ui->setupUi(this);

m_pageStack->addWidget(m_startPage);
m_pageStack->addWidget(m_resultsPage);
m_pageStack->addWidget(m_recordPage);
m_pageStack->addWidget(m_diffView);

QVBoxLayout* layout = new QVBoxLayout;
layout->setContentsMargins(0, 0, 0, 0);
Expand Down Expand Up @@ -148,6 +151,7 @@ MainWindow::MainWindow(QWidget* parent)
connect(m_startPage, &StartPage::recordButtonClicked, this, &MainWindow::onRecordButtonClicked);
connect(m_startPage, &StartPage::stopParseButtonClicked, this,
static_cast<void (MainWindow::*)()>(&MainWindow::clear));
connect(m_startPage, &StartPage::diffFilesButtonClicked, this, &MainWindow::onDiffButtonClicked);
connect(m_parser, &PerfParser::progress, m_startPage, &StartPage::onParseFileProgress);
connect(this, &MainWindow::openFileError, m_startPage, &StartPage::onOpenFileError);
connect(m_recordPage, &RecordPage::homeButtonClicked, this, &MainWindow::onHomeButtonClicked);
Expand Down Expand Up @@ -335,6 +339,16 @@ void MainWindow::onHomeButtonClicked()
m_pageStack->setCurrentWidget(m_startPage);
}

void MainWindow::onDiffButtonClicked()
{
const auto file1 = QFileDialog::getOpenFileName(this, tr("Open File"), QDir::currentPath(),
tr("Data Files (perf*.data perf.data.*);;All Files (*)"));
const auto file2 = QFileDialog::getOpenFileName(this, tr("Open File"), QDir::currentPath(),
tr("Data Files (perf*.data perf.data.*);;All Files (*)"));
m_pageStack->setCurrentWidget(m_diffView);
m_diffView->open(file1, file2);
}

void MainWindow::onRecordButtonClicked()
{
clear();
Expand Down
3 changes: 3 additions & 0 deletions src/mainwindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class StartPage;
class ResultsPage;
class RecordPage;
class SettingsDialog;
class DiffViewWidget;

class MainWindow : public KParts::MainWindow
{
Expand All @@ -65,6 +66,7 @@ public slots:
void onOpenFileButtonClicked();
void onRecordButtonClicked();
void onHomeButtonClicked();
void onDiffButtonClicked();

void aboutKDAB();
void openSettingsDialog();
Expand All @@ -90,6 +92,7 @@ public slots:
RecordPage* m_recordPage;
ResultsPage* m_resultsPage;
SettingsDialog* m_settingsDialog;
DiffViewWidget* m_diffView;

QString m_lastUsedSettings;
KRecentFilesAction* m_recentFilesAction = nullptr;
Expand Down
22 changes: 16 additions & 6 deletions src/models/costdelegate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,15 @@

#include <cmath>

float clamp(float value, float low, float high)
{
if (value < low)
return low;
if (value > high)
return high;
return value;
}

CostDelegate::CostDelegate(quint32 sortRole, quint32 totalCostRole, QObject* parent)
: QStyledItemDelegate(parent)
, m_sortRole(sortRole)
Expand All @@ -43,18 +52,18 @@ CostDelegate::~CostDelegate() = default;

void CostDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
// TODO: handle negative values
const auto cost = index.data(m_sortRole).toULongLong();
auto cost = index.data(m_sortRole).toLongLong();
if (cost == 0) {
QStyledItemDelegate::paint(painter, option, index);
return;
}

const auto totalCost = index.data(m_totalCostRole).toULongLong();
const auto fraction = std::abs(float(cost) / totalCost);
const auto totalCost = index.data(m_totalCostRole).toLongLong();
const auto fraction = clamp(float(cost) / totalCost, -1, 1);
const auto absFraction = std::abs(fraction);

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

const auto& brush = painter->brush();
const auto& pen = painter->pen();
Expand All @@ -68,7 +77,8 @@ void CostDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option,
painter->drawRect(option.rect);
}

auto color = QColor::fromHsv(120 - fraction * 120, 255, 255, (-((fraction - 1) * (fraction - 1))) * 120 + 120);
auto color =
QColor::fromHsv(120 - fraction * 120, 255, 255, (-((absFraction - 1) * (absFraction - 1))) * 120 + 120);
painter->setBrush(color);
painter->drawRect(rect);

Expand Down
30 changes: 30 additions & 0 deletions src/models/data.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -353,3 +353,33 @@ const Data::ThreadEvents* Data::EventResults::findThread(qint32 pid, qint32 tid)
{
return const_cast<Data::EventResults*>(this)->findThread(pid, tid);
}

DiffViewResults DiffViewResults::fromTopDown(const Data::TopDownResults& a, const Data::TopDownResults& b)
{
DiffViewResults results;
results.costsA = a.selfCosts;
results.costsB = b.selfCosts;

std::function<void(const TopDown&, DiffView& parent)> iterateModels = [&iterateModels, b](const TopDown& node, DiffView& parent) {
DiffView diffView;
diffView.costIdA = node.id;
diffView.symbol = node.symbol;
const auto sibling = b.root.entryForSymbol(node.symbol);
if (sibling) {
diffView.costIdB = sibling->id;
}
parent.children.push_back(diffView);

for (const auto& child : node.children) {
iterateModels(child, parent.children.back());
}
};

for (const auto& entry : a.root.children) {
iterateModels(entry, results.root);
}

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

return results;
}
Loading

0 comments on commit 50d15b4

Please sign in to comment.