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
qqmlprofilerservice.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
8
9#include <private/qjsengine_p.h>
10#include <private/qqmldebugpluginmanager_p.h>
11
12#include <QtCore/qurl.h>
13#include <QtCore/qtimer.h>
14#include <QtCore/qthread.h>
15#include <QtCore/qcoreapplication.h>
16
17#include <algorithm>
18
20
22
25 m_waitingForStop(false), m_globalEnabled(false), m_globalFeatures(0)
26{
27 m_timer.start();
28 QQmlAbstractProfilerAdapter *quickAdapter =
29 loadQQmlAbstractProfilerAdapter(QLatin1String("QQuickProfilerAdapter"));
30 if (quickAdapter) {
31 addGlobalProfiler(quickAdapter);
32 quickAdapter->setService(this);
33 }
34
35 // try to load QQuick3D profiler adapter if it exists
36 QQmlAbstractProfilerAdapter *quick3DAdapter =
37 loadQQmlAbstractProfilerAdapter(QLatin1String("QQuick3DProfilerAdapter"));
38 if (quick3DAdapter) {
39 addGlobalProfiler(quick3DAdapter);
40 quick3DAdapter->setService(this);
41 }
42
43
44}
45
47{
48 // No need to lock here. If any engine or global profiler is still trying to register at this
49 // point we have a nasty bug anyway.
50 qDeleteAll(m_engineProfilers);
51 qDeleteAll(m_globalProfilers);
52}
53
55{
57 bool dataComplete = true;
58 for (QMultiMap<qint64, QQmlAbstractProfilerAdapter *>::iterator i(m_startTimes.begin()); i != m_startTimes.end();) {
59 if (i.value() == profiler) {
60 m_startTimes.erase(i++);
61 } else {
62 if (i.key() == -1)
63 dataComplete = false;
64 ++i;
65 }
66 }
67 m_startTimes.insert(0, profiler);
68 if (dataComplete) {
69 QList<QJSEngine *> enginesToRelease;
70 for (QJSEngine *engine : std::as_const(m_stoppingEngines)) {
71 const auto range = std::as_const(m_engineProfilers).equal_range(engine);
72 const auto startTimesEnd = m_startTimes.cend();
73 for (auto it = range.first; it != range.second; ++it) {
74 if (std::find(m_startTimes.cbegin(), startTimesEnd, *it) != startTimesEnd) {
75 enginesToRelease.append(engine);
76 break;
77 }
78 }
79 }
80 sendMessages();
81 for (QJSEngine *engine : std::as_const(enginesToRelease)) {
82 m_stoppingEngines.removeOne(engine);
83 emit detachedFromEngine(engine);
84 }
85 }
86}
87
89{
91 "QML profilers have to be added from the engine thread");
92
94 if (QQmlEngine *qmlEngine = qobject_cast<QQmlEngine *>(engine)) {
96 QQmlProfilerAdapter *qmlAdapter = new QQmlProfilerAdapter(this, enginePrivate);
97 addEngineProfiler(qmlAdapter, engine);
98 QQmlProfilerAdapter *compileAdapter
99 = new QQmlProfilerAdapter(this, &(enginePrivate->typeLoader));
100 addEngineProfiler(compileAdapter, engine);
101 }
102 QV4ProfilerAdapter *v4Adapter = new QV4ProfilerAdapter(this, engine->handle());
103 addEngineProfiler(v4Adapter, engine);
105}
106
108{
110 "QML profilers have to be added from the engine thread");
111
113
114 if (m_globalEnabled)
115 startProfiling(engine, m_globalFeatures);
116
117 const auto range = std::as_const(m_engineProfilers).equal_range(engine);
118 for (auto it = range.first; it != range.second; ++it)
119 (*it)->stopWaiting();
120}
121
123{
125 "QML profilers have to be removed from the engine thread");
126
128 bool isRunning = false;
129 const auto range = std::as_const(m_engineProfilers).equal_range(engine);
130 for (auto it = range.first; it != range.second; ++it) {
131 QQmlAbstractProfilerAdapter *profiler = *it;
132 if (profiler->isRunning())
133 isRunning = true;
134 profiler->startWaiting();
135 }
136 if (isRunning) {
137 m_stoppingEngines.append(engine);
139 } else {
140 emit detachedFromEngine(engine);
141 }
142}
143
145{
147 "QML profilers have to be removed from the engine thread");
148
150 const auto range = std::as_const(m_engineProfilers).equal_range(engine);
151 for (auto it = range.first; it != range.second; ++it) {
152 QQmlAbstractProfilerAdapter *profiler = *it;
153 removeProfilerFromStartTimes(profiler);
154 delete profiler;
155 }
156 m_engineProfilers.remove(engine);
157}
158
159void QQmlProfilerServiceImpl::addEngineProfiler(QQmlAbstractProfilerAdapter *profiler, QJSEngine *engine)
160{
161 profiler->moveToThread(thread());
162 profiler->synchronize(m_timer);
163 m_engineProfilers.insert(engine, profiler);
164}
165
167{
169 profiler->synchronize(m_timer);
170 m_globalProfilers.append(profiler);
171 // Global profiler, not connected to a specific engine.
172 // Global profilers are started whenever any engine profiler is started and stopped when
173 // all engine profilers are stopped.
174 quint64 features = 0;
175 for (QQmlAbstractProfilerAdapter *engineProfiler : std::as_const(m_engineProfilers))
176 features |= engineProfiler->features();
177
178 if (features != 0)
179 profiler->startProfiling(features);
180}
181
183{
185 removeProfilerFromStartTimes(profiler);
186 m_globalProfilers.removeOne(profiler);
187}
188
189void QQmlProfilerServiceImpl::removeProfilerFromStartTimes(const QQmlAbstractProfilerAdapter *profiler)
190{
192 i != m_startTimes.end();) {
193 if (i.value() == profiler) {
194 m_startTimes.erase(i++);
195 break;
196 } else {
197 ++i;
198 }
199 }
200}
201
209{
211
212 if (features & static_cast<quint64>(1) << ProfileDebugMessages) {
213 if (QDebugMessageService *messageService =
214 QQmlDebugConnector::instance()->service<QDebugMessageService>())
215 messageService->synchronizeTime(m_timer);
216 }
217
219
220 d << m_timer.nsecsElapsed() << static_cast<qint32>(Event) << static_cast<qint32>(StartTrace);
221 bool startedAny = false;
222 if (engine != nullptr) {
223 const auto range = std::as_const(m_engineProfilers).equal_range(engine);
224 for (auto it = range.first; it != range.second; ++it) {
225 QQmlAbstractProfilerAdapter *profiler = *it;
226 if (!profiler->isRunning()) {
227 profiler->startProfiling(features);
228 startedAny = true;
229 }
230 }
231 if (startedAny)
232 d << idForObject(engine);
233 } else {
234 m_globalEnabled = true;
235 m_globalFeatures = features;
236
237 QSet<QJSEngine *> engines;
239 i != m_engineProfilers.end(); ++i) {
240 if (!i.value()->isRunning()) {
241 engines << i.key();
242 i.value()->startProfiling(features);
243 startedAny = true;
244 }
245 }
246 for (QJSEngine *profiledEngine : std::as_const(engines))
247 d << idForObject(profiledEngine);
248 }
249
250 if (startedAny) {
251 for (QQmlAbstractProfilerAdapter *profiler : std::as_const(m_globalProfilers)) {
252 if (!profiler->isRunning())
253 profiler->startProfiling(features);
254 }
255
257 emit messageToClient(name(), d.data());
258 }
259}
260
269{
271 QList<QQmlAbstractProfilerAdapter *> stopping;
272 QList<QQmlAbstractProfilerAdapter *> reporting;
273
274 if (engine == nullptr)
275 m_globalEnabled = false;
276
277 bool stillRunning = false;
279 i != m_engineProfilers.end(); ++i) {
280 if (i.value()->isRunning()) {
281 m_startTimes.insert(-1, i.value());
282 if (engine == nullptr || i.key() == engine) {
283 stopping << i.value();
284 } else {
285 reporting << i.value();
286 stillRunning = true;
287 }
288 }
289 }
290
291 if (stopping.isEmpty())
292 return;
293
294 for (QQmlAbstractProfilerAdapter *profiler : std::as_const(m_globalProfilers)) {
295 if (!profiler->isRunning())
296 continue;
297 m_startTimes.insert(-1, profiler);
298 if (stillRunning) {
299 reporting << profiler;
300 } else {
301 stopping << profiler;
302 }
303 }
304
306 m_waitingForStop = true;
307
308 for (QQmlAbstractProfilerAdapter *profiler : std::as_const(reporting))
309 profiler->reportData();
310
311 for (QQmlAbstractProfilerAdapter *profiler : std::as_const(stopping))
312 profiler->stopProfiling();
313}
314
315/*
316 Send the queued up messages.
317*/
318void QQmlProfilerServiceImpl::sendMessages()
319{
320 QList<QByteArray> messages;
321
322 QQmlDebugPacket traceEnd;
323 if (m_waitingForStop) {
324 traceEnd << m_timer.nsecsElapsed() << static_cast<qint32>(Event)
325 << static_cast<qint32>(EndTrace);
326
327 QSet<QJSEngine *> seen;
328 for (QQmlAbstractProfilerAdapter *profiler : std::as_const(m_startTimes)) {
330 i != m_engineProfilers.end(); ++i) {
331 if (i.value() == profiler && !seen.contains(i.key())) {
332 seen << i.key();
333 traceEnd << idForObject(i.key());
334 }
335 }
336 }
337 }
338
339 while (!m_startTimes.empty()) {
340 QQmlAbstractProfilerAdapter *first = m_startTimes.begin().value();
341 m_startTimes.erase(m_startTimes.begin());
342 qint64 next = first->sendMessages(m_startTimes.isEmpty() ?
343 std::numeric_limits<qint64>::max() :
344 m_startTimes.begin().key(), messages);
345 if (next != -1)
346 m_startTimes.insert(next, first);
347
349 emit messagesToClient(name(), messages);
350 messages.clear();
351 }
352 }
353
354 bool stillRunning = false;
355 for (const QQmlAbstractProfilerAdapter *profiler : std::as_const(m_engineProfilers)) {
356 if (profiler->isRunning()) {
357 stillRunning = true;
358 break;
359 }
360 }
361
362 if (m_waitingForStop) {
363 // EndTrace can be sent multiple times, as it's engine specific.
364 messages << traceEnd.data();
365
366 if (!stillRunning) {
367 // Complete is only sent once, when no engines are running anymore.
369 ds << static_cast<qint64>(-1) << static_cast<qint32>(Complete);
370 messages << ds.data();
371 m_waitingForStop = false;
372 }
373 }
374
375 emit messagesToClient(name(), messages);
376
377 // Restart flushing if any profilers are still running
378 if (stillRunning)
380}
381
383{
385
386 if (state() == newState)
387 return;
388
389 // Stop all profiling and send the data before we get disabled.
390 if (newState != Enabled) {
391 for (auto it = m_engineProfilers.keyBegin(), end = m_engineProfilers.keyEnd();
392 it != end; ++it) {
394 }
395 }
396}
397
399{
401
403
404 int engineId = -1;
405 quint64 features = std::numeric_limits<quint64>::max();
406 bool enabled;
407 quint32 flushInterval = 0;
408 stream >> enabled;
409 if (!stream.atEnd())
410 stream >> engineId;
411 if (!stream.atEnd())
412 stream >> features;
413 if (!stream.atEnd()) {
414 stream >> flushInterval;
415 m_flushTimer.setInterval(
416 static_cast<int>(qMin(flushInterval,
417 static_cast<quint32>(std::numeric_limits<int>::max()))));
418 auto timerStart = static_cast<void(QTimer::*)()>(&QTimer::start);
419 if (flushInterval > 0) {
420 connect(&m_flushTimer, &QTimer::timeout, this, &QQmlProfilerServiceImpl::flush);
421 connect(this, &QQmlProfilerServiceImpl::startFlushTimer, &m_flushTimer, timerStart);
423 } else {
424 disconnect(&m_flushTimer, &QTimer::timeout, this, &QQmlProfilerServiceImpl::flush);
425 disconnect(this, &QQmlProfilerServiceImpl::startFlushTimer, &m_flushTimer, timerStart);
427 &m_flushTimer, &QTimer::stop);
428 }
429 }
430
431 bool useMessageTypes = false;
432 if (!stream.atEnd())
433 stream >> useMessageTypes;
434
435 // If engineId == -1 objectForId() and then the cast will return 0.
436 if (enabled && useMessageTypes) // If the client doesn't support message types don't profile.
437 startProfiling(qobject_cast<QJSEngine *>(objectForId(engineId)), features);
438 else if (!enabled) // On stopProfiling the client doesn't repeat useMessageTypes.
439 stopProfiling(qobject_cast<QJSEngine *>(objectForId(engineId)));
440
441 stopWaiting();
442}
443
444void QQmlProfilerServiceImpl::flush()
445{
447 QList<QQmlAbstractProfilerAdapter *> reporting;
448
449 for (QQmlAbstractProfilerAdapter *profiler : std::as_const(m_engineProfilers)) {
450 if (profiler->isRunning()) {
451 m_startTimes.insert(-1, profiler);
452 reporting.append(profiler);
453 }
454 }
455
456 for (QQmlAbstractProfilerAdapter *profiler : std::as_const(m_globalProfilers)) {
457 if (profiler->isRunning()) {
458 m_startTimes.insert(-1, profiler);
459 reporting.append(profiler);
460 }
461 }
462
463 for (QQmlAbstractProfilerAdapter *profiler : std::as_const(reporting))
464 profiler->reportData();
465}
466
468
469#include "moc_qqmlprofilerservice.cpp"
\inmodule QtCore
Definition qbytearray.h:57
qint64 nsecsElapsed() const noexcept
The QJSEngine class provides an environment for evaluating JavaScript code.
Definition qjsengine.h:26
QV4::ExecutionEngine * handle() const
Definition qjsengine.h:298
bool removeOne(const AT &t)
Definition qlist.h:598
void append(parameter_type t)
Definition qlist.h:458
\inmodule QtCore
Definition qhash.h:1788
key_iterator keyBegin() const noexcept
Definition qhash.h:1926
qsizetype remove(const Key &key)
Definition qhash.h:1596
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:2034
iterator begin()
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the first item in the hash.
Definition qhash.h:1918
key_iterator keyEnd() const noexcept
Definition qhash.h:1927
iterator end() noexcept
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the last ...
Definition qhash.h:1922
iterator end()
Definition qmap.h:1327
bool empty() const
Definition qmap.h:1520
bool isEmpty() const
Definition qmap.h:940
iterator insert(const Key &key, const T &value)
Definition qmap.h:1452
iterator erase(const_iterator it)
Definition qmap.h:1344
const_iterator cbegin() const
Definition qmap.h:1326
iterator begin()
Definition qmap.h:1323
const_iterator cend() const
Definition qmap.h:1330
\inmodule QtCore
Definition qmutex.h:313
\inmodule QtCore
Definition qobject.h:103
QThread * thread() const
Returns the thread in which the object lives.
Definition qobject.cpp:1598
bool moveToThread(QThread *thread QT6_DECL_NEW_OVERLOAD_TAIL)
Changes the thread affinity for this object and its children and returns true on success.
Definition qobject.cpp:1643
const QByteArray & data() const
Returns a reference to the raw packet data.
Definition qpacket.cpp:77
void synchronize(const QElapsedTimer &t)
void engineAboutToBeAdded(QJSEngine *engine) override
static QQmlDebugConnector * instance()
QQmlTypeLoader typeLoader
static QQmlEnginePrivate * get(QQmlEngine *e)
The QQmlEngine class provides an environment for instantiating QML components.
Definition qqmlengine.h:57
void engineAboutToBeRemoved(QJSEngine *engine) override
void engineAboutToBeAdded(QJSEngine *engine) override
void engineAdded(QJSEngine *engine) override
void messageReceived(const QByteArray &) override
void stateAboutToBeChanged(State state) override
void addGlobalProfiler(QQmlAbstractProfilerAdapter *profiler) override
void dataReady(QQmlAbstractProfilerAdapter *profiler) override
void engineRemoved(QJSEngine *engine) override
void stopProfiling(QJSEngine *engine) override
Stop profiling the given engine.
void removeGlobalProfiler(QQmlAbstractProfilerAdapter *profiler) override
void startProfiling(QJSEngine *engine, quint64 features=std::numeric_limits< quint64 >::max()) override
Start profiling the given engine.
static QThread * currentThread()
Definition qthread.cpp:1039
\inmodule QtCore
Definition qtimer.h:20
void setInterval(int msec)
Definition qtimer.cpp:579
void start()
Definition qtimer.cpp:210
void stop()
Stops the timer.
Definition qtimer.cpp:267
void timeout(QPrivateSignal)
This signal is emitted when the timer times out.
qDeleteAll(list.begin(), list.end())
QSet< QString >::iterator it
else opt state
[0]
void newState(QList< State > &states, const char *token, const char *lexem, bool pre)
short next
Definition keywords.cpp:445
Combined button and popup list for selecting options.
#define Q_FUNC_INFO
EGLStreamKHR stream
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
GLuint64 key
GLuint GLuint end
GLenum GLenum GLsizei const GLuint GLboolean enabled
GLsizei range
GLuint GLsizei const GLchar * message
GLuint name
GLint first
QQmlEngine * qmlEngine(const QObject *obj)
Definition qqml.cpp:80
#define Q_QML_DEBUG_PLUGIN_LOADER(interfaceName)
QVersionedPacket< QQmlDebugConnector > QQmlDebugPacket
int objectForId(const ObjectContainer *objectContainer, const CompiledObject &component, int id)
#define Q_ASSERT_X(cond, x, msg)
Definition qrandom.cpp:48
QtPrivate::QRegularExpressionMatchIteratorRangeBasedForIterator begin(const QRegularExpressionMatchIterator &iterator)
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
static bool isRunning()
Definition main.cpp:452
#define emit
unsigned int quint32
Definition qtypes.h:50
int qint32
Definition qtypes.h:49
unsigned long long quint64
Definition qtypes.h:61
long long qint64
Definition qtypes.h:60
#define enabled
connect(quitButton, &QPushButton::clicked, &app, &QCoreApplication::quit, Qt::QueuedConnection)
myObject disconnect()
[26]
QReadWriteLock lock
[0]
QJSEngine engine
[0]