From 7d309632712e9ea02ce16a3a63c72da5fbea4870 Mon Sep 17 00:00:00 2001 From: Lieven Hey Date: Tue, 23 May 2023 15:26:13 +0200 Subject: [PATCH] add detection for inlined code in disassembly this adds a dector to detect and hide inlined code in the disassembly --- src/models/CMakeLists.txt | 1 + src/models/disassemblyentry.cpp | 99 +++++++++++++++++++++++ src/models/disassemblyentry.h | 93 +++++++++++++++++++++ src/models/disassemblymodel.cpp | 133 ++++++++++++++++++++++++------- src/models/disassemblymodel.h | 9 ++- src/models/disassemblyoutput.cpp | 7 ++ 6 files changed, 313 insertions(+), 29 deletions(-) create mode 100644 src/models/disassemblyentry.cpp create mode 100644 src/models/disassemblyentry.h diff --git a/src/models/CMakeLists.txt b/src/models/CMakeLists.txt index faa31587..8d7caaa2 100644 --- a/src/models/CMakeLists.txt +++ b/src/models/CMakeLists.txt @@ -7,6 +7,7 @@ add_library( codedelegate.cpp costdelegate.cpp data.cpp + disassemblyentry.cpp disassemblymodel.cpp disassemblyoutput.cpp eventmodel.cpp diff --git a/src/models/disassemblyentry.cpp b/src/models/disassemblyentry.cpp new file mode 100644 index 00000000..244448f5 --- /dev/null +++ b/src/models/disassemblyentry.cpp @@ -0,0 +1,99 @@ +/* + SPDX-FileCopyrightText: Lieven Hey + SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#include "disassemblyentry.h" + +DisassemblyEntry::DisassemblyEntry(DisassemblyEntry* parent, int row, + const DisassemblyOutput::DisassemblyLine& disassemblyLine, QTextLine textLine) + : m_parent(parent) + , m_disassemblyLine(disassemblyLine) + , m_textLine(textLine) + , m_row(row) +{ +} + +DisassemblyEntry* DisassemblyEntry::lastChild() +{ + if (m_lines.isEmpty()) { + return nullptr; + } + + return &m_lines[m_lines.size() - 1]; +} + +void DisassemblyEntry::addChild(const DisassemblyEntry& line) +{ + m_lines.push_back(line); +} + +void DisassemblyEntry::clear() +{ + m_lines.clear(); +} + +DisassemblyEntry::Iterator::Iterator(const DisassemblyEntry* entry, int child) + : m_entry(const_cast(entry)) + , m_child(child) +{ +} + +DisassemblyEntry& DisassemblyEntry::Iterator::operator*() const +{ + if (m_child == -1) { + return *m_entry; + } + return *m_entry->child(m_child); +} + +DisassemblyEntry* DisassemblyEntry::Iterator::operator->() const +{ + if (m_child == -1) { + return m_entry; + } + return m_entry->child(m_child); +} + +DisassemblyEntry::Iterator& DisassemblyEntry::Iterator::operator++() +{ + if (m_child == -1) { + m_entry++; + if (m_entry->childCount() > 0) { + m_child = 0; + } else { + m_child = -1; + } + return *this; + } + + m_child++; + + if (m_child >= m_entry->childCount()) { + m_entry++; + if (m_entry->childCount() > 0) { + m_child = 0; + } else { + m_child = -1; + } + } + + return *this; +} + +bool DisassemblyEntry::Iterator::operator==(const DisassemblyEntry::Iterator& other) const +{ + return this->m_entry == other.m_entry && this->m_child == other.m_child; +} + +DisassemblyEntry::Iterator DisassemblyEntry::begin() const +{ + return Iterator(&m_lines[0], m_lines[0].childCount() == 0 ? -1 : 0); +} + +DisassemblyEntry::Iterator DisassemblyEntry::end() const +{ + return Iterator(m_lines.end(), -1); +} diff --git a/src/models/disassemblyentry.h b/src/models/disassemblyentry.h new file mode 100644 index 00000000..781a202f --- /dev/null +++ b/src/models/disassemblyentry.h @@ -0,0 +1,93 @@ +/* + SPDX-FileCopyrightText: Lieven Hey + SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#pragma once + +#include "disassemblyoutput.h" +#include +#include +#include + +class DisassemblyEntry +{ +public: + DisassemblyEntry(DisassemblyEntry* parent, int row, const DisassemblyOutput::DisassemblyLine& disassemblyLine = {}, + QTextLine textLine = {}); + + ~DisassemblyEntry() = default; + + DisassemblyEntry* parent() const + { + return m_parent; + } + + int row() const + { + return m_row; + } + + DisassemblyEntry* child(int row) + { + return &m_lines[row]; + } + + DisassemblyEntry* lastChild(); + + DisassemblyOutput::DisassemblyLine disassemblyLine() const + { + return m_disassemblyLine; + } + + QTextLine textLine() const + { + return m_textLine; + } + + int childCount() const + { + return m_lines.size(); + } + + void clear(); + void addChild(const DisassemblyEntry& line); + + class Iterator : public std::iterator + { + public: + Iterator(const DisassemblyEntry* entry, int child); + DisassemblyEntry& operator*() const; + DisassemblyEntry* operator->() const; + + Iterator& operator++(); + Iterator operator++(int) + { + Iterator tmp = *this; + ++(*this); + return tmp; + } + + bool operator==(const Iterator& other) const; + bool operator!=(const Iterator& other) const + { + return !(*this == other); + } + + private: + DisassemblyEntry* m_entry = nullptr; + int m_child = -1; + }; + + Iterator begin() const; + Iterator end() const; + +private: + DisassemblyEntry* m_parent; + QVector m_lines; + DisassemblyOutput::DisassemblyLine m_disassemblyLine; + QTextLine m_textLine; + int m_row; +}; diff --git a/src/models/disassemblymodel.cpp b/src/models/disassemblymodel.cpp index f4999b9c..21ca9ccf 100644 --- a/src/models/disassemblymodel.cpp +++ b/src/models/disassemblymodel.cpp @@ -17,9 +17,10 @@ #include "sourcecodemodel.h" DisassemblyModel::DisassemblyModel(KSyntaxHighlighting::Repository* repository, QObject* parent) - : QAbstractTableModel(parent) + : QAbstractItemModel(parent) , m_document(new QTextDocument(this)) , m_highlighter(new Highlighter(m_document, repository, this)) + , m_disassemblyLines(nullptr, 0) { m_document->setUndoRedoEnabled(false); } @@ -38,15 +39,58 @@ QModelIndex DisassemblyModel::findIndexWithOffset(int offset) quint64 address = m_data.disassemblyLines[0].addr + offset; const auto& found = - std::find_if(m_data.disassemblyLines.begin(), m_data.disassemblyLines.end(), - [address](const DisassemblyOutput::DisassemblyLine& line) { return line.addr == address; }); + std::find_if(m_disassemblyLines.begin(), m_disassemblyLines.end(), + [address](const DisassemblyEntry& entry) { return entry.disassemblyLine().addr == address; }); - if (found != m_data.disassemblyLines.end()) { - return createIndex(std::distance(m_data.disassemblyLines.begin(), found), DisassemblyColumn); + if (found != m_disassemblyLines.end()) { + return createIndex(std::distance(m_disassemblyLines.begin(), found), DisassemblyColumn); } + return {}; } +QModelIndex DisassemblyModel::parent(const QModelIndex& index) const +{ + if (!index.isValid()) { + return QModelIndex(); + } + + DisassemblyEntry* line = static_cast(index.internalPointer()); + + if (line == nullptr) { + return QModelIndex(); + } + + auto parentItem = line->parent(); + + if (parentItem == &m_disassemblyLines) { + return QModelIndex(); + } + + return createIndex(parentItem->row(), 0, parentItem); +} + +QModelIndex DisassemblyModel::index(int row, int column, const QModelIndex& parent) const +{ + if (!hasIndex(row, column, parent)) { + return QModelIndex(); + } + + DisassemblyEntry* parentItem; + + if (!parent.isValid()) { + parentItem = const_cast(&m_disassemblyLines); + } else { + parentItem = static_cast(parent.internalPointer()); + } + + if (row < parentItem->childCount()) { + return createIndex(row, column, parentItem->child(row)); + } else { + return QModelIndex(); + } +} + void DisassemblyModel::setDisassembly(const DisassemblyOutput& disassemblyOutput, const Data::CallerCalleeResults& results) { @@ -57,9 +101,11 @@ void DisassemblyModel::setDisassembly(const DisassemblyOutput& disassemblyOutput m_numTypes = results.selfCosts.numTypes(); m_document->clear(); + m_disassemblyLines.clear(); QTextCursor cursor(m_document); cursor.beginEditBlock(); + DisassemblyEntry* lastChild = nullptr; for (const auto& it : disassemblyOutput.disassemblyLines) { cursor.insertText(it.disassembly); cursor.insertBlock(); @@ -68,6 +114,30 @@ void DisassemblyModel::setDisassembly(const DisassemblyOutput& disassemblyOutput m_document->setTextWidth(m_document->idealWidth()); + int childRow = 0; + int row = 0; + int linecounter = 0; + for (const auto& it : disassemblyOutput.disassemblyLines) { + auto textLine = m_document->findBlockByLineNumber(linecounter++).layout()->lineAt(0); + + if (it.fileLine.file != disassemblyOutput.mainSourceFileName) { + if (lastChild) { + lastChild->addChild({lastChild, childRow, it, textLine}); + childRow++; + } else { + childRow = 1; + m_disassemblyLines.addChild({&m_disassemblyLines, row, {0, QLatin1String("[inlined]"), {}, {}}}); + lastChild = m_disassemblyLines.lastChild(); + Q_ASSERT(lastChild); + lastChild->addChild({lastChild, 0, it, textLine}); + } + } else { + lastChild = nullptr; + m_disassemblyLines.addChild({&m_disassemblyLines, row, it, textLine}); + row++; + } + } + endResetModel(); } @@ -107,29 +177,30 @@ QVariant DisassemblyModel::data(const QModelIndex& index, int role) const return static_cast(Qt::AlignLeft | Qt::AlignVCenter); } - const auto& data = m_data.disassemblyLines.at(index.row()); + DisassemblyEntry* entry = static_cast(index.internalPointer()); + Q_ASSERT(entry); + auto line = entry->disassemblyLine(); if (role == Qt::DisplayRole || role == CostRole || role == TotalCostRole || role == SyntaxHighlightRole || role == Qt::ToolTipRole) { if (role != Qt::ToolTipRole) { if (index.column() == AddrColumn) { - if (!data.addr) + if (!line.addr) return {}; - return QString::number(data.addr, 16); + return QString::number(line.addr, 16); } else if (index.column() == DisassemblyColumn) { - const auto block = m_document->findBlockByLineNumber(index.row()); if (role == SyntaxHighlightRole) - return QVariant::fromValue(block.layout()->lineAt(0)); - return block.text(); + return QVariant::fromValue(entry->textLine()); + return line.disassembly; } } - if (data.addr == 0) { + if (line.addr == 0) { return {}; } const auto entry = m_results.entries.value(m_data.symbol); - auto it = entry.offsetMap.find(data.addr); + auto it = entry.offsetMap.find(line.addr); if (it != entry.offsetMap.end()) { int event = index.column() - COLUMN_COUNT; @@ -143,7 +214,7 @@ QVariant DisassemblyModel::data(const QModelIndex& index, int role) const return totalCost; } else if (role == Qt::ToolTipRole) { auto tooltip = tr("addr: %1
assembly: %2
disassembly: %3") - .arg(QString::number(data.addr, 16), data.disassembly); + .arg(QString::number(line.addr, 16), line.disassembly); return Util::formatTooltip(tooltip, locationCost, m_results.selfCosts); } @@ -153,18 +224,18 @@ QVariant DisassemblyModel::data(const QModelIndex& index, int role) const } else { if (role == Qt::ToolTipRole) return tr("%1
No samples at this location.
") - .arg(data.disassembly.toHtmlEscaped()); + .arg(line.disassembly.toHtmlEscaped()); else return QString(); } } else if (role == DisassemblyModel::HighlightRole) { - return data.fileLine.line == m_highlightLine; + return line.fileLine.line == m_highlightLine; } else if (role == LinkedFunctionNameRole) { - return data.linkedFunction.name; + return line.linkedFunction.name; } else if (role == LinkedFunctionOffsetRole) { - return data.linkedFunction.offset; - } else if (role == RainbowLineNumberRole && data.addr) { - return data.fileLine.line; + return line.linkedFunction.offset; + } else if (role == RainbowLineNumberRole && line.addr) { + return line.fileLine.line; } return {}; @@ -172,12 +243,19 @@ QVariant DisassemblyModel::data(const QModelIndex& index, int role) const int DisassemblyModel::columnCount(const QModelIndex& parent) const { - return parent.isValid() ? 0 : COLUMN_COUNT + m_numTypes; + Q_UNUSED(parent); + return COLUMN_COUNT + m_numTypes; } int DisassemblyModel::rowCount(const QModelIndex& parent) const { - return parent.isValid() ? 0 : m_data.disassemblyLines.count(); + if (parent.column() > 0) + return 0; + if (parent.isValid()) { + auto item = static_cast(parent.internalPointer()); + return item->childCount(); + } + return m_disassemblyLines.childCount(); } void DisassemblyModel::updateHighlighting(int line) @@ -188,7 +266,8 @@ void DisassemblyModel::updateHighlighting(int line) Data::FileLine DisassemblyModel::fileLineForIndex(const QModelIndex& index) const { - return m_data.disassemblyLines[index.row()].fileLine; + auto entry = reinterpret_cast(index.internalPointer()); + return entry->disassemblyLine().fileLine; } QModelIndex DisassemblyModel::indexForFileLine(const Data::FileLine& fileLine) const @@ -197,9 +276,9 @@ QModelIndex DisassemblyModel::indexForFileLine(const Data::FileLine& fileLine) c int bestMatch = -1; qint64 bestCost = 0; const auto entry = m_results.entries.value(m_data.symbol); - for (const auto& line : m_data.disassemblyLines) { + for (const auto& line : m_disassemblyLines) { ++i; - if (line.fileLine != fileLine) { + if (line.disassemblyLine().fileLine != fileLine) { continue; } @@ -207,7 +286,7 @@ QModelIndex DisassemblyModel::indexForFileLine(const Data::FileLine& fileLine) c bestMatch = i; } - auto it = entry.offsetMap.find(line.addr); + auto it = entry.offsetMap.find(line.disassemblyLine().addr); if (it != entry.offsetMap.end()) { const auto& locationCost = it.value(); @@ -220,5 +299,5 @@ QModelIndex DisassemblyModel::indexForFileLine(const Data::FileLine& fileLine) c if (bestMatch == -1) return {}; - return index(bestMatch, 0); + return QModelIndex(); // index(bestMatch, 0); } diff --git a/src/models/disassemblymodel.h b/src/models/disassemblymodel.h index 45d2d648..380f0dde 100644 --- a/src/models/disassemblymodel.h +++ b/src/models/disassemblymodel.h @@ -9,10 +9,11 @@ #pragma once #include -#include +#include #include #include "data.h" +#include "disassemblyentry.h" #include "disassemblyoutput.h" class QTextDocument; @@ -23,7 +24,7 @@ class Definition; class Repository; } -class DisassemblyModel : public QAbstractTableModel +class DisassemblyModel : public QAbstractItemModel { Q_OBJECT public: @@ -35,6 +36,9 @@ class DisassemblyModel : public QAbstractTableModel void clear(); QModelIndex findIndexWithOffset(int offset); + QModelIndex parent(const QModelIndex& index) const override; + QModelIndex index(int row, int column, const QModelIndex& parent) const override; + int rowCount(const QModelIndex& parent = QModelIndex()) const override; int columnCount(const QModelIndex& parent = QModelIndex()) const override; @@ -73,6 +77,7 @@ public slots: private: QTextDocument* m_document; Highlighter* m_highlighter; + DisassemblyEntry m_disassemblyLines; DisassemblyOutput m_data; Data::CallerCalleeResults m_results; int m_numTypes = 0; diff --git a/src/models/disassemblyoutput.cpp b/src/models/disassemblyoutput.cpp index 68b013ec..b2e06ee9 100644 --- a/src/models/disassemblyoutput.cpp +++ b/src/models/disassemblyoutput.cpp @@ -153,6 +153,13 @@ static ObjectdumpOutput objdumpParse(const QByteArray& output) continue; } + // inlining create lines like these + // std::ostream::operator<<(std::ostream& (*)(std::ostream&)): + // we want to skip those + if (asmLine.endsWith(QLatin1Char(':'))) { + continue; + } + // we don't care about the file name if (asmLine.startsWith(QLatin1Char('/')) && asmLine.contains(QStringLiteral("file format"))) { continue;