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 | |
29 | QT_BEGIN_NAMESPACE |
30 | |
31 | namespace QV4 { |
32 | namespace Profiling { |
33 | class Profiler {}; |
34 | class FunctionCallProfiler { |
35 | public: |
36 | FunctionCallProfiler(ExecutionEngine *, Function *) {} |
37 | }; |
38 | } |
39 | } |
40 | |
41 | QT_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 | |
55 | QT_BEGIN_NAMESPACE |
56 | |
57 | namespace QV4 { |
58 | |
59 | namespace Profiling { |
60 | |
61 | enum Features { |
62 | FeatureFunctionCall, |
63 | FeatureMemoryAllocation |
64 | }; |
65 | |
66 | enum MemoryType { |
67 | HeapPage, |
68 | LargeItem, |
69 | SmallItem |
70 | }; |
71 | |
72 | struct FunctionCallProperties { |
73 | qint64 start; |
74 | qint64 end; |
75 | quintptr id; |
76 | }; |
77 | |
78 | struct 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 | |
95 | typedef QHash<quintptr, QV4::Profiling::FunctionLocation> FunctionLocationHash; |
96 | |
97 | struct MemoryAllocationProperties { |
98 | qint64 timestamp; |
99 | qint64 size; |
100 | MemoryType type; |
101 | }; |
102 | |
103 | class FunctionCall { |
104 | public: |
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 | |
157 | private: |
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 | |
165 | class Q_QML_EXPORT Profiler : public QObject { |
166 | Q_OBJECT |
167 | public: |
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 | |
239 | Q_SIGNALS: |
240 | void dataReady(const QV4::Profiling::FunctionLocationHash &, |
241 | const QVector<QV4::Profiling::FunctionCallProperties> &, |
242 | const QVector<QV4::Profiling::MemoryAllocationProperties> &); |
243 | |
244 | private: |
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 | |
254 | class FunctionCallProfiler { |
255 | Q_DISABLE_COPY(FunctionCallProfiler) |
256 | public: |
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 | |
285 | Q_DECLARE_TYPEINFO(QV4::Profiling::MemoryAllocationProperties, Q_RELOCATABLE_TYPE); |
286 | Q_DECLARE_TYPEINFO(QV4::Profiling::FunctionCallProperties, Q_RELOCATABLE_TYPE); |
287 | Q_DECLARE_TYPEINFO(QV4::Profiling::FunctionCall, Q_RELOCATABLE_TYPE); |
288 | Q_DECLARE_TYPEINFO(QV4::Profiling::FunctionLocation, Q_RELOCATABLE_TYPE); |
289 | Q_DECLARE_TYPEINFO(QV4::Profiling::Profiler::SentMarker, Q_RELOCATABLE_TYPE); |
290 | |
291 | QT_END_NAMESPACE |
292 | Q_DECLARE_METATYPE(QV4::Profiling::FunctionLocationHash) |
293 | Q_DECLARE_METATYPE(QVector<QV4::Profiling::FunctionCallProperties>) |
294 | Q_DECLARE_METATYPE(QVector<QV4::Profiling::MemoryAllocationProperties>) |
295 | |
296 | #endif // QT_CONFIG(qml_debug) |
297 | |
298 | #endif // QV4PROFILING_H |
299 | |