7#include <private/qobject_p.h>
9#include <QtCore/qfile.h>
10#include <QtCore/qqueue.h>
11#include <QtCore/qregularexpression.h>
12#include <QtCore/qurl.h>
13#include <QtCore/qxmlstream.h>
14#include <QtCore/qxpfunctional.h>
20using namespace Qt::StringLiterals;
33Q_STATIC_ASSERT(
sizeof(RANGE_TYPE_STRINGS) == MaximumRangeType *
sizeof(
const char *));
54 Q_DECLARE_PUBLIC(QQmlProfilerQtdWriter)
74 bool isEmpty()
const {
return events.isEmpty(); }
82 QQmlProfilerEventReceiver(*
new QQmlProfilerQtdWriterPrivate, parent)
90 Q_D(QQmlProfilerQtdWriter);
93 d->traceStartTime = std::numeric_limits<qint64>::max();
94 d->traceEndTime = std::numeric_limits<qint64>::min();
96 d->qmlMeasuredTime = 0;
97 d->setState(QQmlProfilerQtdWriterPrivate::Empty);
102 QQmlProfilerEventReceiver::startTrace(time, engineIds);
104 Q_D(QQmlProfilerQtdWriter);
105 if (time < d->traceStartTime)
106 d->traceStartTime = time;
111 QQmlProfilerEventReceiver::endTrace(time, engineIds);
113 Q_D(QQmlProfilerQtdWriter);
114 if (time > d->traceEndTime)
115 d->traceEndTime = time;
124 return QString::number(type);
132 return QString::number(type);
137 Q_D(QQmlProfilerQtdWriter);
138 d->setState(QQmlProfilerQtdWriterPrivate::AcquiringData);
139 d->events.append(event);
144 QQmlProfilerEventType newType = type;
148 if (!type.data().isEmpty()) {
149 details = type.data().simplified();
150 QRegularExpression rewrite(QStringLiteral(
"^\\(function \\$(\\w+)\\(\\) \\{ (return |)(.+) \\}\\)$"));
151 QRegularExpressionMatch match = rewrite.match(details);
152 if (match.hasMatch()) {
153 details = match.captured(1) +QLatin1String(
": ") + match.captured(3);
155 if (details.startsWith(QLatin1String(
"file://")))
156 details = details.mid(details.lastIndexOf(QLatin1Char(
'/')) + 1);
159 newType.setData(details);
162 switch (type.message()) {
164 switch (type.detailType()) {
167 displayName = QString::fromLatin1(
"Input:%1").arg(type.detailType());
170 displayName = QString::fromLatin1(
"AnimationFrame");
173 displayName = QString::fromLatin1(
"Unknown");
184 case PixmapCacheEvent: {
185 const QString filePath = QUrl(type.location().filename()).path();
186 displayName = QStringView{filePath}.mid(filePath.lastIndexOf(QLatin1Char(
'/')) + 1)
187 + QLatin1Char(
':') + QString::number(type.detailType());
190 case SceneGraphFrame:
191 displayName = QString::fromLatin1(
"SceneGraph:%1").arg(type.detailType());
193 case MemoryAllocation:
194 displayName = QString::fromLatin1(
"MemoryAllocation:%1").arg(type.detailType());
197 displayName = QString::fromLatin1(
"DebugMessage:%1").arg(type.detailType());
200 displayName = QString::fromLatin1(
"Quick3DFrame:%1").arg(type.detailType());
202 case MaximumMessage: {
203 const QQmlProfilerEventLocation eventLocation = type.location();
205 if (eventLocation.filename().isEmpty()) {
206 displayName = QString::fromLatin1(
"Unknown");
208 const QString filePath = QUrl(eventLocation.filename()).path();
209 displayName = QStringView{filePath}.mid(
210 filePath.lastIndexOf(QLatin1Char(
'/')) + 1) +
211 QLatin1Char(
':') + QString::number(eventLocation.line());
217 newType.setDisplayName(displayName);
218 d_func()->eventTypes.append(newType);
224 qint64 level0Start = -1;
227 for (
const QQmlProfilerEvent &event : std::as_const(events)) {
228 const QQmlProfilerEventType &type = eventTypes.at(event.typeIndex());
229 if (type.message() != MaximumMessage)
232 switch (type.rangeType()) {
238 switch (event.rangeStage()) {
241 level0Start = event.timestamp();
245 qmlMeasuredTime += event.timestamp() - level0Start;
259 return t1.timestamp() < t2.timestamp();
264 if (events.size() < 2)
269 QList<QQmlProfilerEvent>::iterator itFrom = events.end() - 2;
270 QList<QQmlProfilerEvent>::iterator itTo = events.end() - 1;
272 while (itFrom != events.begin() && itTo != events.begin()) {
274 while (itFrom != events.begin() && itTo->timestamp() > itFrom->timestamp()) {
280 if (itFrom == events.begin())
284 while (itFrom != events.begin() && itTo->timestamp() <= itFrom->timestamp())
287 if (itTo->timestamp() <= itFrom->timestamp())
288 std::sort(itFrom, itTo + 1, compareStartTimes);
290 std::sort(itFrom + 1, itTo + 1, compareStartTimes);
300 Q_D(QQmlProfilerQtdWriter);
301 d->setState(QQmlProfilerQtdWriterPrivate::ProcessingData);
304 d->setState(QQmlProfilerQtdWriterPrivate::Done);
305 QQmlProfilerEventReceiver::complete(maximumTime);
313 if (!filename.isEmpty()) {
314 file.setFileName(filename);
315 if (!file.open(QIODevice::WriteOnly)) {
316 error = QQmlProfilerQtdWriter::tr(
"Could not open %1 for writing").arg(filename);
320 if (!file.open(stdout, QIODevice::WriteOnly)) {
321 error = QQmlProfilerQtdWriter::tr(
"Could not open stdout for writing");
326 stream.setDevice(&file);
327 stream.setAutoFormatting(
true);
328 stream.writeStartDocument();
333 if (!error.isEmpty())
336 stream.writeEndDocument();
340 template<
typename Number>
343 stream.writeAttribute(QLatin1String(name), QString::number(number));
348 stream.writeAttribute(QLatin1String(name), QLatin1String(value));
351 void writeAttribute(
const char *name,
const QQmlProfilerEvent &event,
int i,
bool printZero =
true)
353 const qint64 number = event.number<qint64>(i);
354 if (printZero || number != 0)
355 writeAttribute(name, number);
358 template<
typename Number>
361 writeTextElement(name, QString::number(number));
366 stream.writeTextElement(QLatin1String(name), QLatin1String(value));
371 stream.writeTextElement(QLatin1String(name), value);
376 stream.writeStartElement(QLatin1String(name));
381 stream.writeEndElement();
386 QXmlStreamWriter stream;
393 qxp::function_ref<
void(
const QQmlProfilerEvent &, qint64)> &&sendEvent)
401 void handleRangeEvent(
const QQmlProfilerEvent &event,
const QQmlProfilerEventType &type);
406 const qxp::function_ref<
void(
const QQmlProfilerEvent &, qint64)> sendEvent;
408 QQueue<QQmlProfilerEvent> pointEvents;
416 const QQmlProfilerEvent &event,
const QQmlProfilerEventType &type)
418 QList<QQmlProfilerEvent> &starts = rangeStarts[type.rangeType()];
419 switch (event.rangeStage()) {
422 starts.append(event);
426 const qint64 invalidTimestamp = -1;
427 QList<qint64> &ends = rangeEnds[type.rangeType()];
430 ends.resize(starts.size(), invalidTimestamp);
432 qsizetype i = starts.size();
433 while (ends[--i] != invalidTimestamp) {}
436 Q_ASSERT(starts[i].timestamp() <= event.timestamp());
438 ends[i] = event.timestamp();
452 qsizetype index[MaximumRangeType] = { 0, 0, 0, 0, 0, 0 };
456 qsizetype minimum = MaximumRangeType;
457 qint64 minimumTime = std::numeric_limits<qint64>::max();
458 for (qsizetype i = 0; i < MaximumRangeType; ++i) {
459 const QList<QQmlProfilerEvent> &starts = rangeStarts[i];
460 if (starts.size() == index[i])
462 const qint64 timestamp = starts[index[i]].timestamp();
463 if (timestamp < minimumTime) {
464 minimumTime = timestamp;
468 if (minimum == MaximumRangeType)
472 while (!pointEvents.isEmpty() && pointEvents.front().timestamp() < minimumTime)
473 sendEvent(pointEvents.dequeue(), 0);
476 sendEvent(rangeStarts[minimum][index[minimum]],
477 rangeEnds[minimum][index[minimum]] - minimumTime);
487 for (qsizetype i = 0; i < MaximumRangeType; ++i) {
488 rangeStarts[i].clear();
489 rangeEnds[i].clear();
495 for (
const QQmlProfilerEvent &event : std::as_const(d->events)) {
496 const QQmlProfilerEventType &type = d->eventTypes.at(event.typeIndex());
497 if (type.rangeType() != MaximumRangeType)
498 handleRangeEvent(event, type);
502 pointEvents.enqueue(event);
505 for (qsizetype i = 0; i < MaximumRangeType; ++i) {
506 while (rangeEnds[i].size() < rangeStarts[i].size()) {
507 rangeEnds[i].append(d->traceEndTime);
517 Q_D(QQmlProfilerQtdWriter);
520 emit error(tr(
"No data to save"));
525 if (!stream.error.isEmpty()) {
526 emit error(stream.error);
531 stream.writeAttribute(
"traceStart", d->traceStartTime);
532 stream.writeAttribute(
"traceEnd", d->traceEndTime);
535 stream.writeAttribute(
"totalTime", d->qmlMeasuredTime);
537 for (
int typeIndex = 0, end = d->eventTypes.size(); typeIndex < end; ++typeIndex) {
538 const QQmlProfilerEventType &eventData = d->eventTypes.at(typeIndex);
540 stream.writeAttribute(
"index", typeIndex);
541 if (!eventData.displayName().isEmpty())
542 stream.writeTextElement(
"displayname", eventData.displayName());
544 stream.writeTextElement(
"type", eventData.rangeType() == MaximumRangeType
545 ? qmlMessageAsString(eventData.message())
546 : qmlRangeTypeAsString(eventData.rangeType()));
548 const QQmlProfilerEventLocation location = eventData.location();
549 if (!location.filename().isEmpty())
550 stream.writeTextElement(
"filename", location.filename());
551 if (location.line() >= 0)
552 stream.writeTextElement(
"line", location.line());
553 if (location.column() >= 0)
554 stream.writeTextElement(
"column", location.column());
555 if (!eventData.data().isEmpty())
556 stream.writeTextElement(
"details", eventData.data());
557 if (eventData.rangeType() == Binding)
558 stream.writeTextElement(
"bindingType", eventData.detailType());
559 else if (eventData.message() == Event) {
560 switch (eventData.detailType()) {
562 stream.writeTextElement(
"animationFrame", eventData.detailType());
565 stream.writeTextElement(
"keyEvent", eventData.detailType());
568 stream.writeTextElement(
"mouseEvent", eventData.detailType());
571 }
else if (eventData.message() == PixmapCacheEvent)
572 stream.writeTextElement(
"cacheEventType", eventData.detailType());
573 else if (eventData.message() == SceneGraphFrame)
574 stream.writeTextElement(
"sgEventType", eventData.detailType());
575 else if (eventData.message() == MemoryAllocation)
576 stream.writeTextElement(
"memoryEventType", eventData.detailType());
583 auto sendEvent = [&](
const QQmlProfilerEvent &event, qint64 duration = 0) {
584 Q_ASSERT(duration >= 0);
585 const QQmlProfilerEventType &type = d->eventTypes.at(event.typeIndex());
587 stream.writeAttribute(
"startTime", event.timestamp());
589 stream.writeAttribute(
"duration", duration);
590 stream.writeAttribute(
"eventIndex", event.typeIndex());
591 if (type.message() == Event) {
592 if (type.detailType() == AnimationFrame) {
594 stream.writeAttribute(
"framerate", event, 0);
595 stream.writeAttribute(
"animationcount", event, 1);
596 stream.writeAttribute(
"thread", event, 2);
597 }
else if (type.detailType() == Key || type.detailType() == Mouse) {
599 stream.writeAttribute(
"type", event, 0);
600 stream.writeAttribute(
"data1", event, 1);
601 stream.writeAttribute(
"data2", event, 2);
603 }
else if (type.message() == PixmapCacheEvent) {
605 if (type.detailType() == PixmapSizeKnown) {
606 stream.writeAttribute(
"width", event, 0);
607 stream.writeAttribute(
"height", event, 1);
608 }
else if (type.detailType() == PixmapReferenceCountChanged
609 || type.detailType() == PixmapCacheCountChanged) {
610 stream.writeAttribute(
"refCount", event, 1);
612 }
else if (type.message() == SceneGraphFrame) {
613 stream.writeAttribute(
"timing1", event, 0,
false);
614 stream.writeAttribute(
"timing2", event, 1,
false);
615 stream.writeAttribute(
"timing3", event, 2,
false);
616 stream.writeAttribute(
"timing4", event, 3,
false);
617 stream.writeAttribute(
"timing5", event, 4,
false);
618 }
else if (type.message() == MemoryAllocation) {
619 stream.writeAttribute(
"amount", event, 0);
624 DataIterator(d, std::move(sendEvent)).run();
634 if (state == newState)
637 Q_Q(QQmlProfilerQtdWriter);
642 emit q->error(
"Invalid qmlprofiler state change (Empty)"_L1);
646 if (state == ProcessingData)
647 emit q->error(
"Invalid qmlprofiler state change (AcquiringData)"_L1);
650 if (state != AcquiringData)
651 emit q->error(
"Invalid qmlprofiler state change (ProcessingData)"_L1);
654 if (state != ProcessingData && state != Empty)
655 emit q->error(
"Invalid qmlprofiler state change (Done)"_L1);
658 emit q->error(
"Trying to set unknown state in events list"_L1);
665 if (state == Done && isEmpty())
672 return d_func()->eventTypes.size();
677 return d_func()->events.size();
682 return d_func()->eventTypes;
687 return d_func()->events;
692#include "moc_qqmlprofilerqtdwriter_p.cpp"
QList< QQmlProfilerEventType > eventTypes
void setState(State state)
QList< QQmlProfilerEvent > events
void addEvent(const QQmlProfilerEvent &event) final
QList< QQmlProfilerEvent > events() const
void complete(qint64 maximumTime) final
void endTrace(qint64 time, const QList< int > &engineIds) final
QList< QQmlProfilerEventType > eventTypes() const
qsizetype numLoadedEventTypes() const final
qsizetype numLoadedEvents() const final
bool save(const QString &filename) final
void addEventType(const QQmlProfilerEventType &type) final
void startTrace(qint64 time, const QList< int > &engineIds) final
static const char * MESSAGE_STRINGS[]
static const char * RANGE_TYPE_STRINGS[]
static QString qmlRangeTypeAsString(RangeType type)
bool compareStartTimes(const QQmlProfilerEvent &t1, const QQmlProfilerEvent &t2)
const char PROFILER_FILE_VERSION[]
static QString qmlMessageAsString(Message type)
Q_STATIC_ASSERT(sizeof(SharedImageHeader) % 4==0)
DataIterator(const QQmlProfilerQtdWriterPrivate *d, qxp::function_ref< void(const QQmlProfilerEvent &, qint64)> &&sendEvent)
void writeStartElement(const char *name)
StreamWriter(const QString &filename)
void writeAttribute(const char *name, Number number)
void writeAttribute(const char *name, const char *value)
void writeTextElement(const char *name, Number number)
void writeTextElement(const char *name, const char *value)
void writeAttribute(const char *name, const QQmlProfilerEvent &event, int i, bool printZero=true)
void writeTextElement(const char *name, const QString &value)