1cd76064162fef1b3bfb33a6bf16f6adf845cbb6
[profile/ivi/qtdeclarative.git] / src / qml / debugger / qv8profilerservice.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qv8profilerservice_p.h"
43 #include "qqmldebugservice_p_p.h"
44 #include "private/qjsconverter_impl_p.h"
45 #include <private/qv8profiler_p.h>
46
47 #include <QtCore/QHash>
48
49 QT_BEGIN_NAMESPACE
50
51 Q_GLOBAL_STATIC(QV8ProfilerService, v8ProfilerInstance)
52
53 class DebugServiceOutputStream : public v8::OutputStream
54 {
55 public:
56     DebugServiceOutputStream()
57         : v8::OutputStream() {}
58     void EndOfStream() {}
59     WriteResult WriteAsciiChunk(char *rawData, int size)
60     {
61         QByteArray data;
62         QDataStream ds(&data, QIODevice::WriteOnly);
63         ds << QV8ProfilerService::V8SnapshotChunk << QByteArray(rawData, size);
64         messages.append(data);
65         return kContinue;
66     }
67     QList<QByteArray> messages;
68 };
69
70 // convert to a QByteArray that can be sent to the debug client
71 QByteArray QV8ProfilerData::toByteArray() const
72 {
73     QByteArray data;
74     //### using QDataStream is relatively expensive
75     QDataStream ds(&data, QIODevice::WriteOnly);
76     ds << messageType << filename << functionname << lineNumber << totalTime << selfTime << treeLevel;
77
78     return data;
79 }
80
81 class QV8ProfilerServicePrivate : public QQmlDebugServicePrivate
82 {
83     Q_DECLARE_PUBLIC(QV8ProfilerService)
84
85 public:
86     QV8ProfilerServicePrivate()
87         :initialized(false)
88     {
89     }
90
91     void takeSnapshot(v8::HeapSnapshot::Type);
92
93     void printProfileTree(const v8::CpuProfileNode *node, int level = 0);
94     void sendMessages();
95
96     QList<QV8ProfilerData> m_data;
97
98     bool initialized;
99     QList<QString> m_ongoing;
100 };
101
102 QV8ProfilerService::QV8ProfilerService(QObject *parent)
103     : QQmlDebugService(*(new QV8ProfilerServicePrivate()), QStringLiteral("V8Profiler"), 1, parent)
104 {
105     Q_D(QV8ProfilerService);
106
107     if (registerService() == Enabled) {
108         // ,block mode, client attached
109         while (!d->initialized)
110             waitForMessage();
111     }
112 }
113
114 QV8ProfilerService::~QV8ProfilerService()
115 {
116 }
117
118 QV8ProfilerService *QV8ProfilerService::instance()
119 {
120     return v8ProfilerInstance();
121 }
122
123 void QV8ProfilerService::initialize()
124 {
125     // just make sure that the service is properly registered
126     v8ProfilerInstance();
127 }
128
129 void QV8ProfilerService::stateAboutToBeChanged(QQmlDebugService::State newState)
130 {
131     Q_D(QV8ProfilerService);
132
133     if (state() == newState)
134         return;
135
136     if (state() == Enabled) {
137         foreach (const QString &title, d->m_ongoing) {
138             QMetaObject::invokeMethod(this, "stopProfiling", Qt::BlockingQueuedConnection,
139                                       Q_ARG(QString, title));
140         }
141         QMetaObject::invokeMethod(this, "sendProfilingData", Qt::BlockingQueuedConnection);
142     }
143 }
144
145 void QV8ProfilerService::messageReceived(const QByteArray &message)
146 {
147     Q_D(QV8ProfilerService);
148
149     QDataStream ds(message);
150     QByteArray command;
151     QByteArray option;
152     QByteArray title;
153     ds >> command >> option;
154
155     if (command == "V8PROFILER") {
156         ds >>  title;
157         QString titleStr = QString::fromUtf8(title);
158         if (option == "start") {
159             QMetaObject::invokeMethod(this, "startProfiling", Qt::QueuedConnection, Q_ARG(QString, titleStr));
160         } else if (option == "stop" && d->initialized) {
161             QMetaObject::invokeMethod(this, "stopProfiling", Qt::QueuedConnection, Q_ARG(QString, titleStr));
162             QMetaObject::invokeMethod(this, "sendProfilingData", Qt::QueuedConnection);
163         }
164         d->initialized = true;
165     }
166
167     if (command == "V8SNAPSHOT") {
168         if (option == "full")
169             QMetaObject::invokeMethod(this, "takeSnapshot", Qt::QueuedConnection);
170         else if (option == "delete") {
171             QMetaObject::invokeMethod(this, "deleteSnapshots", Qt::QueuedConnection);
172         }
173     }
174
175     QQmlDebugService::messageReceived(message);
176 }
177
178 void QV8ProfilerService::startProfiling(const QString &title)
179 {
180     Q_D(QV8ProfilerService);
181     // Start Profiling
182
183     if (d->m_ongoing.contains(title))
184         return;
185
186     v8::HandleScope handle_scope;
187     v8::Handle<v8::String> v8title = v8::String::New(reinterpret_cast<const uint16_t*>(title.data()), title.size());
188     v8::CpuProfiler::StartProfiling(v8title);
189
190     d->m_ongoing.append(title);
191
192     // indicate profiling started
193     QByteArray data;
194     QDataStream ds(&data, QIODevice::WriteOnly);
195     ds << (int)QV8ProfilerService::V8Started;
196
197     sendMessage(data);
198 }
199
200 void QV8ProfilerService::stopProfiling(const QString &title)
201 {
202     Q_D(QV8ProfilerService);
203     // Stop profiling
204
205     if (!d->m_ongoing.contains(title))
206         return;
207     d->m_ongoing.removeOne(title);
208
209     v8::HandleScope handle_scope;
210     v8::Handle<v8::String> v8title = v8::String::New(reinterpret_cast<const uint16_t*>(title.data()), title.size());
211     const v8::CpuProfile *cpuProfile = v8::CpuProfiler::StopProfiling(v8title);
212     if (cpuProfile) {
213         // can happen at start
214         const v8::CpuProfileNode *rootNode = cpuProfile->GetTopDownRoot();
215         d->printProfileTree(rootNode);
216     } else {
217         // indicate completion, even without data
218         QByteArray data;
219         QDataStream ds(&data, QIODevice::WriteOnly);
220         ds << (int)QV8ProfilerService::V8Complete;
221
222         sendMessage(data);
223     }
224 }
225
226 void QV8ProfilerService::takeSnapshot()
227 {
228     Q_D(QV8ProfilerService);
229     d->takeSnapshot(v8::HeapSnapshot::kFull);
230 }
231
232 void QV8ProfilerService::deleteSnapshots()
233 {
234     v8::HeapProfiler::DeleteAllSnapshots();
235 }
236
237 void QV8ProfilerService::sendProfilingData()
238 {
239     Q_D(QV8ProfilerService);
240     // Send messages to client
241     d->sendMessages();
242 }
243
244 void QV8ProfilerServicePrivate::printProfileTree(const v8::CpuProfileNode *node, int level)
245 {
246     for (int index = 0 ; index < node->GetChildrenCount() ; index++) {
247         const v8::CpuProfileNode* childNode = node->GetChild(index);
248         QString scriptResourceName = QJSConverter::toString(childNode->GetScriptResourceName());
249         if (scriptResourceName.length() > 0) {
250
251             QV8ProfilerData rd = {(int)QV8ProfilerService::V8Entry, scriptResourceName,
252                 QJSConverter::toString(childNode->GetFunctionName()),
253                 childNode->GetLineNumber(), childNode->GetTotalTime(), childNode->GetSelfTime(), level};
254             m_data.append(rd);
255
256             // different nodes might have common children: fix at client side
257             if (childNode->GetChildrenCount() > 0) {
258                 printProfileTree(childNode, level+1);
259             }
260         }
261     }
262 }
263
264 void QV8ProfilerServicePrivate::takeSnapshot(v8::HeapSnapshot::Type snapshotType)
265 {
266     Q_Q(QV8ProfilerService);
267
268     v8::HandleScope scope;
269     v8::Local<v8::String> title = v8::String::New("");
270
271     DebugServiceOutputStream outputStream;
272     const v8::HeapSnapshot *snapshot = v8::HeapProfiler::TakeSnapshot(title, snapshotType);
273     snapshot->Serialize(&outputStream, v8::HeapSnapshot::kJSON);
274     QList<QByteArray> messages = outputStream.messages;
275
276     //indicate completion
277     QByteArray data;
278     QDataStream ds(&data, QIODevice::WriteOnly);
279     ds << (int)QV8ProfilerService::V8SnapshotComplete;
280     messages.append(data);
281
282     q->sendMessages(messages);
283 }
284
285 void QV8ProfilerServicePrivate::sendMessages()
286 {
287     Q_Q(QV8ProfilerService);
288
289     QList<QByteArray> messages;
290     for (int i = 0; i < m_data.count(); ++i)
291         messages.append(m_data.at(i).toByteArray());
292     m_data.clear();
293
294     //indicate completion
295     QByteArray data;
296     QDataStream ds(&data, QIODevice::WriteOnly);
297     ds << (int)QV8ProfilerService::V8Complete;
298     messages.append(data);
299
300     q->sendMessages(messages);
301 }
302
303
304 QT_END_NAMESPACE