QmlDebugging: Revert the names of services
[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     QQmlDebugService &_service;
56 public:
57     DebugServiceOutputStream(QQmlDebugService &service)
58         : v8::OutputStream(),
59           _service(service) {}
60     void EndOfStream() {}
61     WriteResult WriteAsciiChunk(char *rawData, int size)
62     {
63         QByteArray data;
64         QDataStream ds(&data, QIODevice::WriteOnly);
65         ds << QV8ProfilerService::V8SnapshotChunk << QByteArray(rawData, size);
66         _service.sendMessage(data);
67         return kContinue;
68     }
69 };
70
71 // convert to a QByteArray that can be sent to the debug client
72 QByteArray QV8ProfilerData::toByteArray() const
73 {
74     QByteArray data;
75     //### using QDataStream is relatively expensive
76     QDataStream ds(&data, QIODevice::WriteOnly);
77     ds << messageType << filename << functionname << lineNumber << totalTime << selfTime << treeLevel;
78
79     return data;
80 }
81
82 class QV8ProfilerServicePrivate : public QQmlDebugServicePrivate
83 {
84     Q_DECLARE_PUBLIC(QV8ProfilerService)
85
86 public:
87     QV8ProfilerServicePrivate()
88         :initialized(false)
89     {
90     }
91
92     void takeSnapshot(v8::HeapSnapshot::Type);
93
94     void printProfileTree(const v8::CpuProfileNode *node, int level = 0);
95     void sendMessages();
96
97     QList<QV8ProfilerData> m_data;
98
99     bool initialized;
100     QList<QString> m_ongoing;
101 };
102
103 QV8ProfilerService::QV8ProfilerService(QObject *parent)
104     : QQmlDebugService(*(new QV8ProfilerServicePrivate()), QLatin1String("V8Profiler"), 1, parent)
105 {
106     Q_D(QV8ProfilerService);
107
108     if (registerService() == Enabled) {
109         // ,block mode, client attached
110         while (!d->initialized)
111             waitForMessage();
112     }
113 }
114
115 QV8ProfilerService::~QV8ProfilerService()
116 {
117 }
118
119 QV8ProfilerService *QV8ProfilerService::instance()
120 {
121     return v8ProfilerInstance();
122 }
123
124 void QV8ProfilerService::initialize()
125 {
126     // just make sure that the service is properly registered
127     v8ProfilerInstance();
128 }
129
130 void QV8ProfilerService::stateAboutToBeChanged(QQmlDebugService::State newState)
131 {
132     Q_D(QV8ProfilerService);
133
134     if (state() == newState)
135         return;
136
137     if (state() == Enabled) {
138         foreach (const QString &title, d->m_ongoing)
139             QMetaObject::invokeMethod(this, "stopProfiling", Qt::QueuedConnection, Q_ARG(QString, title));
140         sendProfilingData();
141     }
142 }
143
144 void QV8ProfilerService::messageReceived(const QByteArray &message)
145 {
146     Q_D(QV8ProfilerService);
147
148     QDataStream ds(message);
149     QByteArray command;
150     QByteArray option;
151     QByteArray title;
152     ds >> command >> option;
153
154     if (command == "V8PROFILER") {
155         ds >>  title;
156         QString titleStr = QString::fromUtf8(title);
157         if (option == "start") {
158             QMetaObject::invokeMethod(this, "startProfiling", Qt::QueuedConnection, Q_ARG(QString, titleStr));
159         } else if (option == "stop" && d->initialized) {
160             QMetaObject::invokeMethod(this, "stopProfiling", Qt::QueuedConnection, Q_ARG(QString, titleStr));
161             QMetaObject::invokeMethod(this, "sendProfilingData", Qt::QueuedConnection);
162         }
163         d->initialized = true;
164     }
165
166     if (command == "V8SNAPSHOT") {
167         if (option == "full")
168             QMetaObject::invokeMethod(this, "takeSnapshot", Qt::QueuedConnection);
169         else if (option == "delete") {
170             QMetaObject::invokeMethod(this, "deleteSnapshots", Qt::QueuedConnection);
171         }
172     }
173
174     QQmlDebugService::messageReceived(message);
175 }
176
177 void QV8ProfilerService::startProfiling(const QString &title)
178 {
179     Q_D(QV8ProfilerService);
180     // Start Profiling
181
182     if (d->m_ongoing.contains(title))
183         return;
184
185     v8::HandleScope handle_scope;
186     v8::Handle<v8::String> v8title = v8::String::New(reinterpret_cast<const uint16_t*>(title.data()), title.size());
187     v8::CpuProfiler::StartProfiling(v8title);
188
189     d->m_ongoing.append(title);
190
191     // indicate profiling started
192     QByteArray data;
193     QDataStream ds(&data, QIODevice::WriteOnly);
194     ds << (int)QV8ProfilerService::V8Started;
195
196     sendMessage(data);
197 }
198
199 void QV8ProfilerService::stopProfiling(const QString &title)
200 {
201     Q_D(QV8ProfilerService);
202     // Stop profiling
203
204     if (!d->m_ongoing.contains(title))
205         return;
206     d->m_ongoing.removeOne(title);
207
208     v8::HandleScope handle_scope;
209     v8::Handle<v8::String> v8title = v8::String::New(reinterpret_cast<const uint16_t*>(title.data()), title.size());
210     const v8::CpuProfile *cpuProfile = v8::CpuProfiler::StopProfiling(v8title);
211     if (cpuProfile) {
212         // can happen at start
213         const v8::CpuProfileNode *rootNode = cpuProfile->GetTopDownRoot();
214         d->printProfileTree(rootNode);
215     } else {
216         // indicate completion, even without data
217         QByteArray data;
218         QDataStream ds(&data, QIODevice::WriteOnly);
219         ds << (int)QV8ProfilerService::V8Complete;
220
221         sendMessage(data);
222     }
223 }
224
225 void QV8ProfilerService::takeSnapshot()
226 {
227     Q_D(QV8ProfilerService);
228     d->takeSnapshot(v8::HeapSnapshot::kFull);
229 }
230
231 void QV8ProfilerService::deleteSnapshots()
232 {
233     v8::HeapProfiler::DeleteAllSnapshots();
234 }
235
236 void QV8ProfilerService::sendProfilingData()
237 {
238     Q_D(QV8ProfilerService);
239     // Send messages to client
240     d->sendMessages();
241 }
242
243 void QV8ProfilerServicePrivate::printProfileTree(const v8::CpuProfileNode *node, int level)
244 {
245     for (int index = 0 ; index < node->GetChildrenCount() ; index++) {
246         const v8::CpuProfileNode* childNode = node->GetChild(index);
247         QString scriptResourceName = QJSConverter::toString(childNode->GetScriptResourceName());
248         if (scriptResourceName.length() > 0) {
249
250             QV8ProfilerData rd = {(int)QV8ProfilerService::V8Entry, scriptResourceName,
251                 QJSConverter::toString(childNode->GetFunctionName()),
252                 childNode->GetLineNumber(), childNode->GetTotalTime(), childNode->GetSelfTime(), level};
253             m_data.append(rd);
254
255             // different nodes might have common children: fix at client side
256             if (childNode->GetChildrenCount() > 0) {
257                 printProfileTree(childNode, level+1);
258             }
259         }
260     }
261 }
262
263 void QV8ProfilerServicePrivate::takeSnapshot(v8::HeapSnapshot::Type snapshotType)
264 {
265     Q_Q(QV8ProfilerService);
266
267     v8::HandleScope scope;
268     v8::Local<v8::String> title = v8::String::New("");
269
270     DebugServiceOutputStream outputStream(*q);
271     const v8::HeapSnapshot *snapshot = v8::HeapProfiler::TakeSnapshot(title, snapshotType);
272     snapshot->Serialize(&outputStream, v8::HeapSnapshot::kJSON);
273
274     //indicate completion
275     QByteArray data;
276     QDataStream ds(&data, QIODevice::WriteOnly);
277     ds << (int)QV8ProfilerService::V8SnapshotComplete;
278
279     q->sendMessage(data);
280 }
281
282 void QV8ProfilerServicePrivate::sendMessages()
283 {
284     Q_Q(QV8ProfilerService);
285
286     QList<QByteArray> messages;
287     for (int i = 0; i < m_data.count(); ++i)
288         messages << m_data.at(i).toByteArray();
289     q->sendMessages(messages);
290     m_data.clear();
291
292     //indicate completion
293     QByteArray data;
294     QDataStream ds(&data, QIODevice::WriteOnly);
295     ds << (int)QV8ProfilerService::V8Complete;
296
297     q->sendMessage(data);
298 }
299
300
301 QT_END_NAMESPACE