Properly support all events in trace files and clean up a bit.
[qt:qtdeclarative.git] / tools / qmlprofiler / qmlprofilerclient.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
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 Digia.  For licensing terms and
14 ** conditions see http://qt.digia.com/licensing.  For further information
15 ** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights.  These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file.  Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qmlprofilerclient.h"
43
44 #include <QtCore/QStack>
45 #include <QtCore/QStringList>
46
47 ProfilerClient::ProfilerClient(const QString &clientName,
48                              QQmlDebugConnection *client)
49     : QQmlDebugClient(clientName, client),
50       m_recording(false),
51       m_enabled(false)
52 {
53 }
54
55 ProfilerClient::~ProfilerClient()
56 {
57     //Disable profiling if started by client
58     //Profiling data will be lost!!
59     if (isRecording())
60         setRecording(false);
61 }
62
63 void ProfilerClient::clearData()
64 {
65     emit cleared();
66 }
67
68 bool ProfilerClient::isEnabled() const
69 {
70     return m_enabled;
71 }
72
73 void ProfilerClient::sendRecordingStatus()
74 {
75 }
76
77 bool ProfilerClient::isRecording() const
78 {
79     return m_recording;
80 }
81
82 void ProfilerClient::setRecording(bool v)
83 {
84     if (v == m_recording)
85         return;
86
87     m_recording = v;
88
89     if (state() == Enabled) {
90         sendRecordingStatus();
91     }
92
93     emit recordingChanged(v);
94 }
95
96 void ProfilerClient::stateChanged(State status)
97 {
98     if ((m_enabled && status != Enabled) ||
99             (!m_enabled && status == Enabled))
100         emit enabledChanged();
101
102     m_enabled = status == Enabled;
103
104 }
105
106 class QmlProfilerClientPrivate
107 {
108 public:
109     QmlProfilerClientPrivate()
110         : inProgressRanges(0)
111         , maximumTime(0)
112     {
113         ::memset(rangeCount, 0,
114                  QQmlProfilerService::MaximumRangeType * sizeof(int));
115     }
116
117     qint64 inProgressRanges;
118     QStack<qint64> rangeStartTimes[QQmlProfilerService::MaximumRangeType];
119     QStack<QStringList> rangeDatas[QQmlProfilerService::MaximumRangeType];
120     QStack<QmlEventLocation> rangeLocations[QQmlProfilerService::MaximumRangeType];
121     QStack<QQmlProfilerService::BindingType> bindingTypes;
122     int rangeCount[QQmlProfilerService::MaximumRangeType];
123     qint64 maximumTime;
124 };
125
126 QmlProfilerClient::QmlProfilerClient(
127         QQmlDebugConnection *client)
128     : ProfilerClient(QStringLiteral("CanvasFrameRate"), client),
129       d(new QmlProfilerClientPrivate)
130 {
131 }
132
133 QmlProfilerClient::~QmlProfilerClient()
134 {
135     delete d;
136 }
137
138 void QmlProfilerClient::clearData()
139 {
140     ::memset(d->rangeCount, 0,
141              QQmlProfilerService::MaximumRangeType * sizeof(int));
142     d->bindingTypes.clear();
143     ProfilerClient::clearData();
144 }
145
146 void QmlProfilerClient::sendRecordingStatus()
147 {
148     QByteArray ba;
149     QDataStream stream(&ba, QIODevice::WriteOnly);
150     stream << isRecording();
151     sendMessage(ba);
152 }
153
154 void QmlProfilerClient::messageReceived(const QByteArray &data)
155 {
156     QByteArray rwData = data;
157     QDataStream stream(&rwData, QIODevice::ReadOnly);
158
159     qint64 time;
160     int messageType;
161
162     stream >> time >> messageType;
163
164     if (messageType >= QQmlProfilerService::MaximumMessage)
165         return;
166
167     if (messageType == QQmlProfilerService::Event) {
168         int event;
169         stream >> event;
170
171         if (event == QQmlProfilerService::EndTrace) {
172             emit this->traceFinished(time);
173             d->maximumTime = time;
174             d->maximumTime = qMax(time, d->maximumTime);
175         } else if (event == QQmlProfilerService::AnimationFrame) {
176             int frameRate, animationCount;
177             int threadId = 0;
178             stream >> frameRate >> animationCount;
179             if (!stream.atEnd())
180                 stream >> threadId;
181             emit this->frame(time, frameRate, animationCount, threadId);
182             d->maximumTime = qMax(time, d->maximumTime);
183         } else if (event == QQmlProfilerService::StartTrace) {
184             emit this->traceStarted(time);
185             d->maximumTime = time;
186         } else if (event < QQmlProfilerService::MaximumEventType) {
187             d->maximumTime = qMax(time, d->maximumTime);
188         }
189     } else if (messageType == QQmlProfilerService::Complete) {
190         emit complete();
191     } else if (messageType == QQmlProfilerService::SceneGraphFrame) {
192         int sgEventType;
193         int count = 0;
194         qint64 params[5];
195
196         stream >> sgEventType;
197         while (!stream.atEnd()) {
198             stream >> params[count++];
199         }
200         while (count<5)
201             params[count++] = 0;
202         emit sceneGraphFrame((QQmlProfilerService::SceneGraphFrameType)sgEventType, time,
203                              params[0], params[1], params[2], params[3], params[4]);
204         d->maximumTime = qMax(time, d->maximumTime);
205     } else if (messageType == QQmlProfilerService::PixmapCacheEvent) {
206         int pixEvTy, width = 0, height = 0, refcount = 0;
207         QString pixUrl;
208         stream >> pixEvTy >> pixUrl;
209         if (pixEvTy == (int)QQmlProfilerService::PixmapReferenceCountChanged ||
210                 pixEvTy == (int)QQmlProfilerService::PixmapCacheCountChanged) {
211             stream >> refcount;
212         } else if (pixEvTy == (int)QQmlProfilerService::PixmapSizeKnown) {
213             stream >> width >> height;
214             refcount = 1;
215         }
216         emit pixmapCache((QQmlProfilerService::PixmapEventType)pixEvTy, time,
217                          QmlEventLocation(pixUrl,0,0), width, height, refcount);
218         d->maximumTime = qMax(time, d->maximumTime);
219     } else {
220         int range;
221         stream >> range;
222
223         if (range >= QQmlProfilerService::MaximumRangeType)
224             return;
225
226         if (messageType == QQmlProfilerService::RangeStart) {
227             d->rangeStartTimes[range].push(time);
228             d->inProgressRanges |= (static_cast<qint64>(1) << range);
229             ++d->rangeCount[range];
230
231             // read binding type
232             if (range == (int)QQmlProfilerService::Binding) {
233                 int bindingType = (int)QQmlProfilerService::QmlBinding;
234                 if (!stream.atEnd())
235                     stream >> bindingType;
236                 d->bindingTypes.push((QQmlProfilerService::BindingType)bindingType);
237             }
238         } else if (messageType == QQmlProfilerService::RangeData) {
239             QString data;
240             stream >> data;
241
242             int count = d->rangeCount[range];
243             if (count > 0) {
244                 while (d->rangeDatas[range].count() < count)
245                     d->rangeDatas[range].push(QStringList());
246                 d->rangeDatas[range][count-1] << data;
247             }
248
249         } else if (messageType == QQmlProfilerService::RangeLocation) {
250             QString fileName;
251             int line;
252             int column = -1;
253             stream >> fileName >> line;
254
255             if (!stream.atEnd())
256                 stream >> column;
257
258             if (d->rangeCount[range] > 0) {
259                 d->rangeLocations[range].push(QmlEventLocation(fileName, line,
260                                                             column));
261             }
262         } else {
263             if (d->rangeCount[range] > 0) {
264                 --d->rangeCount[range];
265                 if (d->inProgressRanges & (static_cast<qint64>(1) << range))
266                     d->inProgressRanges &= ~(static_cast<qint64>(1) << range);
267
268                 d->maximumTime = qMax(time, d->maximumTime);
269                 QStringList data = d->rangeDatas[range].count() ?
270                             d->rangeDatas[range].pop() : QStringList();
271                 QmlEventLocation location = d->rangeLocations[range].count() ?
272                             d->rangeLocations[range].pop() : QmlEventLocation();
273
274                 qint64 startTime = d->rangeStartTimes[range].pop();
275                 QQmlProfilerService::BindingType bindingType = QQmlProfilerService::QmlBinding;
276                 if (range == (int)QQmlProfilerService::Binding)
277                     bindingType = d->bindingTypes.pop();
278                 emit this->range((QQmlProfilerService::RangeType)range,
279                                  bindingType, startTime, time - startTime, data, location);
280                 if (d->rangeCount[range] == 0) {
281                     int count = d->rangeDatas[range].count() +
282                                 d->rangeStartTimes[range].count() +
283                                 d->rangeLocations[range].count();
284                     if (count != 0)
285                         qWarning() << "incorrectly nested data";
286                 }
287             }
288         }
289     }
290 }
291
292 V8ProfilerClient::V8ProfilerClient(QQmlDebugConnection *client)
293     : ProfilerClient(QStringLiteral("V8Profiler"), client)
294 {
295 }
296
297 V8ProfilerClient::~V8ProfilerClient()
298 {
299 }
300
301 void V8ProfilerClient::sendRecordingStatus()
302 {
303     QByteArray ba;
304     QDataStream stream(&ba, QIODevice::WriteOnly);
305     QByteArray cmd("V8PROFILER");
306     QByteArray option("");
307     QByteArray title("");
308
309     if (m_recording) {
310         option = "start";
311     } else {
312         option = "stop";
313     }
314     stream << cmd << option << title;
315     sendMessage(ba);
316 }
317
318 void V8ProfilerClient::messageReceived(const QByteArray &data)
319 {
320     QByteArray rwData = data;
321     QDataStream stream(&rwData, QIODevice::ReadOnly);
322
323     int messageType;
324
325     stream >> messageType;
326
327     if (messageType == V8Complete) {
328         emit complete();
329     } else if (messageType == V8Entry) {
330         QString filename;
331         QString function;
332         int lineNumber;
333         double totalTime;
334         double selfTime;
335         int depth;
336
337         stream  >> filename >> function >> lineNumber >> totalTime >>
338                    selfTime >> depth;
339         emit this->range(depth, function, filename, lineNumber, totalTime,
340                          selfTime);
341     }
342 }
343