Skip to content

Commit

Permalink
feat: add Favourites to TimeLineWidget
Browse files Browse the repository at this point in the history
this allows the user to group important timelines together so that he
can compare them better
  • Loading branch information
lievenhey committed Nov 19, 2024
1 parent 623cc98 commit 72b2321
Show file tree
Hide file tree
Showing 6 changed files with 213 additions and 59 deletions.
102 changes: 99 additions & 3 deletions src/models/eventmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,19 @@ enum class Tag : quint8
Processes,
Threads,
Tracepoints,
Favorites,
};

enum OverviewRow : quint8
{
CpuRow,
ProcessRow,
TracepointRow,
FavoriteRow,
};
constexpr auto numRows = TracepointRow + 1;
constexpr auto numRows = FavoriteRow + 1;

constexpr auto LAST_TAG = Tag::Tracepoints;
constexpr auto LAST_TAG = Tag::Favorites;

const auto DATATAG_SHIFT = sizeof(Tag) * 8;
const auto DATATAG_UNSHIFT = (sizeof(quintptr) - sizeof(Tag)) * 8;
Expand Down Expand Up @@ -87,6 +89,7 @@ int EventModel::rowCount(const QModelIndex& parent) const
case Tag::Cpus:
case Tag::Threads:
case Tag::Tracepoints:
case Tag::Favorites:
return 0;
case Tag::Processes:
return m_processes.value(parent.row()).threads.size();
Expand All @@ -98,6 +101,8 @@ int EventModel::rowCount(const QModelIndex& parent) const
return m_processes.size();
case OverviewRow::TracepointRow:
return m_data.tracepoints.size();
case OverviewRow::FavoriteRow:
return m_favourites.size();
}
Q_UNREACHABLE();
case Tag::Root:
Expand Down Expand Up @@ -165,6 +170,8 @@ QVariant EventModel::data(const QModelIndex& index, int role) const
return tr("Processes");
case OverviewRow::TracepointRow:
return tr("Tracepoints");
case OverviewRow::FavoriteRow:
return tr("Favorites");
}
} else if (role == Qt::ToolTipRole) {
switch (static_cast<OverviewRow>(index.row())) {
Expand All @@ -175,6 +182,8 @@ QVariant EventModel::data(const QModelIndex& index, int role) const
return tr("Event timelines for the individual threads and processes.");
case OverviewRow::TracepointRow:
return tr("Event timelines for tracepoints");
case OverviewRow::FavoriteRow:
return tr("A list of favourites to group important events");
}
} else if (role == SortRole) {
return index.row();
Expand Down Expand Up @@ -242,6 +251,13 @@ QVariant EventModel::data(const QModelIndex& index, int role) const
Q_ASSERT(thread);
} else if (tag == Tag::Tracepoints) {
tracepoint = &m_data.tracepoints[index.row()];
} else if (tag == Tag::Favorites) {
if (role == IsFavoriteRole) {
return true;
}

const auto& favourite = m_favourites[index.row()];
return data(favourite.siblingAtColumn(index.column()), role);
}

if (role == ThreadStartRole) {
Expand All @@ -255,6 +271,9 @@ QVariant EventModel::data(const QModelIndex& index, int role) const
case Tag::Processes:
case Tag::Tracepoints:
return m_time.start;
case Tag::Favorites:
// they are handled elsewhere
Q_UNREACHABLE();
}
} else if (role == ThreadEndRole) {
switch (tag) {
Expand All @@ -267,6 +286,9 @@ QVariant EventModel::data(const QModelIndex& index, int role) const
case Tag::Processes:
case Tag::Tracepoints:
return m_time.end;
case Tag::Favorites:
// they are handled elsewhere
Q_UNREACHABLE();
}
} else if (role == ThreadNameRole) {
switch (tag) {
Expand All @@ -280,6 +302,9 @@ QVariant EventModel::data(const QModelIndex& index, int role) const
case Tag::Processes:
case Tag::Tracepoints:
return {};
case Tag::Favorites:
// they are handled elsewhere
Q_UNREACHABLE();
}
} else if (role == ThreadIdRole) {
switch (tag) {
Expand All @@ -292,6 +317,9 @@ QVariant EventModel::data(const QModelIndex& index, int role) const
case Tag::Processes:
case Tag::Tracepoints:
return Data::INVALID_TID;
case Tag::Favorites:
// they are handled elsewhere
Q_UNREACHABLE();
}
} else if (role == ProcessIdRole) {
switch (tag) {
Expand All @@ -304,6 +332,9 @@ QVariant EventModel::data(const QModelIndex& index, int role) const
case Tag::Processes:
case Tag::Tracepoints:
return Data::INVALID_PID;
case Tag::Favorites:
// they are handled elsewhere
Q_UNREACHABLE();
}
} else if (role == CpuIdRole) {
switch (tag) {
Expand All @@ -316,6 +347,9 @@ QVariant EventModel::data(const QModelIndex& index, int role) const
case Tag::Threads:
case Tag::Tracepoints:
return Data::INVALID_CPU_ID;
case Tag::Favorites:
// they are handled elsewhere
Q_UNREACHABLE();
}
} else if (role == EventsRole) {
switch (tag) {
Expand All @@ -330,6 +364,9 @@ QVariant EventModel::data(const QModelIndex& index, int role) const
case Tag::Overview:
case Tag::Processes:
return {};
case Tag::Favorites:
// they are handled elsewhere
Q_UNREACHABLE();
}
} else if (role == SortRole) {
if (index.column() == ThreadColumn) {
Expand All @@ -345,6 +382,9 @@ QVariant EventModel::data(const QModelIndex& index, int role) const
case Tag::Overview:
case Tag::Processes:
return {};
case Tag::Favorites:
// they are handled elsewhere
Q_UNREACHABLE();
}
} else {
switch (tag) {
Expand All @@ -359,8 +399,13 @@ QVariant EventModel::data(const QModelIndex& index, int role) const
case Tag::Overview:
case Tag::Processes:
return {};
case Tag::Favorites:
// they are handled elsewhere
Q_UNREACHABLE();
}
}
} else if (role == IsFavoriteRole) {
return false;
}

switch (static_cast<Columns>(index.column())) {
Expand All @@ -378,6 +423,9 @@ QVariant EventModel::data(const QModelIndex& index, int role) const
case Tag::Overview:
case Tag::Processes:
return {};
case Tag::Favorites:
// they are handled elsewhere
Q_UNREACHABLE();
}
} else if (role == Qt::ToolTipRole) {
QString tooltip;
Expand Down Expand Up @@ -418,6 +466,9 @@ QVariant EventModel::data(const QModelIndex& index, int role) const
case Tag::Overview:
case Tag::Processes:
return {};
case Tag::Favorites:
// they are handled elsewhere
Q_UNREACHABLE();
}

tooltip += tr("Number of Events: %1 (%2% of the total)")
Expand All @@ -439,6 +490,9 @@ QVariant EventModel::data(const QModelIndex& index, int role) const
case Tag::Overview:
case Tag::Processes:
return {};
case Tag::Favorites:
// they are handled elsewhere
Q_UNREACHABLE();
}
}
break;
Expand All @@ -453,6 +507,8 @@ QVariant EventModel::data(const QModelIndex& index, int role) const
void EventModel::setData(const Data::EventResults& data)
{
beginResetModel();
m_favourites.clear();

m_data = data;
m_totalEvents = 0;
m_maxCost = 0;
Expand Down Expand Up @@ -513,6 +569,7 @@ QModelIndex EventModel::index(int row, int column, const QModelIndex& parent) co
case Tag::Cpus:
case Tag::Tracepoints:
case Tag::Threads:
case Tag::Favorites:
break;
case Tag::Root: // root has the 1st level children: Overview
return createIndex(row, column, static_cast<quintptr>(Tag::Overview));
Expand All @@ -524,6 +581,8 @@ QModelIndex EventModel::index(int row, int column, const QModelIndex& parent) co
return createIndex(row, column, static_cast<quintptr>(Tag::Processes));
case OverviewRow::TracepointRow:
return createIndex(row, column, static_cast<quintptr>(Tag::Tracepoints));
case OverviewRow::FavoriteRow:
return createIndex(row, column, static_cast<quintptr>(Tag::Favorites));
}
Q_UNREACHABLE();
case Tag::Processes: // 3rd level children: Threads
Expand All @@ -545,7 +604,9 @@ QModelIndex EventModel::parent(const QModelIndex& child) const
case Tag::Processes:
return createIndex(OverviewRow::ProcessRow, 0, static_cast<quintptr>(Tag::Overview));
case Tag::Tracepoints:
return createIndex(OverviewRow::TracepointRow, 0, static_cast<qintptr>(Tag::Overview));
return createIndex(OverviewRow::TracepointRow, 0, static_cast<quintptr>(Tag::Overview));
case Tag::Favorites:
return createIndex(OverviewRow::FavoriteRow, 0, static_cast<quintptr>(Tag::Overview));
case Tag::Threads: {
const auto parentRow = tagData(child.internalId());
return createIndex(parentRow, 0, static_cast<quintptr>(Tag::Processes));
Expand All @@ -554,3 +615,38 @@ QModelIndex EventModel::parent(const QModelIndex& child) const

return {};
}

void EventModel::addToFavorites(const QModelIndex& index)
{
Q_ASSERT(index.model() == this);

if (index.column() != 0) {
// we only want one index per row, so we force it to be column zero
// this way we can easily check if we have duplicate rows
addToFavorites(index.siblingAtColumn(0));
return;
}

if (m_favourites.contains(index)) {
return;
}

const auto row = m_favourites.size();

beginInsertRows(createIndex(FavoriteRow, 0, static_cast<quintptr>(Tag::Overview)), row, row);
m_favourites.push_back(index);
endInsertRows();
}

void EventModel::removeFromFavorites(const QModelIndex& index)
{
Q_ASSERT(index.model() == this);
Q_ASSERT(dataTag(index) == Tag::Favorites);

const auto row = index.row();
Q_ASSERT(row >= 0 && row < m_favourites.size());

beginRemoveRows(createIndex(FavoriteRow, 0, static_cast<quintptr>(Tag::Overview)), row, row);
m_favourites.remove(row);
endRemoveRows();
}
6 changes: 6 additions & 0 deletions src/models/eventmodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class EventModel : public QAbstractItemModel
SortRole,
TotalCostsRole,
EventResultsRole,
IsFavoriteRole,
};

int rowCount(const QModelIndex& parent = {}) const override;
Expand Down Expand Up @@ -71,9 +72,14 @@ class EventModel : public QAbstractItemModel
QString name;
};

public:
void addToFavorites(const QModelIndex& index);
void removeFromFavorites(const QModelIndex& index);

private:
Data::EventResults m_data;
QVector<Process> m_processes;
QVector<QModelIndex> m_favourites;
Data::TimeRange m_time;
quint64 m_totalOnCpuTime = 0;
quint64 m_totalOffCpuTime = 0;
Expand Down
15 changes: 15 additions & 0 deletions src/models/timelinedelegate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <QHelpEvent>
#include <QMenu>
#include <QPainter>
#include <QSortFilterProxyModel>
#include <QToolTip>

#include "../util.h"
Expand Down Expand Up @@ -459,6 +460,20 @@ bool TimeLineDelegate::eventFilter(QObject* watched, QEvent* event)
const auto isMainThread = threadStartTime == minTime && threadEndTime == maxTime;
const auto cpuId = index.data(EventModel::CpuIdRole).value<quint32>();
const auto numCpus = index.data(EventModel::NumCpusRole).value<uint>();
const auto isFavorite = index.data(EventModel::IsFavoriteRole).value<bool>();

contextMenu->addAction(QIcon::fromTheme(QStringLiteral("favorite")),
isFavorite ? tr("Remove from favorites") : tr("Add to favorites"), this,
[this, index, isFavorite] {
auto model = qobject_cast<const QSortFilterProxyModel*>(index.model());
Q_ASSERT(model);
if (isFavorite) {
emit removeFromFavorites(model->mapToSource(index));
} else {
emit addToFavorites(model->mapToSource(index));
}
});

if (isTimeSpanSelected && (minTime != timeSlice.start || maxTime != timeSlice.end)) {
contextMenu->addAction(QIcon::fromTheme(QStringLiteral("zoom-in")), tr("Zoom In On Selection"), this,
[this, timeSlice]() { m_filterAndZoomStack->zoomIn(timeSlice); });
Expand Down
2 changes: 2 additions & 0 deletions src/models/timelinedelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ class TimeLineDelegate : public QStyledItemDelegate

signals:
void stacksHovered(const QSet<qint32>& stacks);
void addToFavorites(const QModelIndex& index);
void removeFromFavorites(const QModelIndex& index);

protected:
bool eventFilter(QObject* watched, QEvent* event) override;
Expand Down
5 changes: 5 additions & 0 deletions src/timelinewidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,11 @@ TimeLineWidget::TimeLineWidget(PerfParser* parser, QMenu* filterMenu, FilterAndZ
m_timeLineDelegate->setEventType(typeId);
});

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

connect(m_timeLineDelegate, &TimeLineDelegate::stacksHovered, this, [this](const QSet<qint32>& stackIds) {
if (stackIds.isEmpty()) {
++m_currentHoverStacksJobId;
Expand Down
Loading

0 comments on commit 72b2321

Please sign in to comment.