aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2017-12-14 20:06:57 +0100
committerUlf Hermann <ulf.hermann@qt.io>2018-02-02 09:38:46 +0000
commitb82296f825daf0ba110fea4aa1b61f96d63f371b (patch)
treeb4d0101fcc6992c4d567b9fafe2c7ec855b915a5
parent65606ea1559572d66ee8bfac77e87f3e8f447c3e (diff)
Use better QmlProfiler client from Qt Creator
This client can track locations itself, and thus doesn't require the server to send the event types over and over with each message. Once all our client implementations have this feature we can drop a lot of code. Furthermore, this way we can write regression tests for bugs that only occur when client side location tracking is active. Change-Id: I3735392452e20a7be98e92b900fadef04701d85f Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
-rw-r--r--src/qml/debugger/qqmlprofilerdefinitions_p.h21
-rw-r--r--src/qmldebug/qmldebug.pro16
-rw-r--r--src/qmldebug/qqmldebugmessageclient.cpp95
-rw-r--r--src/qmldebug/qqmldebugmessageclient_p.h87
-rw-r--r--src/qmldebug/qqmlprofilerclient.cpp489
-rw-r--r--src/qmldebug/qqmlprofilerclient_p.h62
-rw-r--r--src/qmldebug/qqmlprofilerclient_p_p.h59
-rw-r--r--src/qmldebug/qqmlprofilerevent.cpp273
-rw-r--r--src/qmldebug/qqmlprofilerevent_p.h355
-rw-r--r--src/qmldebug/qqmlprofilereventlocation.cpp56
-rw-r--r--src/qmldebug/qqmlprofilereventlocation_p.h121
-rw-r--r--src/qmldebug/qqmlprofilereventreceiver_p.h63
-rw-r--r--src/qmldebug/qqmlprofilereventtype.cpp92
-rw-r--r--src/qmldebug/qqmlprofilereventtype_p.h126
-rw-r--r--src/qmldebug/qqmlprofilertypedevent.cpp226
-rw-r--r--src/qmldebug/qqmlprofilertypedevent_p.h (renamed from src/qmldebug/qqmleventlocation_p.h)31
-rw-r--r--tests/auto/qml/debugger/qqmlprofilerservice/data/controlFromJS.qml7
-rw-r--r--tests/auto/qml/debugger/qqmlprofilerservice/data/exit.qml2
-rw-r--r--tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp668
-rw-r--r--tools/qmlprofiler/qmlprofilerapplication.cpp57
-rw-r--r--tools/qmlprofiler/qmlprofilerapplication.h6
-rw-r--r--tools/qmlprofiler/qmlprofilerclient.cpp141
-rw-r--r--tools/qmlprofiler/qmlprofilerclient.h21
-rw-r--r--tools/qmlprofiler/qmlprofilerdata.cpp727
-rw-r--r--tools/qmlprofiler/qmlprofilerdata.h23
25 files changed, 2561 insertions, 1263 deletions
diff --git a/src/qml/debugger/qqmlprofilerdefinitions_p.h b/src/qml/debugger/qqmlprofilerdefinitions_p.h
index 91d0376837..f84a2c44e2 100644
--- a/src/qml/debugger/qqmlprofilerdefinitions_p.h
+++ b/src/qml/debugger/qqmlprofilerdefinitions_p.h
@@ -69,6 +69,7 @@ struct QQmlProfilerDefinitions {
PixmapCacheEvent,
SceneGraphFrame,
MemoryAllocation,
+ DebugMessage,
MaximumMessage
};
@@ -161,6 +162,26 @@ struct QQmlProfilerDefinitions {
MaximumInputEventType
};
+
+ static ProfileFeature featureFromRangeType(RangeType range)
+ {
+ switch (range) {
+ case Painting:
+ return ProfilePainting;
+ case Compiling:
+ return ProfileCompiling;
+ case Creating:
+ return ProfileCreating;
+ case Binding:
+ return ProfileBinding;
+ case HandlingSignal:
+ return ProfileHandlingSignal;
+ case Javascript:
+ return ProfileJavaScript;
+ default:
+ return MaximumProfileFeature;
+ }
+ }
};
QT_END_NAMESPACE
diff --git a/src/qmldebug/qmldebug.pro b/src/qmldebug/qmldebug.pro
index e5f6de3314..2539414d8f 100644
--- a/src/qmldebug/qmldebug.pro
+++ b/src/qmldebug/qmldebug.pro
@@ -7,15 +7,25 @@ load(qt_module)
SOURCES += \
qqmldebugclient.cpp \
qqmldebugconnection.cpp \
+ qqmldebugmessageclient.cpp \
qqmlenginecontrolclient.cpp \
- qqmlprofilerclient.cpp
+ qqmlprofilerclient.cpp \
+ qqmlprofilerevent.cpp \
+ qqmlprofilereventlocation.cpp \
+ qqmlprofilereventtype.cpp \
+ qqmlprofilertypedevent.cpp
HEADERS += \
qqmldebugclient_p.h \
qqmldebugclient_p_p.h \
qqmldebugconnection_p.h \
+ qqmldebugmessageclient_p.h \
qqmlenginecontrolclient_p.h \
qqmlenginecontrolclient_p_p.h \
- qqmleventlocation_p.h \
qqmlprofilerclient_p.h \
- qqmlprofilerclient_p_p.h
+ qqmlprofilerclient_p_p.h \
+ qqmlprofilerevent_p.h \
+ qqmlprofilereventlocation_p.h \
+ qqmlprofilereventreceiver_p.h \
+ qqmlprofilereventtype_p.h \
+ qqmlprofilertypedevent_p.h
diff --git a/src/qmldebug/qqmldebugmessageclient.cpp b/src/qmldebug/qqmldebugmessageclient.cpp
new file mode 100644
index 0000000000..a03c1f8af2
--- /dev/null
+++ b/src/qmldebug/qqmldebugmessageclient.cpp
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmldebugmessageclient_p.h"
+
+#include <QtCore/qdatastream.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QQmlDebugMessageClient
+ \internal
+
+ \brief Client for the debug message service
+
+ The QQmlDebugMessageClient receives debug messages routed through the QML
+ debug connection via QDebugMessageService.
+ */
+
+QQmlDebugMessageClient::QQmlDebugMessageClient(QQmlDebugConnection *client)
+ : QQmlDebugClient(QLatin1String("DebugMessages"), client)
+{
+}
+
+void QQmlDebugMessageClient::stateChanged(State state)
+{
+ emit newState(state);
+}
+
+void QQmlDebugMessageClient::messageReceived(const QByteArray &data)
+{
+ QDataStream ds(data);
+ QByteArray command;
+ ds >> command;
+
+ if (command == "MESSAGE") {
+ int type;
+ int line;
+ QByteArray debugMessage;
+ QByteArray file;
+ QByteArray function;
+ ds >> type >> debugMessage >> file >> line >> function;
+ QQmlDebugContextInfo info;
+ info.line = line;
+ info.file = QString::fromUtf8(file);
+ info.function = QString::fromUtf8(function);
+ info.timestamp = -1;
+ if (!ds.atEnd()) {
+ QByteArray category;
+ ds >> category;
+ info.category = QString::fromUtf8(category);
+ if (!ds.atEnd())
+ ds >> info.timestamp;
+ }
+ emit message(QtMsgType(type), QString::fromUtf8(debugMessage), info);
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/qmldebug/qqmldebugmessageclient_p.h b/src/qmldebug/qqmldebugmessageclient_p.h
new file mode 100644
index 0000000000..75c70044e4
--- /dev/null
+++ b/src/qmldebug/qqmldebugmessageclient_p.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLDEBUGMESSAGECLIENT_P_H
+#define QQMLDEBUGMESSAGECLIENT_P_H
+
+#include "qqmldebugclient_p.h"
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+QT_BEGIN_NAMESPACE
+
+struct QQmlDebugContextInfo
+{
+ int line;
+ QString file;
+ QString function;
+ QString category;
+ qint64 timestamp;
+};
+
+class QQmlDebugMessageClient : public QQmlDebugClient
+{
+ Q_OBJECT
+
+public:
+ explicit QQmlDebugMessageClient(QQmlDebugConnection *client);
+
+ virtual void stateChanged(State state) override;
+ virtual void messageReceived(const QByteArray &) override;
+
+signals:
+ void newState(QQmlDebugClient::State);
+ void message(QtMsgType, const QString &, const QQmlDebugContextInfo &);
+
+private:
+ Q_DISABLE_COPY(QQmlDebugMessageClient)
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLDEBUGMESSAGECLIENT_P_H
diff --git a/src/qmldebug/qqmlprofilerclient.cpp b/src/qmldebug/qqmlprofilerclient.cpp
index 3676bd933c..dca564cd76 100644
--- a/src/qmldebug/qqmlprofilerclient.cpp
+++ b/src/qmldebug/qqmlprofilerclient.cpp
@@ -43,337 +43,296 @@
QT_BEGIN_NAMESPACE
-QQmlProfilerClient::QQmlProfilerClient(QQmlDebugConnection *connection) :
- QQmlDebugClient(*(new QQmlProfilerClientPrivate(connection)))
+int QQmlProfilerClientPrivate::resolveType(const QQmlProfilerTypedEvent &event)
{
-}
+ int typeIndex = -1;
+ if (event.serverTypeId != 0) {
+ QHash<qint64, int>::ConstIterator it = serverTypeIds.constFind(event.serverTypeId);
-QQmlProfilerClient::QQmlProfilerClient(QQmlProfilerClientPrivate &dd) :
- QQmlDebugClient(dd)
-{
+ if (it != serverTypeIds.constEnd()) {
+ typeIndex = it.value();
+ } else {
+ typeIndex = eventReceiver->numLoadedEventTypes();
+ eventReceiver->addEventType(event.type);
+ serverTypeIds[event.serverTypeId] = typeIndex;
+ }
+ } else {
+ QHash<QQmlProfilerEventType, int>::ConstIterator it = eventTypeIds.constFind(event.type);
+
+ if (it != eventTypeIds.constEnd()) {
+ typeIndex = it.value();
+ } else {
+ typeIndex = eventReceiver->numLoadedEventTypes();
+ eventReceiver->addEventType(event.type);
+ eventTypeIds[event.type] = typeIndex;
+ }
+ }
+ return typeIndex;
}
-QQmlProfilerClientPrivate::QQmlProfilerClientPrivate(QQmlDebugConnection *connection) :
- QQmlDebugClientPrivate(QQmlProfilerService::s_key, connection),
- features(std::numeric_limits<quint64>::max())
+int QQmlProfilerClientPrivate::resolveStackTop()
{
+ if (rangesInProgress.isEmpty())
+ return -1;
+
+ QQmlProfilerTypedEvent &typedEvent = rangesInProgress.top();
+ int typeIndex = typedEvent.event.typeIndex();
+ if (typeIndex >= 0)
+ return typeIndex;
+
+ typeIndex = resolveType(typedEvent);
+ typedEvent.event.setTypeIndex(typeIndex);
+ while (!pendingMessages.isEmpty()
+ && pendingMessages.head().timestamp() < typedEvent.event.timestamp()) {
+ forwardEvents(pendingMessages.dequeue());
+ }
+ forwardEvents(typedEvent.event);
+ return typeIndex;
}
-void QQmlProfilerClient::setFeatures(quint64 features)
+void QQmlProfilerClientPrivate::forwardEvents(const QQmlProfilerEvent &last)
{
- Q_D(QQmlProfilerClient);
- d->features = features;
+ while (!pendingDebugMessages.isEmpty()
+ && pendingDebugMessages.front().timestamp() <= last.timestamp()) {
+ eventReceiver->addEvent(pendingDebugMessages.dequeue());
+ }
+ eventReceiver->addEvent(last);
}
-void QQmlProfilerClient::sendRecordingStatus(bool record, int engineId, quint32 flushInterval)
+void QQmlProfilerClientPrivate::processCurrentEvent()
{
- Q_D(const QQmlProfilerClient);
-
- QPacket stream(d->connection->currentDataStreamVersion());
- stream << record << engineId << d->features << flushInterval << true;
- sendMessage(stream.data());
+ // RangeData and RangeLocation always apply to the range on the top of the stack. Furthermore,
+ // all ranges are perfectly nested. This is why we can defer the type resolution until either
+ // the range ends or a child range starts. With only the information in RangeStart we wouldn't
+ // be able to uniquely identify the event type.
+ Message rangeStage = currentEvent.type.rangeType() == MaximumRangeType ?
+ currentEvent.type.message() : currentEvent.event.rangeStage();
+ switch (rangeStage) {
+ case RangeStart:
+ resolveStackTop();
+ rangesInProgress.push(currentEvent);
+ break;
+ case RangeEnd: {
+ int typeIndex = resolveStackTop();
+ if (typeIndex == -1)
+ break;
+ currentEvent.event.setTypeIndex(typeIndex);
+ while (!pendingMessages.isEmpty())
+ forwardEvents(pendingMessages.dequeue());
+ forwardEvents(currentEvent.event);
+ rangesInProgress.pop();
+ break;
+ }
+ case RangeData:
+ if (!rangesInProgress.isEmpty())
+ rangesInProgress.top().type.setData(currentEvent.type.data());
+ break;
+ case RangeLocation:
+ if (!rangesInProgress.isEmpty())
+ rangesInProgress.top().type.setLocation(currentEvent.type.location());
+ break;
+ case DebugMessage:
+ currentEvent.event.setTypeIndex(resolveType(currentEvent));
+ pendingDebugMessages.enqueue(currentEvent.event);
+ break;
+ default: {
+ int typeIndex = resolveType(currentEvent);
+ currentEvent.event.setTypeIndex(typeIndex);
+ if (rangesInProgress.isEmpty())
+ eventReceiver->addEvent(currentEvent.event);
+ else
+ pendingMessages.enqueue(currentEvent.event);
+ break;
+ }
+ }
}
-void QQmlProfilerClient::traceStarted(qint64 time, int engineId)
+void QQmlProfilerClientPrivate::sendRecordingStatus(int engineId)
{
- Q_UNUSED(time);
- Q_UNUSED(engineId);
+ Q_Q(QQmlProfilerClient);
+ QPacket stream(connection->currentDataStreamVersion());
+ stream << recording << engineId; // engineId -1 is OK. It means "all of them"
+ if (recording) {
+ stream << requestedFeatures << flushInterval;
+ stream << true; // yes, we support type IDs
+ }
+ q->sendMessage(stream.data());
}
-void QQmlProfilerClient::traceFinished(qint64 time, int engineId)
+QQmlProfilerClient::QQmlProfilerClient(QQmlDebugConnection *connection,
+ QQmlProfilerEventReceiver *eventReceiver,
+ quint64 features)
+ : QQmlDebugClient(*(new QQmlProfilerClientPrivate(connection, eventReceiver)))
{
- Q_UNUSED(time);
- Q_UNUSED(engineId);
+ Q_D(QQmlProfilerClient);
+ setRequestedFeatures(features);
+ connect(d->engineControl.data(), &QQmlEngineControlClient::engineAboutToBeAdded,
+ this, &QQmlProfilerClient::sendRecordingStatus);
}
-void QQmlProfilerClient::rangeStart(QQmlProfilerDefinitions::RangeType type, qint64 startTime)
+QQmlProfilerClient::~QQmlProfilerClient()
{
- Q_UNUSED(type);
- Q_UNUSED(startTime);
+ //Disable profiling if started by client
+ //Profiling data will be lost!!
+ if (isRecording())
+ setRecording(false);
}
-void QQmlProfilerClient::rangeData(QQmlProfilerDefinitions::RangeType type, qint64 time,
- const QString &data)
+void QQmlProfilerClient::clearEvents()
{
- Q_UNUSED(type);
- Q_UNUSED(time);
- Q_UNUSED(data);
+ Q_D(QQmlProfilerClient);
+ d->rangesInProgress.clear();
+ d->pendingMessages.clear();
+ d->pendingDebugMessages.clear();
+ if (d->recordedFeatures != 0) {
+ d->recordedFeatures = 0;
+ emit recordedFeaturesChanged(0);
+ }
+ emit cleared();
}
-void QQmlProfilerClient::rangeLocation(QQmlProfilerDefinitions::RangeType type, qint64 time,
- const QQmlEventLocation &location)
+void QQmlProfilerClient::clearAll()
{
- Q_UNUSED(type);
- Q_UNUSED(time);
- Q_UNUSED(location);
+ Q_D(QQmlProfilerClient);
+ d->serverTypeIds.clear();
+ d->eventTypeIds.clear();
+ clearEvents();
}
-void QQmlProfilerClient::rangeEnd(QQmlProfilerDefinitions::RangeType type, qint64 endTime)
+void QQmlProfilerClientPrivate::finalize()
{
- Q_UNUSED(type);
- Q_UNUSED(endTime);
+ while (!rangesInProgress.isEmpty()) {
+ currentEvent = rangesInProgress.top();
+ currentEvent.event.setRangeStage(RangeEnd);
+ currentEvent.event.setTimestamp(maximumTime);
+ processCurrentEvent();
+ }
+ while (!pendingDebugMessages.isEmpty())
+ eventReceiver->addEvent(pendingDebugMessages.dequeue());
}
-void QQmlProfilerClient::animationFrame(qint64 time, int frameRate, int animationCount,
- int threadId)
+
+void QQmlProfilerClient::sendRecordingStatus(int engineId)
{
- Q_UNUSED(time);
- Q_UNUSED(frameRate);
- Q_UNUSED(animationCount);
- Q_UNUSED(threadId);
+ Q_D(QQmlProfilerClient);
+ d->sendRecordingStatus(engineId);
}
-void QQmlProfilerClient::sceneGraphEvent(QQmlProfilerDefinitions::SceneGraphFrameType type,
- qint64 time, qint64 numericData1, qint64 numericData2,
- qint64 numericData3, qint64 numericData4,
- qint64 numericData5)
+bool QQmlProfilerClient::isRecording() const
{
- Q_UNUSED(type);
- Q_UNUSED(time);
- Q_UNUSED(numericData1);
- Q_UNUSED(numericData2);
- Q_UNUSED(numericData3);
- Q_UNUSED(numericData4);
- Q_UNUSED(numericData5);
+ Q_D(const QQmlProfilerClient);
+ return d->recording;
}
-void QQmlProfilerClient::pixmapCacheEvent(QQmlProfilerDefinitions::PixmapEventType type,
- qint64 time, const QString &url, int numericData1,
- int numericData2)
+void QQmlProfilerClient::setRecording(bool v)
{
- Q_UNUSED(type);
- Q_UNUSED(time);
- Q_UNUSED(url);
- Q_UNUSED(numericData1);
- Q_UNUSED(numericData2);
+ Q_D(QQmlProfilerClient);
+ if (v == d->recording)
+ return;
+
+ d->recording = v;
+
+ if (state() == Enabled)
+ sendRecordingStatus();
+
+ emit recordingChanged(v);
}
-void QQmlProfilerClient::memoryAllocation(QQmlProfilerDefinitions::MemoryType type, qint64 time,
- qint64 amount)
+quint64 QQmlProfilerClient::recordedFeatures() const
{
- Q_UNUSED(type);
- Q_UNUSED(time);
- Q_UNUSED(amount);
+ Q_D(const QQmlProfilerClient);
+ return d->recordedFeatures;
}
-void QQmlProfilerClient::inputEvent(QQmlProfilerDefinitions::InputEventType type, qint64 time,
- int a, int b)
+void QQmlProfilerClient::setRequestedFeatures(quint64 features)
{
- Q_UNUSED(type);
- Q_UNUSED(time);
- Q_UNUSED(a);
- Q_UNUSED(b);
+ Q_D(QQmlProfilerClient);
+ d->requestedFeatures = features;
+ if (features & static_cast<quint64>(1) << ProfileDebugMessages) {
+ if (d->messageClient.isNull()) {
+ d->messageClient.reset(new QQmlDebugMessageClient(connection()));
+ connect(d->messageClient.data(), &QQmlDebugMessageClient::message, this,
+ [this](QtMsgType type, const QString &text, const QQmlDebugContextInfo &context)
+ {
+ Q_D(QQmlProfilerClient);
+ d->updateFeatures(ProfileDebugMessages);
+ d->currentEvent.event.setTimestamp(context.timestamp > 0 ? context.timestamp : 0);
+ d->currentEvent.event.setTypeIndex(-1);
+ d->currentEvent.event.setString(text);
+ d->currentEvent.type = QQmlProfilerEventType(
+ DebugMessage, MaximumRangeType, type,
+ QQmlProfilerEventLocation(context.file, context.line, 1));
+ d->currentEvent.serverTypeId = 0;
+ d->processCurrentEvent();
+ });
+ }
+ } else {
+ d->messageClient.reset();
+ }
}
-void QQmlProfilerClient::complete()
+void QQmlProfilerClient::setFlushInterval(quint32 flushInterval)
{
+ Q_D(QQmlProfilerClient);
+ d->flushInterval = flushInterval;
}
-void QQmlProfilerClient::unknownEvent(QQmlProfilerDefinitions::Message messageType, qint64 time,
- int detailType)
+QQmlProfilerClient::QQmlProfilerClient(QQmlProfilerClientPrivate &dd) :
+ QQmlDebugClient(dd)
{
- Q_UNUSED(messageType);
- Q_UNUSED(time);
- Q_UNUSED(detailType);
+ Q_D(QQmlProfilerClient);
+ connect(d->engineControl.data(), &QQmlEngineControlClient::engineAboutToBeAdded,
+ this, &QQmlProfilerClient::sendRecordingStatus);
}
-void QQmlProfilerClient::unknownData(QPacket &stream)
+bool QQmlProfilerClientPrivate::updateFeatures(ProfileFeature feature)
{
- Q_UNUSED(stream);
+ Q_Q(QQmlProfilerClient);
+ quint64 flag = 1ULL << feature;
+ if (!(requestedFeatures & flag))
+ return false;
+ if (!(recordedFeatures & flag)) {
+ recordedFeatures |= flag;
+ emit q->recordedFeaturesChanged(recordedFeatures);
+ }
+ return true;
}
-inline QQmlProfilerDefinitions::ProfileFeature featureFromRangeType(
- QQmlProfilerDefinitions::RangeType range)
+void QQmlProfilerClient::stateChanged(State status)
{
- switch (range) {
- case QQmlProfilerDefinitions::Painting:
- return QQmlProfilerDefinitions::ProfilePainting;
- case QQmlProfilerDefinitions::Compiling:
- return QQmlProfilerDefinitions::ProfileCompiling;
- case QQmlProfilerDefinitions::Creating:
- return QQmlProfilerDefinitions::ProfileCreating;
- case QQmlProfilerDefinitions::Binding:
- return QQmlProfilerDefinitions::ProfileBinding;
- case QQmlProfilerDefinitions::HandlingSignal:
- return QQmlProfilerDefinitions::ProfileHandlingSignal;
- case QQmlProfilerDefinitions::Javascript:
- return QQmlProfilerDefinitions::ProfileJavaScript;
- default:
- return QQmlProfilerDefinitions::MaximumProfileFeature;
+ if (status == Enabled) {
+ sendRecordingStatus(-1);
+ } else {
+ Q_D(QQmlProfilerClient);
+ d->finalize();
}
+
}
void QQmlProfilerClient::messageReceived(const QByteArray &data)
{
Q_D(QQmlProfilerClient);
-
QPacket stream(d->connection->currentDataStreamVersion(), data);
- // Force all the 1 << <FLAG> expressions to be done in 64 bit, to silence some warnings
- const quint64 one = static_cast<quint64>(1);
-
- qint64 time;
- int messageType;
-
- stream >> time >> messageType;
-
- if (messageType >= QQmlProfilerDefinitions::MaximumMessage) {
- unknownEvent(static_cast<QQmlProfilerDefinitions::Message>(messageType), time, -1);
- return;
+ stream >> d->currentEvent;
+
+ d->maximumTime = qMax(d->currentEvent.event.timestamp(), d->maximumTime);
+ if (d->currentEvent.type.message() == Complete) {
+ d->finalize();
+ emit complete(d->maximumTime);
+ } else if (d->currentEvent.type.message() == Event
+ && d->currentEvent.type.detailType() == StartTrace) {
+ emit traceStarted(d->currentEvent.event.timestamp(),
+ d->currentEvent.event.numbers<QList<int>, qint32>());
+ } else if (d->currentEvent.type.message() == Event
+ && d->currentEvent.type.detailType() == EndTrace) {
+ emit traceFinished(d->currentEvent.event.timestamp(),
+ d->currentEvent.event.numbers<QList<int>, qint32>());
+ } else if (d->updateFeatures(d->currentEvent.type.feature())) {
+ d->processCurrentEvent();
}
-
- if (messageType == QQmlProfilerDefinitions::Event) {
- int type;
- stream >> type;
-
- QQmlProfilerDefinitions::EventType eventType =
- static_cast<QQmlProfilerDefinitions::EventType>(type);
-
- if (eventType == QQmlProfilerDefinitions::EndTrace) {
- int engineId = -1;
- if (!stream.atEnd())
- stream >> engineId;
- traceFinished(time, engineId);
- } else if (eventType == QQmlProfilerDefinitions::AnimationFrame) {
- if (!(d->features & one << QQmlProfilerDefinitions::ProfileAnimations))
- return;
-
- int frameRate, animationCount;
- int threadId = 0;
- stream >> frameRate >> animationCount;
- if (!stream.atEnd())
- stream >> threadId;
-
- animationFrame(time, frameRate, animationCount, threadId);
- } else if (type == QQmlProfilerDefinitions::StartTrace) {
- int engineId = -1;
- if (!stream.atEnd())
- stream >> engineId;
- traceStarted(time, engineId);
- } else if (eventType == QQmlProfilerDefinitions::Key ||
- eventType == QQmlProfilerDefinitions::Mouse) {
-
- if (!(d->features & one << QQmlProfilerDefinitions::ProfileInputEvents))
- return;
-
- int type;
- if (!stream.atEnd()) {
- stream >> type;
- } else {
- type = (eventType == QQmlProfilerDefinitions::Key) ?
- QQmlProfilerDefinitions::InputKeyUnknown :
- QQmlProfilerDefinitions::InputMouseUnknown;
- }
-
- int a = 0;
- if (!stream.atEnd())
- stream >> a;
-
- int b = 0;
- if (!stream.atEnd())
- stream >> b;
-
- inputEvent(static_cast<QQmlProfilerDefinitions::InputEventType>(type), time, a, b);
- } else {
- unknownEvent(QQmlProfilerDefinitions::Event, time, type);
- }
- } else if (messageType == QQmlProfilerDefinitions::Complete) {
- complete();
- } else if (messageType == QQmlProfilerDefinitions::SceneGraphFrame) {
- if (!(d->features & one << QQmlProfilerDefinitions::ProfileSceneGraph))
- return;
-
- int type;
- int count = 0;
- qint64 params[5];
-
- stream >> type;
- while (!stream.atEnd())
- stream >> params[count++];
-
- while (count < 5)
- params[count++] = 0;
-
- sceneGraphEvent(static_cast<QQmlProfilerDefinitions::SceneGraphFrameType>(type), time,
- params[0], params[1], params[2], params[3], params[4]);
- } else if (messageType == QQmlProfilerDefinitions::PixmapCacheEvent) {
- if (!(d->features & one << QQmlProfilerDefinitions::ProfilePixmapCache))
- return;
-
- int type, param1 = 0, param2 = 0;
- QString pixUrl;
- stream >> type >> pixUrl;
-
- QQmlProfilerDefinitions::PixmapEventType pixmapEventType =
- static_cast<QQmlProfilerDefinitions::PixmapEventType>(type);
-
- if (pixmapEventType == QQmlProfilerDefinitions::PixmapReferenceCountChanged ||
- pixmapEventType == QQmlProfilerDefinitions::PixmapCacheCountChanged) {
- stream >> param1;
- } else if (pixmapEventType == QQmlProfilerDefinitions::PixmapSizeKnown) {
- stream >> param1 >> param2;
- }
-
- pixmapCacheEvent(pixmapEventType, time, pixUrl, param1, param2);
- } else if (messageType == QQmlProfilerDefinitions::MemoryAllocation) {
- if (!(d->features & one << QQmlProfilerDefinitions::ProfileMemory))
- return;
- int type;
- qint64 delta;
- stream >> type >> delta;
- memoryAllocation((QQmlProfilerDefinitions::MemoryType)type, time, delta);
- } else {
- int range;
- stream >> range;
-
- QQmlProfilerDefinitions::RangeType rangeType =
- static_cast<QQmlProfilerDefinitions::RangeType>(range);
-
- if (range >= QQmlProfilerDefinitions::MaximumRangeType ||
- !(d->features & one << featureFromRangeType(rangeType)))
- return;
-
- qint64 typeId = 0;
- if (messageType == QQmlProfilerDefinitions::RangeStart) {
- rangeStart(rangeType, time);
- if (!stream.atEnd()) {
- stream >> typeId;
- auto i = d->types.constFind(typeId);
- if (i != d->types.constEnd()) {
- rangeLocation(rangeType, time, i->location);
- rangeData(rangeType, time, i->name);
- }
- }
- } else if (messageType == QQmlProfilerDefinitions::RangeData) {
- QString data;
- stream >> data;
- rangeData(rangeType, time, data);
- if (!stream.atEnd()) {
- stream >> typeId;
- d->types[typeId].name = data;
- }
- } else if (messageType == QQmlProfilerDefinitions::RangeLocation) {
- QQmlEventLocation location;
- stream >> location.filename >> location.line;
-
- if (!stream.atEnd())
- stream >> location.column;
-
- rangeLocation(rangeType, time, location);
- if (!stream.atEnd()) {
- stream >> typeId;
- d->types[typeId].location = location;
- }
- } else if (messageType == QQmlProfilerDefinitions::RangeEnd) {
- rangeEnd(rangeType, time);
- } else {
- unknownEvent(static_cast<QQmlProfilerDefinitions::Message>(messageType), time, range);
- }
- }
-
- if (!stream.atEnd())
- unknownData(stream);
}
+
QT_END_NAMESPACE
#include "moc_qqmlprofilerclient_p.cpp"
diff --git a/src/qmldebug/qqmlprofilerclient_p.h b/src/qmldebug/qqmlprofilerclient_p.h
index a328cad26c..68a32a1a5a 100644
--- a/src/qmldebug/qqmlprofilerclient_p.h
+++ b/src/qmldebug/qqmlprofilerclient_p.h
@@ -41,7 +41,9 @@
#define QQMLPROFILERCLIENT_P_H
#include "qqmldebugclient_p.h"
-#include "qqmleventlocation_p.h"
+#include "qqmlprofilereventlocation_p.h"
+#include "qqmlprofilereventreceiver_p.h"
+
#include <private/qqmlprofilerdefinitions_p.h>
#include <private/qpacket_p.h>
@@ -59,52 +61,42 @@
QT_BEGIN_NAMESPACE
class QQmlProfilerClientPrivate;
-class QQmlProfilerClient : public QQmlDebugClient
+class QQmlProfilerClient : public QQmlDebugClient, public QQmlProfilerDefinitions
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQmlProfilerClient)
+ Q_PROPERTY(bool recording READ isRecording WRITE setRecording NOTIFY recordingChanged)
public:
- QQmlProfilerClient(QQmlDebugConnection *connection);
- void setFeatures(quint64 features);
- void sendRecordingStatus(bool record, int engineId = -1, quint32 flushInterval = 0);
-
-protected:
- QQmlProfilerClient(QQmlProfilerClientPrivate &dd);
-
-private:
- void messageReceived(const QByteArray &message) override;
+ QQmlProfilerClient(QQmlDebugConnection *connection, QQmlProfilerEventReceiver *eventReceiver,
+ quint64 features = std::numeric_limits<quint64>::max());
+ ~QQmlProfilerClient();
- virtual void traceStarted(qint64 time, int engineId);
- virtual void traceFinished(qint64 time, int engineId);
+ bool isRecording() const;
+ void setRecording(bool);
+ quint64 recordedFeatures() const;
+ virtual void messageReceived(const QByteArray &) override;
+ virtual void stateChanged(State status) override;
- virtual void rangeStart(QQmlProfilerDefinitions::RangeType type, qint64 startTime);
- virtual void rangeData(QQmlProfilerDefinitions::RangeType type, qint64 time,
- const QString &data);
- virtual void rangeLocation(QQmlProfilerDefinitions::RangeType type, qint64 time,
- const QQmlEventLocation &location);
- virtual void rangeEnd(QQmlProfilerDefinitions::RangeType type, qint64 endTime);
+ void clearEvents();
+ void clearAll();
- virtual void animationFrame(qint64 time, int frameRate, int animationCount, int threadId);
+ void sendRecordingStatus(int engineId = -1);
+ void setRequestedFeatures(quint64 features);
+ void setFlushInterval(quint32 flushInterval);
- virtual void sceneGraphEvent(QQmlProfilerDefinitions::SceneGraphFrameType type, qint64 time,
- qint64 numericData1, qint64 numericData2, qint64 numericData3,
- qint64 numericData4, qint64 numericData5);
-
- virtual void pixmapCacheEvent(QQmlProfilerDefinitions::PixmapEventType type, qint64 time,
- const QString &url, int numericData1, int numericData2);
-
- virtual void memoryAllocation(QQmlProfilerDefinitions::MemoryType type, qint64 time,
- qint64 amount);
+protected:
+ QQmlProfilerClient(QQmlProfilerClientPrivate &dd);
- virtual void inputEvent(QQmlProfilerDefinitions::InputEventType type, qint64 time, int a,
- int b);
+signals:
+ void complete(qint64 maximumTime);
+ void traceFinished(qint64 timestamp, const QList<int> &engineIds);
+ void traceStarted(qint64 timestamp, const QList<int> &engineIds);
- virtual void complete();
+ void recordingChanged(bool arg);
+ void recordedFeaturesChanged(quint64 features);
- virtual void unknownEvent(QQmlProfilerDefinitions::Message messageType, qint64 time,
- int detailType);
- virtual void unknownData(QPacket &stream);
+ void cleared();
};
QT_END_NAMESPACE
diff --git a/src/qmldebug/qqmlprofilerclient_p_p.h b/src/qmldebug/qqmlprofilerclient_p_p.h
index 9c44113aa8..cf0145409a 100644
--- a/src/qmldebug/qqmlprofilerclient_p_p.h
+++ b/src/qmldebug/qqmlprofilerclient_p_p.h
@@ -40,8 +40,15 @@
#ifndef QQMLPROFILERCLIENT_P_P_H
#define QQMLPROFILERCLIENT_P_P_H
-#include "qqmlprofilerclient_p.h"
#include "qqmldebugclient_p_p.h"
+#include "qqmldebugmessageclient_p.h"
+#include "qqmlenginecontrolclient_p.h"
+#include "qqmlprofilerclient_p.h"
+#include "qqmlprofilertypedevent_p.h"
+
+#include <private/qqmlprofilerdefinitions_p.h>
+
+#include <QtCore/qqueue.h>
//
// W A R N I N G
@@ -56,20 +63,48 @@
QT_BEGIN_NAMESPACE
-struct QQmlProfilerRangeType
-{
- QQmlEventLocation location;
- QString name;
-};
-
-class QQmlProfilerClientPrivate : public QQmlDebugClientPrivate
-{
+class QQmlProfilerClientPrivate : public QQmlDebugClientPrivate, public QQmlProfilerDefinitions {
Q_DECLARE_PUBLIC(QQmlProfilerClient)
public:
- QQmlProfilerClientPrivate(QQmlDebugConnection *connection);
- quint64 features;
+ QQmlProfilerClientPrivate(QQmlDebugConnection *connection,
+ QQmlProfilerEventReceiver *eventReceiver)
+ : QQmlDebugClientPrivate(QLatin1String("CanvasFrameRate"), connection)
+ , eventReceiver(eventReceiver)
+ , engineControl(new QQmlEngineControlClient(connection))
+ , maximumTime(0)
+ , recording(false)
+ , requestedFeatures(0)
+ , recordedFeatures(0)
+ , flushInterval(0)
+ {
+ }
+
+ virtual ~QQmlProfilerClientPrivate() override {}
+
+ void sendRecordingStatus(int engineId);
+ bool updateFeatures(ProfileFeature feature);
+ int resolveType(const QQmlProfilerTypedEvent &type);
+ int resolveStackTop();
+ void forwardEvents(const QQmlProfilerEvent &last);
+ void processCurrentEvent();
+ void finalize();
+
+ QQmlProfilerEventReceiver *eventReceiver;
+ QScopedPointer<QQmlEngineControlClient> engineControl;
+ QScopedPointer<QQmlDebugMessageClient> messageClient;
+ qint64 maximumTime;
+ bool recording;
+ quint64 requestedFeatures;
+ quint64 recordedFeatures;
+ quint32 flushInterval;
- QHash<qint64, QQmlProfilerRangeType> types;
+ // Reuse the same event, so that we don't have to constantly reallocate all the data.
+ QQmlProfilerTypedEvent currentEvent;
+ QHash<QQmlProfilerEventType, int> eventTypeIds;
+ QHash<qint64, int> serverTypeIds;
+ QStack<QQmlProfilerTypedEvent> rangesInProgress;
+ QQueue<QQmlProfilerEvent> pendingMessages;
+ QQueue<QQmlProfilerEvent> pendingDebugMessages;
};
QT_END_NAMESPACE
diff --git a/src/qmldebug/qqmlprofilerevent.cpp b/src/qmldebug/qqmlprofilerevent.cpp
new file mode 100644
index 0000000000..30ae1c79a1
--- /dev/null
+++ b/src/qmldebug/qqmlprofilerevent.cpp
@@ -0,0 +1,273 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmlprofilerevent_p.h"
+#include <QtCore/qdatastream.h>
+
+QT_BEGIN_NAMESPACE
+
+bool operator==(const QQmlProfilerEvent &event1, const QQmlProfilerEvent &event2)
+{
+ if (event1.timestamp() != event2.timestamp() || event1.typeIndex() != event2.typeIndex())
+ return false;
+
+ // This is not particularly efficient, but we also don't need to do this very often.
+ return event1.numbers<QVarLengthArray<qint64>>() == event2.numbers<QVarLengthArray<qint64>>();
+}
+
+bool operator!=(const QQmlProfilerEvent &event1, const QQmlProfilerEvent &event2)
+{
+ return !(event1 == event2);
+}
+
+enum SerializationType {
+ OneByte = 0,
+ TwoByte = 1,
+ FourByte = 2,
+ EightByte = 3,
+ TypeMask = 0x3
+};
+
+enum SerializationTypeOffset {
+ TimestampOffset = 0,
+ TypeIndexOffset = 2,
+ DataLengthOffset = 4,
+ DataOffset = 6
+};
+
+template<typename Number>
+static inline void readNumbers(QDataStream &stream, Number *data, quint16 length)
+{
+ for (quint16 i = 0; i != length; ++i)
+ stream >> data[i];
+}
+
+template<typename Number>
+static inline Number readNumber(QDataStream &stream, qint8 type)
+{
+ switch (type) {
+ case OneByte: {
+ qint8 value;
+ stream >> value;
+ return static_cast<Number>(value);
+ }
+ case TwoByte: {
+ qint16 value;
+ stream >> value;
+ return static_cast<Number>(value);
+ }
+ case FourByte: {
+ qint32 value;
+ stream >> value;
+ return static_cast<Number>(value);
+ }
+ case EightByte: {
+ qint64 value;
+ stream >> value;
+ return static_cast<Number>(value);
+ }
+ default:
+ Q_UNREACHABLE();
+ return 0;
+ }
+}
+
+QDataStream &operator>>(QDataStream &stream, QQmlProfilerEvent &event)
+{
+ qint8 type;
+ stream >> type;
+
+ event.m_timestamp = readNumber<qint64>(stream, (type >> TimestampOffset) & TypeMask);
+ event.m_typeIndex = readNumber<qint32>(stream, (type >> TypeIndexOffset) & TypeMask);
+ event.m_dataLength = readNumber<quint16>(stream, (type >> DataLengthOffset) & TypeMask);
+
+ uint bytesPerNumber = 1 << ((type >> DataOffset) & TypeMask);
+
+ if (event.m_dataLength * bytesPerNumber > sizeof(event.m_data)) {
+ event.m_dataType = static_cast<QQmlProfilerEvent::Type>((bytesPerNumber * 8)
+ | QQmlProfilerEvent::External);
+ event.m_data.external = malloc(event.m_dataLength * bytesPerNumber);
+ } else {
+ event.m_dataType = static_cast<QQmlProfilerEvent::Type>(bytesPerNumber * 8);
+ }
+
+ switch (event.m_dataType) {
+ case QQmlProfilerEvent::Inline8Bit:
+ readNumbers<qint8>(stream, event.m_data.internal8bit, event.m_dataLength);
+ break;
+ case QQmlProfilerEvent::External8Bit:
+ readNumbers<qint8>(stream, static_cast<qint8 *>(event.m_data.external),
+ event.m_dataLength);
+ break;
+ case QQmlProfilerEvent::Inline16Bit:
+ readNumbers<qint16>(stream, event.m_data.internal16bit, event.m_dataLength);
+ break;
+ case QQmlProfilerEvent::External16Bit:
+ readNumbers<qint16>(stream, static_cast<qint16 *>(event.m_data.external),
+ event.m_dataLength);
+ break;
+ case QQmlProfilerEvent::Inline32Bit:
+ readNumbers<qint32>(stream, event.m_data.internal32bit, event.m_dataLength);
+ break;
+ case QQmlProfilerEvent::External32Bit:
+ readNumbers<qint32>(stream, static_cast<qint32 *>(event.m_data.external),
+ event.m_dataLength);
+ break;
+ case QQmlProfilerEvent::Inline64Bit:
+ readNumbers<qint64>(stream, event.m_data.internal64bit, event.m_dataLength);
+ break;
+ case QQmlProfilerEvent::External64Bit:
+ readNumbers<qint64>(stream, static_cast<qint64 *>(event.m_data.external),
+ event.m_dataLength);
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+
+ return stream;
+}
+
+static inline qint8 minimumType(const QQmlProfilerEvent &event, quint16 length,
+ quint16 origBitsPerNumber)
+{
+ qint8 type = OneByte;
+ bool ok = true;
+ for (quint16 i = 0; i < length;) {
+ if ((1 << type) == origBitsPerNumber / 8)
+ return type;
+ switch (type) {
+ case OneByte:
+ ok = (event.number<qint8>(i) == event.number<qint64>(i));
+ break;
+ case TwoByte:
+ ok = (event.number<qint16>(i) == event.number<qint64>(i));
+ break;
+ case FourByte:
+ ok = (event.number<qint32>(i) == event.number<qint64>(i));
+ break;
+ default:
+ // EightByte isn't possible, as (1 << type) == origBitsPerNumber / 8 then.
+ Q_UNREACHABLE();
+ break;
+ }
+
+ if (ok)
+ ++i;
+ else
+ ++type;
+ }
+ return type;
+}
+
+template<typename Number>
+static inline qint8 minimumType(Number number)
+{
+ if (static_cast<qint8>(number) == number)
+ return OneByte;
+ if (static_cast<qint16>(number) == number)
+ return TwoByte;
+ if (static_cast<qint32>(number) == number)
+ return FourByte;
+ return EightByte;
+}
+
+template<typename Number>
+static inline void writeNumbers(QDataStream &stream, const QQmlProfilerEvent &event, quint16 length)
+{
+ for (quint16 i = 0; i != length; ++i)
+ stream << event.number<Number>(i);
+}
+
+template<typename Number>
+static inline void writeNumber(QDataStream &stream, Number number, qint8 type)
+{
+ switch (type) {
+ case OneByte:
+ stream << static_cast<qint8>(number);
+ break;
+ case TwoByte:
+ stream << static_cast<qint16>(number);
+ break;
+ case FourByte:
+ stream << static_cast<qint32>(number);
+ break;
+ case EightByte:
+ stream << static_cast<qint64>(number);
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+}
+
+QDataStream &operator<<(QDataStream &stream, const QQmlProfilerEvent &event)
+{
+ qint8 type = minimumType(event.m_timestamp); // << TimestampOffset;
+ type |= minimumType(event.m_typeIndex) << TypeIndexOffset;
+ type |= minimumType(event.m_dataLength) << DataLengthOffset;
+ type |= minimumType(event, event.m_dataLength, event.m_dataType) << DataOffset;
+ stream << type;
+
+ writeNumber(stream, event.m_timestamp, (type >> TimestampOffset) & TypeMask);
+ writeNumber(stream, event.m_typeIndex, (type >> TypeIndexOffset) & TypeMask);
+ writeNumber(stream, event.m_dataLength, (type >> DataLengthOffset) & TypeMask);
+
+ switch ((type >> DataOffset) & TypeMask) {
+ case OneByte:
+ writeNumbers<qint8>(stream, event, event.m_dataLength);
+ break;
+ case TwoByte:
+ writeNumbers<qint16>(stream, event, event.m_dataLength);
+ break;
+ case FourByte:
+ writeNumbers<qint32>(stream, event, event.m_dataLength);
+ break;
+ case EightByte:
+ writeNumbers<qint64>(stream, event, event.m_dataLength);
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+
+ return stream;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qmldebug/qqmlprofilerevent_p.h b/src/qmldebug/qqmlprofilerevent_p.h
new file mode 100644
index 0000000000..93562302e9
--- /dev/null
+++ b/src/qmldebug/qqmlprofilerevent_p.h
@@ -0,0 +1,355 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLPROFILEREVENT_P_H
+#define QQMLPROFILEREVENT_P_H
+
+#include <private/qqmlprofilerdefinitions_p.h>
+
+#include <QtCore/qstring.h>
+#include <QtCore/qbytearray.h>
+#include <QtCore/qvarlengtharray.h>
+#include <QtCore/qmetatype.h>
+
+#include <initializer_list>
+#include <type_traits>
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+QT_BEGIN_NAMESPACE
+
+struct QQmlProfilerEvent : public QQmlProfilerDefinitions {
+ QQmlProfilerEvent() :
+ m_timestamp(-1), m_typeIndex(-1), m_dataType(Inline8Bit), m_dataLength(0)
+ {}
+
+ template<typename Number>
+ QQmlProfilerEvent(qint64 timestamp, int typeIndex, std::initializer_list<Number> list)
+ : m_timestamp(timestamp), m_typeIndex(typeIndex)
+ {
+ assignNumbers<std::initializer_list<Number>, Number>(list);
+ }
+
+ QQmlProfilerEvent(qint64 timestamp, int typeIndex, const QString &data)
+ : m_timestamp(timestamp), m_typeIndex(typeIndex)
+ {
+ assignNumbers<QByteArray, qint8>(data.toUtf8());
+ }
+
+ template<typename Number>
+ QQmlProfilerEvent(qint64 timestamp, int typeIndex, const QVector<Number> &data)
+ : m_timestamp(timestamp), m_typeIndex(typeIndex)
+ {
+ assignNumbers<QVector<Number>, Number>(data);
+ }
+
+ QQmlProfilerEvent(const QQmlProfilerEvent &other)
+ : m_timestamp(other.m_timestamp), m_typeIndex(other.m_typeIndex),
+ m_dataType(other.m_dataType), m_dataLength(other.m_dataLength)
+ {
+ assignData(other);
+ }
+
+ QQmlProfilerEvent(QQmlProfilerEvent &&other)
+ {
+ memcpy(this, &other, sizeof(QQmlProfilerEvent));
+ other.m_dataType = Inline8Bit; // prevent dtor from deleting the pointer
+ }
+
+ QQmlProfilerEvent &operator=(const QQmlProfilerEvent &other)
+ {
+ if (this != &other) {
+ clearPointer();
+ m_timestamp = other.m_timestamp;
+ m_typeIndex = other.m_typeIndex;
+ m_dataType = other.m_dataType;
+ m_dataLength = other.m_dataLength;
+ assignData(other);
+ }
+ return *this;
+ }
+
+ QQmlProfilerEvent &operator=(QQmlProfilerEvent &&other)
+ {
+ if (this != &other) {
+ memcpy(this, &other, sizeof(QQmlProfilerEvent));
+ other.m_dataType = Inline8Bit;
+ }
+ return *this;
+ }
+
+ ~QQmlProfilerEvent()
+ {
+ clearPointer();
+ }
+
+ qint64 timestamp() const { return m_timestamp; }
+ void setTimestamp(qint64 timestamp) { m_timestamp = timestamp; }
+
+ int typeIndex() const { return m_typeIndex; }
+ void setTypeIndex(int typeIndex) { m_typeIndex = typeIndex; }
+
+ template<typename Number>
+ Number number(int i) const
+ {
+ // Trailing zeroes can be omitted, for example for SceneGraph events
+ if (i >= m_dataLength)
+ return 0;
+ switch (m_dataType) {
+ case Inline8Bit:
+ return m_data.internal8bit[i];
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_GCC("-Warray-bounds") // Mingw 5.3 gcc doesn't get the type/length logic.
+ case Inline16Bit:
+ return m_data.internal16bit[i];
+ case Inline32Bit:
+ return m_data.internal32bit[i];
+ case Inline64Bit:
+ return m_data.internal64bit[i];
+QT_WARNING_POP
+ case External8Bit:
+ return static_cast<const qint8 *>(m_data.external)[i];
+ case External16Bit:
+ return static_cast<const qint16 *>(m_data.external)[i];
+ case External32Bit:
+ return static_cast<const qint32 *>(m_data.external)[i];
+ case External64Bit:
+ return static_cast<const qint64 *>(m_data.external)[i];
+ default:
+ return 0;
+ }
+ }
+
+ template<typename Number>
+ void setNumber(int i, Number number)
+ {
+ QVarLengthArray<Number> nums = numbers<QVarLengthArray<Number>, Number>();
+ int prevSize = nums.size();
+ if (i >= prevSize) {
+ nums.resize(i + 1);
+ // Fill with zeroes. We don't want to accidentally prevent squeezing.
+ while (prevSize < i)
+ nums[prevSize++] = 0;
+ }
+ nums[i] = number;
+ setNumbers<QVarLengthArray<Number>, Number>(nums);
+ }
+
+ template<typename Container, typename Number>
+ void setNumbers(const Container &numbers)
+ {
+ clearPointer();
+ assignNumbers<Container, Number>(numbers);
+ }
+
+ template<typename Number>
+ void setNumbers(std::initializer_list<Number> numbers)
+ {
+ setNumbers<std::initializer_list<Number>, Number>(numbers);
+ }
+
+ template<typename Container, typename Number = qint64>
+ Container numbers() const
+ {
+ Container container;
+ for (int i = 0; i < m_dataLength; ++i)
+ container.append(number<Number>(i));
+ return container;
+ }
+
+ QString string() const
+ {
+ switch (m_dataType) {
+ case External8Bit:
+ return QString::fromUtf8(static_cast<const char *>(m_data.external), m_dataLength);
+ case Inline8Bit:
+ return QString::fromUtf8(m_data.internalChar, m_dataLength);
+ default:
+ Q_UNREACHABLE();
+ return QString();
+ }
+ }
+
+ void setString(const QString &data)
+ {
+ clearPointer();
+ assignNumbers<QByteArray, char>(data.toUtf8());
+ }
+
+ Message rangeStage() const
+ {
+ Q_ASSERT(m_dataType == Inline8Bit);
+ return static_cast<Message>(m_data.internal8bit[0]);
+ }
+
+ void setRangeStage(Message stage)
+ {
+ clearPointer();
+ m_dataType = Inline8Bit;
+ m_dataLength = 1;
+ m_data.internal8bit[0] = stage;
+ }
+
+ bool isValid() const
+ {
+ return m_timestamp != -1;
+ }
+
+private:
+ enum Type: quint16 {
+ External = 1,
+ Inline8Bit = 8,
+ External8Bit = Inline8Bit | External,
+ Inline16Bit = 16,
+ External16Bit = Inline16Bit | External,
+ Inline32Bit = 32,
+ External32Bit = Inline32Bit | External,
+ Inline64Bit = 64,
+ External64Bit = Inline64Bit | External
+ };
+
+ qint64 m_timestamp;
+
+ static const int s_internalDataLength = 8;
+ union {
+ void *external;
+ char internalChar [s_internalDataLength];
+ qint8 internal8bit [s_internalDataLength];
+ qint16 internal16bit[s_internalDataLength / 2];
+ qint32 internal32bit[s_internalDataLength / 4];
+ qint64 internal64bit[s_internalDataLength / 8];
+ } m_data;
+
+ qint32 m_typeIndex;
+ Type m_dataType;
+ quint16 m_dataLength;
+
+ void assignData(const QQmlProfilerEvent &other)
+ {
+ if (m_dataType & External) {
+ uint length = m_dataLength * (other.m_dataType / 8);
+ m_data.external = malloc(length);
+ memcpy(m_data.external, other.m_data.external, length);
+ } else {
+ memcpy(&m_data, &other.m_data, sizeof(m_data));
+ }
+ }
+
+ template<typename Big, typename Small>
+ bool squeezable(Big source)
+ {
+ return static_cast<Small>(source) == source;
+ }
+
+ template<typename Container, typename Number>
+ typename std::enable_if<(sizeof(Number) > 1), bool>::type
+ squeeze(const Container &numbers)
+ {
+ typedef typename QIntegerForSize<sizeof(Number) / 2>::Signed Small;
+ foreach (Number item, numbers) {
+ if (!squeezable<Number, Small>(item))
+ return false;
+ }
+ assignNumbers<Container, Small>(numbers);
+ return true;
+ }
+
+ template<typename Container, typename Number>
+ typename std::enable_if<(sizeof(Number) <= 1), bool>::type
+ squeeze(const Container &)
+ {
+ return false;
+ }
+
+ template<typename Container, typename Number>
+ void assignNumbers(const Container &numbers)
+ {
+ Number *data;
+ m_dataLength = squeezable<size_t, quint16>(static_cast<size_t>(numbers.size())) ?
+ static_cast<quint16>(numbers.size()) : std::numeric_limits<quint16>::max();
+ if (m_dataLength > sizeof(m_data) / sizeof(Number)) {
+ if (squeeze<Container, Number>(numbers))
+ return;
+ m_dataType = static_cast<Type>((sizeof(Number) * 8) | External);
+ m_data.external = malloc(m_dataLength * sizeof(Number));
+ data = static_cast<Number *>(m_data.external);
+ } else {
+ m_dataType = static_cast<Type>(sizeof(Number) * 8);
+ data = static_cast<Number *>(m_dataType & External ? m_data.external : &m_data);
+ }
+ quint16 i = 0;
+ for (Number item : numbers) {
+ if (i >= m_dataLength)
+ break;
+ data[i++] = item;
+ }
+ }
+
+ void clearPointer()
+ {
+ if (m_dataType & External)
+ free(m_data.external);
+ }
+
+ friend QDataStream &operator>>(QDataStream &stream, QQmlProfilerEvent &event);
+ friend QDataStream &operator<<(QDataStream &stream, const QQmlProfilerEvent &event);
+};
+
+bool operator==(const QQmlProfilerEvent &event1, const QQmlProfilerEvent &event2);
+bool operator!=(const QQmlProfilerEvent &event1, const QQmlProfilerEvent &event2);
+
+QDataStream &operator>>(QDataStream &stream, QQmlProfilerEvent &event);
+QDataStream &operator<<(QDataStream &stream, const QQmlProfilerEvent &event);
+
+Q_DECLARE_TYPEINFO(QQmlProfilerEvent, Q_MOVABLE_TYPE);
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(QQmlProfilerEvent)
+
+#endif // QQMLPROFILEREVENT_P_H
diff --git a/src/qmldebug/qqmlprofilereventlocation.cpp b/src/qmldebug/qqmlprofilereventlocation.cpp
new file mode 100644
index 0000000000..8be44c17a3
--- /dev/null
+++ b/src/qmldebug/qqmlprofilereventlocation.cpp
@@ -0,0 +1,56 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmlprofilereventlocation_p.h"
+
+#include <QtCore/qdatastream.h>
+
+QT_BEGIN_NAMESPACE
+
+QDataStream &operator>>(QDataStream &stream, QQmlProfilerEventLocation &location)
+{
+ return stream >> location.m_filename >> location.m_line >> location.m_column;
+}
+
+QDataStream &operator<<(QDataStream &stream, const QQmlProfilerEventLocation &location)
+{
+ return stream << location.m_filename << location.m_line << location.m_column;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qmldebug/qqmlprofilereventlocation_p.h b/src/qmldebug/qqmlprofilereventlocation_p.h
new file mode 100644
index 0000000000..6f37eab14b
--- /dev/null
+++ b/src/qmldebug/qqmlprofilereventlocation_p.h
@@ -0,0 +1,121 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLPROFILEREVENTLOCATION_P_H
+#define QQMLPROFILEREVENTLOCATION_P_H
+
+#include <QtCore/qstring.h>
+#include <QtCore/qhash.h>
+#include <QtCore/qdatastream.h>
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+QT_BEGIN_NAMESPACE
+
+class QQmlProfilerEventLocation
+{
+public:
+ QQmlProfilerEventLocation() : m_line(-1),m_column(-1) {}
+ QQmlProfilerEventLocation(const QString &file, int lineNumber, int columnNumber) :
+ m_filename(file), m_line(lineNumber), m_column(columnNumber)
+ {}
+
+ void clear()
+ {
+ m_filename.clear();
+ m_line = m_column = -1;
+ }
+
+ bool isValid() const
+ {
+ return !m_filename.isEmpty();
+ }
+
+ QString filename() const { return m_filename; }
+ int line() const { return m_line; }
+ int column() const { return m_column; }
+
+private:
+ friend QDataStream &operator>>(QDataStream &stream, QQmlProfilerEventLocation &location);
+ friend QDataStream &operator<<(QDataStream &stream, const QQmlProfilerEventLocation &location);
+
+ QString m_filename;
+ int m_line;
+ int m_column;
+};
+
+inline bool operator==(const QQmlProfilerEventLocation &location1,
+ const QQmlProfilerEventLocation &location2)
+{
+ // compare filename last as it's expensive.
+ return location1.line() == location2.line() && location1.column() == location2.column()
+ && location1.filename() == location2.filename();
+}
+
+inline bool operator!=(const QQmlProfilerEventLocation &location1,
+ const QQmlProfilerEventLocation &location2)
+{
+ return !(location1 == location2);
+}
+
+inline uint qHash(const QQmlProfilerEventLocation &location)
+{
+ return qHash(location.filename())
+ ^ ((location.line() & 0xfff) // 12 bits of line number
+ | ((location.column() << 16) & 0xff0000)); // 8 bits of column
+
+}
+
+QDataStream &operator>>(QDataStream &stream, QQmlProfilerEventLocation &location);
+QDataStream &operator<<(QDataStream &stream, const QQmlProfilerEventLocation &location);
+
+Q_DECLARE_TYPEINFO(QQmlProfilerEventLocation, Q_MOVABLE_TYPE);
+
+QT_END_NAMESPACE
+
+#endif // QQMLPROFILEREVENTLOCATION_P_H
diff --git a/src/qmldebug/qqmlprofilereventreceiver_p.h b/src/qmldebug/qqmlprofilereventreceiver_p.h
new file mode 100644
index 0000000000..ee8e592858
--- /dev/null
+++ b/src/qmldebug/qqmlprofilereventreceiver_p.h
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLPROFILEREVENTRECEIVER_P_H
+#define QQMLPROFILEREVENTRECEIVER_P_H
+
+#include "qqmlprofilerevent_p.h"
+#include "qqmlprofilereventtype_p.h"
+
+#include <QtCore/qobject.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlProfilerEventReceiver : public QObject
+{
+ Q_OBJECT
+public:
+ QQmlProfilerEventReceiver(QObject *parent = nullptr) : QObject(parent) {}
+
+ virtual int numLoadedEventTypes() const = 0;
+ virtual void addEventType(const QQmlProfilerEventType &type) = 0;
+ virtual void addEvent(const QQmlProfilerEvent &event) = 0;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLPROFILEREVENTRECEIVER_P_H
diff --git a/src/qmldebug/qqmlprofilereventtype.cpp b/src/qmldebug/qqmlprofilereventtype.cpp
new file mode 100644
index 0000000000..b0aad3fc5b
--- /dev/null
+++ b/src/qmldebug/qqmlprofilereventtype.cpp
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmlprofilereventtype_p.h"
+
+#include <QtCore/qdatastream.h>
+
+QT_BEGIN_NAMESPACE
+
+QDataStream &operator>>(QDataStream &stream, QQmlProfilerEventType &type)
+{
+ quint8 message;
+ quint8 rangeType;
+ stream >> type.m_displayName >> type.m_data >> type.m_location >> message >> rangeType
+ >> type.m_detailType;
+ type.m_message = static_cast<QQmlProfilerDefinitions::Message>(message);
+ type.m_rangeType = static_cast<QQmlProfilerDefinitions::RangeType>(rangeType);
+ return stream;
+}
+
+QDataStream &operator<<(QDataStream &stream, const QQmlProfilerEventType &type)
+{
+ return stream << type.m_displayName << type.m_data << type.m_location
+ << static_cast<quint8>(type.m_message) << static_cast<quint8>(type.m_rangeType)
+ << type.m_detailType;
+}
+
+QQmlProfilerDefinitions::ProfileFeature QQmlProfilerEventType::feature() const
+{
+ switch (m_message) {
+ case Event: {
+ switch (m_detailType) {
+ case Mouse:
+ case Key:
+ return ProfileInputEvents;
+ case AnimationFrame:
+ return ProfileAnimations;
+ default:
+ return MaximumProfileFeature;
+ }
+ }
+ case PixmapCacheEvent:
+ return ProfilePixmapCache;
+ case SceneGraphFrame:
+ return ProfileSceneGraph;
+ case MemoryAllocation:
+ return ProfileMemory;
+ case DebugMessage:
+ return ProfileDebugMessages;
+ default:
+ return featureFromRangeType(m_rangeType);
+ }
+}
+
+
+QT_END_NAMESPACE
diff --git a/src/qmldebug/qqmlprofilereventtype_p.h b/src/qmldebug/qqmlprofilereventtype_p.h
new file mode 100644
index 0000000000..7bd67f9ca8
--- /dev/null
+++ b/src/qmldebug/qqmlprofilereventtype_p.h
@@ -0,0 +1,126 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLPROFILEREVENTTYPE_P_H
+#define QQMLPROFILEREVENTTYPE_P_H
+
+#include "qqmlprofilereventlocation_p.h"
+
+#include <private/qqmlprofilerdefinitions_p.h>
+
+#include <QtCore/qstring.h>
+#include <QtCore/qmetatype.h>
+#include <QtCore/qhash.h>
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+QT_BEGIN_NAMESPACE
+
+class QQmlProfilerEventType : public QQmlProfilerDefinitions {
+public:
+ QQmlProfilerEventType(Message message = MaximumMessage, RangeType rangeType = MaximumRangeType,
+ int detailType = -1,
+ const QQmlProfilerEventLocation &location = QQmlProfilerEventLocation(),
+ const QString &data = QString(), const QString displayName = QString()) :
+ m_displayName(displayName), m_data(data), m_location(location), m_message(message),
+ m_rangeType(rangeType), m_detailType(detailType)
+ {}
+
+ void setDisplayName(const QString &displayName) { m_displayName = displayName; }
+ void setData(const QString &data) { m_data = data; }
+ void setLocation(const QQmlProfilerEventLocation &location) { m_location = location; }
+
+ ProfileFeature feature() const;
+ QString displayName() const { return m_displayName; }
+ QString data() const { return m_data; }
+ QQmlProfilerEventLocation location() const { return m_location; }
+ Message message() const { return m_message; }
+ RangeType rangeType() const { return m_rangeType; }
+ int detailType() const { return m_detailType; }
+
+private:
+ friend QDataStream &operator>>(QDataStream &stream, QQmlProfilerEventType &type);
+ friend QDataStream &operator<<(QDataStream &stream, const QQmlProfilerEventType &type);
+
+ QString m_displayName;
+ QString m_data;
+ QQmlProfilerEventLocation m_location;
+ Message m_message;
+ RangeType m_rangeType;
+ int m_detailType; // can be EventType, BindingType, PixmapEventType or SceneGraphFrameType
+};
+
+QDataStream &operator>>(QDataStream &stream, QQmlProfilerEventType &type);
+QDataStream &operator<<(QDataStream &stream, const QQmlProfilerEventType &type);
+
+inline uint qHash(const QQmlProfilerEventType &type)
+{
+ return qHash(type.location())
+ ^ (((type.message() << 12) & 0xf000) // 4 bits message
+ | ((type.rangeType() << 24) & 0xf000000) // 4 bits rangeType
+ | ((static_cast<uint>(type.detailType()) << 28) & 0xf0000000)); // 4 bits detailType
+}
+
+inline bool operator==(const QQmlProfilerEventType &type1, const QQmlProfilerEventType &type2)
+{
+ return type1.message() == type2.message() && type1.rangeType() == type2.rangeType()
+ && type1.detailType() == type2.detailType() && type1.location() == type2.location();
+}
+
+inline bool operator!=(const QQmlProfilerEventType &type1, const QQmlProfilerEventType &type2)
+{
+ return !(type1 == type2);
+}
+
+Q_DECLARE_TYPEINFO(QQmlProfilerEventType, Q_MOVABLE_TYPE);
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(QQmlProfilerEventType)
+
+#endif // QQMLPROFILEREVENTTYPE_P_H
diff --git a/src/qmldebug/qqmlprofilertypedevent.cpp b/src/qmldebug/qqmlprofilertypedevent.cpp
new file mode 100644
index 0000000000..9b00481e17
--- /dev/null
+++ b/src/qmldebug/qqmlprofilertypedevent.cpp
@@ -0,0 +1,226 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmlprofilertypedevent_p.h"
+#include <QtCore/qvarlengtharray.h>
+
+QT_BEGIN_NAMESPACE
+
+QDataStream &operator>>(QDataStream &stream, QQmlProfilerTypedEvent &event)
+{
+ qint64 time;
+ qint32 messageType;
+ qint32 subtype;
+
+ stream >> time >> messageType;
+
+ if (messageType < 0 || messageType > QQmlProfilerDefinitions::MaximumMessage)
+ messageType = QQmlProfilerDefinitions::MaximumMessage;
+
+ QQmlProfilerDefinitions::RangeType rangeType = QQmlProfilerDefinitions::MaximumRangeType;
+ if (!stream.atEnd()) {
+ stream >> subtype;
+ rangeType = static_cast<QQmlProfilerDefinitions::RangeType>(subtype);
+ if (rangeType < 0 || rangeType > QQmlProfilerDefinitions::MaximumRangeType)
+ rangeType = QQmlProfilerDefinitions::MaximumRangeType;
+ } else {
+ subtype = -1;
+ }
+
+ event.event.setTimestamp(time > 0 ? time : 0);
+ event.event.setTypeIndex(-1);
+ event.serverTypeId = 0;
+
+ switch (messageType) {
+ case QQmlProfilerDefinitions::Event: {
+ event.type = QQmlProfilerEventType(
+ static_cast<QQmlProfilerDefinitions::Message>(messageType),
+ QQmlProfilerDefinitions::MaximumRangeType, subtype);
+ switch (subtype) {
+ case QQmlProfilerDefinitions::StartTrace:
+ case QQmlProfilerDefinitions::EndTrace: {
+ QVarLengthArray<qint32> engineIds;
+ while (!stream.atEnd()) {
+ qint32 id;
+ stream >> id;
+ engineIds << id;
+ }
+ event.event.setNumbers<QVarLengthArray<qint32>, qint32>(engineIds);
+ break;
+ }
+ case QQmlProfilerDefinitions::AnimationFrame: {
+ qint32 frameRate, animationCount;
+ qint32 threadId;
+ stream >> frameRate >> animationCount;
+ if (!stream.atEnd())
+ stream >> threadId;
+ else
+ threadId = 0;
+
+ event.event.setNumbers<qint32>({frameRate, animationCount, threadId});
+ break;
+ }
+ case QQmlProfilerDefinitions::Mouse:
+ case QQmlProfilerDefinitions::Key:
+ int inputType = (subtype == QQmlProfilerDefinitions::Key
+ ? QQmlProfilerDefinitions::InputKeyUnknown
+ : QQmlProfilerDefinitions::InputMouseUnknown);
+ if (!stream.atEnd())
+ stream >> inputType;
+ qint32 a = -1;
+ if (!stream.atEnd())
+ stream >> a;
+ qint32 b = -1;
+ if (!stream.atEnd())
+ stream >> b;
+
+ event.event.setNumbers<qint32>({inputType, a, b});
+ break;
+ }
+
+ break;
+ }
+ case QQmlProfilerDefinitions::Complete: {
+ event.type = QQmlProfilerEventType(
+ static_cast<QQmlProfilerDefinitions::Message>(messageType),
+ QQmlProfilerDefinitions::MaximumRangeType, subtype);
+ break;
+ }
+ case QQmlProfilerDefinitions::SceneGraphFrame: {
+ QVarLengthArray<qint64> params;
+ qint64 param;
+
+ while (!stream.atEnd()) {
+ stream >> param;
+ params.push_back(param);
+ }
+
+ event.type = QQmlProfilerEventType(
+ static_cast<QQmlProfilerDefinitions::Message>(messageType),
+ QQmlProfilerDefinitions::MaximumRangeType, subtype);
+ event.event.setNumbers<QVarLengthArray<qint64>, qint64>(params);
+ break;
+ }
+ case QQmlProfilerDefinitions::PixmapCacheEvent: {
+ qint32 width = 0, height = 0, refcount = 0;
+ QString filename;
+ stream >> filename;
+ if (subtype == QQmlProfilerDefinitions::PixmapReferenceCountChanged
+ || subtype == QQmlProfilerDefinitions::PixmapCacheCountChanged) {
+ stream >> refcount;
+ } else if (subtype == QQmlProfilerDefinitions::PixmapSizeKnown) {
+ stream >> width >> height;
+ refcount = 1;
+ }
+
+ event.type = QQmlProfilerEventType(
+ static_cast<QQmlProfilerDefinitions::Message>(messageType),
+ QQmlProfilerDefinitions::MaximumRangeType, subtype,
+ QQmlProfilerEventLocation(filename, 0, 0));
+ event.event.setNumbers<qint32>({width, height, refcount});
+ break;
+ }
+ case QQmlProfilerDefinitions::MemoryAllocation: {
+ qint64 delta;
+ stream >> delta;
+
+ event.type = QQmlProfilerEventType(
+ static_cast<QQmlProfilerDefinitions::Message>(messageType),
+ QQmlProfilerDefinitions::MaximumRangeType, subtype);
+ event.event.setNumbers<qint64>({delta});
+ break;
+ }
+ case QQmlProfilerDefinitions::RangeStart: {
+ if (!stream.atEnd()) {
+ qint64 typeId;
+ stream >> typeId;
+ if (stream.status() == QDataStream::Ok)
+ event.serverTypeId = typeId;
+ // otherwise it's the old binding type of 4 bytes
+ }
+
+ event.type = QQmlProfilerEventType(QQmlProfilerDefinitions::MaximumMessage, rangeType, -1);
+ event.event.setRangeStage(QQmlProfilerDefinitions::RangeStart);
+ break;
+ }
+ case QQmlProfilerDefinitions::RangeData: {
+ QString data;
+ stream >> data;
+
+ event.type = QQmlProfilerEventType(QQmlProfilerDefinitions::MaximumMessage, rangeType, -1,
+ QQmlProfilerEventLocation(), data);
+ event.event.setRangeStage(QQmlProfilerDefinitions::RangeData);
+ if (!stream.atEnd())
+ stream >> event.serverTypeId;
+ break;
+ }
+ case QQmlProfilerDefinitions::RangeLocation: {
+ QString filename;
+ qint32 line = 0;
+ qint32 column = 0;
+ stream >> filename >> line;
+
+ if (!stream.atEnd()) {
+ stream >> column;
+ if (!stream.atEnd())
+ stream >> event.serverTypeId;
+ }
+
+ event.type = QQmlProfilerEventType(QQmlProfilerDefinitions::MaximumMessage, rangeType, -1,
+ QQmlProfilerEventLocation(filename, line, column));
+ event.event.setRangeStage(QQmlProfilerDefinitions::RangeLocation);
+ break;
+ }
+ case QQmlProfilerDefinitions::RangeEnd: {
+ event.type = QQmlProfilerEventType(QQmlProfilerDefinitions::MaximumMessage, rangeType, -1);
+ event.event.setRangeStage(QQmlProfilerDefinitions::RangeEnd);
+ break;
+ }
+ default:
+ event.event.setNumbers<char>({});
+ event.type = QQmlProfilerEventType(
+ static_cast<QQmlProfilerDefinitions::Message>(messageType),
+ QQmlProfilerDefinitions::MaximumRangeType, subtype);
+ break;
+ }
+
+ return stream;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qmldebug/qqmleventlocation_p.h b/src/qmldebug/qqmlprofilertypedevent_p.h
index c3a2b93f0f..e7e947f2ef 100644
--- a/src/qmldebug/qqmleventlocation_p.h
+++ b/src/qmldebug/qqmlprofilertypedevent_p.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQml module of the Qt Toolkit.
@@ -37,10 +37,13 @@
**
****************************************************************************/
-#ifndef QQMLEVENTLOCATION_P_H
-#define QQMLEVENTLOCATION_P_H
+#ifndef QQMLPROFILERTYPEDEVENT_P_H
+#define QQMLPROFILERTYPEDEVENT_P_H
-#include <QtCore/qstring.h>
+#include "qqmlprofilerevent_p.h"
+#include "qqmlprofilereventtype_p.h"
+
+#include <QtCore/qdatastream.h>
//
// W A R N I N G
@@ -55,19 +58,19 @@
QT_BEGIN_NAMESPACE
-struct QQmlEventLocation
+struct QQmlProfilerTypedEvent
{
- QQmlEventLocation() : line(-1), column(-1) {}
- QQmlEventLocation(const QString &file, int lineNumber, int columnNumber) :
- filename(file), line(lineNumber), column(columnNumber) {}
-
- QString filename;
- int line;
- int column;
+ QQmlProfilerEvent event;
+ QQmlProfilerEventType type;
+ qint64 serverTypeId = 0;
};
-Q_DECLARE_TYPEINFO(QQmlEventLocation, Q_MOVABLE_TYPE);
+QDataStream &operator>>(QDataStream &stream, QQmlProfilerTypedEvent &event);
+
+Q_DECLARE_TYPEINFO(QQmlProfilerTypedEvent, Q_MOVABLE_TYPE);
QT_END_NAMESPACE
-#endif // QQMLEVENTLOCATION_P_H
+Q_DECLARE_METATYPE(QQmlProfilerTypedEvent)
+
+#endif // QQMLPROFILERTYPEDEVENT_P_H
diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/data/controlFromJS.qml b/tests/auto/qml/debugger/qqmlprofilerservice/data/controlFromJS.qml
index dd7cb2055d..4235a2d55f 100644
--- a/tests/auto/qml/debugger/qqmlprofilerservice/data/controlFromJS.qml
+++ b/tests/auto/qml/debugger/qqmlprofilerservice/data/controlFromJS.qml
@@ -43,13 +43,6 @@ QtObject {
interval: 1000
onTriggered: {
console.profileEnd();
- endTimer.start();
}
}
-
- property var endTimer: Timer {
- id: endTimer
- interval: 1000
- onTriggered: Qt.quit();
- }
}
diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/data/exit.qml b/tests/auto/qml/debugger/qqmlprofilerservice/data/exit.qml
index 4236d70ea3..3b28e65174 100644
--- a/tests/auto/qml/debugger/qqmlprofilerservice/data/exit.qml
+++ b/tests/auto/qml/debugger/qqmlprofilerservice/data/exit.qml
@@ -1,7 +1,7 @@
import QtQml 2.0
QtObject {
- Timer {
+ property Timer timer: Timer {
running: true
interval: 1
onTriggered: Qt.quit();
diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp b/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp
index 43ab3e1171..c32fa7ea7d 100644
--- a/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp
+++ b/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp
@@ -40,223 +40,134 @@
#include <QtGui/private/qguiapplication_p.h>
#include <QtGui/qpa/qplatformintegration.h>
-struct QQmlProfilerData
-{
- QQmlProfilerData(qint64 time = -2, int messageType = -1, int detailType = -1,
- const QString &detailData = QString()) :
- time(time), messageType(messageType), detailType(detailType), detailData(detailData),
- line(-1), column(-1), framerate(-1), animationcount(-1), amount(-1)
- {}
-
- qint64 time;
- int messageType;
- int detailType;
-
- //###
- QString detailData; //used by RangeData and RangeLocation
- int line; //used by RangeLocation
- int column; //used by RangeLocation
- int framerate; //used by animation events
- int animationcount; //used by animation events
- qint64 amount; //used by heap events
-};
-
-class QQmlProfilerTestClient : public QQmlProfilerClient
+class QQmlProfilerTestClient : public QQmlProfilerEventReceiver, public QQmlProfilerDefinitions
{
Q_OBJECT
public:
- QQmlProfilerTestClient(QQmlDebugConnection *connection) : QQmlProfilerClient(connection),
- lastTimestamp(-1) {}
+ QQmlProfilerTestClient(QQmlDebugConnection *connection) :
+ client(new QQmlProfilerClient(connection, this))
+ {
+ connect(client.data(), &QQmlProfilerClient::traceStarted,
+ this, &QQmlProfilerTestClient::startTrace);
+ connect(client.data(), &QQmlProfilerClient::traceFinished,
+ this, &QQmlProfilerTestClient::endTrace);
+ }
- QVector<QQmlProfilerData> qmlMessages;
- QVector<QQmlProfilerData> javascriptMessages;
- QVector<QQmlProfilerData> jsHeapMessages;
- QVector<QQmlProfilerData> asynchronousMessages;
- QVector<QQmlProfilerData> pixmapMessages;
+ void startTrace(qint64 timestamp, const QList<int> &engineIds);
+ void endTrace(qint64 timestamp, const QList<int> &engineIds);
- qint64 lastTimestamp;
+ QPointer<QQmlProfilerClient> client; // Owned by QQmlDebugTest
+ QVector<QQmlProfilerEventType> types;
-signals:
- void recordingFinished();
+ QVector<QQmlProfilerEvent> qmlMessages;
+ QVector<QQmlProfilerEvent> javascriptMessages;
+ QVector<QQmlProfilerEvent> jsHeapMessages;
+ QVector<QQmlProfilerEvent> asynchronousMessages;
+ QVector<QQmlProfilerEvent> pixmapMessages;
-private:
- void traceStarted(qint64 time, int engineId);
- void traceFinished(qint64 time, int engineId);
- void rangeStart(QQmlProfilerDefinitions::RangeType type, qint64 startTime);
- void rangeData(QQmlProfilerDefinitions::RangeType type, qint64 time, const QString &data);
- void rangeLocation(QQmlProfilerDefinitions::RangeType type, qint64 time,
- const QQmlEventLocation &location);
- void rangeEnd(QQmlProfilerDefinitions::RangeType type, qint64 endTime);
- void animationFrame(qint64 time, int frameRate, int animationCount, int threadId);
- void sceneGraphEvent(QQmlProfilerDefinitions::SceneGraphFrameType type, qint64 time,
- qint64 numericData1, qint64 numericData2, qint64 numericData3,
- qint64 numericData4, qint64 numericData5);
- void pixmapCacheEvent(QQmlProfilerDefinitions::PixmapEventType type, qint64 time,
- const QString &url, int numericData1, int numericData2);
- void memoryAllocation(QQmlProfilerDefinitions::MemoryType type, qint64 time, qint64 amount);
- void inputEvent(QQmlProfilerDefinitions::InputEventType type, qint64 time, int a, int b);
- void complete();
-
- void unknownEvent(QQmlProfilerDefinitions::Message messageType, qint64 time, int detailType);
- void unknownData(QPacket &stream);
+ int numLoadedEventTypes() const override;
+ void addEventType(const QQmlProfilerEventType &type) override;
+ void addEvent(const QQmlProfilerEvent &event) override;
};
-void QQmlProfilerTestClient::traceStarted(qint64 time, int engineId)
-{
- asynchronousMessages.append(QQmlProfilerData(time, QQmlProfilerDefinitions::Event,
- QQmlProfilerDefinitions::StartTrace,
- QString::number(engineId)));
-}
-
-void QQmlProfilerTestClient::traceFinished(qint64 time, int engineId)
+void QQmlProfilerTestClient::startTrace(qint64 timestamp, const QList<int> &engineIds)
{
- asynchronousMessages.append(QQmlProfilerData(time, QQmlProfilerDefinitions::Event,
- QQmlProfilerDefinitions::EndTrace,
- QString::number(engineId)));
+ types.append(QQmlProfilerEventType(Event, MaximumRangeType, StartTrace));
+ asynchronousMessages.append(QQmlProfilerEvent(timestamp, types.length() - 1,
+ engineIds.toVector()));
}
-void QQmlProfilerTestClient::rangeStart(QQmlProfilerDefinitions::RangeType type, qint64 startTime)
+void QQmlProfilerTestClient::endTrace(qint64 timestamp, const QList<int> &engineIds)
{
- QVERIFY(type >= 0 && type < QQmlProfilerDefinitions::MaximumRangeType);
- QVERIFY(lastTimestamp <= startTime);
- lastTimestamp = startTime;
- QQmlProfilerData data(startTime, QQmlProfilerDefinitions::RangeStart, type);
- if (type == QQmlProfilerDefinitions::Javascript)
- javascriptMessages.append(data);
- else
- qmlMessages.append(data);
+ types.append(QQmlProfilerEventType(Event, MaximumRangeType, EndTrace));
+ asynchronousMessages.append(QQmlProfilerEvent(timestamp, types.length() - 1,
+ engineIds.toVector()));
}
-void QQmlProfilerTestClient::rangeData(QQmlProfilerDefinitions::RangeType type, qint64 time,
- const QString &string)
+int QQmlProfilerTestClient::numLoadedEventTypes() const
{
- QVERIFY(type >= 0 && type < QQmlProfilerDefinitions::MaximumRangeType);
- QVERIFY(lastTimestamp <= time);
- lastTimestamp = time;
- QQmlProfilerData data(time, QQmlProfilerDefinitions::RangeData, type, string);
- if (type == QQmlProfilerDefinitions::Javascript)
- javascriptMessages.append(data);
- else
- qmlMessages.append(data);
+ return types.length();
}
-void QQmlProfilerTestClient::rangeLocation(QQmlProfilerDefinitions::RangeType type, qint64 time,
- const QQmlEventLocation &location)
+void QQmlProfilerTestClient::addEventType(const QQmlProfilerEventType &type)
{
- QVERIFY(type >= 0 && type < QQmlProfilerDefinitions::MaximumRangeType);
- QVERIFY(location.line >= -2);
- QVERIFY(lastTimestamp <= time);
- lastTimestamp = time;
- QQmlProfilerData data(time, QQmlProfilerDefinitions::RangeLocation, type, location.filename);
- data.line = location.line;
- data.column = location.column;
- if (type == QQmlProfilerDefinitions::Javascript)
- javascriptMessages.append(data);
- else
- qmlMessages.append(data);
+ types.append(type);
}
-void QQmlProfilerTestClient::rangeEnd(QQmlProfilerDefinitions::RangeType type, qint64 endTime)
+void QQmlProfilerTestClient::addEvent(const QQmlProfilerEvent &event)
{
- QVERIFY(type >= 0 && type < QQmlProfilerDefinitions::MaximumRangeType);
- QVERIFY(lastTimestamp <= endTime);
- lastTimestamp = endTime;
- QQmlProfilerData data(endTime, QQmlProfilerDefinitions::RangeEnd, type);
- if (type == QQmlProfilerDefinitions::Javascript)
- javascriptMessages.append(data);
- else
- qmlMessages.append(data);
-}
+ const int typeIndex = event.typeIndex();
+ QVERIFY(typeIndex < types.length());
-void QQmlProfilerTestClient::animationFrame(qint64 time, int frameRate, int animationCount, int threadId)
-{
- QVERIFY(threadId >= 0);
- QVERIFY(frameRate != -1);
- QVERIFY(animationCount != -1);
- QVERIFY(lastTimestamp <= time);
- lastTimestamp = time;
- QQmlProfilerData data(time, QQmlProfilerDefinitions::Event,
- QQmlProfilerDefinitions::AnimationFrame);
- data.framerate = frameRate;
- data.animationcount = animationCount;
- asynchronousMessages.append(data);
-}
+ const QQmlProfilerEventType &type = types[typeIndex];
-void QQmlProfilerTestClient::sceneGraphEvent(QQmlProfilerDefinitions::SceneGraphFrameType type,
- qint64 time, qint64 numericData1, qint64 numericData2,
- qint64 numericData3, qint64 numericData4,
- qint64 numericData5)
-{
- Q_UNUSED(numericData1);
- Q_UNUSED(numericData2);
- Q_UNUSED(numericData3);
- Q_UNUSED(numericData4);
- Q_UNUSED(numericData5);
- QVERIFY(lastTimestamp <= time);
- lastTimestamp = time;
- asynchronousMessages.append(QQmlProfilerData(time, QQmlProfilerDefinitions::SceneGraphFrame,
- type));
-}
-
-void QQmlProfilerTestClient::pixmapCacheEvent(QQmlProfilerDefinitions::PixmapEventType type,
- qint64 time, const QString &url, int numericData1,
- int numericData2)
-{
- QVERIFY(lastTimestamp <= time);
- lastTimestamp = time;
- QQmlProfilerData data(time, QQmlProfilerDefinitions::PixmapCacheEvent, type, url);
- switch (type) {
- case QQmlProfilerDefinitions::PixmapSizeKnown:
- data.line = numericData1;
- data.column = numericData2;
+ switch (type.message()) {
+ case Event: {
+ switch (type.detailType()) {
+ case StartTrace:
+ QFAIL("StartTrace should not be passed on as event");
+ break;
+ case EndTrace:
+ QFAIL("EndTrace should not be passed on as event");
+ break;
+ case AnimationFrame:
+ asynchronousMessages.append(event);
+ break;
+ case Mouse:
+ case Key:
+ qmlMessages.append(event);
+ break;
+ default:
+ QFAIL(qPrintable(QString::fromLatin1("Event with unknown detailType %1 received at %2.")
+ .arg(type.detailType()).arg(event.timestamp())));
+ break;
+ }
break;
- case QQmlProfilerDefinitions::PixmapReferenceCountChanged:
- case QQmlProfilerDefinitions::PixmapCacheCountChanged:
- data.animationcount = numericData1;
+ }
+ case RangeStart:
+ case RangeData:
+ case RangeLocation:
+ case RangeEnd:
+ QFAIL("Range stages are transmitted as part of events");
break;
- default:
+ case Complete:
+ QFAIL("Complete should not be passed on as event");
+ break;
+ case PixmapCacheEvent:
+ pixmapMessages.append(event);
+ break;
+ case SceneGraphFrame:
+ asynchronousMessages.append(event);
+ break;
+ case MemoryAllocation:
+ jsHeapMessages.append(event);
+ break;
+ case DebugMessage:
+ // Unhandled
+ break;
+ case MaximumMessage:
+ switch (type.rangeType()) {
+ case Painting:
+ QFAIL("QtQuick1 paint message received.");
+ break;
+ case Compiling:
+ case Creating:
+ case Binding:
+ case HandlingSignal:
+ qmlMessages.append(event);
+ break;
+ case Javascript:
+ javascriptMessages.append(event);
+ break;
+ default:
+ QFAIL(qPrintable(
+ QString::fromLatin1("Unknown range event %1 received at %2.")
+ .arg(type.rangeType()).arg(event.timestamp())));
+ break;
+ }
break;
}
- pixmapMessages.append(data);
-}
-
-void QQmlProfilerTestClient::memoryAllocation(QQmlProfilerDefinitions::MemoryType type, qint64 time,
- qint64 amount)
-{
- QVERIFY(lastTimestamp <= time);
- lastTimestamp = time;
- QQmlProfilerData data(time, QQmlProfilerDefinitions::MemoryAllocation, type);
- data.amount = amount;
- jsHeapMessages.append(data);
-}
-
-void QQmlProfilerTestClient::inputEvent(QQmlProfilerDefinitions::InputEventType type, qint64 time,
- int a, int b)
-{
- QVERIFY(lastTimestamp <= time);
- lastTimestamp = time;
- qmlMessages.append(QQmlProfilerData(time, QQmlProfilerDefinitions::Event, type,
- QString::number(a) + QLatin1Char('x') +
- QString::number(b)));
-}
-
-void QQmlProfilerTestClient::unknownEvent(QQmlProfilerDefinitions::Message messageType, qint64 time,
- int detailType)
-{
- QFAIL(qPrintable(QString::fromLatin1("Unknown event %1 with detail type %2 received at %3.")
- .arg(messageType).arg(detailType).arg(time)));
-}
-
-void QQmlProfilerTestClient::unknownData(QPacket &stream)
-{
- QFAIL(qPrintable(QString::fromLatin1("%1 bytes of extra data after receiving message.")
- .arg(stream.device()->bytesAvailable())));
-}
-
-void QQmlProfilerTestClient::complete()
-{
- emit recordingFinished();
}
class tst_QQmlProfilerService : public QQmlDebugTest
@@ -278,18 +189,23 @@ private:
CheckLine = 1 << 2,
CheckColumn = 1 << 3,
CheckDataEndsWith = 1 << 4,
+ CheckFileEndsWith = 1 << 5,
+ CheckNumbers = 1 << 6,
- CheckAll = CheckMessageType | CheckDetailType | CheckLine | CheckColumn | CheckDataEndsWith
+ CheckType = CheckMessageType | CheckDetailType | CheckLine | CheckColumn | CheckFileEndsWith
};
- ConnectResult connect(bool block, const QString &testFile, bool restrictServices = true);
+ ConnectResult connect(bool block, const QString &testFile, bool recordFromStart = true,
+ uint flushInterval = 0, bool restrictServices = true);
+ void checkProcessTerminated();
void checkTraceReceived();
void checkJsHeap();
- bool verify(MessageListType type, int expectedPosition, const QQmlProfilerData &expected,
- quint32 checks);
+ bool verify(MessageListType type, int expectedPosition,
+ const QQmlProfilerEventType &expectedType, quint32 checks,
+ const QVector<qint64> &numbers);
QList<QQmlDebugClient *> createClients() override;
- QPointer<QQmlProfilerTestClient> m_client;
+ QScopedPointer<QQmlProfilerTestClient> m_client;
private slots:
void cleanup() override;
@@ -305,33 +221,63 @@ private slots:
void flushInterval();
void translationBinding();
void memory();
+
+private:
+ bool m_recordFromStart = true;
+ bool m_flushInterval = 0;
+ bool m_isComplete = false;
+
+ // Don't use ({...}) here as MSVC will interpret that as the "QVector(int size)" ctor.
+ const QVector<qint64> m_rangeStart = (QVector<qint64>() << QQmlProfilerDefinitions::RangeStart);
+ const QVector<qint64> m_rangeEnd = (QVector<qint64>() << QQmlProfilerDefinitions::RangeEnd);
};
-#define VERIFY(type, position, expected, checks) QVERIFY(verify(type, position, expected, checks))
+#define VERIFY(type, position, expected, checks, numbers) \
+ QVERIFY(verify(type, position, expected, checks, numbers))
-QQmlDebugTest::ConnectResult tst_QQmlProfilerService::connect(bool block, const QString &file,
- bool restrictServices)
+QQmlDebugTest::ConnectResult tst_QQmlProfilerService::connect(
+ bool block, const QString &file, bool recordFromStart, uint flushInterval,
+ bool restrictServices)
{
+ m_recordFromStart = recordFromStart;
+ m_flushInterval = flushInterval;
+ m_isComplete = false;
+
// ### Still using qmlscene due to QTBUG-33377
return QQmlDebugTest::connect(QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmlscene",
restrictServices ? QStringLiteral("CanvasFrameRate") : QString(),
testFile(file), block);
}
+void tst_QQmlProfilerService::checkProcessTerminated()
+{
+ // If the process ends before connect(), we get a non-success value from connect()
+ // That's not a problem as we will still receive the trace. We check that process has terminated
+ // cleanly here.
+
+ // Wait for the process to finish by itself, if that hasn't happened already
+ QTRY_COMPARE(m_client->client->state(), QQmlDebugClient::NotConnected);
+ QTRY_COMPARE(m_process->exitStatus(), QProcess::NormalExit);
+}
+
void tst_QQmlProfilerService::checkTraceReceived()
{
- QVERIFY2(QQmlDebugTest::waitForSignal(m_client, SIGNAL(recordingFinished())),
- "No trace received in time.");
+ QTRY_VERIFY2(m_isComplete, "No trace received in time.");
+
+ QVector<qint64> numbers;
// must start with "StartTrace"
- QQmlProfilerData expected(0, QQmlProfilerDefinitions::Event,
- QQmlProfilerDefinitions::StartTrace);
- VERIFY(MessageListAsynchronous, 0, expected, CheckMessageType | CheckDetailType);
+ QQmlProfilerEventType expected(QQmlProfilerDefinitions::Event,
+ QQmlProfilerDefinitions::MaximumRangeType,
+ QQmlProfilerDefinitions::StartTrace);
+ VERIFY(MessageListAsynchronous, 0, expected, CheckMessageType | CheckDetailType, numbers);
// must end with "EndTrace"
- expected.detailType = QQmlProfilerDefinitions::EndTrace;
+ expected = QQmlProfilerEventType(QQmlProfilerDefinitions::Event,
+ QQmlProfilerDefinitions::MaximumRangeType,
+ QQmlProfilerDefinitions::EndTrace);
VERIFY(MessageListAsynchronous, m_client->asynchronousMessages.length() - 1, expected,
- CheckMessageType | CheckDetailType);
+ CheckMessageType | CheckDetailType, numbers);
}
void tst_QQmlProfilerService::checkJsHeap()
@@ -344,33 +290,35 @@ void tst_QQmlProfilerService::checkJsHeap()
qint64 allocated = 0;
qint64 used = 0;
qint64 lastTimestamp = -1;
- foreach (const QQmlProfilerData &message, m_client->jsHeapMessages) {
- switch (message.detailType) {
+ foreach (const QQmlProfilerEvent &message, m_client->jsHeapMessages) {
+ const qint64 amount = message.number<qint64>(0);
+ const QQmlProfilerEventType &type = m_client->types.at(message.typeIndex());
+ switch (type.detailType()) {
case QV4::Profiling::HeapPage:
- allocated += message.amount;
+ allocated += amount;
seen_alloc = true;
break;
case QV4::Profiling::SmallItem:
- used += message.amount;
+ used += amount;
seen_small = true;
break;
case QV4::Profiling::LargeItem:
- allocated += message.amount;
- used += message.amount;
+ allocated += amount;
+ used += amount;
seen_large = true;
break;
}
- QVERIFY(message.time >= lastTimestamp);
+ QVERIFY(message.timestamp() >= lastTimestamp);
// The heap will only be consistent after all events of the same timestamp are processed.
if (lastTimestamp == -1) {
- lastTimestamp = message.time;
+ lastTimestamp = message.timestamp();
continue;
- } else if (message.time == lastTimestamp) {
+ } else if (message.timestamp() == lastTimestamp) {
continue;
}
- lastTimestamp = message.time;
+ lastTimestamp = message.timestamp();
QVERIFY2(used >= 0, QString::fromLatin1("Negative memory usage seen: %1")
.arg(used).toUtf8().constData());
@@ -389,10 +337,10 @@ void tst_QQmlProfilerService::checkJsHeap()
}
bool tst_QQmlProfilerService::verify(tst_QQmlProfilerService::MessageListType type,
- int expectedPosition, const QQmlProfilerData &expected,
- quint32 checks)
+ int expectedPosition, const QQmlProfilerEventType &expected,
+ quint32 checks, const QVector<qint64> &expectedNumbers)
{
- QVector<QQmlProfilerData> *target = 0;
+ const QVector<QQmlProfilerEvent> *target = 0;
switch (type) {
case MessageListQML: target = &(m_client->qmlMessages); break;
case MessageListJavaScript: target = &(m_client->javascriptMessages); break;
@@ -408,41 +356,81 @@ bool tst_QQmlProfilerService::verify(tst_QQmlProfilerService::MessageListType ty
}
uint position = expectedPosition;
- qint64 timestamp = target->at(expectedPosition).time;
- while (position > 0 && target->at(position - 1).time == timestamp)
+ qint64 timestamp = target->at(expectedPosition).timestamp();
+ while (position > 0 && target->at(position - 1).timestamp() == timestamp)
--position;
QStringList warnings;
do {
- const QQmlProfilerData &actual = target->at(position);
- if ((checks & CheckMessageType) && actual.messageType != expected.messageType) {
- warnings << QString::fromLatin1("%1: unexpected messageType. actual: %2 - expected: %3")
- .arg(position).arg(actual.messageType).arg(expected.messageType);
+ const QQmlProfilerEvent &event = target->at(position);
+ const QQmlProfilerEventType &actual = m_client->types.at(event.typeIndex());
+ if ((checks & CheckMessageType) &&
+ (actual.message() != expected.message()
+ || actual.rangeType() != expected.rangeType())) {
+ warnings << QString::fromLatin1("%1: unexpected messageType or rangeType. "
+ "actual: %2, %3 - expected: %4, %5")
+ .arg(position).arg(actual.message()).arg(actual.rangeType())
+ .arg(expected.message()).arg(expected.rangeType());
continue;
}
- if ((checks & CheckDetailType) && actual.detailType != expected.detailType) {
+ if ((checks & CheckDetailType) && actual.detailType() != expected.detailType()) {
warnings << QString::fromLatin1("%1: unexpected detailType. actual: %2 - expected: %3")
- .arg(position).arg(actual.detailType).arg(expected.detailType);
+ .arg(position).arg(actual.detailType()).arg(expected.detailType());
continue;
}
- if ((checks & CheckLine) && actual.line != expected.line) {
+
+ const QQmlProfilerEventLocation expectedLocation = expected.location();
+ const QQmlProfilerEventLocation actualLocation = actual.location();
+
+ if ((checks & CheckLine) && actualLocation.line() != expectedLocation.line()) {
warnings << QString::fromLatin1("%1: unexpected line. actual: %2 - expected: %3")
- .arg(position).arg(actual.line).arg(expected.line);
+ .arg(position).arg(actualLocation.line())
+ .arg(expectedLocation.line());
continue;
}
- if ((checks & CheckColumn) && actual.column != expected.column) {
+ if ((checks & CheckColumn) && actualLocation.column() != expectedLocation.column()) {
warnings << QString::fromLatin1("%1: unexpected column. actual: %2 - expected: %3")
- .arg(position).arg(actual.column).arg(expected.column);
+ .arg(position).arg(actualLocation.column())
+ .arg(expectedLocation.column());
continue;
}
- if ((checks & CheckDataEndsWith) && !actual.detailData.endsWith(expected.detailData)) {
+ if ((checks & CheckFileEndsWith) &&
+ !actualLocation.filename().endsWith(expectedLocation.filename())) {
+ warnings << QString::fromLatin1("%1: unexpected fileName. actual: %2 - expected: %3")
+ .arg(position).arg(actualLocation.filename())
+ .arg(expectedLocation.filename());
+ continue;
+ }
+
+ if ((checks & CheckDataEndsWith) && !actual.data().endsWith(expected.data())) {
warnings << QString::fromLatin1("%1: unexpected detailData. actual: %2 - expected: %3")
- .arg(position).arg(actual.detailData).arg(expected.detailData);
+ .arg(position).arg(actual.data()).arg(expected.data());
continue;
}
+
+ if (checks & CheckNumbers) {
+ const QVector<qint64> actualNumbers = event.numbers<QVector<qint64>>();
+ if (actualNumbers != expectedNumbers) {
+
+ QStringList expectedList;
+ for (qint64 number : expectedNumbers)
+ expectedList.append(QString::number(number));
+ QStringList actualList;
+ for (qint64 number : actualNumbers)
+ actualList.append(QString::number(number));
+
+ warnings << QString::fromLatin1(
+ "%1: unexpected numbers. actual [%2] - expected: [%3]")
+ .arg(position)
+ .arg(actualList.join(QLatin1String(", ")))
+ .arg(expectedList.join(QLatin1String(", ")));
+ continue;
+ }
+ }
+
return true;
- } while (target->at(++position).time == timestamp);
+ } while (target->at(++position).timestamp() == timestamp);
foreach (const QString &message, warnings)
qWarning() << message.toLocal8Bit().constData();
@@ -452,49 +440,59 @@ bool tst_QQmlProfilerService::verify(tst_QQmlProfilerService::MessageListType ty
QList<QQmlDebugClient *> tst_QQmlProfilerService::createClients()
{
- m_client = new QQmlProfilerTestClient(m_connection);
- return QList<QQmlDebugClient *>({m_client});
+ m_client.reset(new QQmlProfilerTestClient(m_connection));
+ m_client->client->setRecording(m_recordFromStart);
+ m_client->client->setFlushInterval(m_flushInterval);
+ QObject::connect(m_client->client, &QQmlProfilerClient::complete,
+ this, [this](){ m_isComplete = true; });
+ return QList<QQmlDebugClient *>({m_client->client});
}
void tst_QQmlProfilerService::cleanup()
{
+ auto log = [this](const QQmlProfilerEvent &data, int i) {
+ const QQmlProfilerEventType &type = m_client->types.at(data.typeIndex());
+ const QQmlProfilerEventLocation location = type.location();
+ qDebug() << i << data.timestamp() << type.message() << type.rangeType() << type.detailType()
+ << location.filename() << location.line() << location.column()
+ << data.numbers<QVector<qint64>>();
+ };
+
if (m_client && QTest::currentTestFailed()) {
qDebug() << "QML Messages:" << m_client->qmlMessages.count();
int i = 0;
- foreach (const QQmlProfilerData &data, m_client->qmlMessages) {
- qDebug() << i++ << data.time << data.messageType << data.detailType << data.detailData
- << data.line << data.column;
- }
+ for (const QQmlProfilerEvent &data : qAsConst(m_client->qmlMessages))
+ log(data, i++);
+
qDebug() << " ";
qDebug() << "JavaScript Messages:" << m_client->javascriptMessages.count();
i = 0;
- foreach (const QQmlProfilerData &data, m_client->javascriptMessages) {
- qDebug() << i++ << data.time << data.messageType << data.detailType << data.detailData
- << data.line << data.column;
- }
+
+ for (const QQmlProfilerEvent &data : qAsConst(m_client->javascriptMessages))
+ log(data, i++);
+
qDebug() << " ";
qDebug() << "Asynchronous Messages:" << m_client->asynchronousMessages.count();
i = 0;
- foreach (const QQmlProfilerData &data, m_client->asynchronousMessages) {
- qDebug() << i++ << data.time << data.messageType << data.detailType << data.detailData
- << data.framerate << data.animationcount << data.line << data.column;
- }
+ for (const QQmlProfilerEvent &data : qAsConst(m_client->asynchronousMessages))
+ log(data, i++);
+
qDebug() << " ";
qDebug() << "Pixmap Cache Messages:" << m_client->pixmapMessages.count();
i = 0;
- foreach (const QQmlProfilerData &data, m_client->pixmapMessages) {
- qDebug() << i++ << data.time << data.messageType << data.detailType << data.detailData
- << data.line << data.column;
- }
+ for (const QQmlProfilerEvent &data : qAsConst(m_client->pixmapMessages))
+ log(data, i++);
+
qDebug() << " ";
qDebug() << "Javascript Heap Messages:" << m_client->jsHeapMessages.count();
i = 0;
- foreach (const QQmlProfilerData &data, m_client->jsHeapMessages) {
- qDebug() << i++ << data.time << data.messageType << data.detailType;
- }
+ for (const QQmlProfilerEvent &data : qAsConst(m_client->jsHeapMessages))
+ log(data, i++);
+
qDebug() << " ";
}
+ m_client.reset();
QQmlDebugTest::cleanup();
}
@@ -519,63 +517,62 @@ void tst_QQmlProfilerService::connect()
QFETCH(bool, restrictMode);
QFETCH(bool, traceEnabled);
- QCOMPARE(connect(blockMode, "test.qml", restrictMode), ConnectSuccess);
+ QCOMPARE(connect(blockMode, "test.qml", traceEnabled, 0, restrictMode), ConnectSuccess);
- // if the engine is waiting, then the first message determines if it starts with trace enabled
if (!traceEnabled)
- m_client->sendRecordingStatus(false);
- m_client->sendRecordingStatus(true);
- m_client->sendRecordingStatus(false);
+ m_client->client->setRecording(true);
+ m_client->client->setRecording(false);
checkTraceReceived();
checkJsHeap();
}
void tst_QQmlProfilerService::pixmapCacheData()
{
- QCOMPARE(connect(true, "pixmapCacheTest.qml"), ConnectSuccess);
- m_client->sendRecordingStatus(true);
- QVERIFY(QQmlDebugTest::waitForSignal(m_process, SIGNAL(readyReadStandardOutput())));
+ QCOMPARE(connect(true, "pixmapCacheTest.qml"), ConnectSuccess);
+ // Don't wait for readyReadStandardOutput before the loop. It may have already arrived.
while (m_process->output().indexOf(QLatin1String("image loaded")) == -1 &&
m_process->output().indexOf(QLatin1String("image error")) == -1)
QVERIFY(QQmlDebugTest::waitForSignal(m_process, SIGNAL(readyReadStandardOutput())));
- m_client->sendRecordingStatus(false);
+ m_client->client->setRecording(false);
checkTraceReceived();
checkJsHeap();
- QQmlProfilerData expected(0, QQmlProfilerDefinitions::PixmapCacheEvent);
+ auto createType = [](QQmlProfilerDefinitions::PixmapEventType type) {
+ return QQmlProfilerEventType(QQmlProfilerDefinitions::PixmapCacheEvent,
+ QQmlProfilerDefinitions::MaximumRangeType, type);
+ };
+
+ QVector<qint64> numbers;
// image starting to load
- expected.detailType = QQmlProfilerDefinitions::PixmapLoadingStarted;
- VERIFY(MessageListPixmap, 0, expected, CheckMessageType | CheckDetailType);
+ VERIFY(MessageListPixmap, 0, createType(QQmlProfilerDefinitions::PixmapLoadingStarted),
+ CheckMessageType | CheckDetailType, numbers);
// image size
- expected.detailType = QQmlProfilerDefinitions::PixmapSizeKnown;
- expected.line = expected.column = 2; // width and height, in fact
- VERIFY(MessageListPixmap, 1, expected,
- CheckMessageType | CheckDetailType | CheckLine | CheckColumn);
+ numbers = QVector<qint64>({2, 2, 1});
+ VERIFY(MessageListPixmap, 1, createType(QQmlProfilerDefinitions::PixmapSizeKnown),
+ CheckMessageType | CheckDetailType | CheckNumbers, numbers);
// image loaded
- expected.detailType = QQmlProfilerDefinitions::PixmapLoadingFinished;
- VERIFY(MessageListPixmap, 2, expected, CheckMessageType | CheckDetailType);
+ VERIFY(MessageListPixmap, 2, createType(QQmlProfilerDefinitions::PixmapLoadingFinished),
+ CheckMessageType | CheckDetailType, numbers);
// cache size
- expected.detailType = QQmlProfilerDefinitions::PixmapCacheCountChanged;
- VERIFY(MessageListPixmap, 3, expected, CheckMessageType | CheckDetailType);
+ VERIFY(MessageListPixmap, 3, createType(QQmlProfilerDefinitions::PixmapCacheCountChanged),
+ CheckMessageType | CheckDetailType, numbers);
}
void tst_QQmlProfilerService::scenegraphData()
{
QCOMPARE(connect(true, "scenegraphTest.qml"), ConnectSuccess);
- m_client->sendRecordingStatus(true);
-
while (!m_process->output().contains(QLatin1String("tick")))
QVERIFY(QQmlDebugTest::waitForSignal(m_process, SIGNAL(readyReadStandardOutput())));
- m_client->sendRecordingStatus(false);
+ m_client->client->setRecording(false);
checkTraceReceived();
checkJsHeap();
@@ -591,10 +588,11 @@ void tst_QQmlProfilerService::scenegraphData()
qint64 renderFrameTime = -1;
#if QT_CONFIG(opengl) //Software renderer doesn't have context frames
if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::OpenGL)) {
- foreach (const QQmlProfilerData &msg, m_client->asynchronousMessages) {
- if (msg.messageType == QQmlProfilerDefinitions::SceneGraphFrame) {
- if (msg.detailType == QQmlProfilerDefinitions::SceneGraphContextFrame) {
- contextFrameTime = msg.time;
+ foreach (const QQmlProfilerEvent &msg, m_client->asynchronousMessages) {
+ const QQmlProfilerEventType &type = m_client->types.at(msg.typeIndex());
+ if (type.message() == QQmlProfilerDefinitions::SceneGraphFrame) {
+ if (type.detailType() == QQmlProfilerDefinitions::SceneGraphContextFrame) {
+ contextFrameTime = msg.timestamp();
break;
}
}
@@ -603,23 +601,25 @@ void tst_QQmlProfilerService::scenegraphData()
QVERIFY(contextFrameTime != -1);
}
#endif
- foreach (const QQmlProfilerData &msg, m_client->asynchronousMessages) {
- if (msg.detailType == QQmlProfilerDefinitions::SceneGraphRendererFrame) {
- QVERIFY(msg.time >= contextFrameTime);
- renderFrameTime = msg.time;
+ foreach (const QQmlProfilerEvent &msg, m_client->asynchronousMessages) {
+ const QQmlProfilerEventType &type = m_client->types.at(msg.typeIndex());
+ if (type.detailType() == QQmlProfilerDefinitions::SceneGraphRendererFrame) {
+ QVERIFY(msg.timestamp() >= contextFrameTime);
+ renderFrameTime = msg.timestamp();
break;
}
}
QVERIFY(renderFrameTime != -1);
- foreach (const QQmlProfilerData &msg, m_client->asynchronousMessages) {
- if (msg.detailType == QQmlProfilerDefinitions::SceneGraphRenderLoopFrame) {
- if (msg.time >= contextFrameTime) {
+ foreach (const QQmlProfilerEvent &msg, m_client->asynchronousMessages) {
+ const QQmlProfilerEventType &type = m_client->types.at(msg.typeIndex());
+ if (type.detailType() == QQmlProfilerDefinitions::SceneGraphRenderLoopFrame) {
+ if (msg.timestamp() >= contextFrameTime) {
// Make sure SceneGraphRenderLoopFrame is not between SceneGraphContextFrame and
// SceneGraphRendererFrame. A SceneGraphRenderLoopFrame before everything else is
// OK as the scene graph might decide to do an initial rendering.
- QVERIFY(msg.time >= renderFrameTime);
+ QVERIFY(msg.timestamp() >= renderFrameTime);
break;
}
}
@@ -628,9 +628,8 @@ void tst_QQmlProfilerService::scenegraphData()
void tst_QQmlProfilerService::profileOnExit()
{
- QCOMPARE(connect(true, "exit.qml"), ConnectSuccess);
-
- m_client->sendRecordingStatus(true);
+ connect(true, "exit.qml");
+ checkProcessTerminated();
checkTraceReceived();
checkJsHeap();
@@ -638,9 +637,9 @@ void tst_QQmlProfilerService::profileOnExit()
void tst_QQmlProfilerService::controlFromJS()
{
- QCOMPARE(connect(true, "controlFromJS.qml"), ConnectSuccess);
+ QCOMPARE(connect(true, "controlFromJS.qml", false), ConnectSuccess);
- m_client->sendRecordingStatus(false);
+ m_client->client->setRecording(false);
checkTraceReceived();
checkJsHeap();
}
@@ -649,104 +648,97 @@ void tst_QQmlProfilerService::signalSourceLocation()
{
QCOMPARE(connect(true, "signalSourceLocation.qml"), ConnectSuccess);
- m_client->sendRecordingStatus(true);
while (!(m_process->output().contains(QLatin1String("500"))))
QVERIFY(QQmlDebugTest::waitForSignal(m_process, SIGNAL(readyReadStandardOutput())));
- m_client->sendRecordingStatus(false);
+ m_client->client->setRecording(false);
checkTraceReceived();
checkJsHeap();
- QQmlProfilerData expected(0, QQmlProfilerDefinitions::RangeLocation,
- QQmlProfilerDefinitions::HandlingSignal,
- QLatin1String("signalSourceLocation.qml"));
- expected.line = 8;
- expected.column = 28;
- VERIFY(MessageListQML, 9, expected, CheckAll);
+ auto createType = [](int line, int column) {
+ return QQmlProfilerEventType(
+ QQmlProfilerDefinitions::MaximumMessage,
+ QQmlProfilerDefinitions::HandlingSignal, -1,
+ QQmlProfilerEventLocation(QLatin1String("signalSourceLocation.qml"), line,
+ column));
+ };
- expected.line = 7;
- expected.column = 21;
- VERIFY(MessageListQML, 11, expected, CheckAll);
+ VERIFY(MessageListQML, 4, createType(8, 28), CheckType | CheckNumbers, m_rangeStart);
+ VERIFY(MessageListQML, 6, createType(7, 21), CheckType | CheckNumbers, m_rangeEnd);
}
void tst_QQmlProfilerService::javascript()
{
QCOMPARE(connect(true, "javascript.qml"), ConnectSuccess);
- m_client->sendRecordingStatus(true);
while (!(m_process->output().contains(QLatin1String("done"))))
QVERIFY(QQmlDebugTest::waitForSignal(m_process, SIGNAL(readyReadStandardOutput())));
- m_client->sendRecordingStatus(false);
+ m_client->client->setRecording(false);
checkTraceReceived();
checkJsHeap();
- QQmlProfilerData expected(0, QQmlProfilerDefinitions::RangeStart,
- QQmlProfilerDefinitions::Javascript);
- VERIFY(MessageListJavaScript, 6, expected, CheckMessageType | CheckDetailType);
+ VERIFY(MessageListJavaScript, 2, QQmlProfilerEventType(QQmlProfilerDefinitions::MaximumMessage,
+ QQmlProfilerDefinitions::Javascript),
+ CheckMessageType | CheckDetailType | CheckNumbers, m_rangeStart);
- expected.messageType = QQmlProfilerDefinitions::RangeLocation;
- expected.detailData = QLatin1String("javascript.qml");
- expected.line = 4;
- expected.column = 5;
- VERIFY(MessageListJavaScript, 7, expected, CheckAll);
+ VERIFY(MessageListJavaScript, 3,
+ QQmlProfilerEventType(QQmlProfilerDefinitions::MaximumMessage,
+ QQmlProfilerDefinitions::Javascript, -1,
+ QQmlProfilerEventLocation(QLatin1String("javascript.qml"), 4, 5)),
+ CheckType | CheckNumbers, m_rangeStart);
- expected.messageType = QQmlProfilerDefinitions::RangeData;
- expected.detailData = QLatin1String("something");
- VERIFY(MessageListJavaScript, 8, expected,
- CheckMessageType | CheckDetailType | CheckDataEndsWith);
+ VERIFY(MessageListJavaScript, 4, QQmlProfilerEventType(
+ QQmlProfilerDefinitions::MaximumMessage, QQmlProfilerDefinitions::Javascript, -1,
+ QQmlProfilerEventLocation(), QLatin1String("something")),
+ CheckMessageType | CheckDetailType | CheckDataEndsWith | CheckNumbers, m_rangeStart);
- expected.messageType = QQmlProfilerDefinitions::RangeEnd;
- VERIFY(MessageListJavaScript, 21, expected, CheckMessageType | CheckDetailType);
+ VERIFY(MessageListJavaScript, 10, QQmlProfilerEventType(QQmlProfilerDefinitions::MaximumMessage,
+ QQmlProfilerDefinitions::Javascript),
+ CheckMessageType | CheckDetailType | CheckNumbers, m_rangeEnd);
}
void tst_QQmlProfilerService::flushInterval()
{
- QCOMPARE(connect(true, "timer.qml"), ConnectSuccess);
-
- m_client->sendRecordingStatus(true, -1, 1);
+ QCOMPARE(connect(true, "timer.qml", true, 1), ConnectSuccess);
// Make sure we get multiple messages
QTRY_VERIFY(m_client->qmlMessages.length() > 0);
QVERIFY(m_client->qmlMessages.length() < 100);
QTRY_VERIFY(m_client->qmlMessages.length() > 100);
- m_client->sendRecordingStatus(false);
+ m_client->client->setRecording(false);
checkTraceReceived();
checkJsHeap();
}
void tst_QQmlProfilerService::translationBinding()
{
- QCOMPARE(connect(true, "qstr.qml"), ConnectSuccess);
-
- m_client->sendRecordingStatus(true);
+ connect(true, "qstr.qml");
+ checkProcessTerminated();
checkTraceReceived();
checkJsHeap();
- QQmlProfilerData expected(0, QQmlProfilerDefinitions::RangeStart,
- QQmlProfilerDefinitions::Binding);
- VERIFY(MessageListQML, 8, expected,
- CheckDetailType | CheckMessageType);
+ const QQmlProfilerEventType type(QQmlProfilerDefinitions::MaximumMessage,
+ QQmlProfilerDefinitions::Binding);
- expected.messageType = QQmlProfilerDefinitions::RangeEnd;
- VERIFY(MessageListQML, 10, expected,
- CheckDetailType | CheckMessageType);
+ VERIFY(MessageListQML, 4, type, CheckDetailType | CheckMessageType | CheckNumbers,
+ m_rangeStart);
+ VERIFY(MessageListQML, 5, type, CheckDetailType | CheckMessageType | CheckNumbers,
+ m_rangeEnd);
}
void tst_QQmlProfilerService::memory()
{
connect(true, "memory.qml");
- if (QTest::currentTestFailed() || QTestResult::skipCurrentTest())
- return;
-
- m_client->sendRecordingStatus(true);
+ checkProcessTerminated();
checkTraceReceived();
checkJsHeap();
int smallItems = 0;
for (auto message : m_client->jsHeapMessages) {
- if (message.detailType == QV4::Profiling::SmallItem)
+ const QQmlProfilerEventType &type = m_client->types[message.typeIndex()];
+ if (type.detailType() == QV4::Profiling::SmallItem)
++smallItems;
}
diff --git a/tools/qmlprofiler/qmlprofilerapplication.cpp b/tools/qmlprofiler/qmlprofilerapplication.cpp
index 033492b516..0b0417bd7d 100644
--- a/tools/qmlprofiler/qmlprofilerapplication.cpp
+++ b/tools/qmlprofiler/qmlprofilerapplication.cpp
@@ -83,24 +83,27 @@ QmlProfilerApplication::QmlProfilerApplication(int &argc, char **argv) :
m_verbose(false),
m_recording(true),
m_interactive(false),
- m_qmlProfilerClient(&m_connection, &m_profilerData),
m_connectionAttempts(0)
{
+ m_connection.reset(new QQmlDebugConnection);
+ m_profilerData.reset(new QmlProfilerData);
+ m_qmlProfilerClient.reset(new QmlProfilerClient(m_connection.data(), m_profilerData.data()));
m_connectTimer.setInterval(1000);
connect(&m_connectTimer, &QTimer::timeout, this, &QmlProfilerApplication::tryToConnect);
- connect(&m_connection, &QQmlDebugConnection::connected,
+ connect(m_connection.data(), &QQmlDebugConnection::connected,
this, &QmlProfilerApplication::connected);
- connect(&m_qmlProfilerClient, &QmlProfilerClient::enabledChanged,
+ connect(m_qmlProfilerClient.data(), &QmlProfilerClient::enabledChanged,
this, &QmlProfilerApplication::traceClientEnabledChanged);
- connect(&m_qmlProfilerClient, &QmlProfilerClient::recordingStarted,
+ connect(m_qmlProfilerClient.data(), &QmlProfilerClient::traceStarted,
this, &QmlProfilerApplication::notifyTraceStarted);
- connect(&m_qmlProfilerClient, &QmlProfilerClient::error,
+ connect(m_qmlProfilerClient.data(), &QmlProfilerClient::error,
this, &QmlProfilerApplication::logError);
- connect(&m_profilerData, &QmlProfilerData::error, this, &QmlProfilerApplication::logError);
- connect(&m_profilerData, &QmlProfilerData::dataReady,
+ connect(m_profilerData.data(), &QmlProfilerData::error,
+ this, &QmlProfilerApplication::logError);
+ connect(m_profilerData.data(), &QmlProfilerData::dataReady,
this, &QmlProfilerApplication::traceFinished);
}
@@ -239,7 +242,7 @@ void QmlProfilerApplication::parseArguments()
if (features == 0)
parser.showHelp(4);
- m_qmlProfilerClient.setFeatures(features);
+ m_qmlProfilerClient->setRequestedFeatures(features);
if (parser.isSet(verbose))
m_verbose = true;
@@ -295,10 +298,10 @@ void QmlProfilerApplication::flush()
{
if (m_recording) {
m_pendingRequest = REQUEST_FLUSH;
- m_qmlProfilerClient.sendRecordingStatus(false);
+ m_qmlProfilerClient->sendRecordingStatus(false);
} else {
- if (m_profilerData.save(m_interactiveOutputFile)) {
- m_profilerData.clear();
+ if (m_profilerData->save(m_interactiveOutputFile)) {
+ m_profilerData->clear();
if (!m_interactiveOutputFile.isEmpty())
prompt(tr("Data written to %1.").arg(m_interactiveOutputFile));
else
@@ -313,7 +316,7 @@ void QmlProfilerApplication::flush()
void QmlProfilerApplication::output()
{
- if (m_profilerData.save(m_interactiveOutputFile)) {
+ if (m_profilerData->save(m_interactiveOutputFile)) {
if (!m_interactiveOutputFile.isEmpty())
prompt(tr("Data written to %1.").arg(m_interactiveOutputFile));
else
@@ -385,12 +388,12 @@ void QmlProfilerApplication::userCommand(const QString &command)
if (cmd == Constants::CMD_RECORD || cmd == Constants::CMD_RECORD2) {
m_pendingRequest = REQUEST_TOGGLE_RECORDING;
- m_qmlProfilerClient.sendRecordingStatus(!m_recording);
+ m_qmlProfilerClient->sendRecordingStatus(!m_recording);
} else if (cmd == Constants::CMD_QUIT || cmd == Constants::CMD_QUIT2) {
m_pendingRequest = REQUEST_QUIT;
if (m_recording) {
prompt(tr("The application is still generating data. Really quit (y/n)?"));
- } else if (!m_profilerData.isEmpty()) {
+ } else if (!m_profilerData->isEmpty()) {
prompt(tr("There is still trace data in memory. Really quit (y/n)?"));
} else {
quit();
@@ -398,7 +401,7 @@ void QmlProfilerApplication::userCommand(const QString &command)
} else if (cmd == Constants::CMD_OUTPUT || cmd == Constants::CMD_OUTPUT2) {
if (m_recording) {
prompt(tr("Cannot output while recording data."));
- } else if (m_profilerData.isEmpty()) {
+ } else if (m_profilerData->isEmpty()) {
prompt(tr("No data was recorded so far."));
} else {
m_interactiveOutputFile = args.length() > 0 ? args.at(0).toString() : m_outputFile;
@@ -408,14 +411,14 @@ void QmlProfilerApplication::userCommand(const QString &command)
} else if (cmd == Constants::CMD_CLEAR || cmd == Constants::CMD_CLEAR2) {
if (m_recording) {
prompt(tr("Cannot clear data while recording."));
- } else if (m_profilerData.isEmpty()) {
+ } else if (m_profilerData->isEmpty()) {
prompt(tr("No data was recorded so far."));
} else {
- m_profilerData.clear();
+ m_profilerData->clear();
prompt(tr("Trace data cleared."));
}
} else if (cmd == Constants::CMD_FLUSH || cmd == Constants::CMD_FLUSH2) {
- if (!m_recording && m_profilerData.isEmpty()) {
+ if (!m_recording && m_profilerData->isEmpty()) {
prompt(tr("No data was recorded so far."));
} else {
m_interactiveOutputFile = args.length() > 0 ? args.at(0).toString() : m_outputFile;
@@ -443,9 +446,9 @@ void QmlProfilerApplication::notifyTraceStarted()
void QmlProfilerApplication::outputData()
{
- if (!m_profilerData.isEmpty()) {
- m_profilerData.save(m_outputFile);
- m_profilerData.clear();
+ if (!m_profilerData->isEmpty()) {
+ m_profilerData->save(m_outputFile);
+ m_profilerData->clear();
}
}
@@ -454,7 +457,7 @@ void QmlProfilerApplication::run()
if (m_runMode == LaunchMode) {
if (!m_socketFile.isEmpty()) {
logStatus(QString::fromLatin1("Listening on %1 ...").arg(m_socketFile));
- m_connection.startLocalServer(m_socketFile);
+ m_connection->startLocalServer(m_socketFile);
}
m_process = new QProcess(this);
QStringList arguments;
@@ -481,7 +484,7 @@ void QmlProfilerApplication::run()
void QmlProfilerApplication::tryToConnect()
{
- Q_ASSERT(!m_connection.isConnected());
+ Q_ASSERT(!m_connection->isConnected());
++ m_connectionAttempts;
if (!m_verbose && !(m_connectionAttempts % 5)) {// print every 5 seconds
@@ -497,7 +500,7 @@ void QmlProfilerApplication::tryToConnect()
if (m_socketFile.isEmpty()) {
logStatus(QString::fromLatin1("Connecting to %1:%2 ...").arg(m_hostName).arg(m_port));
- m_connection.connectToHost(m_hostName, m_port);
+ m_connection->connectToHost(m_hostName, m_port);
}
}
@@ -538,7 +541,7 @@ void QmlProfilerApplication::processFinished()
if (!m_interactive)
exit(exitCode);
else
- m_qmlProfilerClient.clearPendingData();
+ m_qmlProfilerClient->clearAll();
}
void QmlProfilerApplication::traceClientEnabledChanged(bool enabled)
@@ -547,7 +550,7 @@ void QmlProfilerApplication::traceClientEnabledChanged(bool enabled)
logStatus("Trace client is attached.");
// blocked server is waiting for recording message from both clients
// once the last one is connected, both messages should be sent
- m_qmlProfilerClient.sendRecordingStatus(m_recording);
+ m_qmlProfilerClient->setRecording(m_recording);
}
}
@@ -564,7 +567,7 @@ void QmlProfilerApplication::traceFinished()
prompt(tr("Application stopped recording."), false);
}
- m_qmlProfilerClient.clearPendingData();
+ m_qmlProfilerClient->clearEvents();
}
void QmlProfilerApplication::prompt(const QString &line, bool ready)
diff --git a/tools/qmlprofiler/qmlprofilerapplication.h b/tools/qmlprofiler/qmlprofilerapplication.h
index 13f0f041f0..f7a8efd61b 100644
--- a/tools/qmlprofiler/qmlprofilerapplication.h
+++ b/tools/qmlprofiler/qmlprofilerapplication.h
@@ -105,9 +105,9 @@ private:
bool m_recording;
bool m_interactive;
- QQmlDebugConnection m_connection;
- QmlProfilerClient m_qmlProfilerClient;
- QmlProfilerData m_profilerData;
+ QScopedPointer<QQmlDebugConnection> m_connection;
+ QScopedPointer<QmlProfilerClient> m_qmlProfilerClient;
+ QScopedPointer<QmlProfilerData> m_profilerData;
QTimer m_connectTimer;
uint m_connectionAttempts;
};
diff --git a/tools/qmlprofiler/qmlprofilerclient.cpp b/tools/qmlprofiler/qmlprofilerclient.cpp
index 0c031db914..b69c7e73e1 100644
--- a/tools/qmlprofiler/qmlprofilerclient.cpp
+++ b/tools/qmlprofiler/qmlprofilerclient.cpp
@@ -43,36 +43,26 @@ public:
QmlProfilerClientPrivate(QQmlDebugConnection *connection, QmlProfilerData *data);
QmlProfilerData *data;
-
- qint64 inProgressRanges;
- QStack<qint64> rangeStartTimes[QQmlProfilerDefinitions::MaximumRangeType];
- QStack<QStringList> rangeDatas[QQmlProfilerDefinitions::MaximumRangeType];
- QStack<QQmlEventLocation> rangeLocations[QQmlProfilerDefinitions::MaximumRangeType];
- int rangeCount[QQmlProfilerDefinitions::MaximumRangeType];
-
bool enabled;
};
QmlProfilerClientPrivate::QmlProfilerClientPrivate(QQmlDebugConnection *connection,
QmlProfilerData *data) :
- QQmlProfilerClientPrivate(connection), data(data), inProgressRanges(0), enabled(false)
+ QQmlProfilerClientPrivate(connection, data), data(data), enabled(false)
{
- ::memset(rangeCount, 0, QQmlProfilerDefinitions::MaximumRangeType * sizeof(int));
}
QmlProfilerClient::QmlProfilerClient(QQmlDebugConnection *connection, QmlProfilerData *data) :
QQmlProfilerClient(*(new QmlProfilerClientPrivate(connection, data)))
{
-}
-
-void QmlProfilerClient::clearPendingData()
-{
Q_D(QmlProfilerClient);
- for (int i = 0; i < QQmlProfilerDefinitions::MaximumRangeType; ++i) {
- d->rangeCount[i] = 0;
- d->rangeDatas[i].clear();
- d->rangeLocations[i].clear();
- }
+ setRequestedFeatures(std::numeric_limits<quint64>::max());
+ connect(this, &QQmlProfilerClient::traceStarted,
+ d->data, &QmlProfilerData::setTraceStartTime);
+ connect(this, &QQmlProfilerClient::traceFinished,
+ d->data, &QmlProfilerData::setTraceEndTime);
+ connect(this, &QQmlProfilerClient::complete,
+ d->data, &QmlProfilerData::complete);
}
void QmlProfilerClient::stateChanged(State state)
@@ -84,119 +74,4 @@ void QmlProfilerClient::stateChanged(State state)
}
}
-void QmlProfilerClient::traceStarted(qint64 time, int engineId)
-{
- Q_UNUSED(engineId);
- Q_D(QmlProfilerClient);
- d->data->setTraceStartTime(time);
- emit recordingStarted();
-}
-
-void QmlProfilerClient::traceFinished(qint64 time, int engineId)
-{
- Q_UNUSED(engineId);
- Q_D(QmlProfilerClient);
- d->data->setTraceEndTime(time);
-}
-
-void QmlProfilerClient::rangeStart(QQmlProfilerDefinitions::RangeType type, qint64 startTime)
-{
- Q_D(QmlProfilerClient);
- d->rangeStartTimes[type].push(startTime);
- d->inProgressRanges |= (static_cast<qint64>(1) << type);
- ++d->rangeCount[type];
-}
-
-void QmlProfilerClient::rangeData(QQmlProfilerDefinitions::RangeType type, qint64 time,
- const QString &data)
-{
- Q_UNUSED(time);
- Q_D(QmlProfilerClient);
- int count = d->rangeCount[type];
- if (count > 0) {
- while (d->rangeDatas[type].count() < count)
- d->rangeDatas[type].push(QStringList());
- d->rangeDatas[type][count - 1] << data;
- }
-}
-
-void QmlProfilerClient::rangeLocation(QQmlProfilerDefinitions::RangeType type, qint64 time,
- const QQmlEventLocation &location)
-{
- Q_UNUSED(time);
- Q_D(QmlProfilerClient);
- if (d->rangeCount[type] > 0)
- d->rangeLocations[type].push(location);
-}
-
-void QmlProfilerClient::rangeEnd(QQmlProfilerDefinitions::RangeType type, qint64 endTime)
-{
- Q_D(QmlProfilerClient);
-
- if (d->rangeCount[type] == 0) {
- emit error(tr("Spurious range end detected."));
- return;
- }
-
- --d->rangeCount[type];
- if (d->inProgressRanges & (static_cast<qint64>(1) << type))
- d->inProgressRanges &= ~(static_cast<qint64>(1) << type);
- QStringList data = d->rangeDatas[type].count() ? d->rangeDatas[type].pop() : QStringList();
- QQmlEventLocation location = d->rangeLocations[type].count() ? d->rangeLocations[type].pop() :
- QQmlEventLocation();
- qint64 startTime = d->rangeStartTimes[type].pop();
-
- if (d->rangeCount[type] == 0 && d->rangeDatas[type].count() + d->rangeStartTimes[type].count()
- + d->rangeLocations[type].count() != 0) {
- emit error(tr("Incorrectly nested range data"));
- return;
- }
-
- d->data->addQmlEvent(type, QQmlProfilerDefinitions::QmlBinding, startTime, endTime - startTime,
- data, location);
-}
-
-void QmlProfilerClient::animationFrame(qint64 time, int frameRate, int animationCount, int threadId)
-{
- Q_D(QmlProfilerClient);
- d->data->addFrameEvent(time, frameRate, animationCount, threadId);
-}
-
-void QmlProfilerClient::sceneGraphEvent(QQmlProfilerDefinitions::SceneGraphFrameType type,
- qint64 time, qint64 numericData1, qint64 numericData2,
- qint64 numericData3, qint64 numericData4,
- qint64 numericData5)
-{
- Q_D(QmlProfilerClient);
- d->data->addSceneGraphFrameEvent(type, time, numericData1, numericData2, numericData3,
- numericData4, numericData5);
-}
-
-void QmlProfilerClient::pixmapCacheEvent(QQmlProfilerDefinitions::PixmapEventType type, qint64 time,
- const QString &url, int numericData1, int numericData2)
-{
- Q_D(QmlProfilerClient);
- d->data->addPixmapCacheEvent(type, time, url, numericData1, numericData2);
-}
-
-void QmlProfilerClient::memoryAllocation(QQmlProfilerDefinitions::MemoryType type, qint64 time,
- qint64 amount)
-{
- Q_D(QmlProfilerClient);
- d->data->addMemoryEvent(type, time, amount);
-}
-
-void QmlProfilerClient::inputEvent(QQmlProfilerDefinitions::InputEventType type, qint64 time,
- int a, int b)
-{
- Q_D(QmlProfilerClient);
- d->data->addInputEvent(type, time, a, b);
-}
-
-void QmlProfilerClient::complete()
-{
- Q_D(QmlProfilerClient);
- d->data->complete();
-}
-
#include "moc_qmlprofilerclient.cpp"
diff --git a/tools/qmlprofiler/qmlprofilerclient.h b/tools/qmlprofiler/qmlprofilerclient.h
index a04a412bb0..b9d8ce241f 100644
--- a/tools/qmlprofiler/qmlprofilerclient.h
+++ b/tools/qmlprofiler/qmlprofilerclient.h
@@ -29,9 +29,9 @@
#ifndef QMLPROFILERCLIENT_H
#define QMLPROFILERCLIENT_H
-#include <private/qqmleventlocation_p.h>
#include <private/qqmlprofilerclient_p.h>
#include <private/qqmlprofilerdefinitions_p.h>
+#include <private/qqmlprofilereventlocation_p.h>
class QmlProfilerData;
class QmlProfilerClientPrivate;
@@ -42,32 +42,13 @@ class QmlProfilerClient : public QQmlProfilerClient
public:
QmlProfilerClient(QQmlDebugConnection *connection, QmlProfilerData *data);
- void clearPendingData();
signals:
void enabledChanged(bool enabled);
- void recordingStarted();
void error(const QString &error);
private:
void stateChanged(State state) override;
-
- void traceStarted(qint64 time, int engineId) override;
- void traceFinished(qint64 time, int engineId) override;
- void rangeStart(QQmlProfilerDefinitions::RangeType type, qint64 startTime) override;
- void rangeData(QQmlProfilerDefinitions::RangeType type, qint64 time, const QString &data) override;
- void rangeLocation(QQmlProfilerDefinitions::RangeType type, qint64 time,
- const QQmlEventLocation &location) override;
- void rangeEnd(QQmlProfilerDefinitions::RangeType type, qint64 endTime) override;
- void animationFrame(qint64 time, int frameRate, int animationCount, int threadId) override;
- void sceneGraphEvent(QQmlProfilerDefinitions::SceneGraphFrameType type, qint64 time,
- qint64 numericData1, qint64 numericData2, qint64 numericData3,
- qint64 numericData4, qint64 numericData5) override;
- void pixmapCacheEvent(QQmlProfilerDefinitions::PixmapEventType type, qint64 time,
- const QString &url, int numericData1, int numericData2) override;
- void memoryAllocation(QQmlProfilerDefinitions::MemoryType type, qint64 time, qint64 amount) override;
- void inputEvent(QQmlProfilerDefinitions::InputEventType type, qint64 time, int a, int b) override;
- void complete() override;
};
#endif // QMLPROFILERCLIENT_H
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"
diff --git a/tools/qmlprofiler/qmlprofilerdata.h b/tools/qmlprofiler/qmlprofilerdata.h
index 00ef037071..2be0b73aee 100644
--- a/tools/qmlprofiler/qmlprofilerdata.h
+++ b/tools/qmlprofiler/qmlprofilerdata.h
@@ -29,13 +29,14 @@
#ifndef QMLPROFILERDATA_H
#define QMLPROFILERDATA_H
-#include <private/qqmleventlocation_p.h>
#include <private/qqmlprofilerdefinitions_p.h>
+#include <private/qqmlprofilereventlocation_p.h>
+#include <private/qqmlprofilereventreceiver_p.h>
#include <QObject>
class QmlProfilerDataPrivate;
-class QmlProfilerData : public QObject
+class QmlProfilerData : public QQmlProfilerEventReceiver
{
Q_OBJECT
public:
@@ -49,7 +50,11 @@ public:
explicit QmlProfilerData(QObject *parent = 0);
~QmlProfilerData();
- static QString getHashStringForQmlEvent(const QQmlEventLocation &location, int eventType);
+ int numLoadedEventTypes() const override;
+ void addEventType(const QQmlProfilerEventType &type) override;
+ void addEvent(const QQmlProfilerEvent &event) override;
+
+ static QString getHashStringForQmlEvent(const QQmlProfilerEventLocation &location, int eventType);
static QString qmlRangeTypeAsString(QQmlProfilerDefinitions::RangeType type);
static QString qmlMessageAsString(QQmlProfilerDefinitions::Message type);
@@ -61,18 +66,6 @@ public:
void clear();
void setTraceEndTime(qint64 time);
void setTraceStartTime(qint64 time);
- void addQmlEvent(QQmlProfilerDefinitions::RangeType type,
- QQmlProfilerDefinitions::BindingType bindingType,
- qint64 startTime, qint64 duration, const QStringList &data,
- const QQmlEventLocation &location);
- void addFrameEvent(qint64 time, int framerate, int animationcount, int threadId);
- void addSceneGraphFrameEvent(QQmlProfilerDefinitions::SceneGraphFrameType type, qint64 time,
- qint64 numericData1, qint64 numericData2, qint64 numericData3,
- qint64 numericData4, qint64 numericData5);
- void addPixmapCacheEvent(QQmlProfilerDefinitions::PixmapEventType type, qint64 time,
- const QString &location, int numericData1, int numericData2);
- void addMemoryEvent(QQmlProfilerDefinitions::MemoryType type, qint64 time, qint64 size);
- void addInputEvent(QQmlProfilerDefinitions::InputEventType type, qint64 time, int a, int b);
void complete();
bool save(const QString &filename);