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 <kai.koehne@qt.io>
This commit is contained in:
Ulf Hermann
2016-09-08 18:26:55 +02:00
parent bf8a1c07b1
commit 0d5f5bdd6c
2 changed files with 39 additions and 43 deletions

View File

@@ -30,11 +30,9 @@
#include <utils/algorithm.h> #include <utils/algorithm.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <QVector>
#include <QHash> #include <QHash>
#include <QString>
#include <QStack>
#include <QSet> #include <QSet>
#include <QString>
#include <QPointer> #include <QPointer>
#include <functional> #include <functional>
@@ -60,6 +58,7 @@ public:
QHash<int, QString> notes; QHash<int, QString> notes;
QStack<QmlEvent> callStack; QStack<QmlEvent> callStack;
QStack<QmlEvent> compileStack;
qint64 qmlTime = 0; qint64 qmlTime = 0;
qint64 lastEndTime = 0; qint64 lastEndTime = 0;
QHash <int, QVector<qint64> > durations; QHash <int, QVector<qint64> > durations;
@@ -71,6 +70,7 @@ QmlProfilerStatisticsModel::QmlProfilerStatisticsModel(QmlProfilerModelManager *
{ {
d->modelManager = modelManager; d->modelManager = modelManager;
d->callStack.push(QmlEvent()); d->callStack.push(QmlEvent());
d->compileStack.push(QmlEvent());
connect(modelManager, &QmlProfilerModelManager::stateChanged, connect(modelManager, &QmlProfilerModelManager::stateChanged,
this, &QmlProfilerStatisticsModel::dataChanged); this, &QmlProfilerStatisticsModel::dataChanged);
connect(modelManager->notesModel(), &Timeline::TimelineNotesModel::changed, connect(modelManager->notesModel(), &Timeline::TimelineNotesModel::changed,
@@ -143,7 +143,9 @@ void QmlProfilerStatisticsModel::clear()
d->eventsInBindingLoop.clear(); d->eventsInBindingLoop.clear();
d->notes.clear(); d->notes.clear();
d->callStack.clear(); d->callStack.clear();
d->compileStack.clear();
d->callStack.push(QmlEvent()); d->callStack.push(QmlEvent());
d->compileStack.push(QmlEvent());
d->qmlTime = 0; d->qmlTime = 0;
d->lastEndTime = 0; d->lastEndTime = 0;
d->durations.clear(); d->durations.clear();
@@ -210,22 +212,25 @@ void QmlProfilerStatisticsModel::loadEvent(const QmlEvent &event, const QmlEvent
if (!d->acceptedTypes.contains(type.rangeType())) if (!d->acceptedTypes.contains(type.rangeType()))
return; return;
QStack<QmlEvent> &stack = type.rangeType() == Compiling ? d->compileStack : d->callStack;
switch (event.rangeStage()) { switch (event.rangeStage()) {
case RangeStart: case RangeStart:
// binding loop detection: check whether event is already in stack // binding loop detection: check whether event is already in stack
for (int ii = 1; ii < d->callStack.size(); ++ii) { if (type.rangeType() == Binding || type.rangeType() == HandlingSignal) {
if (d->callStack.at(ii).typeIndex() == event.typeIndex() for (int ii = 1; ii < stack.size(); ++ii) {
&& type.rangeType() != Javascript) { if (stack.at(ii).typeIndex() == event.typeIndex()) {
d->eventsInBindingLoop.insert(event.typeIndex()); d->eventsInBindingLoop.insert(event.typeIndex());
break; break;
}
} }
} }
d->callStack.push(event); stack.push(event);
break; break;
case RangeEnd: { case RangeEnd: {
// update stats // update stats
QmlEventStats *stats = &d->data[event.typeIndex()]; 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->duration += duration;
stats->durationSelf += duration; stats->durationSelf += duration;
if (duration < stats->minTime) if (duration < stats->minTime)
@@ -241,21 +246,20 @@ void QmlProfilerStatisticsModel::loadEvent(const QmlEvent &event, const QmlEvent
d->lastEndTime = event.timestamp(); d->lastEndTime = event.timestamp();
} }
d->callStack.pop(); stack.pop();
if (d->callStack.count() > 1)
d->data[d->callStack.top().typeIndex()].durationSelf -= duration;
if (stack.count() > 1)
d->data[stack.top().typeIndex()].durationSelf -= duration;
break; break;
} }
default: default:
break; return;
} }
if (!d->childrenModel.isNull()) if (!d->childrenModel.isNull())
d->childrenModel->loadEvent(event); d->childrenModel->loadEvent(type.rangeType(), event);
if (!d->parentsModel.isNull()) if (!d->parentsModel.isNull())
d->parentsModel->loadEvent(event); d->parentsModel->loadEvent(type.rangeType(), event);
} }
@@ -310,8 +314,6 @@ QmlProfilerStatisticsRelativesModel::QmlProfilerStatisticsRelativesModel(
QmlProfilerStatisticsRelation relation, QObject *parent) : QmlProfilerStatisticsRelation relation, QObject *parent) :
QObject(parent), m_relation(relation) QObject(parent), m_relation(relation)
{ {
m_startTimesPerLevel[0] = 0;
QTC_CHECK(modelManager); QTC_CHECK(modelManager);
m_modelManager = modelManager; m_modelManager = modelManager;
@@ -341,21 +343,16 @@ const QVector<QmlEventType> &QmlProfilerStatisticsRelativesModel::getTypes() con
return m_modelManager->qmlModel()->eventTypes(); return m_modelManager->qmlModel()->eventTypes();
} }
void QmlProfilerStatisticsRelativesModel::loadEvent(const QmlEvent &event) void QmlProfilerStatisticsRelativesModel::loadEvent(RangeType type, const QmlEvent &event)
{ {
// level computation QStack<Frame> &stack = (type == Compiling) ? m_compileStack : m_callStack;
switch (event.rangeStage()) { switch (event.rangeStage()) {
case RangeStart: case RangeStart:
// now lastparent is the new type stack.push({event.timestamp(), event.typeIndex()});
++m_level;
m_typesPerLevel[m_level] = event.typeIndex();
m_startTimesPerLevel[m_level] = event.timestamp();
break; break;
case RangeEnd: { case RangeEnd: {
int parentTypeIndex = -1; int parentTypeIndex = stack.count() > 1 ? stack[stack.count() - 2].typeId : -1;
if (m_level > Constants::QML_MIN_LEVEL && m_typesPerLevel.contains(m_level-1))
parentTypeIndex = m_typesPerLevel[m_level-1];
int relativeTypeIndex = (m_relation == QmlProfilerStatisticsParents) ? parentTypeIndex : int relativeTypeIndex = (m_relation == QmlProfilerStatisticsParents) ? parentTypeIndex :
event.typeIndex(); event.typeIndex();
int selfTypeIndex = (m_relation == QmlProfilerStatisticsParents) ? 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); QmlStatisticsRelativesMap::Iterator it = relativesMap.find(relativeTypeIndex);
if (it != relativesMap.end()) { if (it != relativesMap.end()) {
it.value().calls++; it.value().calls++;
it.value().duration += event.timestamp() - m_startTimesPerLevel[m_level]; it.value().duration += event.timestamp() - stack.top().startTime;
} else { } else {
QmlStatisticsRelativesData relative = { QmlStatisticsRelativesData relative = {
event.timestamp() - m_startTimesPerLevel[m_level], event.timestamp() - stack.top().startTime,
1, 1,
false false
}; };
relativesMap.insert(relativeTypeIndex, relative); relativesMap.insert(relativeTypeIndex, relative);
} }
--m_level; stack.pop();
break; break;
} }
default: default:
@@ -407,10 +404,8 @@ int QmlProfilerStatisticsRelativesModel::count() const
void QmlProfilerStatisticsRelativesModel::clear() void QmlProfilerStatisticsRelativesModel::clear()
{ {
m_data.clear(); m_data.clear();
m_startTimesPerLevel.clear(); m_callStack.clear();
m_level = Constants::QML_MIN_LEVEL; m_compileStack.clear();
m_startTimesPerLevel[0] = 0;
m_typesPerLevel.clear();
} }
} // namespace QmlProfiler } // namespace QmlProfiler

View File

@@ -32,6 +32,7 @@
#include "qmlprofilerconstants.h" #include "qmlprofilerconstants.h"
#include <QHash> #include <QHash>
#include <QStack>
#include <QVector> #include <QVector>
#include <QObject> #include <QObject>
@@ -121,7 +122,7 @@ public:
const QmlStatisticsRelativesMap &getData(int typeId) const; const QmlStatisticsRelativesMap &getData(int typeId) const;
const QVector<QmlEventType> &getTypes() const; const QVector<QmlEventType> &getTypes() const;
void loadEvent(const QmlEvent &event); void loadEvent(RangeType type, const QmlEvent &event);
void finalize(const QSet<int> &eventsInBindingLoop); void finalize(const QSet<int> &eventsInBindingLoop);
QmlProfilerStatisticsRelation relation() const; QmlProfilerStatisticsRelation relation() const;
@@ -133,12 +134,12 @@ protected:
QHash <int, QmlStatisticsRelativesMap> m_data; QHash <int, QmlStatisticsRelativesMap> m_data;
QmlProfilerModelManager *m_modelManager; QmlProfilerModelManager *m_modelManager;
// for level computation struct Frame {
QHash<int, qint64> m_startTimesPerLevel; qint64 startTime;
int m_level = Constants::QML_MIN_LEVEL; int typeId;
};
// compute parent-child relationship and call count QStack<Frame> m_callStack;
QHash<int, int> m_typesPerLevel; QStack<Frame> m_compileStack;
const QmlProfilerStatisticsRelation m_relation; const QmlProfilerStatisticsRelation m_relation;
}; };