From d0b37e5fb447d4e0be407fa455de0e3aeb57fcd8 Mon Sep 17 00:00:00 2001 From: Lieven Hey Date: Wed, 10 Nov 2021 13:02:51 +0100 Subject: [PATCH] glue diffing on perfparser --- src/mainwindow.cpp | 19 ++++- src/mainwindow.h | 2 +- src/models/data.cpp | 40 +++++++++ src/models/data.h | 2 + src/parsers/perf/perfparser.cpp | 147 +++++++++++++++++++++----------- src/parsers/perf/perfparser.h | 5 +- src/startpage.cpp | 1 + src/startpage.h | 1 + src/startpage.ui | 41 +++++---- 9 files changed, 187 insertions(+), 71 deletions(-) diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index a576cbe68..233c8a0e8 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -148,6 +148,21 @@ MainWindow::MainWindow(QWidget* parent) connect(m_startPage, &StartPage::recordButtonClicked, this, &MainWindow::onRecordButtonClicked); connect(m_startPage, &StartPage::stopParseButtonClicked, this, static_cast(&MainWindow::clear)); + connect(m_startPage, &StartPage::diffButtonClicked, this, [this] { + const auto fileNameA = QFileDialog::getOpenFileName(this, tr("Open File A"), QDir::currentPath(), + tr("Data Files (perf*.data perf.data.*);;All Files (*)")); + if (fileNameA.isEmpty()) { + return; + } + + const auto fileNameB = QFileDialog::getOpenFileName(this, tr("Open File B"), QDir::currentPath(), + tr("Data Files (perf*.data perf.data.*);;All Files (*)")); + if (fileNameB.isEmpty()) { + return; + } + + openFile(fileNameA, false, fileNameB); + }); 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); @@ -363,7 +378,7 @@ void MainWindow::clear() clear(false); } -void MainWindow::openFile(const QString& path, bool isReload) +void MainWindow::openFile(const QString& path, bool isReload, const QString& diffFile) { clear(isReload); @@ -374,7 +389,7 @@ void MainWindow::openFile(const QString& path, bool isReload) m_pageStack->setCurrentWidget(m_startPage); // TODO: support input files of different types via plugins - m_parser->startParseFile(path); + m_parser->startParseFile(path, diffFile); m_reloadAction->setData(path); m_exportAction->setData(QUrl::fromLocalFile(file.absoluteFilePath() + QLatin1String(".perfparser"))); diff --git a/src/mainwindow.h b/src/mainwindow.h index 3fccb7771..4ffeb8eff 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -78,7 +78,7 @@ public slots: private: void clear(bool isReload); - void openFile(const QString& path, bool isReload); + void openFile(const QString& path, bool isReload, const QString& diffFile = QStringLiteral("")); void closeEvent(QCloseEvent* event) override; void setupCodeNavigationMenu(); diff --git a/src/models/data.cpp b/src/models/data.cpp index 05fa8637a..3af7bb608 100644 --- a/src/models/data.cpp +++ b/src/models/data.cpp @@ -295,6 +295,26 @@ void buildPerLibrary(const TopDown* node, PerLibraryResults& results, QHashentryForSymbol(node.symbol); + if (sibling) { + BottomUp diffed; + diffed.id = node.id; + diffed.symbol = node.symbol; + + for (int i = 0; i < costs_result->numTypes(); i += 2) { + costs_result->add(i, diffed.id, costs_a.cost(i, node.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); + } + } +} } QString Data::prettifySymbol(const QString& name) @@ -389,3 +409,23 @@ const Data::ThreadEvents* Data::EventResults::findThread(qint32 pid, qint32 tid) { return const_cast(this)->findThread(pid, tid); } + +BottomUpResults BottomUpResults::diffBottomUpResults(const BottomUpResults& a, const BottomUpResults& b) +{ + if (a.costs.numTypes() != b.costs.numTypes()) { + return {}; + } + + BottomUpResults results; + + for (int i = 0; i < a.costs.numTypes(); i++) { + results.costs.addType(i, a.costs.typeName(i), a.costs.unit(i)); + results.costs.addTotalCost(i , a.costs.totalCost(i) - b.costs.totalCost(i)); + } + + ::diffBottomUpResults(a.root, &b.root, &results.root, a.costs, b.costs, &results.costs); + + Data::BottomUp::initializeParents(&results.root); + + return results; +} diff --git a/src/models/data.h b/src/models/data.h index 88abc05af..4dc635236 100644 --- a/src/models/data.h +++ b/src/models/data.h @@ -423,6 +423,8 @@ struct BottomUpResults return parent; } + static BottomUpResults diffBottomUpResults(const Data::BottomUpResults& a, const Data::BottomUpResults& b); + private: quint32 maxBottomUpId = 0; diff --git a/src/parsers/perf/perfparser.cpp b/src/parsers/perf/perfparser.cpp index 55b254bf7..1f154f9dc 100644 --- a/src/parsers/perf/perfparser.cpp +++ b/src/parsers/perf/perfparser.cpp @@ -1332,7 +1332,7 @@ PerfParser::PerfParser(QObject* parent) PerfParser::~PerfParser() = default; -void PerfParser::startParseFile(const QString& path) +void PerfParser::startParseFile(const QString& path, const QString& diffFile) { Q_ASSERT(!m_isParsing); @@ -1358,6 +1358,7 @@ void PerfParser::startParseFile(const QString& path) auto parserArgs = [this](const QString& filename) { const auto settings = Settings::instance(); + QStringList parserArgs = {QStringLiteral("--input"), decompressIfNeeded(filename), QStringLiteral("--max-frames"), QStringLiteral("1024")}; const auto sysroot = settings->sysroot(); @@ -1405,7 +1406,7 @@ void PerfParser::startParseFile(const QString& path) emit parsingStarted(); using namespace ThreadWeaver; - stream() << make_job([path, parserBinary, parserArgs, env, this]() { + stream() << make_job([path, parserBinary, env, this]() { PerfParserPrivate d; connect(&d, &PerfParserPrivate::progress, this, &PerfParser::progress); connect(this, &PerfParser::stopRequested, &d, &PerfParserPrivate::stop); @@ -1451,55 +1452,55 @@ void PerfParser::startParseFile(const QString& path) d.setInput(&process); + const auto exitCodeHandler = [finalize, this](int exitCode, QProcess::ExitStatus exitStatus) { + if (m_stopRequested) { + emit parsingFailed(tr("Parsing stopped.")); + return; + } + qCDebug(LOG_PERFPARSER) << exitCode << exitStatus; + + enum ErrorCodes + { + NoError, + TcpSocketError, + CannotOpen, + BadMagic, + HeaderError, + DataError, + MissingData, + InvalidOption + }; + switch (exitCode) { + case NoError: + finalize(); + break; + case TcpSocketError: + emit parsingFailed( + tr("The hotspot-perfparser binary exited with code %1 (TCP socket error).").arg(exitCode)); + break; + case CannotOpen: + emit parsingFailed( + tr("The hotspot-perfparser binary exited with code %1 (file could not be opened).").arg(exitCode)); + break; + case BadMagic: + case HeaderError: + case DataError: + case MissingData: + emit parsingFailed( + tr("The hotspot-perfparser binary exited with code %1 (invalid perf data file).").arg(exitCode)); + break; + case InvalidOption: + emit parsingFailed( + tr("The hotspot-perfparser binary exited with code %1 (invalid option).").arg(exitCode)); + break; + default: + emit parsingFailed(tr("The hotspot-perfparser binary exited with code %1.").arg(exitCode)); + break; + } + }; + connect(&process, static_cast(&QProcess::finished), &process, - [finalize, this](int exitCode, QProcess::ExitStatus exitStatus) { - if (m_stopRequested) { - emit parsingFailed(tr("Parsing stopped.")); - return; - } - qCDebug(LOG_PERFPARSER) << exitCode << exitStatus; - - enum ErrorCodes - { - NoError, - TcpSocketError, - CannotOpen, - BadMagic, - HeaderError, - DataError, - MissingData, - InvalidOption - }; - switch (exitCode) { - case NoError: - finalize(); - break; - case TcpSocketError: - emit parsingFailed( - tr("The hotspot-perfparser binary exited with code %1 (TCP socket error).").arg(exitCode)); - break; - case CannotOpen: - emit parsingFailed( - tr("The hotspot-perfparser binary exited with code %1 (file could not be opened).") - .arg(exitCode)); - break; - case BadMagic: - case HeaderError: - case DataError: - case MissingData: - emit parsingFailed( - tr("The hotspot-perfparser binary exited with code %1 (invalid perf data file).") - .arg(exitCode)); - break; - case InvalidOption: - emit parsingFailed( - tr("The hotspot-perfparser binary exited with code %1 (invalid option).").arg(exitCode)); - break; - default: - emit parsingFailed(tr("The hotspot-perfparser binary exited with code %1.").arg(exitCode)); - break; - } - }); + exitCodeHandler); connect(&process, &QProcess::errorOccurred, &process, [&d, &process, this](QProcess::ProcessError error) { if (m_stopRequested) { @@ -1523,6 +1524,52 @@ void PerfParser::startParseFile(const QString& path) &QEventLoop::quit); loop.exec(); }); + + if (diffFile.isEmpty()) { + return; + } + + PerfParser otherData; + + otherData.startParseFile(diffFile); + QEventLoop loop; + connect(&otherData, &PerfParser::parsingFinished, &loop, &QEventLoop::quit); + connect(&otherData, &PerfParser::parsingFailed, &loop, &QEventLoop::quit); + connect(&otherData, &PerfParser::parsingFailed, this, [this](const QString& error) { emit parsingFailed(error); }); + + connect(&otherData, &PerfParser::bottomUpDataAvailable, &otherData, + [this, &otherData](const Data::BottomUpResults& otherResults) { + if (m_bottomUpResults.root.children.size() > 0) { + this->m_callerCalleeResults = {}; + + const auto diffResults = + Data::BottomUpResults::mergeBottomUpResults(m_bottomUpResults, otherResults); + m_bottomUpResults = diffResults; + const auto topDown = Data::TopDownResults::fromBottomUp(diffResults); + Data::CallerCalleeResults callerCalleeResults; + Data::callerCalleesFromBottomUpData(diffResults, &callerCalleeResults); + emit bottomUpDataAvailable(diffResults); + emit topDownDataAvailable(Data::TopDownResults::fromBottomUp(diffResults)); + emit callerCalleeDataAvailable(callerCalleeResults); + } else { + connect(this, &PerfParser::bottomUpDataAvailable, &otherData, + [this, &otherData, otherResults](const Data::BottomUpResults& results) { + this->m_bottomUpResults = {}; + this->m_callerCalleeResults = {}; + + const auto diffResults = + Data::BottomUpResults::mergeBottomUpResults(results, otherResults); + const auto topDown = Data::TopDownResults::fromBottomUp(diffResults); + Data::CallerCalleeResults callerCalleeResults; + Data::callerCalleesFromBottomUpData(diffResults, &callerCalleeResults); + emit bottomUpDataAvailable(diffResults); + emit topDownDataAvailable(Data::TopDownResults::fromBottomUp(diffResults)); + emit callerCalleeDataAvailable(callerCalleeResults); + }); + } + }); + + loop.exec(); } void PerfParser::filterResults(const Data::FilterAction& filter) diff --git a/src/parsers/perf/perfparser.h b/src/parsers/perf/perfparser.h index 33cad9df5..825cad651 100644 --- a/src/parsers/perf/perfparser.h +++ b/src/parsers/perf/perfparser.h @@ -44,7 +44,7 @@ class PerfParser : public QObject explicit PerfParser(QObject* parent = nullptr); ~PerfParser(); - void startParseFile(const QString& path); + void startParseFile(const QString& path, const QString& diffFile = QStringLiteral("")); void filterResults(const Data::FilterAction& filter); @@ -69,10 +69,13 @@ class PerfParser : public QObject void parsingStarted(); void summaryDataAvailable(const Data::Summary& data); void bottomUpDataAvailable(const Data::BottomUpResults& data); + void firstBottomUpDataAvailable(const Data::BottomUpResults& data); + void secondBottomUpDataAvailable(const Data::BottomUpResults& data); void topDownDataAvailable(const Data::TopDownResults& data); void perLibraryDataAvailable(const Data::PerLibraryResults& data); void callerCalleeDataAvailable(const Data::CallerCalleeResults& data); void eventsAvailable(const Data::EventResults& events); + void secondEventsAvailable(const Data::EventResults& events); void parsingFinished(); void parsingFailed(const QString& errorMessage); void progress(float progress); diff --git a/src/startpage.cpp b/src/startpage.cpp index b00f8a4c6..46de3e2c6 100644 --- a/src/startpage.cpp +++ b/src/startpage.cpp @@ -42,6 +42,7 @@ StartPage::StartPage(QWidget* parent) connect(ui->recordDataButton, &QAbstractButton::clicked, this, &StartPage::recordButtonClicked); connect(ui->stopParseButton, &QAbstractButton::clicked, this, &StartPage::stopParseButtonClicked); connect(ui->pathSettings, &QAbstractButton::clicked, this, &StartPage::pathSettingsButtonClicked); + connect(ui->diffButton, &QAbstractButton::clicked, this, &StartPage::diffButtonClicked); ui->openFileButton->setFocus(); updateBackground(); diff --git a/src/startpage.h b/src/startpage.h index 58c793c12..7dde41577 100644 --- a/src/startpage.h +++ b/src/startpage.h @@ -58,6 +58,7 @@ public slots: void recordButtonClicked(); void stopParseButtonClicked(); void pathSettingsButtonClicked(); + void diffButtonClicked(); private: void updateBackground(); diff --git a/src/startpage.ui b/src/startpage.ui index 59f924c4f..83635b117 100644 --- a/src/startpage.ui +++ b/src/startpage.ui @@ -7,7 +7,7 @@ 0 0 1081 - 340 + 414 @@ -71,49 +71,49 @@ - - + + - + 0 0 - Record perf data + Configure hotspot settings to properly load data files from embedded systems or remote machines - Record Data + ... - + .. - - true + + QToolButton::InstantPopup - - + + - + 0 0 - Configure hotspot settings to properly load data files from embedded systems or remote machines + Record perf data - ... + Record Data - + .. - - QToolButton::InstantPopup + + true @@ -134,6 +134,13 @@ + + + + Diff two Files + + +