Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Show multiple costs in timelinewidget
Browse files Browse the repository at this point in the history
Showing only one cost is fine if we only show a hardware event, but
since we now support tracepoints and some come in an enter/exit pair it
requires us to rework the timeline delegate.
This patch makes the event source combobox multi select and allows to
select multiple event sources.
lievenhey committed Nov 19, 2024
1 parent 6ca09e8 commit 88417d5
Showing 7 changed files with 95 additions and 23 deletions.
14 changes: 13 additions & 1 deletion src/models/eventmodelproxy.cpp
Original file line number Diff line number Diff line change
@@ -21,6 +21,18 @@ EventModelProxy::EventModelProxy(QObject* parent)

EventModelProxy::~EventModelProxy() = default;

void EventModelProxy::showCostId(qint32 costId)
{
m_hiddenCostIds.remove(costId);
invalidate();
}

void EventModelProxy::hideCostId(qint32 costId)
{
m_hiddenCostIds.insert(costId);
invalidate();
}

bool EventModelProxy::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const
{
// index is invalid -> we are at the root node
@@ -36,7 +48,7 @@ bool EventModelProxy::filterAcceptsRow(int source_row, const QModelIndex& source
.data(EventModel::EventsRole)
.value<Data::Events>();

if (data.empty()) {
if (data.empty() || m_hiddenCostIds.contains(data[0].type)) {
return false;
}

7 changes: 7 additions & 0 deletions src/models/eventmodelproxy.h
Original file line number Diff line number Diff line change
@@ -7,6 +7,7 @@

#pragma once

#include <QSet>
#include <QSortFilterProxyModel>

class EventModelProxy : public QSortFilterProxyModel
@@ -16,7 +17,13 @@ class EventModelProxy : public QSortFilterProxyModel
explicit EventModelProxy(QObject* parent = nullptr);
~EventModelProxy() override;

void showCostId(qint32 costId);
void hideCostId(qint32 costId);

protected:
bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const override;
bool lessThan(const QModelIndex& source_left, const QModelIndex& source_right) const override;

private:
QSet<qint32> m_hiddenCostIds;
};
33 changes: 21 additions & 12 deletions src/models/timelinedelegate.cpp
Original file line number Diff line number Diff line change
@@ -161,7 +161,6 @@ void TimeLineDelegate::paint(QPainter* painter, const QStyleOptionViewItem& opti
const auto results = index.data(EventModel::EventResultsRole).value<Data::EventResults>();
const auto offCpuCostId = results.offCpuTimeCostId;
const auto lostEventCostId = results.lostEventCostId;
const auto tracepointEventCostId = results.tracepointEventCostId;
const bool is_alternate = option.features & QStyleOptionViewItem::Alternate;
const auto& palette = option.palette;

@@ -231,10 +230,6 @@ void TimeLineDelegate::paint(QPainter* painter, const QStyleOptionViewItem& opti
// see also: https://www.spinics.net/lists/linux-perf-users/msg03486.html
for (const auto& event : data.events) {
const auto isLostEvent = event.type == lostEventCostId;
const auto isTracepointEvent = event.type == tracepointEventCostId;
if (event.type != m_eventType && !isLostEvent && !isTracepointEvent) {
continue;
}

const auto x = data.mapTimeToX(event.time);
if (x < TimeLineData::padding || x >= data.w) {
@@ -334,14 +329,22 @@ bool TimeLineDelegate::helpEvent(QHelpEvent* event, QAbstractItemView* view, con
Util::formatTimeString(found.totalCost),
Util::formatTimeString(found.maxCost)));
} else if (found.numSamples > 0) {
QToolTip::showText(event->globalPos(),
tr("time: %1\n%5 samples: %2\ntotal sample cost: %3\nmax sample cost: %4")
.arg(formattedTime, QString::number(found.numSamples),
Util::formatCost(found.totalCost), Util::formatCost(found.maxCost),
totalCosts.value(found.type).label));
if (m_eventType == results.tracepointEventCostId) {
// currently tracepoint cost is saying nothig, so don't show it
QToolTip::showText(
event->globalPos(),
tr("time: %1\n%3 samples: %2")
.arg(formattedTime, QString::number(found.numSamples), results.tracepoints[index.row()].name));

} else {
QToolTip::showText(event->globalPos(),
tr("time: %1\n%5 samples: %2\ntotal sample cost: %3\nmax sample cost: %4")
.arg(formattedTime, QString::number(found.numSamples),
Util::formatCost(found.totalCost), Util::formatCost(found.maxCost),
totalCosts.value(found.type).label));
}
} else {
QToolTip::showText(event->globalPos(),
tr("time: %1 (no %2 samples)").arg(formattedTime, totalCosts.value(m_eventType).label));
QToolTip::showText(event->globalPos(), tr("time: %1 (no samples)").arg(formattedTime));
}
return true;
}
@@ -390,6 +393,12 @@ bool TimeLineDelegate::eventFilter(QObject* watched, QEvent* event)

const auto time = data.mapXToTime(pos.x() - visualRect.left() - TimeLineData::padding);
const auto start = findEvent(data.events.constBegin(), data.events.constEnd(), time);

// we can show multiple events in one row so we need to dynamically figure out which costId is needed
auto hoveringEntry = std::find_if(start, data.events.cend(),
[time](const Data::Event& event) { return event.time >= time; });
setEventType(hoveringEntry != data.events.cend() ? hoveringEntry->type : 0);

auto findSamples = [&](int costType, bool contains) {
bool foundAny = false;
data.findSamples(hoverX, costType, results.lostEventCostId, contains, start,
2 changes: 1 addition & 1 deletion src/models/timelinedelegate.h
Original file line number Diff line number Diff line change
@@ -59,7 +59,6 @@ class TimeLineDelegate : public QStyledItemDelegate
bool helpEvent(QHelpEvent* event, QAbstractItemView* view, const QStyleOptionViewItem& option,
const QModelIndex& index) override;

void setEventType(int type);
void setSelectedStacks(const QSet<qint32>& selectedStacks);

signals:
@@ -71,6 +70,7 @@ class TimeLineDelegate : public QStyledItemDelegate
bool eventFilter(QObject* watched, QEvent* event) override;

private:
void setEventType(int type);
void updateView();
void updateZoomState();

32 changes: 32 additions & 0 deletions src/resultsutil.cpp
Original file line number Diff line number Diff line change
@@ -18,6 +18,8 @@
#include <QTimer>
#include <QTreeView>

#include <QStandardItemModel>

#include "models/costdelegate.h"
#include "models/data.h"
#include "models/filterandzoomstack.h"
@@ -206,6 +208,36 @@ void fillEventSourceComboBox(QComboBox* combo, const Data::Costs& costs, const Q
}
}

void fillEventSourceComboBoxMultiSelect(QComboBox* combo, const Data::Costs& costs, const QString& /*tooltipTemplate*/)
{
// restore selection if possible
const auto oldData = combo->currentData();

combo->clear();

auto model = new QStandardItemModel(costs.numTypes(), 1, combo);
int itemCounter = 0;
for (int costId = 0, c = costs.numTypes(); costId < c; costId++) {
if (!costs.totalCost(costId)) {
continue;
}

auto item = new QStandardItem(costs.typeName(costId));
item->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);
item->setData(Qt::Checked, Qt::CheckStateRole);
item->setData(costId, Qt::UserRole + 1);
model->setItem(itemCounter, item);
itemCounter++;
}
model->setRowCount(itemCounter);
combo->setModel(model);

const auto index = combo->findData(oldData);
if (index != -1) {
combo->setCurrentIndex(index);
}
}

void setupResultsAggregation(QComboBox* costAggregationComboBox)
{
struct AggregationType
1 change: 1 addition & 0 deletions src/resultsutil.h
Original file line number Diff line number Diff line change
@@ -98,6 +98,7 @@ void hideEmptyColumns(const Data::Costs& costs, QTreeView* view, int numBaseColu
void hideTracepointColumns(const Data::Costs& costs, QTreeView* view, int numBaseColumns);

void fillEventSourceComboBox(QComboBox* combo, const Data::Costs& costs, const QString& tooltipTemplate);
void fillEventSourceComboBoxMultiSelect(QComboBox* combo, const Data::Costs& costs, const QString& tooltipTemplate);

void setupResultsAggregation(QComboBox* costAggregationComboBox);
}
29 changes: 20 additions & 9 deletions src/timelinewidget.cpp
Original file line number Diff line number Diff line change
@@ -17,9 +17,11 @@
#include "parsers/perf/perfparser.h"

#include <QLabel>
#include <QListView>
#include <QPointer>
#include <QProgressBar>
#include <QSortFilterProxyModel>
#include <QStandardItemModel>
#include <QVBoxLayout>

#include <KLocalizedString>
@@ -82,9 +84,24 @@ TimeLineWidget::TimeLineWidget(PerfParser* parser, QMenu* filterMenu, FilterAndZ
connect(timeLineProxy, &QAbstractItemModel::rowsInserted, this, [this]() { ui->timeLineView->expandToDepth(1); });
connect(timeLineProxy, &QAbstractItemModel::modelReset, this, [this]() { ui->timeLineView->expandToDepth(1); });

connect(m_parser, &PerfParser::bottomUpDataAvailable, this, [this](const Data::BottomUpResults& data) {
ResultsUtil::fillEventSourceComboBox(ui->timeLineEventSource, data.costs, tr("Show timeline for %1 events."));
});
connect(m_parser, &PerfParser::bottomUpDataAvailable, this,
[this, timeLineProxy](const Data::BottomUpResults& data) {
ResultsUtil::fillEventSourceComboBoxMultiSelect(ui->timeLineEventSource, data.costs,
tr("Show timeline for %1 events."));

auto model = qobject_cast<QStandardItemModel*>(ui->timeLineEventSource->model());
connect(ui->timeLineEventSource->model(), &QStandardItemModel::dataChanged, model,
[timeLineProxy](const QModelIndex& topLeft, const QModelIndex& /*bottomRight*/,
const QVector<int>& /*roles*/) {
auto checkState = topLeft.data(Qt::CheckStateRole).value<Qt::CheckState>();

if (checkState == Qt::CheckState::Checked) {
timeLineProxy->showCostId(topLeft.data(Qt::UserRole + 1).toUInt());
} else {
timeLineProxy->hideCostId(topLeft.data(Qt::UserRole + 1).toUInt());
}
});
});

connect(m_parser, &PerfParser::eventsAvailable, this, [this, eventModel](const Data::EventResults& data) {
eventModel->setData(data);
@@ -101,12 +118,6 @@ TimeLineWidget::TimeLineWidget(PerfParser* parser, QMenu* filterMenu, FilterAndZ
}
});

connect(ui->timeLineEventSource, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
[this](int index) {
const auto typeId = ui->timeLineEventSource->itemData(index).toInt();
m_timeLineDelegate->setEventType(typeId);
});

connect(m_timeLineDelegate, &TimeLineDelegate::addToFavorites, this,
[eventModel](const QModelIndex& index) { eventModel->addToFavorites(index); });
connect(m_timeLineDelegate, &TimeLineDelegate::removeFromFavorites, this,

0 comments on commit 88417d5

Please sign in to comment.