Skip to content

Commit cc761e9

Browse files
author
Ulf Hermann
committed
qmlprofiler: Fix data sorting
When analyzing a range with multiple child ranges, qmlprofiler would create a rather random association between the start and end events. Pick-to: 6.7 6.6 6.5 6.2 Change-Id: I564d2c74656dda1cb0963c75cd7b947a7f86d05e Reviewed-by: Fabian Kosmale <[email protected]>
1 parent cd16d2d commit cc761e9

File tree

1 file changed

+134
-76
lines changed

1 file changed

+134
-76
lines changed

tools/qmlprofiler/qmlprofilerdata.cpp

Lines changed: 134 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,12 @@
33

44
#include "qmlprofilerdata.h"
55

6-
#include <QStringList>
7-
#include <QUrl>
8-
#include <QHash>
9-
#include <QFile>
10-
#include <QXmlStreamReader>
11-
#include <QRegularExpression>
12-
#include <QQueue>
13-
#include <QStack>
6+
#include <QtCore/qfile.h>
7+
#include <QtCore/qqueue.h>
8+
#include <QtCore/qregularexpression.h>
9+
#include <QtCore/qurl.h>
10+
#include <QtCore/qxmlstream.h>
11+
#include <QtCore/qxpfunctional.h>
1412

1513
#include <limits>
1614

@@ -375,6 +373,132 @@ struct StreamWriter {
375373
QXmlStreamWriter stream;
376374
};
377375

376+
struct DataIterator
377+
{
378+
DataIterator(
379+
const QmlProfilerDataPrivate *d,
380+
qxp::function_ref<void(const QQmlProfilerEvent &, qint64)> &&sendEvent)
381+
: d(d)
382+
, sendEvent(std::move(sendEvent))
383+
{}
384+
385+
void run();
386+
387+
private:
388+
void handleRangeEvent(const QQmlProfilerEvent &event, const QQmlProfilerEventType &type);
389+
void sendPending();
390+
void endLevel0();
391+
392+
const QmlProfilerDataPrivate *d = nullptr;
393+
const qxp::function_ref<void(const QQmlProfilerEvent &, qint64)> sendEvent;
394+
395+
QQueue<QQmlProfilerEvent> pointEvents;
396+
QList<QQmlProfilerEvent> rangeStarts[MaximumRangeType];
397+
QList<qint64> rangeEnds[MaximumRangeType];
398+
399+
int level = 0;
400+
};
401+
402+
void DataIterator::handleRangeEvent(
403+
const QQmlProfilerEvent &event, const QQmlProfilerEventType &type)
404+
{
405+
QList<QQmlProfilerEvent> &starts = rangeStarts[type.rangeType()];
406+
switch (event.rangeStage()) {
407+
case RangeStart: {
408+
++level;
409+
starts.append(event);
410+
break;
411+
}
412+
case RangeEnd: {
413+
const qint64 invalidTimestamp = -1;
414+
QList<qint64> &ends = rangeEnds[type.rangeType()];
415+
416+
// -1 because all valid timestamps are >= 0.
417+
ends.resize(starts.size(), invalidTimestamp);
418+
419+
qsizetype i = starts.size();
420+
while (ends[--i] != invalidTimestamp) {}
421+
422+
Q_ASSERT(i >= 0);
423+
Q_ASSERT(starts[i].timestamp() <= event.timestamp());
424+
425+
ends[i] = event.timestamp();
426+
if (--level == 0)
427+
endLevel0();
428+
break;
429+
}
430+
default:
431+
break;
432+
}
433+
}
434+
435+
void DataIterator::sendPending()
436+
{
437+
// Send all pending events in the order of their start times.
438+
439+
qsizetype index[MaximumRangeType] = { 0, 0, 0, 0, 0, 0 };
440+
while (true) {
441+
442+
// Find the range type with the minimum start time.
443+
qsizetype minimum = MaximumRangeType;
444+
qint64 minimumTime = std::numeric_limits<qint64>::max();
445+
for (qsizetype i = 0; i < MaximumRangeType; ++i) {
446+
const QList<QQmlProfilerEvent> &starts = rangeStarts[i];
447+
if (starts.size() == index[i])
448+
continue;
449+
const qint64 timestamp = starts[index[i]].timestamp();
450+
if (timestamp < minimumTime) {
451+
minimumTime = timestamp;
452+
minimum = i;
453+
}
454+
}
455+
if (minimum == MaximumRangeType)
456+
break;
457+
458+
// Send all point events that happened before the range we've found.
459+
while (!pointEvents.isEmpty() && pointEvents.front().timestamp() < minimumTime)
460+
sendEvent(pointEvents.dequeue(), 0);
461+
462+
// Send the range itself
463+
sendEvent(rangeStarts[minimum][index[minimum]],
464+
rangeEnds[minimum][index[minimum]] - minimumTime);
465+
466+
// Bump the index so that we don't send the same range again
467+
++index[minimum];
468+
}
469+
}
470+
471+
void DataIterator::endLevel0()
472+
{
473+
sendPending();
474+
for (qsizetype i = 0; i < MaximumRangeType; ++i) {
475+
rangeStarts[i].clear();
476+
rangeEnds[i].clear();
477+
}
478+
}
479+
480+
void DataIterator::run()
481+
{
482+
for (const QQmlProfilerEvent &event : std::as_const(d->events)) {
483+
const QQmlProfilerEventType &type = d->eventTypes.at(event.typeIndex());
484+
if (type.rangeType() != MaximumRangeType)
485+
handleRangeEvent(event, type);
486+
else if (level == 0)
487+
sendEvent(event, 0);
488+
else
489+
pointEvents.enqueue(event);
490+
}
491+
492+
for (qsizetype i = 0; i < MaximumRangeType; ++i) {
493+
while (rangeEnds[i].size() < rangeStarts[i].size()) {
494+
rangeEnds[i].append(d->traceEndTime);
495+
--level;
496+
}
497+
}
498+
499+
sendPending();
500+
}
501+
378502
bool QmlProfilerData::save(const QString &filename)
379503
{
380504
if (isEmpty()) {
@@ -442,6 +566,7 @@ bool QmlProfilerData::save(const QString &filename)
442566
stream.writeStartElement("profilerDataModel");
443567

444568
auto sendEvent = [&](const QQmlProfilerEvent &event, qint64 duration = 0) {
569+
Q_ASSERT(duration >= 0);
445570
const QQmlProfilerEventType &type = d->eventTypes.at(event.typeIndex());
446571
stream.writeStartElement("range");
447572
stream.writeAttribute("startTime", event.timestamp());
@@ -481,74 +606,7 @@ bool QmlProfilerData::save(const QString &filename)
481606
stream.writeEndElement();
482607
};
483608

484-
QQueue<QQmlProfilerEvent> pointEvents;
485-
QQueue<QQmlProfilerEvent> rangeStarts[MaximumRangeType];
486-
QStack<qint64> rangeEnds[MaximumRangeType];
487-
int level = 0;
488-
489-
auto sendPending = [&]() {
490-
forever {
491-
int minimum = MaximumRangeType;
492-
qint64 minimumTime = std::numeric_limits<qint64>::max();
493-
for (int i = 0; i < MaximumRangeType; ++i) {
494-
const QQueue<QQmlProfilerEvent> &starts = rangeStarts[i];
495-
if (starts.isEmpty())
496-
continue;
497-
if (starts.head().timestamp() < minimumTime) {
498-
minimumTime = starts.head().timestamp();
499-
minimum = i;
500-
}
501-
}
502-
if (minimum == MaximumRangeType)
503-
break;
504-
505-
while (!pointEvents.isEmpty() && pointEvents.front().timestamp() < minimumTime)
506-
sendEvent(pointEvents.dequeue());
507-
508-
sendEvent(rangeStarts[minimum].dequeue(),
509-
rangeEnds[minimum].pop() - minimumTime);
510-
}
511-
};
512-
513-
for (const QQmlProfilerEvent &event : std::as_const(d->events)) {
514-
const QQmlProfilerEventType &type = d->eventTypes.at(event.typeIndex());
515-
516-
if (type.rangeType() != MaximumRangeType) {
517-
QQueue<QQmlProfilerEvent> &starts = rangeStarts[type.rangeType()];
518-
switch (event.rangeStage()) {
519-
case RangeStart: {
520-
++level;
521-
starts.enqueue(event);
522-
break;
523-
}
524-
case RangeEnd: {
525-
QStack<qint64> &ends = rangeEnds[type.rangeType()];
526-
if (starts.size() > ends.size()) {
527-
ends.push(event.timestamp());
528-
if (--level == 0)
529-
sendPending();
530-
}
531-
break;
532-
}
533-
default:
534-
break;
535-
}
536-
} else {
537-
if (level == 0)
538-
sendEvent(event);
539-
else
540-
pointEvents.enqueue(event);
541-
}
542-
}
543-
544-
for (int i = 0; i < MaximumRangeType; ++i) {
545-
while (rangeEnds[i].size() < rangeStarts[i].size()) {
546-
rangeEnds[i].push(d->traceEndTime);
547-
--level;
548-
}
549-
}
550-
551-
sendPending();
609+
DataIterator(d, std::move(sendEvent)).run();
552610

553611
stream.writeEndElement(); // profilerDataModel
554612

0 commit comments

Comments
 (0)