diff options
author | Ulf Hermann <ulf.hermann@theqtcompany.com> | 2015-07-20 18:09:42 +0200 |
---|---|---|
committer | Ulf Hermann <ulf.hermann@theqtcompany.com> | 2015-08-04 13:35:21 +0000 |
commit | f8e5cfcfc26499eef30fc222e24957a753651cbc (patch) | |
tree | 23943933c7ad81d1bb535978bba39f94a1606614 /src/plugins | |
parent | 275ddd68af1881c5712848a4be9892f84b62b321 (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')
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 |