aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@digia.com>2014-01-23 17:18:05 +0100
committerThe Qt Project <gerrit-noreply@qt-project.org>2014-02-13 08:18:42 +0100
commiteeaba26596d447c531dfac9d6e6bf5cfe4537813 (patch)
tree90cb8aa8a60c26ff7bf6ad85242069185453931e /src
parent68dabbf62b867e092a2860e5cde1813768176a3e (diff)
Infrastructure for supporting multiple profilers in qqmlprofilerservice
The concepts of "global" and "engine" profilers are introduced. Profilers can either be attached to specific QML engines and run in their threads or they can be unspecific global profilers running in arbitrary threads. Change-Id: I3862fc65c07ccb33a1ca08cd2425e4079d3ffc02 Reviewed-by: Kai Koehne <kai.koehne@digia.com>
Diffstat (limited to 'src')
-rw-r--r--src/qml/debugger/debugger.pri7
-rw-r--r--src/qml/debugger/qqmlabstractprofileradapter.cpp109
-rw-r--r--src/qml/debugger/qqmlabstractprofileradapter_p.h108
-rw-r--r--src/qml/debugger/qqmlprofilerdefinitions_p.h133
-rw-r--r--src/qml/debugger/qqmlprofilerservice.cpp287
-rw-r--r--src/qml/debugger/qqmlprofilerservice_p.h111
-rw-r--r--src/qml/qml/v8/qqmlbuiltinfunctions.cpp28
7 files changed, 646 insertions, 137 deletions
diff --git a/src/qml/debugger/debugger.pri b/src/qml/debugger/debugger.pri
index e9f1d6f5fe..2f9844ae7a 100644
--- a/src/qml/debugger/debugger.pri
+++ b/src/qml/debugger/debugger.pri
@@ -8,7 +8,8 @@ SOURCES += \
$$PWD/qdebugmessageservice.cpp \
$$PWD/qv4debugservice.cpp \
$$PWD/qqmlconfigurabledebugservice.cpp \
- $$PWD/qqmlenginecontrolservice.cpp
+ $$PWD/qqmlenginecontrolservice.cpp \
+ $$PWD/qqmlabstractprofileradapter.cpp
HEADERS += \
$$PWD/qqmldebugservice_p.h \
@@ -26,4 +27,6 @@ HEADERS += \
$$PWD/qv4debugservice_p.h \
$$PWD/qqmlconfigurabledebugservice_p.h \
$$PWD/qqmlconfigurabledebugservice_p_p.h \
- $$PWD/qqmlenginecontrolservice_p.h
+ $$PWD/qqmlenginecontrolservice_p.h \
+ $$PWD/qqmlprofilerdefinitions_p.h \
+ $$PWD/qqmlabstractprofileradapter_p.h
diff --git a/src/qml/debugger/qqmlabstractprofileradapter.cpp b/src/qml/debugger/qqmlabstractprofileradapter.cpp
new file mode 100644
index 0000000000..b5962657b1
--- /dev/null
+++ b/src/qml/debugger/qqmlabstractprofileradapter.cpp
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmlabstractprofileradapter_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ * \class QQmlAbstractProfilerAdapter
+ * Abstract base class for all adapters between profilers and the QQmlProfilerService. Adapters have
+ * to retrieve profiler-specific data and convert it to the format sent over the wire. Adapters must
+ * live in the QDebugServer thread but the actual profilers can live in different threads. The
+ * recommended way to deal with this is passing the profiling data through a signal/slot connection.
+ */
+
+/*!
+ * \fn void QQmlAbstractProfilerAdapter::dataReady(QQmlAbstractProfilerAdapter *)
+ * Signals that data has been extracted from the profiler and is readily available in the adapter.
+ * The primary data representation is in satellite's format. It should be transformed and deleted
+ * on the fly with sendMessages.
+ */
+
+/*!
+ * \fn void QQmlAbstractProfilerAdapter::dataRequested()
+ * Signals that data has been requested by the \c QQmlProfilerService. This signal should be
+ * connected to a slot in the profiler and the profiler should then transfer its currently available
+ * profiling data to the adapter as soon as possible.
+ */
+
+/*!
+ * \fn qint64 QQmlAbstractProfilerAdapter::sendMessages(qint64 until, QList<QByteArray> &messages)
+ * Append the messages up to the timestamp \a until, chronologically sorted, to \a messages. Keep
+ * track of the messages already sent and with each subsequent call to this method start with the
+ * first one not yet sent. Messages that have been sent can be deleted. When new data from the
+ * profiler arrives the information about the last sent message must be reset. Return the timestamp
+ * of the next message after \a until or \c -1 if there is no such message.
+ * The profiler service keeps a list of adapters, sorted by time of next message and keeps querying
+ * the first one to send messages up to the time of the second one. Like that we get chronologically
+ * sorted messages and can occasionally post the messages to exploit parallelism and save memory.
+ */
+
+/*!
+ * \fn qint64 QQmlAbstractProfilerAdapter::startProfiling()
+ * Emits either \c profilingEnabled() or \c profilingEnabledWhileWaiting(), depending on \c waiting.
+ * If the profiler's thread is waiting for an initial start signal we can emit the signal over a
+ * \c Qt::DirectConnection to avoid the delay of the event loop.
+ */
+void QQmlAbstractProfilerAdapter::startProfiling()
+{
+ if (waiting)
+ emit profilingEnabledWhileWaiting();
+ else
+ emit profilingEnabled();
+ running = true;
+}
+
+/*!
+ * \fn qint64 QQmlAbstractProfilerAdapter::stopProfiling()
+ * Emits either \c profilingDisabled() or \c profilingDisabledWhileWaiting(), depending on
+ * \c waiting. If the profiler's thread is waiting for an initial start signal we can emit the
+ * signal over a \c Qt::DirectConnection to avoid the delay of the event loop.
+ */
+void QQmlAbstractProfilerAdapter::stopProfiling() {
+ if (waiting)
+ emit profilingDisabledWhileWaiting();
+ else
+ emit profilingDisabled();
+ running = false;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/debugger/qqmlabstractprofileradapter_p.h b/src/qml/debugger/qqmlabstractprofileradapter_p.h
new file mode 100644
index 0000000000..89b241e8d6
--- /dev/null
+++ b/src/qml/debugger/qqmlabstractprofileradapter_p.h
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLABSTRACTPROFILERADAPTER_P_H
+#define QQMLABSTRACTPROFILERADAPTER_P_H
+
+#include <private/qtqmlglobal_p.h>
+#include <private/qqmlprofilerdefinitions_p.h>
+
+#include <QtCore/QObject>
+#include <QtCore/QElapsedTimer>
+
+//
+// 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 QQmlProfilerService;
+class Q_QML_PRIVATE_EXPORT QQmlAbstractProfilerAdapter : public QObject, public QQmlProfilerDefinitions {
+ Q_OBJECT
+
+public:
+ QQmlAbstractProfilerAdapter(QQmlProfilerService *service) :
+ service(service), waiting(true), running(false) {}
+ virtual ~QQmlAbstractProfilerAdapter() {}
+
+ virtual qint64 sendMessages(qint64 until, QList<QByteArray> &messages) = 0;
+
+ void startProfiling();
+
+ void stopProfiling();
+
+ void reportData() { emit dataRequested(); }
+
+ void stopWaiting() { waiting = false; }
+ void startWaiting() { waiting = true; }
+
+ bool isRunning() const { return running; }
+
+ void synchronize(const QElapsedTimer &t) { emit referenceTimeKnown(t); }
+
+signals:
+ void profilingEnabled();
+ void profilingEnabledWhileWaiting();
+
+ void profilingDisabled();
+ void profilingDisabledWhileWaiting();
+
+ void dataRequested();
+ void referenceTimeKnown(const QElapsedTimer &timer);
+
+protected:
+ QQmlProfilerService *service;
+
+private:
+ bool waiting;
+ bool running;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLABSTRACTPROFILERADAPTER_P_H
diff --git a/src/qml/debugger/qqmlprofilerdefinitions_p.h b/src/qml/debugger/qqmlprofilerdefinitions_p.h
new file mode 100644
index 0000000000..9452260ce4
--- /dev/null
+++ b/src/qml/debugger/qqmlprofilerdefinitions_p.h
@@ -0,0 +1,133 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLPROFILERDEFINITIONS_P_H
+#define QQMLPROFILERDEFINITIONS_P_H
+
+#include <private/qtqmlglobal_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 QQmlProfilerDefinitions {
+ enum Message {
+ Event,
+ RangeStart,
+ RangeData,
+ RangeLocation,
+ RangeEnd,
+ Complete, // end of transmission
+ PixmapCacheEvent,
+ SceneGraphFrame,
+
+ MaximumMessage
+ };
+
+ enum EventType {
+ FramePaint,
+ Mouse,
+ Key,
+ AnimationFrame,
+ EndTrace,
+ StartTrace,
+
+ MaximumEventType
+ };
+
+ enum RangeType {
+ Painting,
+ Compiling,
+ Creating,
+ Binding, //running a binding
+ HandlingSignal, //running a signal handler
+ Javascript,
+
+ MaximumRangeType
+ };
+
+ enum BindingType {
+ QmlBinding,
+ V8Binding,
+ V4Binding,
+
+ MaximumBindingType
+ };
+
+ enum PixmapEventType {
+ PixmapSizeKnown,
+ PixmapReferenceCountChanged,
+ PixmapCacheCountChanged,
+ PixmapLoadingStarted,
+ PixmapLoadingFinished,
+ PixmapLoadingError,
+
+ MaximumPixmapEventType
+ };
+
+ enum SceneGraphFrameType {
+ SceneGraphRendererFrame,
+ SceneGraphAdaptationLayerFrame,
+ SceneGraphContextFrame,
+ SceneGraphRenderLoopFrame,
+ SceneGraphTexturePrepare,
+ SceneGraphTextureDeletion,
+ SceneGraphPolishAndSync,
+ SceneGraphWindowsRenderShow,
+ SceneGraphWindowsAnimations,
+ SceneGraphWindowsPolishFrame,
+
+ MaximumSceneGraphFrameType
+ };
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/debugger/qqmlprofilerservice.cpp b/src/qml/debugger/qqmlprofilerservice.cpp
index a9ef5ff6b1..cb84d8201f 100644
--- a/src/qml/debugger/qqmlprofilerservice.cpp
+++ b/src/qml/debugger/qqmlprofilerservice.cpp
@@ -40,6 +40,8 @@
****************************************************************************/
#include "qqmlprofilerservice_p.h"
+#include "qqmldebugserver_p.h"
+#include <private/qqmlengine_p.h>
#include <QtCore/qdatastream.h>
#include <QtCore/qurl.h>
@@ -146,12 +148,52 @@ QQmlProfilerService::QQmlProfilerService()
// case we might miss the callback registration.
if (state() == Enabled)
QUnifiedTimer::instance()->registerProfilerCallback(&animationTimerCallback);
+
+ // If there is no debug server it doesn't matter as we'll never get enabled anyway.
+ if (QQmlDebugServer::instance() != 0)
+ moveToThread(QQmlDebugServer::instance()->thread());
}
QQmlProfilerService::~QQmlProfilerService()
{
+ // 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.
enabled = false;
m_instance = 0;
+ qDeleteAll(m_engineProfilers.keys());
+ qDeleteAll(m_globalProfilers);
+}
+
+void QQmlProfilerService::dataReady(QQmlAbstractProfilerAdapter *profiler)
+{
+ QMutexLocker lock(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);
+ }
+ }
}
QQmlProfilerService *QQmlProfilerService::instance()
@@ -161,61 +203,214 @@ QQmlProfilerService *QQmlProfilerService::instance()
return m_instance;
}
-bool QQmlProfilerService::startProfiling()
+void QQmlProfilerService::engineAboutToBeAdded(QQmlEngine *engine)
+{
+ Q_ASSERT_X(QThread::currentThread() != thread(), Q_FUNC_INFO, "QML profilers have to be added from the engine thread");
+
+ QMutexLocker lock(configMutex());
+ QQmlProfiler *qmlAdapter = new QQmlProfiler(this);
+ addEngineProfiler(qmlAdapter, engine);
+ QQmlConfigurableDebugService::engineAboutToBeAdded(engine);
+}
+
+void QQmlProfilerService::engineAdded(QQmlEngine *engine)
+{
+ Q_ASSERT_X(QThread::currentThread() != thread(), Q_FUNC_INFO, "QML profilers have to be added from the engine thread");
+
+ QMutexLocker lock(configMutex());
+ foreach (QQmlAbstractProfilerAdapter *profiler, m_engineProfilers.values(engine))
+ profiler->stopWaiting();
+}
+
+void QQmlProfilerService::engineAboutToBeRemoved(QQmlEngine *engine)
{
- return profilerInstance()->startProfilingImpl();
+ Q_ASSERT_X(QThread::currentThread() != thread(), Q_FUNC_INFO, "QML profilers have to be removed from the engine thread");
+
+ QMutexLocker lock(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);
+ }
}
-bool QQmlProfilerService::stopProfiling()
+void QQmlProfilerService::engineRemoved(QQmlEngine *engine)
{
- return profilerInstance()->stopProfilingImpl();
+ Q_ASSERT_X(QThread::currentThread() != thread(), Q_FUNC_INFO, "QML profilers have to be removed from the engine thread");
+
+ QMutexLocker lock(configMutex());
+ foreach (QQmlAbstractProfilerAdapter *profiler, m_engineProfilers.values(engine))
+ delete profiler;
+ m_engineProfilers.remove(engine);
}
-void QQmlProfilerService::sendProfilingData()
+void QQmlProfilerService::addEngineProfiler(QQmlAbstractProfilerAdapter *profiler, QQmlEngine *engine)
{
- profilerInstance()->sendMessages();
+ profiler->moveToThread(thread());
+ profiler->synchronize(m_timer);
+ m_engineProfilers.insert(engine, profiler);
}
-bool QQmlProfilerService::startProfilingImpl()
+void QQmlProfilerService::addGlobalProfiler(QQmlAbstractProfilerAdapter *profiler)
{
- if (QQmlDebugService::isDebuggingEnabled() && !enabled) {
- enabled = true;
- QList<QByteArray> messages;
- QQmlProfilerData(m_timer.nsecsElapsed(), 1 << Event, 1 << StartTrace).toByteArrays(messages);
- QQmlDebugService::sendMessages(messages);
- return true;
- } else {
- return false;
+ QMutexLocker lock(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.
+ foreach (QQmlAbstractProfilerAdapter *engineProfiler, m_engineProfilers) {
+ if (engineProfiler->isRunning()) {
+ profiler->startProfiling();
+ break;
+ }
}
}
-bool QQmlProfilerService::stopProfilingImpl()
+void QQmlProfilerService::removeGlobalProfiler(QQmlAbstractProfilerAdapter *profiler)
{
- if (enabled) {
- enabled = false;
- // We cannot use instance here as this is called from the debugger thread.
- // It may be called before the QML engine (and the profiler) is ready.
- processMessage(QQmlProfilerData(m_timer.nsecsElapsed(), 1 << Event, 1 << EndTrace));
- return true;
- } else {
- return false;
+ QMutexLocker lock(configMutex());
+ for (QMultiMap<qint64, QQmlAbstractProfilerAdapter *>::iterator i(m_startTimes.begin()); i != m_startTimes.end();) {
+ if (i.value() == profiler)
+ m_startTimes.erase(i++);
+ else
+ ++i;
+ }
+ m_globalProfilers.removeOne(profiler);
+ delete profiler;
+}
+
+void QQmlProfilerService::startProfiling(QQmlEngine *engine)
+{
+ QMutexLocker lock(configMutex());
+
+ QByteArray message;
+ QQmlDebugStream d(&message, QIODevice::WriteOnly);
+ d << m_timer.nsecsElapsed() << (int)Event << (int)StartTrace << idForObject(engine);
+ foreach (QQmlAbstractProfilerAdapter *profiler, m_engineProfilers.values(engine)) {
+ profiler->startProfiling();
+ }
+ if (!m_engineProfilers.values(engine).empty()) {
+ foreach (QQmlAbstractProfilerAdapter *profiler, m_globalProfilers) {
+ if (!profiler->isRunning())
+ profiler->startProfiling();
+ }
+ }
+
+ QQmlDebugService::sendMessage(message);
+}
+
+void QQmlProfilerService::stopProfiling(QQmlEngine *engine)
+{
+ QMutexLocker lock(configMutex());
+
+ bool stillRunning = false;
+ m_startTimes.clear();
+ for (QMultiHash<QQmlEngine *, QQmlAbstractProfilerAdapter *>::iterator i(m_engineProfilers.begin());
+ i != m_engineProfilers.end(); ++i) {
+ if (i.value()->isRunning()) {
+ if (i.key() == engine) {
+ m_startTimes.insert(-1, i.value());
+ i.value()->stopProfiling();
+ } else {
+ stillRunning = true;
+ }
+ }
+ }
+ foreach (QQmlAbstractProfilerAdapter *profiler, m_globalProfilers) {
+ if (!profiler->isRunning())
+ continue;
+ m_startTimes.insert(-1, profiler);
+ if (stillRunning) {
+ profiler->reportData();
+ } else {
+ profiler->stopProfiling();
+ }
+ }
+}
+
+QQmlProfiler::QQmlProfiler(QQmlProfilerService *service) :
+ QQmlAbstractProfilerAdapter(service), next(0)
+{
+ connect(this, SIGNAL(profilingEnabled()), this, SLOT(startProfiling()));
+ connect(this, SIGNAL(profilingEnabledWhileWaiting()), this, SLOT(startProfiling()),
+ Qt::DirectConnection);
+ connect(this, SIGNAL(profilingDisabled()), this, SLOT(stopProfiling()));
+ connect(this, SIGNAL(profilingDisabledWhileWaiting()), this, SLOT(stopProfiling()),
+ Qt::DirectConnection);
+}
+
+qint64 QQmlProfiler::sendMessages(qint64 until, QList<QByteArray> &messages)
+{
+ QMutexLocker lock(&QQmlProfilerService::instance()->m_dataMutex);
+ QVector<QQmlProfilerData> *data = &(QQmlProfilerService::instance()->m_data);
+ while (next < data->size() && data->at(next).time <= until) {
+ data->at(next++).toByteArrays(messages);
+ }
+ return next < data->size() ? data->at(next).time : -1;
+}
+
+void QQmlProfiler::startProfiling()
+{
+ if (!QQmlProfilerService::enabled) {
+ next = 0;
+ service->m_data.clear();
+ QQmlProfilerService::enabled = true;
}
}
+void QQmlProfiler::stopProfiling()
+{
+ next = 0;
+ QQmlProfilerService::enabled = false;
+ service->dataReady(this);
+}
+
/*
- Send the messages queued up by processMessage
+ Send the queued up messages.
*/
void QQmlProfilerService::sendMessages()
{
- QMutexLocker locker(&m_dataMutex);
-
QList<QByteArray> messages;
- for (int i = 0; i < m_data.count(); ++i)
- m_data.at(i).toByteArrays(messages);
- m_data.clear();
- //indicate completion
QByteArray data;
+ 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);
+ }
+ }
+
+ //indicate completion
+ messages << data;
+ data.clear();
+
QQmlDebugStream ds(&data, QIODevice::WriteOnly);
ds << (qint64)-1 << (int)Complete;
messages << data;
@@ -230,9 +425,10 @@ void QQmlProfilerService::stateAboutToBeChanged(QQmlDebugService::State newState
if (state() == newState)
return;
- if (newState != Enabled && enabled) {
- stopProfilingImpl();
- sendMessages();
+ // Stop all profiling and send the data before we get disabled.
+ if (newState != Enabled) {
+ foreach (QQmlEngine *engine, m_engineProfilers.keys())
+ stopProfiling(engine);
}
}
@@ -243,14 +439,27 @@ void QQmlProfilerService::messageReceived(const QByteArray &message)
QByteArray rwData = message;
QQmlDebugStream stream(&rwData, QIODevice::ReadOnly);
+ int engineId = -1;
bool enabled;
stream >> enabled;
-
- if (enabled) {
- startProfilingImpl();
+ if (!stream.atEnd())
+ stream >> engineId;
+
+ // The second time around there will be specific engineIds.
+ // We only have to wait after the first, empty start message.
+ if (engineId == -1) {
+ // Wait until no engine registers within RegisterTimeout anymore.
+ foreach (QQmlEngine *engine, m_engineProfilers.keys().toSet()) {
+ if (enabled)
+ startProfiling(engine);
+ else
+ stopProfiling(engine);
+ }
} else {
- if (stopProfilingImpl())
- sendMessages();
+ if (enabled)
+ startProfiling(qobject_cast<QQmlEngine *>(objectForId(engineId)));
+ else
+ stopProfiling(qobject_cast<QQmlEngine *>(objectForId(engineId)));
}
stopWaiting();
diff --git a/src/qml/debugger/qqmlprofilerservice_p.h b/src/qml/debugger/qqmlprofilerservice_p.h
index b688141730..4549814a02 100644
--- a/src/qml/debugger/qqmlprofilerservice_p.h
+++ b/src/qml/debugger/qqmlprofilerservice_p.h
@@ -54,6 +54,9 @@
//
#include "qqmlconfigurabledebugservice_p.h"
+#include "qqmlprofilerdefinitions_p.h"
+#include "qqmlabstractprofileradapter_p.h"
+
#include <private/qqmlboundsignal_p.h>
// this contains QUnifiedTimer
#include <private/qabstractanimation_p.h>
@@ -163,81 +166,22 @@ class QUrl;
class QQmlEngine;
-class Q_QML_PRIVATE_EXPORT QQmlProfilerService : public QQmlConfigurableDebugService
+class Q_QML_PRIVATE_EXPORT QQmlProfilerService : public QQmlConfigurableDebugService, public QQmlProfilerDefinitions
{
+ Q_OBJECT
public:
- enum Message {
- Event,
- RangeStart,
- RangeData,
- RangeLocation,
- RangeEnd,
- Complete, // end of transmission
- PixmapCacheEvent,
- SceneGraphFrame,
-
- MaximumMessage
- };
-
- enum EventType {
- FramePaint,
- Mouse,
- Key,
- AnimationFrame,
- EndTrace,
- StartTrace,
-
- MaximumEventType
- };
-
- enum RangeType {
- Painting,
- Compiling,
- Creating,
- Binding, //running a binding
- HandlingSignal, //running a signal handler
-
- MaximumRangeType
- };
-
- enum BindingType {
- QmlBinding,
- V8Binding,
- V4Binding,
-
- MaximumBindingType
- };
-
- enum PixmapEventType {
- PixmapSizeKnown,
- PixmapReferenceCountChanged,
- PixmapCacheCountChanged,
- PixmapLoadingStarted,
- PixmapLoadingFinished,
- PixmapLoadingError,
-
- MaximumPixmapEventType
- };
-
- enum SceneGraphFrameType {
- SceneGraphRendererFrame,
- SceneGraphAdaptationLayerFrame,
- SceneGraphContextFrame,
- SceneGraphRenderLoopFrame,
- SceneGraphTexturePrepare,
- SceneGraphTextureDeletion,
- SceneGraphPolishAndSync,
- SceneGraphWindowsRenderShow,
- SceneGraphWindowsAnimations,
- SceneGraphWindowsPolishFrame,
-
- MaximumSceneGraphFrameType
- };
static QQmlProfilerService *instance();
+ void engineAboutToBeAdded(QQmlEngine *engine);
+ void engineAboutToBeRemoved(QQmlEngine *engine);
+ void engineAdded(QQmlEngine *engine);
+ void engineRemoved(QQmlEngine *engine);
- static bool startProfiling();
- static bool stopProfiling();
+ void addGlobalProfiler(QQmlAbstractProfilerAdapter *profiler);
+ void removeGlobalProfiler(QQmlAbstractProfilerAdapter *profiler);
+
+ void startProfiling(QQmlEngine *engine);
+ void stopProfiling(QQmlEngine *engine);
template<EventType DetailType>
static void addEvent()
@@ -290,18 +234,16 @@ public:
qint64 timestamp() {return m_timer.nsecsElapsed();}
- static void sendProfilingData();
-
QQmlProfilerService();
~QQmlProfilerService();
+ void dataReady(QQmlAbstractProfilerAdapter *profiler);
+
protected:
virtual void stateAboutToBeChanged(State state);
virtual void messageReceived(const QByteArray &);
private:
- bool startProfilingImpl();
- bool stopProfilingImpl();
static void startBinding(const QString &fileName, int line, int column, BindingType bindingType)
{
@@ -357,6 +299,7 @@ private:
}
void sendMessages();
+ void addEngineProfiler(QQmlAbstractProfilerAdapter *profiler, QQmlEngine *engine);
void processMessage(const QQmlProfilerData &message)
{
@@ -373,12 +316,32 @@ private:
QVector<QQmlProfilerData> m_data;
QMutex m_dataMutex;
+ QList<QQmlAbstractProfilerAdapter *> m_globalProfilers;
+ QMultiHash<QQmlEngine *, QQmlAbstractProfilerAdapter *> m_engineProfilers;
+ QList<QQmlEngine *> m_stoppingEngines;
+ QMultiMap<qint64, QQmlAbstractProfilerAdapter *> m_startTimes;
+
static QQmlProfilerService *m_instance;
friend struct QQmlBindingProfiler;
friend struct QQmlHandlingSignalProfiler;
friend struct QQmlVmeProfiler;
friend struct QQmlCompilingProfiler;
+ friend class QQmlProfiler;
+};
+
+// Temporary shim around QQmlProfilerService to make it look like a QQmlAbstractProfilerAdapter.
+class QQmlProfiler : public QQmlAbstractProfilerAdapter {
+ Q_OBJECT
+public:
+ QQmlProfiler(QQmlProfilerService *service);
+ qint64 sendMessages(qint64 until, QList<QByteArray> &messages);
+
+public slots:
+ void startProfiling();
+ void stopProfiling();
+private:
+ int next;
};
//
diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
index 26ef86836c..fbc41201fb 100644
--- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
+++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
@@ -1422,10 +1422,6 @@ QV4::ReturnedValue ConsoleObject::method_log(CallContext *ctx)
QV4::ReturnedValue ConsoleObject::method_profile(CallContext *ctx)
{
- //DeclarativeDebugTrace cannot handle nested profiling
- //although v8 can handle several profiling at once,
- //we do not allow that. Hence, we pass an empty(default) title
- QString title;
QV4::ExecutionEngine *v4 = ctx->engine;
QV4::StackFrame frame = v4->currentStackFrame();
@@ -1434,12 +1430,9 @@ QV4::ReturnedValue ConsoleObject::method_profile(CallContext *ctx)
QMessageLogger logger(baSource.constData(), frame.line, baFunction.constData());
if (!QQmlDebugService::isDebuggingEnabled()) {
logger.warning("Cannot start profiling because debug service is disabled. Start with -qmljsdebugger=port:XXXXX.");
- } else if (QQmlProfilerService::startProfiling()) {
- QV4ProfilerService::instance()->startProfiling(title);
-
- logger.debug("Profiling started.");
} else {
- logger.warning("Profiling is already in progress. First, end current profiling session.");
+ QQmlProfilerService::instance()->startProfiling(v4->v8Engine->engine());
+ logger.debug("Profiling started.");
}
return QV4::Encode::undefined();
@@ -1447,11 +1440,6 @@ QV4::ReturnedValue ConsoleObject::method_profile(CallContext *ctx)
QV4::ReturnedValue ConsoleObject::method_profileEnd(CallContext *ctx)
{
- //DeclarativeDebugTrace cannot handle nested profiling
- //although v8 can handle several profiling at once,
- //we do not allow that. Hence, we pass an empty(default) title
- QString title;
-
QV4::ExecutionEngine *v4 = ctx->engine;
QV4::StackFrame frame = v4->currentStackFrame();
@@ -1459,15 +1447,11 @@ QV4::ReturnedValue ConsoleObject::method_profileEnd(CallContext *ctx)
const QByteArray baFunction = frame.function.toUtf8();
QMessageLogger logger(baSource.constData(), frame.line, baFunction.constData());
- if (QQmlProfilerService::stopProfiling()) {
- QV4ProfilerService *profiler = QV4ProfilerService::instance();
- profiler->stopProfiling(title);
- QQmlProfilerService::sendProfilingData();
- profiler->sendProfilingData();
-
- logger.debug("Profiling ended.");
+ if (!QQmlDebugService::isDebuggingEnabled()) {
+ logger.warning("Ignoring console.profileEnd(): the debug service is disabled.");
} else {
- logger.warning("Profiling was not started.");
+ QQmlProfilerService::instance()->stopProfiling(v4->v8Engine->engine());
+ logger.debug("Profiling ended.");
}
return QV4::Encode::undefined();