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

source code of qtdeclarative/src/qml/debugger/qqmlprofiler_p.h