1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQml module of the Qt Toolkit.
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.
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.
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.
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.
40 ****************************************************************************/
42 #include "qv8profilerservice_p.h"
43 #include "qqmldebugservice_p_p.h"
44 #include "private/qjsconverter_impl_p.h"
45 #include <private/qv8profiler_p.h>
47 #include <QtCore/QHash>
51 Q_GLOBAL_STATIC(QV8ProfilerService, v8ProfilerInstance)
53 class DebugServiceOutputStream : public v8::OutputStream
56 DebugServiceOutputStream()
57 : v8::OutputStream() {}
59 WriteResult WriteAsciiChunk(char *rawData, int size)
62 QDataStream ds(&data, QIODevice::WriteOnly);
63 ds << QV8ProfilerService::V8SnapshotChunk << QByteArray(rawData, size);
64 messages.append(data);
67 QList<QByteArray> messages;
70 // convert to a QByteArray that can be sent to the debug client
71 QByteArray QV8ProfilerData::toByteArray() const
74 //### using QDataStream is relatively expensive
75 QDataStream ds(&data, QIODevice::WriteOnly);
76 ds << messageType << filename << functionname << lineNumber << totalTime << selfTime << treeLevel;
81 class QV8ProfilerServicePrivate : public QQmlDebugServicePrivate
83 Q_DECLARE_PUBLIC(QV8ProfilerService)
86 QV8ProfilerServicePrivate()
91 void takeSnapshot(v8::HeapSnapshot::Type);
93 void printProfileTree(const v8::CpuProfileNode *node, int level = 0);
96 QList<QV8ProfilerData> m_data;
99 QList<QString> m_ongoing;
102 QV8ProfilerService::QV8ProfilerService(QObject *parent)
103 : QQmlDebugService(*(new QV8ProfilerServicePrivate()), QStringLiteral("V8Profiler"), 1, parent)
105 Q_D(QV8ProfilerService);
107 if (registerService() == Enabled) {
108 // ,block mode, client attached
109 while (!d->initialized)
114 QV8ProfilerService::~QV8ProfilerService()
118 QV8ProfilerService *QV8ProfilerService::instance()
120 return v8ProfilerInstance();
123 void QV8ProfilerService::initialize()
125 // just make sure that the service is properly registered
126 v8ProfilerInstance();
129 void QV8ProfilerService::stateAboutToBeChanged(QQmlDebugService::State newState)
131 Q_D(QV8ProfilerService);
133 if (state() == newState)
136 if (state() == Enabled) {
137 foreach (const QString &title, d->m_ongoing) {
138 QMetaObject::invokeMethod(this, "stopProfiling", Qt::BlockingQueuedConnection,
139 Q_ARG(QString, title));
141 QMetaObject::invokeMethod(this, "sendProfilingData", Qt::BlockingQueuedConnection);
145 void QV8ProfilerService::messageReceived(const QByteArray &message)
147 Q_D(QV8ProfilerService);
149 QDataStream ds(message);
153 ds >> command >> option;
155 if (command == "V8PROFILER") {
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);
164 d->initialized = true;
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);
175 QQmlDebugService::messageReceived(message);
178 void QV8ProfilerService::startProfiling(const QString &title)
180 Q_D(QV8ProfilerService);
183 if (d->m_ongoing.contains(title))
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);
190 d->m_ongoing.append(title);
192 // indicate profiling started
194 QDataStream ds(&data, QIODevice::WriteOnly);
195 ds << (int)QV8ProfilerService::V8Started;
200 void QV8ProfilerService::stopProfiling(const QString &title)
202 Q_D(QV8ProfilerService);
205 if (!d->m_ongoing.contains(title))
207 d->m_ongoing.removeOne(title);
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);
213 // can happen at start
214 const v8::CpuProfileNode *rootNode = cpuProfile->GetTopDownRoot();
215 d->printProfileTree(rootNode);
217 // indicate completion, even without data
219 QDataStream ds(&data, QIODevice::WriteOnly);
220 ds << (int)QV8ProfilerService::V8Complete;
226 void QV8ProfilerService::takeSnapshot()
228 Q_D(QV8ProfilerService);
229 d->takeSnapshot(v8::HeapSnapshot::kFull);
232 void QV8ProfilerService::deleteSnapshots()
234 v8::HeapProfiler::DeleteAllSnapshots();
237 void QV8ProfilerService::sendProfilingData()
239 Q_D(QV8ProfilerService);
240 // Send messages to client
244 void QV8ProfilerServicePrivate::printProfileTree(const v8::CpuProfileNode *node, int level)
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) {
251 QV8ProfilerData rd = {(int)QV8ProfilerService::V8Entry, scriptResourceName,
252 QJSConverter::toString(childNode->GetFunctionName()),
253 childNode->GetLineNumber(), childNode->GetTotalTime(), childNode->GetSelfTime(), level};
256 // different nodes might have common children: fix at client side
257 if (childNode->GetChildrenCount() > 0) {
258 printProfileTree(childNode, level+1);
264 void QV8ProfilerServicePrivate::takeSnapshot(v8::HeapSnapshot::Type snapshotType)
266 Q_Q(QV8ProfilerService);
268 v8::HandleScope scope;
269 v8::Local<v8::String> title = v8::String::New("");
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;
276 //indicate completion
278 QDataStream ds(&data, QIODevice::WriteOnly);
279 ds << (int)QV8ProfilerService::V8SnapshotComplete;
280 messages.append(data);
282 q->sendMessages(messages);
285 void QV8ProfilerServicePrivate::sendMessages()
287 Q_Q(QV8ProfilerService);
289 QList<QByteArray> messages;
290 for (int i = 0; i < m_data.count(); ++i)
291 messages.append(m_data.at(i).toByteArray());
294 //indicate completion
296 QDataStream ds(&data, QIODevice::WriteOnly);
297 ds << (int)QV8ProfilerService::V8Complete;
298 messages.append(data);
300 q->sendMessages(messages);