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