diff options
author | Christiaan Janssen <[email protected]> | 2011-09-27 14:38:22 +0200 |
---|---|---|
committer | Christiaan Janssen <[email protected]> | 2011-10-07 17:19:50 +0200 |
commit | a26259997c966b4fcc8013589ebbfdc313323520 (patch) | |
tree | 24b3b18b565302ad7eace7c5f4c3b11275cdd822 | |
parent | 9057d87805abfa5dc412f53c2a9a12704ff7aab9 (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.pri | 6 | ||||
-rw-r--r-- | src/libs/qmljsdebugclient/qmlprofilereventlist.cpp | 274 | ||||
-rw-r--r-- | src/libs/qmljsdebugclient/qmlprofilereventlist.h | 18 | ||||
-rw-r--r-- | src/libs/qmljsdebugclient/qv8profilerclient.cpp | 138 | ||||
-rw-r--r-- | src/libs/qmljsdebugclient/qv8profilerclient.h | 87 | ||||
-rw-r--r-- | src/plugins/qmlprofiler/qmlprofilereventview.cpp | 96 | ||||
-rw-r--r-- | src/plugins/qmlprofiler/qmlprofilereventview.h | 3 | ||||
-rw-r--r-- | src/plugins/qmlprofiler/qmlprofilertool.cpp | 11 | ||||
-rw-r--r-- | src/plugins/qmlprofiler/tracewindow.cpp | 42 | ||||
-rw-r--r-- | src/plugins/qmlprofiler/tracewindow.h | 11 | ||||
-rw-r--r-- | src/tools/qmlprofilertool/qmlprofilerapplication.cpp | 27 | ||||
-rw-r--r-- | src/tools/qmlprofilertool/qmlprofilerapplication.h | 11 |
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 |