From 5e1dd3348dcb2c71b5ef9c9f6669fd6c23b24369 Mon Sep 17 00:00:00 2001 From: Lieven Hey Date: Thu, 21 Sep 2023 11:32:58 +0200 Subject: [PATCH] add Favourites to TimeLineWidget this allows the user to group important timelines together so that he can compare them better --- src/models/eventmodel.cpp | 42 ++++++++++++++++++++++++++------- src/models/eventmodel.h | 4 ++++ src/models/timelinedelegate.cpp | 13 ++++++++++ src/models/timelinedelegate.h | 1 + src/timelinewidget.cpp | 3 +++ tests/modeltests/tst_models.cpp | 2 +- 6 files changed, 56 insertions(+), 9 deletions(-) diff --git a/src/models/eventmodel.cpp b/src/models/eventmodel.cpp index 2ba3a2ee..565828f9 100644 --- a/src/models/eventmodel.cpp +++ b/src/models/eventmodel.cpp @@ -29,6 +29,7 @@ enum class Tag : quint8 Processes, Threads, Tracepoints, + Favorites, LAST_TAG, }; const auto DATATAG_SHIFT = sizeof(Tag) * 8; @@ -83,6 +84,7 @@ int EventModel::rowCount(const QModelIndex& parent) const case Tag::Cpus: case Tag::Threads: case Tag::Tracepoints: + case Tag::Favorites: return 0; break; case Tag::Processes: @@ -95,11 +97,13 @@ int EventModel::rowCount(const QModelIndex& parent) const return m_processes.size(); case 2: return m_data.tracepoints.size(); + case 3: + return m_favourites.size(); default: Q_ASSERT(false); } case Tag::Root: - return m_data.tracepoints.isEmpty() ? 2 : 3; + return 4; case Tag::LAST_TAG: Q_ASSERT(false); }; @@ -166,6 +170,8 @@ QVariant EventModel::data(const QModelIndex& index, int role) const return tr("Processes"); case 2: return tr("Tracepoints"); + case 3: + return tr("Favorites"); } } else if (role == Qt::ToolTipRole) { if (index.row() == 0) { @@ -175,6 +181,8 @@ QVariant EventModel::data(const QModelIndex& index, int role) const return tr("Event timelines for the individual threads and processes."); } else if (index.row() == 2) { return tr("Event timelines for tracepoints"); + } else if (index.row() == 3) { + return tr("A list of favourites to group important events"); } } else if (role == SortRole) { return index.row(); @@ -244,6 +252,10 @@ QVariant EventModel::data(const QModelIndex& index, int role) const } else if (tag == Tag::Tracepoints) { tracepoint = &m_data.tracepoints[index.row()]; Q_ASSERT(tracepoint); + } else if (tag == Tag::Favorites) { + auto& favourite = m_favourites[index.row()]; + auto res = data(favourite, role); + return res; } if (role == ThreadStartRole) { @@ -467,17 +479,21 @@ 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(Tag::Overview)); case Tag::Overview: // 2nd level children: Cpus and the Processes - if (parent.row() == 0) + if (parent.row() == 0) { return createIndex(row, column, static_cast(Tag::Cpus)); - else if (parent.row() == 1) + } else if (parent.row() == 1) { return createIndex(row, column, static_cast(Tag::Processes)); - else { + } else if (parent.row() == 2) { return createIndex(row, column, static_cast(Tag::Tracepoints)); + } else if (parent.row() == 3) { + return createIndex(row, column, static_cast(Tag::Favorites)); } + break; case Tag::Processes: // 3rd level children: Threads return createIndex(row, column, combineDataTag(Tag::Threads, parent.row())); } @@ -497,14 +513,24 @@ QModelIndex EventModel::parent(const QModelIndex& child) const return createIndex(0, 0, static_cast(Tag::Overview)); case Tag::Processes: return createIndex(1, 0, static_cast(Tag::Overview)); - case Tag::Tracepoints: { + case Tag::Tracepoints: return createIndex(2, 0, static_cast(Tag::Overview)); - } - case Tag::Threads: { + case Tag::Favorites: + return createIndex(3, 0, static_cast(Tag::Overview)); + case Tag::Threads: const auto parentRow = tagData(child.internalId()); return createIndex(parentRow, 0, static_cast(Tag::Processes)); } - } return {}; } + +void EventModel::addToFavourites(const QModelIndex& index) +{ + const int position = m_favourites.size(); + beginInsertRows(createIndex(3, 0), position, position + 1); + m_favourites.push_back(index); + endInsertRows(); + beginResetModel(); + endResetModel(); +} diff --git a/src/models/eventmodel.h b/src/models/eventmodel.h index 53fc0394..c5cbf790 100644 --- a/src/models/eventmodel.h +++ b/src/models/eventmodel.h @@ -71,9 +71,13 @@ class EventModel : public QAbstractItemModel QString name; }; +public: + void addToFavourites(const QModelIndex& index); + private: Data::EventResults m_data; QVector m_processes; + QVector m_favourites; Data::TimeRange m_time; quint64 m_totalOnCpuTime = 0; quint64 m_totalOffCpuTime = 0; diff --git a/src/models/timelinedelegate.cpp b/src/models/timelinedelegate.cpp index f75967cf..1a29e322 100644 --- a/src/models/timelinedelegate.cpp +++ b/src/models/timelinedelegate.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include "../util.h" @@ -133,6 +134,11 @@ Data::Events::const_iterator findEvent(const Data::Events::const_iterator& begin auto it = std::lower_bound(begin, end, time, byTime); // it points to the first item for which our predicate returns false, we want to find the item before that // so decrement it if possible or return begin otherwise + + // it can be end so it->time cases an buffer overflow + if (it == end) { + return end; + } return (it == begin || it->time == time) ? it : (it - 1); } } @@ -456,6 +462,13 @@ bool TimeLineDelegate::eventFilter(QObject* watched, QEvent* event) const auto isMainThread = threadStartTime == minTime && threadEndTime == maxTime; const auto cpuId = index.data(EventModel::CpuIdRole).value(); const auto numCpus = index.data(EventModel::NumCpusRole).value(); + + contextMenu->addAction(QIcon::fromTheme(QStringLiteral("favorite")), tr("Add to favorites"), this, + [this, index] { + auto model = qobject_cast(index.model()); + emit addToFavourites(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); }); diff --git a/src/models/timelinedelegate.h b/src/models/timelinedelegate.h index 3019b41b..9cae9308 100644 --- a/src/models/timelinedelegate.h +++ b/src/models/timelinedelegate.h @@ -64,6 +64,7 @@ class TimeLineDelegate : public QStyledItemDelegate signals: void stacksHovered(const QSet& stacks); + void addToFavourites(const QModelIndex& index); protected: bool eventFilter(QObject* watched, QEvent* event) override; diff --git a/src/timelinewidget.cpp b/src/timelinewidget.cpp index 8acea5ae..9bc40aef 100644 --- a/src/timelinewidget.cpp +++ b/src/timelinewidget.cpp @@ -110,6 +110,9 @@ TimeLineWidget::TimeLineWidget(PerfParser* parser, QMenu* filterMenu, FilterAndZ m_timeLineDelegate->setEventType(typeId); }); + connect(m_timeLineDelegate, &TimeLineDelegate::addToFavourites, this, + [eventModel](const QModelIndex& index) { eventModel->addToFavourites(index); }); + connect(m_timeLineDelegate, &TimeLineDelegate::stacksHovered, this, [this](const QSet& stackIds) { if (stackIds.isEmpty()) { ++m_currentHoverStacksJobId; diff --git a/tests/modeltests/tst_models.cpp b/tests/modeltests/tst_models.cpp index 482e6615..a0e57b82 100644 --- a/tests/modeltests/tst_models.cpp +++ b/tests/modeltests/tst_models.cpp @@ -537,7 +537,7 @@ private slots: model.setData(events); QCOMPARE(model.columnCount(), static_cast(EventModel::NUM_COLUMNS)); - QCOMPARE(model.rowCount(), 2); + QCOMPARE(model.rowCount(), 4); auto simplifiedEvents = events; simplifiedEvents.cpus.remove(1);