diff options
Diffstat (limited to 'tools/qmlprofiler/qmlprofilerdata.cpp')
-rw-r--r-- | tools/qmlprofiler/qmlprofilerdata.cpp | 727 |
1 files changed, 337 insertions, 390 deletions
diff --git a/tools/qmlprofiler/qmlprofilerdata.cpp b/tools/qmlprofiler/qmlprofilerdata.cpp index 7dcfa4cdaa..32e03298da 100644 --- a/tools/qmlprofiler/qmlprofilerdata.cpp +++ b/tools/qmlprofiler/qmlprofilerdata.cpp @@ -34,6 +34,8 @@ #include <QFile> #include <QXmlStreamReader> #include <QRegularExpression> +#include <QQueue> +#include <QStack> #include <limits> @@ -60,72 +62,13 @@ static const char *MESSAGE_STRINGS[] = { "Complete", "PixmapCache", "SceneGraph", - "MemoryAllocation" + "MemoryAllocation", + "DebugMessage" }; Q_STATIC_ASSERT(sizeof(MESSAGE_STRINGS) == QQmlProfilerDefinitions::MaximumMessage * sizeof(const char *)); -struct QmlRangeEventData { - QmlRangeEventData() {} // never called - QmlRangeEventData(const QString &_displayName, int _detailType, const QString &_eventHashStr, - const QQmlEventLocation &_location, const QString &_details, - QQmlProfilerDefinitions::Message _message, - QQmlProfilerDefinitions::RangeType _rangeType) - : displayName(_displayName), eventHashStr(_eventHashStr), location(_location), - details(_details), message(_message), rangeType(_rangeType), detailType(_detailType) {} - QString displayName; - QString eventHashStr; - QQmlEventLocation location; - QString details; - QQmlProfilerDefinitions::Message message; - QQmlProfilerDefinitions::RangeType rangeType; - int detailType; // can be BindingType, PixmapCacheEventType or SceneGraphFrameType -}; - -struct QmlRangeEventStartInstance { - QmlRangeEventStartInstance() {} // never called - QmlRangeEventStartInstance(qint64 _startTime, qint64 _duration, int _frameRate, - int _animationCount, int _threadId, QmlRangeEventData *_data) - : startTime(_startTime), duration(_duration), frameRate(_frameRate), - animationCount(_animationCount), threadId(_threadId), numericData4(-1), numericData5(-1), - data(_data) - { } - - QmlRangeEventStartInstance(qint64 _startTime, qint64 _numericData1, qint64 _numericData2, - qint64 _numericData3, qint64 _numericData4, qint64 _numericData5, - QmlRangeEventData *_data) - : startTime(_startTime), duration(-1), numericData1(_numericData1), - numericData2(_numericData2), numericData3(_numericData3), numericData4(_numericData4), - numericData5(_numericData5), data(_data) - { } - qint64 startTime; - qint64 duration; - union { - int frameRate; - int inputType; - qint64 numericData1; - }; - union { - int animationCount; - int inputA; - qint64 numericData2; - }; - union { - int threadId; - int inputB; - qint64 numericData3; - }; - qint64 numericData4; - qint64 numericData5; - QmlRangeEventData *data; -}; - -QT_BEGIN_NAMESPACE -Q_DECLARE_TYPEINFO(QmlRangeEventData, Q_MOVABLE_TYPE); -Q_DECLARE_TYPEINFO(QmlRangeEventStartInstance, Q_MOVABLE_TYPE); -QT_END_NAMESPACE - ///////////////////////////////////////////////////////////////// class QmlProfilerDataPrivate { @@ -133,8 +76,8 @@ public: QmlProfilerDataPrivate(QmlProfilerData *qq){ Q_UNUSED(qq); } // data storage - QHash<QString, QmlRangeEventData *> eventDescriptions; - QVector<QmlRangeEventStartInstance> startInstanceList; + QVector<QQmlProfilerEventType> eventTypes; + QVector<QQmlProfilerEvent> events; qint64 traceStartTime; qint64 traceEndTime; @@ -146,7 +89,7 @@ public: ///////////////////////////////////////////////////////////////// QmlProfilerData::QmlProfilerData(QObject *parent) : - QObject(parent),d(new QmlProfilerDataPrivate(this)) + QQmlProfilerEventReceiver(parent), d(new QmlProfilerDataPrivate(this)) { d->state = Empty; clear(); @@ -160,9 +103,8 @@ QmlProfilerData::~QmlProfilerData() void QmlProfilerData::clear() { - qDeleteAll(d->eventDescriptions); - d->eventDescriptions.clear(); - d->startInstanceList.clear(); + d->eventTypes.clear(); + d->events.clear(); d->traceEndTime = std::numeric_limits<qint64>::min(); d->traceStartTime = std::numeric_limits<qint64>::max(); @@ -171,15 +113,6 @@ void QmlProfilerData::clear() setState(Empty); } -QString QmlProfilerData::getHashStringForQmlEvent(const QQmlEventLocation &location, int eventType) -{ - return QString(QStringLiteral("%1:%2:%3:%4")).arg( - location.filename, - QString::number(location.line), - QString::number(location.column), - QString::number(eventType)); -} - QString QmlProfilerData::qmlRangeTypeAsString(QQmlProfilerDefinitions::RangeType type) { if (type * sizeof(char *) < sizeof(RANGE_TYPE_STRINGS)) @@ -218,20 +151,20 @@ qint64 QmlProfilerData::traceEndTime() const return d->traceEndTime; } -void QmlProfilerData::addQmlEvent(QQmlProfilerDefinitions::RangeType type, - QQmlProfilerDefinitions::BindingType bindingType, - qint64 startTime, - qint64 duration, - const QStringList &data, - const QQmlEventLocation &location) +void QmlProfilerData::addEvent(const QQmlProfilerEvent &event) { setState(AcquiringData); + d->events.append(event); +} + +void QmlProfilerData::addEventType(const QQmlProfilerEventType &type) +{ + QQmlProfilerEventType newType = type; QString details; // generate details string - if (!data.isEmpty()) { - details = data.join(QLatin1Char(' ')).replace( - QLatin1Char('\n'), QLatin1Char(' ')).simplified(); + if (!type.data().isEmpty()) { + details = type.data().simplified(); QRegularExpression rewrite(QStringLiteral("^\\(function \\$(\\w+)\\(\\) \\{ (return |)(.+) \\}\\)$")); QRegularExpressionMatch match = rewrite.match(details); if (match.hasMatch()) { @@ -241,223 +174,132 @@ void QmlProfilerData::addQmlEvent(QQmlProfilerDefinitions::RangeType type, details = details.mid(details.lastIndexOf(QLatin1Char('/')) + 1); } - QQmlEventLocation eventLocation = location; - QString displayName, eventHashStr; - // generate hash - if (eventLocation.filename.isEmpty()) { - displayName = tr("<bytecode>"); - eventHashStr = getHashStringForQmlEvent(eventLocation, type); - } else { - const QString filePath = QUrl(eventLocation.filename).path(); - displayName = filePath.midRef( - filePath.lastIndexOf(QLatin1Char('/')) + 1) + - QLatin1Char(':') + QString::number(eventLocation.line); - eventHashStr = getHashStringForQmlEvent(eventLocation, type); - } - - QmlRangeEventData *newEvent; - if (d->eventDescriptions.contains(eventHashStr)) { - newEvent = d->eventDescriptions[eventHashStr]; - } else { - newEvent = new QmlRangeEventData(displayName, bindingType, eventHashStr, location, details, - QQmlProfilerDefinitions::MaximumMessage, type); - d->eventDescriptions.insert(eventHashStr, newEvent); - } - - QmlRangeEventStartInstance rangeEventStartInstance(startTime, duration, -1, -1, -1, newEvent); - - d->startInstanceList.append(rangeEventStartInstance); -} - -void QmlProfilerData::addFrameEvent(qint64 time, int framerate, int animationcount, int threadId) -{ - setState(AcquiringData); - - QString details = tr("Animation Timer Update"); - QString displayName = tr("<Animation Update>"); - QString eventHashStr = displayName; - - QmlRangeEventData *newEvent; - if (d->eventDescriptions.contains(eventHashStr)) { - newEvent = d->eventDescriptions[eventHashStr]; - } else { - newEvent = new QmlRangeEventData(displayName, QQmlProfilerDefinitions::AnimationFrame, - eventHashStr, - QQmlEventLocation(), details, - QQmlProfilerDefinitions::Event, - QQmlProfilerDefinitions::MaximumRangeType); - d->eventDescriptions.insert(eventHashStr, newEvent); - } - - QmlRangeEventStartInstance rangeEventStartInstance(time, -1, framerate, animationcount, - threadId, newEvent); - - d->startInstanceList.append(rangeEventStartInstance); -} - -void QmlProfilerData::addSceneGraphFrameEvent(QQmlProfilerDefinitions::SceneGraphFrameType type, - qint64 time, qint64 numericData1, qint64 numericData2, - qint64 numericData3, qint64 numericData4, - qint64 numericData5) -{ - setState(AcquiringData); - - QString eventHashStr = QString::fromLatin1("SceneGraph:%1").arg(type); - QmlRangeEventData *newEvent; - if (d->eventDescriptions.contains(eventHashStr)) { - newEvent = d->eventDescriptions[eventHashStr]; - } else { - newEvent = new QmlRangeEventData(QStringLiteral("<SceneGraph>"), type, eventHashStr, - QQmlEventLocation(), QString(), - QQmlProfilerDefinitions::SceneGraphFrame, - QQmlProfilerDefinitions::MaximumRangeType); - d->eventDescriptions.insert(eventHashStr, newEvent); - } + newType.setData(details); - QmlRangeEventStartInstance rangeEventStartInstance(time, numericData1, numericData2, - numericData3, numericData4, numericData5, - newEvent); - d->startInstanceList.append(rangeEventStartInstance); -} - -void QmlProfilerData::addPixmapCacheEvent(QQmlProfilerDefinitions::PixmapEventType type, - qint64 time, const QString &location, - int numericData1, int numericData2) -{ - setState(AcquiringData); - - QString filePath = QUrl(location).path(); - - const QString eventHashStr = filePath.midRef(filePath.lastIndexOf(QLatin1Char('/')) + 1) - + QLatin1Char(':') + QString::number(type); - QmlRangeEventData *newEvent; - if (d->eventDescriptions.contains(eventHashStr)) { - newEvent = d->eventDescriptions[eventHashStr]; - } else { - newEvent = new QmlRangeEventData(eventHashStr, type, eventHashStr, - QQmlEventLocation(location, -1, -1), QString(), - QQmlProfilerDefinitions::PixmapCacheEvent, - QQmlProfilerDefinitions::MaximumRangeType); - d->eventDescriptions.insert(eventHashStr, newEvent); + QString displayName; + switch (type.message()) { + case QQmlProfilerDefinitions::Event: { + switch (type.detailType()) { + case QQmlProfilerDefinitions::Mouse: + case QQmlProfilerDefinitions::Key: + displayName = QString::fromLatin1("Input:%1").arg(type.detailType()); + break; + case QQmlProfilerDefinitions::AnimationFrame: + displayName = QString::fromLatin1("AnimationFrame"); + break; + default: + displayName = QString::fromLatin1("Unknown"); + } + break; } - - QmlRangeEventStartInstance rangeEventStartInstance(time, numericData1, numericData2, 0, 0, 0, - newEvent); - d->startInstanceList.append(rangeEventStartInstance); -} - -void QmlProfilerData::addMemoryEvent(QQmlProfilerDefinitions::MemoryType type, qint64 time, - qint64 size) -{ - setState(AcquiringData); - QString eventHashStr = QString::fromLatin1("MemoryAllocation:%1").arg(type); - QmlRangeEventData *newEvent; - if (d->eventDescriptions.contains(eventHashStr)) { - newEvent = d->eventDescriptions[eventHashStr]; - } else { - newEvent = new QmlRangeEventData(eventHashStr, type, eventHashStr, QQmlEventLocation(), - QString(), QQmlProfilerDefinitions::MemoryAllocation, - QQmlProfilerDefinitions::MaximumRangeType); - d->eventDescriptions.insert(eventHashStr, newEvent); + case QQmlProfilerDefinitions::RangeStart: + case QQmlProfilerDefinitions::RangeData: + case QQmlProfilerDefinitions::RangeLocation: + case QQmlProfilerDefinitions::RangeEnd: + case QQmlProfilerDefinitions::Complete: + Q_UNREACHABLE(); + break; + case QQmlProfilerDefinitions::PixmapCacheEvent: { + const QString filePath = QUrl(type.location().filename()).path(); + displayName = filePath.midRef(filePath.lastIndexOf(QLatin1Char('/')) + 1) + + QLatin1Char(':') + QString::number(type.detailType()); + break; } - QmlRangeEventStartInstance rangeEventStartInstance(time, size, 0, 0, 0, 0, newEvent); - d->startInstanceList.append(rangeEventStartInstance); -} - -void QmlProfilerData::addInputEvent(QQmlProfilerDefinitions::InputEventType type, qint64 time, - int a, int b) -{ - setState(AcquiringData); - - QQmlProfilerDefinitions::EventType eventType; - switch (type) { - case QQmlProfilerDefinitions::InputKeyPress: - case QQmlProfilerDefinitions::InputKeyRelease: - case QQmlProfilerDefinitions::InputKeyUnknown: - eventType = QQmlProfilerDefinitions::Key; + case QQmlProfilerDefinitions::SceneGraphFrame: + displayName = QString::fromLatin1("SceneGraph:%1").arg(type.detailType()); break; - default: - eventType = QQmlProfilerDefinitions::Mouse; + case QQmlProfilerDefinitions::MemoryAllocation: + displayName = QString::fromLatin1("MemoryAllocation:%1").arg(type.detailType()); + break; + case QQmlProfilerDefinitions::DebugMessage: + displayName = QString::fromLatin1("DebugMessage:%1").arg(type.detailType()); + break; + case QQmlProfilerDefinitions::MaximumMessage: { + const QQmlProfilerEventLocation eventLocation = type.location(); + // generate hash + if (eventLocation.filename().isEmpty()) { + displayName = QString::fromLatin1("Unknown"); + } else { + const QString filePath = QUrl(eventLocation.filename()).path(); + displayName = filePath.midRef( + filePath.lastIndexOf(QLatin1Char('/')) + 1) + + QLatin1Char(':') + QString::number(eventLocation.line()); + } break; } - - QString eventHashStr = QString::fromLatin1("Input:%1").arg(eventType); - - QmlRangeEventData *newEvent; - if (d->eventDescriptions.contains(eventHashStr)) { - newEvent = d->eventDescriptions[eventHashStr]; - } else { - newEvent = new QmlRangeEventData(QString(), eventType, eventHashStr, QQmlEventLocation(), - QString(), QQmlProfilerDefinitions::Event, - QQmlProfilerDefinitions::MaximumRangeType); - d->eventDescriptions.insert(eventHashStr, newEvent); } - d->startInstanceList.append(QmlRangeEventStartInstance(time, -1, type, a, b, newEvent)); + newType.setDisplayName(displayName); + d->eventTypes.append(newType); } void QmlProfilerData::computeQmlTime() { // compute levels - QHash<int, qint64> endtimesPerLevel; - int minimumLevel = 1; - int level = minimumLevel; - - for (int i = 0; i < d->startInstanceList.count(); i++) { - qint64 st = d->startInstanceList.at(i).startTime; + qint64 level0Start = -1; + int level = 0; - if (d->startInstanceList.at(i).data->rangeType == QQmlProfilerDefinitions::Painting) { + for (const QQmlProfilerEvent &event : qAsConst(d->events)) { + const QQmlProfilerEventType &type = d->eventTypes.at(event.typeIndex()); + if (type.message() != QQmlProfilerDefinitions::MaximumMessage) continue; - } - - // general level - if (endtimesPerLevel.value(level) > st) { - level++; - } else { - while (level > minimumLevel && endtimesPerLevel[level-1] <= st) - level--; - } - endtimesPerLevel[level] = st + d->startInstanceList.at(i).duration; - if (level == minimumLevel) { - d->qmlMeasuredTime += d->startInstanceList.at(i).duration; + switch (type.rangeType()) { + case QQmlProfilerDefinitions::Compiling: + case QQmlProfilerDefinitions::Creating: + case QQmlProfilerDefinitions::Binding: + case QQmlProfilerDefinitions::HandlingSignal: + case QQmlProfilerDefinitions::Javascript: + switch (event.rangeStage()) { + case QQmlProfilerDefinitions::RangeStart: + if (level++ == 0) + level0Start = event.timestamp(); + break; + case QQmlProfilerDefinitions::RangeEnd: + if (--level == 0) + d->qmlMeasuredTime += event.timestamp() - level0Start; + break; + default: + break; + } + break; + default: + break; } } } -bool compareStartTimes(const QmlRangeEventStartInstance &t1, const QmlRangeEventStartInstance &t2) +bool compareStartTimes(const QQmlProfilerEvent &t1, const QQmlProfilerEvent &t2) { - return t1.startTime < t2.startTime; + return t1.timestamp() < t2.timestamp(); } void QmlProfilerData::sortStartTimes() { - if (d->startInstanceList.count() < 2) + if (d->events.count() < 2) return; // assuming startTimes is partially sorted // identify blocks of events and sort them with quicksort - QVector<QmlRangeEventStartInstance>::iterator itFrom = d->startInstanceList.end() - 2; - QVector<QmlRangeEventStartInstance>::iterator itTo = d->startInstanceList.end() - 1; + QVector<QQmlProfilerEvent>::iterator itFrom = d->events.end() - 2; + QVector<QQmlProfilerEvent>::iterator itTo = d->events.end() - 1; - while (itFrom != d->startInstanceList.begin() && itTo != d->startInstanceList.begin()) { + while (itFrom != d->events.begin() && itTo != d->events.begin()) { // find block to sort - while ( itFrom != d->startInstanceList.begin() - && itTo->startTime > itFrom->startTime ) { + while (itFrom != d->events.begin() && itTo->timestamp() > itFrom->timestamp()) { --itTo; itFrom = itTo - 1; } // if we're at the end of the list - if (itFrom == d->startInstanceList.begin()) + if (itFrom == d->events.begin()) break; // find block length - while ( itFrom != d->startInstanceList.begin() - && itTo->startTime <= itFrom->startTime ) + while (itFrom != d->events.begin() && itTo->timestamp() <= itFrom->timestamp()) --itFrom; - if (itTo->startTime <= itFrom->startTime) + if (itTo->timestamp() <= itFrom->timestamp()) std::sort(itFrom, itTo + 1, compareStartTimes); else std::sort(itFrom + 1, itTo + 1, compareStartTimes); @@ -479,9 +321,88 @@ void QmlProfilerData::complete() bool QmlProfilerData::isEmpty() const { - return d->startInstanceList.isEmpty(); + return d->events.isEmpty(); } +struct StreamWriter { + QString error; + + StreamWriter(const QString &filename) + { + if (!filename.isEmpty()) { + file.setFileName(filename); + if (!file.open(QIODevice::WriteOnly)) { + error = QmlProfilerData::tr("Could not open %1 for writing").arg(filename); + return; + } + } else { + if (!file.open(stdout, QIODevice::WriteOnly)) { + error = QmlProfilerData::tr("Could not open stdout for writing"); + return; + } + } + + stream.setDevice(&file); + stream.setAutoFormatting(true); + stream.writeStartDocument(); + writeStartElement("trace"); + } + + ~StreamWriter() { + writeEndElement(); + stream.writeEndDocument(); + file.close(); + } + + template<typename Number> + void writeAttribute(const char *name, Number number) + { + stream.writeAttribute(QLatin1String(name), QString::number(number)); + } + + void writeAttribute(const char *name, const char *value) + { + stream.writeAttribute(QLatin1String(name), QLatin1String(value)); + } + + void writeAttribute(const char *name, const QQmlProfilerEvent &event, int i, bool printZero = true) + { + const qint64 number = event.number<qint64>(i); + if (printZero || number != 0) + writeAttribute(name, number); + } + + template<typename Number> + void writeTextElement(const char *name, Number number) + { + writeTextElement(name, QString::number(number)); + } + + void writeTextElement(const char *name, const char *value) + { + stream.writeTextElement(QLatin1String(name), QLatin1String(value)); + } + + void writeTextElement(const char *name, const QString &value) + { + stream.writeTextElement(QLatin1String(name), value); + } + + void writeStartElement(const char *name) + { + stream.writeStartElement(QLatin1String(name)); + } + + void writeEndElement() + { + stream.writeEndElement(); + } + +private: + QFile file; + QXmlStreamWriter stream; +}; + bool QmlProfilerData::save(const QString &filename) { if (isEmpty()) { @@ -489,157 +410,178 @@ bool QmlProfilerData::save(const QString &filename) return false; } - QFile file; - if (!filename.isEmpty()) { - file.setFileName(filename); - if (!file.open(QIODevice::WriteOnly)) { - emit error(tr("Could not open %1 for writing").arg(filename)); - return false; - } - } else { - if (!file.open(stdout, QIODevice::WriteOnly)) { - emit error(tr("Could not open stdout for writing")); - return false; - } + StreamWriter stream(filename); + if (!stream.error.isEmpty()) { + emit error(stream.error); + return false; } - QXmlStreamWriter stream(&file); - stream.setAutoFormatting(true); - stream.writeStartDocument(); - - stream.writeStartElement(QStringLiteral("trace")); - stream.writeAttribute(QStringLiteral("version"), PROFILER_FILE_VERSION); - - stream.writeAttribute(QStringLiteral("traceStart"), QString::number(traceStartTime())); - stream.writeAttribute(QStringLiteral("traceEnd"), QString::number(traceEndTime())); - - stream.writeStartElement(QStringLiteral("eventData")); - stream.writeAttribute(QStringLiteral("totalTime"), QString::number(d->qmlMeasuredTime)); - - const auto eventDescriptionsKeys = d->eventDescriptions.keys(); - for (auto it = d->eventDescriptions.cbegin(), end = d->eventDescriptions.cend(); - it != end; ++it) { - const QmlRangeEventData *eventData = it.value(); - stream.writeStartElement(QStringLiteral("event")); - stream.writeAttribute(QStringLiteral("index"), QString::number( - eventDescriptionsKeys.indexOf(eventData->eventHashStr))); - if (!eventData->displayName.isEmpty()) - stream.writeTextElement(QStringLiteral("displayname"), eventData->displayName); - if (eventData->rangeType != QQmlProfilerDefinitions::MaximumRangeType) - stream.writeTextElement(QStringLiteral("type"), - qmlRangeTypeAsString(eventData->rangeType)); - else - stream.writeTextElement(QStringLiteral("type"), - qmlMessageAsString(eventData->message)); - if (!eventData->location.filename.isEmpty()) - stream.writeTextElement(QStringLiteral("filename"), eventData->location.filename); - if (eventData->location.line >= 0) - stream.writeTextElement(QStringLiteral("line"), - QString::number(eventData->location.line)); - if (eventData->location.column >= 0) - stream.writeTextElement(QStringLiteral("column"), - QString::number(eventData->location.column)); - if (!eventData->details.isEmpty()) - stream.writeTextElement(QStringLiteral("details"), eventData->details); - if (eventData->rangeType == QQmlProfilerDefinitions::Binding) - stream.writeTextElement(QStringLiteral("bindingType"), - QString::number((int)eventData->detailType)); - else if (eventData->message == QQmlProfilerDefinitions::Event) { - switch (eventData->detailType) { + stream.writeAttribute("version", PROFILER_FILE_VERSION); + stream.writeAttribute("traceStart", traceStartTime()); + stream.writeAttribute("traceEnd", traceEndTime()); + + stream.writeStartElement("eventData"); + stream.writeAttribute("totalTime", d->qmlMeasuredTime); + + for (int typeIndex = 0, end = d->eventTypes.size(); typeIndex < end; ++typeIndex) { + const QQmlProfilerEventType &eventData = d->eventTypes.at(typeIndex); + stream.writeStartElement("event"); + stream.writeAttribute("index", typeIndex); + if (!eventData.displayName().isEmpty()) + stream.writeTextElement("displayname", eventData.displayName()); + + stream.writeTextElement("type", + eventData.rangeType() == QQmlProfilerDefinitions::MaximumRangeType + ? qmlMessageAsString(eventData.message()) + : qmlRangeTypeAsString(eventData.rangeType())); + + const QQmlProfilerEventLocation location = eventData.location(); + if (!location.filename().isEmpty()) + stream.writeTextElement("filename", location.filename()); + if (location.line() >= 0) + stream.writeTextElement("line", location.line()); + if (location.column() >= 0) + stream.writeTextElement("column", location.column()); + if (!eventData.data().isEmpty()) + stream.writeTextElement("details", eventData.data()); + if (eventData.rangeType() == QQmlProfilerDefinitions::Binding) + stream.writeTextElement("bindingType", eventData.detailType()); + else if (eventData.message() == QQmlProfilerDefinitions::Event) { + switch (eventData.detailType()) { case QQmlProfilerDefinitions::AnimationFrame: - stream.writeTextElement(QStringLiteral("animationFrame"), - QString::number((int)eventData->detailType)); + stream.writeTextElement("animationFrame", eventData.detailType()); break; case QQmlProfilerDefinitions::Key: - stream.writeTextElement(QStringLiteral("keyEvent"), - QString::number((int)eventData->detailType)); + stream.writeTextElement("keyEvent", eventData.detailType()); break; case QQmlProfilerDefinitions::Mouse: - stream.writeTextElement(QStringLiteral("mouseEvent"), - QString::number((int)eventData->detailType)); + stream.writeTextElement("mouseEvent", eventData.detailType()); break; } - } else if (eventData->message == QQmlProfilerDefinitions::PixmapCacheEvent) - stream.writeTextElement(QStringLiteral("cacheEventType"), - QString::number((int)eventData->detailType)); - else if (eventData->message == QQmlProfilerDefinitions::SceneGraphFrame) - stream.writeTextElement(QStringLiteral("sgEventType"), - QString::number((int)eventData->detailType)); - else if (eventData->message == QQmlProfilerDefinitions::MemoryAllocation) - stream.writeTextElement(QStringLiteral("memoryEventType"), - QString::number((int)eventData->detailType)); + } else if (eventData.message() == QQmlProfilerDefinitions::PixmapCacheEvent) + stream.writeTextElement("cacheEventType", eventData.detailType()); + else if (eventData.message() == QQmlProfilerDefinitions::SceneGraphFrame) + stream.writeTextElement("sgEventType", eventData.detailType()); + else if (eventData.message() == QQmlProfilerDefinitions::MemoryAllocation) + stream.writeTextElement("memoryEventType", eventData.detailType()); stream.writeEndElement(); } stream.writeEndElement(); // eventData - stream.writeStartElement(QStringLiteral("profilerDataModel")); - for (const QmlRangeEventStartInstance &event : qAsConst(d->startInstanceList)) { - stream.writeStartElement(QStringLiteral("range")); - stream.writeAttribute(QStringLiteral("startTime"), QString::number(event.startTime)); - if (event.duration >= 0) - stream.writeAttribute(QStringLiteral("duration"), - QString::number(event.duration)); - stream.writeAttribute(QStringLiteral("eventIndex"), QString::number( - eventDescriptionsKeys.indexOf(event.data->eventHashStr))); - if (event.data->message == QQmlProfilerDefinitions::Event) { - if (event.data->detailType == QQmlProfilerDefinitions::AnimationFrame) { + stream.writeStartElement("profilerDataModel"); + + auto sendEvent = [&](const QQmlProfilerEvent &event, qint64 duration = 0) { + const QQmlProfilerEventType &type = d->eventTypes.at(event.typeIndex()); + stream.writeStartElement("range"); + stream.writeAttribute("startTime", event.timestamp()); + if (duration != 0) + stream.writeAttribute("duration", duration); + stream.writeAttribute("eventIndex", event.typeIndex()); + if (type.message() == QQmlProfilerDefinitions::Event) { + if (type.detailType() == QQmlProfilerDefinitions::AnimationFrame) { // special: animation frame - stream.writeAttribute(QStringLiteral("framerate"), QString::number(event.frameRate)); - stream.writeAttribute(QStringLiteral("animationcount"), - QString::number(event.animationCount)); - stream.writeAttribute(QStringLiteral("thread"), QString::number(event.threadId)); - } else if (event.data->detailType == QQmlProfilerDefinitions::Key || - event.data->detailType == QQmlProfilerDefinitions::Mouse) { + stream.writeAttribute("framerate", event, 0); + stream.writeAttribute("animationcount", event, 1); + stream.writeAttribute("thread", event, 2); + } else if (type.detailType() == QQmlProfilerDefinitions::Key || + type.detailType() == QQmlProfilerDefinitions::Mouse) { // numerical value here, to keep the format a bit more compact - stream.writeAttribute(QStringLiteral("type"), - QString::number(event.inputType)); - stream.writeAttribute(QStringLiteral("data1"), - QString::number(event.inputA)); - stream.writeAttribute(QStringLiteral("data2"), - QString::number(event.inputB)); + stream.writeAttribute("type", event, 0); + stream.writeAttribute("data1", event, 1); + stream.writeAttribute("data2", event, 2); } - } else if (event.data->message == QQmlProfilerDefinitions::PixmapCacheEvent) { + } else if (type.message() == QQmlProfilerDefinitions::PixmapCacheEvent) { // special: pixmap cache event - if (event.data->detailType == QQmlProfilerDefinitions::PixmapSizeKnown) { - stream.writeAttribute(QStringLiteral("width"), - QString::number(event.numericData1)); - stream.writeAttribute(QStringLiteral("height"), - QString::number(event.numericData2)); - } else if (event.data->detailType == - QQmlProfilerDefinitions::PixmapReferenceCountChanged || - event.data->detailType == - QQmlProfilerDefinitions::PixmapCacheCountChanged) { - stream.writeAttribute(QStringLiteral("refCount"), - QString::number(event.numericData1)); + if (type.detailType() == QQmlProfilerDefinitions::PixmapSizeKnown) { + stream.writeAttribute("width", event, 0); + stream.writeAttribute("height", event, 1); + } else if (type.detailType() == QQmlProfilerDefinitions::PixmapReferenceCountChanged + || type.detailType() == QQmlProfilerDefinitions::PixmapCacheCountChanged) { + stream.writeAttribute("refCount", event, 1); } - } else if (event.data->message == QQmlProfilerDefinitions::SceneGraphFrame) { - // special: scenegraph frame events - if (event.numericData1 > 0) - stream.writeAttribute(QStringLiteral("timing1"), - QString::number(event.numericData1)); - if (event.numericData2 > 0) - stream.writeAttribute(QStringLiteral("timing2"), - QString::number(event.numericData2)); - if (event.numericData3 > 0) - stream.writeAttribute(QStringLiteral("timing3"), - QString::number(event.numericData3)); - if (event.numericData4 > 0) - stream.writeAttribute(QStringLiteral("timing4"), - QString::number(event.numericData4)); - if (event.numericData5 > 0) - stream.writeAttribute(QStringLiteral("timing5"), - QString::number(event.numericData5)); - } else if (event.data->message == QQmlProfilerDefinitions::MemoryAllocation) { - stream.writeAttribute(QStringLiteral("amount"), QString::number(event.numericData1)); + } else if (type.message() == QQmlProfilerDefinitions::SceneGraphFrame) { + stream.writeAttribute("timing1", event, 0, false); + stream.writeAttribute("timing2", event, 1, false); + stream.writeAttribute("timing3", event, 2, false); + stream.writeAttribute("timing4", event, 3, false); + stream.writeAttribute("timing5", event, 4, false); + } else if (type.message() == QQmlProfilerDefinitions::MemoryAllocation) { + stream.writeAttribute("amount", event, 0); } stream.writeEndElement(); + }; + + QQueue<QQmlProfilerEvent> pointEvents; + QQueue<QQmlProfilerEvent> rangeStarts[QQmlProfilerDefinitions::MaximumRangeType]; + QStack<qint64> rangeEnds[QQmlProfilerDefinitions::MaximumRangeType]; + int level = 0; + + auto sendPending = [&]() { + forever { + int minimum = QQmlProfilerDefinitions::MaximumRangeType; + qint64 minimumTime = std::numeric_limits<qint64>::max(); + for (int i = 0; i < QQmlProfilerDefinitions::MaximumRangeType; ++i) { + const QQueue<QQmlProfilerEvent> &starts = rangeStarts[i]; + if (starts.isEmpty()) + continue; + if (starts.head().timestamp() < minimumTime) { + minimumTime = starts.head().timestamp(); + minimum = i; + } + } + if (minimum == QQmlProfilerDefinitions::MaximumRangeType) + break; + + while (!pointEvents.isEmpty() && pointEvents.front().timestamp() < minimumTime) + sendEvent(pointEvents.dequeue()); + + sendEvent(rangeStarts[minimum].dequeue(), + rangeEnds[minimum].pop() - minimumTime); + } + }; + + for (const QQmlProfilerEvent &event : qAsConst(d->events)) { + const QQmlProfilerEventType &type = d->eventTypes.at(event.typeIndex()); + + if (type.rangeType() != QQmlProfilerDefinitions::MaximumRangeType) { + QQueue<QQmlProfilerEvent> &starts = rangeStarts[type.rangeType()]; + switch (event.rangeStage()) { + case QQmlProfilerDefinitions::RangeStart: { + ++level; + starts.enqueue(event); + break; + } + case QQmlProfilerDefinitions::RangeEnd: { + QStack<qint64> &ends = rangeEnds[type.rangeType()]; + if (starts.length() > ends.length()) { + ends.push(event.timestamp()); + if (--level == 0) + sendPending(); + } + break; + } + default: + break; + } + } else { + if (level == 0) + sendEvent(event); + else + pointEvents.enqueue(event); + } } - stream.writeEndElement(); // profilerDataModel - stream.writeEndElement(); // trace - stream.writeEndDocument(); + for (int i = 0; i < QQmlProfilerDefinitions::MaximumRangeType; ++i) { + while (rangeEnds[i].length() < rangeStarts[i].length()) { + rangeEnds[i].push(d->traceEndTime); + --level; + } + } + + sendPending(); + + stream.writeEndElement(); // profilerDataModel - file.close(); return true; } @@ -683,4 +625,9 @@ void QmlProfilerData::setState(QmlProfilerData::State state) return; } +int QmlProfilerData::numLoadedEventTypes() const +{ + return d->eventTypes.length(); +} + #include "moc_qmlprofilerdata.cpp" |