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.2 Change-Id: I564d2c74656dda1cb0963c75cd7b947a7f86d05e Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io> (cherry picked from commitcc761e9c5a
) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> (cherry picked from commit378de2f412
) (cherry picked from commit06fc186371
)
This commit is contained in:
parent
461d31d2d5
commit
2311fc6a82
|
@ -3,14 +3,12 @@
|
|||
|
||||
#include "qmlprofilerdata.h"
|
||||
|
||||
#include <QStringList>
|
||||
#include <QUrl>
|
||||
#include <QHash>
|
||||
#include <QFile>
|
||||
#include <QXmlStreamReader>
|
||||
#include <QRegularExpression>
|
||||
#include <QQueue>
|
||||
#include <QStack>
|
||||
#include <QtCore/qfile.h>
|
||||
#include <QtCore/qqueue.h>
|
||||
#include <QtCore/qregularexpression.h>
|
||||
#include <QtCore/qurl.h>
|
||||
#include <QtCore/qxmlstream.h>
|
||||
#include <QtCore/qxpfunctional.h>
|
||||
|
||||
#include <limits>
|
||||
|
||||
|
@ -375,6 +373,132 @@ private:
|
|||
QXmlStreamWriter stream;
|
||||
};
|
||||
|
||||
struct DataIterator
|
||||
{
|
||||
DataIterator(
|
||||
const QmlProfilerDataPrivate *d,
|
||||
qxp::function_ref<void(const QQmlProfilerEvent &, qint64)> &&sendEvent)
|
||||
: d(d)
|
||||
, sendEvent(std::move(sendEvent))
|
||||
{}
|
||||
|
||||
void run();
|
||||
|
||||
private:
|
||||
void handleRangeEvent(const QQmlProfilerEvent &event, const QQmlProfilerEventType &type);
|
||||
void sendPending();
|
||||
void endLevel0();
|
||||
|
||||
const QmlProfilerDataPrivate *d = nullptr;
|
||||
const qxp::function_ref<void(const QQmlProfilerEvent &, qint64)> sendEvent;
|
||||
|
||||
QQueue<QQmlProfilerEvent> pointEvents;
|
||||
QList<QQmlProfilerEvent> rangeStarts[MaximumRangeType];
|
||||
QList<qint64> rangeEnds[MaximumRangeType];
|
||||
|
||||
int level = 0;
|
||||
};
|
||||
|
||||
void DataIterator::handleRangeEvent(
|
||||
const QQmlProfilerEvent &event, const QQmlProfilerEventType &type)
|
||||
{
|
||||
QList<QQmlProfilerEvent> &starts = rangeStarts[type.rangeType()];
|
||||
switch (event.rangeStage()) {
|
||||
case RangeStart: {
|
||||
++level;
|
||||
starts.append(event);
|
||||
break;
|
||||
}
|
||||
case RangeEnd: {
|
||||
const qint64 invalidTimestamp = -1;
|
||||
QList<qint64> &ends = rangeEnds[type.rangeType()];
|
||||
|
||||
// -1 because all valid timestamps are >= 0.
|
||||
ends.resize(starts.size(), invalidTimestamp);
|
||||
|
||||
qsizetype i = starts.size();
|
||||
while (ends[--i] != invalidTimestamp) {}
|
||||
|
||||
Q_ASSERT(i >= 0);
|
||||
Q_ASSERT(starts[i].timestamp() <= event.timestamp());
|
||||
|
||||
ends[i] = event.timestamp();
|
||||
if (--level == 0)
|
||||
endLevel0();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DataIterator::sendPending()
|
||||
{
|
||||
// Send all pending events in the order of their start times.
|
||||
|
||||
qsizetype index[MaximumRangeType] = { 0, 0, 0, 0, 0, 0 };
|
||||
while (true) {
|
||||
|
||||
// Find the range type with the minimum start time.
|
||||
qsizetype minimum = MaximumRangeType;
|
||||
qint64 minimumTime = std::numeric_limits<qint64>::max();
|
||||
for (qsizetype i = 0; i < MaximumRangeType; ++i) {
|
||||
const QList<QQmlProfilerEvent> &starts = rangeStarts[i];
|
||||
if (starts.size() == index[i])
|
||||
continue;
|
||||
const qint64 timestamp = starts[index[i]].timestamp();
|
||||
if (timestamp < minimumTime) {
|
||||
minimumTime = timestamp;
|
||||
minimum = i;
|
||||
}
|
||||
}
|
||||
if (minimum == MaximumRangeType)
|
||||
break;
|
||||
|
||||
// Send all point events that happened before the range we've found.
|
||||
while (!pointEvents.isEmpty() && pointEvents.front().timestamp() < minimumTime)
|
||||
sendEvent(pointEvents.dequeue(), 0);
|
||||
|
||||
// Send the range itself
|
||||
sendEvent(rangeStarts[minimum][index[minimum]],
|
||||
rangeEnds[minimum][index[minimum]] - minimumTime);
|
||||
|
||||
// Bump the index so that we don't send the same range again
|
||||
++index[minimum];
|
||||
}
|
||||
}
|
||||
|
||||
void DataIterator::endLevel0()
|
||||
{
|
||||
sendPending();
|
||||
for (qsizetype i = 0; i < MaximumRangeType; ++i) {
|
||||
rangeStarts[i].clear();
|
||||
rangeEnds[i].clear();
|
||||
}
|
||||
}
|
||||
|
||||
void DataIterator::run()
|
||||
{
|
||||
for (const QQmlProfilerEvent &event : std::as_const(d->events)) {
|
||||
const QQmlProfilerEventType &type = d->eventTypes.at(event.typeIndex());
|
||||
if (type.rangeType() != MaximumRangeType)
|
||||
handleRangeEvent(event, type);
|
||||
else if (level == 0)
|
||||
sendEvent(event, 0);
|
||||
else
|
||||
pointEvents.enqueue(event);
|
||||
}
|
||||
|
||||
for (qsizetype i = 0; i < MaximumRangeType; ++i) {
|
||||
while (rangeEnds[i].size() < rangeStarts[i].size()) {
|
||||
rangeEnds[i].append(d->traceEndTime);
|
||||
--level;
|
||||
}
|
||||
}
|
||||
|
||||
sendPending();
|
||||
}
|
||||
|
||||
bool QmlProfilerData::save(const QString &filename)
|
||||
{
|
||||
if (isEmpty()) {
|
||||
|
@ -442,6 +566,7 @@ bool QmlProfilerData::save(const QString &filename)
|
|||
stream.writeStartElement("profilerDataModel");
|
||||
|
||||
auto sendEvent = [&](const QQmlProfilerEvent &event, qint64 duration = 0) {
|
||||
Q_ASSERT(duration >= 0);
|
||||
const QQmlProfilerEventType &type = d->eventTypes.at(event.typeIndex());
|
||||
stream.writeStartElement("range");
|
||||
stream.writeAttribute("startTime", event.timestamp());
|
||||
|
@ -481,74 +606,7 @@ bool QmlProfilerData::save(const QString &filename)
|
|||
stream.writeEndElement();
|
||||
};
|
||||
|
||||
QQueue<QQmlProfilerEvent> pointEvents;
|
||||
QQueue<QQmlProfilerEvent> rangeStarts[MaximumRangeType];
|
||||
QStack<qint64> rangeEnds[MaximumRangeType];
|
||||
int level = 0;
|
||||
|
||||
auto sendPending = [&]() {
|
||||
forever {
|
||||
int minimum = MaximumRangeType;
|
||||
qint64 minimumTime = std::numeric_limits<qint64>::max();
|
||||
for (int i = 0; i < 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 == MaximumRangeType)
|
||||
break;
|
||||
|
||||
while (!pointEvents.isEmpty() && pointEvents.front().timestamp() < minimumTime)
|
||||
sendEvent(pointEvents.dequeue());
|
||||
|
||||
sendEvent(rangeStarts[minimum].dequeue(),
|
||||
rangeEnds[minimum].pop() - minimumTime);
|
||||
}
|
||||
};
|
||||
|
||||
for (const QQmlProfilerEvent &event : std::as_const(d->events)) {
|
||||
const QQmlProfilerEventType &type = d->eventTypes.at(event.typeIndex());
|
||||
|
||||
if (type.rangeType() != MaximumRangeType) {
|
||||
QQueue<QQmlProfilerEvent> &starts = rangeStarts[type.rangeType()];
|
||||
switch (event.rangeStage()) {
|
||||
case RangeStart: {
|
||||
++level;
|
||||
starts.enqueue(event);
|
||||
break;
|
||||
}
|
||||
case RangeEnd: {
|
||||
QStack<qint64> &ends = rangeEnds[type.rangeType()];
|
||||
if (starts.size() > ends.size()) {
|
||||
ends.push(event.timestamp());
|
||||
if (--level == 0)
|
||||
sendPending();
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (level == 0)
|
||||
sendEvent(event);
|
||||
else
|
||||
pointEvents.enqueue(event);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < MaximumRangeType; ++i) {
|
||||
while (rangeEnds[i].size() < rangeStarts[i].size()) {
|
||||
rangeEnds[i].push(d->traceEndTime);
|
||||
--level;
|
||||
}
|
||||
}
|
||||
|
||||
sendPending();
|
||||
DataIterator(d, std::move(sendEvent)).run();
|
||||
|
||||
stream.writeEndElement(); // profilerDataModel
|
||||
|
||||
|
|
Loading…
Reference in New Issue