aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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
-rw-r--r--tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp10
-rw-r--r--tests/auto/qml/qqmlconsole/tst_qqmlconsole.cpp2
9 files changed, 656 insertions, 139 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();
diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp b/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp
index 14a6178b09..d05dfa8496 100644
--- a/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp
+++ b/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp
@@ -100,6 +100,7 @@ public:
Creating,
Binding, //running a binding
HandlingSignal, //running a signal handler
+ Javascript,
MaximumRangeType
};
@@ -214,9 +215,16 @@ void QQmlProfilerClient::messageReceived(const QByteArray &message)
case QQmlProfilerClient::FramePaint:
case QQmlProfilerClient::Mouse:
case QQmlProfilerClient::Key:
- case QQmlProfilerClient::StartTrace:
+ break;
case QQmlProfilerClient::EndTrace:
+ case QQmlProfilerClient::StartTrace: {
+ int engineId = -1;
+ if (!stream.atEnd()) {
+ stream >> engineId;
+ QVERIFY(engineId >= 0);
+ }
break;
+ }
default: {
QString failMsg = QString("Unknown event type:") + data.detailType;
QFAIL(qPrintable(failMsg));
diff --git a/tests/auto/qml/qqmlconsole/tst_qqmlconsole.cpp b/tests/auto/qml/qqmlconsole/tst_qqmlconsole.cpp
index a37d705284..43aa82e1e4 100644
--- a/tests/auto/qml/qqmlconsole/tst_qqmlconsole.cpp
+++ b/tests/auto/qml/qqmlconsole/tst_qqmlconsole.cpp
@@ -116,7 +116,7 @@ void tst_qqmlconsole::profiling()
// profiling()
QTest::ignoreMessage(QtWarningMsg, "Cannot start profiling because debug service is disabled. Start with -qmljsdebugger=port:XXXXX.");
- QTest::ignoreMessage(QtWarningMsg, "Profiling was not started.");
+ QTest::ignoreMessage(QtWarningMsg, "Ignoring console.profileEnd(): the debug service is disabled.");
QQmlComponent component(&engine, testUrl);
QObject *object = component.create();