Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qqmlnativedebugconnector.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5
6#include <private/qhooks_p.h>
7#include <private/qversionedpacket_p.h>
8
9#include <QtQml/qjsengine.h>
10#include <QtCore/qdebug.h>
11#include <QtCore/qjsonarray.h>
12#include <QtCore/qjsondocument.h>
13#include <QtCore/qjsonobject.h>
14#include <QtCore/qjsonvalue.h>
15#include <QtCore/qpointer.h>
16#include <QtCore/qvector.h>
17
18//#define TRACE_PROTOCOL(s) qDebug() << s
19#define TRACE_PROTOCOL(s)
20
22
23static bool expectSyncronousResponse = false;
24Q_GLOBAL_STATIC(QByteArray, responseBuffer)
25
26extern "C" {
27
31
32// In blocking mode, this will busy wait until the debugger sets block to false.
34
35// First thing, set the debug stream version. Please use this function as we might move the version
36// member to some other place.
41
42
43// Break in this one to process output from an asynchronous message/
47
48
49// Break in this one to get notified about construction and destruction of
50// interesting objects, such as QmlEngines.
54
56{
57 responseBuffer->clear();
60}
61
62// Send a message to a service.
63Q_DECL_EXPORT bool qt_qmlDebugSendDataToService(const char *serviceName, const char *hexData)
64{
65 QByteArray msg = QByteArray::fromHex(hexData);
66
68 if (!instance)
69 return false;
70
71 QQmlDebugService *recipient = instance->service(serviceName);
72 if (!recipient)
73 return false;
74
75 TRACE_PROTOCOL("Recipient: " << recipient << " got message: " << msg);
77 recipient->messageReceived(msg);
79
80 return true;
81}
82
83// Enable a service.
85{
87 if (!instance)
88 return false;
89
91 QQmlDebugService *service = instance->service(name);
92 if (!service || service->state() == QQmlDebugService::Enabled)
93 return false;
94
95 service->stateAboutToBeChanged(QQmlDebugService::Enabled);
96 service->setState(QQmlDebugService::Enabled);
97 service->stateChanged(QQmlDebugService::Enabled);
98 return true;
99}
100
102{
104 if (!instance)
105 return false;
106
108 QQmlDebugService *service = instance->service(name);
109 if (!service || service->state() == QQmlDebugService::Unavailable)
110 return false;
111
112 service->stateAboutToBeChanged(QQmlDebugService::Unavailable);
113 service->setState(QQmlDebugService::Unavailable);
114 service->stateChanged(QQmlDebugService::Unavailable);
115 return true;
116}
117
129
130// In blocking mode, this will busy wait until the debugger sets block to false.
132{
133 TRACE_PROTOCOL("Opening native debug connector");
134
135 // FIXME: Use a dedicated hook. Startup is a safe workaround, though,
136 // as we are already beyond its only use.
138
140 ;
141
142 TRACE_PROTOCOL("Opened native debug connector");
143}
144
145} // extern "C"
146
148
150 : m_blockingMode(false)
151{
152 const QString args = commandLineArguments();
153 const auto lstjsDebugArguments = QStringView{args}.split(QLatin1Char(','), Qt::SkipEmptyParts);
155 for (const QStringView &strArgument : lstjsDebugArguments) {
156 if (strArgument == QLatin1String("block")) {
157 m_blockingMode = true;
158 } else if (strArgument == QLatin1String("native")) {
159 // Ignore. This is used to signal that this connector
160 // should be loaded and that has already happened.
161 } else if (strArgument.startsWith(QLatin1String("services:"))) {
162 services.append(strArgument.mid(9).toString());
163 } else if (!services.isEmpty()) {
164 services.append(strArgument.toString());
165 } else if (!strArgument.startsWith(QLatin1String("connector:"))) {
166 qWarning("QML Debugger: Invalid argument \"%s\" detected. Ignoring the same.",
167 strArgument.toUtf8().constData());
168 }
169 }
170 setServices(services);
171}
172
174{
175 for (QQmlDebugService *service : std::as_const(m_services)) {
176 service->stateAboutToBeChanged(QQmlDebugService::NotConnected);
179 }
180}
181
183{
184 return m_blockingMode;
185}
186
188{
189 for (QVector<QQmlDebugService *>::ConstIterator i = m_services.begin(); i != m_services.end();
190 ++i) {
191 if ((*i)->name() == name)
192 return *i;
193 }
194 return nullptr;
195}
196
198{
199 Q_ASSERT(!m_engines.contains(engine));
200
201 TRACE_PROTOCOL("Add engine to connector:" << engine);
202 for (QQmlDebugService *service : std::as_const(m_services))
203 service->engineAboutToBeAdded(engine);
204
205 announceObjectAvailability(QLatin1String("qmlengine"), engine, true);
206
207 for (QQmlDebugService *service : std::as_const(m_services))
208 service->engineAdded(engine);
209
210 m_engines.append(engine);
211}
212
214{
215 Q_ASSERT(m_engines.contains(engine));
216
217 TRACE_PROTOCOL("Remove engine from connector:" << engine);
218 for (QQmlDebugService *service : std::as_const(m_services))
219 service->engineAboutToBeRemoved(engine);
220
221 announceObjectAvailability(QLatin1String("qmlengine"), engine, false);
222
223 for (QQmlDebugService *service : std::as_const(m_services))
224 service->engineRemoved(engine);
225
226 m_engines.removeOne(engine);
227}
228
230{
231 return m_engines.contains(engine);
232}
233
234void QQmlNativeDebugConnector::announceObjectAvailability(const QString &objectType,
235 QObject *object, bool available)
236{
237 QJsonObject ob;
238 ob.insert(QLatin1String("objecttype"), objectType);
239 ob.insert(QLatin1String("object"), QString::number(quintptr(object)));
240 ob.insert(QLatin1String("available"), available);
241 QJsonDocument doc;
242 doc.setObject(ob);
243
247 TRACE_PROTOCOL("Reporting engine availabilty");
248 qt_qmlDebugObjectAvailable(); // Trigger native breakpoint.
249 qt_qmlDebugMessageBuffer = nullptr;
251}
252
254{
255 TRACE_PROTOCOL("Add service to connector: " << qPrintable(name) << service);
256 for (auto it = m_services.cbegin(), end = m_services.cend(); it != end; ++it) {
257 if ((*it)->name() == name)
258 return false;
259 }
260
262 this, &QQmlNativeDebugConnector::sendMessage);
264 this, &QQmlNativeDebugConnector::sendMessages);
265
267
268 m_services << service;
269 return true;
270}
271
273{
274 for (QVector<QQmlDebugService *>::Iterator i = m_services.begin(); i != m_services.end(); ++i) {
275 if ((*i)->name() == name) {
277 m_services.erase(i);
279
281 this, &QQmlNativeDebugConnector::sendMessages);
283 this, &QQmlNativeDebugConnector::sendMessage);
284
285 return true;
286 }
287 }
288 return false;
289}
290
292{
293 m_blockingMode = configuration.value(QStringLiteral("block"), m_blockingMode).toBool();
294 qt_qmlDebugConnectionBlocker = m_blockingMode;
296 return true;
297}
298
300{
301 Q_ASSERT(version <= QDataStream::Qt_DefaultCompiledVersion);
302 s_dataStreamVersion = version;
303}
304
305void QQmlNativeDebugConnector::sendMessage(const QString &name, const QByteArray &message)
306{
307 (*responseBuffer) += name.toUtf8() + ' ' + QByteArray::number(message.size()) + ' ' + message;
308 qt_qmlDebugMessageBuffer = responseBuffer->constData();
309 qt_qmlDebugMessageLength = responseBuffer->size();
310 // Responses are allowed to accumulate, the buffer will be cleared by
311 // separate calls to qt_qmlDebugClearBuffer() once the synchronous
312 // function return ('if' branch below) or in the native breakpoint handler
313 // ('else' branch below).
315 TRACE_PROTOCOL("Expected synchronous response in " << message);
316 // Do not trigger the native breakpoint on qt_qmlDebugMessageFromService.
317 } else {
318 TRACE_PROTOCOL("Found asynchronous message in " << message);
319 // Trigger native breakpoint.
321 }
322}
323
324void QQmlNativeDebugConnector::sendMessages(const QString &name, const QList<QByteArray> &messages)
325{
326 for (int i = 0; i != messages.size(); ++i)
327 sendMessage(name, messages.at(i));
328}
329
331{
332 return key == QLatin1String("QQmlNativeDebugConnector") ? new QQmlNativeDebugConnector : nullptr;
333}
334
336
337#include "moc_qqmlnativedebugconnector.cpp"
std::vector< ObjCStrongReference< CBMutableService > > services
\inmodule QtCore
Definition qbytearray.h:57
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:494
static QByteArray fromHex(const QByteArray &hexEncoded)
Returns a decoded copy of the hex encoded array hexEncoded.
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:124
static QByteArray number(int, int base=10)
Returns a byte-array representing the whole number n as text.
T value(const Key &key) const noexcept
Definition qhash.h:1054
The QJSEngine class provides an environment for evaluating JavaScript code.
Definition qjsengine.h:26
\inmodule QtCore\reentrant
QByteArray toJson(JsonFormat format=Indented) const
void setObject(const QJsonObject &object)
Sets object as the main object of this document.
\inmodule QtCore\reentrant
Definition qjsonobject.h:20
iterator insert(const QString &key, const QJsonValue &value)
Inserts a new item with the key key and a value of value.
\inmodule QtCore
Definition qobject.h:103
static Service * service()
static QQmlDebugConnector * instance()
void messageToClient(const QString &name, const QByteArray &message)
void messagesToClient(const QString &name, const QList< QByteArray > &messages)
QQmlDebugConnector * create(const QString &key) override
bool addService(const QString &name, QQmlDebugService *service) override
static void setDataStreamVersion(int version)
void addEngine(QJSEngine *engine) override
bool open(const QVariantHash &configuration) override
bool hasEngine(QJSEngine *engine) const override
bool removeService(const QString &name) override
void removeEngine(QJSEngine *engine) override
\inmodule QtCore
\inmodule QtCore
Definition qstringview.h:78
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5871
static QString number(int, int base=10)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:8084
bool toBool() const
Returns the variant as a bool if the variant has userType() Bool.
QSet< QString >::iterator it
@ Startup
Definition qhooks_p.h:33
Combined button and popup list for selecting options.
@ SkipEmptyParts
Definition qnamespace.h:128
#define Q_DECL_EXPORT
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
QT_BEGIN_NAMESPACE quintptr Q_CORE_EXPORT qtHookData[]
Definition qhooks.cpp:9
#define qWarning
Definition qlogging.h:166
GLuint64 key
GLuint GLuint end
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint GLsizei const GLchar * message
GLuint name
Q_DECL_EXPORT int qt_qmlDebugMessageLength
static QT_USE_NAMESPACE bool expectSyncronousResponse
Q_DECL_EXPORT bool qt_qmlDebugConnectionBlocker
quintptr qt_qmlDebugTestHooks[]
Q_DECL_EXPORT void qt_qmlDebugClearBuffer()
Q_DECL_EXPORT void qt_qmlDebugConnectorOpen()
Q_DECL_EXPORT bool qt_qmlDebugEnableService(const char *data)
Q_DECL_EXPORT bool qt_qmlDebugDisableService(const char *data)
Q_DECL_EXPORT bool qt_qmlDebugSendDataToService(const char *serviceName, const char *hexData)
Q_DECL_EXPORT void qt_qmlDebugSetStreamVersion(int version)
Q_DECL_EXPORT void qt_qmlDebugMessageAvailable()
#define TRACE_PROTOCOL(s)
Q_DECL_EXPORT void qt_qmlDebugObjectAvailable()
Q_DECL_EXPORT const char * qt_qmlDebugMessageBuffer
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define qPrintable(string)
Definition qstring.h:1531
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define QStringLiteral(str)
size_t quintptr
Definition qtypes.h:167
#define TRACE_PROTOCOL(x)
QByteArray ba
[0]
connect(quitButton, &QPushButton::clicked, &app, &QCoreApplication::quit, Qt::QueuedConnection)
myObject disconnect()
[26]
QJSValueList args
QJSEngine engine
[0]
\inmodule QtCore \reentrant
Definition qchar.h:18