aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2024-01-11 16:19:27 +0100
committerUlf Hermann <ulf.hermann@qt.io>2024-01-25 18:35:05 +0100
commitcc761e9c5a1c5249f4760c8e5e57755a3fe042ca (patch)
treefea4c754b59ac8cc1a2c0c7dd240f65f52c7f31a /tools
parentcd16d2d965cf2b7f8a11b2e1d0f33bc783bd54eb (diff)
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 <fabian.kosmale@qt.io>
Diffstat (limited to 'tools')
-rw-r--r--tools/qmlprofiler/qmlprofilerdata.cpp210
1 files changed, 134 insertions, 76 deletions
diff --git a/tools/qmlprofiler/qmlprofilerdata.cpp b/tools/qmlprofiler/qmlprofilerdata.cpp
index 8f75226fd8..c9e917cc00 100644
--- a/tools/qmlprofiler/qmlprofilerdata.cpp
+++ b/tools/qmlprofiler/qmlprofilerdata.cpp
@@ -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