Skip to content

Commit

Permalink
use QTextLayout for resultsdisassemblypage
Browse files Browse the repository at this point in the history
This patch replaces the usage of QTextDocument in the sourcecode and
disassembly view. QTextDocument is way to overkill for that job.
This patch also includes an highlighter based on ansi codes, which can
be used instead of KSyntaxHighlighting for objdump (if supported).
  • Loading branch information
lievenhey committed Oct 30, 2023
1 parent 163ea31 commit b4a785e
Show file tree
Hide file tree
Showing 16 changed files with 608 additions and 246 deletions.
3 changes: 2 additions & 1 deletion src/models/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ add_library(
disassemblyoutput.cpp
eventmodel.cpp
filterandzoomstack.cpp
formattingutils.cpp
frequencymodel.cpp
highlighter.cpp
highlightedtext.cpp
processfiltermodel.cpp
processlist_unix.cpp
processmodel.cpp
Expand Down
39 changes: 14 additions & 25 deletions src/models/disassemblymodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,15 @@
SPDX-License-Identifier: GPL-2.0-or-later
*/

#include <QFontDatabase>
#include <QTextBlock>
#include <QTextDocument>

#include "disassemblymodel.h"

#include "highlighter.hpp"
#include "search.h"
#include "sourcecodemodel.h"

DisassemblyModel::DisassemblyModel(KSyntaxHighlighting::Repository* repository, QObject* parent)
: QAbstractTableModel(parent)
, m_document(new QTextDocument(this))
, m_highlighter(new Highlighter(m_document, repository, this))
, m_highlightedText(repository)
{
m_document->setUndoRedoEnabled(false);
}

DisassemblyModel::~DisassemblyModel() = default;
Expand Down Expand Up @@ -56,17 +49,13 @@ void DisassemblyModel::setDisassembly(const DisassemblyOutput& disassemblyOutput
m_results = results;
m_numTypes = results.selfCosts.numTypes();

m_document->clear();

QTextCursor cursor(m_document);
cursor.beginEditBlock();
for (const auto& it : disassemblyOutput.disassemblyLines) {
cursor.insertText(it.disassembly);
cursor.insertBlock();
}
cursor.endEditBlock();
QStringList assemblyLines;
assemblyLines.reserve(disassemblyOutput.disassemblyLines.size());
std::transform(disassemblyOutput.disassemblyLines.cbegin(), disassemblyOutput.disassemblyLines.cend(),
std::back_inserter(assemblyLines),
[](const DisassemblyOutput::DisassemblyLine& line) { return line.disassembly; });

m_document->setTextWidth(m_document->idealWidth());
m_highlightedText.setText(assemblyLines);

endResetModel();
}
Expand Down Expand Up @@ -112,6 +101,7 @@ QVariant DisassemblyModel::data(const QModelIndex& index, int role) const
}

const auto& data = m_data.disassemblyLines.at(index.row());
const auto& line = m_highlightedText.textAt(index.row());
if (role == AddrRole)
return data.addr;

Expand All @@ -127,10 +117,10 @@ QVariant DisassemblyModel::data(const QModelIndex& index, int role) const
} else if (index.column() == HexdumpColumn) {
return data.hexdump;
} 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();
if (role == SyntaxHighlightRole) {
return QVariant::fromValue(m_highlightedText.lineAt(index.row()));
}
return m_highlightedText.textAt(index.row());
}
}

Expand All @@ -153,7 +143,7 @@ QVariant DisassemblyModel::data(const QModelIndex& index, int role) const
return totalCost;
} else if (role == Qt::ToolTipRole) {
auto tooltip = tr("addr: <tt>%1</tt><br/>assembly: <tt>%2</tt><br/>disassembly: <tt>%3</tt>")
.arg(QString::number(data.addr, 16), data.disassembly);
.arg(QString::number(data.addr, 16), line);
return Util::formatTooltip(tooltip, locationCost, m_results.selfCosts);
}

Expand All @@ -162,8 +152,7 @@ QVariant DisassemblyModel::data(const QModelIndex& index, int role) const
return Util::formatCostRelative(costLine, totalCost, true);
} else {
if (role == Qt::ToolTipRole)
return tr("<qt><tt>%1</tt><hr/>No samples at this location.</qt>")
.arg(data.disassembly.toHtmlEscaped());
return tr("<qt><tt>%1</tt><hr/>No samples at this location.</qt>").arg(line.toHtmlEscaped());
else
return QString();
}
Expand Down
11 changes: 4 additions & 7 deletions src/models/disassemblymodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@

#include "data.h"
#include "disassemblyoutput.h"

class QTextDocument;
class Highlighter;
#include "highlightedtext.h"

namespace KSyntaxHighlighting {
class Definition;
Expand Down Expand Up @@ -47,9 +45,9 @@ class DisassemblyModel : public QAbstractTableModel
Data::FileLine fileLineForIndex(const QModelIndex& index) const;
QModelIndex indexForFileLine(const Data::FileLine& line) const;

Highlighter* highlighter() const
HighlightedText* highlightedText()
{
return m_highlighter;
return &m_highlightedText;
}

enum Columns
Expand Down Expand Up @@ -82,8 +80,7 @@ public slots:
void find(const QString& search, Direction direction, int offset);

private:
QTextDocument* m_document;
Highlighter* m_highlighter;
HighlightedText m_highlightedText;
DisassemblyOutput m_data;
Data::CallerCalleeResults m_results;
int m_numTypes = 0;
Expand Down
41 changes: 37 additions & 4 deletions src/models/disassemblyoutput.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,28 +17,46 @@
#include <QProcess>
#include <QStandardPaths>

#include "formattingutils.h"

namespace {
Q_LOGGING_CATEGORY(disassemblyoutput, "hotspot.disassemblyoutput")

bool canVisualizeJumps(const QString& objdump)
QString objdumpHelp(const QString& objdump)
{
QProcess process;
process.setProcessChannelMode(QProcess::ForwardedErrorChannel);
process.start(objdump, {QStringLiteral("-H")});
if (!process.waitForFinished(1000)) {
qCWarning(disassemblyoutput) << "failed to query objdump help output:" << objdump << process.errorString();
return false;
return {};
}
const auto help = process.readAllStandardOutput();
return help.contains("--visualize-jumps");
return QString::fromUtf8(help);
}

bool canVisualizeJumps(const QString& objdump)
{
return objdumpHelp(objdump).contains(QStringLiteral("--visualize-jumps"));
}

bool canUseSyntaxHighlighting(const QString& objdump)
{
return objdumpHelp(objdump).contains(QStringLiteral("--disassembler-color"));
}

DisassemblyOutput::LinkedFunction extractLinkedFunction(const QString& disassembly)
DisassemblyOutput::LinkedFunction extractLinkedFunction(const QString& disassemblyWithAnsi)
{
DisassemblyOutput::LinkedFunction function = {};

const auto escapeChar = QLatin1Char('\u001B');

const auto disassembly =
disassemblyWithAnsi.contains(escapeChar) ? utils::removeAnsi(disassemblyWithAnsi) : disassemblyWithAnsi;

const auto leftBracketIndex = disassembly.indexOf(QLatin1Char('<'));
const auto rightBracketIndex = disassembly.indexOf(QLatin1Char('>'));

if (leftBracketIndex != -1 && rightBracketIndex != -1) {
if (leftBracketIndex < rightBracketIndex) {
function.name = disassembly.mid(leftBracketIndex + 1, rightBracketIndex - leftBracketIndex - 1);
Expand All @@ -61,6 +79,7 @@ DisassemblyOutput::LinkedFunction extractLinkedFunction(const QString& disassemb
}
}
}

return function;
}

Expand Down Expand Up @@ -213,6 +232,14 @@ DisassemblyOutput::ObjectdumpOutput DisassemblyOutput::objdumpParse(const QByteA

const auto parts = asmLine.split(QLatin1Char('\t'));

if (parts.size() == 1 && asmLine.endsWith(QLatin1Char(':'))) {
// we got a line like:
// std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_local_data():
// pass them to the disassembler since this can be used for inlining
disassemblyLines.push_back({0, asmLine, {}, {}, {}, {currentSourceFileName, sourceCodeLine}});
continue;
}

const auto addr = [addrString = parts.value(0).trimmed(), &asmLine]() -> uint64_t {
const auto suffix = QLatin1Char(':');
if (!addrString.endsWith(suffix))
Expand Down Expand Up @@ -300,6 +327,12 @@ DisassemblyOutput DisassemblyOutput::disassemble(const QString& objdump, const Q
else
qCInfo(disassemblyoutput) << "objdump binary does not support `--visualize-jumps`:" << processPath;

if (canUseSyntaxHighlighting(processPath)) {
arguments.append(QStringLiteral("--disassembler-color=color"));
} else {
qCInfo(disassemblyoutput) << "objdump binary does not support `--disassembler-color`:" << processPath;
}

auto binary = findBinaryForSymbol(debugPaths, extraLibPaths, symbol);
if (binary.isEmpty()) {
disassemblyOutput.errorMessage +=
Expand Down
24 changes: 24 additions & 0 deletions src/models/formattingutils.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
SPDX-FileCopyrightText: Lieven Hey <lieven.hey@kdab.com>
SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
SPDX-License-Identifier: GPL-2.0-or-later
*/

#include "formattingutils.h"

QString utils::removeAnsi(const QString& stringWithAnsi)
{
const static QChar escapeChar = QLatin1Char('\u001B');
if (!stringWithAnsi.contains(escapeChar)) {
return stringWithAnsi;
}

QString ansiFreeString = stringWithAnsi;
while (ansiFreeString.contains(escapeChar)) {
const auto escapeStart = ansiFreeString.indexOf(escapeChar);
const auto escapeEnd = ansiFreeString.indexOf(QLatin1Char('m'), escapeStart);
ansiFreeString.remove(escapeStart, escapeEnd - escapeStart + 1);
}
return ansiFreeString;
}
14 changes: 14 additions & 0 deletions src/models/formattingutils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
SPDX-FileCopyrightText: Lieven Hey <lieven.hey@kdab.com>
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 <QString>

namespace utils {
QString removeAnsi(const QString& stringWithAnsi);
}
Loading

0 comments on commit b4a785e

Please sign in to comment.