aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp')
-rw-r--r--src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp254
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());