6#include <private/qqmldebugserver_p.h>
7#include <private/qqmldebugserverconnection_p.h>
8#include <private/qqmldebugservice_p.h>
9#include <private/qjsengine_p.h>
10#include <private/qqmlglobal_p.h>
11#include <private/qqmldebugpluginmanager_p.h>
12#include <private/qqmldebugserviceinterfaces_p.h>
13#include <private/qpacketprotocol_p.h>
14#include <private/qversionedpacket_p.h>
16#include <QtCore/QAtomicInt>
18#include <QtCore/QPluginLoader>
19#include <QtCore/QStringList>
20#include <QtCore/QVector>
21#include <QtCore/qwaitcondition.h>
66 m_portFrom = portFrom;
68 m_hostAddress = hostAddress;
121 class EngineCondition {
125 bool waitForServices(
QMutex *locked,
int numEngines);
126 bool isWaiting()
const {
return numServices > 0; }
143 void receiveMessage();
144 void protocolError();
154 mutable QMutex m_helloMutex;
171 i !=
server->m_plugins.constEnd(); ++
i) {
172 server->m_changeServiceStateCalls.ref();
185 while (!
server->m_changeServiceStateCalls.testAndSetOrdered(0, 0))
195 Q_ASSERT_X(m_server !=
nullptr,
Q_FUNC_INFO,
"There should always be a debug server available here.");
202 m_server->m_helloCondition.
wakeAll();
216 qWarning() <<
"QML Debugger: Couldn't load plugin" << m_pluginName;
229 return m_blockingMode;
243 m_blockingMode(
false)
245 static bool postRoutineAdded =
false;
246 if (!postRoutineAdded) {
248 postRoutineAdded =
true;
252 qRegisterMetaType<QList<QByteArray> >(
"QList<QByteArray>");
254 qRegisterMetaType<QQmlDebugService::State>(
"QQmlDebugService::State");
271 if (!configuration.isEmpty()) {
272 m_blockingMode = configuration[
QLatin1String(
"block")].toBool();
274 int portFrom = configuration[
QLatin1String(
"portFrom")].toInt();
276 m_thread.
setPortRange(portFrom, portTo == -1 ? portFrom : portTo,
278 }
else if (configuration.contains(
QLatin1String(
"fileName"))) {
290 m_helloCondition.
wait(&m_helloMutex);
291 if (m_blockingMode && !m_gotHello)
292 m_helloCondition.
wait(&m_helloMutex);
313 for (
auto argsIt = lstjsDebugArguments.begin(), argsItEnd = lstjsDebugArguments.end(); argsIt != argsItEnd; ++argsIt) {
318 const auto argsNext = argsIt + 1;
319 if (argsNext == argsItEnd)
322 portTo = argsNext->toString().toInt(&
ok);
342 const QString message =
tr(
"QML Debugger: Invalid argument \"%1\" detected."
343 " Ignoring the same.").arg(strArgument.
toString());
350 m_blockingMode = block;
358 str <<
tr(
"QML Debugger: Ignoring \"-qmljsdebugger=%1\".").arg(
args) <<
'\n'
359 <<
tr(
"The format is \"-qmljsdebugger=[file:<file>|port:<port_from>][,<port_to>]"
360 "[,host:<ip address>][,block][,services:<service>][,<service>]*\"") <<
'\n'
361 <<
tr(
"\"file:\" can be used to specify the name of a file the debugger will try "
362 "to connect to using a QLocalSocket. If \"file:\" is given any \"host:\" and"
363 "\"port:\" arguments will be ignored.") <<
'\n'
364 <<
tr(
"\"host:\" and \"port:\" can be used to specify an address and a single "
365 "port or a range of ports the debugger will try to bind to with a "
366 "QTcpServer.") <<
'\n'
367 <<
tr(
"\"block\" makes the debugger and some services wait for clients to be "
368 "connected and ready before the first QML engine starts.") <<
'\n'
369 <<
tr(
"\"services:\" can be used to specify which debug services the debugger "
370 "should load. Some debug services interact badly with others. The V4 "
371 "debugger should not be loaded when using the QML profiler as it will force "
372 "any V4 engines to use the JavaScript interpreter rather than the JIT. The "
373 "following debug services are available by default:") <<
'\n'
374 << QQmlEngineDebugService::s_key <<
"\t- " <<
tr(
"The QML debugger") <<
'\n'
375 << QV4DebugService::s_key <<
"\t- " <<
tr(
"The V4 debugger") <<
'\n'
376 << QQmlInspectorService::s_key <<
"\t- " <<
tr(
"The QML inspector") <<
'\n'
377 << QQmlProfilerService::s_key <<
"\t- " <<
tr(
"The QML profiler") <<
'\n'
378 << QQmlEngineControlService::s_key <<
"\t- "
380 <<
tr(
"Allows the client to delay the starting and stopping of\n"
381 "\t\t QML engines until other services are ready. QtCreator\n"
382 "\t\t uses this service with the QML profiler in order to\n"
383 "\t\t profile multiple QML engines at the same time.")
384 <<
'\n' << QDebugMessageService::s_key <<
"\t- "
386 <<
tr(
"Sends qDebug() and similar messages over the QML debug\n"
387 "\t\t connection. QtCreator uses this for showing debug\n"
388 "\t\t messages in the debugger console.") <<
'\n'
389#if QT_CONFIG(translation)
390 <<
'\n' << QQmlDebugTranslationService::s_key <<
"\t- "
392 <<
tr(
"helps to see if a translated text\n"
393 "\t\t will result in an elided text\n"
394 "\t\t in QML elements.") <<
'\n'
396 <<
tr(
"Other services offered by qmltooling plugins that implement "
397 "QQmlDebugServiceFactory and which can be found in the standard plugin "
398 "paths will also be available and can be specified. If no \"services\" "
399 "argument is given, all services found this way, including the default "
400 "ones, are loaded.");
405void QQmlDebugServerImpl::receiveMessage()
425 in >> version >> m_clientPlugins;
429 in >> s_dataStreamVersion;
434 bool clientSupportsMultiPackets =
false;
436 in >> clientSupportsMultiPackets;
444 if (clientSupportsMultiPackets) {
446 pluginNames.reserve(
count);
450 pluginNames <<
i.key();
451 pluginVersions <<
i.value()->version();
456 << pluginNames << pluginVersions << dataStreamVersion();
459 m_connection->
flush();
466 if (m_clientPlugins.contains(
iter.key()))
468 m_changeServiceStateCalls.
ref();
474 }
else if (op == 1) {
477 in >> m_clientPlugins;
482 if (m_clientPlugins.contains(pluginName))
485 if (oldClientPlugins.contains(pluginName)
486 != m_clientPlugins.contains(pluginName)) {
487 m_changeServiceStateCalls.
ref();
493 qWarning(
"QML Debugger: Invalid control message %d.", op);
502 qWarning() <<
"QML Debugger: Message received for missing plugin" <<
name <<
'.';
506 while (!
in.atEnd()) {
512 qWarning(
"QML Debugger: Invalid hello message.");
518void QQmlDebugServerImpl::changeServiceState(
const QString &serviceName,
531 m_changeServiceStateCalls.
deref();
534void QQmlDebugServerImpl::removeThread()
542 m_connection =
nullptr;
564 m_engineConditions[
engine].waitForServices(&m_helloMutex, m_plugins.
size());
581 m_engineConditions[
engine].waitForServices(&m_helloMutex, m_plugins.
size());
594 return i != m_engineConditions.
constEnd() && !
i.value().isWaiting();
606 this, &QQmlDebugServerImpl::sendMessage);
608 this, &QQmlDebugServerImpl::sendMessages);
634 this, &QQmlDebugServerImpl::wakeEngine);
636 this, &QQmlDebugServerImpl::wakeEngine);
639 this, &QQmlDebugServerImpl::sendMessages);
641 this, &QQmlDebugServerImpl::sendMessage);
646bool QQmlDebugServerImpl::canSendMessage(
const QString &
name)
650 return m_connection && m_connection->
isConnected() && m_protocol &&
651 m_clientPlugins.contains(
name);
663 if (canSendMessage(
name)) {
665 m_connection->
flush();
671 if (canSendMessage(
name)) {
677 m_connection->
flush();
687 m_engineConditions[
engine].wake();
690bool QQmlDebugServerImpl::EngineCondition::waitForServices(
QMutex *locked,
int num)
694 return numServices > 0 ?
condition->wait(locked) :
true;
697void QQmlDebugServerImpl::EngineCondition::wake()
699 if (--numServices == 0)
708 this, &QQmlDebugServerImpl::receiveMessage);
710 this, &QQmlDebugServerImpl::protocolError);
716void QQmlDebugServerImpl::protocolError()
718 qWarning(
"QML Debugger: A protocol error has occurred! Giving up ...");
722 m_protocol =
nullptr;
733#include "moc_qqmldebugserverfactory.cpp"
734#include "qqmldebugserverfactory.moc"
std::vector< ObjCStrongReference< CBMutableService > > services
@ Qt_DefaultCompiledVersion
bool processEvents(ProcessEventsFlags flags=AllEvents)
Processes some pending events that match flags.
bool remove(const Key &key)
Removes the item that has the key from the hash.
qsizetype size() const noexcept
Returns the number of items in the hash.
const_iterator constFind(const Key &key) const noexcept
const_iterator constEnd() const noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the ...
iterator find(const Key &key)
Returns an iterator pointing to the item with the key in the hash.
const_iterator constBegin() const noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the first item in the hash.
bool contains(const Key &key) const noexcept
Returns true if the hash contains an item with the key; otherwise returns false.
T value(const Key &key) const noexcept
iterator end() noexcept
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the last ...
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
\inmodule QtCore \reentrant
The QJSEngine class provides an environment for evaluating JavaScript code.
bool isEmpty() const noexcept
void reserve(qsizetype size)
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
QThread * thread() const
Returns the thread in which the object lives.
Q_WEAK_OVERLOAD void setObjectName(const QString &name)
Sets the object's name to name.
void destroyed(QObject *=nullptr)
This signal is emitted immediately before the object obj is destroyed, after any instances of QPointe...
void deleteLater()
\threadsafe
The QPacketProtocol class encapsulates communicating discrete packets across fragmented IO channels,...
void send(const QByteArray &data)
Transmit the packet.
void readyRead()
Emitted whenever a new packet is received.
bool waitForReadyRead(int msecs=3000)
This function locks until a new packet is available for reading and the \l{QIODevice::}{readyRead()} ...
QByteArray read()
Return the next unread packet, or an empty QByteArray if no packets are available.
static Service * service()
static QQmlDebugConnector * instance()
virtual bool isConnected() const =0
virtual void disconnect()=0
QQmlDebugConnector * create(const QString &key) override
void addEngine(QJSEngine *engine) override
bool removeService(const QString &name) override
bool addService(const QString &name, QQmlDebugService *service) override
bool blockingMode() const override
void removeEngine(QJSEngine *engine) override
void setDevice(QIODevice *socket) override
bool open(const QVariantHash &configuration) override
bool hasEngine(QJSEngine *engine) const override
void setPortRange(int portFrom, int portTo, const QString &hostAddress)
void setServer(QQmlDebugServerImpl *server)
void setFileName(const QString &fileName)
const QString & pluginName() const
void messageToClient(const QString &name, const QByteArray &message)
void detachedFromEngine(QJSEngine *)
void attachedToEngine(QJSEngine *)
void messagesToClient(const QString &name, const QList< QByteArray > &messages)
bool startsWith(QStringView s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
QString toString() const
Returns a deep copy of this string view's data as a QString.
int toInt(bool *ok=nullptr, int base=10) const
Returns the string view converted to an int using base base, which is 10 by default and must be betwe...
constexpr QStringView mid(qsizetype pos, qsizetype n=-1) const noexcept
Returns the substring of length length starting at position start in this object.
\macro QT_RESTRICTED_CAST_FROM_ASCII
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
void start(Priority=InheritPriority)
static QThread * currentThread()
void finished(QPrivateSignal)
bool wait(QMutex *, QDeadlineTimer=QDeadlineTimer(QDeadlineTimer::Forever))
void newState(QList< State > &states, const char *token, const char *lexem, bool pre)
const PluginKeyMapConstIterator cend
Combined button and popup list for selecting options.
QHash< QString, QVariant > QVariantHash
void qAddPostRoutine(QtCleanUpFunction p)
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter * iter
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char return DBusMessage dbus_bool_t return DBusMessage dbus_uint32_t return DBusMessage return DBusPendingCall DBusPendingCall return DBusPendingCall return dbus_int32_t return DBusServer * server
DBusConnection * connection
GLenum GLenum GLsizei count
GLuint GLsizei const GLchar * message
GLsizeiptr const void GLenum usage
#define Q_QML_DEBUG_PLUGIN_LOADER(interfaceName)
QT_BEGIN_NAMESPACE const int protocolVersion
static void cleanupOnShutdown()
#define Q_ASSERT_X(cond, x, msg)
#define qPrintable(string)
QLatin1StringView QLatin1String
#define QStringLiteral(str)
QTextStream out(stdout)
[7]
connect(quitButton, &QPushButton::clicked, &app, &QCoreApplication::quit, Qt::QueuedConnection)
myObject disconnect()
[26]
myObject moveToThread(QApplication::instance() ->thread())
[6]
\inmodule QtCore \reentrant