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..845e9bf2 --- /dev/null +++ b/src/models/disassemblyentry.cpp @@ -0,0 +1,36 @@ +/* + 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(); +} diff --git a/src/models/disassemblyentry.h b/src/models/disassemblyentry.h new file mode 100644 index 00000000..f2f637c5 --- /dev/null +++ b/src/models/disassemblyentry.h @@ -0,0 +1,63 @@ +/* + 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 + +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); + +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 015cd62c..066918b7 100644 --- a/src/models/disassemblymodel.cpp +++ b/src/models/disassemblymodel.cpp @@ -18,9 +18,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); } @@ -48,6 +49,43 @@ QModelIndex DisassemblyModel::findIndexWithOffset(int offset) return {}; } +QModelIndex DisassemblyModel::parent(const QModelIndex& index) const +{ + if (!index.isValid()) { + return QModelIndex(); + } + + DisassemblyEntry* line = static_cast(index.internalPointer()); + 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) { @@ -58,12 +96,34 @@ 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; + int childRow = 0; + int row = 0; for (const auto& it : disassemblyOutput.disassemblyLines) { cursor.insertText(it.disassembly); + auto textLine = cursor.block().layout()->lineAt(0); cursor.insertBlock(); + + 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}); + } + } else { + lastChild = nullptr; + m_disassemblyLines.addChild({&m_disassemblyLines, row, it, textLine}); + row++; + } } cursor.endEditBlock(); @@ -108,29 +168,32 @@ 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()); + // 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; @@ -144,7 +207,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); } @@ -154,18 +217,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 {}; @@ -173,18 +236,27 @@ 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(); + // 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) { m_highlightLine = line; - emit dataChanged(createIndex(0, Columns::DisassemblyColumn), createIndex(rowCount(), Columns::DisassemblyColumn)); + // emit dataChanged(createIndex(0, Columns::DisassemblyColumn), createIndex(rowCount(), + // Columns::DisassemblyColumn)); } Data::FileLine DisassemblyModel::fileLineForIndex(const QModelIndex& index) const @@ -221,7 +293,7 @@ QModelIndex DisassemblyModel::indexForFileLine(const Data::FileLine& fileLine) c if (bestMatch == -1) return {}; - return index(bestMatch, 0); + return QModelIndex(); // index(bestMatch, 0); } void DisassemblyModel::find(const QString& search, Direction direction, int offset) diff --git a/src/models/disassemblymodel.h b/src/models/disassemblymodel.h index 1da6f1db..f5a69d76 100644 --- a/src/models/disassemblymodel.h +++ b/src/models/disassemblymodel.h @@ -13,6 +13,7 @@ #include #include "data.h" +#include "disassemblyentry.h" #include "disassemblyoutput.h" class QTextDocument; @@ -25,7 +26,7 @@ class Repository; enum class Direction; -class DisassemblyModel : public QAbstractTableModel +class DisassemblyModel : public QAbstractItemModel { Q_OBJECT public: @@ -37,6 +38,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; @@ -80,6 +84,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;