diff --git a/src/disassemblysettingspage.ui b/src/disassemblysettingspage.ui
index 60dfcee8..b1f2471b 100644
--- a/src/disassemblysettingspage.ui
+++ b/src/disassemblysettingspage.ui
@@ -32,14 +32,14 @@
0
- -
+
-
Source Code Search Paths:
- -
+
-
KEditListWidget::All
@@ -60,6 +60,20 @@
+ -
+
+
+ Show hexdump:
+
+
+
+ -
+
+
+
+
+
+
diff --git a/src/models/disassemblymodel.cpp b/src/models/disassemblymodel.cpp
index 6d2a58e2..d2ee7cd6 100644
--- a/src/models/disassemblymodel.cpp
+++ b/src/models/disassemblymodel.cpp
@@ -82,6 +82,8 @@ QVariant DisassemblyModel::headerData(int section, Qt::Orientation orientation,
return tr("Address");
else if (section == BranchColumn)
return tr("Branches");
+ else if (section == HexdumpColumn)
+ return tr("Hexdump");
else if (section == DisassemblyColumn)
return tr("Assembly / Disassembly");
@@ -122,6 +124,8 @@ QVariant DisassemblyModel::data(const QModelIndex& index, int role) const
return QString::number(data.addr, 16);
} else if (index.column() == BranchColumn) {
return data.branchVisualisation;
+ } else if (index.column() == HexdumpColumn) {
+ return data.hexdump;
} else if (index.column() == DisassemblyColumn) {
const auto block = m_document->findBlockByLineNumber(index.row());
if (role == SyntaxHighlightRole)
diff --git a/src/models/disassemblymodel.h b/src/models/disassemblymodel.h
index 8456d1d4..8835225e 100644
--- a/src/models/disassemblymodel.h
+++ b/src/models/disassemblymodel.h
@@ -56,6 +56,7 @@ class DisassemblyModel : public QAbstractTableModel
{
AddrColumn,
BranchColumn,
+ HexdumpColumn,
DisassemblyColumn,
COLUMN_COUNT
};
diff --git a/src/models/disassemblyoutput.cpp b/src/models/disassemblyoutput.cpp
index 0cf72df6..53d16e4d 100644
--- a/src/models/disassemblyoutput.cpp
+++ b/src/models/disassemblyoutput.cpp
@@ -207,29 +207,37 @@ DisassemblyOutput::ObjectdumpOutput DisassemblyOutput::objdumpParse(const QByteA
continue;
}
- // detect lines like:
- // 4f616:\t84 c0\ttest %al,%al
- auto lineRest = QStringView(asmLine);
-
- // :\t
- const auto addr = [&]() -> quint64 {
- const auto prefix = QLatin1String(" ");
- const auto suffix = QLatin1String(":\t");
- if (!lineRest.startsWith(prefix))
- return 0;
+ // a line looks like this:
+ // [spaces]addr:\t [branch visualization] [hexdump]\tdiassembly
+ // we can simplify parsing by splitting it into three parts
+
+ const auto parts = asmLine.split(QLatin1Char('\t'));
- const auto addrEnd = lineRest.indexOf(suffix, prefix.size());
- if (addrEnd == -1)
+ if (parts.size() == 1 && asmLine.endsWith(QLatin1Char(':'))) {
+ // we got a line like:
+ // std::__cxx11::basic_string, std::allocator >::_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))
return 0;
bool ok = false;
- auto ret = lineRest.mid(0, addrEnd).toULongLong(&ok, 16);
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+ auto ret = addrString.leftRef(addrString.length() - 1).toULongLong(&ok, 16);
+#else
+ auto ret = addrString.left(addrString.length() - 1).toULongLong(&ok, 16);
+#endif
+
if (!ok) {
qCWarning(disassemblyoutput) << "unhandled asm line format:" << asmLine;
return 0;
}
- lineRest = lineRest.mid(addrEnd + suffix.size());
return ret;
}();
@@ -238,19 +246,23 @@ DisassemblyOutput::ObjectdumpOutput DisassemblyOutput::objdumpParse(const QByteA
// | 64 a3 ....
// \-> 65 23 ....
// so we can simply skip all characters until we meet a letter or a number
- const auto branchVisualisation = [&]() -> QStringView {
- if (!addr)
- return {};
- auto firstHexIt = std::find_if(lineRest.cbegin(), lineRest.cend(), isHexCharacter);
- auto size = std::distance(lineRest.cbegin(), firstHexIt);
- auto ret = lineRest.mid(0, size);
- lineRest = lineRest.mid(size);
- return ret;
+ struct BranchesAndHexdump
+ {
+ QString branchVisualisation;
+ QString hexdump;
+ };
+ const auto [branchVisualisation, hexdump] = [branchesAndHex = parts.value(1)]() -> BranchesAndHexdump {
+ auto firstHexIt = std::find_if(branchesAndHex.cbegin(), branchesAndHex.cend(), isHexCharacter);
+ auto size = std::distance(branchesAndHex.cbegin(), firstHexIt);
+ auto branchVisualisation = branchesAndHex.mid(0, size);
+ auto hexdump = branchesAndHex.mid(size);
+ return {branchVisualisation, hexdump.trimmed()};
}();
disassemblyLines.push_back({addr,
- lineRest.toString(),
- branchVisualisation.toString(),
+ parts.value(2).trimmed(),
+ branchVisualisation,
+ hexdump,
extractLinkedFunction(asmLine),
{currentSourceFileName, sourceCodeLine}});
}
diff --git a/src/models/disassemblyoutput.h b/src/models/disassemblyoutput.h
index 8f9ab896..d4d636ee 100644
--- a/src/models/disassemblyoutput.h
+++ b/src/models/disassemblyoutput.h
@@ -25,6 +25,7 @@ struct DisassemblyOutput
quint64 addr = 0;
QString disassembly;
QString branchVisualisation;
+ QString hexdump;
LinkedFunction linkedFunction;
Data::FileLine fileLine;
};
diff --git a/src/resultsdisassemblypage.cpp b/src/resultsdisassemblypage.cpp
index daac9722..9bbdb3dd 100644
--- a/src/resultsdisassemblypage.cpp
+++ b/src/resultsdisassemblypage.cpp
@@ -425,11 +425,16 @@ ResultsDisassemblyPage::ResultsDisassemblyPage(CostContextMenu* costContextMenu,
m_disassemblyModel, &m_currentDisasmSearchIndex, 0);
ui->assemblyView->setColumnHidden(DisassemblyModel::BranchColumn, !settings->showBranches());
+ ui->assemblyView->setColumnHidden(DisassemblyModel::HexdumpColumn, !settings->showHexdump());
connect(settings, &Settings::showBranchesChanged, this, [this](bool showBranches) {
ui->assemblyView->setColumnHidden(DisassemblyModel::BranchColumn, !showBranches);
});
+ connect(settings, &Settings::showHexdumpChanged, this, [this](bool showHexdump) {
+ ui->assemblyView->setColumnHidden(DisassemblyModel::HexdumpColumn, !showHexdump);
+ });
+
#if KFSyntaxHighlighting_FOUND
QStringList schemes;
@@ -489,6 +494,7 @@ void ResultsDisassemblyPage::setupAsmViewModel()
ui->assemblyView->header()->setStretchLastSection(false);
ui->assemblyView->header()->setSectionResizeMode(DisassemblyModel::AddrColumn, QHeaderView::ResizeToContents);
ui->assemblyView->header()->setSectionResizeMode(DisassemblyModel::BranchColumn, QHeaderView::Interactive);
+ ui->assemblyView->header()->setSectionResizeMode(DisassemblyModel::HexdumpColumn, QHeaderView::Interactive);
ui->assemblyView->header()->setSectionResizeMode(DisassemblyModel::DisassemblyColumn, QHeaderView::Stretch);
for (int col = DisassemblyModel::COLUMN_COUNT; col < m_disassemblyModel->columnCount(); col++) {
diff --git a/src/settings.cpp b/src/settings.cpp
index 1439aa16..d3578325 100644
--- a/src/settings.cpp
+++ b/src/settings.cpp
@@ -245,6 +245,11 @@ void Settings::loadFromFile()
connect(this, &Settings::showBranchesChanged, [sharedConfig](bool showBranches) {
sharedConfig->group("Disassembly").writeEntry("showBranches", showBranches);
});
+
+ setShowBranches(sharedConfig->group("Disassembly").readEntry("showHexdump", false));
+ connect(this, &Settings::showHexdumpChanged, [sharedConfig](bool showHexdump) {
+ sharedConfig->group("Disassembly").writeEntry("showHexdump", showHexdump);
+ });
}
void Settings::setSourceCodePaths(const QString& paths)
@@ -270,3 +275,11 @@ void Settings::setShowBranches(bool showBranches)
emit showBranchesChanged(m_showBranches);
}
}
+
+void Settings::setShowHexdump(bool showHexdump)
+{
+ if (m_showHexdump != showHexdump) {
+ m_showHexdump = showHexdump;
+ emit showHexdumpChanged(m_showHexdump);
+ }
+}
diff --git a/src/settings.h b/src/settings.h
index d892ce97..abca4c9e 100644
--- a/src/settings.h
+++ b/src/settings.h
@@ -158,6 +158,10 @@ class Settings : public QObject
{
return m_showBranches;
}
+ bool showHexdump() const
+ {
+ return m_showHexdump;
+ }
void loadFromFile();
@@ -182,6 +186,7 @@ class Settings : public QObject
void sourceCodePathsChanged(const QString& paths);
void perfPathChanged(const QString& perfPath);
void showBranchesChanged(bool showBranches);
+ void showHexdumpChanged(bool showHexdump);
public slots:
void setPrettifySymbols(bool prettifySymbols);
@@ -206,6 +211,7 @@ public slots:
void setSourceCodePaths(const QString& paths);
void setPerfPath(const QString& path);
void setShowBranches(bool showBranches);
+ void setShowHexdump(bool showHexdump);
private:
using QObject::QObject;
@@ -230,6 +236,7 @@ public slots:
QString m_sourceCodePaths;
QString m_perfMapPath;
bool m_showBranches = true;
+ bool m_showHexdump = false;
QString m_lastUsedEnvironment;
diff --git a/src/settingsdialog.cpp b/src/settingsdialog.cpp
index 92b1a73a..db4c7818 100644
--- a/src/settingsdialog.cpp
+++ b/src/settingsdialog.cpp
@@ -336,9 +336,11 @@ void SettingsDialog::addSourcePathPage()
setupMultiPath(disassemblyPage->sourcePaths, disassemblyPage->label, buttonBox()->button(QDialogButtonBox::Ok));
disassemblyPage->showBranches->setChecked(settings->showBranches());
+ disassemblyPage->showHexdump->setChecked(settings->showHexdump());
connect(buttonBox(), &QDialogButtonBox::accepted, this, [this, colon, settings] {
settings->setSourceCodePaths(disassemblyPage->sourcePaths->items().join(colon));
settings->setShowBranches(disassemblyPage->showBranches->isChecked());
+ settings->setShowHexdump(disassemblyPage->showHexdump->isChecked());
});
}
diff --git a/tests/modeltests/tst_disassemblyoutput.cpp b/tests/modeltests/tst_disassemblyoutput.cpp
index c84a53e9..b67e9ff8 100644
--- a/tests/modeltests/tst_disassemblyoutput.cpp
+++ b/tests/modeltests/tst_disassemblyoutput.cpp
@@ -129,6 +129,7 @@ private slots:
}
file->write(text.toUtf8());
+ file->flush();
return file->fileName();
}
@@ -242,14 +243,24 @@ private slots:
QVERIFY(result.errorMessage.isEmpty());
auto isValidVisualisationCharacter = [](QChar character) {
- const static auto validCharacters =
- std::initializer_list {QLatin1Char(' '), QLatin1Char('\t'), QLatin1Char('|'), QLatin1Char('/'),
- QLatin1Char('\\'), QLatin1Char('-'), QLatin1Char('>'), QLatin1Char('+')};
+ const static auto validCharacters = std::initializer_list {
+ QLatin1Char(' '), QLatin1Char('\t'), QLatin1Char('|'), QLatin1Char('/'), QLatin1Char('\\'),
+ QLatin1Char('-'), QLatin1Char('>'), QLatin1Char('+'), QLatin1Char('X')};
return std::any_of(validCharacters.begin(), validCharacters.end(),
[character](auto validCharacter) { return character == validCharacter; });
};
+ auto isValidHexdumpCharacter = [](QChar character) {
+ const static auto validCharacters = std::initializer_list {
+ QLatin1Char(' '), QLatin1Char('0'), QLatin1Char('1'), QLatin1Char('2'), QLatin1Char('3'),
+ QLatin1Char('4'), QLatin1Char('5'), QLatin1Char('6'), QLatin1Char('7'), QLatin1Char('8'),
+ QLatin1Char('9'), QLatin1Char('a'), QLatin1Char('b'), QLatin1Char('c'), QLatin1Char('d'),
+ QLatin1Char('e'), QLatin1Char('f')};
+ return std::any_of(validCharacters.begin(), validCharacters.end(),
+ [character](auto validCharacter) { return character == validCharacter; });
+ };
+
for (const auto& line : result.disassemblyLines) {
QVERIFY(!line.branchVisualisation.isEmpty());
@@ -257,15 +268,7 @@ private slots:
QVERIFY(std::all_of(line.branchVisualisation.cbegin(), line.branchVisualisation.cend(),
isValidVisualisationCharacter));
- QVERIFY(!line.disassembly.isEmpty());
- // check that we removed everyting before the hexdump
- bool ok = false;
-#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
- line.disassembly.leftRef(2).toInt(&ok, 16);
-#else
- line.disassembly.left(2).toInt(&ok, 16);
-#endif
- QVERIFY(ok);
+ QVERIFY(std::all_of(line.hexdump.cbegin(), line.hexdump.cend(), isValidHexdumpCharacter));
// Check that address is valid
QVERIFY(line.addr >= address && line.addr < address + size);
@@ -310,6 +313,23 @@ private slots:
const auto parsed = DisassemblyOutput::objdumpParse(dataFile.readAll());
QCOMPARE(parsed.mainSourceFileName, mainSourceFileName);
QCOMPARE(parsed.disassemblyLines.size(), numLines);
+
+ auto checkForMultiLineInstruction = [](const QString& lastDisasm) {
+ // some instructions translate to multiple lines
+ // like 66 41 83 ba 00 80 ff 7f 00 which translates to:
+ // 0: 66 41 83 ba 00 80 ff cmp WORD PTR [r10+0x7fff8000],0x0
+ // 7: 7f 00
+
+ const auto multiLineOpcodes = {
+ QLatin1String("movsbl"), QLatin1String("compb"), QLatin1String("movsd"), QLatin1String("%fs"),
+ QLatin1String("movabs"), QLatin1String("cs nopw"), QLatin1String("cmpq"), QLatin1String("cmpb"),
+ QLatin1String("cmpw"), QLatin1String("lea 0x0")};
+
+ return std::any_of(multiLineOpcodes.begin(), multiLineOpcodes.end(),
+ [lastDisasm](const auto& opcode) { return lastDisasm.contains(opcode); });
+ };
+
+ QString lastOpcode;
for (const auto& line : parsed.disassemblyLines) {
if (line.fileLine.file.isEmpty()) {
QCOMPARE(line.fileLine.line, -1);
@@ -320,12 +340,17 @@ private slots:
if (line.addr) {
QVERIFY(line.addr >= minAddr);
QVERIFY(line.addr <= maxAddr);
- QVERIFY(!line.disassembly.isEmpty());
+ QVERIFY(!line.disassembly.isEmpty()
+ || (line.disassembly.isEmpty() && checkForMultiLineInstruction(lastOpcode)));
- if (!line.branchVisualisation.isEmpty()) {
+ if (!line.branchVisualisation.isEmpty())
+
+ {
QVERIFY(std::all_of(line.branchVisualisation.begin(), line.branchVisualisation.end(),
[](QChar c) { return QLatin1String(" |\\/->+X").contains(c); }));
}
+
+ lastOpcode = line.disassembly;
} else {
QVERIFY(line.branchVisualisation.isEmpty());
}