diff options
Diffstat (limited to 'src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp')
-rw-r--r-- | src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp | 254 |
1 files changed, 164 insertions, 90 deletions
diff --git a/src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp b/src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp index 2e895778f0..cbde86e389 100644 --- a/src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp +++ b/src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp @@ -1,31 +1,37 @@ /**************************************************************************** ** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ +** 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:LGPL21$ +** $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 http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. +** 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 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** 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. ** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** 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$ ** @@ -33,23 +39,23 @@ #include "qqmldebugserver.h" #include "qqmldebugserverfactory.h" -#include "qpacketprotocol.h" #include "qqmldebugserverconnection.h" +#include "qqmldebugpacket.h" #include <private/qqmldebugservice_p.h> -#include <private/qqmlengine_p.h> +#include <private/qjsengine_p.h> #include <private/qqmlglobal_p.h> #include <private/qqmldebugpluginmanager_p.h> +#include <private/qqmldebugserviceinterfaces_p.h> +#include <private/qpacketprotocol_p.h> #include <QtCore/QAtomicInt> #include <QtCore/QDir> #include <QtCore/QPluginLoader> #include <QtCore/QStringList> +#include <QtCore/QVector> #include <QtCore/qwaitcondition.h> -#include <private/qobject_p.h> -#include <private/qcoreapplication_p.h> - QT_BEGIN_NAMESPACE /* @@ -103,6 +109,11 @@ public: m_fileName = fileName; } + const QString &pluginName() const + { + return m_pluginName; + } + void run(); private: @@ -120,25 +131,26 @@ class QQmlDebugServerImpl : public QQmlDebugServer public: QQmlDebugServerImpl(); - bool blockingMode() const; + bool blockingMode() const Q_DECL_OVERRIDE; - QQmlDebugService *service(const QString &name) const; + QQmlDebugService *service(const QString &name) const Q_DECL_OVERRIDE; - void addEngine(QQmlEngine *engine); - void removeEngine(QQmlEngine *engine); + void addEngine(QJSEngine *engine) Q_DECL_OVERRIDE; + void removeEngine(QJSEngine *engine) Q_DECL_OVERRIDE; + bool hasEngine(QJSEngine *engine) const Q_DECL_OVERRIDE; - bool addService(const QString &name, QQmlDebugService *service); - bool removeService(const QString &name); + bool addService(const QString &name, QQmlDebugService *service) Q_DECL_OVERRIDE; + bool removeService(const QString &name) Q_DECL_OVERRIDE; - bool open(const QVariantHash &configuration); - void setDevice(QIODevice *socket); + bool open(const QVariantHash &configuration) Q_DECL_OVERRIDE; + void setDevice(QIODevice *socket) Q_DECL_OVERRIDE; void parseArguments(); static void cleanup(); private slots: - void wakeEngine(QQmlEngine *engine); + void wakeEngine(QJSEngine *engine); void sendMessage(const QString &name, const QByteArray &message); void sendMessages(const QString &name, const QList<QByteArray> &messages); void changeServiceState(const QString &serviceName, QQmlDebugService::State state); @@ -155,6 +167,7 @@ private: EngineCondition() : numServices(0), condition(new QWaitCondition) {} bool waitForServices(QMutex *locked, int numEngines); + bool isWaiting() const { return numServices > 0; } void wake(); private: @@ -172,10 +185,11 @@ private: QStringList m_clientPlugins; bool m_gotHello; bool m_blockingMode; + bool m_clientSupportsMultiPackets; - QHash<QQmlEngine *, EngineCondition> m_engineConditions; + QHash<QJSEngine *, EngineCondition> m_engineConditions; - QMutex m_helloMutex; + mutable QMutex m_helloMutex; QWaitCondition m_helloCondition; QQmlDebugServerThread m_thread; QPacketProtocol *m_protocol; @@ -260,7 +274,8 @@ static void cleanupOnShutdown() QQmlDebugServerImpl::QQmlDebugServerImpl() : m_connection(0), m_gotHello(false), - m_blockingMode(false) + m_blockingMode(false), + m_clientSupportsMultiPackets(false) { static bool postRoutineAdded = false; if (!postRoutineAdded) { @@ -302,6 +317,9 @@ bool QQmlDebugServerImpl::open(const QVariantHash &configuration = QVariantHash( } } + if (m_thread.pluginName().isEmpty()) + return false; + QMutexLocker locker(&m_helloMutex); m_thread.start(); m_helloCondition.wait(&m_helloMutex); // wait for connection @@ -326,41 +344,41 @@ void QQmlDebugServerImpl::parseArguments() QString fileName; QStringList services; - const QStringList lstjsDebugArguments = args.split(QLatin1Char(',')); - QStringList::const_iterator argsItEnd = lstjsDebugArguments.cend(); - QStringList::const_iterator argsIt = lstjsDebugArguments.cbegin(); - for (; argsIt != argsItEnd; ++argsIt) { - const QString strArgument = *argsIt; + const auto lstjsDebugArguments = args.splitRef(QLatin1Char(',')); + for (auto argsIt = lstjsDebugArguments.begin(), argsItEnd = lstjsDebugArguments.end(); argsIt != argsItEnd; ++argsIt) { + const QStringRef &strArgument = *argsIt; if (strArgument.startsWith(QLatin1String("port:"))) { portFrom = strArgument.mid(5).toInt(&ok); portTo = portFrom; - QStringList::const_iterator argsNext = argsIt + 1; + const auto argsNext = argsIt + 1; if (argsNext == argsItEnd) break; - const QString nextArgument = *argsNext; - - // Don't use QStringLiteral here. QRegExp has a global cache and will save an implicitly - // shared copy of the passed string. That copy isn't properly detached when the library - // is unloaded if the original string lives in the library's .rodata - if (ok && nextArgument.contains(QRegExp(QLatin1String("^\\s*\\d+\\s*$")))) { - portTo = nextArgument.toInt(&ok); - ++argsIt; + if (ok) { + const QString nextArgument = argsNext->toString(); + + // Don't use QStringLiteral here. QRegExp has a global cache and will save an implicitly + // shared copy of the passed string. That copy isn't properly detached when the library + // is unloaded if the original string lives in the library's .rodata + if (nextArgument.contains(QRegExp(QLatin1String("^\\s*\\d+\\s*$")))) { + portTo = nextArgument.toInt(&ok); + ++argsIt; + } } } else if (strArgument.startsWith(QLatin1String("host:"))) { - hostAddress = strArgument.mid(5); + hostAddress = strArgument.mid(5).toString(); } else if (strArgument == QLatin1String("block")) { block = true; } else if (strArgument.startsWith(QLatin1String("file:"))) { - fileName = strArgument.mid(5); + fileName = strArgument.mid(5).toString(); ok = !fileName.isEmpty(); } else if (strArgument.startsWith(QLatin1String("services:"))) { - services.append(strArgument.mid(9)); + services.append(strArgument.mid(9).toString()); } else if (!services.isEmpty()) { - services.append(strArgument); + services.append(strArgument.toString()); } else { - qWarning() << QString::fromLatin1("QML Debugger: Invalid argument '%1' " - "detected. Ignoring the same.") - .arg(strArgument); + const QString message = tr("QML Debugger: Invalid argument \"%1\" detected." + " Ignoring the same.").arg(strArgument.toString()); + qWarning("%s", qPrintable(message)); } } @@ -372,9 +390,45 @@ void QQmlDebugServerImpl::parseArguments() else m_thread.setPortRange(portFrom, portTo, hostAddress); } else { - qWarning() << QString::fromLatin1("QML Debugger: Ignoring \"-qmljsdebugger=%1\". " - "Format is qmljsdebugger=port:<port_from>[,port_to],host:" - "<ip address>][,block]").arg(args); + QString usage; + QTextStream str(&usage); + str << tr("QML Debugger: Ignoring \"-qmljsdebugger=%1\".").arg(args) << '\n' + << tr("The format is \"-qmljsdebugger=[file:<file>|port:<port_from>][,<port_to>]" + "[,host:<ip address>][,block][,services:<service>][,<service>]*\"") << '\n' + << tr("\"file:\" can be used to specify the name of a file the debugger will try " + "to connect to using a QLocalSocket. If \"file:\" is given any \"host:\" and" + "\"port:\" arguments will be ignored.") << '\n' + << tr("\"host:\" and \"port:\" can be used to specify an address and a single " + "port or a range of ports the debugger will try to bind to with a " + "QTcpServer.") << '\n' + << tr("\"block\" makes the debugger and some services wait for clients to be " + "connected and ready before the first QML engine starts.") << '\n' + << tr("\"services:\" can be used to specify which debug services the debugger " + "should load. Some debug services interact badly with others. The V4 " + "debugger should not be loaded when using the QML profiler as it will force " + "any V4 engines to use the JavaScript interpreter rather than the JIT. The " + "following debug services are available by default:") << '\n' + << QQmlEngineDebugService::s_key << "\t- " << tr("The QML debugger") << '\n' + << QV4DebugService::s_key << "\t- " << tr("The V4 debugger") << '\n' + << QQmlInspectorService::s_key << "\t- " << tr("The QML inspector") << '\n' + << QQmlProfilerService::s_key << "\t- " << tr("The QML profiler") << '\n' + << QQmlEngineControlService::s_key << "\t- " + //: Please preserve the line breaks and formatting + << tr("Allows the client to delay the starting and stopping of\n" + "\t\t QML engines until other services are ready. QtCreator\n" + "\t\t uses this service with the QML profiler in order to\n" + "\t\t profile multiple QML engines at the same time.") + << '\n' << QDebugMessageService::s_key << "\t- " + //: Please preserve the line breaks and formatting + << tr("Sends qDebug() and similar messages over the QML debug\n" + "\t\t connection. QtCreator uses this for showing debug\n" + "\t\t messages in the debugger console.") << '\n' + << tr("Other services offered by qmltooling plugins that implement " + "QQmlDebugServiceFactory and which can be found in the standard plugin " + "paths will also be available and can be specified. If no \"services\" " + "argument is given, all services found this way, including the default " + "ones, are loaded."); + qWarning("%s", qPrintable(usage)); } } @@ -388,7 +442,7 @@ void QQmlDebugServerImpl::receiveMessage() if (!m_protocol) return; - QQmlDebugStream in(m_protocol->read().data()); + QQmlDebugPacket in(m_protocol->read()); QString name; @@ -402,16 +456,20 @@ void QQmlDebugServerImpl::receiveMessage() //Get the supported QDataStream version if (!in.atEnd()) { - in >> QQmlDebugStream::s_dataStreamVersion; - if (QQmlDebugStream::s_dataStreamVersion > QDataStream().version()) - QQmlDebugStream::s_dataStreamVersion = QDataStream().version(); + in >> s_dataStreamVersion; + if (s_dataStreamVersion > QDataStream::Qt_DefaultCompiledVersion) + s_dataStreamVersion = QDataStream::Qt_DefaultCompiledVersion; } + if (!in.atEnd()) + in >> m_clientSupportsMultiPackets; + else + m_clientSupportsMultiPackets = false; + // Send the hello answer immediately, since it needs to arrive before // the plugins below start sending messages. - QByteArray helloAnswer; - QQmlDebugStream out(&helloAnswer, QIODevice::WriteOnly); + QQmlDebugPacket out; QStringList pluginNames; QList<float> pluginVersions; const int count = m_plugins.count(); @@ -424,11 +482,9 @@ void QQmlDebugServerImpl::receiveMessage() } out << QString(QStringLiteral("QDeclarativeDebugClient")) << 0 << protocolVersion - << pluginNames << pluginVersions << QQmlDebugStream::s_dataStreamVersion; + << pluginNames << pluginVersions << dataStreamVersion(); - QPacket pack; - pack.writeRawData(helloAnswer.data(), helloAnswer.length()); - m_protocol->send(pack); + m_protocol->send(out.data()); m_connection->flush(); QMutexLocker helloLock(&m_helloMutex); @@ -470,14 +526,16 @@ void QQmlDebugServerImpl::receiveMessage() } else { if (m_gotHello) { - QByteArray message; - in >> message; - QHash<QString, QQmlDebugService *>::Iterator iter = m_plugins.find(name); if (iter == m_plugins.end()) { qWarning() << "QML Debugger: Message received for missing plugin" << name << '.'; } else { - (*iter)->messageReceived(message); + QQmlDebugService *service = *iter; + QByteArray message; + while (!in.atEnd()) { + in >> message; + service->messageReceived(message); + } } } else { qWarning("QML Debugger: Invalid hello message."); @@ -521,12 +579,14 @@ QQmlDebugService *QQmlDebugServerImpl::service(const QString &name) const return m_plugins.value(name); } -void QQmlDebugServerImpl::addEngine(QQmlEngine *engine) +void QQmlDebugServerImpl::addEngine(QJSEngine *engine) { // to be executed outside of debugger thread Q_ASSERT(QThread::currentThread() != &m_thread); QMutexLocker locker(&m_helloMutex); + Q_ASSERT(!m_engineConditions.contains(engine)); + foreach (QQmlDebugService *service, m_plugins) service->engineAboutToBeAdded(engine); @@ -536,12 +596,14 @@ void QQmlDebugServerImpl::addEngine(QQmlEngine *engine) service->engineAdded(engine); } -void QQmlDebugServerImpl::removeEngine(QQmlEngine *engine) +void QQmlDebugServerImpl::removeEngine(QJSEngine *engine) { // to be executed outside of debugger thread Q_ASSERT(QThread::currentThread() != &m_thread); QMutexLocker locker(&m_helloMutex); + Q_ASSERT(m_engineConditions.contains(engine)); + foreach (QQmlDebugService *service, m_plugins) service->engineAboutToBeRemoved(engine); @@ -549,6 +611,16 @@ void QQmlDebugServerImpl::removeEngine(QQmlEngine *engine) foreach (QQmlDebugService *service, m_plugins) service->engineRemoved(engine); + + m_engineConditions.remove(engine); +} + +bool QQmlDebugServerImpl::hasEngine(QJSEngine *engine) const +{ + QMutexLocker locker(&m_helloMutex); + QHash<QJSEngine *, EngineCondition>::ConstIterator i = m_engineConditions.constFind(engine); + // if we're still waiting the engine isn't fully "there", yet, nor fully removed. + return i != m_engineConditions.constEnd() && !i.value().isWaiting(); } bool QQmlDebugServerImpl::addService(const QString &name, QQmlDebugService *service) @@ -564,10 +636,10 @@ bool QQmlDebugServerImpl::addService(const QString &name, QQmlDebugService *serv connect(service, SIGNAL(messagesToClient(QString,QList<QByteArray>)), this, SLOT(sendMessages(QString,QList<QByteArray>))); - connect(service, SIGNAL(attachedToEngine(QQmlEngine*)), - this, SLOT(wakeEngine(QQmlEngine*)), Qt::QueuedConnection); - connect(service, SIGNAL(detachedFromEngine(QQmlEngine*)), - this, SLOT(wakeEngine(QQmlEngine*)), Qt::QueuedConnection); + connect(service, SIGNAL(attachedToEngine(QJSEngine*)), + this, SLOT(wakeEngine(QJSEngine*)), Qt::QueuedConnection); + connect(service, SIGNAL(detachedFromEngine(QJSEngine*)), + this, SLOT(wakeEngine(QJSEngine*)), Qt::QueuedConnection); service->setState(QQmlDebugService::Unavailable); m_plugins.insert(name, service); @@ -587,18 +659,16 @@ bool QQmlDebugServerImpl::removeService(const QString &name) m_plugins.remove(name); service->setState(QQmlDebugService::NotConnected); - disconnect(service, SIGNAL(detachedFromEngine(QQmlEngine*)), - this, SLOT(wakeEngine(QQmlEngine*))); - disconnect(service, SIGNAL(attachedToEngine(QQmlEngine*)), - this, SLOT(wakeEngine(QQmlEngine*))); + disconnect(service, SIGNAL(detachedFromEngine(QJSEngine*)), + this, SLOT(wakeEngine(QJSEngine*))); + disconnect(service, SIGNAL(attachedToEngine(QJSEngine*)), + this, SLOT(wakeEngine(QJSEngine*))); disconnect(service, SIGNAL(messagesToClient(QString,QList<QByteArray>)), this, SLOT(sendMessages(QString,QList<QByteArray>))); disconnect(service, SIGNAL(messageToClient(QString,QByteArray)), this, SLOT(sendMessage(QString,QByteArray))); - m_plugins.remove(service->name()); - return true; } @@ -612,13 +682,9 @@ bool QQmlDebugServerImpl::canSendMessage(const QString &name) void QQmlDebugServerImpl::doSendMessage(const QString &name, const QByteArray &message) { - QByteArray prefixed; - QQmlDebugStream out(&prefixed, QIODevice::WriteOnly); + QQmlDebugPacket out; out << name << message; - - QPacket pack; - pack.writeRawData(prefixed.data(), prefixed.length()); - m_protocol->send(pack); + m_protocol->send(out.data()); } void QQmlDebugServerImpl::sendMessage(const QString &name, const QByteArray &message) @@ -632,13 +698,21 @@ void QQmlDebugServerImpl::sendMessage(const QString &name, const QByteArray &mes void QQmlDebugServerImpl::sendMessages(const QString &name, const QList<QByteArray> &messages) { if (canSendMessage(name)) { - foreach (const QByteArray &message, messages) - doSendMessage(name, message); + if (m_clientSupportsMultiPackets) { + QQmlDebugPacket out; + out << name; + foreach (const QByteArray &message, messages) + out << message; + m_protocol->send(out.data()); + } else { + foreach (const QByteArray &message, messages) + doSendMessage(name, message); + } m_connection->flush(); } } -void QQmlDebugServerImpl::wakeEngine(QQmlEngine *engine) +void QQmlDebugServerImpl::wakeEngine(QJSEngine *engine) { // to be executed in debugger thread Q_ASSERT(QThread::currentThread() == thread()); |