diff --git a/src/models/eventmodelproxy.cpp b/src/models/eventmodelproxy.cpp index 19e96a81..721d86d6 100644 --- a/src/models/eventmodelproxy.cpp +++ b/src/models/eventmodelproxy.cpp @@ -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(); - if (data.empty()) { + if (data.empty() || m_hiddenCostIds.contains(data[0].type)) { return false; } diff --git a/src/models/eventmodelproxy.h b/src/models/eventmodelproxy.h index eaedf37a..a382aa3c 100644 --- a/src/models/eventmodelproxy.h +++ b/src/models/eventmodelproxy.h @@ -7,6 +7,7 @@ #pragma once +#include #include 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 m_hiddenCostIds; }; diff --git a/src/models/timelinedelegate.cpp b/src/models/timelinedelegate.cpp index 48d90edc..b4c1c831 100644 --- a/src/models/timelinedelegate.cpp +++ b/src/models/timelinedelegate.cpp @@ -161,7 +161,6 @@ void TimeLineDelegate::paint(QPainter* painter, const QStyleOptionViewItem& opti const auto results = index.data(EventModel::EventResultsRole).value(); 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, diff --git a/src/models/timelinedelegate.h b/src/models/timelinedelegate.h index 47e5e2bb..af610b0e 100644 --- a/src/models/timelinedelegate.h +++ b/src/models/timelinedelegate.h @@ -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& 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(); diff --git a/src/resultsutil.cpp b/src/resultsutil.cpp index 6fac24f7..7699b229 100644 --- a/src/resultsutil.cpp +++ b/src/resultsutil.cpp @@ -19,6 +19,8 @@ #include #include +#include + #include "models/costdelegate.h" #include "models/data.h" #include "models/filterandzoomstack.h" @@ -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 diff --git a/src/resultsutil.h b/src/resultsutil.h index 568d8870..0551a007 100644 --- a/src/resultsutil.h +++ b/src/resultsutil.h @@ -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); } diff --git a/src/timelinewidget.cpp b/src/timelinewidget.cpp index 8f6cc1b5..e714421c 100644 --- a/src/timelinewidget.cpp +++ b/src/timelinewidget.cpp @@ -17,9 +17,11 @@ #include "parsers/perf/perfparser.h" #include +#include #include #include #include +#include #include #include @@ -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(ui->timeLineEventSource->model()); + connect(ui->timeLineEventSource->model(), &QStandardItemModel::dataChanged, model, + [timeLineProxy](const QModelIndex& topLeft, const QModelIndex& /*bottomRight*/, + const QVector& /*roles*/) { + auto checkState = topLeft.data(Qt::CheckStateRole).value(); + + 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(&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,