Skip to content

Commit 662443d

Browse files
authored
Merge pull request #356 from KDAB/update-readme
update readme to announce annotate view
2 parents c0efbd0 + c15dc71 commit 662443d

11 files changed

+164
-4
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,7 @@ Compared to `perf report`, hotspot misses a lot of features. Some of these are p
379379
in the future. Others may potentially never get implemented. But be aware that the following features
380380
are _not_ available in hotspot currently:
381381

382-
- annotate: the caller/callee view shows cost attributed to individual source lines. But a proper annotation view like `perf annotate` on the source level, is currently missing. To reach the annotation view on the instruction level use Disassembly feature.
382+
- annotate: the caller/callee view shows cost attributed to individual source lines. A basic annotation view like `perf annotate` on the source level, is available using the Disassembly feature.
383383
- the columns in the tables are currently hardcoded, while potentially a user may want to change this to show e.g. cost per-process or thread and so forth
384384
- many of the more advanced features, such as `--itrace`, `--mem-mode`, `--branch-stack` and `--branch-history`, are unsupported
385385

screenshots/dockwidgets.png

-730 KB
Loading

src/models/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ add_library(models STATIC
1717
../util.cpp
1818
callercalleeproxy.cpp
1919
frequencymodel.cpp
20+
disassemblydelegate.cpp
2021
)
2122

2223
target_link_libraries(models

src/models/disassemblydelegate.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
SPDX-FileCopyrightText: Lieven Hey <lieven.hey@kdab.com>
3+
SPDX-FileCopyrightText: 2022 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
4+
5+
SPDX-License-Identifier: GPL-2.0-or-later
6+
*/
7+
8+
#include "disassemblydelegate.h"
9+
10+
#include <QEvent>
11+
#include <QMouseEvent>
12+
13+
#include "disassemblymodel.h"
14+
15+
DisassemblyDelegate::DisassemblyDelegate(QObject* parent)
16+
: QItemDelegate(parent)
17+
{
18+
}
19+
20+
DisassemblyDelegate::~DisassemblyDelegate() = default;
21+
22+
bool DisassemblyDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& /*option*/,
23+
const QModelIndex& index)
24+
{
25+
if (event->type() != QMouseEvent::MouseButtonPress)
26+
return false;
27+
28+
const QString functionName =
29+
model->data(model->index(index.row(), DisassemblyModel::LinkedFunctionName), Qt::DisplayRole).toString();
30+
if (functionName.isEmpty())
31+
return false;
32+
33+
int funtionOffset =
34+
model->data(model->index(index.row(), DisassemblyModel::LinkedFunctionOffset), Qt::DisplayRole).toInt();
35+
emit gotoFunction(functionName, funtionOffset);
36+
37+
return true;
38+
}

src/models/disassemblydelegate.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
SPDX-FileCopyrightText: Lieven Hey <lieven.hey@kdab.com>
3+
SPDX-FileCopyrightText: 2022 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
4+
5+
SPDX-License-Identifier: GPL-2.0-or-later
6+
*/
7+
8+
#pragma once
9+
10+
#include <QItemDelegate>
11+
12+
class DisassemblyDelegate : public QItemDelegate
13+
{
14+
Q_OBJECT
15+
public:
16+
DisassemblyDelegate(QObject* parent = nullptr);
17+
~DisassemblyDelegate();
18+
19+
bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option,
20+
const QModelIndex& index) override;
21+
22+
signals:
23+
void gotoFunction(const QString& function, int offset);
24+
};

src/models/disassemblymodel.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,20 @@ void DisassemblyModel::clear()
2222
endResetModel();
2323
}
2424

25+
QModelIndex DisassemblyModel::findIndexWithOffset(int offset)
26+
{
27+
quint64 address = m_data.disassemblyLines[0].addr + offset;
28+
29+
const auto& found =
30+
std::find_if(m_data.disassemblyLines.begin(), m_data.disassemblyLines.end(),
31+
[address](const DisassemblyOutput::DisassemblyLine& line) { return line.addr == address; });
32+
33+
if (found != m_data.disassemblyLines.end()) {
34+
return index(std::distance(m_data.disassemblyLines.begin(), found), 0);
35+
}
36+
return {};
37+
}
38+
2539
void DisassemblyModel::setDisassembly(const DisassemblyOutput& disassemblyOutput)
2640
{
2741
beginResetModel();
@@ -47,6 +61,14 @@ QVariant DisassemblyModel::headerData(int section, Qt::Orientation orientation,
4761
if (section == DisassemblyColumn)
4862
return tr("Assembly");
4963

64+
if (section == LinkedFunctionName) {
65+
return tr("Linked Function Name");
66+
}
67+
68+
if (section == LinkedFunctionOffset) {
69+
return tr("Linked Function Offset");
70+
}
71+
5072
if (section - COLUMN_COUNT <= m_numTypes)
5173
return m_results.selfCosts.typeName(section - COLUMN_COUNT);
5274

@@ -67,6 +89,12 @@ QVariant DisassemblyModel::data(const QModelIndex& index, int role) const
6789
if (index.column() == DisassemblyColumn)
6890
return data.disassembly;
6991

92+
if (index.column() == LinkedFunctionName)
93+
return data.linkedFunctionName;
94+
95+
if (index.column() == LinkedFunctionOffset)
96+
return data.linkedFunctionOffset;
97+
7098
if (data.addr == 0) {
7199
return {};
72100
}

src/models/disassemblymodel.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ class DisassemblyModel : public QAbstractTableModel
2323
void setResults(const Data::CallerCalleeResults& results);
2424

2525
void clear();
26+
QModelIndex findIndexWithOffset(int offset);
2627

2728
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
2829
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
@@ -33,6 +34,8 @@ class DisassemblyModel : public QAbstractTableModel
3334
enum Columns
3435
{
3536
DisassemblyColumn,
37+
LinkedFunctionName,
38+
LinkedFunctionOffset,
3639
COLUMN_COUNT
3740
};
3841

src/models/disassemblyoutput.cpp

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,39 @@
1414
#include <QRegularExpression>
1515
#include <QStandardPaths>
1616

17+
namespace {
18+
DisassemblyOutput::LinkedFunction extractLinkedFunction(const QString& disassembly)
19+
{
20+
DisassemblyOutput::LinkedFunction function = {};
21+
22+
const auto leftBracketIndex = disassembly.indexOf(QLatin1Char('<'));
23+
const auto rightBracketIndex = disassembly.indexOf(QLatin1Char('>'));
24+
if (leftBracketIndex != -1 && rightBracketIndex != -1) {
25+
if (leftBracketIndex < rightBracketIndex) {
26+
function.name = disassembly.mid(leftBracketIndex + 1, rightBracketIndex - leftBracketIndex - 1);
27+
28+
const auto atindex = function.name.indexOf(QLatin1Char('@'));
29+
if (atindex > 0) {
30+
function.name = function.name.left(atindex);
31+
}
32+
33+
const auto plusIndex = function.name.indexOf(QLatin1Char('+'));
34+
if (plusIndex > 0) {
35+
bool ok;
36+
// ignore 0x in offset
37+
const auto& offsetStr = function.name.mid(plusIndex + 3);
38+
function.name = function.name.left(plusIndex);
39+
auto offset = offsetStr.toInt(&ok, 16);
40+
if (ok) {
41+
function.offset = offset;
42+
}
43+
}
44+
}
45+
}
46+
return function;
47+
}
48+
}
49+
1750
static QVector<DisassemblyOutput::DisassemblyLine> objdumpParse(const QByteArray &output)
1851
{
1952
QVector<DisassemblyOutput::DisassemblyLine> disassemblyLines;
@@ -22,7 +55,7 @@ static QVector<DisassemblyOutput::DisassemblyLine> objdumpParse(const QByteArray
2255
QString asmLine;
2356
// detect lines like:
2457
// 4f616: 84 c0 test %al,%al
25-
static const QRegularExpression disassemblyRegex(QStringLiteral("^ ([0-9a-f]{4,}):\t[0-9a-f ]{2,}"));
58+
static const QRegularExpression disassemblyRegex(QStringLiteral("^[ ]+([0-9a-f]{4,}):\t[0-9a-f ]{2,}"));
2659
while (stream.readLineInto(&asmLine))
2760
{
2861
if (asmLine.isEmpty() || asmLine.startsWith(QLatin1String("Disassembly")))
@@ -51,7 +84,7 @@ static QVector<DisassemblyOutput::DisassemblyLine> objdumpParse(const QByteArray
5184
}
5285
}
5386

54-
disassemblyLines.push_back({addr, asmLine});
87+
disassemblyLines.push_back({addr, asmLine, extractLinkedFunction(asmLine)});
5588
}
5689
return disassemblyLines;
5790
}
@@ -85,6 +118,7 @@ DisassemblyOutput DisassemblyOutput::disassemble(const QString& objdump, const Q
85118
auto toHex = [](quint64 addr) -> QString { return QLatin1String("0x") + QString::number(addr, 16); };
86119
const auto arguments = QStringList {QStringLiteral("-d"),
87120
QStringLiteral("-S"),
121+
QStringLiteral("-C"),
88122
QStringLiteral("--start-address"),
89123
toHex(symbol.relAddr),
90124
QStringLiteral("--stop-address"),

src/models/disassemblyoutput.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,17 @@
1313

1414
struct DisassemblyOutput
1515
{
16+
struct LinkedFunction
17+
{
18+
QString name;
19+
int offset = 0; // offset from the entrypoint of the function
20+
};
21+
1622
struct DisassemblyLine
1723
{
1824
quint64 addr = 0;
1925
QString disassembly;
26+
LinkedFunction linkedFunction;
2027
};
2128
QVector<DisassemblyLine> disassemblyLines;
2229

src/resultsdisassemblypage.cpp

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,19 +31,39 @@
3131

3232
#include "data.h"
3333
#include "models/costdelegate.h"
34+
#include "models/disassemblydelegate.h"
35+
#include "models/disassemblymodel.h"
3436
#include "models/hashmodel.h"
3537
#include "models/topproxy.h"
3638
#include "models/treemodel.h"
37-
#include "models/disassemblymodel.h"
3839

3940
ResultsDisassemblyPage::ResultsDisassemblyPage(QWidget* parent)
4041
: QWidget(parent)
4142
, ui(new Ui::ResultsDisassemblyPage)
4243
, m_model(new DisassemblyModel(this))
4344
, m_costDelegate(new CostDelegate(DisassemblyModel::CostRole, DisassemblyModel::TotalCostRole, this))
45+
, m_disassemblyDelegate(new DisassemblyDelegate(this))
4446
{
4547
ui->setupUi(this);
4648
ui->asmView->setModel(m_model);
49+
50+
connect(m_disassemblyDelegate, &DisassemblyDelegate::gotoFunction, this,
51+
[this](const QString& functionName, int offset) {
52+
if (m_curSymbol.symbol == functionName) {
53+
ui->asmView->scrollTo(m_model->findIndexWithOffset(offset),
54+
QAbstractItemView::ScrollHint::PositionAtTop);
55+
} else {
56+
const auto symbol = std::find_if(
57+
m_callerCalleeResults.entries.keyBegin(), m_callerCalleeResults.entries.keyEnd(),
58+
[functionName](const Data::Symbol& symbol) { return symbol.symbol == functionName; });
59+
60+
if (symbol != m_callerCalleeResults.entries.keyEnd()) {
61+
setSymbol(*symbol);
62+
showDisassembly();
63+
emit jumpToCallerCallee(*symbol);
64+
}
65+
}
66+
});
4767
}
4868

4969
ResultsDisassemblyPage::~ResultsDisassemblyPage() = default;
@@ -57,6 +77,9 @@ void ResultsDisassemblyPage::setupAsmViewModel()
5777
{
5878
ui->asmView->header()->setStretchLastSection(false);
5979
ui->asmView->header()->setSectionResizeMode(0, QHeaderView::Stretch);
80+
ui->asmView->hideColumn(DisassemblyModel::LinkedFunctionName);
81+
ui->asmView->hideColumn(DisassemblyModel::LinkedFunctionOffset);
82+
ui->asmView->setItemDelegateForColumn(0, m_disassemblyDelegate);
6083
for (int col = 1; col < m_model->columnCount(); col++) {
6184
ui->asmView->setColumnWidth(col, 100);
6285
ui->asmView->header()->setSectionResizeMode(col, QHeaderView::Interactive);

0 commit comments

Comments
 (0)