diff options
Diffstat (limited to 'src/qmldebug')
33 files changed, 4297 insertions, 370 deletions
diff --git a/src/qmldebug/qmldebug.pro b/src/qmldebug/qmldebug.pro index e5f6de3314..94d300b765 100644 --- a/src/qmldebug/qmldebug.pro +++ b/src/qmldebug/qmldebug.pro @@ -1,5 +1,5 @@ TARGET = QtQmlDebug -QT = core-private network packetprotocol-private qml-private +QT = core-private network packetprotocol-private CONFIG += static internal_module load(qt_module) @@ -7,15 +7,38 @@ load(qt_module) SOURCES += \ qqmldebugclient.cpp \ qqmldebugconnection.cpp \ + qqmldebugmessageclient.cpp \ qqmlenginecontrolclient.cpp \ - qqmlprofilerclient.cpp + qqmlenginedebugclient.cpp \ + qqmlinspectorclient.cpp \ + qqmlpreviewclient.cpp \ + qqmlprofilerclient.cpp \ + qqmlprofilerevent.cpp \ + qqmlprofilereventlocation.cpp \ + qqmlprofilereventtype.cpp \ + qqmlprofilertypedevent.cpp \ + qv4debugclient.cpp HEADERS += \ qqmldebugclient_p.h \ qqmldebugclient_p_p.h \ qqmldebugconnection_p.h \ + qqmldebugmessageclient_p.h \ + qqmlenginedebugclient_p.h \ + qqmlenginedebugclient_p_p.h \ qqmlenginecontrolclient_p.h \ qqmlenginecontrolclient_p_p.h \ - qqmleventlocation_p.h \ + qqmlinspectorclient_p.h \ + qqmlinspectorclient_p_p.h \ + qqmlpreviewclient_p.h \ + qqmlpreviewclient_p_p.h \ qqmlprofilerclient_p.h \ - qqmlprofilerclient_p_p.h + qqmlprofilerclient_p_p.h \ + qqmlprofilerevent_p.h \ + qqmlprofilereventlocation_p.h \ + qqmlprofilereventreceiver_p.h \ + qqmlprofilereventtype_p.h \ + qqmlprofilertypedevent_p.h \ + qqmlprofilerclientdefinitions_p.h \ + qv4debugclient_p.h \ + qv4debugclient_p_p.h diff --git a/src/qmldebug/qqmldebugclient.cpp b/src/qmldebug/qqmldebugclient.cpp index 7f1e8c637c..03123cc6e0 100644 --- a/src/qmldebug/qqmldebugclient.cpp +++ b/src/qmldebug/qqmldebugclient.cpp @@ -77,7 +77,7 @@ void QQmlDebugClientPrivate::addToConnection() Q_Q(QQmlDebugClient); if (connection && !connection->addClient(name, q)) { qWarning() << "QQmlDebugClient: Conflicting plugin name" << name; - connection = 0; + connection = nullptr; } } @@ -117,11 +117,6 @@ QQmlDebugConnection *QQmlDebugClient::connection() const return d->connection; } -void QQmlDebugClient::stateChanged(QQmlDebugClient::State state) -{ - Q_UNUSED(state); -} - void QQmlDebugClient::messageReceived(const QByteArray &message) { Q_UNUSED(message); diff --git a/src/qmldebug/qqmldebugclient_p.h b/src/qmldebug/qqmldebugclient_p.h index 723de5ee43..469b65d4a9 100644 --- a/src/qmldebug/qqmldebugclient_p.h +++ b/src/qmldebug/qqmldebugclient_p.h @@ -76,13 +76,14 @@ public: QQmlDebugConnection *connection() const; +signals: + void stateChanged(State state); + protected: QQmlDebugClient(QQmlDebugClientPrivate &dd); private: friend class QQmlDebugConnection; - - virtual void stateChanged(State state); virtual void messageReceived(const QByteArray &message); }; diff --git a/src/qmldebug/qqmldebugconnection.cpp b/src/qmldebug/qqmldebugconnection.cpp index 73ccd7c854..4e087ee6db 100644 --- a/src/qmldebug/qqmldebugconnection.cpp +++ b/src/qmldebug/qqmldebugconnection.cpp @@ -63,15 +63,15 @@ class QQmlDebugConnectionPrivate : public QObjectPrivate public: QQmlDebugConnectionPrivate(); - QPacketProtocol *protocol; - QIODevice *device; - QLocalServer *server; + QPacketProtocol *protocol = nullptr; + QIODevice *device = nullptr; + QLocalServer *server = nullptr; QEventLoop handshakeEventLoop; QTimer handshakeTimer; - bool gotHello; - int currentDataStreamVersion; - int maximumDataStreamVersion; + bool gotHello = false; + int currentDataStreamVersion = QDataStream::Qt_4_7; + int maximumDataStreamVersion = QDataStream::Qt_DefaultCompiledVersion; QHash <QString, float> serverPlugins; QHash<QString, QQmlDebugClient *> plugins; QStringList removedPlugins; @@ -81,10 +81,7 @@ public: void flush(); }; -QQmlDebugConnectionPrivate::QQmlDebugConnectionPrivate() : - protocol(0), device(0), server(0), gotHello(false), - currentDataStreamVersion(QDataStream::Qt_4_7), - maximumDataStreamVersion(QDataStream::Qt_DefaultCompiledVersion) +QQmlDebugConnectionPrivate::QQmlDebugConnectionPrivate() { handshakeTimer.setSingleShot(true); handshakeTimer.setInterval(3000); @@ -262,7 +259,7 @@ QQmlDebugConnection::~QQmlDebugConnection() Q_D(QQmlDebugConnection); QHash<QString, QQmlDebugClient*>::iterator iter = d->plugins.begin(); for (; iter != d->plugins.end(); ++iter) - iter.value()->stateChanged(QQmlDebugClient::NotConnected); + emit iter.value()->stateChanged(QQmlDebugClient::NotConnected); } int QQmlDebugConnection::currentDataStreamVersion() const @@ -298,12 +295,12 @@ void QQmlDebugConnection::close() QHash<QString, QQmlDebugClient*>::iterator iter = d->plugins.begin(); for (; iter != d->plugins.end(); ++iter) - iter.value()->stateChanged(QQmlDebugClient::NotConnected); + emit iter.value()->stateChanged(QQmlDebugClient::NotConnected); } if (d->device) { d->device->deleteLater(); - d->device = 0; + d->device = nullptr; } } diff --git a/src/qmldebug/qqmldebugconnection_p.h b/src/qmldebug/qqmldebugconnection_p.h index be425b6cbf..ad9376886c 100644 --- a/src/qmldebug/qqmldebugconnection_p.h +++ b/src/qmldebug/qqmldebugconnection_p.h @@ -64,7 +64,7 @@ class QQmlDebugConnection : public QObject Q_DISABLE_COPY(QQmlDebugConnection) Q_DECLARE_PRIVATE(QQmlDebugConnection) public: - QQmlDebugConnection(QObject *parent = 0); + QQmlDebugConnection(QObject *parent = nullptr); ~QQmlDebugConnection(); void connectToHost(const QString &hostName, quint16 port); diff --git a/src/qmldebug/qqmldebugmessageclient.cpp b/src/qmldebug/qqmldebugmessageclient.cpp new file mode 100644 index 0000000000..0892404194 --- /dev/null +++ b/src/qmldebug/qqmldebugmessageclient.cpp @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmldebugmessageclient_p.h" + +#include <QtCore/qdatastream.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QQmlDebugMessageClient + \internal + + \brief Client for the debug message service + + The QQmlDebugMessageClient receives debug messages routed through the QML + debug connection via QDebugMessageService. + */ + +QQmlDebugMessageClient::QQmlDebugMessageClient(QQmlDebugConnection *client) + : QQmlDebugClient(QLatin1String("DebugMessages"), client) +{ +} + +void QQmlDebugMessageClient::messageReceived(const QByteArray &data) +{ + QDataStream ds(data); + QByteArray command; + ds >> command; + + if (command == "MESSAGE") { + int type; + int line; + QByteArray debugMessage; + QByteArray file; + QByteArray function; + ds >> type >> debugMessage >> file >> line >> function; + QQmlDebugContextInfo info; + info.line = line; + info.file = QString::fromUtf8(file); + info.function = QString::fromUtf8(function); + info.timestamp = -1; + if (!ds.atEnd()) { + QByteArray category; + ds >> category; + info.category = QString::fromUtf8(category); + if (!ds.atEnd()) + ds >> info.timestamp; + } + emit message(QtMsgType(type), QString::fromUtf8(debugMessage), info); + } +} + +QT_END_NAMESPACE diff --git a/src/qmldebug/qqmldebugmessageclient_p.h b/src/qmldebug/qqmldebugmessageclient_p.h new file mode 100644 index 0000000000..a2a7f28f81 --- /dev/null +++ b/src/qmldebug/qqmldebugmessageclient_p.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLDEBUGMESSAGECLIENT_P_H +#define QQMLDEBUGMESSAGECLIENT_P_H + +#include "qqmldebugclient_p.h" + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +QT_BEGIN_NAMESPACE + +struct QQmlDebugContextInfo +{ + int line; + QString file; + QString function; + QString category; + qint64 timestamp; +}; + +class QQmlDebugMessageClient : public QQmlDebugClient +{ + Q_OBJECT + +public: + explicit QQmlDebugMessageClient(QQmlDebugConnection *client); + + virtual void messageReceived(const QByteArray &) override; + +signals: + void message(QtMsgType, const QString &, const QQmlDebugContextInfo &); +}; + +QT_END_NAMESPACE + +#endif // QQMLDEBUGMESSAGECLIENT_P_H diff --git a/src/qmldebug/qqmlenginecontrolclient.cpp b/src/qmldebug/qqmlenginecontrolclient.cpp index 3f75298e51..cdc724ef2f 100644 --- a/src/qmldebug/qqmlenginecontrolclient.cpp +++ b/src/qmldebug/qqmlenginecontrolclient.cpp @@ -41,7 +41,6 @@ #include "qqmlenginecontrolclient_p_p.h" #include "qqmldebugconnection_p.h" -#include <private/qqmldebugserviceinterfaces_p.h> #include <private/qpacket_p.h> QT_BEGIN_NAMESPACE @@ -99,8 +98,8 @@ void QQmlEngineControlClient::messageReceived(const QByteArray &data) { Q_D(QQmlEngineControlClient); QPacket stream(d->connection->currentDataStreamVersion(), data); - int message; - int id; + qint32 message; + qint32 id; QString name; stream >> message >> id; @@ -108,36 +107,41 @@ void QQmlEngineControlClient::messageReceived(const QByteArray &data) if (!stream.atEnd()) stream >> name; - QQmlEngineControlClientPrivate::EngineState &state = d->blockedEngines[id]; - Q_ASSERT(state.blockers == 0); - Q_ASSERT(state.releaseCommand == QQmlEngineControlClientPrivate::InvalidCommand); + auto handleWaiting = [&]( + QQmlEngineControlClientPrivate::CommandType command, std::function<void()> emitter) { + QQmlEngineControlClientPrivate::EngineState &state = d->blockedEngines[id]; + Q_ASSERT(state.blockers == 0); + Q_ASSERT(state.releaseCommand == QQmlEngineControlClientPrivate::InvalidCommand); + state.releaseCommand = command; + emitter(); + if (state.blockers == 0) { + d->sendCommand(state.releaseCommand, id); + d->blockedEngines.remove(id); + } + }; switch (message) { case QQmlEngineControlClientPrivate::EngineAboutToBeAdded: - state.releaseCommand = QQmlEngineControlClientPrivate::StartWaitingEngine; - emit engineAboutToBeAdded(id, name); + handleWaiting(QQmlEngineControlClientPrivate::StartWaitingEngine, [&](){ + emit engineAboutToBeAdded(id, name); + }); break; case QQmlEngineControlClientPrivate::EngineAdded: emit engineAdded(id, name); break; case QQmlEngineControlClientPrivate::EngineAboutToBeRemoved: - state.releaseCommand = QQmlEngineControlClientPrivate::StopWaitingEngine; - emit engineAboutToBeRemoved(id, name); + handleWaiting(QQmlEngineControlClientPrivate::StopWaitingEngine, [&](){ + emit engineAboutToBeRemoved(id, name); + }); break; case QQmlEngineControlClientPrivate::EngineRemoved: emit engineRemoved(id, name); break; } - - if (state.blockers == 0 && - state.releaseCommand != QQmlEngineControlClientPrivate::InvalidCommand) { - d->sendCommand(state.releaseCommand, id); - d->blockedEngines.remove(id); - } } QQmlEngineControlClientPrivate::QQmlEngineControlClientPrivate(QQmlDebugConnection *connection) : - QQmlDebugClientPrivate(QQmlEngineControlService::s_key, connection) + QQmlDebugClientPrivate(QLatin1String("EngineControl"), connection) { } @@ -146,7 +150,7 @@ void QQmlEngineControlClientPrivate::sendCommand( { Q_Q(QQmlEngineControlClient); QPacket stream(connection->currentDataStreamVersion()); - stream << int(command) << engineId; + stream << static_cast<qint32>(command) << engineId; q->sendMessage(stream.data()); } diff --git a/src/qmldebug/qqmlenginedebugclient.cpp b/src/qmldebug/qqmlenginedebugclient.cpp new file mode 100644 index 0000000000..ec45ec33bc --- /dev/null +++ b/src/qmldebug/qqmlenginedebugclient.cpp @@ -0,0 +1,566 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlenginedebugclient_p_p.h" +#include <private/qqmldebugconnection_p.h> + +QT_BEGIN_NAMESPACE + +struct QQmlObjectData { + QUrl url; + int lineNumber = -1; + int columnNumber = -1; + QString idString; + QString objectName; + QString objectType; + int objectId = -1; + int contextId = -1; + int parentId = -1; +}; + +QPacket &operator>>(QPacket &ds, QQmlObjectData &data) +{ + ds >> data.url >> data.lineNumber >> data.columnNumber >> data.idString + >> data.objectName >> data.objectType >> data.objectId >> data.contextId + >> data.parentId; + return ds; +} + +struct QQmlObjectProperty { + enum Type { Unknown, Basic, Object, List, SignalProperty }; + Type type = Unknown; + QString name; + QVariant value; + QString valueTypeName; + QString binding; + bool hasNotifySignal = false; +}; + +QPacket &operator>>(QPacket &ds, QQmlObjectProperty &data) +{ + int type; + ds >> type >> data.name >> data.value >> data.valueTypeName + >> data.binding >> data.hasNotifySignal; + data.type = (QQmlObjectProperty::Type)type; + return ds; +} + +QQmlEngineDebugClient::QQmlEngineDebugClient(QQmlDebugConnection *connection) : + QQmlDebugClient(*new QQmlEngineDebugClientPrivate(connection)) +{ +} + +QQmlEngineDebugClientPrivate::QQmlEngineDebugClientPrivate(QQmlDebugConnection *connection) : + QQmlDebugClientPrivate (QLatin1String("QmlDebugger"), connection) +{ +} + + +quint32 QQmlEngineDebugClient::addWatch( + const QQmlEngineDebugPropertyReference &property, bool *success) +{ + quint32 id = -1; + *success = false; + if (state() == QQmlDebugClient::Enabled) { + id = getId(); + QPacket ds(connection()->currentDataStreamVersion()); + ds << QByteArray("WATCH_PROPERTY") << id << property.objectDebugId + << property.name.toUtf8(); + sendMessage(ds.data()); + *success = true; + } + return id; +} + +quint32 QQmlEngineDebugClient::addWatch( + const QQmlEngineDebugContextReference &, const QString &, bool *success) +{ + *success = false; + qWarning("QQmlEngineDebugClient::addWatch(): Not implemented"); + return 0; +} + +quint32 QQmlEngineDebugClient::addWatch( + const QQmlEngineDebugObjectReference &object, const QString &expr, + bool *success) +{ + quint32 id = -1; + *success = false; + if (state() == QQmlDebugClient::Enabled) { + id = getId(); + QPacket ds(connection()->currentDataStreamVersion()); + ds << QByteArray("WATCH_EXPR_OBJECT") << id << object.debugId << expr; + sendMessage(ds.data()); + *success = true; + } + return id; +} + +quint32 QQmlEngineDebugClient::addWatch( + const QQmlEngineDebugObjectReference &object, bool *success) +{ + quint32 id = -1; + *success = false; + if (state() == QQmlDebugClient::Enabled) { + id = getId(); + QPacket ds(connection()->currentDataStreamVersion()); + ds << QByteArray("WATCH_OBJECT") << id << object.debugId; + sendMessage(ds.data()); + *success = true; + } + return id; +} + +quint32 QQmlEngineDebugClient::addWatch( + const QQmlEngineDebugFileReference &, bool *success) +{ + *success = false; + qWarning("QQmlEngineDebugClient::addWatch(): Not implemented"); + return 0; +} + +void QQmlEngineDebugClient::removeWatch(quint32 id, bool *success) +{ + *success = false; + if (state() == QQmlDebugClient::Enabled) { + QPacket ds(connection()->currentDataStreamVersion()); + ds << QByteArray("NO_WATCH") << id; + sendMessage(ds.data()); + *success = true; + } +} + +quint32 QQmlEngineDebugClient::queryAvailableEngines(bool *success) +{ + Q_D(QQmlEngineDebugClient); + d->engines.clear(); + quint32 id = -1; + *success = false; + if (state() == QQmlDebugClient::Enabled) { + id = getId(); + QPacket ds(connection()->currentDataStreamVersion()); + ds << QByteArray("LIST_ENGINES") << id; + sendMessage(ds.data()); + *success = true; + } + return id; +} + +quint32 QQmlEngineDebugClient::queryRootContexts( + const QQmlEngineDebugEngineReference &engine, bool *success) +{ + Q_D(QQmlEngineDebugClient); + d->rootContext = QQmlEngineDebugContextReference(); + quint32 id = -1; + *success = false; + if (state() == QQmlDebugClient::Enabled && engine.debugId != -1) { + id = getId(); + QPacket ds(connection()->currentDataStreamVersion()); + ds << QByteArray("LIST_OBJECTS") << id << engine.debugId; + sendMessage(ds.data()); + *success = true; + } + return id; +} + +quint32 QQmlEngineDebugClient::queryObject( + const QQmlEngineDebugObjectReference &object, bool *success) +{ + Q_D(QQmlEngineDebugClient); + d->object = QQmlEngineDebugObjectReference(); + quint32 id = -1; + *success = false; + if (state() == QQmlDebugClient::Enabled && object.debugId != -1) { + id = getId(); + QPacket ds(connection()->currentDataStreamVersion()); + ds << QByteArray("FETCH_OBJECT") << id << object.debugId << false << true; + sendMessage(ds.data()); + *success = true; + } + return id; +} + +quint32 QQmlEngineDebugClient::queryObjectsForLocation( + const QString &file, int lineNumber, int columnNumber, bool *success) +{ + Q_D(QQmlEngineDebugClient); + d->objects.clear(); + quint32 id = -1; + *success = false; + if (state() == QQmlDebugClient::Enabled) { + id = getId(); + QPacket ds(connection()->currentDataStreamVersion()); + ds << QByteArray("FETCH_OBJECTS_FOR_LOCATION") << id << file << lineNumber + << columnNumber << false << true; + sendMessage(ds.data()); + *success = true; + } + return id; +} + +quint32 QQmlEngineDebugClient::queryObjectRecursive( + const QQmlEngineDebugObjectReference &object, bool *success) +{ + Q_D(QQmlEngineDebugClient); + d->object = QQmlEngineDebugObjectReference(); + quint32 id = -1; + *success = false; + if (state() == QQmlDebugClient::Enabled && object.debugId != -1) { + id = getId(); + QPacket ds(connection()->currentDataStreamVersion()); + ds << QByteArray("FETCH_OBJECT") << id << object.debugId << true << true; + sendMessage(ds.data()); + *success = true; + } + return id; +} + +quint32 QQmlEngineDebugClient::queryObjectsForLocationRecursive(const QString &file, + int lineNumber, int columnNumber, bool *success) +{ + Q_D(QQmlEngineDebugClient); + d->objects.clear(); + quint32 id = -1; + *success = false; + if (state() == QQmlDebugClient::Enabled) { + id = getId(); + QPacket ds(connection()->currentDataStreamVersion()); + ds << QByteArray("FETCH_OBJECTS_FOR_LOCATION") << id << file << lineNumber + << columnNumber << true << true; + sendMessage(ds.data()); + *success = true; + } + return id; +} + +quint32 QQmlEngineDebugClient::queryExpressionResult( + int objectDebugId, const QString &expr, bool *success) +{ + Q_D(QQmlEngineDebugClient); + d->exprResult = QVariant(); + quint32 id = -1; + *success = false; + if (state() == QQmlDebugClient::Enabled) { + id = getId(); + QPacket ds(connection()->currentDataStreamVersion()); + ds << QByteArray("EVAL_EXPRESSION") << id << objectDebugId << expr + << engines()[0].debugId; + sendMessage(ds.data()); + *success = true; + } + return id; +} + +quint32 QQmlEngineDebugClient::queryExpressionResultBC( + int objectDebugId, const QString &expr, bool *success) +{ + Q_D(QQmlEngineDebugClient); + d->exprResult = QVariant(); + quint32 id = -1; + *success = false; + if (state() == QQmlDebugClient::Enabled) { + id = getId(); + QPacket ds(connection()->currentDataStreamVersion()); + ds << QByteArray("EVAL_EXPRESSION") << id << objectDebugId << expr; + sendMessage(ds.data()); + *success = true; + } + return id; +} + +quint32 QQmlEngineDebugClient::setBindingForObject( + int objectDebugId, + const QString &propertyName, + const QVariant &bindingExpression, + bool isLiteralValue, + const QString &source, int line, + bool *success) +{ + quint32 id = -1; + *success = false; + if (state() == QQmlDebugClient::Enabled && objectDebugId != -1) { + id = getId(); + QPacket ds(connection()->currentDataStreamVersion()); + ds << QByteArray("SET_BINDING") << id << objectDebugId << propertyName + << bindingExpression << isLiteralValue << source << line; + sendMessage(ds.data()); + *success = true; + } + return id; +} + +quint32 QQmlEngineDebugClient::resetBindingForObject( + int objectDebugId, + const QString &propertyName, + bool *success) +{ + quint32 id = -1; + *success = false; + if (state() == QQmlDebugClient::Enabled && objectDebugId != -1) { + id = getId(); + QPacket ds(connection()->currentDataStreamVersion()); + ds << QByteArray("RESET_BINDING") << id << objectDebugId << propertyName; + sendMessage(ds.data()); + *success = true; + } + return id; +} + +quint32 QQmlEngineDebugClient::setMethodBody( + int objectDebugId, const QString &methodName, + const QString &methodBody, bool *success) +{ + quint32 id = -1; + *success = false; + if (state() == QQmlDebugClient::Enabled && objectDebugId != -1) { + id = getId(); + QPacket ds(connection()->currentDataStreamVersion()); + ds << QByteArray("SET_METHOD_BODY") << id << objectDebugId + << methodName << methodBody; + sendMessage(ds.data()); + *success = true; + } + return id; +} + +void QQmlEngineDebugClient::decode(QPacket &ds, + QQmlEngineDebugObjectReference &o, + bool simple) +{ + QQmlObjectData data; + ds >> data; + o.debugId = data.objectId; + o.className = data.objectType; + o.idString = data.idString; + o.name = data.objectName; + o.source.url = data.url; + o.source.lineNumber = data.lineNumber; + o.source.columnNumber = data.columnNumber; + o.contextDebugId = data.contextId; + + if (simple) + return; + + int childCount; + bool recur; + ds >> childCount >> recur; + + for (int ii = 0; ii < childCount; ++ii) { + o.children.append(QQmlEngineDebugObjectReference()); + decode(ds, o.children.last(), !recur); + } + + int propCount; + ds >> propCount; + + for (int ii = 0; ii < propCount; ++ii) { + QQmlObjectProperty data; + ds >> data; + QQmlEngineDebugPropertyReference prop; + prop.objectDebugId = o.debugId; + prop.name = data.name; + prop.binding = data.binding; + prop.hasNotifySignal = data.hasNotifySignal; + prop.valueTypeName = data.valueTypeName; + switch (data.type) { + case QQmlObjectProperty::Basic: + case QQmlObjectProperty::List: + case QQmlObjectProperty::SignalProperty: + { + prop.value = data.value; + break; + } + case QQmlObjectProperty::Object: + { + QQmlEngineDebugObjectReference obj; + obj.name = data.value.toString(); + obj.className = prop.valueTypeName; + prop.value = qVariantFromValue(obj); + break; + } + case QQmlObjectProperty::Unknown: + break; + } + o.properties << prop; + } +} + +void QQmlEngineDebugClient::decode(QPacket &ds, + QList<QQmlEngineDebugObjectReference> &o, + bool simple) +{ + int count; + ds >> count; + for (int i = 0; i < count; i++) { + QQmlEngineDebugObjectReference obj; + decode(ds, obj, simple); + o << obj; + } +} + +QList<QQmlEngineDebugEngineReference> QQmlEngineDebugClient::engines() const +{ + Q_D(const QQmlEngineDebugClient); + return d->engines; +} + +QQmlEngineDebugContextReference QQmlEngineDebugClient::rootContext() const +{ + Q_D(const QQmlEngineDebugClient); + return d->rootContext; +} + +QQmlEngineDebugObjectReference QQmlEngineDebugClient::object() const +{ + Q_D(const QQmlEngineDebugClient); + return d->object; +} + +QList<QQmlEngineDebugObjectReference> QQmlEngineDebugClient::objects() const +{ + Q_D(const QQmlEngineDebugClient); + return d->objects; +} + +QVariant QQmlEngineDebugClient::resultExpr() const +{ + Q_D(const QQmlEngineDebugClient); + return d->exprResult; +} + +bool QQmlEngineDebugClient::valid() const +{ + Q_D(const QQmlEngineDebugClient); + return d->valid; +} + +void QQmlEngineDebugClient::decode(QPacket &ds, + QQmlEngineDebugContextReference &c) +{ + ds >> c.name >> c.debugId; + + int contextCount; + ds >> contextCount; + + for (int ii = 0; ii < contextCount; ++ii) { + c.contexts.append(QQmlEngineDebugContextReference()); + decode(ds, c.contexts.last()); + } + + int objectCount; + ds >> objectCount; + + for (int ii = 0; ii < objectCount; ++ii) { + QQmlEngineDebugObjectReference obj; + decode(ds, obj, true); + + obj.contextDebugId = c.debugId; + c.objects << obj; + } +} + +void QQmlEngineDebugClient::messageReceived(const QByteArray &data) +{ + Q_D(QQmlEngineDebugClient); + d->valid = false; + QPacket ds(connection()->currentDataStreamVersion(), data); + + int queryId; + QByteArray type; + ds >> type >> queryId; + + //qDebug() << "QQmlEngineDebugPrivate::message()" << type; + + if (type == "LIST_ENGINES_R") { + int count; + ds >> count; + + d->engines.clear(); + for (int ii = 0; ii < count; ++ii) { + QQmlEngineDebugEngineReference eng; + ds >> eng.name; + ds >> eng.debugId; + d->engines << eng; + } + } else if (type == "LIST_OBJECTS_R") { + if (!ds.atEnd()) + decode(ds, d->rootContext); + + } else if (type == "FETCH_OBJECT_R") { + if (!ds.atEnd()) + decode(ds, d->object, false); + + } else if (type == "FETCH_OBJECTS_FOR_LOCATION_R") { + if (!ds.atEnd()) + decode(ds, d->objects, false); + + } else if (type == "EVAL_EXPRESSION_R") {; + ds >> d->exprResult; + + } else if (type == "WATCH_PROPERTY_R") { + ds >> d->valid; + + } else if (type == "WATCH_OBJECT_R") { + ds >> d->valid; + + } else if (type == "WATCH_EXPR_OBJECT_R") { + ds >> d->valid; + + } else if (type == "UPDATE_WATCH") { + int debugId; + QByteArray name; + QVariant value; + ds >> debugId >> name >> value; + emit valueChanged(name, value); + return; + + } else if (type == "OBJECT_CREATED") { + int engineId, objectId, parentId; + ds >> engineId >> objectId >> parentId; + emit newObject(objectId); + return; + } else if (type == "SET_BINDING_R") { + ds >> d->valid; + } else if (type == "RESET_BINDING_R") { + ds >> d->valid; + } else if (type == "SET_METHOD_BODY_R") { + ds >> d->valid; + } else if (type == "NO_WATCH_R") { + ds >> d->valid; + } + emit result(); +} + + +quint32 QQmlEngineDebugClient::getId() +{ + Q_D(QQmlEngineDebugClient); + return d->nextId++; +} + +QT_END_NAMESPACE diff --git a/src/qmldebug/qqmlenginedebugclient_p.h b/src/qmldebug/qqmlenginedebugclient_p.h new file mode 100644 index 0000000000..4a9cc3a020 --- /dev/null +++ b/src/qmldebug/qqmlenginedebugclient_p.h @@ -0,0 +1,168 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLENGINEDEBUGCLIENT_H +#define QQMLENGINEDEBUGCLIENT_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/qqmldebugclient_p.h> +#include <private/qpacket_p.h> + +#include <QtCore/qurl.h> +#include <QtCore/qvariant.h> + +QT_BEGIN_NAMESPACE + +struct QQmlEngineDebugPropertyReference +{ + int objectDebugId = -1; + QString name; + QVariant value; + QString valueTypeName; + QString binding; + bool hasNotifySignal = false; +}; + +struct QQmlEngineDebugFileReference +{ + QUrl url; + int lineNumber = -1; + int columnNumber = -1; +}; + +struct QQmlEngineDebugObjectReference +{ + int debugId = -1; + QString className; + QString idString; + QString name; + QQmlEngineDebugFileReference source; + int contextDebugId = -1; + QList<QQmlEngineDebugPropertyReference> properties; + QList<QQmlEngineDebugObjectReference> children; +}; + +struct QQmlEngineDebugContextReference +{ + int debugId = -1; + QString name; + QList<QQmlEngineDebugObjectReference> objects; + QList<QQmlEngineDebugContextReference> contexts; +}; + +struct QQmlEngineDebugEngineReference +{ + int debugId = -1; + QString name; +}; + +class QQmlEngineDebugClientPrivate; +class QQmlEngineDebugClient : public QQmlDebugClient +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QQmlEngineDebugClient) + +public: + explicit QQmlEngineDebugClient(QQmlDebugConnection *conn); + + quint32 addWatch(const QQmlEngineDebugPropertyReference &, + bool *success); + quint32 addWatch(const QQmlEngineDebugContextReference &, const QString &, + bool *success); + quint32 addWatch(const QQmlEngineDebugObjectReference &, const QString &, + bool *success); + quint32 addWatch(const QQmlEngineDebugObjectReference &, + bool *success); + quint32 addWatch(const QQmlEngineDebugFileReference &, + bool *success); + + void removeWatch(quint32 watch, bool *success); + + quint32 queryAvailableEngines(bool *success); + quint32 queryRootContexts(const QQmlEngineDebugEngineReference &, + bool *success); + quint32 queryObject(const QQmlEngineDebugObjectReference &, + bool *success); + quint32 queryObjectsForLocation(const QString &file, + int lineNumber, int columnNumber, bool *success); + quint32 queryObjectRecursive(const QQmlEngineDebugObjectReference &, + bool *success); + quint32 queryObjectsForLocationRecursive(const QString &file, + int lineNumber, int columnNumber, bool *success); + quint32 queryExpressionResult(int objectDebugId, + const QString &expr, + bool *success); + quint32 queryExpressionResultBC(int objectDebugId, + const QString &expr, + bool *success); + quint32 setBindingForObject(int objectDebugId, const QString &propertyName, + const QVariant &bindingExpression, + bool isLiteralValue, + const QString &source, int line, bool *success); + quint32 resetBindingForObject(int objectDebugId, + const QString &propertyName, bool *success); + quint32 setMethodBody(int objectDebugId, const QString &methodName, + const QString &methodBody, bool *success); + + quint32 getId(); + + void decode(QPacket &ds, QQmlEngineDebugContextReference &); + void decode(QPacket &ds, QQmlEngineDebugObjectReference &, bool simple); + void decode(QPacket &ds, QList<QQmlEngineDebugObjectReference> &o, bool simple); + + QList<QQmlEngineDebugEngineReference> engines() const; + QQmlEngineDebugContextReference rootContext() const; + QQmlEngineDebugObjectReference object() const; + QList<QQmlEngineDebugObjectReference> objects() const; + QVariant resultExpr() const; + bool valid() const; + +signals: + void newObject(int objectId); + void valueChanged(QByteArray,QVariant); + void result(); + +protected: + void messageReceived(const QByteArray &) override; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QQmlEngineDebugObjectReference) + +#endif // QQMLENGINEDEBUGCLIENT_H diff --git a/src/qmldebug/qqmlenginedebugclient_p_p.h b/src/qmldebug/qqmlenginedebugclient_p_p.h new file mode 100644 index 0000000000..7c992ad3ab --- /dev/null +++ b/src/qmldebug/qqmlenginedebugclient_p_p.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLENGINEDEBUGCLIENT_P_P_H +#define QQMLENGINEDEBUGCLIENT_P_P_H + +#include "qqmlenginedebugclient_p.h" +#include "qqmldebugclient_p_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 QQmlEngineDebugClientPrivate : public QQmlDebugClientPrivate +{ + Q_DECLARE_PUBLIC(QQmlEngineDebugClient) +public: + QQmlEngineDebugClientPrivate(QQmlDebugConnection *connection); + + quint32 nextId = 0; + bool valid = false; + QList<QQmlEngineDebugEngineReference> engines; + QQmlEngineDebugContextReference rootContext; + QQmlEngineDebugObjectReference object; + QList<QQmlEngineDebugObjectReference> objects; + QVariant exprResult; +}; + +QT_END_NAMESPACE + +#endif // QQMLENGINEDEBUGCLIENT_P_P_H diff --git a/src/qmldebug/qqmlinspectorclient.cpp b/src/qmldebug/qqmlinspectorclient.cpp new file mode 100644 index 0000000000..1de52bd0c1 --- /dev/null +++ b/src/qmldebug/qqmlinspectorclient.cpp @@ -0,0 +1,150 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlinspectorclient_p_p.h" + +#include <private/qpacket_p.h> +#include <private/qqmldebugconnection_p.h> +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE + +QQmlInspectorClient::QQmlInspectorClient(QQmlDebugConnection *connection) : + QQmlDebugClient(*new QQmlInspectorClientPrivate(connection)) +{ +} + +QQmlInspectorClientPrivate::QQmlInspectorClientPrivate(QQmlDebugConnection *connection) : + QQmlDebugClientPrivate(QLatin1String("QmlInspector"), connection) +{ +} + +int QQmlInspectorClient::setInspectToolEnabled(bool enabled) +{ + Q_D(QQmlInspectorClient); + QPacket ds(connection()->currentDataStreamVersion()); + ds << QByteArray("request") << ++(d->m_lastRequestId) + << QByteArray(enabled ? "enable" : "disable"); + + sendMessage(ds.data()); + return d->m_lastRequestId; +} + +int QQmlInspectorClient::setShowAppOnTop(bool showOnTop) +{ + Q_D(QQmlInspectorClient); + QPacket ds(connection()->currentDataStreamVersion()); + ds << QByteArray("request") << ++(d->m_lastRequestId) + << QByteArray("showAppOnTop") << showOnTop; + + sendMessage(ds.data()); + return d->m_lastRequestId; +} + +int QQmlInspectorClient::setAnimationSpeed(qreal speed) +{ + Q_D(QQmlInspectorClient); + QPacket ds(connection()->currentDataStreamVersion()); + ds << QByteArray("request") << ++(d->m_lastRequestId) + << QByteArray("setAnimationSpeed") << speed; + + sendMessage(ds.data()); + return d->m_lastRequestId; +} + +int QQmlInspectorClient::select(const QList<int> &objectIds) +{ + Q_D(QQmlInspectorClient); + QPacket ds(connection()->currentDataStreamVersion()); + ds << QByteArray("request") << ++(d->m_lastRequestId) + << QByteArray("select") << objectIds; + + sendMessage(ds.data()); + return d->m_lastRequestId; +} + +int QQmlInspectorClient::createObject(const QString &qml, int parentId, const QStringList &imports, + const QString &filename) +{ + Q_D(QQmlInspectorClient); + QPacket ds(connection()->currentDataStreamVersion()); + ds << QByteArray("request") << ++(d->m_lastRequestId) + << QByteArray("createObject") << qml << parentId << imports << filename; + sendMessage(ds.data()); + return d->m_lastRequestId; +} + +int QQmlInspectorClient::moveObject(int childId, int newParentId) +{ + Q_D(QQmlInspectorClient); + QPacket ds(connection()->currentDataStreamVersion()); + ds << QByteArray("request") << ++(d->m_lastRequestId) + << QByteArray("moveObject") << childId << newParentId; + sendMessage(ds.data()); + return d->m_lastRequestId; +} + +int QQmlInspectorClient::destroyObject(int objectId) +{ + Q_D(QQmlInspectorClient); + QPacket ds(connection()->currentDataStreamVersion()); + ds << QByteArray("request") << ++(d->m_lastRequestId) + << QByteArray("destroyObject") << objectId; + sendMessage(ds.data()); + return d->m_lastRequestId; +} + +void QQmlInspectorClient::messageReceived(const QByteArray &message) +{ + QPacket ds(connection()->currentDataStreamVersion(), message); + QByteArray type; + ds >> type; + + if (type != QByteArray("response")) { + qDebug() << "Unhandled message of type" << type; + return; + } + + int responseId; + bool result; + ds >> responseId >> result; + emit responseReceived(responseId, result); +} + +QT_END_NAMESPACE diff --git a/src/qmldebug/qqmlinspectorclient_p.h b/src/qmldebug/qqmlinspectorclient_p.h new file mode 100644 index 0000000000..d4ab136c9a --- /dev/null +++ b/src/qmldebug/qqmlinspectorclient_p.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLINSPECTORCLIENT_P_H +#define QQMLINSPECTORCLIENT_P_H + +#include <private/qqmldebugclient_p.h> + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +QT_BEGIN_NAMESPACE + +class QQmlInspectorClientPrivate; +class QQmlInspectorClient : public QQmlDebugClient +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QQmlInspectorClient) + +public: + QQmlInspectorClient(QQmlDebugConnection *connection); + + int setInspectToolEnabled(bool enabled); + int setShowAppOnTop(bool showOnTop); + int setAnimationSpeed(qreal speed); + int select(const QList<int> &objectIds); + int createObject(const QString &qml, int parentId, const QStringList &imports, + const QString &filename); + int moveObject(int childId, int newParentId); + int destroyObject(int objectId); + +signals: + void responseReceived(int requestId, bool result); + +protected: + void messageReceived(const QByteArray &message) override; +}; + +QT_END_NAMESPACE + +#endif // QQMLINSPECTORCLIENT_P_H diff --git a/src/qmldebug/qqmleventlocation_p.h b/src/qmldebug/qqmlinspectorclient_p_p.h index c3a2b93f0f..91537dd994 100644 --- a/src/qmldebug/qqmleventlocation_p.h +++ b/src/qmldebug/qqmlinspectorclient_p_p.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtQml module of the Qt Toolkit. @@ -37,10 +37,11 @@ ** ****************************************************************************/ -#ifndef QQMLEVENTLOCATION_P_H -#define QQMLEVENTLOCATION_P_H +#ifndef QQMLINSPECTORCLIENT_P_P_H +#define QQMLINSPECTORCLIENT_P_P_H -#include <QtCore/qstring.h> +#include "qqmlinspectorclient_p.h" +#include "qqmldebugclient_p_p.h" // // W A R N I N G @@ -55,19 +56,14 @@ QT_BEGIN_NAMESPACE -struct QQmlEventLocation +class QQmlInspectorClientPrivate : public QQmlDebugClientPrivate { - QQmlEventLocation() : line(-1), column(-1) {} - QQmlEventLocation(const QString &file, int lineNumber, int columnNumber) : - filename(file), line(lineNumber), column(columnNumber) {} - - QString filename; - int line; - int column; + Q_DECLARE_PUBLIC(QQmlInspectorClient) +public: + QQmlInspectorClientPrivate(QQmlDebugConnection *connection); + int m_lastRequestId = -1; }; -Q_DECLARE_TYPEINFO(QQmlEventLocation, Q_MOVABLE_TYPE); - QT_END_NAMESPACE -#endif // QQMLEVENTLOCATION_P_H +#endif // QQMLINSPECTORCLIENT_P_P_H diff --git a/src/qmldebug/qqmlpreviewclient.cpp b/src/qmldebug/qqmlpreviewclient.cpp new file mode 100644 index 0000000000..60937b9cfd --- /dev/null +++ b/src/qmldebug/qqmlpreviewclient.cpp @@ -0,0 +1,139 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qqmlpreviewclient_p_p.h" +#include <private/qpacket_p.h> + +#include <QtCore/qurl.h> +#include <QtCore/qfile.h> +#include <QtCore/qfileinfo.h> +#include <QtCore/qdir.h> +#include <QtQml/qqmlfile.h> + +QT_BEGIN_NAMESPACE + +QQmlPreviewClient::QQmlPreviewClient(QQmlDebugConnection *connection) + : QQmlDebugClient(*(new QQmlPreviewClientPrivate(connection))) +{ +} + +void QQmlPreviewClient::messageReceived(const QByteArray &message) +{ + QPacket packet(connection()->currentDataStreamVersion(), message); + + qint8 command; + packet >> command; + + switch (command) { + case Error: { + QString seviceError; + packet >> seviceError; + emit error(seviceError); + break; + } + case Request: { + QString fileName; + packet >> fileName; + emit request(fileName); + break; + } + case Fps: { + FpsInfo info; + packet >> info.numSyncs >> info.minSync >> info.maxSync >> info.totalSync + >> info.numRenders >> info.minRender >> info.maxRender >> info.totalRender; + emit fps(info); + break; + } + default: + emit error(QString::fromLatin1("Unknown command received: %1").arg(command)); + break; + } +} + +void QQmlPreviewClient::sendDirectory(const QString &path, const QStringList &entries) +{ + QPacket packet(connection()->currentDataStreamVersion()); + packet << static_cast<qint8>(Directory) << path << entries; + sendMessage(packet.data()); +} + +void QQmlPreviewClient::sendFile(const QString &path, const QByteArray &contents) +{ + QPacket packet(connection()->currentDataStreamVersion()); + packet << static_cast<qint8>(File) << path << contents; + sendMessage(packet.data()); +} + +void QQmlPreviewClient::sendError(const QString &path) +{ + QPacket packet(connection()->currentDataStreamVersion()); + packet << static_cast<qint8>(Error) << path; + sendMessage(packet.data()); +} + +void QQmlPreviewClient::triggerLoad(const QUrl &url) +{ + QPacket packet(connection()->currentDataStreamVersion()); + packet << static_cast<qint8>(Load) << url; + sendMessage(packet.data()); +} + +void QQmlPreviewClient::triggerRerun() +{ + QPacket packet(connection()->currentDataStreamVersion()); + packet << static_cast<qint8>(Rerun); + sendMessage(packet.data()); +} + +void QQmlPreviewClient::triggerZoom(float factor) +{ + QPacket packet(connection()->currentDataStreamVersion()); + packet << static_cast<qint8>(Zoom) << factor; + sendMessage(packet.data()); +} + +void QQmlPreviewClient::triggerLanguage(const QUrl &url, const QString &locale) +{ + QPacket packet(connection()->currentDataStreamVersion()); + packet << static_cast<qint8>(Language) << url << locale; + sendMessage(packet.data()); +} + +QT_END_NAMESPACE diff --git a/src/qmldebug/qqmlpreviewclient_p.h b/src/qmldebug/qqmlpreviewclient_p.h new file mode 100644 index 0000000000..65661613e9 --- /dev/null +++ b/src/qmldebug/qqmlpreviewclient_p.h @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QQMLPREVIEWCLIENT_P_H +#define QQMLPREVIEWCLIENT_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/qqmldebugclient_p.h> +#include <private/qqmldebugconnection_p.h> + +QT_BEGIN_NAMESPACE + +class QQmlPreviewClientPrivate; +class QQmlPreviewClient : public QQmlDebugClient +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QQmlPreviewClient) +public: + enum Command { + File, + Load, + Request, + Error, + Rerun, + Directory, + ClearCache, + Zoom, + Fps, + Language + }; + + struct FpsInfo { + quint16 numSyncs = 0; + quint16 minSync = std::numeric_limits<quint16>::max(); + quint16 maxSync = 0; + quint16 totalSync = 0; + + quint16 numRenders = 0; + quint16 minRender = std::numeric_limits<quint16>::max(); + quint16 maxRender = 0; + quint16 totalRender = 0; + }; + + QQmlPreviewClient(QQmlDebugConnection *parent); + void messageReceived(const QByteArray &message) override; + + void sendDirectory(const QString &path, const QStringList &entries); + void sendFile(const QString &path, const QByteArray &contents); + void sendError(const QString &path); + + void triggerLoad(const QUrl &url); + void triggerRerun(); + void triggerZoom(float factor); + void triggerLanguage(const QUrl &url, const QString &locale); + +signals: + void request(const QString &path); + void error(const QString &message); + void fps(const FpsInfo &info); +}; + +QT_END_NAMESPACE + +#endif // QQMLPREVIEWCLIENT_P_H diff --git a/src/qmldebug/qqmlpreviewclient_p_p.h b/src/qmldebug/qqmlpreviewclient_p_p.h new file mode 100644 index 0000000000..8fb2b4b18c --- /dev/null +++ b/src/qmldebug/qqmlpreviewclient_p_p.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLPREVIEWCLIENT_P_P_H +#define QQMLPREVIEWCLIENT_P_P_H + +#include "qqmlpreviewclient_p.h" +#include "qqmldebugclient_p_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 QQmlPreviewClientPrivate : public QQmlDebugClientPrivate +{ + Q_DECLARE_PUBLIC(QQmlPreviewClient) +public: + QQmlPreviewClientPrivate(QQmlDebugConnection *connection) + : QQmlDebugClientPrivate(QLatin1String("QmlPreview"), connection) + {} +}; + +QT_END_NAMESPACE + +#endif // QQMLPREVIEWCLIENT_P_P_H diff --git a/src/qmldebug/qqmlprofilerclient.cpp b/src/qmldebug/qqmlprofilerclient.cpp index 3676bd933c..5477af89d4 100644 --- a/src/qmldebug/qqmlprofilerclient.cpp +++ b/src/qmldebug/qqmlprofilerclient.cpp @@ -39,341 +39,330 @@ #include "qqmlprofilerclient_p_p.h" #include "qqmldebugconnection_p.h" -#include <private/qqmldebugserviceinterfaces_p.h> QT_BEGIN_NAMESPACE -QQmlProfilerClient::QQmlProfilerClient(QQmlDebugConnection *connection) : - QQmlDebugClient(*(new QQmlProfilerClientPrivate(connection))) +QQmlProfilerClientPrivate::~QQmlProfilerClientPrivate() { } -QQmlProfilerClient::QQmlProfilerClient(QQmlProfilerClientPrivate &dd) : - QQmlDebugClient(dd) +int QQmlProfilerClientPrivate::resolveType(const QQmlProfilerTypedEvent &event) { + int typeIndex = -1; + if (event.serverTypeId != 0) { + QHash<qint64, int>::ConstIterator it = serverTypeIds.constFind(event.serverTypeId); + + if (it != serverTypeIds.constEnd()) { + typeIndex = it.value(); + } else { + typeIndex = eventReceiver->numLoadedEventTypes(); + eventReceiver->addEventType(event.type); + serverTypeIds[event.serverTypeId] = typeIndex; + } + } else { + QHash<QQmlProfilerEventType, int>::ConstIterator it = eventTypeIds.constFind(event.type); + + if (it != eventTypeIds.constEnd()) { + typeIndex = it.value(); + } else { + typeIndex = eventReceiver->numLoadedEventTypes(); + eventReceiver->addEventType(event.type); + eventTypeIds[event.type] = typeIndex; + } + } + return typeIndex; } -QQmlProfilerClientPrivate::QQmlProfilerClientPrivate(QQmlDebugConnection *connection) : - QQmlDebugClientPrivate(QQmlProfilerService::s_key, connection), - features(std::numeric_limits<quint64>::max()) +int QQmlProfilerClientPrivate::resolveStackTop() { + if (rangesInProgress.isEmpty()) + return -1; + + QQmlProfilerTypedEvent &typedEvent = rangesInProgress.top(); + int typeIndex = typedEvent.event.typeIndex(); + if (typeIndex >= 0) + return typeIndex; + + typeIndex = resolveType(typedEvent); + typedEvent.event.setTypeIndex(typeIndex); + while (!pendingMessages.isEmpty() + && pendingMessages.head().timestamp() < typedEvent.event.timestamp()) { + forwardEvents(pendingMessages.dequeue()); + } + forwardEvents(typedEvent.event); + return typeIndex; } -void QQmlProfilerClient::setFeatures(quint64 features) +void QQmlProfilerClientPrivate::forwardEvents(const QQmlProfilerEvent &last) { - Q_D(QQmlProfilerClient); - d->features = features; + forwardDebugMessages(last.timestamp()); + eventReceiver->addEvent(last); } -void QQmlProfilerClient::sendRecordingStatus(bool record, int engineId, quint32 flushInterval) +void QQmlProfilerClientPrivate::forwardDebugMessages(qint64 untilTimestamp) { - Q_D(const QQmlProfilerClient); + while (!pendingDebugMessages.isEmpty() + && pendingDebugMessages.front().timestamp() <= untilTimestamp) { + eventReceiver->addEvent(pendingDebugMessages.dequeue()); + } +} - QPacket stream(d->connection->currentDataStreamVersion()); - stream << record << engineId << d->features << flushInterval << true; - sendMessage(stream.data()); +void QQmlProfilerClientPrivate::processCurrentEvent() +{ + // RangeData and RangeLocation always apply to the range on the top of the stack. Furthermore, + // all ranges are perfectly nested. This is why we can defer the type resolution until either + // the range ends or a child range starts. With only the information in RangeStart we wouldn't + // be able to uniquely identify the event type. + Message rangeStage = currentEvent.type.rangeType() == MaximumRangeType ? + currentEvent.type.message() : currentEvent.event.rangeStage(); + switch (rangeStage) { + case RangeStart: + resolveStackTop(); + rangesInProgress.push(currentEvent); + break; + case RangeEnd: { + int typeIndex = resolveStackTop(); + if (typeIndex == -1) + break; + currentEvent.event.setTypeIndex(typeIndex); + while (!pendingMessages.isEmpty()) + forwardEvents(pendingMessages.dequeue()); + forwardEvents(currentEvent.event); + rangesInProgress.pop(); + break; + } + case RangeData: + if (!rangesInProgress.isEmpty()) + rangesInProgress.top().type.setData(currentEvent.type.data()); + break; + case RangeLocation: + if (!rangesInProgress.isEmpty()) + rangesInProgress.top().type.setLocation(currentEvent.type.location()); + break; + case DebugMessage: + currentEvent.event.setTypeIndex(resolveType(currentEvent)); + pendingDebugMessages.enqueue(currentEvent.event); + break; + default: { + int typeIndex = resolveType(currentEvent); + currentEvent.event.setTypeIndex(typeIndex); + if (rangesInProgress.isEmpty()) + forwardEvents(currentEvent.event); + else + pendingMessages.enqueue(currentEvent.event); + break; + } + } } -void QQmlProfilerClient::traceStarted(qint64 time, int engineId) +void QQmlProfilerClientPrivate::sendRecordingStatus(int engineId) { - Q_UNUSED(time); - Q_UNUSED(engineId); + Q_Q(QQmlProfilerClient); + QPacket stream(connection->currentDataStreamVersion()); + stream << recording << engineId; // engineId -1 is OK. It means "all of them" + if (recording) { + stream << requestedFeatures << flushInterval; + stream << true; // yes, we support type IDs + } + q->sendMessage(stream.data()); } -void QQmlProfilerClient::traceFinished(qint64 time, int engineId) +QQmlProfilerClient::QQmlProfilerClient(QQmlDebugConnection *connection, + QQmlProfilerEventReceiver *eventReceiver, + quint64 features) + : QQmlDebugClient(*(new QQmlProfilerClientPrivate(connection, eventReceiver))) { - Q_UNUSED(time); - Q_UNUSED(engineId); + Q_D(QQmlProfilerClient); + setRequestedFeatures(features); + connect(this, &QQmlDebugClient::stateChanged, this, &QQmlProfilerClient::onStateChanged); + connect(d->engineControl.data(), &QQmlEngineControlClient::engineAboutToBeAdded, + this, &QQmlProfilerClient::sendRecordingStatus); + connect(d->engineControl.data(), &QQmlEngineControlClient::engineAboutToBeRemoved, + this, [d](int engineId) { + // We may already be done with that engine. Then we don't need to block it. + if (d->trackedEngines.contains(engineId)) + d->engineControl->blockEngine(engineId); + }); + connect(this, &QQmlProfilerClient::traceFinished, + d->engineControl.data(), [d](qint64 timestamp, const QList<int> &engineIds) { + Q_UNUSED(timestamp); + // The engines might not be blocked because the trace can get finished before engine control + // sees them. + for (int blocked : d->engineControl->blockedEngines()) { + if (engineIds.contains(blocked)) + d->engineControl->releaseEngine(blocked); + } + }); } -void QQmlProfilerClient::rangeStart(QQmlProfilerDefinitions::RangeType type, qint64 startTime) +QQmlProfilerClient::~QQmlProfilerClient() { - Q_UNUSED(type); - Q_UNUSED(startTime); + //Disable profiling if started by client + //Profiling data will be lost!! + if (isRecording()) + setRecording(false); } -void QQmlProfilerClient::rangeData(QQmlProfilerDefinitions::RangeType type, qint64 time, - const QString &data) +void QQmlProfilerClient::clearEvents() { - Q_UNUSED(type); - Q_UNUSED(time); - Q_UNUSED(data); + Q_D(QQmlProfilerClient); + d->rangesInProgress.clear(); + d->pendingMessages.clear(); + d->pendingDebugMessages.clear(); + if (d->recordedFeatures != 0) { + d->recordedFeatures = 0; + emit recordedFeaturesChanged(0); + } + emit cleared(); } -void QQmlProfilerClient::rangeLocation(QQmlProfilerDefinitions::RangeType type, qint64 time, - const QQmlEventLocation &location) +void QQmlProfilerClient::clearAll() { - Q_UNUSED(type); - Q_UNUSED(time); - Q_UNUSED(location); + Q_D(QQmlProfilerClient); + d->serverTypeIds.clear(); + d->eventTypeIds.clear(); + d->trackedEngines.clear(); + clearEvents(); } -void QQmlProfilerClient::rangeEnd(QQmlProfilerDefinitions::RangeType type, qint64 endTime) +void QQmlProfilerClientPrivate::finalize() { - Q_UNUSED(type); - Q_UNUSED(endTime); + while (!rangesInProgress.isEmpty()) { + currentEvent = rangesInProgress.top(); + currentEvent.event.setRangeStage(RangeEnd); + currentEvent.event.setTimestamp(maximumTime); + processCurrentEvent(); + } + forwardDebugMessages(std::numeric_limits<qint64>::max()); } -void QQmlProfilerClient::animationFrame(qint64 time, int frameRate, int animationCount, - int threadId) + +void QQmlProfilerClient::sendRecordingStatus(int engineId) { - Q_UNUSED(time); - Q_UNUSED(frameRate); - Q_UNUSED(animationCount); - Q_UNUSED(threadId); + Q_D(QQmlProfilerClient); + d->sendRecordingStatus(engineId); } -void QQmlProfilerClient::sceneGraphEvent(QQmlProfilerDefinitions::SceneGraphFrameType type, - qint64 time, qint64 numericData1, qint64 numericData2, - qint64 numericData3, qint64 numericData4, - qint64 numericData5) +bool QQmlProfilerClient::isRecording() const { - Q_UNUSED(type); - Q_UNUSED(time); - Q_UNUSED(numericData1); - Q_UNUSED(numericData2); - Q_UNUSED(numericData3); - Q_UNUSED(numericData4); - Q_UNUSED(numericData5); + Q_D(const QQmlProfilerClient); + return d->recording; } -void QQmlProfilerClient::pixmapCacheEvent(QQmlProfilerDefinitions::PixmapEventType type, - qint64 time, const QString &url, int numericData1, - int numericData2) +void QQmlProfilerClient::setRecording(bool v) { - Q_UNUSED(type); - Q_UNUSED(time); - Q_UNUSED(url); - Q_UNUSED(numericData1); - Q_UNUSED(numericData2); + Q_D(QQmlProfilerClient); + if (v == d->recording) + return; + + d->recording = v; + + if (state() == Enabled) + sendRecordingStatus(); + + emit recordingChanged(v); } -void QQmlProfilerClient::memoryAllocation(QQmlProfilerDefinitions::MemoryType type, qint64 time, - qint64 amount) +quint64 QQmlProfilerClient::recordedFeatures() const { - Q_UNUSED(type); - Q_UNUSED(time); - Q_UNUSED(amount); + Q_D(const QQmlProfilerClient); + return d->recordedFeatures; } -void QQmlProfilerClient::inputEvent(QQmlProfilerDefinitions::InputEventType type, qint64 time, - int a, int b) +void QQmlProfilerClient::setRequestedFeatures(quint64 features) { - Q_UNUSED(type); - Q_UNUSED(time); - Q_UNUSED(a); - Q_UNUSED(b); + Q_D(QQmlProfilerClient); + d->requestedFeatures = features; + if (features & static_cast<quint64>(1) << ProfileDebugMessages) { + if (d->messageClient.isNull()) { + d->messageClient.reset(new QQmlDebugMessageClient(connection())); + connect(d->messageClient.data(), &QQmlDebugMessageClient::message, this, + [this](QtMsgType type, const QString &text, const QQmlDebugContextInfo &context) + { + Q_D(QQmlProfilerClient); + d->updateFeatures(ProfileDebugMessages); + d->currentEvent.event.setTimestamp(context.timestamp > 0 ? context.timestamp : 0); + d->currentEvent.event.setTypeIndex(-1); + d->currentEvent.event.setString(text); + d->currentEvent.type = QQmlProfilerEventType( + DebugMessage, MaximumRangeType, type, + QQmlProfilerEventLocation(context.file, context.line, 1)); + d->currentEvent.serverTypeId = 0; + d->processCurrentEvent(); + }); + } + } else { + d->messageClient.reset(); + } } -void QQmlProfilerClient::complete() +void QQmlProfilerClient::setFlushInterval(quint32 flushInterval) { + Q_D(QQmlProfilerClient); + d->flushInterval = flushInterval; } -void QQmlProfilerClient::unknownEvent(QQmlProfilerDefinitions::Message messageType, qint64 time, - int detailType) +QQmlProfilerClient::QQmlProfilerClient(QQmlProfilerClientPrivate &dd) : + QQmlDebugClient(dd) { - Q_UNUSED(messageType); - Q_UNUSED(time); - Q_UNUSED(detailType); + Q_D(QQmlProfilerClient); + connect(d->engineControl.data(), &QQmlEngineControlClient::engineAboutToBeAdded, + this, &QQmlProfilerClient::sendRecordingStatus); } -void QQmlProfilerClient::unknownData(QPacket &stream) +bool QQmlProfilerClientPrivate::updateFeatures(ProfileFeature feature) { - Q_UNUSED(stream); + Q_Q(QQmlProfilerClient); + quint64 flag = 1ULL << feature; + if (!(requestedFeatures & flag)) + return false; + if (!(recordedFeatures & flag)) { + recordedFeatures |= flag; + emit q->recordedFeaturesChanged(recordedFeatures); + } + return true; } -inline QQmlProfilerDefinitions::ProfileFeature featureFromRangeType( - QQmlProfilerDefinitions::RangeType range) +void QQmlProfilerClient::onStateChanged(State status) { - switch (range) { - case QQmlProfilerDefinitions::Painting: - return QQmlProfilerDefinitions::ProfilePainting; - case QQmlProfilerDefinitions::Compiling: - return QQmlProfilerDefinitions::ProfileCompiling; - case QQmlProfilerDefinitions::Creating: - return QQmlProfilerDefinitions::ProfileCreating; - case QQmlProfilerDefinitions::Binding: - return QQmlProfilerDefinitions::ProfileBinding; - case QQmlProfilerDefinitions::HandlingSignal: - return QQmlProfilerDefinitions::ProfileHandlingSignal; - case QQmlProfilerDefinitions::Javascript: - return QQmlProfilerDefinitions::ProfileJavaScript; - default: - return QQmlProfilerDefinitions::MaximumProfileFeature; + if (status == Enabled) { + sendRecordingStatus(-1); + } else { + Q_D(QQmlProfilerClient); + d->finalize(); } + } void QQmlProfilerClient::messageReceived(const QByteArray &data) { Q_D(QQmlProfilerClient); - QPacket stream(d->connection->currentDataStreamVersion(), data); - // Force all the 1 << <FLAG> expressions to be done in 64 bit, to silence some warnings - const quint64 one = static_cast<quint64>(1); - - qint64 time; - int messageType; - - stream >> time >> messageType; - - if (messageType >= QQmlProfilerDefinitions::MaximumMessage) { - unknownEvent(static_cast<QQmlProfilerDefinitions::Message>(messageType), time, -1); - return; + stream >> d->currentEvent; + + d->maximumTime = qMax(d->currentEvent.event.timestamp(), d->maximumTime); + if (d->currentEvent.type.message() == Complete) { + d->finalize(); + emit complete(d->maximumTime); + } else if (d->currentEvent.type.message() == Event + && d->currentEvent.type.detailType() == StartTrace) { + const QList<int> engineIds = d->currentEvent.event.numbers<QList<int>, qint32>(); + d->trackedEngines.append(engineIds); + d->forwardDebugMessages(d->currentEvent.event.timestamp()); + emit traceStarted(d->currentEvent.event.timestamp(), engineIds); + } else if (d->currentEvent.type.message() == Event + && d->currentEvent.type.detailType() == EndTrace) { + const QList<int> engineIds = d->currentEvent.event.numbers<QList<int>, qint32>(); + for (int engineId : engineIds) + d->trackedEngines.removeAll(engineId); + d->forwardDebugMessages(d->currentEvent.event.timestamp()); + emit traceFinished(d->currentEvent.event.timestamp(), engineIds); + } else if (d->updateFeatures(d->currentEvent.type.feature())) { + d->processCurrentEvent(); } - - if (messageType == QQmlProfilerDefinitions::Event) { - int type; - stream >> type; - - QQmlProfilerDefinitions::EventType eventType = - static_cast<QQmlProfilerDefinitions::EventType>(type); - - if (eventType == QQmlProfilerDefinitions::EndTrace) { - int engineId = -1; - if (!stream.atEnd()) - stream >> engineId; - traceFinished(time, engineId); - } else if (eventType == QQmlProfilerDefinitions::AnimationFrame) { - if (!(d->features & one << QQmlProfilerDefinitions::ProfileAnimations)) - return; - - int frameRate, animationCount; - int threadId = 0; - stream >> frameRate >> animationCount; - if (!stream.atEnd()) - stream >> threadId; - - animationFrame(time, frameRate, animationCount, threadId); - } else if (type == QQmlProfilerDefinitions::StartTrace) { - int engineId = -1; - if (!stream.atEnd()) - stream >> engineId; - traceStarted(time, engineId); - } else if (eventType == QQmlProfilerDefinitions::Key || - eventType == QQmlProfilerDefinitions::Mouse) { - - if (!(d->features & one << QQmlProfilerDefinitions::ProfileInputEvents)) - return; - - int type; - if (!stream.atEnd()) { - stream >> type; - } else { - type = (eventType == QQmlProfilerDefinitions::Key) ? - QQmlProfilerDefinitions::InputKeyUnknown : - QQmlProfilerDefinitions::InputMouseUnknown; - } - - int a = 0; - if (!stream.atEnd()) - stream >> a; - - int b = 0; - if (!stream.atEnd()) - stream >> b; - - inputEvent(static_cast<QQmlProfilerDefinitions::InputEventType>(type), time, a, b); - } else { - unknownEvent(QQmlProfilerDefinitions::Event, time, type); - } - } else if (messageType == QQmlProfilerDefinitions::Complete) { - complete(); - } else if (messageType == QQmlProfilerDefinitions::SceneGraphFrame) { - if (!(d->features & one << QQmlProfilerDefinitions::ProfileSceneGraph)) - return; - - int type; - int count = 0; - qint64 params[5]; - - stream >> type; - while (!stream.atEnd()) - stream >> params[count++]; - - while (count < 5) - params[count++] = 0; - - sceneGraphEvent(static_cast<QQmlProfilerDefinitions::SceneGraphFrameType>(type), time, - params[0], params[1], params[2], params[3], params[4]); - } else if (messageType == QQmlProfilerDefinitions::PixmapCacheEvent) { - if (!(d->features & one << QQmlProfilerDefinitions::ProfilePixmapCache)) - return; - - int type, param1 = 0, param2 = 0; - QString pixUrl; - stream >> type >> pixUrl; - - QQmlProfilerDefinitions::PixmapEventType pixmapEventType = - static_cast<QQmlProfilerDefinitions::PixmapEventType>(type); - - if (pixmapEventType == QQmlProfilerDefinitions::PixmapReferenceCountChanged || - pixmapEventType == QQmlProfilerDefinitions::PixmapCacheCountChanged) { - stream >> param1; - } else if (pixmapEventType == QQmlProfilerDefinitions::PixmapSizeKnown) { - stream >> param1 >> param2; - } - - pixmapCacheEvent(pixmapEventType, time, pixUrl, param1, param2); - } else if (messageType == QQmlProfilerDefinitions::MemoryAllocation) { - if (!(d->features & one << QQmlProfilerDefinitions::ProfileMemory)) - return; - int type; - qint64 delta; - stream >> type >> delta; - memoryAllocation((QQmlProfilerDefinitions::MemoryType)type, time, delta); - } else { - int range; - stream >> range; - - QQmlProfilerDefinitions::RangeType rangeType = - static_cast<QQmlProfilerDefinitions::RangeType>(range); - - if (range >= QQmlProfilerDefinitions::MaximumRangeType || - !(d->features & one << featureFromRangeType(rangeType))) - return; - - qint64 typeId = 0; - if (messageType == QQmlProfilerDefinitions::RangeStart) { - rangeStart(rangeType, time); - if (!stream.atEnd()) { - stream >> typeId; - auto i = d->types.constFind(typeId); - if (i != d->types.constEnd()) { - rangeLocation(rangeType, time, i->location); - rangeData(rangeType, time, i->name); - } - } - } else if (messageType == QQmlProfilerDefinitions::RangeData) { - QString data; - stream >> data; - rangeData(rangeType, time, data); - if (!stream.atEnd()) { - stream >> typeId; - d->types[typeId].name = data; - } - } else if (messageType == QQmlProfilerDefinitions::RangeLocation) { - QQmlEventLocation location; - stream >> location.filename >> location.line; - - if (!stream.atEnd()) - stream >> location.column; - - rangeLocation(rangeType, time, location); - if (!stream.atEnd()) { - stream >> typeId; - d->types[typeId].location = location; - } - } else if (messageType == QQmlProfilerDefinitions::RangeEnd) { - rangeEnd(rangeType, time); - } else { - unknownEvent(static_cast<QQmlProfilerDefinitions::Message>(messageType), time, range); - } - } - - if (!stream.atEnd()) - unknownData(stream); } + QT_END_NAMESPACE #include "moc_qqmlprofilerclient_p.cpp" diff --git a/src/qmldebug/qqmlprofilerclient_p.h b/src/qmldebug/qqmlprofilerclient_p.h index a328cad26c..89c117a8b5 100644 --- a/src/qmldebug/qqmlprofilerclient_p.h +++ b/src/qmldebug/qqmlprofilerclient_p.h @@ -41,8 +41,10 @@ #define QQMLPROFILERCLIENT_P_H #include "qqmldebugclient_p.h" -#include "qqmleventlocation_p.h" -#include <private/qqmlprofilerdefinitions_p.h> +#include "qqmlprofilereventlocation_p.h" +#include "qqmlprofilereventreceiver_p.h" +#include "qqmlprofilerclientdefinitions_p.h" + #include <private/qpacket_p.h> // @@ -63,48 +65,38 @@ class QQmlProfilerClient : public QQmlDebugClient { Q_OBJECT Q_DECLARE_PRIVATE(QQmlProfilerClient) + Q_PROPERTY(bool recording READ isRecording WRITE setRecording NOTIFY recordingChanged) public: - QQmlProfilerClient(QQmlDebugConnection *connection); - void setFeatures(quint64 features); - void sendRecordingStatus(bool record, int engineId = -1, quint32 flushInterval = 0); - -protected: - QQmlProfilerClient(QQmlProfilerClientPrivate &dd); - -private: - void messageReceived(const QByteArray &message) override; + QQmlProfilerClient(QQmlDebugConnection *connection, QQmlProfilerEventReceiver *eventReceiver, + quint64 features = std::numeric_limits<quint64>::max()); + ~QQmlProfilerClient(); - virtual void traceStarted(qint64 time, int engineId); - virtual void traceFinished(qint64 time, int engineId); + bool isRecording() const; + void setRecording(bool); + quint64 recordedFeatures() const; + virtual void messageReceived(const QByteArray &) override; - virtual void rangeStart(QQmlProfilerDefinitions::RangeType type, qint64 startTime); - virtual void rangeData(QQmlProfilerDefinitions::RangeType type, qint64 time, - const QString &data); - virtual void rangeLocation(QQmlProfilerDefinitions::RangeType type, qint64 time, - const QQmlEventLocation &location); - virtual void rangeEnd(QQmlProfilerDefinitions::RangeType type, qint64 endTime); + void clearEvents(); + void clearAll(); - virtual void animationFrame(qint64 time, int frameRate, int animationCount, int threadId); + void sendRecordingStatus(int engineId = -1); + void setRequestedFeatures(quint64 features); + void setFlushInterval(quint32 flushInterval); - virtual void sceneGraphEvent(QQmlProfilerDefinitions::SceneGraphFrameType type, qint64 time, - qint64 numericData1, qint64 numericData2, qint64 numericData3, - qint64 numericData4, qint64 numericData5); - - virtual void pixmapCacheEvent(QQmlProfilerDefinitions::PixmapEventType type, qint64 time, - const QString &url, int numericData1, int numericData2); - - virtual void memoryAllocation(QQmlProfilerDefinitions::MemoryType type, qint64 time, - qint64 amount); +protected: + QQmlProfilerClient(QQmlProfilerClientPrivate &dd); + void onStateChanged(State status); - virtual void inputEvent(QQmlProfilerDefinitions::InputEventType type, qint64 time, int a, - int b); +signals: + void complete(qint64 maximumTime); + void traceFinished(qint64 timestamp, const QList<int> &engineIds); + void traceStarted(qint64 timestamp, const QList<int> &engineIds); - virtual void complete(); + void recordingChanged(bool arg); + void recordedFeaturesChanged(quint64 features); - virtual void unknownEvent(QQmlProfilerDefinitions::Message messageType, qint64 time, - int detailType); - virtual void unknownData(QPacket &stream); + void cleared(); }; QT_END_NAMESPACE diff --git a/src/qmldebug/qqmlprofilerclient_p_p.h b/src/qmldebug/qqmlprofilerclient_p_p.h index 9c44113aa8..52d42eae82 100644 --- a/src/qmldebug/qqmlprofilerclient_p_p.h +++ b/src/qmldebug/qqmlprofilerclient_p_p.h @@ -40,8 +40,15 @@ #ifndef QQMLPROFILERCLIENT_P_P_H #define QQMLPROFILERCLIENT_P_P_H -#include "qqmlprofilerclient_p.h" #include "qqmldebugclient_p_p.h" +#include "qqmldebugmessageclient_p.h" +#include "qqmlenginecontrolclient_p.h" +#include "qqmlprofilerclient_p.h" +#include "qqmlprofilertypedevent_p.h" +#include "qqmlprofilerclientdefinitions_p.h" + +#include <QtCore/qqueue.h> +#include <QtCore/qstack.h> // // W A R N I N G @@ -56,23 +63,53 @@ QT_BEGIN_NAMESPACE -struct QQmlProfilerRangeType -{ - QQmlEventLocation location; - QString name; -}; - -class QQmlProfilerClientPrivate : public QQmlDebugClientPrivate -{ +class QQmlProfilerClientPrivate : public QQmlDebugClientPrivate { Q_DECLARE_PUBLIC(QQmlProfilerClient) public: - QQmlProfilerClientPrivate(QQmlDebugConnection *connection); - quint64 features; + QQmlProfilerClientPrivate(QQmlDebugConnection *connection, + QQmlProfilerEventReceiver *eventReceiver) + : QQmlDebugClientPrivate(QLatin1String("CanvasFrameRate"), connection) + , eventReceiver(eventReceiver) + , engineControl(new QQmlEngineControlClient(connection)) + , maximumTime(0) + , recording(false) + , requestedFeatures(0) + , recordedFeatures(0) + , flushInterval(0) + { + } - QHash<qint64, QQmlProfilerRangeType> types; + virtual ~QQmlProfilerClientPrivate() override; + + void sendRecordingStatus(int engineId); + bool updateFeatures(ProfileFeature feature); + int resolveType(const QQmlProfilerTypedEvent &type); + int resolveStackTop(); + void forwardEvents(const QQmlProfilerEvent &last); + void forwardDebugMessages(qint64 untilTimestamp); + void processCurrentEvent(); + void finalize(); + + QQmlProfilerEventReceiver *eventReceiver; + QScopedPointer<QQmlEngineControlClient> engineControl; + QScopedPointer<QQmlDebugMessageClient> messageClient; + qint64 maximumTime; + bool recording; + quint64 requestedFeatures; + quint64 recordedFeatures; + quint32 flushInterval; + + // Reuse the same event, so that we don't have to constantly reallocate all the data. + QQmlProfilerTypedEvent currentEvent; + QHash<QQmlProfilerEventType, int> eventTypeIds; + QHash<qint64, int> serverTypeIds; + QStack<QQmlProfilerTypedEvent> rangesInProgress; + QQueue<QQmlProfilerEvent> pendingMessages; + QQueue<QQmlProfilerEvent> pendingDebugMessages; + + QList<int> trackedEngines; }; QT_END_NAMESPACE #endif // QQMLPROFILERCLIENT_P_P_H - diff --git a/src/qmldebug/qqmlprofilerclientdefinitions_p.h b/src/qmldebug/qqmlprofilerclientdefinitions_p.h new file mode 100644 index 0000000000..be1c6318af --- /dev/null +++ b/src/qmldebug/qqmlprofilerclientdefinitions_p.h @@ -0,0 +1,163 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLPROFILERCLIENTDEFINITIONS_P_H +#define QQMLPROFILERCLIENTDEFINITIONS_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 <QtCore/qglobal.h> + +QT_BEGIN_NAMESPACE + +enum Message { + Event, + RangeStart, + RangeData, + RangeLocation, + RangeEnd, + Complete, // end of transmission + PixmapCacheEvent, + SceneGraphFrame, + MemoryAllocation, + DebugMessage, + + 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 PixmapEventType { + PixmapSizeKnown, + PixmapReferenceCountChanged, + PixmapCacheCountChanged, + PixmapLoadingStarted, + PixmapLoadingFinished, + PixmapLoadingError, + + MaximumPixmapEventType +}; + +enum SceneGraphFrameType { + SceneGraphRendererFrame, // Render Thread + SceneGraphAdaptationLayerFrame, // Render Thread + SceneGraphContextFrame, // Render Thread + SceneGraphRenderLoopFrame, // Render Thread + SceneGraphTexturePrepare, // Render Thread + SceneGraphTextureDeletion, // Render Thread + SceneGraphPolishAndSync, // GUI Thread + SceneGraphWindowsRenderShow, // Unused + SceneGraphWindowsAnimations, // GUI Thread + SceneGraphPolishFrame, // GUI Thread + + MaximumSceneGraphFrameType, + NumRenderThreadFrameTypes = SceneGraphPolishAndSync, + NumGUIThreadFrameTypes = MaximumSceneGraphFrameType - NumRenderThreadFrameTypes +}; + +enum MemoryType { + HeapPage, + LargeItem, + SmallItem +}; + +enum ProfileFeature { + ProfileJavaScript, + ProfileMemory, + ProfilePixmapCache, + ProfileSceneGraph, + ProfileAnimations, + ProfilePainting, + ProfileCompiling, + ProfileCreating, + ProfileBinding, + ProfileHandlingSignal, + ProfileInputEvents, + ProfileDebugMessages, + + MaximumProfileFeature +}; + +enum InputEventType { + InputKeyPress, + InputKeyRelease, + InputKeyUnknown, + + InputMousePress, + InputMouseRelease, + InputMouseMove, + InputMouseDoubleClick, + InputMouseWheel, + InputMouseUnknown, + + MaximumInputEventType +}; + +QT_END_NAMESPACE + +#endif // QQMLPROFILERCLIENTDEFINITIONS_P_H diff --git a/src/qmldebug/qqmlprofilerevent.cpp b/src/qmldebug/qqmlprofilerevent.cpp new file mode 100644 index 0000000000..30ae1c79a1 --- /dev/null +++ b/src/qmldebug/qqmlprofilerevent.cpp @@ -0,0 +1,273 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlprofilerevent_p.h" +#include <QtCore/qdatastream.h> + +QT_BEGIN_NAMESPACE + +bool operator==(const QQmlProfilerEvent &event1, const QQmlProfilerEvent &event2) +{ + if (event1.timestamp() != event2.timestamp() || event1.typeIndex() != event2.typeIndex()) + return false; + + // This is not particularly efficient, but we also don't need to do this very often. + return event1.numbers<QVarLengthArray<qint64>>() == event2.numbers<QVarLengthArray<qint64>>(); +} + +bool operator!=(const QQmlProfilerEvent &event1, const QQmlProfilerEvent &event2) +{ + return !(event1 == event2); +} + +enum SerializationType { + OneByte = 0, + TwoByte = 1, + FourByte = 2, + EightByte = 3, + TypeMask = 0x3 +}; + +enum SerializationTypeOffset { + TimestampOffset = 0, + TypeIndexOffset = 2, + DataLengthOffset = 4, + DataOffset = 6 +}; + +template<typename Number> +static inline void readNumbers(QDataStream &stream, Number *data, quint16 length) +{ + for (quint16 i = 0; i != length; ++i) + stream >> data[i]; +} + +template<typename Number> +static inline Number readNumber(QDataStream &stream, qint8 type) +{ + switch (type) { + case OneByte: { + qint8 value; + stream >> value; + return static_cast<Number>(value); + } + case TwoByte: { + qint16 value; + stream >> value; + return static_cast<Number>(value); + } + case FourByte: { + qint32 value; + stream >> value; + return static_cast<Number>(value); + } + case EightByte: { + qint64 value; + stream >> value; + return static_cast<Number>(value); + } + default: + Q_UNREACHABLE(); + return 0; + } +} + +QDataStream &operator>>(QDataStream &stream, QQmlProfilerEvent &event) +{ + qint8 type; + stream >> type; + + event.m_timestamp = readNumber<qint64>(stream, (type >> TimestampOffset) & TypeMask); + event.m_typeIndex = readNumber<qint32>(stream, (type >> TypeIndexOffset) & TypeMask); + event.m_dataLength = readNumber<quint16>(stream, (type >> DataLengthOffset) & TypeMask); + + uint bytesPerNumber = 1 << ((type >> DataOffset) & TypeMask); + + if (event.m_dataLength * bytesPerNumber > sizeof(event.m_data)) { + event.m_dataType = static_cast<QQmlProfilerEvent::Type>((bytesPerNumber * 8) + | QQmlProfilerEvent::External); + event.m_data.external = malloc(event.m_dataLength * bytesPerNumber); + } else { + event.m_dataType = static_cast<QQmlProfilerEvent::Type>(bytesPerNumber * 8); + } + + switch (event.m_dataType) { + case QQmlProfilerEvent::Inline8Bit: + readNumbers<qint8>(stream, event.m_data.internal8bit, event.m_dataLength); + break; + case QQmlProfilerEvent::External8Bit: + readNumbers<qint8>(stream, static_cast<qint8 *>(event.m_data.external), + event.m_dataLength); + break; + case QQmlProfilerEvent::Inline16Bit: + readNumbers<qint16>(stream, event.m_data.internal16bit, event.m_dataLength); + break; + case QQmlProfilerEvent::External16Bit: + readNumbers<qint16>(stream, static_cast<qint16 *>(event.m_data.external), + event.m_dataLength); + break; + case QQmlProfilerEvent::Inline32Bit: + readNumbers<qint32>(stream, event.m_data.internal32bit, event.m_dataLength); + break; + case QQmlProfilerEvent::External32Bit: + readNumbers<qint32>(stream, static_cast<qint32 *>(event.m_data.external), + event.m_dataLength); + break; + case QQmlProfilerEvent::Inline64Bit: + readNumbers<qint64>(stream, event.m_data.internal64bit, event.m_dataLength); + break; + case QQmlProfilerEvent::External64Bit: + readNumbers<qint64>(stream, static_cast<qint64 *>(event.m_data.external), + event.m_dataLength); + break; + default: + Q_UNREACHABLE(); + break; + } + + return stream; +} + +static inline qint8 minimumType(const QQmlProfilerEvent &event, quint16 length, + quint16 origBitsPerNumber) +{ + qint8 type = OneByte; + bool ok = true; + for (quint16 i = 0; i < length;) { + if ((1 << type) == origBitsPerNumber / 8) + return type; + switch (type) { + case OneByte: + ok = (event.number<qint8>(i) == event.number<qint64>(i)); + break; + case TwoByte: + ok = (event.number<qint16>(i) == event.number<qint64>(i)); + break; + case FourByte: + ok = (event.number<qint32>(i) == event.number<qint64>(i)); + break; + default: + // EightByte isn't possible, as (1 << type) == origBitsPerNumber / 8 then. + Q_UNREACHABLE(); + break; + } + + if (ok) + ++i; + else + ++type; + } + return type; +} + +template<typename Number> +static inline qint8 minimumType(Number number) +{ + if (static_cast<qint8>(number) == number) + return OneByte; + if (static_cast<qint16>(number) == number) + return TwoByte; + if (static_cast<qint32>(number) == number) + return FourByte; + return EightByte; +} + +template<typename Number> +static inline void writeNumbers(QDataStream &stream, const QQmlProfilerEvent &event, quint16 length) +{ + for (quint16 i = 0; i != length; ++i) + stream << event.number<Number>(i); +} + +template<typename Number> +static inline void writeNumber(QDataStream &stream, Number number, qint8 type) +{ + switch (type) { + case OneByte: + stream << static_cast<qint8>(number); + break; + case TwoByte: + stream << static_cast<qint16>(number); + break; + case FourByte: + stream << static_cast<qint32>(number); + break; + case EightByte: + stream << static_cast<qint64>(number); + break; + default: + Q_UNREACHABLE(); + break; + } +} + +QDataStream &operator<<(QDataStream &stream, const QQmlProfilerEvent &event) +{ + qint8 type = minimumType(event.m_timestamp); // << TimestampOffset; + type |= minimumType(event.m_typeIndex) << TypeIndexOffset; + type |= minimumType(event.m_dataLength) << DataLengthOffset; + type |= minimumType(event, event.m_dataLength, event.m_dataType) << DataOffset; + stream << type; + + writeNumber(stream, event.m_timestamp, (type >> TimestampOffset) & TypeMask); + writeNumber(stream, event.m_typeIndex, (type >> TypeIndexOffset) & TypeMask); + writeNumber(stream, event.m_dataLength, (type >> DataLengthOffset) & TypeMask); + + switch ((type >> DataOffset) & TypeMask) { + case OneByte: + writeNumbers<qint8>(stream, event, event.m_dataLength); + break; + case TwoByte: + writeNumbers<qint16>(stream, event, event.m_dataLength); + break; + case FourByte: + writeNumbers<qint32>(stream, event, event.m_dataLength); + break; + case EightByte: + writeNumbers<qint64>(stream, event, event.m_dataLength); + break; + default: + Q_UNREACHABLE(); + break; + } + + return stream; +} + +QT_END_NAMESPACE diff --git a/src/qmldebug/qqmlprofilerevent_p.h b/src/qmldebug/qqmlprofilerevent_p.h new file mode 100644 index 0000000000..1e205d8dbb --- /dev/null +++ b/src/qmldebug/qqmlprofilerevent_p.h @@ -0,0 +1,355 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLPROFILEREVENT_P_H +#define QQMLPROFILEREVENT_P_H + +#include "qqmlprofilerclientdefinitions_p.h" + +#include <QtCore/qstring.h> +#include <QtCore/qbytearray.h> +#include <QtCore/qvarlengtharray.h> +#include <QtCore/qmetatype.h> + +#include <initializer_list> +#include <type_traits> + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +QT_BEGIN_NAMESPACE + +struct QQmlProfilerEvent { + QQmlProfilerEvent() : + m_timestamp(-1), m_typeIndex(-1), m_dataType(Inline8Bit), m_dataLength(0) + {} + + template<typename Number> + QQmlProfilerEvent(qint64 timestamp, int typeIndex, std::initializer_list<Number> list) + : m_timestamp(timestamp), m_typeIndex(typeIndex) + { + assignNumbers<std::initializer_list<Number>, Number>(list); + } + + QQmlProfilerEvent(qint64 timestamp, int typeIndex, const QString &data) + : m_timestamp(timestamp), m_typeIndex(typeIndex) + { + assignNumbers<QByteArray, qint8>(data.toUtf8()); + } + + template<typename Number> + QQmlProfilerEvent(qint64 timestamp, int typeIndex, const QVector<Number> &data) + : m_timestamp(timestamp), m_typeIndex(typeIndex) + { + assignNumbers<QVector<Number>, Number>(data); + } + + QQmlProfilerEvent(const QQmlProfilerEvent &other) + : m_timestamp(other.m_timestamp), m_typeIndex(other.m_typeIndex), + m_dataType(other.m_dataType), m_dataLength(other.m_dataLength) + { + assignData(other); + } + + QQmlProfilerEvent(QQmlProfilerEvent &&other) + { + memcpy(static_cast<void *>(this), static_cast<const void *>(&other), sizeof(QQmlProfilerEvent)); + other.m_dataType = Inline8Bit; // prevent dtor from deleting the pointer + } + + QQmlProfilerEvent &operator=(const QQmlProfilerEvent &other) + { + if (this != &other) { + clearPointer(); + m_timestamp = other.m_timestamp; + m_typeIndex = other.m_typeIndex; + m_dataType = other.m_dataType; + m_dataLength = other.m_dataLength; + assignData(other); + } + return *this; + } + + QQmlProfilerEvent &operator=(QQmlProfilerEvent &&other) + { + if (this != &other) { + memcpy(static_cast<void *>(this), static_cast<const void *>(&other), sizeof(QQmlProfilerEvent)); + other.m_dataType = Inline8Bit; + } + return *this; + } + + ~QQmlProfilerEvent() + { + clearPointer(); + } + + qint64 timestamp() const { return m_timestamp; } + void setTimestamp(qint64 timestamp) { m_timestamp = timestamp; } + + int typeIndex() const { return m_typeIndex; } + void setTypeIndex(int typeIndex) { m_typeIndex = typeIndex; } + + template<typename Number> + Number number(int i) const + { + // Trailing zeroes can be omitted, for example for SceneGraph events + if (i >= m_dataLength) + return 0; + switch (m_dataType) { + case Inline8Bit: + return m_data.internal8bit[i]; +QT_WARNING_PUSH +QT_WARNING_DISABLE_GCC("-Warray-bounds") // Mingw 5.3 gcc doesn't get the type/length logic. + case Inline16Bit: + return m_data.internal16bit[i]; + case Inline32Bit: + return m_data.internal32bit[i]; + case Inline64Bit: + return m_data.internal64bit[i]; +QT_WARNING_POP + case External8Bit: + return static_cast<const qint8 *>(m_data.external)[i]; + case External16Bit: + return static_cast<const qint16 *>(m_data.external)[i]; + case External32Bit: + return static_cast<const qint32 *>(m_data.external)[i]; + case External64Bit: + return static_cast<const qint64 *>(m_data.external)[i]; + default: + return 0; + } + } + + template<typename Number> + void setNumber(int i, Number number) + { + QVarLengthArray<Number> nums = numbers<QVarLengthArray<Number>, Number>(); + int prevSize = nums.size(); + if (i >= prevSize) { + nums.resize(i + 1); + // Fill with zeroes. We don't want to accidentally prevent squeezing. + while (prevSize < i) + nums[prevSize++] = 0; + } + nums[i] = number; + setNumbers<QVarLengthArray<Number>, Number>(nums); + } + + template<typename Container, typename Number> + void setNumbers(const Container &numbers) + { + clearPointer(); + assignNumbers<Container, Number>(numbers); + } + + template<typename Number> + void setNumbers(std::initializer_list<Number> numbers) + { + setNumbers<std::initializer_list<Number>, Number>(numbers); + } + + template<typename Container, typename Number = qint64> + Container numbers() const + { + Container container; + for (int i = 0; i < m_dataLength; ++i) + container.append(number<Number>(i)); + return container; + } + + QString string() const + { + switch (m_dataType) { + case External8Bit: + return QString::fromUtf8(static_cast<const char *>(m_data.external), m_dataLength); + case Inline8Bit: + return QString::fromUtf8(m_data.internalChar, m_dataLength); + default: + Q_UNREACHABLE(); + return QString(); + } + } + + void setString(const QString &data) + { + clearPointer(); + assignNumbers<QByteArray, char>(data.toUtf8()); + } + + Message rangeStage() const + { + Q_ASSERT(m_dataType == Inline8Bit); + return static_cast<Message>(m_data.internal8bit[0]); + } + + void setRangeStage(Message stage) + { + clearPointer(); + m_dataType = Inline8Bit; + m_dataLength = 1; + m_data.internal8bit[0] = stage; + } + + bool isValid() const + { + return m_timestamp != -1; + } + +private: + enum Type: quint16 { + External = 1, + Inline8Bit = 8, + External8Bit = Inline8Bit | External, + Inline16Bit = 16, + External16Bit = Inline16Bit | External, + Inline32Bit = 32, + External32Bit = Inline32Bit | External, + Inline64Bit = 64, + External64Bit = Inline64Bit | External + }; + + qint64 m_timestamp; + + static const int s_internalDataLength = 8; + union { + void *external; + char internalChar [s_internalDataLength]; + qint8 internal8bit [s_internalDataLength]; + qint16 internal16bit[s_internalDataLength / 2]; + qint32 internal32bit[s_internalDataLength / 4]; + qint64 internal64bit[s_internalDataLength / 8]; + } m_data; + + qint32 m_typeIndex; + Type m_dataType; + quint16 m_dataLength; + + void assignData(const QQmlProfilerEvent &other) + { + if (m_dataType & External) { + uint length = m_dataLength * (other.m_dataType / 8); + m_data.external = malloc(length); + memcpy(m_data.external, other.m_data.external, length); + } else { + memcpy(&m_data, &other.m_data, sizeof(m_data)); + } + } + + template<typename Big, typename Small> + bool squeezable(Big source) + { + return static_cast<Small>(source) == source; + } + + template<typename Container, typename Number> + typename std::enable_if<(sizeof(Number) > 1), bool>::type + squeeze(const Container &numbers) + { + typedef typename QIntegerForSize<sizeof(Number) / 2>::Signed Small; + foreach (Number item, numbers) { + if (!squeezable<Number, Small>(item)) + return false; + } + assignNumbers<Container, Small>(numbers); + return true; + } + + template<typename Container, typename Number> + typename std::enable_if<(sizeof(Number) <= 1), bool>::type + squeeze(const Container &) + { + return false; + } + + template<typename Container, typename Number> + void assignNumbers(const Container &numbers) + { + Number *data; + m_dataLength = squeezable<size_t, quint16>(static_cast<size_t>(numbers.size())) ? + static_cast<quint16>(numbers.size()) : std::numeric_limits<quint16>::max(); + if (m_dataLength > sizeof(m_data) / sizeof(Number)) { + if (squeeze<Container, Number>(numbers)) + return; + m_dataType = static_cast<Type>((sizeof(Number) * 8) | External); + m_data.external = malloc(m_dataLength * sizeof(Number)); + data = static_cast<Number *>(m_data.external); + } else { + m_dataType = static_cast<Type>(sizeof(Number) * 8); + data = static_cast<Number *>(m_dataType & External ? m_data.external : &m_data); + } + quint16 i = 0; + for (Number item : numbers) { + if (i >= m_dataLength) + break; + data[i++] = item; + } + } + + void clearPointer() + { + if (m_dataType & External) + free(m_data.external); + } + + friend QDataStream &operator>>(QDataStream &stream, QQmlProfilerEvent &event); + friend QDataStream &operator<<(QDataStream &stream, const QQmlProfilerEvent &event); +}; + +bool operator==(const QQmlProfilerEvent &event1, const QQmlProfilerEvent &event2); +bool operator!=(const QQmlProfilerEvent &event1, const QQmlProfilerEvent &event2); + +QDataStream &operator>>(QDataStream &stream, QQmlProfilerEvent &event); +QDataStream &operator<<(QDataStream &stream, const QQmlProfilerEvent &event); + +Q_DECLARE_TYPEINFO(QQmlProfilerEvent, Q_MOVABLE_TYPE); + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QQmlProfilerEvent) + +#endif // QQMLPROFILEREVENT_P_H diff --git a/src/qmldebug/qqmlprofilereventlocation.cpp b/src/qmldebug/qqmlprofilereventlocation.cpp new file mode 100644 index 0000000000..8be44c17a3 --- /dev/null +++ b/src/qmldebug/qqmlprofilereventlocation.cpp @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlprofilereventlocation_p.h" + +#include <QtCore/qdatastream.h> + +QT_BEGIN_NAMESPACE + +QDataStream &operator>>(QDataStream &stream, QQmlProfilerEventLocation &location) +{ + return stream >> location.m_filename >> location.m_line >> location.m_column; +} + +QDataStream &operator<<(QDataStream &stream, const QQmlProfilerEventLocation &location) +{ + return stream << location.m_filename << location.m_line << location.m_column; +} + +QT_END_NAMESPACE diff --git a/src/qmldebug/qqmlprofilereventlocation_p.h b/src/qmldebug/qqmlprofilereventlocation_p.h new file mode 100644 index 0000000000..6f37eab14b --- /dev/null +++ b/src/qmldebug/qqmlprofilereventlocation_p.h @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLPROFILEREVENTLOCATION_P_H +#define QQMLPROFILEREVENTLOCATION_P_H + +#include <QtCore/qstring.h> +#include <QtCore/qhash.h> +#include <QtCore/qdatastream.h> + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +QT_BEGIN_NAMESPACE + +class QQmlProfilerEventLocation +{ +public: + QQmlProfilerEventLocation() : m_line(-1),m_column(-1) {} + QQmlProfilerEventLocation(const QString &file, int lineNumber, int columnNumber) : + m_filename(file), m_line(lineNumber), m_column(columnNumber) + {} + + void clear() + { + m_filename.clear(); + m_line = m_column = -1; + } + + bool isValid() const + { + return !m_filename.isEmpty(); + } + + QString filename() const { return m_filename; } + int line() const { return m_line; } + int column() const { return m_column; } + +private: + friend QDataStream &operator>>(QDataStream &stream, QQmlProfilerEventLocation &location); + friend QDataStream &operator<<(QDataStream &stream, const QQmlProfilerEventLocation &location); + + QString m_filename; + int m_line; + int m_column; +}; + +inline bool operator==(const QQmlProfilerEventLocation &location1, + const QQmlProfilerEventLocation &location2) +{ + // compare filename last as it's expensive. + return location1.line() == location2.line() && location1.column() == location2.column() + && location1.filename() == location2.filename(); +} + +inline bool operator!=(const QQmlProfilerEventLocation &location1, + const QQmlProfilerEventLocation &location2) +{ + return !(location1 == location2); +} + +inline uint qHash(const QQmlProfilerEventLocation &location) +{ + return qHash(location.filename()) + ^ ((location.line() & 0xfff) // 12 bits of line number + | ((location.column() << 16) & 0xff0000)); // 8 bits of column + +} + +QDataStream &operator>>(QDataStream &stream, QQmlProfilerEventLocation &location); +QDataStream &operator<<(QDataStream &stream, const QQmlProfilerEventLocation &location); + +Q_DECLARE_TYPEINFO(QQmlProfilerEventLocation, Q_MOVABLE_TYPE); + +QT_END_NAMESPACE + +#endif // QQMLPROFILEREVENTLOCATION_P_H diff --git a/src/qmldebug/qqmlprofilereventreceiver_p.h b/src/qmldebug/qqmlprofilereventreceiver_p.h new file mode 100644 index 0000000000..defe64a42e --- /dev/null +++ b/src/qmldebug/qqmlprofilereventreceiver_p.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLPROFILEREVENTRECEIVER_P_H +#define QQMLPROFILEREVENTRECEIVER_P_H + +#include "qqmlprofilerevent_p.h" +#include "qqmlprofilereventtype_p.h" + +#include <QtCore/qobject.h> + +// +// 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 QQmlProfilerEventReceiver : public QObject +{ + Q_OBJECT +public: + QQmlProfilerEventReceiver(QObject *parent = nullptr) : QObject(parent) {} + + virtual int numLoadedEventTypes() const = 0; + virtual void addEventType(const QQmlProfilerEventType &type) = 0; + virtual void addEvent(const QQmlProfilerEvent &event) = 0; +}; + +QT_END_NAMESPACE + +#endif // QQMLPROFILEREVENTRECEIVER_P_H diff --git a/src/qmldebug/qqmlprofilereventtype.cpp b/src/qmldebug/qqmlprofilereventtype.cpp new file mode 100644 index 0000000000..c92e914d74 --- /dev/null +++ b/src/qmldebug/qqmlprofilereventtype.cpp @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlprofilereventtype_p.h" +#include "qqmlprofilerclientdefinitions_p.h" + +#include <QtCore/qdatastream.h> + +QT_BEGIN_NAMESPACE + +QDataStream &operator>>(QDataStream &stream, QQmlProfilerEventType &type) +{ + quint8 message; + quint8 rangeType; + stream >> type.m_displayName >> type.m_data >> type.m_location >> message >> rangeType + >> type.m_detailType; + type.m_message = static_cast<Message>(message); + type.m_rangeType = static_cast<RangeType>(rangeType); + return stream; +} + +QDataStream &operator<<(QDataStream &stream, const QQmlProfilerEventType &type) +{ + return stream << type.m_displayName << type.m_data << type.m_location + << static_cast<quint8>(type.m_message) << static_cast<quint8>(type.m_rangeType) + << type.m_detailType; +} + +ProfileFeature QQmlProfilerEventType::feature() const +{ + switch (m_message) { + case Event: { + switch (m_detailType) { + case Mouse: + case Key: + return ProfileInputEvents; + case AnimationFrame: + return ProfileAnimations; + default: + return MaximumProfileFeature; + } + } + case PixmapCacheEvent: + return ProfilePixmapCache; + case SceneGraphFrame: + return ProfileSceneGraph; + case MemoryAllocation: + return ProfileMemory; + case DebugMessage: + return ProfileDebugMessages; + default: + break; + } + + switch (m_rangeType) { + case Painting: + return ProfilePainting; + case Compiling: + return ProfileCompiling; + case Creating: + return ProfileCreating; + case Binding: + return ProfileBinding; + case HandlingSignal: + return ProfileHandlingSignal; + case Javascript: + return ProfileJavaScript; + default: + return MaximumProfileFeature; + } +} + + +QT_END_NAMESPACE diff --git a/src/qmldebug/qqmlprofilereventtype_p.h b/src/qmldebug/qqmlprofilereventtype_p.h new file mode 100644 index 0000000000..7189df53ef --- /dev/null +++ b/src/qmldebug/qqmlprofilereventtype_p.h @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLPROFILEREVENTTYPE_P_H +#define QQMLPROFILEREVENTTYPE_P_H + +#include "qqmlprofilereventlocation_p.h" +#include "qqmlprofilerclientdefinitions_p.h" + +#include <QtCore/qstring.h> +#include <QtCore/qmetatype.h> +#include <QtCore/qhash.h> + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +QT_BEGIN_NAMESPACE + +class QQmlProfilerEventType { +public: + QQmlProfilerEventType(Message message = MaximumMessage, RangeType rangeType = MaximumRangeType, + int detailType = -1, + const QQmlProfilerEventLocation &location = QQmlProfilerEventLocation(), + const QString &data = QString(), const QString displayName = QString()) : + m_displayName(displayName), m_data(data), m_location(location), m_message(message), + m_rangeType(rangeType), m_detailType(detailType) + {} + + void setDisplayName(const QString &displayName) { m_displayName = displayName; } + void setData(const QString &data) { m_data = data; } + void setLocation(const QQmlProfilerEventLocation &location) { m_location = location; } + + ProfileFeature feature() const; + QString displayName() const { return m_displayName; } + QString data() const { return m_data; } + QQmlProfilerEventLocation location() const { return m_location; } + Message message() const { return m_message; } + RangeType rangeType() const { return m_rangeType; } + int detailType() const { return m_detailType; } + +private: + friend QDataStream &operator>>(QDataStream &stream, QQmlProfilerEventType &type); + friend QDataStream &operator<<(QDataStream &stream, const QQmlProfilerEventType &type); + + QString m_displayName; + QString m_data; + QQmlProfilerEventLocation m_location; + Message m_message; + RangeType m_rangeType; + int m_detailType; // can be EventType, BindingType, PixmapEventType or SceneGraphFrameType +}; + +QDataStream &operator>>(QDataStream &stream, QQmlProfilerEventType &type); +QDataStream &operator<<(QDataStream &stream, const QQmlProfilerEventType &type); + +inline uint qHash(const QQmlProfilerEventType &type) +{ + return qHash(type.location()) + ^ (((type.message() << 12) & 0xf000) // 4 bits message + | ((type.rangeType() << 24) & 0xf000000) // 4 bits rangeType + | ((static_cast<uint>(type.detailType()) << 28) & 0xf0000000)); // 4 bits detailType +} + +inline bool operator==(const QQmlProfilerEventType &type1, const QQmlProfilerEventType &type2) +{ + return type1.message() == type2.message() && type1.rangeType() == type2.rangeType() + && type1.detailType() == type2.detailType() && type1.location() == type2.location(); +} + +inline bool operator!=(const QQmlProfilerEventType &type1, const QQmlProfilerEventType &type2) +{ + return !(type1 == type2); +} + +Q_DECLARE_TYPEINFO(QQmlProfilerEventType, Q_MOVABLE_TYPE); + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QQmlProfilerEventType) + +#endif // QQMLPROFILEREVENTTYPE_P_H diff --git a/src/qmldebug/qqmlprofilertypedevent.cpp b/src/qmldebug/qqmlprofilertypedevent.cpp new file mode 100644 index 0000000000..94591ba7e3 --- /dev/null +++ b/src/qmldebug/qqmlprofilertypedevent.cpp @@ -0,0 +1,224 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlprofilertypedevent_p.h" +#include "qqmlprofilerclientdefinitions_p.h" + +#include <QtCore/qvarlengtharray.h> + +QT_BEGIN_NAMESPACE + +QDataStream &operator>>(QDataStream &stream, QQmlProfilerTypedEvent &event) +{ + qint64 time; + qint32 messageType; + qint32 subtype; + + stream >> time >> messageType; + + if (messageType < 0 || messageType > MaximumMessage) + messageType = MaximumMessage; + + RangeType rangeType = MaximumRangeType; + if (!stream.atEnd()) { + stream >> subtype; + if (subtype >= 0 && subtype < MaximumRangeType) + rangeType = static_cast<RangeType>(subtype); + } else { + subtype = -1; + } + + event.event.setTimestamp(time > 0 ? time : 0); + event.event.setTypeIndex(-1); + event.serverTypeId = 0; + + switch (messageType) { + case Event: { + event.type = QQmlProfilerEventType( + static_cast<Message>(messageType), + MaximumRangeType, subtype); + switch (subtype) { + case StartTrace: + case EndTrace: { + QVarLengthArray<qint32> engineIds; + while (!stream.atEnd()) { + qint32 id; + stream >> id; + engineIds << id; + } + event.event.setNumbers<QVarLengthArray<qint32>, qint32>(engineIds); + break; + } + case AnimationFrame: { + qint32 frameRate, animationCount; + qint32 threadId; + stream >> frameRate >> animationCount; + if (!stream.atEnd()) + stream >> threadId; + else + threadId = 0; + + event.event.setNumbers<qint32>({frameRate, animationCount, threadId}); + break; + } + case Mouse: + case Key: + int inputType = (subtype == Key ? InputKeyUnknown : InputMouseUnknown); + if (!stream.atEnd()) + stream >> inputType; + qint32 a = -1; + if (!stream.atEnd()) + stream >> a; + qint32 b = -1; + if (!stream.atEnd()) + stream >> b; + + event.event.setNumbers<qint32>({inputType, a, b}); + break; + } + + break; + } + case Complete: { + event.type = QQmlProfilerEventType( + static_cast<Message>(messageType), + MaximumRangeType, subtype); + break; + } + case SceneGraphFrame: { + QVarLengthArray<qint64> params; + qint64 param; + + while (!stream.atEnd()) { + stream >> param; + params.push_back(param); + } + + event.type = QQmlProfilerEventType( + static_cast<Message>(messageType), + MaximumRangeType, subtype); + event.event.setNumbers<QVarLengthArray<qint64>, qint64>(params); + break; + } + case PixmapCacheEvent: { + qint32 width = 0, height = 0, refcount = 0; + QString filename; + stream >> filename; + if (subtype == PixmapReferenceCountChanged || subtype == PixmapCacheCountChanged) { + stream >> refcount; + } else if (subtype == PixmapSizeKnown) { + stream >> width >> height; + refcount = 1; + } + + event.type = QQmlProfilerEventType( + static_cast<Message>(messageType), + MaximumRangeType, subtype, + QQmlProfilerEventLocation(filename, 0, 0)); + event.event.setNumbers<qint32>({width, height, refcount}); + break; + } + case MemoryAllocation: { + qint64 delta; + stream >> delta; + + event.type = QQmlProfilerEventType( + static_cast<Message>(messageType), + MaximumRangeType, subtype); + event.event.setNumbers<qint64>({delta}); + break; + } + case RangeStart: { + if (!stream.atEnd()) { + qint64 typeId; + stream >> typeId; + if (stream.status() == QDataStream::Ok) + event.serverTypeId = typeId; + // otherwise it's the old binding type of 4 bytes + } + + event.type = QQmlProfilerEventType(MaximumMessage, rangeType, -1); + event.event.setRangeStage(RangeStart); + break; + } + case RangeData: { + QString data; + stream >> data; + + event.type = QQmlProfilerEventType(MaximumMessage, rangeType, -1, + QQmlProfilerEventLocation(), data); + event.event.setRangeStage(RangeData); + if (!stream.atEnd()) + stream >> event.serverTypeId; + break; + } + case RangeLocation: { + QString filename; + qint32 line = 0; + qint32 column = 0; + stream >> filename >> line; + + if (!stream.atEnd()) { + stream >> column; + if (!stream.atEnd()) + stream >> event.serverTypeId; + } + + event.type = QQmlProfilerEventType(MaximumMessage, rangeType, -1, + QQmlProfilerEventLocation(filename, line, column)); + event.event.setRangeStage(RangeLocation); + break; + } + case RangeEnd: { + event.type = QQmlProfilerEventType(MaximumMessage, rangeType, -1); + event.event.setRangeStage(RangeEnd); + break; + } + default: + event.event.setNumbers<char>({}); + event.type = QQmlProfilerEventType( + static_cast<Message>(messageType), + MaximumRangeType, subtype); + break; + } + + return stream; +} + +QT_END_NAMESPACE diff --git a/src/qmldebug/qqmlprofilertypedevent_p.h b/src/qmldebug/qqmlprofilertypedevent_p.h new file mode 100644 index 0000000000..e7e947f2ef --- /dev/null +++ b/src/qmldebug/qqmlprofilertypedevent_p.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLPROFILERTYPEDEVENT_P_H +#define QQMLPROFILERTYPEDEVENT_P_H + +#include "qqmlprofilerevent_p.h" +#include "qqmlprofilereventtype_p.h" + +#include <QtCore/qdatastream.h> + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +QT_BEGIN_NAMESPACE + +struct QQmlProfilerTypedEvent +{ + QQmlProfilerEvent event; + QQmlProfilerEventType type; + qint64 serverTypeId = 0; +}; + +QDataStream &operator>>(QDataStream &stream, QQmlProfilerTypedEvent &event); + +Q_DECLARE_TYPEINFO(QQmlProfilerTypedEvent, Q_MOVABLE_TYPE); + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QQmlProfilerTypedEvent) + +#endif // QQMLPROFILERTYPEDEVENT_P_H diff --git a/src/qmldebug/qv4debugclient.cpp b/src/qmldebug/qv4debugclient.cpp new file mode 100644 index 0000000000..76c2f1ebea --- /dev/null +++ b/src/qmldebug/qv4debugclient.cpp @@ -0,0 +1,578 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4debugclient_p.h" +#include "qv4debugclient_p_p.h" +#include "qqmldebugconnection_p.h" + +#include <private/qpacket_p.h> + +#include <QJsonDocument> +#include <QJsonObject> +#include <QJsonValue> +#include <QJsonArray> + +QT_BEGIN_NAMESPACE + +const char *V8REQUEST = "v8request"; +const char *V8MESSAGE = "v8message"; +const char *SEQ = "seq"; +const char *TYPE = "type"; +const char *COMMAND = "command"; +const char *ARGUMENTS = "arguments"; +const char *STEPACTION = "stepaction"; +const char *STEPCOUNT = "stepcount"; +const char *EXPRESSION = "expression"; +const char *FRAME = "frame"; +const char *CONTEXT = "context"; +const char *GLOBAL = "global"; +const char *DISABLEBREAK = "disable_break"; +const char *HANDLES = "handles"; +const char *INCLUDESOURCE = "includeSource"; +const char *FROMFRAME = "fromFrame"; +const char *TOFRAME = "toFrame"; +const char *BOTTOM = "bottom"; +const char *NUMBER = "number"; +const char *FRAMENUMBER = "frameNumber"; +const char *TYPES = "types"; +const char *IDS = "ids"; +const char *FILTER = "filter"; +const char *FROMLINE = "fromLine"; +const char *TOLINE = "toLine"; +const char *TARGET = "target"; +const char *LINE = "line"; +const char *COLUMN = "column"; +const char *ENABLED = "enabled"; +const char *CONDITION = "condition"; +const char *IGNORECOUNT = "ignoreCount"; +const char *BREAKPOINT = "breakpoint"; +const char *FLAGS = "flags"; + +const char *CONTINEDEBUGGING = "continue"; +const char *EVALUATE = "evaluate"; +const char *LOOKUP = "lookup"; +const char *BACKTRACE = "backtrace"; +const char *SCOPE = "scope"; +const char *SCOPES = "scopes"; +const char *SCRIPTS = "scripts"; +const char *SOURCE = "source"; +const char *SETBREAKPOINT = "setbreakpoint"; +const char *CLEARBREAKPOINT = "clearbreakpoint"; +const char *CHANGEBREAKPOINT = "changebreakpoint"; +const char *SETEXCEPTIONBREAK = "setexceptionbreak"; +const char *VERSION = "version"; +const char *DISCONNECT = "disconnect"; +const char *GARBAGECOLLECTOR = "gc"; + +const char *CONNECT = "connect"; +const char *INTERRUPT = "interrupt"; + +const char *REQUEST = "request"; +const char *IN = "in"; +const char *NEXT = "next"; +const char *OUT = "out"; + +const char *SCRIPT = "script"; +const char *SCRIPTREGEXP = "scriptRegExp"; +const char *EVENT = "event"; + +const char *ALL = "all"; +const char *UNCAUGHT = "uncaught"; + +#define VARIANTMAPINIT \ + Q_D(QV4DebugClient); \ + QJsonObject jsonVal; \ + jsonVal.insert(QLatin1String(SEQ), d->seq++); \ + jsonVal.insert(QLatin1String(TYPE), QLatin1String(REQUEST)); + +QV4DebugClient::QV4DebugClient(QQmlDebugConnection *connection) + : QQmlDebugClient(*new QV4DebugClientPrivate(connection)) +{ + QObject::connect(this, &QQmlDebugClient::stateChanged, + this, [this](State state) { d_func()->onStateChanged(state); }); +} + +QV4DebugClientPrivate::QV4DebugClientPrivate(QQmlDebugConnection *connection) : + QQmlDebugClientPrivate(QLatin1String("V8Debugger"), connection) +{ +} + +void QV4DebugClient::connect() +{ + Q_D(QV4DebugClient); + d->sendMessage(CONNECT); +} + +void QV4DebugClient::interrupt() +{ + Q_D(QV4DebugClient); + d->sendMessage(INTERRUPT); +} + +void QV4DebugClient::continueDebugging(StepAction action) +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "continue", + // "arguments" : { "stepaction" : <"in", "next" or "out">, + // "stepcount" : <number of steps (default 1)> + // } + // } + VARIANTMAPINIT; + jsonVal.insert(QLatin1String(COMMAND), QLatin1String(CONTINEDEBUGGING)); + + if (action != Continue) { + QJsonObject args; + switch (action) { + case In: + args.insert(QLatin1String(STEPACTION), QLatin1String(IN)); + break; + case Out: + args.insert(QLatin1String(STEPACTION), QLatin1String(OUT)); + break; + case Next: + args.insert(QLatin1String(STEPACTION), QLatin1String(NEXT)); + break; + default: + break; + } + jsonVal.insert(QLatin1String(ARGUMENTS), args); + } + + d->sendMessage(V8REQUEST, jsonVal); +} + +void QV4DebugClient::evaluate(const QString &expr, int frame, int context) +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "evaluate", + // "arguments" : { "expression" : <expression to evaluate>, + // "frame" : <number>, + // "context" : <object ID> + // } + // } + VARIANTMAPINIT; + jsonVal.insert(QLatin1String(COMMAND), QLatin1String(EVALUATE)); + + QJsonObject args; + args.insert(QLatin1String(EXPRESSION), expr); + + if (frame != -1) + args.insert(QLatin1String(FRAME), frame); + + if (context != -1) + args.insert(QLatin1String(CONTEXT), context); + + jsonVal.insert(QLatin1String(ARGUMENTS), args); + + d->sendMessage(V8REQUEST, jsonVal); +} + +void QV4DebugClient::lookup(const QList<int> &handles, bool includeSource) +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "lookup", + // "arguments" : { "handles" : <array of handles>, + // "includeSource" : <boolean indicating whether the source will be included when script objects are returned>, + // } + // } + VARIANTMAPINIT; + jsonVal.insert(QLatin1String(COMMAND),(QLatin1String(LOOKUP))); + + QJsonObject args; + QJsonArray array; + + for (int handle : handles) + array.append(handle); + + args.insert(QLatin1String(HANDLES), array); + + if (includeSource) + args.insert(QLatin1String(INCLUDESOURCE), includeSource); + + jsonVal.insert(QLatin1String(ARGUMENTS), args); + + d->sendMessage(V8REQUEST, jsonVal); +} + +void QV4DebugClient::backtrace(int fromFrame, int toFrame, bool bottom) +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "backtrace", + // "arguments" : { "fromFrame" : <number> + // "toFrame" : <number> + // "bottom" : <boolean, set to true if the bottom of the stack is requested> + // } + // } + VARIANTMAPINIT; + jsonVal.insert(QLatin1String(COMMAND), QLatin1String(BACKTRACE)); + + QJsonObject args; + + if (fromFrame != -1) + args.insert(QLatin1String(FROMFRAME), fromFrame); + + if (toFrame != -1) + args.insert(QLatin1String(TOFRAME), toFrame); + + if (bottom) + args.insert(QLatin1String(BOTTOM), bottom); + + jsonVal.insert(QLatin1String(ARGUMENTS), args); + d->sendMessage(V8REQUEST, jsonVal); +} + +void QV4DebugClient::frame(int number) +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "frame", + // "arguments" : { "number" : <frame number> + // } + // } + VARIANTMAPINIT; + jsonVal.insert(QLatin1String(COMMAND), QLatin1String(FRAME)); + + if (number != -1) { + QJsonObject args; + args.insert(QLatin1String(NUMBER), number); + jsonVal.insert(QLatin1String(ARGUMENTS), args); + } + + d->sendMessage(V8REQUEST, jsonVal); +} + +void QV4DebugClient::scope(int number, int frameNumber) +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "scope", + // "arguments" : { "number" : <scope number> + // "frameNumber" : <frame number, optional uses selected frame if missing> + // } + // } + VARIANTMAPINIT; + jsonVal.insert(QLatin1String(COMMAND), QLatin1String(SCOPE)); + + if (number != -1) { + QJsonObject args; + args.insert(QLatin1String(NUMBER), number); + + if (frameNumber != -1) + args.insert(QLatin1String(FRAMENUMBER), frameNumber); + + jsonVal.insert(QLatin1String(ARGUMENTS), args); + } + + d->sendMessage(V8REQUEST, jsonVal); +} + +void QV4DebugClient::scripts(int types, const QList<int> &ids, bool includeSource) +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "scripts", + // "arguments" : { "types" : <types of scripts to retrieve + // set bit 0 for native scripts + // set bit 1 for extension scripts + // set bit 2 for normal scripts + // (default is 4 for normal scripts)> + // "ids" : <array of id's of scripts to return. If this is not specified all scripts are requrned> + // "includeSource" : <boolean indicating whether the source code should be included for the scripts returned> + // "filter" : <string or number: filter string or script id. + // If a number is specified, then only the script with the same number as its script id will be retrieved. + // If a string is specified, then only scripts whose names contain the filter string will be retrieved.> + // } + // } + VARIANTMAPINIT; + jsonVal.insert(QLatin1String(COMMAND), QLatin1String(SCRIPTS)); + + QJsonObject args; + args.insert(QLatin1String(TYPES), types); + + if (ids.count()) { + QJsonArray array; + for (int id : ids) + array.append(id); + + args.insert(QLatin1String(IDS), array); + } + + if (includeSource) + args.insert(QLatin1String(INCLUDESOURCE), includeSource); + + jsonVal.insert(QLatin1String(ARGUMENTS), args); + d->sendMessage(V8REQUEST, jsonVal); +} + +void QV4DebugClient::setBreakpoint(const QString &target, int line, int column, bool enabled, + const QString &condition, int ignoreCount) +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "setbreakpoint", + // "arguments" : { "type" : "scriptRegExp" + // "target" : <function expression or script identification> + // "line" : <line in script or function> + // "column" : <character position within the line> + // "enabled" : <initial enabled state. True or false, default is true> + // "condition" : <string with break point condition> + // "ignoreCount" : <number specifying the number of break point hits to ignore, default value is 0> + // } + // } + + VARIANTMAPINIT; + jsonVal.insert(QLatin1String(COMMAND), QLatin1String(SETBREAKPOINT)); + + QJsonObject args; + + args.insert(QLatin1String(TYPE), QLatin1String(SCRIPTREGEXP)); + args.insert(QLatin1String(TARGET), target); + + if (line != -1) + args.insert(QLatin1String(LINE), line); + + if (column != -1) + args.insert(QLatin1String(COLUMN), column); + + args.insert(QLatin1String(ENABLED), enabled); + + if (!condition.isEmpty()) + args.insert(QLatin1String(CONDITION), condition); + + if (ignoreCount != -1) + args.insert(QLatin1String(IGNORECOUNT), ignoreCount); + + jsonVal.insert(QLatin1String(ARGUMENTS),args); + d->sendMessage(V8REQUEST, jsonVal); +} + +void QV4DebugClient::clearBreakpoint(int breakpoint) +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "clearbreakpoint", + // "arguments" : { "breakpoint" : <number of the break point to clear> + // } + // } + VARIANTMAPINIT; + jsonVal.insert(QLatin1String(COMMAND), QLatin1String(CLEARBREAKPOINT)); + + QJsonObject args; + args.insert(QLatin1String(BREAKPOINT), breakpoint); + jsonVal.insert(QLatin1String(ARGUMENTS),args); + + d->sendMessage(V8REQUEST, jsonVal); +} + +void QV4DebugClient::changeBreakpoint(int breakpoint, bool enabled) +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "changebreakpoint", + // "arguments" : { "breakpoint" : <number of the break point to change> + // "enabled" : <bool: enables the break type if true, disables if false> + // } + // } + VARIANTMAPINIT; + jsonVal.insert(QLatin1String(COMMAND), QLatin1String(CHANGEBREAKPOINT)); + + QJsonObject args; + args.insert(QLatin1String(BREAKPOINT), breakpoint); + args.insert(QLatin1String(ENABLED), enabled); + + jsonVal.insert(QLatin1String(ARGUMENTS), args); + d->sendMessage(V8REQUEST, jsonVal); +} + +void QV4DebugClient::setExceptionBreak(Exception type, bool enabled) +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "setexceptionbreak", + // "arguments" : { "type" : <string: "all", or "uncaught">, + // "enabled" : <optional bool: enables the break type if true> + // } + // } + VARIANTMAPINIT; + jsonVal.insert(QLatin1String(COMMAND), QLatin1String(SETEXCEPTIONBREAK)); + + QJsonObject args; + + if (type == All) + args.insert(QLatin1String(TYPE), QLatin1String(ALL)); + else if (type == Uncaught) + args.insert(QLatin1String(TYPE), QLatin1String(UNCAUGHT)); + + if (enabled) + args.insert(QLatin1String(ENABLED), enabled); + + jsonVal.insert(QLatin1String(ARGUMENTS), args); + d->sendMessage(V8REQUEST, jsonVal); +} + +void QV4DebugClient::version() +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "version", + // } + VARIANTMAPINIT; + jsonVal.insert(QLatin1String(COMMAND), QLatin1String(VERSION)); + d->sendMessage(V8REQUEST, jsonVal); +} + +QV4DebugClient::Response QV4DebugClient::response() const +{ + Q_D(const QV4DebugClient); + const QJsonObject value = QJsonDocument::fromJson(d->response).object(); + return { + value.value(QLatin1String(COMMAND)).toString(), + value.value(QLatin1String("body")) + }; +} + +void QV4DebugClient::disconnect() +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "disconnect", + // } + VARIANTMAPINIT; + jsonVal.insert(QLatin1String(COMMAND), QLatin1String(DISCONNECT)); + d->sendMessage(DISCONNECT, jsonVal); +} + +void QV4DebugClientPrivate::onStateChanged(QQmlDebugClient::State state) +{ + if (state == QQmlDebugClient::Enabled) + flushSendBuffer(); +} + +void QV4DebugClient::messageReceived(const QByteArray &data) +{ + Q_D(QV4DebugClient); + QPacket ds(connection()->currentDataStreamVersion(), data); + QByteArray command; + ds >> command; + + if (command == "V8DEBUG") { + QByteArray type; + ds >> type >> d->response; + + if (type == CONNECT) { + emit connected(); + + } else if (type == INTERRUPT) { + emit interrupted(); + + } else if (type == V8MESSAGE) { + const QJsonObject value = QJsonDocument::fromJson(d->response).object(); + QString type = value.value(QLatin1String(TYPE)).toString(); + + if (type == QLatin1String("response")) { + + if (!value.value(QLatin1String("success")).toBool()) { + emit failure(); + qDebug() << "Received success == false response from application:" + << value.value(QLatin1String("message")).toString(); + return; + } + + QString debugCommand(value.value(QLatin1String(COMMAND)).toString()); + if (debugCommand == QLatin1String(BACKTRACE) || + debugCommand == QLatin1String(LOOKUP) || + debugCommand == QLatin1String(SETBREAKPOINT) || + debugCommand == QLatin1String(EVALUATE) || + debugCommand == QLatin1String(VERSION) || + debugCommand == QLatin1String(DISCONNECT) || + debugCommand == QLatin1String(GARBAGECOLLECTOR) || + debugCommand == QLatin1String(CHANGEBREAKPOINT) || + debugCommand == QLatin1String(CLEARBREAKPOINT) || + debugCommand == QLatin1String(FRAME) || + debugCommand == QLatin1String(SCOPE) || + debugCommand == QLatin1String(SCOPES) || + debugCommand == QLatin1String(SCRIPTS) || + debugCommand == QLatin1String(SOURCE) || + debugCommand == QLatin1String(SETEXCEPTIONBREAK)) { + emit result(); + } else { + // DO NOTHING + } + + } else if (type == QLatin1String(EVENT)) { + QString event(value.value(QLatin1String(EVENT)).toString()); + + if (event == QLatin1String("break") || event == QLatin1String("exception")) + emit stopped(); + } + } + } +} + +void QV4DebugClientPrivate::sendMessage(const QByteArray &command, const QJsonObject &args) +{ + Q_Q(QV4DebugClient); + const QByteArray msg = packMessage(command, args); + if (q->state() == QQmlDebugClient::Enabled) { + q->sendMessage(msg); + } else { + sendBuffer.append(msg); + } +} + +void QV4DebugClientPrivate::flushSendBuffer() +{ + foreach (const QByteArray &msg, sendBuffer) + sendMessage(msg); + sendBuffer.clear(); +} + +QByteArray QV4DebugClientPrivate::packMessage(const QByteArray &type, const QJsonObject &object) +{ + QPacket rs(connection->currentDataStreamVersion()); + QByteArray cmd = "V8DEBUG"; + rs << cmd << type << QJsonDocument(object).toJson(QJsonDocument::Compact); + return rs.data(); +} + +QT_END_NAMESPACE diff --git a/src/qmldebug/qv4debugclient_p.h b/src/qmldebug/qv4debugclient_p.h new file mode 100644 index 0000000000..fdcf4284c5 --- /dev/null +++ b/src/qmldebug/qv4debugclient_p.h @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4DEBUGCLIENT_P_H +#define QV4DEBUGCLIENT_P_H + +#include <QtQmlDebug/private/qqmldebugclient_p.h> +#include <QtCore/qjsonvalue.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 QV4DebugClientPrivate; +class QV4DebugClient : public QQmlDebugClient +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QV4DebugClient) + +public: + enum StepAction + { + Continue, + In, + Out, + Next + }; + + enum Exception + { + All, + Uncaught + }; + + struct Response + { + QString command; + QJsonValue body; + }; + + QV4DebugClient(QQmlDebugConnection *connection); + + void connect(); + void disconnect(); + + void interrupt(); + void continueDebugging(StepAction stepAction); + void evaluate(const QString &expr, int frame = -1, int context = -1); + void lookup(const QList<int> &handles, bool includeSource = false); + void backtrace(int fromFrame = -1, int toFrame = -1, bool bottom = false); + void frame(int number = -1); + void scope(int number = -1, int frameNumber = -1); + void scripts(int types = 4, const QList<int> &ids = QList<int>(), bool includeSource = false); + void setBreakpoint(const QString &target, int line = -1, int column = -1, bool enabled = true, + const QString &condition = QString(), int ignoreCount = -1); + void clearBreakpoint(int breakpoint); + void changeBreakpoint(int breakpoint, bool enabled); + void setExceptionBreak(Exception type, bool enabled = false); + void version(); + + Response response() const; + +protected: + void messageReceived(const QByteArray &data) override; + +signals: + void connected(); + void interrupted(); + void result(); + void failure(); + void stopped(); +}; + +QT_END_NAMESPACE + +#endif // QV4DEBUGCLIENT_P_H diff --git a/src/qmldebug/qv4debugclient_p_p.h b/src/qmldebug/qv4debugclient_p_p.h new file mode 100644 index 0000000000..993c281632 --- /dev/null +++ b/src/qmldebug/qv4debugclient_p_p.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4DEBUGCLIENT_P_P_H +#define QV4DEBUGCLIENT_P_P_H + +#include "qv4debugclient_p.h" +#include "qqmldebugclient_p_p.h" + +#include <QtCore/qjsonobject.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 QV4DebugClientPrivate : public QQmlDebugClientPrivate +{ + Q_DECLARE_PUBLIC(QV4DebugClient) + +public: + QV4DebugClientPrivate(QQmlDebugConnection *connection); + + void sendMessage(const QByteArray &command, const QJsonObject &args = QJsonObject()); + void flushSendBuffer(); + QByteArray packMessage(const QByteArray &type, const QJsonObject &object); + void onStateChanged(QQmlDebugClient::State state); + + int seq = 0; + QList<QByteArray> sendBuffer; + QByteArray response; +}; + +QT_END_NAMESPACE + +#endif // QV4DEBUGCLIENT_P_P_H |