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#include "qqmlprofilerclient_p_p.h"
41#include "qqmldebugconnection_p.h"
42
43QT_BEGIN_NAMESPACE
44
45QQmlProfilerClientPrivate::~QQmlProfilerClientPrivate()
46{
47}
48
49int QQmlProfilerClientPrivate::resolveType(const QQmlProfilerTypedEvent &event)
50{
51 int typeIndex = -1;
52 if (event.serverTypeId != 0) {
53 QHash<qint64, int>::ConstIterator it = serverTypeIds.constFind(key: event.serverTypeId);
54
55 if (it != serverTypeIds.constEnd()) {
56 typeIndex = it.value();
57 } else {
58 typeIndex = eventReceiver->numLoadedEventTypes();
59 eventReceiver->addEventType(type: event.type);
60 serverTypeIds[event.serverTypeId] = typeIndex;
61 }
62 } else {
63 QHash<QQmlProfilerEventType, int>::ConstIterator it = eventTypeIds.constFind(key: event.type);
64
65 if (it != eventTypeIds.constEnd()) {
66 typeIndex = it.value();
67 } else {
68 typeIndex = eventReceiver->numLoadedEventTypes();
69 eventReceiver->addEventType(type: event.type);
70 eventTypeIds[event.type] = typeIndex;
71 }
72 }
73 return typeIndex;
74}
75
76int QQmlProfilerClientPrivate::resolveStackTop()
77{
78 if (rangesInProgress.isEmpty())
79 return -1;
80
81 QQmlProfilerTypedEvent &typedEvent = rangesInProgress.top();
82 int typeIndex = typedEvent.event.typeIndex();
83 if (typeIndex >= 0)
84 return typeIndex;
85
86 typeIndex = resolveType(event: typedEvent);
87 typedEvent.event.setTypeIndex(typeIndex);
88 while (!pendingMessages.isEmpty()
89 && pendingMessages.head().timestamp() < typedEvent.event.timestamp()) {
90 forwardEvents(last: pendingMessages.dequeue());
91 }
92 forwardEvents(last: typedEvent.event);
93 return typeIndex;
94}
95
96void QQmlProfilerClientPrivate::forwardEvents(const QQmlProfilerEvent &last)
97{
98 forwardDebugMessages(untilTimestamp: last.timestamp());
99 eventReceiver->addEvent(event: last);
100}
101
102void QQmlProfilerClientPrivate::forwardDebugMessages(qint64 untilTimestamp)
103{
104 while (!pendingDebugMessages.isEmpty()
105 && pendingDebugMessages.front().timestamp() <= untilTimestamp) {
106 eventReceiver->addEvent(event: pendingDebugMessages.dequeue());
107 }
108}
109
110void QQmlProfilerClientPrivate::processCurrentEvent()
111{
112 // RangeData and RangeLocation always apply to the range on the top of the stack. Furthermore,
113 // all ranges are perfectly nested. This is why we can defer the type resolution until either
114 // the range ends or a child range starts. With only the information in RangeStart we wouldn't
115 // be able to uniquely identify the event type.
116 Message rangeStage = currentEvent.type.rangeType() == MaximumRangeType ?
117 currentEvent.type.message() : currentEvent.event.rangeStage();
118 switch (rangeStage) {
119 case RangeStart:
120 resolveStackTop();
121 rangesInProgress.push(t: currentEvent);
122 break;
123 case RangeEnd: {
124 int typeIndex = resolveStackTop();
125 if (typeIndex == -1)
126 break;
127 currentEvent.event.setTypeIndex(typeIndex);
128 while (!pendingMessages.isEmpty())
129 forwardEvents(last: pendingMessages.dequeue());
130 forwardEvents(last: currentEvent.event);
131 rangesInProgress.pop();
132 break;
133 }
134 case RangeData:
135 if (!rangesInProgress.isEmpty())
136 rangesInProgress.top().type.setData(currentEvent.type.data());
137 break;
138 case RangeLocation:
139 if (!rangesInProgress.isEmpty())
140 rangesInProgress.top().type.setLocation(currentEvent.type.location());
141 break;
142 case DebugMessage:
143 currentEvent.event.setTypeIndex(resolveType(event: currentEvent));
144 pendingDebugMessages.enqueue(t: currentEvent.event);
145 break;
146 default: {
147 int typeIndex = resolveType(event: currentEvent);
148 currentEvent.event.setTypeIndex(typeIndex);
149 if (rangesInProgress.isEmpty())
150 forwardEvents(last: currentEvent.event);
151 else
152 pendingMessages.enqueue(t: currentEvent.event);
153 break;
154 }
155 }
156}
157
158void QQmlProfilerClientPrivate::sendRecordingStatus(int engineId)
159{
160 Q_Q(QQmlProfilerClient);
161 QPacket stream(connection->currentDataStreamVersion());
162 stream << recording << engineId; // engineId -1 is OK. It means "all of them"
163 if (recording) {
164 stream << requestedFeatures << flushInterval;
165 stream << true; // yes, we support type IDs
166 }
167 q->sendMessage(message: stream.data());
168}
169
170QQmlProfilerClient::QQmlProfilerClient(QQmlDebugConnection *connection,
171 QQmlProfilerEventReceiver *eventReceiver,
172 quint64 features)
173 : QQmlDebugClient(*(new QQmlProfilerClientPrivate(connection, eventReceiver)))
174{
175 Q_D(QQmlProfilerClient);
176 setRequestedFeatures(features);
177 connect(sender: this, signal: &QQmlDebugClient::stateChanged, receiver: this, slot: &QQmlProfilerClient::onStateChanged);
178 connect(sender: d->engineControl.data(), signal: &QQmlEngineControlClient::engineAboutToBeAdded,
179 receiver: this, slot: &QQmlProfilerClient::sendRecordingStatus);
180 connect(sender: d->engineControl.data(), signal: &QQmlEngineControlClient::engineAboutToBeRemoved,
181 context: this, slot: [d](int engineId) {
182 // We may already be done with that engine. Then we don't need to block it.
183 if (d->trackedEngines.contains(t: engineId))
184 d->engineControl->blockEngine(engineId);
185 });
186 connect(sender: this, signal: &QQmlProfilerClient::traceFinished,
187 context: d->engineControl.data(), slot: [d](qint64 timestamp, const QList<int> &engineIds) {
188 Q_UNUSED(timestamp);
189 // The engines might not be blocked because the trace can get finished before engine control
190 // sees them.
191 for (int blocked : d->engineControl->blockedEngines()) {
192 if (engineIds.contains(t: blocked))
193 d->engineControl->releaseEngine(engineId: blocked);
194 }
195 });
196}
197
198QQmlProfilerClient::~QQmlProfilerClient()
199{
200 //Disable profiling if started by client
201 //Profiling data will be lost!!
202 if (isRecording())
203 setRecording(false);
204}
205
206void QQmlProfilerClient::clearEvents()
207{
208 Q_D(QQmlProfilerClient);
209 d->rangesInProgress.clear();
210 d->pendingMessages.clear();
211 d->pendingDebugMessages.clear();
212 if (d->recordedFeatures != 0) {
213 d->recordedFeatures = 0;
214 emit recordedFeaturesChanged(features: 0);
215 }
216 emit cleared();
217}
218
219void QQmlProfilerClient::clearAll()
220{
221 Q_D(QQmlProfilerClient);
222 d->serverTypeIds.clear();
223 d->eventTypeIds.clear();
224 d->trackedEngines.clear();
225 clearEvents();
226}
227
228void QQmlProfilerClientPrivate::finalize()
229{
230 while (!rangesInProgress.isEmpty()) {
231 currentEvent = rangesInProgress.top();
232 currentEvent.event.setRangeStage(RangeEnd);
233 currentEvent.event.setTimestamp(maximumTime);
234 processCurrentEvent();
235 }
236 forwardDebugMessages(untilTimestamp: std::numeric_limits<qint64>::max());
237}
238
239
240void QQmlProfilerClient::sendRecordingStatus(int engineId)
241{
242 Q_D(QQmlProfilerClient);
243 d->sendRecordingStatus(engineId);
244}
245
246bool QQmlProfilerClient::isRecording() const
247{
248 Q_D(const QQmlProfilerClient);
249 return d->recording;
250}
251
252void QQmlProfilerClient::setRecording(bool v)
253{
254 Q_D(QQmlProfilerClient);
255 if (v == d->recording)
256 return;
257
258 d->recording = v;
259
260 if (state() == Enabled)
261 sendRecordingStatus();
262
263 emit recordingChanged(arg: v);
264}
265
266quint64 QQmlProfilerClient::recordedFeatures() const
267{
268 Q_D(const QQmlProfilerClient);
269 return d->recordedFeatures;
270}
271
272void QQmlProfilerClient::setRequestedFeatures(quint64 features)
273{
274 Q_D(QQmlProfilerClient);
275 d->requestedFeatures = features;
276 if (features & static_cast<quint64>(1) << ProfileDebugMessages) {
277 if (d->messageClient.isNull()) {
278 d->messageClient.reset(other: new QQmlDebugMessageClient(connection()));
279 connect(sender: d->messageClient.data(), signal: &QQmlDebugMessageClient::message, context: this,
280 slot: [this](QtMsgType type, const QString &text, const QQmlDebugContextInfo &context)
281 {
282 Q_D(QQmlProfilerClient);
283 d->updateFeatures(feature: ProfileDebugMessages);
284 d->currentEvent.event.setTimestamp(context.timestamp > 0 ? context.timestamp : 0);
285 d->currentEvent.event.setTypeIndex(-1);
286 d->currentEvent.event.setString(text);
287 d->currentEvent.type = QQmlProfilerEventType(
288 DebugMessage, MaximumRangeType, type,
289 QQmlProfilerEventLocation(context.file, context.line, 1));
290 d->currentEvent.serverTypeId = 0;
291 d->processCurrentEvent();
292 });
293 }
294 } else {
295 d->messageClient.reset();
296 }
297}
298
299void QQmlProfilerClient::setFlushInterval(quint32 flushInterval)
300{
301 Q_D(QQmlProfilerClient);
302 d->flushInterval = flushInterval;
303}
304
305QQmlProfilerClient::QQmlProfilerClient(QQmlProfilerClientPrivate &dd) :
306 QQmlDebugClient(dd)
307{
308 Q_D(QQmlProfilerClient);
309 connect(sender: d->engineControl.data(), signal: &QQmlEngineControlClient::engineAboutToBeAdded,
310 receiver: this, slot: &QQmlProfilerClient::sendRecordingStatus);
311}
312
313bool QQmlProfilerClientPrivate::updateFeatures(ProfileFeature feature)
314{
315 Q_Q(QQmlProfilerClient);
316 quint64 flag = 1ULL << feature;
317 if (!(requestedFeatures & flag))
318 return false;
319 if (!(recordedFeatures & flag)) {
320 recordedFeatures |= flag;
321 emit q->recordedFeaturesChanged(features: recordedFeatures);
322 }
323 return true;
324}
325
326void QQmlProfilerClient::onStateChanged(State status)
327{
328 if (status == Enabled) {
329 sendRecordingStatus(engineId: -1);
330 } else {
331 Q_D(QQmlProfilerClient);
332 d->finalize();
333 }
334
335}
336
337void QQmlProfilerClient::messageReceived(const QByteArray &data)
338{
339 Q_D(QQmlProfilerClient);
340 QPacket stream(d->connection->currentDataStreamVersion(), data);
341
342 stream >> d->currentEvent;
343
344 d->maximumTime = qMax(a: d->currentEvent.event.timestamp(), b: d->maximumTime);
345 if (d->currentEvent.type.message() == Complete) {
346 d->finalize();
347 emit complete(maximumTime: d->maximumTime);
348 } else if (d->currentEvent.type.message() == Event
349 && d->currentEvent.type.detailType() == StartTrace) {
350 const QList<int> engineIds = d->currentEvent.event.numbers<QList<int>, qint32>();
351 d->trackedEngines.append(t: engineIds);
352 d->forwardDebugMessages(untilTimestamp: d->currentEvent.event.timestamp());
353 emit traceStarted(timestamp: d->currentEvent.event.timestamp(), engineIds);
354 } else if (d->currentEvent.type.message() == Event
355 && d->currentEvent.type.detailType() == EndTrace) {
356 const QList<int> engineIds = d->currentEvent.event.numbers<QList<int>, qint32>();
357 for (int engineId : engineIds)
358 d->trackedEngines.removeAll(t: engineId);
359 d->forwardDebugMessages(untilTimestamp: d->currentEvent.event.timestamp());
360 emit traceFinished(timestamp: d->currentEvent.event.timestamp(), engineIds);
361 } else if (d->updateFeatures(feature: d->currentEvent.type.feature())) {
362 d->processCurrentEvent();
363 }
364}
365
366QT_END_NAMESPACE
367
368#include "moc_qqmlprofilerclient_p.cpp"
369

source code of qtdeclarative/src/qmldebug/qqmlprofilerclient.cpp