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

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