aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@theqtcompany.com>2015-07-20 18:09:42 +0200
committerUlf Hermann <ulf.hermann@theqtcompany.com>2015-08-04 13:35:21 +0000
commitf8e5cfcfc26499eef30fc222e24957a753651cbc (patch)
tree23943933c7ad81d1bb535978bba39f94a1606614 /src/plugins
parent275ddd68af1881c5712848a4be9892f84b62b321 (diff)
Move profiler and engine control services into a plugin
Change-Id: I12627a07ceedea4aceafa6f0e630c0cab69d156d Reviewed-by: Simon Hausmann <simon.hausmann@theqtcompany.com>
Diffstat (limited to 'src/plugins')
-rw-r--r--src/plugins/qmltooling/qmldbg_profiler/qmldbg_profiler.pro27
-rw-r--r--src/plugins/qmltooling/qmldbg_profiler/qqmlenginecontrolservice.cpp129
-rw-r--r--src/plugins/qmltooling/qmldbg_profiler/qqmlenginecontrolservice.h90
-rw-r--r--src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp123
-rw-r--r--src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.h69
-rw-r--r--src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp426
-rw-r--r--src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.h124
-rw-r--r--src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.json3
-rw-r--r--src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservicefactory.cpp51
-rw-r--r--src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservicefactory.h62
-rw-r--r--src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp153
-rw-r--r--src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.h81
-rw-r--r--src/plugins/qmltooling/qmltooling.pro1
13 files changed, 1339 insertions, 0 deletions
diff --git a/src/plugins/qmltooling/qmldbg_profiler/qmldbg_profiler.pro b/src/plugins/qmltooling/qmldbg_profiler/qmldbg_profiler.pro
new file mode 100644
index 0000000000..dc5ee44711
--- /dev/null
+++ b/src/plugins/qmltooling/qmldbg_profiler/qmldbg_profiler.pro
@@ -0,0 +1,27 @@
+TARGET = qmldbg_profiler
+QT = qml-private core-private
+
+PLUGIN_TYPE = qmltooling
+PLUGIN_CLASS_NAME = QQmlProfilerServiceFactory
+load(qt_plugin)
+
+SOURCES += \
+ $$PWD/qqmlenginecontrolservice.cpp \
+ $$PWD/qqmlprofileradapter.cpp \
+ $$PWD/qqmlprofilerservice.cpp \
+ $$PWD/qqmlprofilerservicefactory.cpp \
+ $$PWD/qv4profileradapter.cpp
+
+HEADERS += \
+ $$PWD/qqmlenginecontrolservice.h \
+ $$PWD/qqmlprofileradapter.h \
+ $$PWD/qqmlprofilerservice.h \
+ $$PWD/qqmlprofilerservicefactory.h \
+ $$PWD/qv4profileradapter.h
+
+INCLUDEPATH += $$PWD \
+ $$PWD/../shared
+
+OTHER_FILES += \
+ $$PWD/qqmlprofilerservice.json
+
diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlenginecontrolservice.cpp b/src/plugins/qmltooling/qmldbg_profiler/qqmlenginecontrolservice.cpp
new file mode 100644
index 0000000000..4f131ac481
--- /dev/null
+++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlenginecontrolservice.cpp
@@ -0,0 +1,129 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** 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 http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://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 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmlenginecontrolservice.h"
+#include <QQmlEngine>
+
+QT_BEGIN_NAMESPACE
+
+const QString QQmlEngineControlService::s_key = QStringLiteral("EngineControl");
+
+QQmlEngineControlService::QQmlEngineControlService(QObject *parent) :
+ QQmlDebugService(s_key, 1, parent)
+{
+}
+
+void QQmlEngineControlService::messageReceived(const QByteArray &message)
+{
+ QMutexLocker lock(&dataMutex);
+ QQmlDebugStream d(message);
+ int command;
+ int engineId;
+ d >> command >> engineId;
+ QQmlEngine *engine = qobject_cast<QQmlEngine *>(objectForId(engineId));
+ if (command == StartWaitingEngine && startingEngines.contains(engine)) {
+ startingEngines.removeOne(engine);
+ emit attachedToEngine(engine);
+ } else if (command == StopWaitingEngine && stoppingEngines.contains(engine)) {
+ stoppingEngines.removeOne(engine);
+ emit detachedFromEngine(engine);
+ }
+}
+
+void QQmlEngineControlService::engineAboutToBeAdded(QQmlEngine *engine)
+{
+ QMutexLocker lock(&dataMutex);
+ if (state() == Enabled) {
+ Q_ASSERT(!stoppingEngines.contains(engine));
+ Q_ASSERT(!startingEngines.contains(engine));
+ startingEngines.append(engine);
+ sendMessage(EngineAboutToBeAdded, engine);
+ } else {
+ emit attachedToEngine(engine);
+ }
+}
+
+void QQmlEngineControlService::engineAboutToBeRemoved(QQmlEngine *engine)
+{
+ QMutexLocker lock(&dataMutex);
+ if (state() == Enabled) {
+ Q_ASSERT(!stoppingEngines.contains(engine));
+ Q_ASSERT(!startingEngines.contains(engine));
+ stoppingEngines.append(engine);
+ sendMessage(EngineAboutToBeRemoved, engine);
+ } else {
+ emit detachedFromEngine(engine);
+ }
+}
+
+void QQmlEngineControlService::engineAdded(QQmlEngine *engine)
+{
+ if (state() == Enabled) {
+ QMutexLocker lock(&dataMutex);
+ Q_ASSERT(!startingEngines.contains(engine));
+ Q_ASSERT(!stoppingEngines.contains(engine));
+ sendMessage(EngineAdded, engine);
+ }
+}
+
+void QQmlEngineControlService::engineRemoved(QQmlEngine *engine)
+{
+ if (state() == Enabled) {
+ QMutexLocker lock(&dataMutex);
+ Q_ASSERT(!startingEngines.contains(engine));
+ Q_ASSERT(!stoppingEngines.contains(engine));
+ sendMessage(EngineRemoved, engine);
+ }
+}
+
+void QQmlEngineControlService::sendMessage(QQmlEngineControlService::MessageType type, QQmlEngine *engine)
+{
+ QByteArray message;
+ QQmlDebugStream d(&message, QIODevice::WriteOnly);
+ d << type << idForObject(engine);
+ emit messageToClient(name(), message);
+}
+
+void QQmlEngineControlService::stateChanged(State)
+{
+ // We flush everything for any kind of state change, to avoid complicated timing issues.
+ QMutexLocker lock(&dataMutex);
+ foreach (QQmlEngine *engine, startingEngines)
+ emit attachedToEngine(engine);
+ startingEngines.clear();
+ foreach (QQmlEngine *engine, stoppingEngines)
+ emit detachedFromEngine(engine);
+ stoppingEngines.clear();
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlenginecontrolservice.h b/src/plugins/qmltooling/qmldbg_profiler/qqmlenginecontrolservice.h
new file mode 100644
index 0000000000..e2a93e562a
--- /dev/null
+++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlenginecontrolservice.h
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** 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 http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://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 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLENGINECONTROLSERVICE_H
+#define QQMLENGINECONTROLSERVICE_H
+
+#include <QMutex>
+#include <private/qqmldebugservice_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
+
+class QQmlEngineControlService : public QQmlDebugService
+{
+public:
+ static const QString s_key;
+
+ enum MessageType {
+ EngineAboutToBeAdded,
+ EngineAdded,
+ EngineAboutToBeRemoved,
+ EngineRemoved
+ };
+
+ enum CommandType {
+ StartWaitingEngine,
+ StopWaitingEngine
+ };
+
+ QQmlEngineControlService(QObject *parent = 0);
+
+protected:
+ QMutex dataMutex;
+ QList<QQmlEngine *> startingEngines;
+ QList<QQmlEngine *> stoppingEngines;
+
+ void messageReceived(const QByteArray &);
+ void engineAboutToBeAdded(QQmlEngine *);
+ void engineAboutToBeRemoved(QQmlEngine *);
+ void engineAdded(QQmlEngine *);
+ void engineRemoved(QQmlEngine *);
+
+ void sendMessage(MessageType type, QQmlEngine *engine);
+
+ void stateChanged(State);
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLENGINECONTROLSERVICE_H
diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp
new file mode 100644
index 0000000000..349c181d13
--- /dev/null
+++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp
@@ -0,0 +1,123 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** 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 http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://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 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmlprofileradapter.h"
+#include <private/qqmldebugserviceinterfaces_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QQmlProfilerAdapter::QQmlProfilerAdapter(QQmlProfilerService *service, QQmlEnginePrivate *engine) :
+ QQmlAbstractProfilerAdapter(service), next(0)
+{
+ engine->enableProfiler();
+ connect(this, SIGNAL(profilingEnabled(quint64)), engine->profiler, SLOT(startProfiling(quint64)));
+ connect(this, SIGNAL(profilingEnabledWhileWaiting(quint64)),
+ engine->profiler, SLOT(startProfiling(quint64)), Qt::DirectConnection);
+ connect(this, SIGNAL(profilingDisabled()), engine->profiler, SLOT(stopProfiling()));
+ connect(this, SIGNAL(profilingDisabledWhileWaiting()),
+ engine->profiler, SLOT(stopProfiling()), Qt::DirectConnection);
+ connect(this, SIGNAL(dataRequested()), engine->profiler, SLOT(reportData()));
+ connect(this, SIGNAL(referenceTimeKnown(QElapsedTimer)),
+ engine->profiler, SLOT(setTimer(QElapsedTimer)));
+ connect(engine->profiler, SIGNAL(dataReady(QVector<QQmlProfilerData>)),
+ this, SLOT(receiveData(QVector<QQmlProfilerData>)));
+}
+
+// convert to QByteArrays that can be sent to the debug client
+// use of QDataStream can skew results
+// (see tst_qqmldebugtrace::trace() benchmark)
+static void qQmlProfilerDataToByteArrays(const QQmlProfilerData *d, QList<QByteArray> &messages)
+{
+ QByteArray data;
+ Q_ASSERT_X(((d->messageType | d->detailType) & (1 << 31)) == 0, Q_FUNC_INFO,
+ "You can use at most 31 message types and 31 detail types.");
+ for (uint decodedMessageType = 0; (d->messageType >> decodedMessageType) != 0;
+ ++decodedMessageType) {
+ if ((d->messageType & (1 << decodedMessageType)) == 0)
+ continue;
+
+ for (uint decodedDetailType = 0; (d->detailType >> decodedDetailType) != 0;
+ ++decodedDetailType) {
+ if ((d->detailType & (1 << decodedDetailType)) == 0)
+ continue;
+
+ //### using QDataStream is relatively expensive
+ QQmlDebugStream ds(&data, QIODevice::WriteOnly);
+ ds << d->time << decodedMessageType << decodedDetailType;
+
+ switch (decodedMessageType) {
+ case QQmlProfilerDefinitions::RangeStart:
+ if (decodedDetailType == (int)QQmlProfilerDefinitions::Binding)
+ ds << QQmlProfilerDefinitions::QmlBinding;
+ break;
+ case QQmlProfilerDefinitions::RangeData:
+ ds << d->detailString;
+ break;
+ case QQmlProfilerDefinitions::RangeLocation:
+ ds << (d->detailUrl.isEmpty() ? d->detailString : d->detailUrl.toString()) << d->x
+ << d->y;
+ break;
+ case QQmlProfilerDefinitions::RangeEnd: break;
+ default:
+ Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid message type.");
+ break;
+ }
+ messages << data;
+ data.clear();
+ }
+ }
+}
+
+qint64 QQmlProfilerAdapter::sendMessages(qint64 until, QList<QByteArray> &messages)
+{
+ while (next != data.length()) {
+ if (data[next].time > until)
+ return data[next].time;
+ qQmlProfilerDataToByteArrays(&(data[next++]), messages);
+ }
+
+ next = 0;
+ data.clear();
+ return -1;
+}
+
+void QQmlProfilerAdapter::receiveData(const QVector<QQmlProfilerData> &new_data)
+{
+ if (data.isEmpty())
+ data = new_data;
+ else
+ data.append(new_data);
+ service->dataReady(this);
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.h b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.h
new file mode 100644
index 0000000000..eceb58ce3a
--- /dev/null
+++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** 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 http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://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 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// 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.
+//
+
+#ifndef QQMLPROFILERADAPTER_H
+#define QQMLPROFILERADAPTER_H
+
+#include <private/qqmlabstractprofileradapter_p.h>
+#include <private/qqmlprofiler_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlProfilerAdapter : public QQmlAbstractProfilerAdapter {
+ Q_OBJECT
+public:
+ QQmlProfilerAdapter(QQmlProfilerService *service, QQmlEnginePrivate *engine);
+ qint64 sendMessages(qint64 until, QList<QByteArray> &messages);
+
+public slots:
+ void receiveData(const QVector<QQmlProfilerData> &new_data);
+
+private:
+ QVector<QQmlProfilerData> data;
+ int next;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLPROFILERADAPTER_H
diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp
new file mode 100644
index 0000000000..65b99ef7ca
--- /dev/null
+++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp
@@ -0,0 +1,426 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** 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 http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://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 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmlprofilerservice.h"
+#include "qv4profileradapter.h"
+#include "qqmlprofileradapter.h"
+#include "qqmlprofilerservicefactory.h"
+#include <private/qqmlengine_p.h>
+
+#include <QtCore/qdatastream.h>
+#include <QtCore/qurl.h>
+#include <QtCore/qtimer.h>
+#include <QtCore/qthread.h>
+#include <QtCore/qcoreapplication.h>
+
+QT_BEGIN_NAMESPACE
+
+QQmlProfilerServiceImpl::QQmlProfilerServiceImpl(QObject *parent) :
+ QQmlConfigurableDebugService<QQmlProfilerService>(1, parent),
+ m_waitingForStop(false)
+{
+ m_timer.start();
+}
+
+QQmlProfilerServiceImpl::~QQmlProfilerServiceImpl()
+{
+ // No need to lock here. If any engine or global profiler is still trying to register at this
+ // point we have a nasty bug anyway.
+ qDeleteAll(m_engineProfilers.values());
+ qDeleteAll(m_globalProfilers);
+}
+
+void QQmlProfilerServiceImpl::dataReady(QQmlAbstractProfilerAdapter *profiler)
+{
+ QMutexLocker lock(&m_configMutex);
+ bool dataComplete = true;
+ for (QMultiMap<qint64, QQmlAbstractProfilerAdapter *>::iterator i(m_startTimes.begin()); i != m_startTimes.end();) {
+ if (i.value() == profiler) {
+ m_startTimes.erase(i++);
+ } else {
+ if (i.key() == -1)
+ dataComplete = false;
+ ++i;
+ }
+ }
+ m_startTimes.insert(0, profiler);
+ if (dataComplete) {
+ QList<QQmlEngine *> enginesToRelease;
+ foreach (QQmlEngine *engine, m_stoppingEngines) {
+ foreach (QQmlAbstractProfilerAdapter *engineProfiler, m_engineProfilers.values(engine)) {
+ if (m_startTimes.values().contains(engineProfiler)) {
+ enginesToRelease.append(engine);
+ break;
+ }
+ }
+ }
+ sendMessages();
+ foreach (QQmlEngine *engine, enginesToRelease) {
+ m_stoppingEngines.removeOne(engine);
+ emit detachedFromEngine(engine);
+ }
+ }
+}
+
+void QQmlProfilerServiceImpl::engineAboutToBeAdded(QQmlEngine *engine)
+{
+ Q_ASSERT_X(QThread::currentThread() == engine->thread(), Q_FUNC_INFO,
+ "QML profilers have to be added from the engine thread");
+
+ QMutexLocker lock(&m_configMutex);
+ QQmlProfilerAdapter *qmlAdapter = new QQmlProfilerAdapter(this, QQmlEnginePrivate::get(engine));
+ QV4ProfilerAdapter *v4Adapter = new QV4ProfilerAdapter(this, QV8Engine::getV4(engine->handle()));
+ addEngineProfiler(qmlAdapter, engine);
+ addEngineProfiler(v4Adapter, engine);
+ QQmlConfigurableDebugService<QQmlProfilerService>::engineAboutToBeAdded(engine);
+}
+
+void QQmlProfilerServiceImpl::engineAdded(QQmlEngine *engine)
+{
+ Q_ASSERT_X(QThread::currentThread() == engine->thread(), Q_FUNC_INFO,
+ "QML profilers have to be added from the engine thread");
+
+ QMutexLocker lock(&m_configMutex);
+ foreach (QQmlAbstractProfilerAdapter *profiler, m_engineProfilers.values(engine))
+ profiler->stopWaiting();
+}
+
+void QQmlProfilerServiceImpl::engineAboutToBeRemoved(QQmlEngine *engine)
+{
+ Q_ASSERT_X(QThread::currentThread() == engine->thread(), Q_FUNC_INFO,
+ "QML profilers have to be removed from the engine thread");
+
+ QMutexLocker lock(&m_configMutex);
+ bool isRunning = false;
+ foreach (QQmlAbstractProfilerAdapter *profiler, m_engineProfilers.values(engine)) {
+ if (profiler->isRunning())
+ isRunning = true;
+ profiler->startWaiting();
+ }
+ if (isRunning) {
+ m_stoppingEngines.append(engine);
+ stopProfiling(engine);
+ } else {
+ emit detachedFromEngine(engine);
+ }
+}
+
+void QQmlProfilerServiceImpl::engineRemoved(QQmlEngine *engine)
+{
+ Q_ASSERT_X(QThread::currentThread() == engine->thread(), Q_FUNC_INFO,
+ "QML profilers have to be removed from the engine thread");
+
+ QMutexLocker lock(&m_configMutex);
+ foreach (QQmlAbstractProfilerAdapter *profiler, m_engineProfilers.values(engine)) {
+ removeProfilerFromStartTimes(profiler);
+ delete profiler;
+ }
+ m_engineProfilers.remove(engine);
+}
+
+void QQmlProfilerServiceImpl::addEngineProfiler(QQmlAbstractProfilerAdapter *profiler, QQmlEngine *engine)
+{
+ profiler->moveToThread(thread());
+ profiler->synchronize(m_timer);
+ m_engineProfilers.insert(engine, profiler);
+}
+
+void QQmlProfilerServiceImpl::addGlobalProfiler(QQmlAbstractProfilerAdapter *profiler)
+{
+ QMutexLocker lock(&m_configMutex);
+ profiler->synchronize(m_timer);
+ m_globalProfilers.append(profiler);
+ // Global profiler, not connected to a specific engine.
+ // Global profilers are started whenever any engine profiler is started and stopped when
+ // all engine profilers are stopped.
+ quint64 features = 0;
+ foreach (QQmlAbstractProfilerAdapter *engineProfiler, m_engineProfilers)
+ features |= engineProfiler->features();
+
+ if (features != 0)
+ profiler->startProfiling(features);
+}
+
+void QQmlProfilerServiceImpl::removeGlobalProfiler(QQmlAbstractProfilerAdapter *profiler)
+{
+ QMutexLocker lock(&m_configMutex);
+ removeProfilerFromStartTimes(profiler);
+ m_globalProfilers.removeOne(profiler);
+ delete profiler;
+}
+
+void QQmlProfilerServiceImpl::removeProfilerFromStartTimes(const QQmlAbstractProfilerAdapter *profiler)
+{
+ for (QMultiMap<qint64, QQmlAbstractProfilerAdapter *>::iterator i(m_startTimes.begin());
+ i != m_startTimes.end();) {
+ if (i.value() == profiler) {
+ m_startTimes.erase(i++);
+ break;
+ } else {
+ ++i;
+ }
+ }
+}
+
+/*!
+ * Start profiling the given \a engine. If \a engine is 0, start all engine profilers that aren't
+ * currently running.
+ *
+ * If any engine profiler is started like that also start all global profilers.
+ */
+void QQmlProfilerServiceImpl::startProfiling(QQmlEngine *engine, quint64 features)
+{
+ QMutexLocker lock(&m_configMutex);
+
+ QByteArray message;
+ QQmlDebugStream d(&message, QIODevice::WriteOnly);
+
+ d << m_timer.nsecsElapsed() << (int)Event << (int)StartTrace;
+ bool startedAny = false;
+ if (engine != 0) {
+ foreach (QQmlAbstractProfilerAdapter *profiler, m_engineProfilers.values(engine)) {
+ if (!profiler->isRunning()) {
+ profiler->startProfiling(features);
+ startedAny = true;
+ }
+ }
+ if (startedAny)
+ d << idForObject(engine);
+ } else {
+ QSet<QQmlEngine *> engines;
+ for (QMultiHash<QQmlEngine *, QQmlAbstractProfilerAdapter *>::iterator i(m_engineProfilers.begin());
+ i != m_engineProfilers.end(); ++i) {
+ if (!i.value()->isRunning()) {
+ engines << i.key();
+ i.value()->startProfiling(features);
+ startedAny = true;
+ }
+ }
+ foreach (QQmlEngine *profiledEngine, engines)
+ d << idForObject(profiledEngine);
+ }
+
+ if (startedAny) {
+ foreach (QQmlAbstractProfilerAdapter *profiler, m_globalProfilers) {
+ if (!profiler->isRunning())
+ profiler->startProfiling(features);
+ }
+
+ emit startFlushTimer();
+ }
+
+ emit messageToClient(name(), message);
+}
+
+/*!
+ * Stop profiling the given \a engine. If \a engine is 0, stop all currently running engine
+ * profilers.
+ *
+ * If afterwards no more engine profilers are running, also stop all global profilers. Otherwise
+ * only make them report their data.
+ */
+void QQmlProfilerServiceImpl::stopProfiling(QQmlEngine *engine)
+{
+ QMutexLocker lock(&m_configMutex);
+ QList<QQmlAbstractProfilerAdapter *> stopping;
+ QList<QQmlAbstractProfilerAdapter *> reporting;
+
+ bool stillRunning = false;
+ for (QMultiHash<QQmlEngine *, QQmlAbstractProfilerAdapter *>::iterator i(m_engineProfilers.begin());
+ i != m_engineProfilers.end(); ++i) {
+ if (i.value()->isRunning()) {
+ if (engine == 0 || i.key() == engine) {
+ m_startTimes.insert(-1, i.value());
+ stopping << i.value();
+ } else {
+ stillRunning = true;
+ }
+ }
+ }
+
+ if (stopping.isEmpty())
+ return;
+
+ foreach (QQmlAbstractProfilerAdapter *profiler, m_globalProfilers) {
+ if (!profiler->isRunning())
+ continue;
+ m_startTimes.insert(-1, profiler);
+ if (stillRunning) {
+ reporting << profiler;
+ } else {
+ stopping << profiler;
+ }
+ }
+
+ emit stopFlushTimer();
+ m_waitingForStop = true;
+
+ foreach (QQmlAbstractProfilerAdapter *profiler, reporting)
+ profiler->reportData();
+
+ foreach (QQmlAbstractProfilerAdapter *profiler, stopping)
+ profiler->stopProfiling();
+}
+
+/*
+ Send the queued up messages.
+*/
+void QQmlProfilerServiceImpl::sendMessages()
+{
+ QList<QByteArray> messages;
+
+ QByteArray data;
+
+ if (m_waitingForStop) {
+ QQmlDebugStream traceEnd(&data, QIODevice::WriteOnly);
+ traceEnd << m_timer.nsecsElapsed() << (int)Event << (int)EndTrace;
+
+ QSet<QQmlEngine *> seen;
+ foreach (QQmlAbstractProfilerAdapter *profiler, m_startTimes) {
+ for (QMultiHash<QQmlEngine *, QQmlAbstractProfilerAdapter *>::iterator i(m_engineProfilers.begin());
+ i != m_engineProfilers.end(); ++i) {
+ if (i.value() == profiler && !seen.contains(i.key())) {
+ seen << i.key();
+ traceEnd << idForObject(i.key());
+ }
+ }
+ }
+ }
+
+ while (!m_startTimes.empty()) {
+ QQmlAbstractProfilerAdapter *first = m_startTimes.begin().value();
+ m_startTimes.erase(m_startTimes.begin());
+ if (!m_startTimes.empty()) {
+ qint64 next = first->sendMessages(m_startTimes.begin().key(), messages);
+ if (next != -1)
+ m_startTimes.insert(next, first);
+ } else {
+ first->sendMessages(std::numeric_limits<qint64>::max(), messages);
+ }
+ }
+
+ if (m_waitingForStop) {
+ //indicate completion
+ messages << data;
+ data.clear();
+
+ QQmlDebugStream ds(&data, QIODevice::WriteOnly);
+ ds << (qint64)-1 << (int)Complete;
+ messages << data;
+ m_waitingForStop = false;
+ }
+
+ emit messagesToClient(name(), messages);
+
+ // Restart flushing if any profilers are still running
+ foreach (const QQmlAbstractProfilerAdapter *profiler, m_engineProfilers) {
+ if (profiler->isRunning()) {
+ emit startFlushTimer();
+ break;
+ }
+ }
+}
+
+void QQmlProfilerServiceImpl::stateAboutToBeChanged(QQmlDebugService::State newState)
+{
+ QMutexLocker lock(&m_configMutex);
+
+ if (state() == newState)
+ return;
+
+ // Stop all profiling and send the data before we get disabled.
+ if (newState != Enabled) {
+ foreach (QQmlEngine *engine, m_engineProfilers.keys())
+ stopProfiling(engine);
+ }
+}
+
+void QQmlProfilerServiceImpl::messageReceived(const QByteArray &message)
+{
+ QMutexLocker lock(&m_configMutex);
+
+ QByteArray rwData = message;
+ QQmlDebugStream stream(&rwData, QIODevice::ReadOnly);
+
+ int engineId = -1;
+ quint64 features = std::numeric_limits<quint64>::max();
+ bool enabled;
+ uint flushInterval = 0;
+ stream >> enabled;
+ if (!stream.atEnd())
+ stream >> engineId;
+ if (!stream.atEnd())
+ stream >> features;
+ if (!stream.atEnd()) {
+ stream >> flushInterval;
+ m_flushTimer.setInterval(flushInterval);
+ if (flushInterval > 0) {
+ connect(&m_flushTimer, SIGNAL(timeout()), this, SLOT(flush()));
+ connect(this, SIGNAL(startFlushTimer()), &m_flushTimer, SLOT(start()));
+ connect(this, SIGNAL(stopFlushTimer()), &m_flushTimer, SLOT(stop()));
+ } else {
+ disconnect(&m_flushTimer, SIGNAL(timeout()), this, SLOT(flush()));
+ disconnect(this, SIGNAL(startFlushTimer()), &m_flushTimer, SLOT(start()));
+ disconnect(this, SIGNAL(stopFlushTimer()), &m_flushTimer, SLOT(stop()));
+ }
+ }
+
+ // If engineId == -1 objectForId() and then the cast will return 0.
+ if (enabled)
+ startProfiling(qobject_cast<QQmlEngine *>(objectForId(engineId)), features);
+ else
+ stopProfiling(qobject_cast<QQmlEngine *>(objectForId(engineId)));
+
+ stopWaiting();
+}
+
+void QQmlProfilerServiceImpl::flush()
+{
+ QMutexLocker lock(&m_configMutex);
+
+ foreach (QQmlAbstractProfilerAdapter *profiler, m_engineProfilers) {
+ if (profiler->isRunning()) {
+ m_startTimes.insert(-1, profiler);
+ profiler->reportData();
+ }
+ }
+
+ foreach (QQmlAbstractProfilerAdapter *profiler, m_globalProfilers) {
+ if (profiler->isRunning()) {
+ m_startTimes.insert(-1, profiler);
+ profiler->reportData();
+ }
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.h b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.h
new file mode 100644
index 0000000000..da96f58ce6
--- /dev/null
+++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.h
@@ -0,0 +1,124 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** 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 http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://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 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLPROFILERSERVICE_P_H
+#define QQMLPROFILERSERVICE_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.
+//
+
+#include <private/qqmlconfigurabledebugservice_p.h>
+#include <private/qqmldebugserviceinterfaces_p.h>
+#include <private/qqmlprofilerdefinitions_p.h>
+#include <private/qqmlabstractprofileradapter_p.h>
+#include <private/qqmlboundsignal_p.h>
+
+#include <QtCore/qelapsedtimer.h>
+#include <QtCore/qmetaobject.h>
+#include <QtCore/qmutex.h>
+#include <QtCore/qvector.h>
+#include <QtCore/qstringbuilder.h>
+#include <QtCore/qwaitcondition.h>
+#include <QtCore/qtimer.h>
+
+#include <limits>
+
+QT_BEGIN_NAMESPACE
+
+class QUrl;
+class QQmlEngine;
+
+
+class QQmlProfilerServiceImpl :
+ public QQmlConfigurableDebugService<QQmlProfilerService>,
+ public QQmlProfilerDefinitions
+{
+ Q_OBJECT
+public:
+
+ void engineAboutToBeAdded(QQmlEngine *engine);
+ void engineAboutToBeRemoved(QQmlEngine *engine);
+ void engineAdded(QQmlEngine *engine);
+ void engineRemoved(QQmlEngine *engine);
+
+ void addGlobalProfiler(QQmlAbstractProfilerAdapter *profiler);
+ void removeGlobalProfiler(QQmlAbstractProfilerAdapter *profiler);
+
+ void startProfiling(QQmlEngine *engine, quint64 features = std::numeric_limits<quint64>::max());
+ void stopProfiling(QQmlEngine *engine);
+
+ QQmlProfilerServiceImpl(QObject *parent = 0);
+ ~QQmlProfilerServiceImpl();
+
+ void dataReady(QQmlAbstractProfilerAdapter *profiler);
+
+signals:
+ void startFlushTimer();
+ void stopFlushTimer();
+
+private slots:
+ void flush();
+
+protected:
+ virtual void stateAboutToBeChanged(State state);
+ virtual void messageReceived(const QByteArray &);
+
+private:
+ friend class QQmlProfilerServiceFactory;
+
+ void sendMessages();
+ void addEngineProfiler(QQmlAbstractProfilerAdapter *profiler, QQmlEngine *engine);
+ void removeProfilerFromStartTimes(const QQmlAbstractProfilerAdapter *profiler);
+
+ QElapsedTimer m_timer;
+ QTimer m_flushTimer;
+ bool m_waitingForStop;
+
+ QList<QQmlAbstractProfilerAdapter *> m_globalProfilers;
+ QMultiHash<QQmlEngine *, QQmlAbstractProfilerAdapter *> m_engineProfilers;
+ QList<QQmlEngine *> m_stoppingEngines;
+ QMultiMap<qint64, QQmlAbstractProfilerAdapter *> m_startTimes;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLPROFILERSERVICE_P_H
+
diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.json b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.json
new file mode 100644
index 0000000000..ec1ec364da
--- /dev/null
+++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.json
@@ -0,0 +1,3 @@
+{
+ "Keys": [ "CanvasFrameRate", "EngineControl" ]
+}
diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservicefactory.cpp b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservicefactory.cpp
new file mode 100644
index 0000000000..83c2075246
--- /dev/null
+++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservicefactory.cpp
@@ -0,0 +1,51 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** 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 http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://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 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmlprofilerservice.h"
+#include "qqmlenginecontrolservice.h"
+#include "qqmlprofilerservicefactory.h"
+
+QT_BEGIN_NAMESPACE
+
+QQmlDebugService *QQmlProfilerServiceFactory::create(const QString &key)
+{
+ if (key == QQmlProfilerServiceImpl::s_key)
+ return new QQmlProfilerServiceImpl(this);
+
+ if (key == QQmlEngineControlService::s_key)
+ return new QQmlEngineControlService(this);
+
+ return 0;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservicefactory.h b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservicefactory.h
new file mode 100644
index 0000000000..b570136e5b
--- /dev/null
+++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservicefactory.h
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** 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 http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://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 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLPROFILERSERVICE_H
+#define QQMLPROFILERSERVICE_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.
+//
+
+#include <private/qqmldebugservicefactory_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlProfilerServiceFactory : public QQmlDebugServiceFactory
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QQmlDebugServiceFactory_iid FILE "qqmlprofilerservice.json")
+public:
+ QQmlDebugService *create(const QString &key);
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLPROFILERSERVICE_H
diff --git a/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp b/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp
new file mode 100644
index 0000000000..24e01f4c68
--- /dev/null
+++ b/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp
@@ -0,0 +1,153 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** 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 http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://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 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4profileradapter.h"
+#include "qqmlprofilerservice.h"
+
+QT_BEGIN_NAMESPACE
+
+QV4ProfilerAdapter::QV4ProfilerAdapter(QQmlProfilerService *service, QV4::ExecutionEngine *engine) :
+ QQmlAbstractProfilerAdapter(service), dataPos(0), memoryPos(0)
+{
+ engine->enableProfiler();
+ connect(this, SIGNAL(profilingEnabled(quint64)),
+ engine->profiler, SLOT(startProfiling(quint64)));
+ connect(this, SIGNAL(profilingEnabledWhileWaiting(quint64)),
+ engine->profiler, SLOT(startProfiling(quint64)), Qt::DirectConnection);
+ connect(this, SIGNAL(profilingDisabled()), engine->profiler, SLOT(stopProfiling()));
+ connect(this, SIGNAL(profilingDisabledWhileWaiting()), engine->profiler, SLOT(stopProfiling()),
+ Qt::DirectConnection);
+ connect(this, SIGNAL(dataRequested()), engine->profiler, SLOT(reportData()));
+ connect(this, SIGNAL(referenceTimeKnown(QElapsedTimer)),
+ engine->profiler, SLOT(setTimer(QElapsedTimer)));
+ connect(engine->profiler, SIGNAL(dataReady(QVector<QV4::Profiling::FunctionCallProperties>,
+ QVector<QV4::Profiling::MemoryAllocationProperties>)),
+ this, SLOT(receiveData(QVector<QV4::Profiling::FunctionCallProperties>,
+ QVector<QV4::Profiling::MemoryAllocationProperties>)));
+}
+
+qint64 QV4ProfilerAdapter::appendMemoryEvents(qint64 until, QList<QByteArray> &messages)
+{
+ QByteArray message;
+ while (memory_data.length() > memoryPos && memory_data[memoryPos].timestamp <= until) {
+ QQmlDebugStream d(&message, QIODevice::WriteOnly);
+ QV4::Profiling::MemoryAllocationProperties &props = memory_data[memoryPos];
+ d << props.timestamp << MemoryAllocation << props.type << props.size;
+ ++memoryPos;
+ messages.append(message);
+ }
+ return memory_data.length() == memoryPos ? -1 : memory_data[memoryPos].timestamp;
+}
+
+qint64 QV4ProfilerAdapter::finalizeMessages(qint64 until, QList<QByteArray> &messages,
+ qint64 callNext)
+{
+ if (callNext == -1) {
+ data.clear();
+ dataPos = 0;
+ }
+
+ qint64 memoryNext = appendMemoryEvents(until, messages);
+
+ if (memoryNext == -1) {
+ memory_data.clear();
+ memoryPos = 0;
+ return callNext;
+ }
+
+ return callNext == -1 ? memoryNext : qMin(callNext, memoryNext);
+}
+
+qint64 QV4ProfilerAdapter::sendMessages(qint64 until, QList<QByteArray> &messages)
+{
+ QByteArray message;
+ while (true) {
+ while (!stack.isEmpty() && (dataPos == data.length() ||
+ stack.top() <= data[dataPos].start)) {
+ if (stack.top() > until)
+ return finalizeMessages(until, messages, stack.top());
+
+ appendMemoryEvents(stack.top(), messages);
+ QQmlDebugStream d(&message, QIODevice::WriteOnly);
+ d << stack.pop() << RangeEnd << Javascript;
+ messages.append(message);
+ }
+ while (dataPos != data.length() && (stack.empty() || data[dataPos].start < stack.top())) {
+ const QV4::Profiling::FunctionCallProperties &props = data[dataPos];
+ if (props.start > until)
+ return finalizeMessages(until, messages, props.start);
+
+ appendMemoryEvents(props.start, messages);
+
+ QQmlDebugStream d_start(&message, QIODevice::WriteOnly);
+ d_start << props.start << RangeStart << Javascript;
+ messages.push_back(message);
+ message.clear();
+ QQmlDebugStream d_location(&message, QIODevice::WriteOnly);
+ d_location << props.start << RangeLocation << Javascript << props.file << props.line
+ << props.column;
+ messages.push_back(message);
+ message.clear();
+ QQmlDebugStream d_data(&message, QIODevice::WriteOnly);
+ d_data << props.start << RangeData << Javascript << props.name;
+ messages.push_back(message);
+ message.clear();
+ stack.push(props.end);
+ ++dataPos;
+ }
+ if (stack.empty() && dataPos == data.length())
+ return finalizeMessages(until, messages, -1);
+ }
+}
+
+void QV4ProfilerAdapter::receiveData(
+ const QVector<QV4::Profiling::FunctionCallProperties> &new_data,
+ const QVector<QV4::Profiling::MemoryAllocationProperties> &new_memory_data)
+{
+ // In rare cases it could be that another flush or stop event is processed while data from
+ // the previous one is still pending. In that case we just append the data.
+
+ if (data.isEmpty())
+ data = new_data;
+ else
+ data.append(new_data);
+
+ if (memory_data.isEmpty())
+ memory_data = new_memory_data;
+ else
+ memory_data.append(new_memory_data);
+
+ service->dataReady(this);
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.h b/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.h
new file mode 100644
index 0000000000..cea3da72e3
--- /dev/null
+++ b/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** 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 http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://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 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4PROFILERADAPTER_P_H
+#define QV4PROFILERADAPTER_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.
+//
+
+#include <private/qv4profiling_p.h>
+#include <private/qqmlabstractprofileradapter_p.h>
+
+#include <QStack>
+#include <QList>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlProfilerService;
+class QV4ProfilerAdapter : public QQmlAbstractProfilerAdapter {
+ Q_OBJECT
+
+public:
+ QV4ProfilerAdapter(QQmlProfilerService *service, QV4::ExecutionEngine *engine);
+
+ virtual qint64 sendMessages(qint64 until, QList<QByteArray> &messages);
+
+public slots:
+ void receiveData(const QVector<QV4::Profiling::FunctionCallProperties> &,
+ const QVector<QV4::Profiling::MemoryAllocationProperties> &);
+
+private:
+ QVector<QV4::Profiling::FunctionCallProperties> data;
+ QVector<QV4::Profiling::MemoryAllocationProperties> memory_data;
+ int dataPos;
+ int memoryPos;
+ QStack<qint64> stack;
+ qint64 appendMemoryEvents(qint64 until, QList<QByteArray> &messages);
+ qint64 finalizeMessages(qint64 until, QList<QByteArray> &messages, qint64 callNext);
+};
+
+QT_END_NAMESPACE
+
+#endif // QV4PROFILERADAPTER_P_H
diff --git a/src/plugins/qmltooling/qmltooling.pro b/src/plugins/qmltooling/qmltooling.pro
index 6af954ccf1..cfcf631f1a 100644
--- a/src/plugins/qmltooling/qmltooling.pro
+++ b/src/plugins/qmltooling/qmltooling.pro
@@ -2,6 +2,7 @@ TEMPLATE = subdirs
SUBDIRS += \
qmldbg_local \
+ qmldbg_profiler \
qmldbg_server \
qmldbg_tcp