Skip to content

Commit

Permalink
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.
  • Loading branch information
lievenhey committed Nov 19, 2024
1 parent fd61b13 commit 2cc266d
Show file tree
Hide file tree
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
Expand Up @@ -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
Expand All @@ -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;
}

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

#pragma once

#include <QSet>
#include <QSortFilterProxyModel>

class EventModelProxy : public QSortFilterProxyModel
Expand All @@ -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
Expand Up @@ -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;

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion src/models/timelinedelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -71,6 +70,7 @@ class TimeLineDelegate : public QStyledItemDelegate
bool eventFilter(QObject* watched, QEvent* event) override;

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

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

#include <QStandardItemModel>

#include "models/costdelegate.h"
#include "models/data.h"
#include "models/filterandzoomstack.h"
Expand Down Expand Up @@ -219,6 +221,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
Expand Down
1 change: 1 addition & 0 deletions src/resultsutil.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,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
Expand Up @@ -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>
Expand Down Expand Up @@ -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);
Expand All @@ -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,
Expand Down

0 comments on commit 2cc266d

Please sign in to comment.