Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add option to hide hexdump #566

Merged
merged 2 commits into from
Dec 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions src/disassemblysettingspage.ui
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,14 @@
<property name="bottomMargin">
<number>0</number>
</property>
<item row="1" column="0">
<item row="2" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Source Code Search Paths:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<item row="2" column="1">
<widget class="KEditListWidget" name="sourcePaths">
<property name="buttons">
<set>KEditListWidget::All</set>
Expand All @@ -60,6 +60,20 @@
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Show hexdump:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="showHexdump">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
Expand Down
4 changes: 4 additions & 0 deletions src/models/disassemblymodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");

Expand Down Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions src/models/disassemblymodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class DisassemblyModel : public QAbstractTableModel
{
AddrColumn,
BranchColumn,
HexdumpColumn,
DisassemblyColumn,
COLUMN_COUNT
};
Expand Down
60 changes: 36 additions & 24 deletions src/models/disassemblyoutput.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -207,29 +207,37 @@ DisassemblyOutput::ObjectdumpOutput DisassemblyOutput::objdumpParse(const QByteA
continue;
}

// detect lines like:
// 4f616:\t<jumps>84 c0\ttest %al,%al
auto lineRest = QStringView(asmLine);

// <four spaces><hex addr>:\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<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))
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;
}();

Expand All @@ -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}});
}
Expand Down
1 change: 1 addition & 0 deletions src/models/disassemblyoutput.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ struct DisassemblyOutput
quint64 addr = 0;
QString disassembly;
QString branchVisualisation;
QString hexdump;
LinkedFunction linkedFunction;
Data::FileLine fileLine;
};
Expand Down
6 changes: 6 additions & 0 deletions src/resultsdisassemblypage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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++) {
Expand Down
13 changes: 13 additions & 0 deletions src/settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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);
}
}
7 changes: 7 additions & 0 deletions src/settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,10 @@ class Settings : public QObject
{
return m_showBranches;
}
bool showHexdump() const
{
return m_showHexdump;
}

void loadFromFile();

Expand All @@ -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);
Expand All @@ -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;
Expand All @@ -230,6 +236,7 @@ public slots:
QString m_sourceCodePaths;
QString m_perfMapPath;
bool m_showBranches = true;
bool m_showHexdump = false;

QString m_lastUsedEnvironment;

Expand Down
2 changes: 2 additions & 0 deletions src/settingsdialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
});
}
53 changes: 39 additions & 14 deletions tests/modeltests/tst_disassemblyoutput.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ private slots:
}

file->write(text.toUtf8());
file->flush();

return file->fileName();
}
Expand Down Expand Up @@ -242,30 +243,32 @@ private slots:
QVERIFY(result.errorMessage.isEmpty());

auto isValidVisualisationCharacter = [](QChar character) {
const static auto validCharacters =
std::initializer_list<QChar> {QLatin1Char(' '), QLatin1Char('\t'), QLatin1Char('|'), QLatin1Char('/'),
QLatin1Char('\\'), QLatin1Char('-'), QLatin1Char('>'), QLatin1Char('+')};
const static auto validCharacters = std::initializer_list<QChar> {
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<QChar> {
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());

// check that we only captures valid visualisation characters
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);
Expand Down Expand Up @@ -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);
Expand All @@ -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());
}
Expand Down