forked from qt-creator/qt-creator
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:
@@ -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
|
||||||
|
@@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user