Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqmlprofiler_p.h
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
4#ifndef QQMLPROFILER_P_H
5#define QQMLPROFILER_P_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists purely as an
12// implementation detail. This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16//
17
18#include <private/qqmlbinding_p.h>
19#include <private/qqmlboundsignal_p.h>
20#include <private/qqmlglobal_p.h>
21#include <private/qv4function_p.h>
22
23#if QT_CONFIG(qml_debug)
24#include "qqmlprofilerdefinitions_p.h"
25#endif
26
27#include <QtCore/qurl.h>
28#include <QtCore/qstring.h>
29
31
32#if !QT_CONFIG(qml_debug)
33
34#define Q_QML_PROFILE_IF_ENABLED(feature, profiler, Code)
35#define Q_QML_PROFILE(feature, profiler, Method)
36#define Q_QML_OC_PROFILE(member, Code)
37
38class QQmlProfiler {};
39
44
49
54
57
58 void init(quintptr, int) {}
59
60 const QV4::CompiledData::Object *pop() { return nullptr; }
61 void push(const QV4::CompiledData::Object *) {}
62
63 static const quintptr profiler = 0;
64};
65
72
77
78#else
79
80#define Q_QML_PROFILE_IF_ENABLED(feature, profiler, Code)
81 if (profiler && (profiler->featuresEnabled & (1 << feature))) {
82 Code;
83 } else
84 (void)0
85
86#define Q_QML_PROFILE(feature, profiler, Method)
87 Q_QML_PROFILE_IF_ENABLED(feature, profiler, profiler->Method)
88
89#define Q_QML_OC_PROFILE(member, Code)
90 Q_QML_PROFILE_IF_ENABLED(QQmlProfilerDefinitions::ProfileCreating, member.profiler, Code)
91
92// This struct is somewhat dangerous to use:
93// The messageType is a bit field. You can pack multiple messages into
94// one object, e.g. RangeStart and RangeLocation. Each one will be read
95// independently when converting to QByteArrays. Thus you can only pack
96// messages if their data doesn't overlap. It's up to you to figure that
97// out.
98struct Q_AUTOTEST_EXPORT QQmlProfilerData : public QQmlProfilerDefinitions
99{
100 QQmlProfilerData(qint64 time = -1, int messageType = -1,
101 RangeType detailType = MaximumRangeType, quintptr locationId = 0) :
102 time(time), locationId(locationId), messageType(messageType), detailType(detailType)
103 {}
104
105 qint64 time;
106 quintptr locationId;
107
108 int messageType; //bit field of QQmlProfilerService::Message
109 RangeType detailType;
110};
111
112Q_DECLARE_TYPEINFO(QQmlProfilerData, Q_RELOCATABLE_TYPE);
113
114class Q_QML_EXPORT QQmlProfiler : public QObject, public QQmlProfilerDefinitions {
115 Q_OBJECT
116public:
117
118 struct Location {
119 Location(const QQmlSourceLocation &location = QQmlSourceLocation(),
120 const QUrl &url = QUrl()) :
121 location(location), url(url) {}
122 QQmlSourceLocation location;
123 QUrl url;
124 };
125
126 // Unfortunately we have to resolve the locations right away because the QML context might not
127 // be available anymore when we send the data.
128 struct RefLocation : public Location {
129 RefLocation()
130 : Location(), locationType(MaximumRangeType), something(nullptr), sent(false)
131 {
132 }
133
134 RefLocation(QV4::Function *ref)
135 : Location(ref->sourceLocation()), locationType(Binding), sent(false)
136 {
137 function = ref;
138 function->executableCompilationUnit()->addref();
139 }
140
141 RefLocation(QV4::ExecutableCompilationUnit *ref, const QUrl &url,
142 const QV4::CompiledData::Object *obj, const QString &type)
143 : Location(QQmlSourceLocation(type, obj->location.line(), obj->location.column()), url),
144 locationType(Creating), sent(false)
145 {
146 unit = ref;
147 unit->addref();
148 }
149
150 RefLocation(QQmlBoundSignalExpression *ref)
151 : Location(ref->sourceLocation()), locationType(HandlingSignal), sent(false)
152 {
153 boundSignal = ref;
154 boundSignal->addref();
155 }
156
157 RefLocation(QQmlDataBlob *ref)
158 : Location(QQmlSourceLocation(), ref->url()), locationType(Compiling), sent(false)
159 {
160 blob = ref;
161 blob->addref();
162 }
163
164 RefLocation(const RefLocation &other)
165 : Location(other),
166 locationType(other.locationType),
167 function(other.function),
168 sent(other.sent)
169 {
170 addref();
171 }
172
173 RefLocation &operator=(const RefLocation &other)
174 {
175 if (this != &other) {
176 release();
177 Location::operator=(other);
178 locationType = other.locationType;
179 function = other.function;
180 sent = other.sent;
181 addref();
182 }
183 return *this;
184 }
185
186 ~RefLocation()
187 {
188 release();
189 }
190
191 void addref()
192 {
193 if (isNull())
194 return;
195
196 switch (locationType) {
197 case Binding:
198 function->executableCompilationUnit()->addref();
199 break;
200 case Creating:
201 unit->addref();
202 break;
203 case HandlingSignal:
204 boundSignal->addref();
205 break;
206 case Compiling:
207 blob->addref();
208 break;
209 default:
210 Q_ASSERT(locationType == MaximumRangeType);
211 break;
212 }
213 }
214
215 void release()
216 {
217 if (isNull())
218 return;
219
220 switch (locationType) {
221 case Binding:
222 function->executableCompilationUnit()->release();
223 break;
224 case Creating:
225 unit->release();
226 break;
227 case HandlingSignal:
228 boundSignal->release();
229 break;
230 case Compiling:
231 blob->release();
232 break;
233 default:
234 Q_ASSERT(locationType == MaximumRangeType);
235 break;
236 }
237 }
238
239 bool isValid() const
240 {
241 return locationType != MaximumRangeType;
242 }
243
244 bool isNull() const
245 {
246 return !something;
247 }
248
249 RangeType locationType;
250 union {
251 QV4::Function *function;
252 QV4::ExecutableCompilationUnit *unit;
253 QQmlBoundSignalExpression *boundSignal;
254 QQmlDataBlob *blob;
255 void *something;
256 };
257 bool sent;
258 };
259
260 typedef QHash<quintptr, Location> LocationHash;
261
262 void startBinding(QV4::Function *function)
263 {
264 // Use the QV4::Function as ID, as that is common among different instances of the same
265 // component. QQmlBinding is per instance.
266 // Add 1 to the ID, to make it different from the IDs the V4 and signal handling profilers
267 // produce. The +1 makes the pointer point into the middle of the QV4::Function. Thus it
268 // still points to valid memory but we cannot accidentally create a duplicate key from
269 // another object.
270 // If there is no function, use a static but valid address: The profiler itself.
271 quintptr locationId = function ? id(function) + 1 : id(this);
272 m_data.append(QQmlProfilerData(m_timer.nsecsElapsed(),
273 (1 << RangeStart | 1 << RangeLocation), Binding,
274 locationId));
275
276 RefLocation &location = m_locations[locationId];
277 if (!location.isValid()) {
278 if (function)
279 location = RefLocation(function);
280 else // Make it valid without actually providing a location
281 location.locationType = Binding;
282 }
283 }
284
285 // Have toByteArrays() construct another RangeData event from the same QString later.
286 // This is somewhat pointless but important for backwards compatibility.
287 void startCompiling(QQmlDataBlob *blob)
288 {
289 quintptr locationId(id(blob));
290 m_data.append(QQmlProfilerData(m_timer.nsecsElapsed(),
291 (1 << RangeStart | 1 << RangeLocation | 1 << RangeData),
292 Compiling, locationId));
293
294 RefLocation &location = m_locations[locationId];
295 if (!location.isValid())
296 location = RefLocation(blob);
297 }
298
299 void startHandlingSignal(QQmlBoundSignalExpression *expression)
300 {
301 // Use the QV4::Function as ID, as that is common among different instances of the same
302 // component. QQmlBoundSignalExpression is per instance.
303 // Add 2 to the ID, to make it different from the IDs the V4 and binding profilers produce.
304 // The +2 makes the pointer point into the middle of the QV4::Function. Thus it still points
305 // to valid memory but we cannot accidentally create a duplicate key from another object.
306 quintptr locationId(id(expression->function()) + 2);
307 m_data.append(QQmlProfilerData(m_timer.nsecsElapsed(),
308 (1 << RangeStart | 1 << RangeLocation), HandlingSignal,
309 locationId));
310
311 RefLocation &location = m_locations[locationId];
312 if (!location.isValid())
313 location = RefLocation(expression);
314 }
315
316 void startCreating(const QV4::CompiledData::Object *obj)
317 {
318 m_data.append(QQmlProfilerData(m_timer.nsecsElapsed(),
319 (1 << RangeStart | 1 << RangeLocation | 1 << RangeData),
320 Creating, id(obj)));
321 }
322
323 void updateCreating(const QV4::CompiledData::Object *obj,
324 QV4::ExecutableCompilationUnit *ref,
325 const QUrl &url, const QString &type)
326 {
327 quintptr locationId(id(obj));
328 RefLocation &location = m_locations[locationId];
329 if (!location.isValid())
330 location = RefLocation(ref, url, obj, type);
331 }
332
333 template<RangeType Range>
334 void endRange()
335 {
336 m_data.append(QQmlProfilerData(m_timer.nsecsElapsed(), 1 << RangeEnd, Range));
337 }
338
339 QQmlProfiler();
340
341 quint64 featuresEnabled;
342
343 template<typename Object>
344 static quintptr id(const Object *pointer)
345 {
346 return reinterpret_cast<quintptr>(pointer);
347 }
348
349 void startProfiling(quint64 features);
350 void stopProfiling();
351 void reportData();
352 void setTimer(const QElapsedTimer &timer) { m_timer = timer; }
353
354Q_SIGNALS:
355 void dataReady(const QVector<QQmlProfilerData> &, const QQmlProfiler::LocationHash &);
356
357protected:
358 QElapsedTimer m_timer;
359 QHash<quintptr, RefLocation> m_locations;
360 QVector<QQmlProfilerData> m_data;
361};
362
363//
364// RAII helper structs
365//
366
367struct QQmlProfilerHelper : public QQmlProfilerDefinitions {
368 QQmlProfiler *profiler;
369 QQmlProfilerHelper(QQmlProfiler *profiler) : profiler(profiler) {}
370};
371
372struct QQmlBindingProfiler : public QQmlProfilerHelper {
373 QQmlBindingProfiler(QQmlProfiler *profiler, QV4::Function *function) :
374 QQmlProfilerHelper(profiler)
375 {
376 Q_QML_PROFILE(QQmlProfilerDefinitions::ProfileBinding, profiler,
377 startBinding(function));
378 }
379
380 ~QQmlBindingProfiler()
381 {
382 Q_QML_PROFILE(QQmlProfilerDefinitions::ProfileBinding, profiler,
383 endRange<Binding>());
384 }
385};
386
387struct QQmlHandlingSignalProfiler : public QQmlProfilerHelper {
388 QQmlHandlingSignalProfiler(QQmlProfiler *profiler, QQmlBoundSignalExpression *expression) :
389 QQmlProfilerHelper(profiler)
390 {
391 Q_QML_PROFILE(QQmlProfilerDefinitions::ProfileHandlingSignal, profiler,
392 startHandlingSignal(expression));
393 }
394
395 ~QQmlHandlingSignalProfiler()
396 {
397 Q_QML_PROFILE(QQmlProfilerDefinitions::ProfileHandlingSignal, profiler,
398 endRange<QQmlProfiler::HandlingSignal>());
399 }
400};
401
402struct QQmlCompilingProfiler : public QQmlProfilerHelper {
403 QQmlCompilingProfiler(QQmlProfiler *profiler, QQmlDataBlob *blob) :
404 QQmlProfilerHelper(profiler)
405 {
406 Q_QML_PROFILE(QQmlProfilerDefinitions::ProfileCompiling, profiler, startCompiling(blob));
407 }
408
409 ~QQmlCompilingProfiler()
410 {
411 Q_QML_PROFILE(QQmlProfilerDefinitions::ProfileCompiling, profiler, endRange<Compiling>());
412 }
413};
414
415struct QQmlVmeProfiler : public QQmlProfilerDefinitions {
416public:
417
418 QQmlVmeProfiler() : profiler(nullptr) {}
419
420 void init(QQmlProfiler *p)
421 {
422 profiler = p;
423 }
424
425 const QV4::CompiledData::Object *pop()
426 {
427 if (ranges.size() > 0) {
428 const auto *result = ranges.back();
429 ranges.pop_back();
430 return result;
431 }
432 return nullptr;
433 }
434
435 void push(const QV4::CompiledData::Object *object)
436 {
437 ranges.push_back(object);
438 }
439
440 QQmlProfiler *profiler;
441
442private:
443 std::vector<const QV4::CompiledData::Object *> ranges;
444};
445
446class QQmlObjectCreationProfiler {
447public:
448
449 QQmlObjectCreationProfiler(QQmlProfiler *profiler, const QV4::CompiledData::Object *obj)
450 : profiler(profiler)
451 {
452 Q_QML_PROFILE(QQmlProfilerDefinitions::ProfileCreating, profiler, startCreating(obj));
453 }
454
455 ~QQmlObjectCreationProfiler()
456 {
457 Q_QML_PROFILE(QQmlProfilerDefinitions::ProfileCreating, profiler, endRange<QQmlProfilerDefinitions::Creating>());
458 }
459
460 void update(QV4::ExecutableCompilationUnit *ref, const QV4::CompiledData::Object *obj,
461 const QString &typeName, const QUrl &url)
462 {
463 profiler->updateCreating(obj, ref, url, typeName);
464 }
465
466private:
467 QQmlProfiler *profiler;
468};
469
470class QQmlObjectCompletionProfiler {
471public:
472 QQmlObjectCompletionProfiler(QQmlVmeProfiler *parent) :
473 profiler(parent->profiler)
474 {
475 Q_QML_PROFILE_IF_ENABLED(QQmlProfilerDefinitions::ProfileCreating, profiler, {
476 profiler->startCreating(parent->pop());
477 });
478 }
479
480 ~QQmlObjectCompletionProfiler()
481 {
482 Q_QML_PROFILE(QQmlProfilerDefinitions::ProfileCreating, profiler,
483 endRange<QQmlProfilerDefinitions::Creating>());
484 }
485private:
486 QQmlProfiler *profiler;
487};
488
489#endif // QT_CONFIG(qml_debug)
490
491QT_END_NAMESPACE
492
493#if QT_CONFIG(qml_debug)
494
495Q_DECLARE_METATYPE(QVector<QQmlProfilerData>)
496Q_DECLARE_METATYPE(QQmlProfiler::LocationHash)
497
498#endif // QT_CONFIG(qml_debug)
499
500#endif // QQMLPROFILER_P_H
Q_DECLARE_TYPEINFO(QByteArrayView, Q_PRIMITIVE_TYPE)
#define Q_QML_PROFILE(feature, profiler, Method)
#define Q_QML_PROFILE_IF_ENABLED(feature, profiler, Code)