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 QV4PROFILING_H
5#define QV4PROFILING_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 <QtQml/private/qv4global_p.h>
19#include "qv4engine_p.h"
20#include "qv4function_p.h"
21
22#include <QElapsedTimer>
23
24#if !QT_CONFIG(qml_debug)
25
26#define Q_V4_PROFILE_ALLOC(engine, size, type) (!engine)
27#define Q_V4_PROFILE_DEALLOC(engine, size, type) (!engine)
28
29QT_BEGIN_NAMESPACE
30
31namespace QV4 {
32namespace Profiling {
33class Profiler {};
34class FunctionCallProfiler {
35public:
36 FunctionCallProfiler(ExecutionEngine *, Function *) {}
37};
38}
39}
40
41QT_END_NAMESPACE
42
43#else
44
45#define Q_V4_PROFILE_ALLOC(engine, size, type)\
46 (engine->profiler() &&\
47 (engine->profiler()->featuresEnabled & (1 << Profiling::FeatureMemoryAllocation)) ?\
48 engine->profiler()->trackAlloc(size, type) : false)
49
50#define Q_V4_PROFILE_DEALLOC(engine, size, type) \
51 (engine->profiler() &&\
52 (engine->profiler()->featuresEnabled & (1 << Profiling::FeatureMemoryAllocation)) ?\
53 engine->profiler()->trackDealloc(size, type) : false)
54
55QT_BEGIN_NAMESPACE
56
57namespace QV4 {
58
59namespace Profiling {
60
61enum Features {
62 FeatureFunctionCall,
63 FeatureMemoryAllocation
64};
65
66enum MemoryType {
67 HeapPage,
68 LargeItem,
69 SmallItem
70};
71
72struct FunctionCallProperties {
73 qint64 start;
74 qint64 end;
75 quintptr id;
76};
77
78struct FunctionLocation {
79 FunctionLocation(const QString &name = QString(), const QString &file = QString(),
80 int line = -1, int column = -1) :
81 name(name), file(file), line(line), column(column)
82 {}
83
84 bool isValid()
85 {
86 return !name.isEmpty();
87 }
88
89 QString name;
90 QString file;
91 int line;
92 int column;
93};
94
95typedef QHash<quintptr, QV4::Profiling::FunctionLocation> FunctionLocationHash;
96
97struct MemoryAllocationProperties {
98 qint64 timestamp;
99 qint64 size;
100 MemoryType type;
101};
102
103class FunctionCall {
104public:
105 FunctionCall() : m_function(nullptr), m_start(0), m_end(0) {}
106
107 FunctionCall(Function *function, qint64 start, qint64 end) :
108 m_function(function), m_start(start), m_end(end)
109 { m_function->executableCompilationUnit()->addref(); }
110
111 FunctionCall(const FunctionCall &other) :
112 m_function(other.m_function), m_start(other.m_start), m_end(other.m_end)
113 { m_function->executableCompilationUnit()->addref(); }
114
115 FunctionCall(FunctionCall &&other) noexcept
116 : m_function(std::exchange(obj&: other.m_function, new_val: nullptr))
117 , m_start(std::exchange(obj&: other.m_start, new_val: 0))
118 , m_end(std::exchange(obj&: other.m_end, new_val: 0))
119 {}
120
121 ~FunctionCall()
122 {
123 if (m_function)
124 m_function->executableCompilationUnit()->release();
125 }
126
127 FunctionCall &operator=(const FunctionCall &other) {
128 if (&other != this) {
129 if (other.m_function)
130 other.m_function->executableCompilationUnit()->addref();
131 if (m_function)
132 m_function->executableCompilationUnit()->release();
133 m_function = other.m_function;
134 m_start = other.m_start;
135 m_end = other.m_end;
136 }
137 return *this;
138 }
139
140 QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(FunctionCall)
141
142 void swap(FunctionCall &other) noexcept
143 {
144 qt_ptr_swap(lhs&: m_function, rhs&: other.m_function);
145 std::swap(a&: m_start, b&: other.m_start);
146 std::swap(a&: m_end, b&: other.m_end);
147 }
148
149 Function *function() const
150 {
151 return m_function;
152 }
153
154 FunctionLocation resolveLocation() const;
155 FunctionCallProperties properties() const;
156
157private:
158 friend bool operator<(const FunctionCall &call1, const FunctionCall &call2);
159
160 Function *m_function;
161 qint64 m_start;
162 qint64 m_end;
163};
164
165class Q_QML_EXPORT Profiler : public QObject {
166 Q_OBJECT
167public:
168 struct SentMarker {
169 SentMarker() : m_function(nullptr) {}
170
171 SentMarker(const SentMarker &other) : m_function(other.m_function)
172 {
173 if (m_function)
174 m_function->executableCompilationUnit()->addref();
175 }
176
177 ~SentMarker()
178 {
179 if (m_function)
180 m_function->executableCompilationUnit()->release();
181 }
182
183 SentMarker &operator=(const SentMarker &other)
184 {
185 if (&other != this) {
186 if (m_function)
187 m_function->executableCompilationUnit()->release();
188 m_function = other.m_function;
189 m_function->executableCompilationUnit()->addref();
190 }
191 return *this;
192 }
193
194 void setFunction(Function *function)
195 {
196 Q_ASSERT(m_function == nullptr);
197 m_function = function;
198 m_function->executableCompilationUnit()->addref();
199 }
200
201 bool isValid() const
202 { return m_function != nullptr; }
203
204 private:
205 Function *m_function;
206 };
207
208 Profiler(QV4::ExecutionEngine *engine);
209
210 bool trackAlloc(size_t size, MemoryType type)
211 {
212 if (size) {
213 MemoryAllocationProperties allocation = {.timestamp: m_timer.nsecsElapsed(), .size: (qint64)size, .type: type};
214 m_memory_data.append(t: allocation);
215 return true;
216 } else {
217 return false;
218 }
219 }
220
221 bool trackDealloc(size_t size, MemoryType type)
222 {
223 if (size) {
224 MemoryAllocationProperties allocation = {.timestamp: m_timer.nsecsElapsed(), .size: -(qint64)size, .type: type};
225 m_memory_data.append(t: allocation);
226 return true;
227 } else {
228 return false;
229 }
230 }
231
232 quint64 featuresEnabled;
233
234 void stopProfiling();
235 void startProfiling(quint64 features);
236 void reportData();
237 void setTimer(const QElapsedTimer &timer) { m_timer = timer; }
238
239Q_SIGNALS:
240 void dataReady(const QV4::Profiling::FunctionLocationHash &,
241 const QVector<QV4::Profiling::FunctionCallProperties> &,
242 const QVector<QV4::Profiling::MemoryAllocationProperties> &);
243
244private:
245 QV4::ExecutionEngine *m_engine;
246 QElapsedTimer m_timer;
247 QVector<FunctionCall> m_data;
248 QVector<MemoryAllocationProperties> m_memory_data;
249 QHash<quintptr, SentMarker> m_sentLocations;
250
251 friend class FunctionCallProfiler;
252};
253
254class FunctionCallProfiler {
255 Q_DISABLE_COPY(FunctionCallProfiler)
256public:
257
258 // It's enough to ref() the function in the destructor as it will probably not disappear while
259 // it's executing ...
260 FunctionCallProfiler(ExecutionEngine *engine, Function *f)
261 {
262 Profiler *p = engine->profiler();
263 if (Q_UNLIKELY(p) && (p->featuresEnabled & (1 << Profiling::FeatureFunctionCall))) {
264 profiler = p;
265 function = f;
266 startTime = profiler->m_timer.nsecsElapsed();
267 }
268 }
269
270 ~FunctionCallProfiler()
271 {
272 if (profiler)
273 profiler->m_data.append(t: FunctionCall(function, startTime, profiler->m_timer.nsecsElapsed()));
274 }
275
276 Profiler *profiler = nullptr;
277 Function *function = nullptr;
278 qint64 startTime = 0;
279};
280
281
282} // namespace Profiling
283} // namespace QV4
284
285Q_DECLARE_TYPEINFO(QV4::Profiling::MemoryAllocationProperties, Q_RELOCATABLE_TYPE);
286Q_DECLARE_TYPEINFO(QV4::Profiling::FunctionCallProperties, Q_RELOCATABLE_TYPE);
287Q_DECLARE_TYPEINFO(QV4::Profiling::FunctionCall, Q_RELOCATABLE_TYPE);
288Q_DECLARE_TYPEINFO(QV4::Profiling::FunctionLocation, Q_RELOCATABLE_TYPE);
289Q_DECLARE_TYPEINFO(QV4::Profiling::Profiler::SentMarker, Q_RELOCATABLE_TYPE);
290
291QT_END_NAMESPACE
292Q_DECLARE_METATYPE(QV4::Profiling::FunctionLocationHash)
293Q_DECLARE_METATYPE(QVector<QV4::Profiling::FunctionCallProperties>)
294Q_DECLARE_METATYPE(QVector<QV4::Profiling::MemoryAllocationProperties>)
295
296#endif // QT_CONFIG(qml_debug)
297
298#endif // QV4PROFILING_H
299

source code of qtdeclarative/src/qml/jsruntime/qv4profiling_p.h