From 0d5f5bdd6cb1d3a6e01638351d2c617732ad38e3 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Thu, 8 Sep 2016 18:26:55 +0200 Subject: [PATCH] QmlProfiler: Keep Compile and QML/JS statistics separate The QML compiler can run asynchronously and produce ranges that don't match up with other QML/JS ranges. The statistics model assumes that all ranges are perfectly nested, and produces incorrect data if they aren't. The compile ranges are perfectly nested among themselves, and the other QML/JS ranges are also perfectly nested among themselves, so we can fix this by keeping separate statistics for them. Also, choose a less insane data structure for the relatives model. Change-Id: I146593b67586e7b5aba6b19d360961c142511289 Reviewed-by: Kai Koehne --- .../qmlprofilerstatisticsmodel.cpp | 67 +++++++++---------- .../qmlprofiler/qmlprofilerstatisticsmodel.h | 15 +++-- 2 files changed, 39 insertions(+), 43 deletions(-) diff --git a/src/plugins/qmlprofiler/qmlprofilerstatisticsmodel.cpp b/src/plugins/qmlprofiler/qmlprofilerstatisticsmodel.cpp index c2e1b92e5f7..c251650e4ff 100644 --- a/src/plugins/qmlprofiler/qmlprofilerstatisticsmodel.cpp +++ b/src/plugins/qmlprofiler/qmlprofilerstatisticsmodel.cpp @@ -30,11 +30,9 @@ #include #include -#include #include -#include -#include #include +#include #include #include @@ -60,6 +58,7 @@ public: QHash notes; QStack callStack; + QStack compileStack; qint64 qmlTime = 0; qint64 lastEndTime = 0; QHash > durations; @@ -71,6 +70,7 @@ QmlProfilerStatisticsModel::QmlProfilerStatisticsModel(QmlProfilerModelManager * { d->modelManager = modelManager; d->callStack.push(QmlEvent()); + d->compileStack.push(QmlEvent()); connect(modelManager, &QmlProfilerModelManager::stateChanged, this, &QmlProfilerStatisticsModel::dataChanged); connect(modelManager->notesModel(), &Timeline::TimelineNotesModel::changed, @@ -143,7 +143,9 @@ void QmlProfilerStatisticsModel::clear() d->eventsInBindingLoop.clear(); d->notes.clear(); d->callStack.clear(); + d->compileStack.clear(); d->callStack.push(QmlEvent()); + d->compileStack.push(QmlEvent()); d->qmlTime = 0; d->lastEndTime = 0; d->durations.clear(); @@ -210,22 +212,25 @@ void QmlProfilerStatisticsModel::loadEvent(const QmlEvent &event, const QmlEvent if (!d->acceptedTypes.contains(type.rangeType())) return; + QStack &stack = type.rangeType() == Compiling ? d->compileStack : d->callStack; switch (event.rangeStage()) { case RangeStart: // binding loop detection: check whether event is already in stack - for (int ii = 1; ii < d->callStack.size(); ++ii) { - if (d->callStack.at(ii).typeIndex() == event.typeIndex() - && type.rangeType() != Javascript) { - d->eventsInBindingLoop.insert(event.typeIndex()); - break; + if (type.rangeType() == Binding || type.rangeType() == HandlingSignal) { + for (int ii = 1; ii < stack.size(); ++ii) { + if (stack.at(ii).typeIndex() == event.typeIndex()) { + d->eventsInBindingLoop.insert(event.typeIndex()); + break; + } } } - d->callStack.push(event); + stack.push(event); break; case RangeEnd: { // update stats + QmlEventStats *stats = &d->data[event.typeIndex()]; - qint64 duration = event.timestamp() - d->callStack.top().timestamp(); + qint64 duration = event.timestamp() - stack.top().timestamp(); stats->duration += duration; stats->durationSelf += duration; if (duration < stats->minTime) @@ -241,21 +246,20 @@ void QmlProfilerStatisticsModel::loadEvent(const QmlEvent &event, const QmlEvent d->lastEndTime = event.timestamp(); } - d->callStack.pop(); - - if (d->callStack.count() > 1) - d->data[d->callStack.top().typeIndex()].durationSelf -= duration; + stack.pop(); + if (stack.count() > 1) + d->data[stack.top().typeIndex()].durationSelf -= duration; break; } default: - break; + return; } if (!d->childrenModel.isNull()) - d->childrenModel->loadEvent(event); + d->childrenModel->loadEvent(type.rangeType(), event); if (!d->parentsModel.isNull()) - d->parentsModel->loadEvent(event); + d->parentsModel->loadEvent(type.rangeType(), event); } @@ -310,8 +314,6 @@ QmlProfilerStatisticsRelativesModel::QmlProfilerStatisticsRelativesModel( QmlProfilerStatisticsRelation relation, QObject *parent) : QObject(parent), m_relation(relation) { - m_startTimesPerLevel[0] = 0; - QTC_CHECK(modelManager); m_modelManager = modelManager; @@ -341,21 +343,16 @@ const QVector &QmlProfilerStatisticsRelativesModel::getTypes() con return m_modelManager->qmlModel()->eventTypes(); } -void QmlProfilerStatisticsRelativesModel::loadEvent(const QmlEvent &event) +void QmlProfilerStatisticsRelativesModel::loadEvent(RangeType type, const QmlEvent &event) { - // level computation + QStack &stack = (type == Compiling) ? m_compileStack : m_callStack; + switch (event.rangeStage()) { case RangeStart: - // now lastparent is the new type - ++m_level; - m_typesPerLevel[m_level] = event.typeIndex(); - m_startTimesPerLevel[m_level] = event.timestamp(); + stack.push({event.timestamp(), event.typeIndex()}); break; case RangeEnd: { - int parentTypeIndex = -1; - if (m_level > Constants::QML_MIN_LEVEL && m_typesPerLevel.contains(m_level-1)) - parentTypeIndex = m_typesPerLevel[m_level-1]; - + int parentTypeIndex = stack.count() > 1 ? stack[stack.count() - 2].typeId : -1; int relativeTypeIndex = (m_relation == QmlProfilerStatisticsParents) ? parentTypeIndex : event.typeIndex(); int selfTypeIndex = (m_relation == QmlProfilerStatisticsParents) ? event.typeIndex() : @@ -365,16 +362,16 @@ void QmlProfilerStatisticsRelativesModel::loadEvent(const QmlEvent &event) QmlStatisticsRelativesMap::Iterator it = relativesMap.find(relativeTypeIndex); if (it != relativesMap.end()) { it.value().calls++; - it.value().duration += event.timestamp() - m_startTimesPerLevel[m_level]; + it.value().duration += event.timestamp() - stack.top().startTime; } else { QmlStatisticsRelativesData relative = { - event.timestamp() - m_startTimesPerLevel[m_level], + event.timestamp() - stack.top().startTime, 1, false }; relativesMap.insert(relativeTypeIndex, relative); } - --m_level; + stack.pop(); break; } default: @@ -407,10 +404,8 @@ int QmlProfilerStatisticsRelativesModel::count() const void QmlProfilerStatisticsRelativesModel::clear() { m_data.clear(); - m_startTimesPerLevel.clear(); - m_level = Constants::QML_MIN_LEVEL; - m_startTimesPerLevel[0] = 0; - m_typesPerLevel.clear(); + m_callStack.clear(); + m_compileStack.clear(); } } // namespace QmlProfiler diff --git a/src/plugins/qmlprofiler/qmlprofilerstatisticsmodel.h b/src/plugins/qmlprofiler/qmlprofilerstatisticsmodel.h index 0af4c8700ff..402e48841b4 100644 --- a/src/plugins/qmlprofiler/qmlprofilerstatisticsmodel.h +++ b/src/plugins/qmlprofiler/qmlprofilerstatisticsmodel.h @@ -32,6 +32,7 @@ #include "qmlprofilerconstants.h" #include +#include #include #include @@ -121,7 +122,7 @@ public: const QmlStatisticsRelativesMap &getData(int typeId) const; const QVector &getTypes() const; - void loadEvent(const QmlEvent &event); + void loadEvent(RangeType type, const QmlEvent &event); void finalize(const QSet &eventsInBindingLoop); QmlProfilerStatisticsRelation relation() const; @@ -133,12 +134,12 @@ protected: QHash m_data; QmlProfilerModelManager *m_modelManager; - // for level computation - QHash m_startTimesPerLevel; - int m_level = Constants::QML_MIN_LEVEL; - - // compute parent-child relationship and call count - QHash m_typesPerLevel; + struct Frame { + qint64 startTime; + int typeId; + }; + QStack m_callStack; + QStack m_compileStack; const QmlProfilerStatisticsRelation m_relation; };