Skip to content

Commit

Permalink
show all costs in timeline delegate
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 Jan 15, 2024
1 parent 2a10dc2 commit e80f5f6
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 22 deletions.
21 changes: 21 additions & 0 deletions 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 @@ -31,6 +43,15 @@ bool EventModelProxy::filterAcceptsRow(int source_row, const QModelIndex& source
return false;
}

auto data = sourceModel()
->index(source_row, EventModel::EventsColumn, source_parent)
.data(EventModel::EventsRole)
.value<Data::Events>();

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

return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent);
}

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 @@ -18,6 +18,8 @@
#include <QTimer>
#include <QTreeView>

#include <QStandardItemModel>

#include "models/costdelegate.h"
#include "models/data.h"
#include "models/filterandzoomstack.h"
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions src/resultsutil.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
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 e80f5f6

Please sign in to comment.