From 95d7be1b28a280ef2bd0c3ecabd8ed2e122d6e8a Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Mon, 7 May 2018 17:08:12 +0200 Subject: [PATCH] Tracing: Generalize event filtering This allows us to not only filter by ranges, but potentially also by other criteria. Change-Id: I7349ceeabbb2781473a3a4c803dab1006b7b8e50 Reviewed-by: Christian Kandeler --- src/libs/tracing/timelinetracemanager.cpp | 7 +- src/libs/tracing/timelinetracemanager.h | 6 +- src/plugins/qmlprofiler/flamegraphmodel.cpp | 7 +- .../qmlprofiler/qmlprofilermodelmanager.cpp | 167 ++++++++++-------- .../qmlprofiler/qmlprofilermodelmanager.h | 10 +- .../qmlprofilerstatisticsmodel.cpp | 7 +- .../qmlprofiler/qmlprofilertracefile.cpp | 4 +- .../tests/qmlprofilertraceclient_test.cpp | 2 +- 8 files changed, 119 insertions(+), 91 deletions(-) diff --git a/src/libs/tracing/timelinetracemanager.cpp b/src/libs/tracing/timelinetracemanager.cpp index 5358517b944..e2d62ddae3f 100644 --- a/src/libs/tracing/timelinetracemanager.cpp +++ b/src/libs/tracing/timelinetracemanager.cpp @@ -365,7 +365,7 @@ void TimelineTraceManager::clearAll() clearTypeStorage(); } -void TimelineTraceManager::restrictToRange(qint64 startTime, qint64 endTime) +void TimelineTraceManager::restrictByFilter(TraceEventFilter filter) { if (d->notesModel) d->notesModel->stash(); @@ -374,9 +374,8 @@ void TimelineTraceManager::restrictToRange(qint64 startTime, qint64 endTime) setVisibleFeatures(0); QFutureInterface future; - replayEvents(startTime, endTime, - std::bind(&TimelineTraceManagerPrivate::dispatch, d, std::placeholders::_1, - std::placeholders::_2), + replayEvents(filter(std::bind(&TimelineTraceManagerPrivate::dispatch, d, + std::placeholders::_1, std::placeholders::_2)), [this]() { initialize(); }, [this]() { diff --git a/src/libs/tracing/timelinetracemanager.h b/src/libs/tracing/timelinetracemanager.h index a71ddb04d48..4acefbe345d 100644 --- a/src/libs/tracing/timelinetracemanager.h +++ b/src/libs/tracing/timelinetracemanager.h @@ -43,6 +43,7 @@ class TRACING_EXPORT TimelineTraceManager : public QObject Q_OBJECT public: typedef std::function TraceEventLoader; + typedef std::function TraceEventFilter; typedef std::function Initializer; typedef std::function Finalizer; typedef std::function Clearer; @@ -99,15 +100,14 @@ protected: virtual void clearEventStorage(); virtual void clearTypeStorage(); - void restrictToRange(qint64 startTime, qint64 endTime); + void restrictByFilter(TraceEventFilter filter); void addEvent(const TraceEvent &event); void addEventType(const TraceEventType &type); virtual const TraceEventType &lookupType(int typeId) const = 0; virtual TimelineTraceFile *createTraceFile() = 0; - virtual void replayEvents(qint64 rangeStart, qint64 rangeEnd, TraceEventLoader loader, - Initializer initializer, Finalizer finalizer, + virtual void replayEvents(TraceEventLoader loader, Initializer initializer, Finalizer finalizer, ErrorHandler errorHandler, QFutureInterface &future) const = 0; private: diff --git a/src/plugins/qmlprofiler/flamegraphmodel.cpp b/src/plugins/qmlprofiler/flamegraphmodel.cpp index 9621cf9bf24..250bb8cbdbf 100644 --- a/src/plugins/qmlprofiler/flamegraphmodel.cpp +++ b/src/plugins/qmlprofiler/flamegraphmodel.cpp @@ -163,9 +163,10 @@ void FlameGraphModel::restrictToFeatures(quint64 visibleFeatures) clear(); QFutureInterface future; - m_modelManager->replayQmlEvents(m_modelManager->traceStart(), m_modelManager->traceEnd(), - std::bind(&FlameGraphModel::loadEvent, this, - std::placeholders::_1, std::placeholders::_2), + const auto filter = m_modelManager->rangeFilter(m_modelManager->traceStart(), + m_modelManager->traceEnd()); + m_modelManager->replayQmlEvents(filter(std::bind(&FlameGraphModel::loadEvent, this, + std::placeholders::_1, std::placeholders::_2)), std::bind(&FlameGraphModel::beginResetModel, this), std::bind(&FlameGraphModel::finalize, this), [this](const QString &message) { diff --git a/src/plugins/qmlprofiler/qmlprofilermodelmanager.cpp b/src/plugins/qmlprofiler/qmlprofilermodelmanager.cpp index 28079373b1d..42b5f280c43 100644 --- a/src/plugins/qmlprofiler/qmlprofilermodelmanager.cpp +++ b/src/plugins/qmlprofiler/qmlprofilermodelmanager.cpp @@ -146,13 +146,12 @@ const QmlEventType &QmlProfilerModelManager::eventType(int typeId) const return d->eventTypes.at(typeId); } -void QmlProfilerModelManager::replayEvents(qint64 rangeStart, qint64 rangeEnd, - TraceEventLoader loader, Initializer initializer, +void QmlProfilerModelManager::replayEvents(TraceEventLoader loader, Initializer initializer, Finalizer finalizer, ErrorHandler errorHandler, QFutureInterface &future) const { - replayQmlEvents(rangeStart, rangeEnd, static_cast(loader), initializer, - finalizer, errorHandler, future); + replayQmlEvents(static_cast(loader), initializer, finalizer, errorHandler, + future); } static bool isStateful(const QmlEventType &type) @@ -164,81 +163,19 @@ static bool isStateful(const QmlEventType &type) return message == PixmapCacheEvent || message == MemoryAllocation; } -void QmlProfilerModelManager::replayQmlEvents(qint64 rangeStart, qint64 rangeEnd, - QmlEventLoader loader, Initializer initializer, - Finalizer finalizer, ErrorHandler errorHandler, +void QmlProfilerModelManager::replayQmlEvents(QmlEventLoader loader, + Initializer initializer, Finalizer finalizer, + ErrorHandler errorHandler, QFutureInterface &future) const { if (initializer) initializer(); - QStack stack; - bool crossedRangeStart = false; - const auto result = d->file.replay([&](const QmlEvent &event) { if (future.isCanceled()) return false; - const QmlEventType &type = d->eventTypes[event.typeIndex()]; - - // No restrictions: load all events - if (rangeStart == -1 || rangeEnd == -1) { - loader(event, type); - return true; - } - - // Double-check if rangeStart has been crossed. Some versions of Qt send dirty data. - qint64 adjustedTimestamp = event.timestamp(); - if (event.timestamp() < rangeStart && !crossedRangeStart) { - if (type.rangeType() != MaximumRangeType) { - if (event.rangeStage() == RangeStart) - stack.push(event); - else if (event.rangeStage() == RangeEnd) - stack.pop(); - return true; - } else if (isStateful(type)) { - adjustedTimestamp = rangeStart; - } else { - return true; - } - } else { - if (!crossedRangeStart) { - for (QmlEvent stashed : stack) { - stashed.setTimestamp(rangeStart); - loader(stashed, d->eventTypes[stashed.typeIndex()]); - } - stack.clear(); - crossedRangeStart = true; - } - if (event.timestamp() > rangeEnd) { - if (type.rangeType() != MaximumRangeType) { - if (event.rangeStage() == RangeEnd) { - if (stack.isEmpty()) { - QmlEvent endEvent(event); - endEvent.setTimestamp(rangeEnd); - loader(endEvent, d->eventTypes[event.typeIndex()]); - } else { - stack.pop(); - } - } else if (event.rangeStage() == RangeStart) { - stack.push(event); - } - return true; - } else if (isStateful(type)) { - adjustedTimestamp = rangeEnd; - } else { - return true; - } - } - } - - if (adjustedTimestamp != event.timestamp()) { - QmlEvent adjusted(event); - adjusted.setTimestamp(adjustedTimestamp); - loader(adjusted, type); - } else { - loader(event, type); - } + loader(event, d->eventTypes[event.typeIndex()]); return true; }); @@ -370,6 +307,22 @@ void QmlProfilerModelManager::detailsChanged(int typeId, const QString &newStrin emit typeDetailsChanged(typeId); } +void QmlProfilerModelManager::restrictByFilter(QmlProfilerModelManager::QmlEventFilter filter) +{ + return Timeline::TimelineTraceManager::restrictByFilter([filter](TraceEventLoader loader) { + const auto filteredQmlLoader = filter([loader](const QmlEvent &event, + const QmlEventType &type) { + loader(event, type); + }); + + return [filteredQmlLoader](const Timeline::TraceEvent &event, + const Timeline::TraceEventType &type) { + filteredQmlLoader(static_cast(event), + static_cast(type)); + }; + }); +} + const Timeline::TraceEventType &QmlProfilerModelManager::lookupType(int typeIndex) const { return eventType(typeIndex); @@ -404,7 +357,7 @@ void QmlProfilerModelManager::addEvent(const QmlEvent &event) void QmlProfilerModelManager::restrictToRange(qint64 start, qint64 end) { d->isRestrictedToRange = (start != -1 || end != -1); - TimelineTraceManager::restrictToRange(start, end); + restrictByFilter(rangeFilter(start, end)); } bool QmlProfilerModelManager::isRestrictedToRange() const @@ -412,6 +365,78 @@ bool QmlProfilerModelManager::isRestrictedToRange() const return d->isRestrictedToRange; } +QmlProfilerModelManager::QmlEventFilter +QmlProfilerModelManager::rangeFilter(qint64 rangeStart, qint64 rangeEnd) const +{ + return [rangeStart, rangeEnd, this] (QmlEventLoader loader) { + QStack stack; + bool crossedRangeStart = false; + + return [=](const QmlEvent &event, const QmlEventType &type) mutable { + + // No restrictions: load all events + if (rangeStart == -1 || rangeEnd == -1) { + loader(event, type); + return true; + } + + // Double-check if rangeStart has been crossed. Some versions of Qt send dirty data. + qint64 adjustedTimestamp = event.timestamp(); + if (event.timestamp() < rangeStart && !crossedRangeStart) { + if (type.rangeType() != MaximumRangeType) { + if (event.rangeStage() == RangeStart) + stack.push(event); + else if (event.rangeStage() == RangeEnd) + stack.pop(); + return true; + } else if (isStateful(type)) { + adjustedTimestamp = rangeStart; + } else { + return true; + } + } else { + if (!crossedRangeStart) { + for (auto stashed : stack) { + stashed.setTimestamp(rangeStart); + loader(stashed, eventType(stashed.typeIndex())); + } + stack.clear(); + crossedRangeStart = true; + } + if (event.timestamp() > rangeEnd) { + if (type.rangeType() != MaximumRangeType) { + if (event.rangeStage() == RangeEnd) { + if (stack.isEmpty()) { + QmlEvent endEvent(event); + endEvent.setTimestamp(rangeEnd); + loader(endEvent, type); + } else { + stack.pop(); + } + } else if (event.rangeStage() == RangeStart) { + stack.push(event); + } + return true; + } else if (isStateful(type)) { + adjustedTimestamp = rangeEnd; + } else { + return true; + } + } + } + + if (adjustedTimestamp != event.timestamp()) { + QmlEvent adjusted(event); + adjusted.setTimestamp(adjustedTimestamp); + loader(adjusted, type); + } else { + loader(event, type); + } + return true; + }; + }; +} + Timeline::TimelineTraceFile *QmlProfilerModelManager::createTraceFile() { return new Internal::QmlProfilerTraceFile(this); diff --git a/src/plugins/qmlprofiler/qmlprofilermodelmanager.h b/src/plugins/qmlprofiler/qmlprofilermodelmanager.h index 033818d9fe9..b7180b36fab 100644 --- a/src/plugins/qmlprofiler/qmlprofilermodelmanager.h +++ b/src/plugins/qmlprofiler/qmlprofilermodelmanager.h @@ -50,6 +50,7 @@ class QMLPROFILER_EXPORT QmlProfilerModelManager : public Timeline::TimelineTrac Q_OBJECT public: typedef std::function QmlEventLoader; + typedef std::function QmlEventFilter; explicit QmlProfilerModelManager(QObject *parent = nullptr); ~QmlProfilerModelManager() override; @@ -64,8 +65,7 @@ public: void addEventTypes(const QVector &types); const QmlEventType &eventType(int typeId) const; - void replayQmlEvents(qint64 rangeStart, qint64 rangeEnd, QmlEventLoader loader, - Initializer initializer, Finalizer finalizer, + void replayQmlEvents(QmlEventLoader loader, Initializer initializer, Finalizer finalizer, ErrorHandler errorHandler, QFutureInterface &future) const; void finalize() override; @@ -81,20 +81,22 @@ public: void restrictToRange(qint64 start, qint64 end); bool isRestrictedToRange() const; + QmlEventFilter rangeFilter(qint64 start, qint64 end) const; + signals: void typeDetailsChanged(int typeId); void typeDetailsFinished(); private: void detailsChanged(int typeId, const QString &newString); + void restrictByFilter(QmlEventFilter filter); void clearEventStorage() override; void clearTypeStorage() override; const Timeline::TraceEventType &lookupType(int typeId) const override; Timeline::TimelineTraceFile *createTraceFile() override; - void replayEvents(qint64 rangeStart, qint64 rangeEnd, TraceEventLoader loader, - Initializer initializer, Finalizer finalizer, + void replayEvents(TraceEventLoader loader, Initializer initializer, Finalizer finalizer, ErrorHandler errorHandler, QFutureInterface &future) const override; class QmlProfilerModelManagerPrivate; diff --git a/src/plugins/qmlprofiler/qmlprofilerstatisticsmodel.cpp b/src/plugins/qmlprofiler/qmlprofilerstatisticsmodel.cpp index de1c3b9ea2e..81e1168f25d 100644 --- a/src/plugins/qmlprofiler/qmlprofilerstatisticsmodel.cpp +++ b/src/plugins/qmlprofiler/qmlprofilerstatisticsmodel.cpp @@ -102,9 +102,10 @@ void QmlProfilerStatisticsModel::restrictToFeatures(quint64 features) clear(); QFutureInterface future; - m_modelManager->replayQmlEvents(m_modelManager->traceStart(), m_modelManager->traceEnd(), - std::bind(&QmlProfilerStatisticsModel::loadEvent, this, - std::placeholders::_1, std::placeholders::_2), + auto filter = m_modelManager->rangeFilter(m_modelManager->traceStart(), + m_modelManager->traceEnd()); + m_modelManager->replayQmlEvents(filter(std::bind(&QmlProfilerStatisticsModel::loadEvent, this, + std::placeholders::_1, std::placeholders::_2)), std::bind(&QmlProfilerStatisticsModel::beginResetModel, this), [this]() { finalize(); diff --git a/src/plugins/qmlprofiler/qmlprofilertracefile.cpp b/src/plugins/qmlprofiler/qmlprofilertracefile.cpp index 46c9738622b..4a79b36bed9 100644 --- a/src/plugins/qmlprofiler/qmlprofilertracefile.cpp +++ b/src/plugins/qmlprofiler/qmlprofilertracefile.cpp @@ -712,7 +712,7 @@ void QmlProfilerTraceFile::saveQtd(QIODevice *device) QStack stack; qint64 lastProgressTimestamp = traceStart(); - modelManager()->replayQmlEvents(-1, -1, [&](const QmlEvent &event, const QmlEventType &type) { + modelManager()->replayQmlEvents([&](const QmlEvent &event, const QmlEventType &type) { if (type.rangeType() != MaximumRangeType && event.rangeStage() == RangeStart) { stack.push(event); return; @@ -858,7 +858,7 @@ void QmlProfilerTraceFile::saveQzt(QIODevice *device) } qint64 lastProgressTimestamp = traceStart(); - modelManager()->replayQmlEvents(-1, -1, [&](const QmlEvent &event, const QmlEventType &type) { + modelManager()->replayQmlEvents([&](const QmlEvent &event, const QmlEventType &type) { Q_UNUSED(type); bufferStream << event; // 32MB buffer should be plenty for efficient compression diff --git a/src/plugins/qmlprofiler/tests/qmlprofilertraceclient_test.cpp b/src/plugins/qmlprofiler/tests/qmlprofilertraceclient_test.cpp index 3642e829b44..f8c189b275c 100644 --- a/src/plugins/qmlprofiler/tests/qmlprofilertraceclient_test.cpp +++ b/src/plugins/qmlprofiler/tests/qmlprofilertraceclient_test.cpp @@ -70,7 +70,7 @@ void QmlProfilerTraceClientTest::testMessageReceived() traceClient.stateChanged(QmlDebug::QmlDebugClient::NotConnected); QFutureInterface future; - modelManager.replayQmlEvents(-1, -1, [&](const QmlEvent &event, const QmlEventType &type) { + modelManager.replayQmlEvents([&](const QmlEvent &event, const QmlEventType &type) { qint64 timestamp; qint32 message; qint32 rangeType;