aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristiaan Janssen <[email protected]>2011-09-27 14:38:22 +0200
committerChristiaan Janssen <[email protected]>2011-10-07 17:19:50 +0200
commita26259997c966b4fcc8013589ebbfdc313323520 (patch)
tree24b3b18b565302ad7eace7c5f4c3b11275cdd822
parent9057d87805abfa5dc412f53c2a9a12704ff7aab9 (diff)
QmlProfiler: V8 profiling
Change-Id: I926c5821d31453064f5dbed2b5a10f6195761f42 Reviewed-on: http://codereview.qt-project.org/5892 Reviewed-by: Kai Koehne <[email protected]> Sanity-Review: Qt Sanity Bot <[email protected]>
-rw-r--r--src/libs/qmljsdebugclient/qmljsdebugclient-lib.pri6
-rw-r--r--src/libs/qmljsdebugclient/qmlprofilereventlist.cpp274
-rw-r--r--src/libs/qmljsdebugclient/qmlprofilereventlist.h18
-rw-r--r--src/libs/qmljsdebugclient/qv8profilerclient.cpp138
-rw-r--r--src/libs/qmljsdebugclient/qv8profilerclient.h87
-rw-r--r--src/plugins/qmlprofiler/qmlprofilereventview.cpp96
-rw-r--r--src/plugins/qmlprofiler/qmlprofilereventview.h3
-rw-r--r--src/plugins/qmlprofiler/qmlprofilertool.cpp11
-rw-r--r--src/plugins/qmlprofiler/tracewindow.cpp42
-rw-r--r--src/plugins/qmlprofiler/tracewindow.h11
-rw-r--r--src/tools/qmlprofilertool/qmlprofilerapplication.cpp27
-rw-r--r--src/tools/qmlprofilertool/qmlprofilerapplication.h11
12 files changed, 682 insertions, 42 deletions
diff --git a/src/libs/qmljsdebugclient/qmljsdebugclient-lib.pri b/src/libs/qmljsdebugclient/qmljsdebugclient-lib.pri
index 0f74211e9dc..0a4391263ad 100644
--- a/src/libs/qmljsdebugclient/qmljsdebugclient-lib.pri
+++ b/src/libs/qmljsdebugclient/qmljsdebugclient-lib.pri
@@ -13,14 +13,16 @@ HEADERS += \
$$PWD/qmljsdebugclient_global.h \
$$PWD/qmlprofilertraceclient.h \
$$PWD/qmlprofilereventtypes.h \
- $$PWD/qmlprofilereventlist.h
+ $$PWD/qmlprofilereventlist.h \
+ $$PWD/qv8profilerclient.h
SOURCES += \
$$PWD/qdeclarativeenginedebug.cpp \
$$PWD/qpacketprotocol.cpp \
$$PWD/qdeclarativedebugclient.cpp \
$$PWD/qmlprofilertraceclient.cpp \
- $$PWD/qmlprofilereventlist.cpp
+ $$PWD/qmlprofilereventlist.cpp \
+ $$PWD/qv8profilerclient.cpp
OTHER_FILES += \
$$PWD/qmljsdebugclient.pri \
diff --git a/src/libs/qmljsdebugclient/qmlprofilereventlist.cpp b/src/libs/qmljsdebugclient/qmlprofilereventlist.cpp
index 51c6fc93b9f..aafffc92ef9 100644
--- a/src/libs/qmljsdebugclient/qmlprofilereventlist.cpp
+++ b/src/libs/qmljsdebugclient/qmlprofilereventlist.cpp
@@ -169,6 +169,10 @@ public:
QList<QmlEventEndTimeData> m_endTimeSortedList;
QList<QmlEventStartTimeData> m_startTimeSortedList;
+ void collectV8Statistics();
+ QV8EventDescriptions m_v8EventList;
+ QHash<int, QV8EventData *> m_v8parents;
+
// file to load
QString m_filename;
ParsingStatus m_parsingStatus;
@@ -198,6 +202,11 @@ void QmlProfilerEventList::clear()
d->m_endTimeSortedList.clear();
d->m_startTimeSortedList.clear();
+
+ qDeleteAll(d->m_v8EventList);
+ d->m_v8EventList.clear();
+ d->m_v8parents.clear();
+
emit countChanged();
}
@@ -206,6 +215,11 @@ QList <QmlEventData *> QmlProfilerEventList::getEventDescriptions() const
return d->m_eventDescriptions.values();
}
+const QV8EventDescriptions& QmlProfilerEventList::getV8Events() const
+{
+ return d->m_v8EventList;
+}
+
void QmlProfilerEventList::addRangedEvent(int type, qint64 startTime, qint64 length,
const QStringList &data, const QString &fileName, int line)
{
@@ -269,8 +283,71 @@ void QmlProfilerEventList::addRangedEvent(int type, qint64 startTime, qint64 len
emit countChanged();
}
+void QmlProfilerEventList::addV8Event(int depth, const QString &function, const QString &filename, int lineNumber, double totalTime, double selfTime)
+{
+ QString displayName = filename.mid(filename.lastIndexOf(QLatin1Char('/')) + 1) + QLatin1Char(':') + QString::number(lineNumber);
+ QV8EventData *newData = 0;
+
+ // time is given in milliseconds, but internally we store it in microseconds
+ totalTime *= 1e6;
+ selfTime *= 1e6;
+
+ // cumulate information
+ foreach (QV8EventData *v8event, d->m_v8EventList) {
+ if (v8event->displayName == displayName && v8event->functionName == function)
+ newData = v8event;
+ }
+
+ if (!newData) {
+ newData = new QV8EventData();
+ newData->displayName = displayName;
+ newData->filename = filename;
+ newData->functionName = function;
+ newData->line = lineNumber;
+ newData->totalTime = totalTime;
+ newData->selfTime = selfTime;
+ d->m_v8EventList << newData;
+ } else {
+ newData->totalTime += totalTime;
+ newData->selfTime += selfTime;
+ }
+ d->m_v8parents[depth] = newData;
+
+ if (depth > 0) {
+ QV8EventData* parentEvent = d->m_v8parents.value(depth-1);
+ if (parentEvent) {
+ if (!newData->parentList.contains(parentEvent))
+ newData->parentList << parentEvent;
+ if (!parentEvent->childrenList.contains(newData))
+ parentEvent->childrenList << newData;
+ }
+ }
+}
+
+void QmlProfilerEventList::QmlProfilerEventListPrivate::collectV8Statistics()
+{
+ double totalTimes = 0;
+ double selfTimes = 0;
+ foreach (QV8EventData *v8event, m_v8EventList) {
+ totalTimes += v8event->totalTime;
+ selfTimes += v8event->selfTime;
+ }
+
+ // prevent divisions by 0
+ if (totalTimes == 0)
+ totalTimes = 1;
+ if (selfTimes == 0)
+ selfTimes = 1;
+
+ foreach (QV8EventData *v8event, m_v8EventList) {
+ v8event->totalPercent = v8event->totalTime * 100.0 / totalTimes;
+ v8event->selfPercent = v8event->selfTime * 100.0 / selfTimes;
+ }
+}
+
void QmlProfilerEventList::complete()
{
+ d->collectV8Statistics();
postProcess();
}
@@ -699,6 +776,31 @@ void QmlProfilerEventList::save(const QString &filename)
}
stream.writeEndElement(); // eventList
+ stream.writeStartElement("v8profile"); // v8 profiler output
+ foreach (QV8EventData *v8event, d->m_v8EventList) {
+ stream.writeStartElement("event");
+ stream.writeAttribute("index", QString::number(d->m_v8EventList.indexOf(v8event)));
+ stream.writeTextElement("displayname", v8event->displayName);
+ stream.writeTextElement("functionname", v8event->functionName);
+ if (!v8event->filename.isEmpty()) {
+ stream.writeTextElement("filename", v8event->filename);
+ stream.writeTextElement("line", QString::number(v8event->line));
+ }
+ stream.writeTextElement("totalTime", QString::number(v8event->totalTime));
+ stream.writeTextElement("selfTime", QString::number(v8event->selfTime));
+ if (!v8event->childrenList.isEmpty()) {
+ stream.writeStartElement("childrenEvents");
+ QStringList childrenIndexes;
+ foreach (QV8EventData *v8child, v8event->childrenList) {
+ childrenIndexes << QString::number(d->m_v8EventList.indexOf(v8child));
+ }
+ stream.writeAttribute("list", childrenIndexes.join(QString(", ")));
+ stream.writeEndElement();
+ }
+ stream.writeEndElement();
+ }
+ stream.writeEndElement(); // v8 profiler output
+
stream.writeEndElement(); // trace
stream.writeEndDocument();
@@ -733,8 +835,13 @@ void QmlProfilerEventList::load()
// erase current
clear();
+ bool readingQmlEvents = false;
+ bool readingV8Events = false;
QHash <int, QmlEventData *> descriptionBuffer;
QmlEventData *currentEvent = 0;
+ QHash <int, QV8EventData *> v8eventBuffer;
+ QHash <int, QString> childrenIndexes;
+ QV8EventData *v8event = 0;
bool startTimesAreSorted = true;
QXmlStreamReader stream(&file);
@@ -745,16 +852,14 @@ void QmlProfilerEventList::load()
switch (token) {
case QXmlStreamReader::StartDocument : continue;
case QXmlStreamReader::StartElement : {
- if (elementName == "event") {
- QXmlStreamAttributes attributes = stream.attributes();
- if (attributes.hasAttribute("index")) {
- int ndx = attributes.value("index").toString().toInt();
- if (!descriptionBuffer.value(ndx))
- descriptionBuffer[ndx] = new QmlEventData;
- currentEvent = descriptionBuffer[ndx];
- }
+ if (elementName == "eventData" && !readingV8Events) {
+ readingQmlEvents = true;
break;
}
+ if (elementName == "v8profile" && !readingQmlEvents) {
+ readingV8Events = true;
+ }
+
if (elementName == "range") {
QmlEventStartTimeData rangedEvent;
QXmlStreamAttributes attributes = stream.attributes();
@@ -783,41 +888,127 @@ void QmlProfilerEventList::load()
break;
}
- // the remaining are eventdata elements
- if (!currentEvent)
- break;
- stream.readNext();
- if (stream.tokenType() != QXmlStreamReader::Characters)
- break;
+ if (readingQmlEvents) {
+ if (elementName == "event") {
+ QXmlStreamAttributes attributes = stream.attributes();
+ if (attributes.hasAttribute("index")) {
+ int ndx = attributes.value("index").toString().toInt();
+ if (!descriptionBuffer.value(ndx))
+ descriptionBuffer[ndx] = new QmlEventData;
+ currentEvent = descriptionBuffer[ndx];
+ } else {
+ currentEvent = 0;
+ }
+ break;
+ }
- QString readData = stream.text().toString();
+ // the remaining are eventdata or v8eventdata elements
+ if (!currentEvent)
+ break;
- if (elementName == "displayname") {
- currentEvent->displayname = readData;
- break;
+ stream.readNext();
+ if (stream.tokenType() != QXmlStreamReader::Characters)
+ break;
+ QString readData = stream.text().toString();
+
+ if (elementName == "displayname") {
+ currentEvent->displayname = readData;
+ break;
+ }
+ if (elementName == "type") {
+ currentEvent->eventType = qmlEventType(readData);
+ break;
+ }
+ if (elementName == "filename") {
+ currentEvent->filename = readData;
+ break;
+ }
+ if (elementName == "line") {
+ currentEvent->line = readData.toInt();
+ break;
+ }
+ if (elementName == "details") {
+ currentEvent->details = readData;
+ break;
+ }
}
- if (elementName == "type") {
- currentEvent->eventType = qmlEventType(readData);
- break;
+
+ if (readingV8Events) {
+ if (elementName == "event") {
+ QXmlStreamAttributes attributes = stream.attributes();
+ if (attributes.hasAttribute("index")) {
+ int ndx = attributes.value("index").toString().toInt();
+ if (!v8eventBuffer.value(ndx))
+ v8eventBuffer[ndx] = new QV8EventData;
+ v8event = v8eventBuffer[ndx];
+ } else {
+ v8event = 0;
+ }
+ break;
+ }
+
+ // the remaining are eventdata or v8eventdata elements
+ if (!v8event)
+ break;
+
+ if (elementName == "childrenEvents") {
+ QXmlStreamAttributes attributes = stream.attributes();
+ if (attributes.hasAttribute("list")) {
+ // store for later parsing (we haven't read all the events yet)
+ childrenIndexes[v8eventBuffer.key(v8event)] = attributes.value("list").toString();
+ }
+ }
+
+ stream.readNext();
+ if (stream.tokenType() != QXmlStreamReader::Characters)
+ break;
+ QString readData = stream.text().toString();
+
+ if (elementName == "displayname") {
+ v8event->displayName = readData;
+ break;
+ }
+
+ if (elementName == "functionname") {
+ v8event->functionName = readData;
+ break;
+ }
+
+ if (elementName == "filename") {
+ v8event->filename = readData;
+ break;
+ }
+
+ if (elementName == "line") {
+ v8event->line = readData.toInt();
+ break;
+ }
+
+ if (elementName == "totalTime") {
+ v8event->totalTime = readData.toDouble();
+ break;
+ }
+
+ if (elementName == "selfTime") {
+ v8event->selfTime = readData.toDouble();
+ break;
+ }
}
- if (elementName == "filename") {
- currentEvent->filename = readData;
+
+ break;
+ }
+ case QXmlStreamReader::EndElement : {
+ if (elementName == "event") {
+ currentEvent = 0;
break;
}
- if (elementName == "line") {
- currentEvent->line = readData.toInt();
+ if (elementName == "eventData") {
+ readingQmlEvents = false;
break;
}
- if (elementName == "details") {
- currentEvent->details = readData;
- break;
+ if (elementName == "v8profile") {
+ readingV8Events = false;
}
- break;
- }
- case QXmlStreamReader::EndElement : {
- if (elementName == "event")
- currentEvent = 0;
- break;
}
default: break;
}
@@ -850,12 +1041,27 @@ void QmlProfilerEventList::load()
qSort(d->m_endTimeSortedList.begin(), d->m_endTimeSortedList.end(), compareStartIndexes);
}
+ // find v8events' children and parents
+ foreach (int parentIndex, childrenIndexes.keys()) {
+ QStringList childrenStrings = childrenIndexes.value(parentIndex).split((","));
+ foreach (const QString &childString, childrenStrings) {
+ int childIndex = childString.toInt();
+ if (v8eventBuffer.value(childIndex)) {
+ v8eventBuffer[parentIndex]->childrenList << v8eventBuffer[childIndex];
+ v8eventBuffer[childIndex]->parentList << v8eventBuffer[parentIndex];
+ }
+ }
+ }
+ // store v8 events
+ d->m_v8EventList = v8eventBuffer.values();
+
emit countChanged();
setParsingStatus(SortingEndsStatus);
descriptionBuffer.clear();
+ d->collectV8Statistics();
postProcess();
}
diff --git a/src/libs/qmljsdebugclient/qmlprofilereventlist.h b/src/libs/qmljsdebugclient/qmlprofilereventlist.h
index 7d8e50760bf..dcd89c82c7a 100644
--- a/src/libs/qmljsdebugclient/qmlprofilereventlist.h
+++ b/src/libs/qmljsdebugclient/qmlprofilereventlist.h
@@ -60,8 +60,23 @@ struct QMLJSDEBUGCLIENT_EXPORT QmlEventData
qint64 medianTime;
};
+struct QMLJSDEBUGCLIENT_EXPORT QV8EventData
+{
+ QString displayName;
+ QString filename;
+ QString functionName;
+ int line;
+ double totalTime; // given in milliseconds
+ double totalPercent;
+ double selfTime;
+ double selfPercent;
+ QList< QV8EventData *> parentList;
+ QList< QV8EventData *> childrenList;
+};
+
typedef QHash<QString, QmlEventData *> QmlEventHash;
typedef QList<QmlEventData *> QmlEventDescriptions;
+typedef QList<QV8EventData *> QV8EventDescriptions;
enum ParsingStatus {
GettingDataStatus = 0,
@@ -81,6 +96,7 @@ public:
~QmlProfilerEventList();
QmlEventDescriptions getEventDescriptions() const;
+ const QV8EventDescriptions& getV8Events() const;
int findFirstIndex(qint64 startTime) const;
int findLastIndex(qint64 endTime) const;
@@ -114,6 +130,8 @@ public slots:
void addRangedEvent(int type, qint64 startTime, qint64 length,
const QStringList &data, const QString &fileName, int line);
void complete();
+
+ void addV8Event(int depth,const QString &function,const QString &filename, int lineNumber, double totalTime, double selfTime);
void save(const QString &filename);
void load(const QString &filename);
void setFilename(const QString &filename);
diff --git a/src/libs/qmljsdebugclient/qv8profilerclient.cpp b/src/libs/qmljsdebugclient/qv8profilerclient.cpp
new file mode 100644
index 00000000000..142712cb1b1
--- /dev/null
+++ b/src/libs/qmljsdebugclient/qv8profilerclient.cpp
@@ -0,0 +1,138 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation ([email protected])
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at [email protected].
+**
+**************************************************************************/
+
+#include "qv8profilerclient.h"
+
+namespace QmlJsDebugClient {
+
+class QV8ProfilerClientPrivate {
+public:
+ QV8ProfilerClientPrivate(QV8ProfilerClient *_q)
+ : q(_q)
+ , recording(false)
+ {
+ }
+
+ void sendRecordingStatus();
+
+ QV8ProfilerClient *q;
+ bool recording;
+};
+
+} // namespace QmlJsDebugClient
+
+using namespace QmlJsDebugClient;
+
+void QV8ProfilerClientPrivate::sendRecordingStatus()
+{
+ QByteArray ba;
+ QDataStream stream(&ba, QIODevice::WriteOnly);
+ QByteArray cmd("V8PROFILER");
+ QByteArray option("");
+ QByteArray title("");
+
+ if (recording) {
+ option = "start";
+ } else {
+ option = "stop";
+ }
+ stream << cmd << option << title;
+ q->sendMessage(ba);
+}
+
+QV8ProfilerClient::QV8ProfilerClient(QDeclarativeDebugConnection *client)
+ : QDeclarativeDebugClient(QLatin1String("V8Profiler"), client)
+ , d(new QV8ProfilerClientPrivate(this))
+{
+}
+
+QV8ProfilerClient::~QV8ProfilerClient()
+{
+ delete d;
+}
+
+void QV8ProfilerClient::clearData()
+{
+ emit cleared();
+}
+
+bool QV8ProfilerClient::isRecording() const
+{
+ return d->recording;
+}
+
+void QV8ProfilerClient::setRecording(bool v)
+{
+ if (v == d->recording)
+ return;
+
+ d->recording = v;
+
+ if (status() == Enabled) {
+ d->sendRecordingStatus();
+ }
+
+ emit recordingChanged(v);
+}
+
+void QV8ProfilerClient::statusChanged(Status status)
+{
+ if (status == Enabled) {
+ d->sendRecordingStatus();
+ emit enabled();
+ }
+}
+
+void QV8ProfilerClient::messageReceived(const QByteArray &data)
+{
+ QByteArray rwData = data;
+ QDataStream stream(&rwData, QIODevice::ReadOnly);
+
+ int messageType;
+
+ stream >> messageType;
+
+ if (messageType == V8Complete) {
+ emit complete();
+ } else if (messageType == V8Entry) {
+ QString filename;
+ QString function;
+ int lineNumber;
+ double totalTime;
+ double selfTime;
+ int depth;
+
+ stream >> filename >> function >> lineNumber >> totalTime >> selfTime >> depth;
+ emit this->v8range(depth, function, filename, lineNumber, totalTime, selfTime);
+ }
+}
+
diff --git a/src/libs/qmljsdebugclient/qv8profilerclient.h b/src/libs/qmljsdebugclient/qv8profilerclient.h
new file mode 100644
index 00000000000..a06b971597f
--- /dev/null
+++ b/src/libs/qmljsdebugclient/qv8profilerclient.h
@@ -0,0 +1,87 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation ([email protected])
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at [email protected].
+**
+**************************************************************************/
+
+#ifndef QV8PROFILERCLIENT_H
+#define QV8PROFILERCLIENT_H
+
+#include "qdeclarativedebugclient.h"
+#include "qmlprofilereventtypes.h"
+#include "qmljsdebugclient_global.h"
+
+#include <QtCore/QStack>
+#include <QtCore/QStringList>
+
+namespace QmlJsDebugClient {
+
+class QMLJSDEBUGCLIENT_EXPORT QV8ProfilerClient : public QDeclarativeDebugClient
+{
+ Q_OBJECT
+ Q_PROPERTY(bool recording READ isRecording WRITE setRecording NOTIFY recordingChanged)
+
+public:
+ enum Message {
+ V8Entry,
+ V8Complete,
+
+ V8MaximumMessage
+ };
+
+ QV8ProfilerClient(QDeclarativeDebugConnection *client);
+ ~QV8ProfilerClient();
+
+ bool isRecording() const;
+
+public slots:
+ void setRecording(bool);
+ void clearData();
+
+signals:
+ void complete();
+ void v8range(int depth, const QString &function, const QString &filename,
+ int lineNumber, double totalTime, double selfTime);
+
+ void recordingChanged(bool arg);
+
+ void enabled();
+ void cleared();
+
+protected:
+ virtual void statusChanged(Status);
+ virtual void messageReceived(const QByteArray &);
+
+private:
+ class QV8ProfilerClientPrivate *d;
+};
+
+} // namespace QmlJsDebugClient
+
+#endif // QV8PROFILERCLIENT_H
diff --git a/src/plugins/qmlprofiler/qmlprofilereventview.cpp b/src/plugins/qmlprofiler/qmlprofilereventview.cpp
index a9ee3a1a63f..b13a4af71b4 100644
--- a/src/plugins/qmlprofiler/qmlprofilereventview.cpp
+++ b/src/plugins/qmlprofiler/qmlprofilereventview.cpp
@@ -83,6 +83,7 @@ public:
QmlProfilerEventsViewPrivate(QmlProfilerEventsView *qq) : q(qq) {}
void buildModelFromList(const QmlEventDescriptions &list, QStandardItem *parentItem, const QmlEventDescriptions &visitedFunctionsList = QmlEventDescriptions() );
+ void buildV8ModelFromList( const QV8EventDescriptions &list );
int getFieldCount();
QString displayTime(double time) const;
QString nameForType(int typeNumber) const;
@@ -92,6 +93,7 @@ public:
QmlProfilerEventsView *q;
+ QmlProfilerEventsView::ViewTypes m_viewType;
QmlProfilerEventList *m_eventStatistics;
QStandardItemModel *m_model;
QList<bool> m_fieldShown;
@@ -137,7 +139,8 @@ void QmlProfilerEventsView::setEventStatisticsModel( QmlProfilerEventList *model
if (d->m_eventStatistics)
disconnect(d->m_eventStatistics,SIGNAL(dataReady()),this,SLOT(buildModel()));
d->m_eventStatistics = model;
- connect(d->m_eventStatistics,SIGNAL(dataReady()),this,SLOT(buildModel()));
+ if (model)
+ connect(d->m_eventStatistics,SIGNAL(dataReady()),this,SLOT(buildModel()));
}
void QmlProfilerEventsView::setFieldViewable(Fields field, bool show)
@@ -154,6 +157,7 @@ void QmlProfilerEventsView::setFieldViewable(Fields field, bool show)
void QmlProfilerEventsView::setViewType(ViewTypes type)
{
+ d->m_viewType = type;
switch (type) {
case EventsView: {
setObjectName("QmlProfilerEventsView");
@@ -161,15 +165,18 @@ void QmlProfilerEventsView::setViewType(ViewTypes type)
setFieldViewable(Type, true);
setFieldViewable(Percent, true);
setFieldViewable(TotalDuration, true);
+ setFieldViewable(SelfPercent, false);
+ setFieldViewable(SelfDuration, false);
setFieldViewable(CallCount, true);
setFieldViewable(TimePerCall, true);
setFieldViewable(MaxTime, true);
setFieldViewable(MinTime, true);
setFieldViewable(MedianTime, true);
- setFieldViewable(Details, false);
+ setFieldViewable(Details, true);
setFieldViewable(Parents, false);
setFieldViewable(Children, false);
setShowAnonymousEvents(false);
+ setToolTip(QString());
break;
}
case CallersView: {
@@ -178,6 +185,8 @@ void QmlProfilerEventsView::setViewType(ViewTypes type)
setFieldViewable(Type, true);
setFieldViewable(Percent, false);
setFieldViewable(TotalDuration, false);
+ setFieldViewable(SelfPercent, false);
+ setFieldViewable(SelfDuration, false);
setFieldViewable(CallCount, false);
setFieldViewable(TimePerCall, false);
setFieldViewable(MaxTime, false);
@@ -187,6 +196,7 @@ void QmlProfilerEventsView::setViewType(ViewTypes type)
setFieldViewable(Parents, true);
setFieldViewable(Children, false);
setShowAnonymousEvents(true);
+ setToolTip(QString());
break;
}
case CalleesView: {
@@ -195,6 +205,8 @@ void QmlProfilerEventsView::setViewType(ViewTypes type)
setFieldViewable(Type, true);
setFieldViewable(Percent, false);
setFieldViewable(TotalDuration, false);
+ setFieldViewable(SelfPercent, false);
+ setFieldViewable(SelfDuration, false);
setFieldViewable(CallCount, false);
setFieldViewable(TimePerCall, false);
setFieldViewable(MaxTime, false);
@@ -204,6 +216,27 @@ void QmlProfilerEventsView::setViewType(ViewTypes type)
setFieldViewable(Parents, false);
setFieldViewable(Children, true);
setShowAnonymousEvents(true);
+ setToolTip(QString());
+ break;
+ }
+ case V8ProfileView: {
+ setObjectName("QmlProfilerV8ProfileView");
+ setFieldViewable(Name, true);
+ setFieldViewable(Type, false);
+ setFieldViewable(Percent, true);
+ setFieldViewable(TotalDuration, true);
+ setFieldViewable(SelfPercent, true);
+ setFieldViewable(SelfDuration, true);
+ setFieldViewable(CallCount, false);
+ setFieldViewable(TimePerCall, false);
+ setFieldViewable(MaxTime, false);
+ setFieldViewable(MinTime, false);
+ setFieldViewable(MedianTime, false);
+ setFieldViewable(Details, true);
+ setFieldViewable(Parents, false);
+ setFieldViewable(Children, false);
+ setShowAnonymousEvents(true);
+ setToolTip(tr("Trace information from the v8 JavaScript engine. Available only in Qt5 based applications"));
break;
}
default: break;
@@ -234,6 +267,10 @@ void QmlProfilerEventsView::setHeaderLabels()
d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Time in Percent")));
if (d->m_fieldShown[TotalDuration])
d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Total Time")));
+ if (d->m_fieldShown[SelfPercent])
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Self Time in Percent")));
+ if (d->m_fieldShown[SelfDuration])
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Self Time")));
if (d->m_fieldShown[CallCount])
d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Calls")));
if (d->m_fieldShown[TimePerCall])
@@ -270,7 +307,10 @@ void QmlProfilerEventsView::buildModel()
{
if (d->m_eventStatistics) {
clear();
- d->buildModelFromList( d->m_eventStatistics->getEventDescriptions(), d->m_model->invisibleRootItem() );
+ if (d->m_viewType == V8ProfileView)
+ d->buildV8ModelFromList( d->m_eventStatistics->getV8Events() );
+ else
+ d->buildModelFromList( d->m_eventStatistics->getEventDescriptions(), d->m_model->invisibleRootItem() );
bool hasBranches = d->m_fieldShown[Parents] || d->m_fieldShown[Children];
setRootIsDecorated(hasBranches);
@@ -381,6 +421,56 @@ void QmlProfilerEventsView::QmlProfilerEventsViewPrivate::buildModelFromList( co
}
}
+void QmlProfilerEventsView::QmlProfilerEventsViewPrivate::buildV8ModelFromList(const QV8EventDescriptions &list)
+{
+ foreach (QV8EventData *v8event, list) {
+
+ QList<QStandardItem *> newRow;
+
+ if (m_fieldShown[Name]) {
+ newRow << new EventsViewItem(v8event->displayName);
+ }
+
+ if (m_fieldShown[Percent]) {
+ newRow << new EventsViewItem(QString::number(v8event->totalPercent,'f',2)+QLatin1String(" %"));
+ newRow.last()->setData(QVariant(v8event->totalPercent));
+ }
+
+ if (m_fieldShown[TotalDuration]) {
+ newRow << new EventsViewItem(displayTime(v8event->totalTime));
+ newRow.last()->setData(QVariant(v8event->totalTime));
+ }
+
+ if (m_fieldShown[SelfPercent]) {
+ newRow << new EventsViewItem(QString::number(v8event->selfPercent,'f',2)+QLatin1String(" %"));
+ newRow.last()->setData(QVariant(v8event->selfPercent));
+ }
+
+ if (m_fieldShown[SelfDuration]) {
+ newRow << new EventsViewItem(displayTime(v8event->selfTime));
+ newRow.last()->setData(QVariant(v8event->selfTime));
+ }
+
+ if (m_fieldShown[Details]) {
+ newRow << new EventsViewItem(v8event->functionName);
+ newRow.last()->setData(QVariant(v8event->functionName));
+ }
+
+ if (!newRow.isEmpty()) {
+ // no edit
+ foreach (QStandardItem *item, newRow)
+ item->setEditable(false);
+
+ // metadata
+ newRow.at(0)->setData(QVariant(v8event->filename),FilenameRole);
+ newRow.at(0)->setData(QVariant(v8event->line),LineRole);
+
+ // append
+ m_model->invisibleRootItem()->appendRow(newRow);
+ }
+ }
+}
+
QString QmlProfilerEventsView::QmlProfilerEventsViewPrivate::displayTime(double time) const
{
if (time < 1e6)
diff --git a/src/plugins/qmlprofiler/qmlprofilereventview.h b/src/plugins/qmlprofiler/qmlprofilereventview.h
index 7a7e16f3318..52ed975d559 100644
--- a/src/plugins/qmlprofiler/qmlprofilereventview.h
+++ b/src/plugins/qmlprofiler/qmlprofilereventview.h
@@ -58,6 +58,8 @@ public:
Type,
Percent,
TotalDuration,
+ SelfPercent,
+ SelfDuration,
CallCount,
TimePerCall,
MaxTime,
@@ -74,6 +76,7 @@ public:
EventsView,
CallersView,
CalleesView,
+ V8ProfileView,
MaxViewTypes
};
diff --git a/src/plugins/qmlprofiler/qmlprofilertool.cpp b/src/plugins/qmlprofiler/qmlprofilertool.cpp
index 1c2dfe66409..32b8690c9b8 100644
--- a/src/plugins/qmlprofiler/qmlprofilertool.cpp
+++ b/src/plugins/qmlprofiler/qmlprofilertool.cpp
@@ -102,6 +102,7 @@ public:
QmlProfilerEventsView *m_eventsView;
QmlProfilerEventsView *m_calleeView;
QmlProfilerEventsView *m_callerView;
+ QmlProfilerEventsView *m_v8profilerView;
Project *m_project;
Utils::FileInProjectFinder m_projectFinder;
RunConfiguration *m_runConfiguration;
@@ -305,6 +306,11 @@ QWidget *QmlProfilerTool::createWidgets()
this, SLOT(gotoSourceLocation(QString,int)));
connect(d->m_callerView, SIGNAL(contextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint)));
+ d->m_v8profilerView = new QmlProfilerEventsView(mw, d->m_traceWindow->getEventList());
+ d->m_v8profilerView->setViewType(QmlProfilerEventsView::V8ProfileView);
+ connect(d->m_v8profilerView, SIGNAL(gotoSourceLocation(QString,int)), this, SLOT(gotoSourceLocation(QString,int)));
+ connect(d->m_v8profilerView, SIGNAL(contextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint)));
+
QDockWidget *eventsDock = AnalyzerManager::createDockWidget
(this, tr("Events"), d->m_eventsView, Qt::BottomDockWidgetArea);
QDockWidget *timelineDock = AnalyzerManager::createDockWidget
@@ -313,16 +319,20 @@ QWidget *QmlProfilerTool::createWidgets()
(this, tr("Callees"), d->m_calleeView, Qt::BottomDockWidgetArea);
QDockWidget *callerDock = AnalyzerManager::createDockWidget
(this, tr("Callers"), d->m_callerView, Qt::BottomDockWidgetArea);
+ QDockWidget *v8profilerDock = AnalyzerManager::createDockWidget
+ (this, tr("JavaScript"), d->m_v8profilerView, Qt::BottomDockWidgetArea);
eventsDock->show();
timelineDock->show();
calleeDock->show();
callerDock->show();
+ v8profilerDock->show();
mw->splitDockWidget(mw->toolBarDockWidget(), eventsDock, Qt::Vertical);
mw->tabifyDockWidget(eventsDock, timelineDock);
mw->tabifyDockWidget(timelineDock, calleeDock);
mw->tabifyDockWidget(calleeDock, callerDock);
+ mw->tabifyDockWidget(callerDock, v8profilerDock);
//
// Toolbar
@@ -484,6 +494,7 @@ void QmlProfilerTool::clearDisplay()
d->m_eventsView->clear();
d->m_calleeView->clear();
d->m_callerView->clear();
+ d->m_v8profilerView->clear();
}
static void startRemoteTool(IAnalyzerTool *tool, StartMode mode)
diff --git a/src/plugins/qmlprofiler/tracewindow.cpp b/src/plugins/qmlprofiler/tracewindow.cpp
index 2b3ab98e57f..d5a77a40d4d 100644
--- a/src/plugins/qmlprofiler/tracewindow.cpp
+++ b/src/plugins/qmlprofiler/tracewindow.cpp
@@ -109,6 +109,8 @@ TraceWindow::TraceWindow(QWidget *parent)
connect(this,SIGNAL(viewUpdated()), m_eventList, SLOT(complete()));
m_view->rootContext()->setContextProperty("qmlEventList", m_eventList);
+ connect(this, SIGNAL(v8range(int,QString,QString,int,double,double)), m_eventList, SLOT(addV8Event(int,QString,QString,int,double,double)));
+
// Minimum height: 5 rows of 20 pixels + scrollbar of 50 pixels + 20 pixels margin
setMinimumHeight(170);
}
@@ -116,18 +118,28 @@ TraceWindow::TraceWindow(QWidget *parent)
TraceWindow::~TraceWindow()
{
delete m_plugin.data();
+ delete m_v8plugin.data();
}
void TraceWindow::reset(QDeclarativeDebugConnection *conn)
{
if (m_plugin)
- disconnect(m_plugin.data(), SIGNAL(complete()), this, SIGNAL(viewUpdated()));
+ disconnect(m_plugin.data(), SIGNAL(complete()), this, SLOT(qmlComplete()));
delete m_plugin.data();
m_plugin = new QmlProfilerTraceClient(conn);
- connect(m_plugin.data(), SIGNAL(complete()), this, SIGNAL(viewUpdated()));
+ connect(m_plugin.data(), SIGNAL(complete()), this, SLOT(qmlComplete()));
connect(m_plugin.data(), SIGNAL(range(int,qint64,qint64,QStringList,QString,int)),
this, SIGNAL(range(int,qint64,qint64,QStringList,QString,int)));
+ if (m_v8plugin) {
+ disconnect(m_v8plugin.data(), SIGNAL(complete()), this, SLOT(v8Complete()));
+ disconnect(m_v8plugin.data(), SIGNAL(v8range(int,QString,QString,int,double,double)), this, SIGNAL(v8range(int,QString,QString,int,double,double)));
+ }
+ delete m_v8plugin.data();
+ m_v8plugin = new QV8ProfilerClient(conn);
+ connect(m_v8plugin.data(), SIGNAL(complete()), this, SLOT(v8Complete()));
+ connect(m_v8plugin.data(), SIGNAL(v8range(int,QString,QString,int,double,double)), this, SIGNAL(v8range(int,QString,QString,int,double,double)));
+
m_view->rootContext()->setContextProperty("connection", m_plugin.data());
m_view->setSource(QUrl("qrc:/qmlprofiler/MainView.qml"));
@@ -142,6 +154,9 @@ void TraceWindow::reset(QDeclarativeDebugConnection *conn)
connect(this, SIGNAL(zoomOut()), m_view->rootObject(), SLOT(zoomOut()));
connect(this, SIGNAL(internalClearDisplay()), m_view->rootObject(), SLOT(clearAll()));
+
+ m_v8DataReady = false;
+ m_qmlDataReady = false;
}
QmlProfilerEventList *TraceWindow::getEventList() const
@@ -171,6 +186,8 @@ void TraceWindow::clearDisplay()
if (m_plugin)
m_plugin.data()->clearData();
+ if (m_v8plugin)
+ m_v8plugin.data()->clearData();
emit internalClearDisplay();
}
@@ -182,8 +199,14 @@ void TraceWindow::updateToolbar()
void TraceWindow::setRecording(bool recording)
{
+ if (recording) {
+ m_v8DataReady = false;
+ m_qmlDataReady = false;
+ }
if (m_plugin)
m_plugin.data()->setRecording(recording);
+ if (m_v8plugin)
+ m_v8plugin.data()->setRecording(recording);
}
bool TraceWindow::isRecording() const
@@ -191,5 +214,20 @@ bool TraceWindow::isRecording() const
return m_plugin.data()->isRecording();
}
+void TraceWindow::qmlComplete()
+{
+ m_qmlDataReady = true;
+
+ if (!m_v8plugin || m_v8plugin.data()->status() != QDeclarativeDebugClient::Enabled || m_v8DataReady)
+ emit viewUpdated();
+}
+
+void TraceWindow::v8Complete()
+{
+ m_v8DataReady = true;
+ if (!m_plugin || m_plugin.data()->status() != QDeclarativeDebugClient::Enabled || m_qmlDataReady)
+ emit viewUpdated();
+}
+
} // namespace Internal
} // namespace QmlProfiler
diff --git a/src/plugins/qmlprofiler/tracewindow.h b/src/plugins/qmlprofiler/tracewindow.h
index c679590a73e..104b1f58d20 100644
--- a/src/plugins/qmlprofiler/tracewindow.h
+++ b/src/plugins/qmlprofiler/tracewindow.h
@@ -36,6 +36,8 @@
#include <qmljsdebugclient/qmlprofilertraceclient.h>
#include <qmljsdebugclient/qmlprofilereventlist.h>
+#include <qmljsdebugclient/qv8profilerclient.h>
+
#include <QtCore/QPointer>
#include <QtGui/QWidget>
@@ -68,12 +70,18 @@ public slots:
void clearDisplay();
void updateToolbar();
+ void qmlComplete();
+ void v8Complete();
+
signals:
void viewUpdated();
void gotoSourceLocation(const QString &fileUrl, int lineNumber);
void timeChanged(qreal newTime);
void range(int type, qint64 startTime, qint64 length, const QStringList &data, const QString &fileName, int line);
+ void v8range(int depth,const QString &function,const QString &filename,
+ int lineNumber, double totalTime, double selfTime);
+
void internalClearDisplay();
void jumpToPrev();
void jumpToNext();
@@ -88,10 +96,13 @@ private:
private:
QWeakPointer<QmlJsDebugClient::QmlProfilerTraceClient> m_plugin;
+ QWeakPointer<QmlJsDebugClient::QV8ProfilerClient> m_v8plugin;
QSize m_sizeHint;
QDeclarativeView *m_view;
QmlJsDebugClient::QmlProfilerEventList *m_eventList;
+ bool m_qmlDataReady;
+ bool m_v8DataReady;
};
} // namespace Internal
diff --git a/src/tools/qmlprofilertool/qmlprofilerapplication.cpp b/src/tools/qmlprofilertool/qmlprofilerapplication.cpp
index 305663a6499..457b85028a5 100644
--- a/src/tools/qmlprofilertool/qmlprofilerapplication.cpp
+++ b/src/tools/qmlprofilertool/qmlprofilerapplication.cpp
@@ -84,6 +84,7 @@ QmlProfilerApplication::QmlProfilerApplication(int &argc, char **argv) :
m_verbose(false),
m_quitAfterSave(false),
m_traceClient(&m_connection),
+ m_v8profilerClient(&m_connection),
m_connectionAttempts(0)
{
m_connectTimer.setInterval(1000);
@@ -96,11 +97,15 @@ QmlProfilerApplication::QmlProfilerApplication(int &argc, char **argv) :
connect(&m_traceClient, SIGNAL(enabled()), this, SLOT(traceClientEnabled()));
connect(&m_traceClient, SIGNAL(recordingChanged(bool)), this, SLOT(recordingChanged()));
connect(&m_traceClient, SIGNAL(range(int,qint64,qint64,QStringList,QString,int)), &m_eventList, SLOT(addRangedEvent(int,qint64,qint64,QStringList,QString,int)));
- connect(&m_traceClient, SIGNAL(complete()), &m_eventList, SLOT(complete()));
+ connect(&m_traceClient, SIGNAL(complete()), this, SLOT(qmlComplete()));
+
+ connect(&m_v8profilerClient, SIGNAL(v8range(int,QString,QString,int,double,double)), &m_eventList, SLOT(addV8Event(int,QString,QString,int,double,double)));
+ connect(&m_v8profilerClient, SIGNAL(v8complete()), this, SLOT(v8complete()));
connect(&m_eventList, SIGNAL(error(QString)), this, SLOT(logError(QString)));
connect(&m_eventList, SIGNAL(dataReady()), this, SLOT(traceFinished()));
connect(&m_eventList, SIGNAL(parsingStatusChanged()), this, SLOT(parsingStatusChanged()));
+ connect(this, SIGNAL(done()), &m_eventList, SLOT(complete()));
}
QmlProfilerApplication::~QmlProfilerApplication()
@@ -140,6 +145,7 @@ bool QmlProfilerApplication::parseArguments()
}
} else if (arg == QLatin1String("-fromStart")) {
m_traceClient.setRecording(true);
+ m_v8profilerClient.setRecording(true);
} else if (arg == QLatin1String("-help") || arg == QLatin1String("-h") || arg == QLatin1String("/h") || arg == QLatin1String("/?")) {
return false;
} else if (arg == QLatin1String("-verbose") || arg == QLatin1String("-v")) {
@@ -212,11 +218,16 @@ void QmlProfilerApplication::userCommand(const QString &command)
} else if (cmd == Constants::CMD_RECORD
|| cmd == Constants::CMD_RECORD2) {
m_traceClient.setRecording(!m_traceClient.isRecording());
+ m_qmlDataReady = false;
+ m_v8DataReady = false;
} else if (cmd == Constants::CMD_QUIT
|| cmd == Constants::CMD_QUIT2) {
if (m_traceClient.isRecording()) {
m_quitAfterSave = true;
+ m_qmlDataReady = false;
+ m_v8DataReady = false;
m_traceClient.setRecording(false);
+ m_v8profilerClient.setRecording(false);
} else {
quit();
}
@@ -383,3 +394,17 @@ void QmlProfilerApplication::logStatus(const QString &status)
QTextStream err(stderr);
err << status << endl;
}
+
+void QmlProfilerApplication::qmlComplete()
+{
+ m_qmlDataReady = true;
+ if (m_v8profilerClient.status() != QDeclarativeDebugClient::Enabled || m_v8DataReady)
+ emit done();
+}
+
+void QmlProfilerApplication::v8Complete()
+{
+ m_v8DataReady = true;
+ if (m_traceClient.status() != QDeclarativeDebugClient::Enabled || m_qmlDataReady)
+ emit done();
+}
diff --git a/src/tools/qmlprofilertool/qmlprofilerapplication.h b/src/tools/qmlprofilertool/qmlprofilerapplication.h
index 5bc0ff0a1ed..a33052d42fd 100644
--- a/src/tools/qmlprofilertool/qmlprofilerapplication.h
+++ b/src/tools/qmlprofilertool/qmlprofilerapplication.h
@@ -40,6 +40,7 @@
#include <qdeclarativedebugclient.h>
#include <qmlprofilertraceclient.h>
#include <qmlprofilereventlist.h>
+#include <qv8profilerclient.h>
QT_FORWARD_DECLARE_CLASS(QProcess)
@@ -54,6 +55,9 @@ public:
void printUsage();
int exec();
+signals:
+ void done();
+
public slots:
void userCommand(const QString &command);
@@ -75,6 +79,9 @@ private slots:
void logError(const QString &error);
void logStatus(const QString &status);
+ void qmlComplete();
+ void v8Complete();
+
private:
void printCommands();
QString traceFileName() const;
@@ -97,9 +104,13 @@ private:
QmlJsDebugClient::QDeclarativeDebugConnection m_connection;
QmlJsDebugClient::QmlProfilerTraceClient m_traceClient;
+ QmlJsDebugClient::QV8ProfilerClient m_v8profilerClient;
QmlJsDebugClient::QmlProfilerEventList m_eventList;
QTimer m_connectTimer;
uint m_connectionAttempts;
+
+ bool m_qmlDataReady;
+ bool m_v8DataReady;
};
#endif // QMLPROFILERAPPLICATION_H