summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristiaan Janssen <christiaan.janssen@nokia.com>2012-02-24 10:47:17 +0100
committerChristiaan Janssen <christiaan.janssen@nokia.com>2012-03-14 11:38:25 +0100
commitb7304e2f2e533b767bcd1c02d8403e3d5fa63ddd (patch)
tree30bb48a69d7c6bd1c35bcdd359a757486f298368
parentd207165f6aa89ee95bd41c41a49d68cfa0b46444 (diff)
downloadqt-creator-b7304e2f2e533b767bcd1c02d8403e3d5fa63ddd.tar.gz
QmlProfiler: Refactor
The code of the qmlprofiler client has become a bit too complex, this patch reorganizes the modules in a more sensible way, having the modules communicate with each other through a state machine instead of the excess of signals and slots from before. Change-Id: I76f7313779888a1bd07a1cdb1acbf2e47aacf42a Reviewed-by: Kai Koehne <kai.koehne@nokia.com>
-rw-r--r--src/libs/qmljsdebugclient/qmljsdebugclient-lib.pri3
-rw-r--r--src/libs/qmljsdebugclient/qmljsdebugclient.pro3
-rw-r--r--src/libs/qmljsdebugclient/qmljsdebugclient.qbs3
-rw-r--r--src/libs/qmljsdebugclient/qmlprofilereventlist.cpp1881
-rw-r--r--src/libs/qmljsdebugclient/qmlprofilereventlocation.h2
-rw-r--r--src/libs/qmljsdebugclient/qmlprofilereventtypes.h13
-rw-r--r--src/libs/qmljsdebugclient/qmlprofilertraceclient.cpp12
-rw-r--r--src/libs/qmljsdebugclient/qmlprofilertraceclient.h6
-rw-r--r--src/libs/qmljsdebugclient/qv8profilerclient.cpp13
-rw-r--r--src/libs/qmljsdebugclient/qv8profilerclient.h8
-rw-r--r--src/plugins/qmlprofiler/qml/Label.qml18
-rw-r--r--src/plugins/qmlprofiler/qml/MainView.qml157
-rw-r--r--src/plugins/qmlprofiler/qml/Overview.js51
-rw-r--r--src/plugins/qmlprofiler/qml/Overview.qml16
-rw-r--r--src/plugins/qmlprofiler/qml/SelectionRange.qml2
-rw-r--r--src/plugins/qmlprofiler/qml/StatusDisplay.qml8
-rw-r--r--src/plugins/qmlprofiler/qml/TimeMarks.qml14
-rw-r--r--src/plugins/qmlprofiler/qmlprofiler.pro26
-rw-r--r--src/plugins/qmlprofiler/qmlprofiler.qbs23
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerclientmanager.cpp426
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerclientmanager.h102
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerconstants.h1
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerdatamodel.cpp1652
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerdatamodel.h (renamed from src/libs/qmljsdebugclient/qmlprofilereventlist.h)137
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerdetailsrewriter.h2
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerengine.cpp188
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerengine.h15
-rw-r--r--src/plugins/qmlprofiler/qmlprofilereventview.cpp302
-rw-r--r--src/plugins/qmlprofiler/qmlprofilereventview.h48
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerstatemanager.cpp163
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerstatemanager.h (renamed from src/tools/qmlprofilertool/commandlistener.h)48
-rw-r--r--src/plugins/qmlprofiler/qmlprofilertool.cpp617
-rw-r--r--src/plugins/qmlprofiler/qmlprofilertool.h52
-rw-r--r--src/plugins/qmlprofiler/qmlprofilertraceview.cpp601
-rw-r--r--src/plugins/qmlprofiler/qmlprofilertraceview.h (renamed from src/plugins/qmlprofiler/tracewindow.h)115
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerviewmanager.cpp172
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerviewmanager.h (renamed from src/tools/qmlprofilertool/main.cpp)56
-rw-r--r--src/plugins/qmlprofiler/qv8profilerdatamodel.cpp445
-rw-r--r--src/plugins/qmlprofiler/qv8profilerdatamodel.h79
-rw-r--r--src/plugins/qmlprofiler/timelinerenderer.cpp (renamed from src/plugins/qmlprofiler/timelineview.cpp)216
-rw-r--r--src/plugins/qmlprofiler/timelinerenderer.h (renamed from src/plugins/qmlprofiler/timelineview.h)28
-rw-r--r--src/plugins/qmlprofiler/tracewindow.cpp649
-rw-r--r--src/tools/qmlprofilertool/commandlistener.cpp57
-rw-r--r--src/tools/qmlprofilertool/constants.h50
-rw-r--r--src/tools/qmlprofilertool/qmlprofilerapplication.h113
-rw-r--r--src/tools/qmlprofilertool/qmlprofilertool.pro28
-rw-r--r--src/tools/tools.pro1
47 files changed, 4734 insertions, 3888 deletions
diff --git a/src/libs/qmljsdebugclient/qmljsdebugclient-lib.pri b/src/libs/qmljsdebugclient/qmljsdebugclient-lib.pri
index f15d920bf9..69b0606451 100644
--- a/src/libs/qmljsdebugclient/qmljsdebugclient-lib.pri
+++ b/src/libs/qmljsdebugclient/qmljsdebugclient-lib.pri
@@ -7,11 +7,11 @@ contains(CONFIG, dll) {
INCLUDEPATH += $$PWD/..
HEADERS += \
+ $$PWD/qmlprofilereventlocation.h \
$$PWD/qdeclarativedebugclient.h \
$$PWD/qdeclarativeenginedebug.h \
$$PWD/qdeclarativeoutputparser.h \
$$PWD/qmljsdebugclient_global.h \
- $$PWD/qmlprofilereventlist.h \
$$PWD/qmlprofilereventtypes.h \
$$PWD/qmlprofilertraceclient.h \
$$PWD/qpacketprotocol.h \
@@ -23,7 +23,6 @@ SOURCES += \
$$PWD/qdeclarativedebugclient.cpp \
$$PWD/qdeclarativeenginedebug.cpp \
$$PWD/qdeclarativeoutputparser.cpp \
- $$PWD/qmlprofilereventlist.cpp \
$$PWD/qmlprofilertraceclient.cpp \
$$PWD/qpacketprotocol.cpp \
$$PWD/qv8profilerclient.cpp \
diff --git a/src/libs/qmljsdebugclient/qmljsdebugclient.pro b/src/libs/qmljsdebugclient/qmljsdebugclient.pro
index c1462ecd5e..893ef89d48 100644
--- a/src/libs/qmljsdebugclient/qmljsdebugclient.pro
+++ b/src/libs/qmljsdebugclient/qmljsdebugclient.pro
@@ -11,6 +11,3 @@ OTHER_FILES += \
qmljsdebugclient.pri \
qmljsdebugclient-lib.pri
-HEADERS += \
- qmlprofilereventlocation.h
-
diff --git a/src/libs/qmljsdebugclient/qmljsdebugclient.qbs b/src/libs/qmljsdebugclient/qmljsdebugclient.qbs
index a975195e44..8cb51a197f 100644
--- a/src/libs/qmljsdebugclient/qmljsdebugclient.qbs
+++ b/src/libs/qmljsdebugclient/qmljsdebugclient.qbs
@@ -24,7 +24,7 @@ DynamicLibrary {
"qdeclarativeoutputparser.h",
"qmljsdebugclient_global.h",
"qmljsdebugclientconstants.h",
- "qmlprofilereventlist.h",
+ "qmlprofilereventlocation.h",
"qmlprofilertraceclient.cpp",
"qpacketprotocol.cpp",
"qv8profilerclient.cpp",
@@ -36,7 +36,6 @@ DynamicLibrary {
"qmlprofilertraceclient.h",
"qpacketprotocol.h",
"qdebugmessageclient.cpp",
- "qmlprofilereventlist.cpp",
"qdebugmessageclient.h"
]
diff --git a/src/libs/qmljsdebugclient/qmlprofilereventlist.cpp b/src/libs/qmljsdebugclient/qmlprofilereventlist.cpp
deleted file mode 100644
index 3576b94645..0000000000
--- a/src/libs/qmljsdebugclient/qmlprofilereventlist.cpp
+++ /dev/null
@@ -1,1881 +0,0 @@
-/**************************************************************************
-**
-** This file is part of Qt Creator
-**
-** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
-**
-** Contact: Nokia Corporation (qt-info@nokia.com)
-**
-**
-** 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 qt-info@nokia.com.
-**
-**************************************************************************/
-
-#include "qmlprofilereventlist.h"
-
-#include <QUrl>
-#include <QHash>
-#include <QtAlgorithms>
-#include <QString>
-#include <QStringList>
-
-#include <QFile>
-#include <QXmlStreamReader>
-#include <QXmlStreamWriter>
-
-#include <QTimer>
-#include <utils/qtcassert.h>
-
-#include <QDebug>
-
-namespace QmlJsDebugClient {
-
-namespace Constants {
-const char *const TYPE_PAINTING_STR = "Painting";
-const char *const TYPE_COMPILING_STR = "Compiling";
-const char *const TYPE_CREATING_STR = "Creating";
-const char *const TYPE_BINDING_STR = "Binding";
-const char *const TYPE_HANDLINGSIGNAL_STR = "HandlingSignal";
-const char *const PROFILER_FILE_VERSION = "1.02";
-}
-
-#define MIN_LEVEL 1
-
-QmlEventData::QmlEventData()
-{
- eventType = MaximumQmlEventType;
- eventId = -1;
- duration = 0;
- calls = 0;
- minTime = 0;
- maxTime = 0;
- timePerCall = 0;
- percentOfTime = 0;
- medianTime = 0;
- isBindingLoop = false;
-}
-
-QmlEventData::~QmlEventData()
-{
- qDeleteAll(parentHash.values());
- parentHash.clear();
- qDeleteAll(childrenHash.values());
- childrenHash.clear();
-}
-
-QmlEventData &QmlEventData::operator=(const QmlEventData &ref)
-{
- if (this == &ref)
- return *this;
-
- displayname = ref.displayname;
- location = ref.location;
- eventHashStr = ref.eventHashStr;
- details = ref.details;
- eventType = ref.eventType;
- duration = ref.duration;
- calls = ref.calls;
- minTime = ref.minTime;
- maxTime = ref.maxTime;
- timePerCall = ref.timePerCall;
- percentOfTime = ref.percentOfTime;
- medianTime = ref.medianTime;
- eventId = ref.eventId;
- isBindingLoop = ref.isBindingLoop;
-
- qDeleteAll(parentHash.values());
- parentHash.clear();
- foreach (const QString &key, ref.parentHash.keys()) {
- parentHash.insert(key, new QmlEventSub(ref.parentHash.value(key)));
- }
-
- qDeleteAll(childrenHash.values());
- childrenHash.clear();
- foreach (const QString &key, ref.childrenHash.keys()) {
- childrenHash.insert(key, new QmlEventSub(ref.childrenHash.value(key)));
- }
-
- return *this;
-}
-
-QV8EventData::QV8EventData()
-{
- line = -1;
- eventId = -1;
- totalTime = 0;
- selfTime = 0;
- totalPercent = 0;
- selfPercent = 0;
-}
-
-QV8EventData::~QV8EventData()
-{
- qDeleteAll(parentHash.values());
- parentHash.clear();
- qDeleteAll(childrenHash.values());
- childrenHash.clear();
-}
-
-QV8EventData &QV8EventData::operator=(const QV8EventData &ref)
-{
- if (this == &ref)
- return *this;
-
- displayName = ref.displayName;
- filename = ref.filename;
- functionName = ref.functionName;
- line = ref.line;
- totalTime = ref.totalTime;
- totalPercent = ref.totalPercent;
- selfTime = ref.selfTime;
- selfPercent = ref.selfPercent;
- eventId = ref.eventId;
-
- qDeleteAll(parentHash.values());
- parentHash.clear();
- foreach (const QString &key, ref.parentHash.keys()) {
- parentHash.insert(key, new QV8EventSub(ref.parentHash.value(key)));
- }
-
- qDeleteAll(childrenHash.values());
- childrenHash.clear();
- foreach (const QString &key, ref.childrenHash.keys()) {
- childrenHash.insert(key, new QV8EventSub(ref.childrenHash.value(key)));
- }
- return *this;
-}
-
-// endtimedata
-struct QmlEventEndTimeData {
- qint64 endTime;
- int startTimeIndex;
- QmlEventData *description;
-};
-
-// starttimedata
-struct QmlEventStartTimeData {
- qint64 startTime;
- qint64 length;
- qint64 level;
- int endTimeIndex;
- qint64 nestingLevel;
- qint64 nestingDepth;
- QmlEventData *description;
-
- // animation-related data
- int frameRate;
- int animationCount;
-
- int bindingLoopHead;
-};
-
-struct QmlEventTypeCount {
- QList <int> eventIds;
- int nestingCount;
-};
-
-// used by quicksort
-bool compareEndTimes(const QmlEventEndTimeData &t1, const QmlEventEndTimeData &t2)
-{
- return t1.endTime < t2.endTime;
-}
-
-bool compareStartTimes(const QmlEventStartTimeData &t1, const QmlEventStartTimeData &t2)
-{
- return t1.startTime < t2.startTime;
-}
-
-bool compareStartIndexes(const QmlEventEndTimeData &t1, const QmlEventEndTimeData &t2)
-{
- return t1.startTimeIndex < t2.startTimeIndex;
-}
-
-QString qmlEventType(QmlEventType typeEnum)
-{
- switch (typeEnum) {
- case Painting:
- return QLatin1String(Constants::TYPE_PAINTING_STR);
- break;
- case Compiling:
- return QLatin1String(Constants::TYPE_COMPILING_STR);
- break;
- case Creating:
- return QLatin1String(Constants::TYPE_CREATING_STR);
- break;
- case Binding:
- return QLatin1String(Constants::TYPE_BINDING_STR);
- break;
- case HandlingSignal:
- return QLatin1String(Constants::TYPE_HANDLINGSIGNAL_STR);
- break;
- default:
- return QString::number((int)typeEnum);
- }
-}
-
-QmlEventType qmlEventType(const QString &typeString)
-{
- if (typeString == QLatin1String(Constants::TYPE_PAINTING_STR)) {
- return Painting;
- } else if (typeString == QLatin1String(Constants::TYPE_COMPILING_STR)) {
- return Compiling;
- } else if (typeString == QLatin1String(Constants::TYPE_CREATING_STR)) {
- return Creating;
- } else if (typeString == QLatin1String(Constants::TYPE_BINDING_STR)) {
- return Binding;
- } else if (typeString == QLatin1String(Constants::TYPE_HANDLINGSIGNAL_STR)) {
- return HandlingSignal;
- } else {
- bool isNumber = false;
- int type = typeString.toUInt(&isNumber);
- if (isNumber) {
- return (QmlEventType)type;
- } else {
- return MaximumQmlEventType;
- }
- }
-}
-
-QString getHashStringForQmlEvent(QmlEventLocation location, int eventType)
-{
- return QString("%1:%2:%3:%4").arg(location.filename, QString::number(location.line), QString::number(location.column), QString::number(eventType));
-}
-
-class QmlProfilerEventList::QmlProfilerEventListPrivate
-{
-public:
- QmlProfilerEventListPrivate(QmlProfilerEventList *qq) : q(qq) {}
-
- QmlProfilerEventList *q;
-
- QmlProfilerEventList::State m_state;
-
- // convenience functions
- void clearQmlRootEvent();
- void clearV8RootEvent();
-
- // Stored data
- QmlEventHash m_eventDescriptions;
- QList<QmlEventEndTimeData> m_endTimeSortedList;
- QList<QmlEventStartTimeData> m_startTimeSortedList;
-
- void collectV8Statistics();
- QV8EventDescriptions m_v8EventList;
- QHash<int, QV8EventData *> m_v8parents;
-
- QmlEventData m_qmlRootEvent;
- QV8EventData m_v8RootEvent;
- QString m_rootEventName;
- QString m_rootEventDesc;
-
- QHash<int, QmlEventTypeCount *> m_typeCounts;
-
- qint64 m_traceEndTime;
- qint64 m_traceStartTime;
- qint64 m_qmlMeasuredTime;
- qint64 m_v8MeasuredTime;
-
- QmlEventStartTimeData *m_lastFrameEvent;
- qint64 m_maximumAnimationCount;
- qint64 m_minimumAnimationCount;
-
- // file to load
- QString m_filename;
-};
-
-
-////////////////////////////////////////////////////////////////////////////////////
-
-
-QmlProfilerEventList::QmlProfilerEventList(QObject *parent) :
- QObject(parent), d(new QmlProfilerEventListPrivate(this))
-{
- setObjectName("QmlProfilerEventStatistics");
-
- d->m_state = Empty;
-
- d->m_traceEndTime = 0;
- d->m_traceStartTime = -1;
- d->m_qmlMeasuredTime = 0;
- d->m_v8MeasuredTime = 0;
- d->m_rootEventName = tr("<program>");
- d->m_rootEventDesc = tr("Main Program");
- d->clearQmlRootEvent();
- d->clearV8RootEvent();
- d->m_lastFrameEvent = 0;
- d->m_maximumAnimationCount = 0;
- d->m_minimumAnimationCount = 0;
-}
-
-QmlProfilerEventList::~QmlProfilerEventList()
-{
- clear();
-}
-
-void QmlProfilerEventList::clear()
-{
- qDeleteAll(d->m_eventDescriptions.values());
- d->m_eventDescriptions.clear();
-
- qDeleteAll(d->m_v8EventList);
- d->m_v8EventList.clear();
-
- d->m_endTimeSortedList.clear();
- d->m_startTimeSortedList.clear();
-
- d->m_v8parents.clear();
-
- d->clearQmlRootEvent();
- d->clearV8RootEvent();
-
- foreach (QmlEventTypeCount *typeCount, d->m_typeCounts.values())
- delete typeCount;
- d->m_typeCounts.clear();
-
- d->m_traceEndTime = 0;
- d->m_traceStartTime = -1;
- d->m_qmlMeasuredTime = 0;
- d->m_v8MeasuredTime = 0;
-
- d->m_lastFrameEvent = 0;
- d->m_maximumAnimationCount = 0;
- d->m_minimumAnimationCount = 0;
-
- emit countChanged();
- setState(Empty);
-}
-
-QList <QmlEventData *> QmlProfilerEventList::getEventDescriptions() const
-{
- return d->m_eventDescriptions.values();
-}
-
-QmlEventData *QmlProfilerEventList::eventDescription(int eventId) const
-{
- foreach (QmlEventData *event, d->m_eventDescriptions.values()) {
- if (event->eventId == eventId)
- return event;
- }
- return 0;
-}
-
-QV8EventData *QmlProfilerEventList::v8EventDescription(int eventId) const
-{
- foreach (QV8EventData *event, d->m_v8EventList) {
- if (event->eventId == eventId)
- return event;
- }
- return 0;
-}
-
-const QV8EventDescriptions& QmlProfilerEventList::getV8Events() const
-{
- return d->m_v8EventList;
-}
-
-void QmlProfilerEventList::addRangedEvent(int type, qint64 startTime, qint64 length,
- const QStringList &data, const QmlJsDebugClient::QmlEventLocation &location)
-{
- const QChar colon = QLatin1Char(':');
- QString displayName, eventHashStr, details;
- QmlJsDebugClient::QmlEventLocation eventLocation = location;
-
- setState(AcquiringData);
-
- // generate details string
- if (data.isEmpty())
- details = tr("Source code not available");
- else {
- details = data.join(" ").replace('\n'," ").simplified();
- QRegExp rewrite("\\(function \\$(\\w+)\\(\\) \\{ (return |)(.+) \\}\\)");
- bool match = rewrite.exactMatch(details);
- if (match) {
- details = rewrite.cap(1) + ": " + rewrite.cap(3);
- }
- if (details.startsWith(QString("file://")))
- details = details.mid(details.lastIndexOf(QChar('/')) + 1);
- }
-
- // backwards compatibility: "compiling" events don't have a proper location in older
- // version of the protocol, but the filename is passed in the details string
- if (type == QmlJsDebugClient::Compiling && eventLocation.filename.isEmpty()) {
- eventLocation.filename = details;
- eventLocation.line = 1;
- eventLocation.column = 1;
- }
-
- // generate hash
- if (eventLocation.filename.isEmpty()) {
- displayName = tr("<bytecode>");
- eventHashStr = getHashStringForQmlEvent(eventLocation, type);
- } else {
- const QString filePath = QUrl(eventLocation.filename).path();
- displayName = filePath.mid(filePath.lastIndexOf(QChar('/')) + 1) + colon + QString::number(eventLocation.line);
- eventHashStr = getHashStringForQmlEvent(eventLocation, type);
- }
-
- QmlEventData *newEvent;
- if (d->m_eventDescriptions.contains(eventHashStr)) {
- newEvent = d->m_eventDescriptions[eventHashStr];
- } else {
- newEvent = new QmlEventData;
- newEvent->displayname = displayName;
- newEvent->location = eventLocation;
- newEvent->eventHashStr = eventHashStr;
- newEvent->eventType = (QmlJsDebugClient::QmlEventType)type;
- newEvent->details = details;
- d->m_eventDescriptions.insert(eventHashStr, newEvent);
- }
-
- QmlEventEndTimeData endTimeData;
- endTimeData.endTime = startTime + length;
- endTimeData.description = newEvent;
- endTimeData.startTimeIndex = d->m_startTimeSortedList.count();
-
- QmlEventStartTimeData startTimeData;
- startTimeData.startTime = startTime;
- startTimeData.length = length;
- startTimeData.description = newEvent;
- startTimeData.endTimeIndex = d->m_endTimeSortedList.count();
- startTimeData.animationCount = -1;
- startTimeData.frameRate = 1e9/length;
-
- d->m_endTimeSortedList << endTimeData;
- d->m_startTimeSortedList << startTimeData;
-
- 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 *eventData = 0;
-
- setState(AcquiringData);
-
- // time is given in milliseconds, but internally we store it in microseconds
- totalTime *= 1e6;
- selfTime *= 1e6;
-
- // cumulate information
- // TODO: use hashes
- foreach (QV8EventData *v8event, d->m_v8EventList) {
- if (v8event->displayName == displayName && v8event->functionName == function) {
- eventData = v8event;
- break;
- }
- }
-
- if (!eventData) {
- eventData = new QV8EventData;
- eventData->displayName = displayName;
- eventData->filename = filename;
- eventData->functionName = function;
- eventData->line = lineNumber;
- eventData->totalTime = totalTime;
- eventData->selfTime = selfTime;
- d->m_v8EventList << eventData;
- } else {
- eventData->totalTime += totalTime;
- eventData->selfTime += selfTime;
- }
- d->m_v8parents[depth] = eventData;
-
- QV8EventData *parentEvent = 0;
- if (depth == 0) {
- parentEvent = &d->m_v8RootEvent;
- d->m_v8MeasuredTime += totalTime;
- }
- if (depth > 0 && d->m_v8parents.contains(depth-1)) {
- parentEvent = d->m_v8parents.value(depth-1);
- }
-
- if (parentEvent != 0) {
- if (!eventData->parentHash.contains(parentEvent->displayName)) {
- QV8EventSub *newParentSub = new QV8EventSub(parentEvent);
- newParentSub->totalTime = totalTime;
-
- eventData->parentHash.insert(parentEvent->displayName, newParentSub );
- } else {
- QV8EventSub *newParentSub = eventData->parentHash.value(parentEvent->displayName);
- newParentSub->totalTime += totalTime;
- }
-
- if (!parentEvent->childrenHash.contains(eventData->displayName)) {
- QV8EventSub *newChildSub = new QV8EventSub(eventData);
- newChildSub->totalTime = totalTime;
-
- parentEvent->childrenHash.insert(eventData->displayName, newChildSub);
- } else {
- QV8EventSub *newChildSub = parentEvent->childrenHash.value(eventData->displayName);
- newChildSub->totalTime += totalTime;
- }
- }
-}
-
-void QmlProfilerEventList::addFrameEvent(qint64 time, int framerate, int animationcount)
-{
- QString displayName, eventHashStr, details;
-
- setState(AcquiringData);
-
- details = tr("Animation Timer Update");
- displayName = tr("<Animation Update>");
- eventHashStr = displayName;
-
- QmlEventData *newEvent;
- if (d->m_eventDescriptions.contains(eventHashStr)) {
- newEvent = d->m_eventDescriptions[eventHashStr];
- } else {
- newEvent = new QmlEventData;
- newEvent->displayname = displayName;
- newEvent->eventHashStr = eventHashStr;
- newEvent->eventType = QmlJsDebugClient::Painting;
- newEvent->details = details;
- d->m_eventDescriptions.insert(eventHashStr, newEvent);
- }
-
- qint64 length = 1e9/framerate;
- // avoid overlap
- if (d->m_lastFrameEvent && d->m_lastFrameEvent->startTime + d->m_lastFrameEvent->length >= time) {
- d->m_lastFrameEvent->length = time - 1 - d->m_lastFrameEvent->startTime;
- d->m_endTimeSortedList[d->m_lastFrameEvent->endTimeIndex].endTime = d->m_lastFrameEvent->startTime + d->m_lastFrameEvent->length;
- }
-
- QmlEventEndTimeData endTimeData;
- endTimeData.endTime = time + length;
- endTimeData.description = newEvent;
- endTimeData.startTimeIndex = d->m_startTimeSortedList.count();
-
- QmlEventStartTimeData startTimeData;
- startTimeData.startTime = time;
- startTimeData.length = length;
- startTimeData.description = newEvent;
- startTimeData.endTimeIndex = d->m_endTimeSortedList.count();
- startTimeData.animationCount = animationcount;
- startTimeData.frameRate = framerate;
-
- d->m_endTimeSortedList << endTimeData;
- d->m_startTimeSortedList << startTimeData;
-
- d->m_lastFrameEvent = &d->m_startTimeSortedList.last();
-
- emit countChanged();
-}
-
-void QmlProfilerEventList::QmlProfilerEventListPrivate::collectV8Statistics()
-{
- if (!m_v8EventList.isEmpty()) {
- double totalTimes = m_v8MeasuredTime;
- double selfTimes = 0;
- foreach (QV8EventData *v8event, m_v8EventList) {
- selfTimes += v8event->selfTime;
- }
-
- // prevent divisions by 0
- if (totalTimes == 0)
- totalTimes = 1;
- if (selfTimes == 0)
- selfTimes = 1;
-
- // insert root event in eventlist
- // the +1 ns is to get it on top of the sorted list
- m_v8RootEvent.totalTime = m_v8MeasuredTime + 1;
- m_v8RootEvent.selfTime = 0;
-
- int rootEventIndex = -1;
- for (int ndx = 0; ndx < m_v8EventList.count(); ndx++)
- {
- if (m_v8EventList.at(ndx)->displayName == m_rootEventName) {
- m_v8RootEvent = *m_v8EventList.at(ndx);
- rootEventIndex = ndx;
- break;
- }
- }
- if (rootEventIndex == -1) {
- rootEventIndex = m_v8EventList.count();
- QV8EventData *newRootEvent = new QV8EventData;
- *newRootEvent = m_v8RootEvent;
- m_v8EventList << newRootEvent;
- }
-
- foreach (QV8EventData *v8event, m_v8EventList) {
- v8event->totalPercent = v8event->totalTime * 100.0 / totalTimes;
- v8event->selfPercent = v8event->selfTime * 100.0 / selfTimes;
- }
-
- int index = 0;
- foreach (QV8EventData *v8event, m_v8EventList) {
- v8event->eventId = index++;
- }
- m_v8RootEvent.eventId = m_v8EventList[rootEventIndex]->eventId;
- }
-}
-
-void QmlProfilerEventList::setTraceEndTime( qint64 time )
-{
- d->m_traceEndTime = time;
-}
-
-void QmlProfilerEventList::setTraceStartTime( qint64 time )
-{
- d->m_traceStartTime = time;
-}
-
-void QmlProfilerEventList::complete()
-{
- setState(ProcessingData);
- d->collectV8Statistics();
- postProcess();
-}
-
-void QmlProfilerEventList::QmlProfilerEventListPrivate::clearQmlRootEvent()
-{
- m_qmlRootEvent.displayname = m_rootEventName;
- m_qmlRootEvent.location = QmlEventLocation();
- m_qmlRootEvent.eventHashStr = m_rootEventName;
- m_qmlRootEvent.details = m_rootEventDesc;
- m_qmlRootEvent.eventType = QmlJsDebugClient::Binding;
- m_qmlRootEvent.duration = 0;
- m_qmlRootEvent.calls = 0;
- m_qmlRootEvent.minTime = 0;
- m_qmlRootEvent.maxTime = 0;
- m_qmlRootEvent.timePerCall = 0;
- m_qmlRootEvent.percentOfTime = 0;
- m_qmlRootEvent.medianTime = 0;
- m_qmlRootEvent.eventId = -1;
-
- qDeleteAll(m_qmlRootEvent.parentHash.values());
- qDeleteAll(m_qmlRootEvent.childrenHash.values());
- m_qmlRootEvent.parentHash.clear();
- m_qmlRootEvent.childrenHash.clear();
-}
-
-void QmlProfilerEventList::QmlProfilerEventListPrivate::clearV8RootEvent()
-{
- m_v8RootEvent.displayName = m_rootEventName;
- m_v8RootEvent.functionName = m_rootEventDesc;
- m_v8RootEvent.line = -1;
- m_v8RootEvent.totalTime = 0;
- m_v8RootEvent.totalPercent = 0;
- m_v8RootEvent.selfTime = 0;
- m_v8RootEvent.selfPercent = 0;
- m_v8RootEvent.eventId = -1;
-
- qDeleteAll(m_v8RootEvent.parentHash.values());
- qDeleteAll(m_v8RootEvent.childrenHash.values());
- m_v8RootEvent.parentHash.clear();
- m_v8RootEvent.childrenHash.clear();
-}
-
-void QmlProfilerEventList::compileStatistics(qint64 startTime, qint64 endTime)
-{
- int index;
- int fromIndex = findFirstIndex(startTime);
- int toIndex = findLastIndex(endTime);
- double totalTime = 0;
-
- // clear existing statistics
- foreach (QmlEventData *eventDescription, d->m_eventDescriptions.values()) {
- eventDescription->calls = 0;
- // maximum possible value
- eventDescription->minTime = d->m_endTimeSortedList.last().endTime;
- eventDescription->maxTime = 0;
- eventDescription->medianTime = 0;
- eventDescription->duration = 0;
- qDeleteAll(eventDescription->parentHash);
- qDeleteAll(eventDescription->childrenHash);
- eventDescription->parentHash.clear();
- eventDescription->childrenHash.clear();
- }
-
- // create root event for statistics & insert into list
- d->clearQmlRootEvent();
- QmlEventData *listedRootEvent = d->m_eventDescriptions.value(d->m_rootEventName);
- if (!listedRootEvent) {
- listedRootEvent = new QmlEventData;
- d->m_eventDescriptions.insert(d->m_rootEventName, listedRootEvent);
- }
- *listedRootEvent = d->m_qmlRootEvent;
-
- // compute parent-child relationship and call count
- QHash<int, QmlEventData*> lastParent;
- for (index = fromIndex; index <= toIndex; index++) {
- QmlEventData *eventDescription = d->m_startTimeSortedList[index].description;
-
- if (d->m_startTimeSortedList[index].startTime > endTime ||
- d->m_startTimeSortedList[index].startTime+d->m_startTimeSortedList[index].length < startTime) {
- continue;
- }
-
- if (eventDescription->eventType == QmlJsDebugClient::Painting) {
- // skip animation/paint events
- continue;
- }
-
- eventDescription->calls++;
- qint64 duration = d->m_startTimeSortedList[index].length;
- eventDescription->duration += duration;
- if (eventDescription->maxTime < duration)
- eventDescription->maxTime = duration;
- if (eventDescription->minTime > duration)
- eventDescription->minTime = duration;
-
- int level = d->m_startTimeSortedList[index].level;
-
- QmlEventData *parentEvent = listedRootEvent;
- if (level > MIN_LEVEL && lastParent.contains(level-1)) {
- parentEvent = lastParent[level-1];
- }
-
- if (!eventDescription->parentHash.contains(parentEvent->eventHashStr)) {
- QmlEventSub *newParentEvent = new QmlEventSub(parentEvent);
- newParentEvent->calls = 1;
- newParentEvent->duration = duration;
-
- eventDescription->parentHash.insert(parentEvent->eventHashStr, newParentEvent);
- } else {
- QmlEventSub *newParentEvent = eventDescription->parentHash.value(parentEvent->eventHashStr);
- newParentEvent->duration += duration;
- newParentEvent->calls++;
- }
-
- if (!parentEvent->childrenHash.contains(eventDescription->eventHashStr)) {
- QmlEventSub *newChildEvent = new QmlEventSub(eventDescription);
- newChildEvent->calls = 1;
- newChildEvent->duration = duration;
-
- parentEvent->childrenHash.insert(eventDescription->eventHashStr, newChildEvent);
- } else {
- QmlEventSub *newChildEvent = parentEvent->childrenHash.value(eventDescription->eventHashStr);
- newChildEvent->duration += duration;
- newChildEvent->calls++;
- }
-
- lastParent[level] = eventDescription;
-
- if (level == MIN_LEVEL) {
- totalTime += duration;
- }
- }
-
- // fake rootEvent statistics
- // the +1 nanosecond is to force it to be on top of the sorted list
- listedRootEvent->duration = totalTime+1;
- listedRootEvent->minTime = totalTime+1;
- listedRootEvent->maxTime = totalTime+1;
- listedRootEvent->medianTime = totalTime+1;
- if (totalTime > 0)
- listedRootEvent->calls = 1;
-
- // copy to the global root reference
- d->m_qmlRootEvent = *listedRootEvent;
-
- // compute percentages
- foreach (QmlEventData *binding, d->m_eventDescriptions.values()) {
- binding->percentOfTime = binding->duration * 100.0 / totalTime;
- binding->timePerCall = binding->calls > 0 ? double(binding->duration) / binding->calls : 0;
- }
-
- // compute median time
- QHash < QmlEventData* , QList<qint64> > durationLists;
- for (index = fromIndex; index <= toIndex; index++) {
- QmlEventData *desc = d->m_startTimeSortedList[index].description;
- qint64 len = d->m_startTimeSortedList[index].length;
- durationLists[desc].append(len);
- }
- QMutableHashIterator < QmlEventData* , QList<qint64> > iter(durationLists);
- while (iter.hasNext()) {
- iter.next();
- if (!iter.value().isEmpty()) {
- qSort(iter.value());
- iter.key()->medianTime = iter.value().at(iter.value().count()/2);
- }
- }
-
- // find binding loops
- findBindingLoops(startTime, endTime);
-}
-
-void QmlProfilerEventList::prepareForDisplay()
-{
- // generate numeric ids
- int ndx = 0;
- foreach (QmlEventData *binding, d->m_eventDescriptions.values()) {
- binding->eventId = ndx++;
- }
-
- // collect type counts
- foreach (const QmlEventStartTimeData &eventStartData, d->m_startTimeSortedList) {
- int typeNumber = eventStartData.description->eventType;
- if (!d->m_typeCounts.contains(typeNumber)) {
- d->m_typeCounts[typeNumber] = new QmlEventTypeCount;
- d->m_typeCounts[typeNumber]->nestingCount = 0;
- }
- if (eventStartData.nestingLevel > d->m_typeCounts[typeNumber]->nestingCount) {
- d->m_typeCounts[typeNumber]->nestingCount = eventStartData.nestingLevel;
- }
- if (!d->m_typeCounts[typeNumber]->eventIds.contains(eventStartData.description->eventId))
- d->m_typeCounts[typeNumber]->eventIds << eventStartData.description->eventId;
- }
-}
-
-void QmlProfilerEventList::sortStartTimes()
-{
- if (d->m_startTimeSortedList.count() < 2)
- return;
-
- // assuming startTimes is partially sorted
- // identify blocks of events and sort them with quicksort
- QList<QmlEventStartTimeData>::iterator itFrom = d->m_startTimeSortedList.end() - 2;
- QList<QmlEventStartTimeData>::iterator itTo = d->m_startTimeSortedList.end() - 1;
-
- while (itFrom != d->m_startTimeSortedList.begin() && itTo != d->m_startTimeSortedList.begin()) {
- // find block to sort
- while ( itFrom != d->m_startTimeSortedList.begin()
- && itTo->startTime > itFrom->startTime ) {
- itTo--;
- itFrom = itTo - 1;
- }
-
- // if we're at the end of the list
- if (itFrom == d->m_startTimeSortedList.begin())
- break;
-
- // find block length
- while ( itFrom != d->m_startTimeSortedList.begin()
- && itTo->startTime <= itFrom->startTime )
- itFrom--;
-
- if (itTo->startTime <= itFrom->startTime)
- qSort(itFrom, itTo + 1, compareStartTimes);
- else
- qSort(itFrom + 1, itTo + 1, compareStartTimes);
-
- // move to next block
- itTo = itFrom;
- itFrom = itTo - 1;
- }
-
- // link back the endTimes
- for (int i = 0; i < d->m_startTimeSortedList.length(); i++)
- d->m_endTimeSortedList[d->m_startTimeSortedList[i].endTimeIndex].startTimeIndex = i;
-}
-
-void QmlProfilerEventList::sortEndTimes()
-{
- // assuming endTimes is partially sorted
- // identify blocks of events and sort them with quicksort
-
- if (d->m_endTimeSortedList.count() < 2)
- return;
-
- QList<QmlEventEndTimeData>::iterator itFrom = d->m_endTimeSortedList.begin();
- QList<QmlEventEndTimeData>::iterator itTo = d->m_endTimeSortedList.begin() + 1;
-
- while (itTo != d->m_endTimeSortedList.end() && itFrom != d->m_endTimeSortedList.end()) {
- // find block to sort
- while ( itTo != d->m_endTimeSortedList.end()
- && d->m_startTimeSortedList[itTo->startTimeIndex].startTime >
- d->m_startTimeSortedList[itFrom->startTimeIndex].startTime +
- d->m_startTimeSortedList[itFrom->startTimeIndex].length ) {
- itFrom++;
- itTo = itFrom+1;
- }
-
- // if we're at the end of the list
- if (itTo == d->m_endTimeSortedList.end())
- break;
-
- // find block length
- while ( itTo != d->m_endTimeSortedList.end()
- && d->m_startTimeSortedList[itTo->startTimeIndex].startTime <=
- d->m_startTimeSortedList[itFrom->startTimeIndex].startTime +
- d->m_startTimeSortedList[itFrom->startTimeIndex].length )
- itTo++;
-
- // sort block
- qSort(itFrom, itTo, compareEndTimes);
-
- // move to next block
- itFrom = itTo;
- itTo = itFrom+1;
-
- }
-
- // link back the startTimes
- for (int i = 0; i < d->m_endTimeSortedList.length(); i++)
- d->m_startTimeSortedList[d->m_endTimeSortedList[i].startTimeIndex].endTimeIndex = i;
-}
-
-void QmlProfilerEventList::findAnimationLimits()
-{
- d->m_maximumAnimationCount = 0;
- d->m_minimumAnimationCount = 0;
- d->m_lastFrameEvent = 0;
-
- for (int i = 0; i < d->m_startTimeSortedList.count(); i++) {
- if (d->m_startTimeSortedList[i].description->eventType == QmlJsDebugClient::Painting &&
- d->m_startTimeSortedList[i].animationCount >= 0) {
- int animationcount = d->m_startTimeSortedList[i].animationCount;
- if (d->m_lastFrameEvent) {
- if (animationcount > d->m_maximumAnimationCount)
- d->m_maximumAnimationCount = animationcount;
- if (animationcount < d->m_minimumAnimationCount)
- d->m_minimumAnimationCount = animationcount;
- } else {
- d->m_maximumAnimationCount = animationcount;
- d->m_minimumAnimationCount = animationcount;
- }
- d->m_lastFrameEvent = &d->m_startTimeSortedList[i];
- }
- }
-}
-
-void QmlProfilerEventList::computeNestingLevels()
-{
- // compute levels
- QHash <int, qint64> endtimesPerLevel;
- QList <int> nestingLevels;
- QList < QHash <int, qint64> > endtimesPerNestingLevel;
- int level = MIN_LEVEL;
- endtimesPerLevel[MIN_LEVEL] = 0;
-
- for (int i = 0; i < QmlJsDebugClient::MaximumQmlEventType; i++) {
- nestingLevels << MIN_LEVEL;
- QHash <int, qint64> dummyHash;
- dummyHash[MIN_LEVEL] = 0;
- endtimesPerNestingLevel << dummyHash;
- }
-
- for (int i=0; i<d->m_startTimeSortedList.count(); i++) {
- qint64 st = d->m_startTimeSortedList[i].startTime;
- int type = d->m_startTimeSortedList[i].description->eventType;
-
- if (type == QmlJsDebugClient::Painting) {
- // animation/paint events have level 1 by definition,
- // but are not considered parents of other events for statistical purposes
- d->m_startTimeSortedList[i].level = MIN_LEVEL;
- d->m_startTimeSortedList[i].nestingLevel = MIN_LEVEL;
- continue;
- }
-
- // general level
- if (endtimesPerLevel[level] > st) {
- level++;
- } else {
- while (level > MIN_LEVEL && endtimesPerLevel[level-1] <= st)
- level--;
- }
- endtimesPerLevel[level] = st + d->m_startTimeSortedList[i].length;
-
- // per type
- if (endtimesPerNestingLevel[type][nestingLevels[type]] > st) {
- nestingLevels[type]++;
- } else {
- while (nestingLevels[type] > MIN_LEVEL &&
- endtimesPerNestingLevel[type][nestingLevels[type]-1] <= st)
- nestingLevels[type]--;
- }
- endtimesPerNestingLevel[type][nestingLevels[type]] = st + d->m_startTimeSortedList[i].length;
-
- d->m_startTimeSortedList[i].level = level;
- d->m_startTimeSortedList[i].nestingLevel = nestingLevels[type];
-
- if (level == MIN_LEVEL) {
- d->m_qmlMeasuredTime += d->m_startTimeSortedList[i].length;
- }
- }
-}
-
-void QmlProfilerEventList::computeNestingDepth()
-{
- QHash <int, int> nestingDepth;
- for (int i = 0; i < d->m_endTimeSortedList.count(); i++) {
- int type = d->m_endTimeSortedList[i].description->eventType;
- int nestingInType = d->m_startTimeSortedList[ d->m_endTimeSortedList[i].startTimeIndex ].nestingLevel;
- if (!nestingDepth.contains(type))
- nestingDepth[type] = nestingInType;
- else {
- int nd = nestingDepth[type];
- nestingDepth[type] = nd > nestingInType ? nd : nestingInType;
- }
-
- d->m_startTimeSortedList[ d->m_endTimeSortedList[i].startTimeIndex ].nestingDepth = nestingDepth[type];
- if (nestingInType == MIN_LEVEL)
- nestingDepth[type] = MIN_LEVEL;
- }
-}
-
-void QmlProfilerEventList::postProcess()
-{
- if (count() != 0) {
- sortStartTimes();
- sortEndTimes();
- findAnimationLimits();
- computeLevels();
- linkEndsToStarts();
- reloadDetails();
- compileStatistics(traceStartTime(), traceEndTime());
- prepareForDisplay();
- setState(Done);
- } else {
- setState(Empty);
- }
-}
-
-void QmlProfilerEventList::linkEndsToStarts()
-{
- for (int i = 0; i < d->m_startTimeSortedList.count(); i++)
- d->m_endTimeSortedList[d->m_startTimeSortedList[i].endTimeIndex].startTimeIndex = i;
-}
-
-void QmlProfilerEventList::computeLevels()
-{
- computeNestingLevels();
- computeNestingDepth();
-}
-
-void QmlProfilerEventList::reloadDetails()
-{
- // request binding/signal details from the AST
- foreach (QmlEventData *event, d->m_eventDescriptions.values()) {
- if (event->eventType != Binding && event->eventType != HandlingSignal)
- continue;
-
- // This skips anonymous bindings in Qt4.8 (we don't have valid location data for them)
- if (event->location.filename.isEmpty())
- continue;
-
- // Skip non-anonymous bindings from Qt4.8 (we already have correct details for them)
- if (event->location.column == -1)
- continue;
-
- emit requestDetailsForLocation(event->eventType, event->location);
- }
- emit reloadDocumentsForDetails();
-}
-
-void QmlProfilerEventList::rewriteDetailsString(int eventType, const QmlJsDebugClient::QmlEventLocation &location, const QString &newString)
-{
- QString eventHashStr = getHashStringForQmlEvent(location, eventType);
- QTC_ASSERT(d->m_eventDescriptions.contains(eventHashStr), return);
- d->m_eventDescriptions.value(eventHashStr)->details = newString;
- emit detailsChanged(d->m_eventDescriptions.value(eventHashStr)->eventId, newString);
-}
-
-void QmlProfilerEventList::finishedRewritingDetails()
-{
- emit reloadDetailLabels();
-}
-
-void QmlProfilerEventList::findBindingLoops(qint64 startTime, qint64 endTime)
-{
- // first clear existing data
- foreach (QmlEventData *event, d->m_eventDescriptions.values()) {
- event->isBindingLoop = false;
- foreach (QmlEventSub *parentEvent, event->parentHash.values())
- parentEvent->inLoopPath = false;
- foreach (QmlEventSub *childEvent, event->childrenHash.values())
- childEvent->inLoopPath = false;
- }
-
- QList <QmlEventData *> stackRefs;
- QList <QmlEventStartTimeData *> stack;
- int fromIndex = findFirstIndex(startTime);
- int toIndex = findLastIndex(endTime);
-
- for (int i = 0; i < d->m_startTimeSortedList.count(); i++) {
- QmlEventData *currentEvent = d->m_startTimeSortedList[i].description;
- QmlEventStartTimeData *inTimeEvent = &d->m_startTimeSortedList[i];
- inTimeEvent->bindingLoopHead = -1;
-
- // managing call stack
- for (int j = stack.count() - 1; j >= 0; j--) {
- if (stack[j]->startTime + stack[j]->length <= inTimeEvent->startTime) {
- stack.removeAt(j);
- stackRefs.removeAt(j);
- }
- }
-
- bool loopDetected = stackRefs.contains(currentEvent);
- stack << inTimeEvent;
- stackRefs << currentEvent;
-
- if (loopDetected) {
- if (i >= fromIndex && i <= toIndex) {
- // for the statistics
- currentEvent->isBindingLoop = true;
- for (int j = stackRefs.indexOf(currentEvent); j < stackRefs.count()-1; j++) {
- QmlEventSub *nextEventSub = stackRefs[j]->childrenHash.value(stackRefs[j+1]->eventHashStr);
- nextEventSub->inLoopPath = true;
- QmlEventSub *prevEventSub = stackRefs[j+1]->parentHash.value(stackRefs[j]->eventHashStr);
- prevEventSub->inLoopPath = true;
- }
- }
-
- // use crossed references to find index in starttimesortedlist
- QmlEventStartTimeData *head = stack[stackRefs.indexOf(currentEvent)];
- inTimeEvent->bindingLoopHead = d->m_endTimeSortedList[head->endTimeIndex].startTimeIndex;
- d->m_startTimeSortedList[inTimeEvent->bindingLoopHead].bindingLoopHead = i;
- }
- }
-}
-
-// get list of events between A and B:
-// find fist event with endtime after A -> aa
-// find last event with starttime before B -> bb
-// list is from parent of aa with level=0 to bb, in the "sorted by starttime" list
-int QmlProfilerEventList::findFirstIndex(qint64 startTime) const
-{
- int candidate = -1;
- // in the "endtime" list, find the first event that ends after startTime
- if (d->m_endTimeSortedList.isEmpty())
- return 0; // -1
- if (d->m_endTimeSortedList.length() == 1 || d->m_endTimeSortedList.first().endTime >= startTime)
- candidate = 0;
- else
- if (d->m_endTimeSortedList.last().endTime <= startTime)
- return 0; // -1
-
- if (candidate == -1)
- {
- int fromIndex = 0;
- int toIndex = d->m_endTimeSortedList.count()-1;
- while (toIndex - fromIndex > 1) {
- int midIndex = (fromIndex + toIndex)/2;
- if (d->m_endTimeSortedList[midIndex].endTime < startTime)
- fromIndex = midIndex;
- else
- toIndex = midIndex;
- }
-
- candidate = toIndex;
- }
-
- int ndx = d->m_endTimeSortedList[candidate].startTimeIndex;
-
- // and then go to the parent
- while (d->m_startTimeSortedList[ndx].level != MIN_LEVEL && ndx > 0)
- ndx--;
-
- return ndx;
-}
-
-int QmlProfilerEventList::findFirstIndexNoParents(qint64 startTime) const
-{
- int candidate = -1;
- // in the "endtime" list, find the first event that ends after startTime
- if (d->m_endTimeSortedList.isEmpty())
- return 0; // -1
- if (d->m_endTimeSortedList.length() == 1 || d->m_endTimeSortedList.first().endTime >= startTime)
- candidate = 0;
- else
- if (d->m_endTimeSortedList.last().endTime <= startTime)
- return 0; // -1
-
- if (candidate == -1) {
- int fromIndex = 0;
- int toIndex = d->m_endTimeSortedList.count()-1;
- while (toIndex - fromIndex > 1) {
- int midIndex = (fromIndex + toIndex)/2;
- if (d->m_endTimeSortedList[midIndex].endTime < startTime)
- fromIndex = midIndex;
- else
- toIndex = midIndex;
- }
-
- candidate = toIndex;
- }
-
- int ndx = d->m_endTimeSortedList[candidate].startTimeIndex;
-
- return ndx;
-}
-
-int QmlProfilerEventList::findLastIndex(qint64 endTime) const
-{
- // in the "starttime" list, find the last event that starts before endtime
- if (d->m_startTimeSortedList.isEmpty())
- return 0; // -1
- if (d->m_startTimeSortedList.first().startTime >= endTime)
- return 0; // -1
- if (d->m_startTimeSortedList.length() == 1)
- return 0;
- if (d->m_startTimeSortedList.last().startTime <= endTime)
- return d->m_startTimeSortedList.count()-1;
-
- int fromIndex = 0;
- int toIndex = d->m_startTimeSortedList.count()-1;
- while (toIndex - fromIndex > 1) {
- int midIndex = (fromIndex + toIndex)/2;
- if (d->m_startTimeSortedList[midIndex].startTime < endTime)
- fromIndex = midIndex;
- else
- toIndex = midIndex;
- }
-
- return fromIndex;
-}
-
-qint64 QmlProfilerEventList::firstTimeMark() const
-{
- if (d->m_startTimeSortedList.isEmpty())
- return 0;
- else {
- return d->m_startTimeSortedList[0].startTime;
- }
-}
-
-qint64 QmlProfilerEventList::lastTimeMark() const
-{
- if (d->m_endTimeSortedList.isEmpty())
- return 0;
- else {
- return d->m_endTimeSortedList.last().endTime;
- }
-}
-
-qint64 QmlProfilerEventList::traceStartTime() const
-{
- return d->m_traceStartTime != -1? d->m_traceStartTime : firstTimeMark();
-}
-
-qint64 QmlProfilerEventList::traceEndTime() const
-{
- return d->m_traceEndTime ? d->m_traceEndTime : lastTimeMark();
-}
-
-qint64 QmlProfilerEventList::traceDuration() const
-{
- return traceEndTime() - traceStartTime();
-}
-
-qint64 QmlProfilerEventList::qmlMeasuredTime() const
-{
- return d->m_qmlMeasuredTime;
-}
-qint64 QmlProfilerEventList::v8MeasuredTime() const
-{
- return d->m_v8MeasuredTime;
-}
-
-int QmlProfilerEventList::count() const
-{
- return d->m_startTimeSortedList.count();
-}
-
-////////////////////////////////////////////////////////////////////////////////////
-
-
-bool QmlProfilerEventList::save(const QString &filename)
-{
- if (count() == 0) {
- emit error(tr("No data to save"));
- return false;
- }
-
- QFile file(filename);
- if (!file.open(QIODevice::WriteOnly)) {
- emit error(tr("Could not open %1 for writing").arg(filename));
- return false;
- }
-
- QXmlStreamWriter stream(&file);
- stream.setAutoFormatting(true);
- stream.writeStartDocument();
-
- stream.writeStartElement("trace");
- stream.writeAttribute("version", Constants::PROFILER_FILE_VERSION);
-
- stream.writeAttribute("traceStart", QString::number(traceStartTime()));
- stream.writeAttribute("traceEnd", QString::number(traceEndTime()));
-
- stream.writeStartElement("eventData");
- stream.writeAttribute("totalTime", QString::number(d->m_qmlMeasuredTime));
-
- foreach (const QmlEventData *eventData, d->m_eventDescriptions.values()) {
- stream.writeStartElement("event");
- stream.writeAttribute("index", QString::number(d->m_eventDescriptions.keys().indexOf(eventData->eventHashStr)));
- stream.writeTextElement("displayname", eventData->displayname);
- stream.writeTextElement("type", qmlEventType(eventData->eventType));
- if (!eventData->location.filename.isEmpty()) {
- stream.writeTextElement("filename", eventData->location.filename);
- stream.writeTextElement("line", QString::number(eventData->location.line));
- stream.writeTextElement("column", QString::number(eventData->location.column));
- }
- stream.writeTextElement("details", eventData->details);
- stream.writeEndElement();
- }
- stream.writeEndElement(); // eventData
-
- stream.writeStartElement("eventList");
- foreach (const QmlEventStartTimeData &rangedEvent, d->m_startTimeSortedList) {
- stream.writeStartElement("range");
- stream.writeAttribute("startTime", QString::number(rangedEvent.startTime));
- stream.writeAttribute("duration", QString::number(rangedEvent.length));
- stream.writeAttribute("eventIndex", QString::number(d->m_eventDescriptions.keys().indexOf(rangedEvent.description->eventHashStr)));
- if (rangedEvent.description->eventType == QmlJsDebugClient::Painting && rangedEvent.animationCount >= 0) {
- // animation frame
- stream.writeAttribute("framerate", QString::number(rangedEvent.frameRate));
- stream.writeAttribute("animationcount", QString::number(rangedEvent.animationCount));
- }
- stream.writeEndElement();
- }
- stream.writeEndElement(); // eventList
-
- stream.writeStartElement("v8profile"); // v8 profiler output
- stream.writeAttribute("totalTime", QString::number(d->m_v8MeasuredTime));
- 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->childrenHash.isEmpty()) {
- stream.writeStartElement("childrenEvents");
- QStringList childrenIndexes;
- QStringList childrenTimes;
- QStringList parentTimes;
- foreach (QV8EventSub *v8child, v8event->childrenHash.values()) {
- childrenIndexes << QString::number(v8child->reference->eventId);
- childrenTimes << QString::number(v8child->totalTime);
- parentTimes << QString::number(d->m_v8EventList[v8child->reference->eventId]->parentHash[v8event->displayName]->totalTime);
- }
-
- stream.writeAttribute("list", childrenIndexes.join(QString(", ")));
- stream.writeAttribute("childrenTimes", childrenTimes.join(QString(", ")));
- stream.writeAttribute("parentTimes", parentTimes.join(QString(", ")));
- stream.writeEndElement();
- }
- stream.writeEndElement();
- }
- stream.writeEndElement(); // v8 profiler output
-
- stream.writeEndElement(); // trace
- stream.writeEndDocument();
-
- file.close();
- return true;
-}
-
-void QmlProfilerEventList::setFilename(const QString &filename)
-{
- d->m_filename = filename;
-}
-
-void QmlProfilerEventList::load(const QString &filename)
-{
- setFilename(filename);
- load();
-}
-
-// "be strict in your output but tolerant in your inputs"
-void QmlProfilerEventList::load()
-{
- QString filename = d->m_filename;
-
- QFile file(filename);
-
- if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
- emit error(tr("Could not open %1 for reading").arg(filename));
- return;
- }
-
- // erase current
- clear();
-
- setState(AcquiringData);
-
- bool readingQmlEvents = false;
- bool readingV8Events = false;
- QHash <int, QmlEventData *> descriptionBuffer;
- QmlEventData *currentEvent = 0;
- QHash <int, QV8EventData *> v8eventBuffer;
- QHash <int, QString> childrenIndexes;
- QHash <int, QString> childrenTimes;
- QHash <int, QString> parentTimes;
- QV8EventData *v8event = 0;
- bool startTimesAreSorted = true;
- bool validVersion = true;
-
- // time computation
- d->m_v8MeasuredTime = 0;
- d->m_qmlMeasuredTime = 0;
- double cumulatedV8Time = 0;
-
- QXmlStreamReader stream(&file);
-
- while (validVersion && !stream.atEnd() && !stream.hasError()) {
- QXmlStreamReader::TokenType token = stream.readNext();
- QString elementName = stream.name().toString();
- switch (token) {
- case QXmlStreamReader::StartDocument : continue;
- case QXmlStreamReader::StartElement : {
- if (elementName == "trace") {
- QXmlStreamAttributes attributes = stream.attributes();
- if (attributes.hasAttribute("version"))
- validVersion = attributes.value("version").toString() == Constants::PROFILER_FILE_VERSION;
- else
- validVersion = false;
- if (attributes.hasAttribute("traceStart"))
- setTraceStartTime(attributes.value("traceStart").toString().toLongLong());
- if (attributes.hasAttribute("traceEnd"))
- setTraceEndTime(attributes.value("traceEnd").toString().toLongLong());
- }
- if (elementName == "eventData" && !readingV8Events) {
- readingQmlEvents = true;
- QXmlStreamAttributes attributes = stream.attributes();
- if (attributes.hasAttribute("totalTime"))
- d->m_qmlMeasuredTime = attributes.value("totalTime").toString().toDouble();
- break;
- }
- if (elementName == "v8profile" && !readingQmlEvents) {
- readingV8Events = true;
- QXmlStreamAttributes attributes = stream.attributes();
- if (attributes.hasAttribute("totalTime"))
- d->m_v8MeasuredTime = attributes.value("totalTime").toString().toDouble();
- break;
- }
-
- if (elementName == "trace") {
- QXmlStreamAttributes attributes = stream.attributes();
- if (attributes.hasAttribute("traceStart"))
- setTraceStartTime(attributes.value("traceStart").toString().toLongLong());
- if (attributes.hasAttribute("traceEnd"))
- setTraceEndTime(attributes.value("traceEnd").toString().toLongLong());
- }
-
- if (elementName == "range") {
- QmlEventStartTimeData rangedEvent;
- QXmlStreamAttributes attributes = stream.attributes();
- if (attributes.hasAttribute("startTime"))
- rangedEvent.startTime = attributes.value("startTime").toString().toLongLong();
- if (attributes.hasAttribute("duration"))
- rangedEvent.length = attributes.value("duration").toString().toLongLong();
- if (attributes.hasAttribute("framerate"))
- rangedEvent.frameRate = attributes.value("framerate").toString().toInt();
- if (attributes.hasAttribute("animationcount"))
- rangedEvent.animationCount = attributes.value("animationcount").toString().toInt();
- else
- rangedEvent.animationCount = -1;
- if (attributes.hasAttribute("eventIndex")) {
- int ndx = attributes.value("eventIndex").toString().toInt();
- if (!descriptionBuffer.value(ndx))
- descriptionBuffer[ndx] = new QmlEventData;
- rangedEvent.description = descriptionBuffer.value(ndx);
- }
- rangedEvent.endTimeIndex = d->m_endTimeSortedList.length();
-
- if (!d->m_startTimeSortedList.isEmpty()
- && rangedEvent.startTime < d->m_startTimeSortedList.last().startTime)
- startTimesAreSorted = false;
- d->m_startTimeSortedList << rangedEvent;
-
- QmlEventEndTimeData endTimeEvent;
- endTimeEvent.endTime = rangedEvent.startTime + rangedEvent.length;
- endTimeEvent.startTimeIndex = d->m_startTimeSortedList.length()-1;
- endTimeEvent.description = rangedEvent.description;
- d->m_endTimeSortedList << endTimeEvent;
- 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;
- }
-
- // the remaining are eventdata or v8eventdata elements
- if (!currentEvent)
- 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->location.filename = readData;
- break;
- }
- if (elementName == "line") {
- currentEvent->location.line = readData.toInt();
- break;
- }
- if (elementName == "column") {
- currentEvent->location.column = readData.toInt();
- }
- if (elementName == "details") {
- currentEvent->details = 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();
- int eventIndex = v8eventBuffer.key(v8event);
- if (attributes.hasAttribute("list")) {
- // store for later parsing (we haven't read all the events yet)
- childrenIndexes[eventIndex] = attributes.value("list").toString();
- }
- if (attributes.hasAttribute("childrenTimes")) {
- childrenTimes[eventIndex] = attributes.value("childrenTimes").toString();
- }
- if (attributes.hasAttribute("parentTimes")) {
- parentTimes[eventIndex] = attributes.value("parentTimes").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();
- cumulatedV8Time += v8event->totalTime;
- break;
- }
-
- if (elementName == "selfTime") {
- v8event->selfTime = readData.toDouble();
- break;
- }
- }
-
- break;
- }
- case QXmlStreamReader::EndElement : {
- if (elementName == "event") {
- currentEvent = 0;
- break;
- }
- if (elementName == "eventData") {
- readingQmlEvents = false;
- break;
- }
- if (elementName == "v8profile") {
- readingV8Events = false;
- }
- }
- default: break;
- }
- }
-
- file.close();
-
- if (stream.hasError()) {
- emit error(tr("Error while parsing %1").arg(filename));
- clear();
- return;
- }
-
- stream.clear();
-
- if (!validVersion) {
- clear();
- emit error(tr("Invalid version of QML Trace file."));
- return;
- }
-
- // backwards compatibility
- if (d->m_v8MeasuredTime == 0)
- d->m_v8MeasuredTime = cumulatedV8Time;
-
- // move the buffered data to the details cache
- foreach (QmlEventData *desc, descriptionBuffer.values()) {
- desc->eventHashStr = getHashStringForQmlEvent(desc->location, desc->eventType);;
- d->m_eventDescriptions[desc->eventHashStr] = desc;
- }
-
- // sort startTimeSortedList
- if (!startTimesAreSorted) {
- qSort(d->m_startTimeSortedList.begin(), d->m_startTimeSortedList.end(), compareStartTimes);
- for (int i = 0; i< d->m_startTimeSortedList.length(); i++) {
- QmlEventStartTimeData startTimeData = d->m_startTimeSortedList[i];
- d->m_endTimeSortedList[startTimeData.endTimeIndex].startTimeIndex = i;
- }
- 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(",");
- QStringList childrenTimesStrings = childrenTimes.value(parentIndex).split(", ");
- QStringList parentTimesStrings = parentTimes.value(parentIndex).split(", ");
- for (int ndx = 0; ndx < childrenStrings.count(); ndx++) {
- int childIndex = childrenStrings[ndx].toInt();
- if (v8eventBuffer.value(childIndex)) {
- QV8EventSub *newChild = new QV8EventSub(v8eventBuffer[childIndex]);
- QV8EventSub *newParent = new QV8EventSub(v8eventBuffer[parentIndex]);
- if (childrenTimesStrings.count() > ndx)
- newChild->totalTime = childrenTimesStrings[ndx].toDouble();
- if (parentTimesStrings.count() > ndx)
- newParent->totalTime = parentTimesStrings[ndx].toDouble();
- v8eventBuffer[parentIndex]->childrenHash.insert(newChild->reference->displayName, newChild);
- v8eventBuffer[childIndex]->parentHash.insert(newParent->reference->displayName, newParent);
- }
- }
- }
- // store v8 events
- d->m_v8EventList = v8eventBuffer.values();
-
- emit countChanged();
-
- descriptionBuffer.clear();
-
- setState(ProcessingData);
- d->collectV8Statistics();
- postProcess();
-}
-
-///////////////////////////////////////////////
-qint64 QmlProfilerEventList::getStartTime(int index) const
-{
- return d->m_startTimeSortedList[index].startTime;
-}
-
-qint64 QmlProfilerEventList::getEndTime(int index) const
-{
- return d->m_startTimeSortedList[index].startTime + d->m_startTimeSortedList[index].length;
-}
-
-qint64 QmlProfilerEventList::getDuration(int index) const
-{
- return d->m_startTimeSortedList[index].length;
-}
-
-int QmlProfilerEventList::getType(int index) const
-{
- return d->m_startTimeSortedList[index].description->eventType;
-}
-
-int QmlProfilerEventList::getNestingLevel(int index) const
-{
- return d->m_startTimeSortedList[index].nestingLevel;
-}
-
-int QmlProfilerEventList::getNestingDepth(int index) const
-{
- return d->m_startTimeSortedList[index].nestingDepth;
-}
-
-QString QmlProfilerEventList::getFilename(int index) const
-{
- return d->m_startTimeSortedList[index].description->location.filename;
-}
-
-int QmlProfilerEventList::getLine(int index) const
-{
- return d->m_startTimeSortedList[index].description->location.line;
-}
-
-int QmlProfilerEventList::getColumn(int index) const
-{
- return d->m_startTimeSortedList[index].description->location.column;
-}
-
-QString QmlProfilerEventList::getDetails(int index) const
-{
- // special: animations
- if (d->m_startTimeSortedList[index].description->eventType == QmlJsDebugClient::Painting &&
- d->m_startTimeSortedList[index].animationCount >= 0)
- return tr("%1 animations at %2 FPS").arg(
- QString::number(d->m_startTimeSortedList[index].animationCount),
- QString::number(d->m_startTimeSortedList[index].frameRate));
- return d->m_startTimeSortedList[index].description->details;
-}
-
-int QmlProfilerEventList::getEventId(int index) const
-{
- return d->m_startTimeSortedList[index].description->eventId;
-}
-
-int QmlProfilerEventList::getBindingLoopDest(int index) const
-{
- return d->m_startTimeSortedList[index].bindingLoopHead;
-}
-
-int QmlProfilerEventList::getFramerate(int index) const
-{
- return d->m_startTimeSortedList[index].frameRate;
-}
-
-int QmlProfilerEventList::getAnimationCount(int index) const
-{
- return d->m_startTimeSortedList[index].animationCount;
-}
-
-int QmlProfilerEventList::getMaximumAnimationCount() const
-{
- return d->m_maximumAnimationCount;
-}
-
-int QmlProfilerEventList::getMinimumAnimationCount() const
-{
- return d->m_minimumAnimationCount;
-}
-
-int QmlProfilerEventList::uniqueEventsOfType(int type) const
-{
- if (!d->m_typeCounts.contains(type))
- return 0;
- return d->m_typeCounts[type]->eventIds.count();
-}
-
-int QmlProfilerEventList::maxNestingForType(int type) const
-{
- if (!d->m_typeCounts.contains(type))
- return 0;
- return d->m_typeCounts[type]->nestingCount;
-}
-
-QString QmlProfilerEventList::eventTextForType(int type, int index) const
-{
- if (!d->m_typeCounts.contains(type))
- return QString();
- return d->m_eventDescriptions.values().at(d->m_typeCounts[type]->eventIds[index])->details;
-}
-
-QString QmlProfilerEventList::eventDisplayNameForType(int type, int index) const
-{
- if (!d->m_typeCounts.contains(type))
- return QString();
- return d->m_eventDescriptions.values().at(d->m_typeCounts[type]->eventIds[index])->displayname;
-}
-
-int QmlProfilerEventList::eventIdForType(int type, int index) const
-{
- if (!d->m_typeCounts.contains(type))
- return -1;
- return d->m_typeCounts[type]->eventIds[index];
-}
-
-int QmlProfilerEventList::eventPosInType(int index) const
-{
- int eventType = d->m_startTimeSortedList[index].description->eventType;
- return d->m_typeCounts[eventType]->eventIds.indexOf(d->m_startTimeSortedList[index].description->eventId);
-}
-
-/////////////////////////////////////////
-QmlProfilerEventList::State QmlProfilerEventList::currentState() const
-{
- return d->m_state;
-}
-
-int QmlProfilerEventList::getCurrentStateFromQml() const
-{
- return (int)d->m_state;
-}
-
-void QmlProfilerEventList::setState(QmlProfilerEventList::State state)
-{
- // It's not an error, we are continuously calling "AcquiringData" for example
- if (d->m_state == state)
- return;
-
- switch (state) {
- case Empty:
- // if it's not empty, complain but go on
- QTC_ASSERT(count() == 0, /**/);
- break;
- case AcquiringData:
- // we're not supposed to receive new data while processing older data
- QTC_ASSERT(d->m_state != ProcessingData, return);
- break;
- case ProcessingData:
- QTC_ASSERT(d->m_state == AcquiringData, return);
- break;
- case Done:
- QTC_ASSERT(d->m_state == ProcessingData, return);
- break;
- default:
- qDebug() << "Trying to set unknown state in events list at" << __FILE__ << __LINE__;
- break;
- }
-
- d->m_state = state;
- emit stateChanged();
- return;
-}
-
-} // namespace QmlJsDebugClient
diff --git a/src/libs/qmljsdebugclient/qmlprofilereventlocation.h b/src/libs/qmljsdebugclient/qmlprofilereventlocation.h
index 45b7df6ae6..cb317fd2e5 100644
--- a/src/libs/qmljsdebugclient/qmlprofilereventlocation.h
+++ b/src/libs/qmljsdebugclient/qmlprofilereventlocation.h
@@ -35,6 +35,8 @@
#include "qmljsdebugclient_global.h"
+#include <QString>
+
namespace QmlJsDebugClient {
struct QMLJSDEBUGCLIENT_EXPORT QmlEventLocation
diff --git a/src/libs/qmljsdebugclient/qmlprofilereventtypes.h b/src/libs/qmljsdebugclient/qmlprofilereventtypes.h
index ae1c490002..02e102f039 100644
--- a/src/libs/qmljsdebugclient/qmlprofilereventtypes.h
+++ b/src/libs/qmljsdebugclient/qmlprofilereventtypes.h
@@ -33,8 +33,6 @@
#ifndef QMLPROFILEREVENTTYPES_H
#define QMLPROFILEREVENTTYPES_H
-#include <QString>
-
namespace QmlJsDebugClient {
enum QmlEventType {
@@ -47,8 +45,15 @@ enum QmlEventType {
MaximumQmlEventType
};
-QString qmlEventType(QmlEventType typeEnum);
-QmlEventType qmlEventType(const QString &typeString);
+namespace Constants {
+const char TYPE_PAINTING_STR[] = "Painting";
+const char TYPE_COMPILING_STR[] = "Compiling";
+const char TYPE_CREATING_STR[] = "Creating";
+const char TYPE_BINDING_STR[] = "Binding";
+const char TYPE_HANDLINGSIGNAL_STR[] = "HandlingSignal";
+const char PROFILER_FILE_VERSION[] = "1.02";
+const int QML_MIN_LEVEL = 1;
+}
} // namespace QmlJsDebugClient
diff --git a/src/libs/qmljsdebugclient/qmlprofilertraceclient.cpp b/src/libs/qmljsdebugclient/qmlprofilertraceclient.cpp
index cc130bba73..01eb3f56b3 100644
--- a/src/libs/qmljsdebugclient/qmlprofilertraceclient.cpp
+++ b/src/libs/qmljsdebugclient/qmlprofilertraceclient.cpp
@@ -159,6 +159,12 @@ void QmlProfilerTraceClient::messageReceived(const QByteArray &data)
int event;
stream >> event;
+ // stop with the first data
+ if (d->recording && event != StartTrace)
+ setRecordingFromServer(false);
+ else if ((!d->recording) && event == StartTrace)
+ setRecordingFromServer(true);
+
if (event == EndTrace) {
emit this->traceFinished(time);
d->maximumTime = time;
@@ -169,9 +175,6 @@ void QmlProfilerTraceClient::messageReceived(const QByteArray &data)
emit this->frame(time, frameRate, animationCount);
d->maximumTime = qMax(time, d->maximumTime);
} else if (event == StartTrace) {
- // special: StartTrace is now asynchronous
- if (!d->recording)
- setRecordingFromServer(true);
emit this->traceStarted(time);
d->maximumTime = time;
} else if (event < MaximumEventType) {
@@ -191,6 +194,9 @@ void QmlProfilerTraceClient::messageReceived(const QByteArray &data)
d->rangeStartTimes[range].push(time);
d->inProgressRanges |= (static_cast<qint64>(1) << range);
++d->rangeCount[range];
+ // stop with the first data
+ if (d->recording)
+ setRecordingFromServer(false);
} else if (messageType == RangeData) {
QString data;
stream >> data;
diff --git a/src/libs/qmljsdebugclient/qmlprofilertraceclient.h b/src/libs/qmljsdebugclient/qmlprofilertraceclient.h
index 4d37c067e3..3ed2071d73 100644
--- a/src/libs/qmljsdebugclient/qmlprofilertraceclient.h
+++ b/src/libs/qmljsdebugclient/qmlprofilertraceclient.h
@@ -80,10 +80,9 @@ public:
bool isEnabled() const;
bool isRecording() const;
+ void setRecording(bool);
public slots:
- void setRecording(bool);
- void setRecordingFromServer(bool);
void clearData();
void sendRecordingStatus();
@@ -107,6 +106,9 @@ protected:
virtual void messageReceived(const QByteArray &);
private:
+ void setRecordingFromServer(bool);
+
+private:
class QmlProfilerTraceClientPrivate *d;
};
diff --git a/src/libs/qmljsdebugclient/qv8profilerclient.cpp b/src/libs/qmljsdebugclient/qv8profilerclient.cpp
index 432ed6c41c..7853efcdb3 100644
--- a/src/libs/qmljsdebugclient/qv8profilerclient.cpp
+++ b/src/libs/qmljsdebugclient/qv8profilerclient.cpp
@@ -118,6 +118,16 @@ void QV8ProfilerClient::setRecording(bool v)
emit recordingChanged(v);
}
+void QV8ProfilerClient::setRecordingFromServer(bool v)
+{
+ if (v == d->recording)
+ return;
+
+ d->recording = v;
+
+ emit recordingChanged(v);
+}
+
void QV8ProfilerClient::statusChanged(Status /*status*/)
{
emit enabledChanged();
@@ -133,7 +143,10 @@ void QV8ProfilerClient::messageReceived(const QByteArray &data)
stream >> messageType;
if (messageType == V8Complete) {
+ setRecordingFromServer(false);
emit complete();
+ } else if (messageType == V8ProfilingStarted) {
+ setRecordingFromServer(true);
} else if (messageType == V8Entry) {
QString filename;
QString function;
diff --git a/src/libs/qmljsdebugclient/qv8profilerclient.h b/src/libs/qmljsdebugclient/qv8profilerclient.h
index 25e1328c73..48d191a458 100644
--- a/src/libs/qmljsdebugclient/qv8profilerclient.h
+++ b/src/libs/qmljsdebugclient/qv8profilerclient.h
@@ -52,6 +52,9 @@ public:
enum Message {
V8Entry,
V8Complete,
+ V8SnapshotChunk,
+ V8SnapshotComplete,
+ V8ProfilingStarted,
V8MaximumMessage
};
@@ -61,9 +64,9 @@ public:
bool isEnabled() const;
bool isRecording() const;
+ void setRecording(bool);
public slots:
- void setRecording(bool);
void clearData();
void sendRecordingStatus();
@@ -77,6 +80,9 @@ signals:
void enabledChanged();
void cleared();
+private:
+ void setRecordingFromServer(bool);
+
protected:
virtual void statusChanged(Status);
virtual void messageReceived(const QByteArray &);
diff --git a/src/plugins/qmlprofiler/qml/Label.qml b/src/plugins/qmlprofiler/qml/Label.qml
index 0d89c057ab..21e322d5be 100644
--- a/src/plugins/qmlprofiler/qml/Label.qml
+++ b/src/plugins/qmlprofiler/qml/Label.qml
@@ -60,17 +60,19 @@ Item {
function updateHeight() {
height = root.singleRowHeight * (1 +
- (expanded ? qmlEventList.uniqueEventsOfType(typeIndex) : qmlEventList.maxNestingForType(typeIndex)));
+ (expanded ? qmlProfilerDataModel.uniqueEventsOfType(typeIndex) :
+ qmlProfilerDataModel.maxNestingForType(typeIndex)));
}
function getDescriptions() {
var desc=[];
var ids=[];
var extdesc=[];
- for (var i=0; i<qmlEventList.uniqueEventsOfType(typeIndex); i++) {
- desc[i] = qmlEventList.eventTextForType(typeIndex, i);
- ids[i] = qmlEventList.eventIdForType(typeIndex, i);
- extdesc[i] = qmlEventList.eventDisplayNameForType(typeIndex, i) + " : " + desc[i];
+ for (var i=0; i<qmlProfilerDataModel.uniqueEventsOfType(typeIndex); i++) {
+ desc[i] = qmlProfilerDataModel.eventTextForType(typeIndex, i);
+ ids[i] = qmlProfilerDataModel.eventIdForType(typeIndex, i);
+ extdesc[i] = qmlProfilerDataModel.eventDisplayNameForType(typeIndex, i) +
+ " : " + desc[i];
}
descriptions = desc;
eventIds = ids;
@@ -79,18 +81,18 @@ Item {
}
Connections {
- target: qmlEventList
+ target: qmlProfilerDataModel
onReloadDetailLabels: getDescriptions();
onStateChanged: {
// Empty
- if (qmlEventList.getCurrentStateFromQml() == 0) {
+ if (qmlProfilerDataModel.getCurrentStateFromQml() == 0) {
descriptions = [];
eventIds = [];
extdescriptions = [];
updateHeight();
} else
// Done
- if (qmlEventList.getCurrentStateFromQml() == 3) {
+ if (qmlProfilerDataModel.getCurrentStateFromQml() == 3) {
getDescriptions();
}
}
diff --git a/src/plugins/qmlprofiler/qml/MainView.qml b/src/plugins/qmlprofiler/qml/MainView.qml
index 9fc2e8e44e..b810a78530 100644
--- a/src/plugins/qmlprofiler/qml/MainView.qml
+++ b/src/plugins/qmlprofiler/qml/MainView.qml
@@ -51,11 +51,16 @@ Rectangle {
property alias selectionLocked : view.selectionLocked
signal updateLockButton
property alias selectedItem: view.selectedItem
- signal selectedEventIdChanged(int eventId)
+ signal selectedEventChanged(int eventId)
property bool lockItemSelection : false
- property variant names: [ qsTr("Painting"), qsTr("Compiling"), qsTr("Creating"), qsTr("Binding"), qsTr("Handling Signal")]
- property variant colors : [ "#99CCB3", "#99CCCC", "#99B3CC", "#9999CC", "#CC99B3", "#CC99CC", "#CCCC99", "#CCB399" ]
+ property variant names: [ qsTr("Painting"),
+ qsTr("Compiling"),
+ qsTr("Creating"),
+ qsTr("Binding"),
+ qsTr("Handling Signal")]
+ property variant colors : [ "#99CCB3", "#99CCCC", "#99B3CC",
+ "#9999CC", "#CC99B3", "#CC99CC", "#CCCC99", "#CCB399" ]
property variant mainviewTimePerPixel : 0
@@ -64,9 +69,6 @@ Rectangle {
property int lineNumber: -1
property int columnNumber: 0
- property real elapsedTime
- signal updateTimer
-
signal updateRangeButton
property bool selectionRangeMode: false
@@ -77,7 +79,11 @@ Rectangle {
signal changeToolTip(string text)
signal updateVerticalScroll(int newPosition)
- property bool applicationDied : false
+ property bool recordingEnabled: false
+ property bool appKilled : false
+
+ property date recordingStartDate
+ property real elapsedTime
// ***** connections with external objects
Connections {
@@ -92,7 +98,8 @@ Rectangle {
backgroundMarks.updateMarks(startTime, endTime);
view.updateFlickRange(startTime, endTime);
if (duration > 0) {
- var candidateWidth = qmlEventList.traceDuration() * flick.width / duration;
+ var candidateWidth = qmlProfilerDataModel.traceDuration() *
+ flick.width / duration;
if (flick.contentWidth !== candidateWidth)
flick.contentWidth = candidateWidth;
}
@@ -101,20 +108,21 @@ Rectangle {
}
Connections {
- target: qmlEventList
+ target: qmlProfilerDataModel
onCountChanged: {
- eventCount = qmlEventList.count();
+ eventCount = qmlProfilerDataModel.count();
if (eventCount === 0)
root.clearAll();
if (eventCount > 1) {
root.progress = Math.min(1.0,
- (qmlEventList.lastTimeMark() - qmlEventList.traceStartTime()) / root.elapsedTime * 1e-9 );
+ (qmlProfilerDataModel.lastTimeMark() -
+ qmlProfilerDataModel.traceStartTime()) / root.elapsedTime * 1e-9 );
} else {
root.progress = 0;
}
}
onStateChanged: {
- switch (qmlEventList.getCurrentStateFromQml()) {
+ switch (qmlProfilerDataModel.getCurrentStateFromQml()) {
case 0: {
root.clearAll();
break;
@@ -133,7 +141,9 @@ Rectangle {
dataAvailable = true;
view.visible = true;
view.requestPaint();
- zoomControl.setRange(qmlEventList.traceStartTime(), qmlEventList.traceStartTime() + qmlEventList.traceDuration()/10);
+ zoomControl.setRange(qmlProfilerDataModel.traceStartTime(),
+ qmlProfilerDataModel.traceStartTime() +
+ qmlProfilerDataModel.traceDuration()/10);
break;
}
}
@@ -151,7 +161,7 @@ Rectangle {
function clearData() {
view.clearData();
dataAvailable = false;
- applicationDied = false;
+ appKilled = false;
eventCount = 0;
hideRangeDetails();
selectionRangeMode = false;
@@ -166,8 +176,6 @@ Rectangle {
function clearAll() {
clearDisplay();
- root.elapsedTime = 0;
- root.updateTimer();
}
function nextEvent() {
@@ -180,9 +188,10 @@ Rectangle {
function updateWindowLength(absoluteFactor) {
var windowLength = view.endTime - view.startTime;
- if (qmlEventList.traceEndTime() <= qmlEventList.traceStartTime() || windowLength <= 0)
+ if (qmlProfilerDataModel.traceEndTime() <= qmlProfilerDataModel.traceStartTime() ||
+ windowLength <= 0)
return;
- var currentFactor = windowLength / qmlEventList.traceDuration();
+ var currentFactor = windowLength / qmlProfilerDataModel.traceDuration();
updateZoom(absoluteFactor / currentFactor);
}
@@ -193,8 +202,8 @@ Rectangle {
windowLength = min_length;
var newWindowLength = windowLength * relativeFactor;
- if (newWindowLength > qmlEventList.traceDuration()) {
- newWindowLength = qmlEventList.traceDuration();
+ if (newWindowLength > qmlProfilerDataModel.traceDuration()) {
+ newWindowLength = qmlProfilerDataModel.traceDuration();
relativeFactor = newWindowLength / windowLength;
}
if (newWindowLength < min_length) {
@@ -205,7 +214,7 @@ Rectangle {
var fixedPoint = (view.startTime + view.endTime) / 2;
if (view.selectedItem !== -1) {
// center on selected item if it's inside the current screen
- var newFixedPoint = qmlEventList.getStartTime(view.selectedItem);
+ var newFixedPoint = qmlProfilerDataModel.getStartTime(view.selectedItem);
if (newFixedPoint >= view.startTime && newFixedPoint < view.endTime)
fixedPoint = newFixedPoint;
}
@@ -222,8 +231,8 @@ Rectangle {
windowLength = min_length;
var newWindowLength = windowLength * relativeFactor;
- if (newWindowLength > qmlEventList.traceDuration()) {
- newWindowLength = qmlEventList.traceDuration();
+ if (newWindowLength > qmlProfilerDataModel.traceDuration()) {
+ newWindowLength = qmlProfilerDataModel.traceDuration();
relativeFactor = newWindowLength / windowLength;
}
if (newWindowLength < min_length) {
@@ -241,8 +250,8 @@ Rectangle {
var newStart = Math.floor(centerPoint - windowLength/2);
if (newStart < 0)
newStart = 0;
- if (newStart + windowLength > qmlEventList.traceEndTime())
- newStart = qmlEventList.traceEndTime() - windowLength;
+ if (newStart + windowLength > qmlProfilerDataModel.traceEndTime())
+ newStart = qmlProfilerDataModel.traceEndTime() - windowLength;
zoomControl.setRange(newStart, newStart + windowLength);
}
@@ -252,17 +261,16 @@ Rectangle {
return;
// if item is outside of the view, jump back to its position
- if (qmlEventList.getEndTime(itemIndex) < view.startTime || qmlEventList.getStartTime(itemIndex) > view.endTime) {
- recenter((qmlEventList.getStartTime(itemIndex) + qmlEventList.getEndTime(itemIndex)) / 2);
+ if (qmlProfilerDataModel.getEndTime(itemIndex) < view.startTime ||
+ qmlProfilerDataModel.getStartTime(itemIndex) > view.endTime) {
+ recenter((qmlProfilerDataModel.getStartTime(itemIndex) +
+ qmlProfilerDataModel.getEndTime(itemIndex)) / 2);
}
}
- function globalZoom() {
- zoomControl.setRange(qmlEventList.traceStartTime(), qmlEventList.traceEndTime());
- }
-
function wheelZoom(wheelCenter, wheelDelta) {
- if (qmlEventList.traceEndTime() > qmlEventList.traceStartTime() && wheelDelta !== 0) {
+ if (qmlProfilerDataModel.traceEndTime() > qmlProfilerDataModel.traceStartTime() &&
+ wheelDelta !== 0) {
if (wheelDelta>0)
updateZoomCentered(wheelCenter, 1/1.2);
else
@@ -311,34 +319,22 @@ Rectangle {
onSelectedItemChanged: {
if (selectedItem != -1 && !lockItemSelection) {
lockItemSelection = true;
- selectedEventIdChanged( qmlEventList.getEventId(selectedItem) );
+ selectedEventChanged( qmlProfilerDataModel.getEventId(selectedItem) );
lockItemSelection = false;
}
}
- // ***** child items
- Timer {
- id: elapsedTimer
- property date startDate
- property bool reset: true
- running: connection.recording && connection.enabled
- repeat: true
- onRunningChanged: {
- if (running) reset = true;
- }
- interval: 100
- triggeredOnStart: true
- onTriggered: {
- if (reset) {
- startDate = new Date();
- reset = false;
- }
- var time = (new Date() - startDate)/1000;
- root.elapsedTime = time.toFixed(1);
- root.updateTimer();
+ onRecordingEnabledChanged: {
+ if (recordingEnabled) {
+ recordingStartDate = new Date();
+ elapsedTime = 0;
+ } else {
+ elapsedTime = (new Date() - recordingStartDate)/1000.0;
}
}
+
+ // ***** child items
TimeMarks {
id: backgroundMarks
y: labels.y
@@ -380,7 +376,8 @@ Rectangle {
selectionRange.isDragging = false;
}
onDoubleClicked: {
- zoomControl.setRange(selectionRange.startTime, selectionRange.startTime + selectionRange.duration);
+ zoomControl.setRange(selectionRange.startTime,
+ selectionRange.startTime + selectionRange.duration);
root.selectionRangeMode = false;
root.updateRangeButton();
}
@@ -394,10 +391,10 @@ Rectangle {
z: 2
}
- TimelineView {
+ TimelineRenderer {
id: view
- eventList: qmlEventList
+ profilerDataModel: qmlProfilerDataModel
x: flick.contentX
width: flick.width
@@ -405,9 +402,13 @@ Rectangle {
property variant startX: 0
onStartXChanged: {
- var newStartTime = Math.round(startX * (endTime - startTime) / flick.width) + qmlEventList.traceStartTime();
+ var newStartTime = Math.round(startX * (endTime - startTime) / flick.width) +
+ qmlProfilerDataModel.traceStartTime();
if (Math.abs(newStartTime - startTime) > 1) {
- var newEndTime = Math.round((startX+flick.width)* (endTime - startTime) / flick.width) + qmlEventList.traceStartTime();
+ var newEndTime = Math.round((startX+flick.width) *
+ (endTime - startTime) /
+ flick.width) +
+ qmlProfilerDataModel.traceStartTime();
zoomControl.setRange(newStartTime, newEndTime);
}
@@ -419,7 +420,8 @@ Rectangle {
if (start !== startTime || end !== endTime) {
startTime = start;
endTime = end;
- var newStartX = (startTime - qmlEventList.traceStartTime()) * flick.width / (endTime-startTime);
+ var newStartX = (startTime - qmlProfilerDataModel.traceStartTime()) *
+ flick.width / (endTime-startTime);
if (Math.abs(newStartX - startX) >= 1)
startX = newStartX;
}
@@ -428,24 +430,25 @@ Rectangle {
onSelectedItemChanged: {
if (selectedItem !== -1) {
// display details
- rangeDetails.duration = qmlEventList.getDuration(selectedItem)/1000.0;
- rangeDetails.label = qmlEventList.getDetails(selectedItem);
- rangeDetails.file = qmlEventList.getFilename(selectedItem);
- rangeDetails.line = qmlEventList.getLine(selectedItem);
- rangeDetails.column = qmlEventList.getColumn(selectedItem);
- rangeDetails.type = root.names[qmlEventList.getType(selectedItem)];
- rangeDetails.isBindingLoop = qmlEventList.getBindingLoopDest(selectedItem)!==-1;
+ rangeDetails.duration = qmlProfilerDataModel.getDuration(selectedItem)/1000.0;
+ rangeDetails.label = qmlProfilerDataModel.getDetails(selectedItem);
+ rangeDetails.file = qmlProfilerDataModel.getFilename(selectedItem);
+ rangeDetails.line = qmlProfilerDataModel.getLine(selectedItem);
+ rangeDetails.column = qmlProfilerDataModel.getColumn(selectedItem);
+ rangeDetails.type = root.names[qmlProfilerDataModel.getType(selectedItem)];
+ rangeDetails.isBindingLoop = qmlProfilerDataModel.getBindingLoopDest(selectedItem)!==-1;
rangeDetails.visible = true;
// center view (horizontally)
var windowLength = view.endTime - view.startTime;
- var eventStartTime = qmlEventList.getStartTime(selectedItem);
- var eventEndTime = eventStartTime + qmlEventList.getDuration(selectedItem);
+ var eventStartTime = qmlProfilerDataModel.getStartTime(selectedItem);
+ var eventEndTime = eventStartTime +
+ qmlProfilerDataModel.getDuration(selectedItem);
if (eventEndTime < view.startTime || eventStartTime > view.endTime) {
var center = (eventStartTime + eventEndTime)/2;
- var from = Math.min(qmlEventList.traceEndTime()-windowLength,
+ var from = Math.min(qmlProfilerDataModel.traceEndTime()-windowLength,
Math.max(0, Math.floor(center - windowLength/2)));
zoomControl.setRange(from, from + windowLength);
@@ -456,8 +459,10 @@ Rectangle {
if (itemY < root.scrollY) {
root.updateVerticalScroll(itemY);
} else
- if (itemY + root.singleRowHeight > root.scrollY + root.candidateHeight) {
- root.updateVerticalScroll(itemY + root.singleRowHeight - root.candidateHeight);
+ if (itemY + root.singleRowHeight >
+ root.scrollY + root.candidateHeight) {
+ root.updateVerticalScroll(itemY + root.singleRowHeight -
+ root.candidateHeight);
}
} else {
root.hideRangeDetails();
@@ -466,14 +471,18 @@ Rectangle {
onItemPressed: {
if (pressedItem !== -1) {
- root.gotoSourceLocation(qmlEventList.getFilename(pressedItem), qmlEventList.getLine(pressedItem), qmlEventList.getColumn(pressedItem));
+ root.gotoSourceLocation(qmlProfilerDataModel.getFilename(pressedItem),
+ qmlProfilerDataModel.getLine(pressedItem),
+ qmlProfilerDataModel.getColumn(pressedItem));
}
}
// hack to pass mouse events to the other mousearea if enabled
- startDragArea: selectionRangeDrag.enabled ? selectionRangeDrag.x : -flick.contentX
+ startDragArea: selectionRangeDrag.enabled ? selectionRangeDrag.x :
+ -flick.contentX
endDragArea: selectionRangeDrag.enabled ?
- selectionRangeDrag.x + selectionRangeDrag.width : -flick.contentX-1
+ selectionRangeDrag.x + selectionRangeDrag.width :
+ -flick.contentX-1
}
MouseArea {
id: selectionRangeControl
diff --git a/src/plugins/qmlprofiler/qml/Overview.js b/src/plugins/qmlprofiler/qml/Overview.js
index 4fe625a7fd..821f344653 100644
--- a/src/plugins/qmlprofiler/qml/Overview.js
+++ b/src/plugins/qmlprofiler/qml/Overview.js
@@ -32,7 +32,7 @@
.pragma library
-var qmlEventList = 0;
+var qmlProfilerDataModel = 0;
//draw background of the graph
function drawGraph(canvas, ctxt, region)
@@ -44,7 +44,7 @@ function drawGraph(canvas, ctxt, region)
//draw the actual data to be graphed
function drawData(canvas, ctxt, region)
{
- if ((!qmlEventList) || qmlEventList.count() == 0)
+ if ((!qmlProfilerDataModel) || qmlProfilerDataModel.count() == 0)
return;
var typeCount = 5;
@@ -53,17 +53,18 @@ function drawData(canvas, ctxt, region)
var height = canvas.height - bump;
var blockHeight = height / typeCount;
- var spacing = width / qmlEventList.traceDuration();
+ var spacing = width / qmlProfilerDataModel.traceDuration();
var highest = [0,0,0,0,0]; // note: change if typeCount changes
- for (var ii = 0; ii < qmlEventList.count(); ++ii) {
+ for (var ii = 0; ii < qmlProfilerDataModel.count(); ++ii) {
- var xx = (qmlEventList.getStartTime(ii) - qmlEventList.traceStartTime()) * spacing;
+ var xx = (qmlProfilerDataModel.getStartTime(ii) -
+ qmlProfilerDataModel.traceStartTime()) * spacing;
if (xx > region.x + region.width)
continue;
- var eventWidth = qmlEventList.getDuration(ii) * spacing;
+ var eventWidth = qmlProfilerDataModel.getDuration(ii) * spacing;
if (xx + eventWidth < region.x)
continue;
@@ -71,24 +72,26 @@ function drawData(canvas, ctxt, region)
eventWidth = 1;
xx = Math.round(xx);
- var ty = qmlEventList.getType(ii);
+ var ty = qmlProfilerDataModel.getType(ii);
if (xx + eventWidth > highest[ty]) {
// special: animations
- if (ty === 0 && qmlEventList.getAnimationCount(ii) >= 0) {
- var vertScale = qmlEventList.getMaximumAnimationCount() - qmlEventList.getMinimumAnimationCount();
+ if (ty === 0 && qmlProfilerDataModel.getAnimationCount(ii) >= 0) {
+ var vertScale = qmlProfilerDataModel.getMaximumAnimationCount() -
+ qmlProfilerDataModel.getMinimumAnimationCount();
if (vertScale < 1)
vertScale = 1;
- var fraction = (qmlEventList.getAnimationCount(ii) - qmlEventList.getMinimumAnimationCount()) / vertScale;
+ var fraction = (qmlProfilerDataModel.getAnimationCount(ii) -
+ qmlProfilerDataModel.getMinimumAnimationCount()) / vertScale;
var eventHeight = blockHeight * (fraction * 0.85 + 0.15);
var yy = bump + ty*blockHeight + blockHeight - eventHeight;
- var fpsFraction = qmlEventList.getFramerate(ii) / 60.0;
+ var fpsFraction = qmlProfilerDataModel.getFramerate(ii) / 60.0;
if (fpsFraction > 1.0)
fpsFraction = 1.0;
ctxt.fillStyle = "hsl("+(fpsFraction*0.27+0.028)+",0.3,0.65)";
ctxt.fillRect(xx, yy, eventWidth, eventHeight);
} else {
- var hue = ( qmlEventList.getEventId(ii) * 25 ) % 360;
+ var hue = ( qmlProfilerDataModel.getEventId(ii) * 25 ) % 360;
ctxt.fillStyle = "hsl("+(hue/360.0+0.001)+",0.3,0.65)";
ctxt.fillRect(xx, bump + ty*blockHeight, eventWidth, blockHeight);
}
@@ -100,12 +103,13 @@ function drawData(canvas, ctxt, region)
ctxt.strokeStyle = "orange";
ctxt.lineWidth = 2;
var radius = 1;
- for (var ii = 0; ii < qmlEventList.count(); ++ii) {
- if (qmlEventList.getBindingLoopDest(ii) >= 0) {
- var xcenter = Math.round(qmlEventList.getStartTime(ii) +
- qmlEventList.getDuration(ii) -
- qmlEventList.traceStartTime()) * spacing;
- var ycenter = Math.round(bump + qmlEventList.getType(ii) * blockHeight + blockHeight/2);
+ for (var ii = 0; ii < qmlProfilerDataModel.count(); ++ii) {
+ if (qmlProfilerDataModel.getBindingLoopDest(ii) >= 0) {
+ var xcenter = Math.round(qmlProfilerDataModel.getStartTime(ii) +
+ qmlProfilerDataModel.getDuration(ii) -
+ qmlProfilerDataModel.traceStartTime()) * spacing;
+ var ycenter = Math.round(bump + qmlProfilerDataModel.getType(ii) *
+ blockHeight + blockHeight/2);
ctxt.arc(xcenter, ycenter, radius, 0, 2*Math.PI, true);
ctxt.stroke();
}
@@ -114,19 +118,20 @@ function drawData(canvas, ctxt, region)
function drawTimeBar(canvas, ctxt, region)
{
- if (!qmlEventList)
+ if (!qmlProfilerDataModel)
return;
var width = canvas.width;
var height = 10;
- var startTime = qmlEventList.traceStartTime();
- var endTime = qmlEventList.traceEndTime();
+ var startTime = qmlProfilerDataModel.traceStartTime();
+ var endTime = qmlProfilerDataModel.traceEndTime();
- var totalTime = qmlEventList.traceDuration();
+ var totalTime = qmlProfilerDataModel.traceDuration();
var spacing = width / totalTime;
var initialBlockLength = 120;
- var timePerBlock = Math.pow(2, Math.floor( Math.log( totalTime / width * initialBlockLength ) / Math.LN2 ) );
+ var timePerBlock = Math.pow(2, Math.floor( Math.log( totalTime / width *
+ initialBlockLength ) / Math.LN2 ) );
var pixelsPerBlock = timePerBlock * spacing;
var pixelsPerSection = pixelsPerBlock / 5;
var blockCount = width / pixelsPerBlock;
diff --git a/src/plugins/qmlprofiler/qml/Overview.qml b/src/plugins/qmlprofiler/qml/Overview.qml
index 5526502169..c82277bc26 100644
--- a/src/plugins/qmlprofiler/qml/Overview.qml
+++ b/src/plugins/qmlprofiler/qml/Overview.qml
@@ -51,8 +51,8 @@ Canvas2D {
}
function updateRange() {
- var newStartTime = Math.round(rangeMover.x * qmlEventList.traceDuration() / width) + qmlEventList.traceStartTime();
- var newEndTime = Math.round((rangeMover.x + rangeMover.width) * qmlEventList.traceDuration() / width) + qmlEventList.traceStartTime();
+ var newStartTime = Math.round(rangeMover.x * qmlProfilerDataModel.traceDuration() / width) + qmlProfilerDataModel.traceStartTime();
+ var newEndTime = Math.round((rangeMover.x + rangeMover.width) * qmlProfilerDataModel.traceDuration() / width) + qmlProfilerDataModel.traceStartTime();
if (startTime !== newStartTime || endTime !== newEndTime) {
zoomControl.setRange(newStartTime, newEndTime);
}
@@ -62,13 +62,13 @@ Canvas2D {
Connections {
target: zoomControl
onRangeChanged: {
- if (qmlEventList) {
+ if (qmlProfilerDataModel) {
startTime = zoomControl.startTime();
endTime = zoomControl.endTime();
- var newRangeX = (startTime - qmlEventList.traceStartTime()) * width / qmlEventList.traceDuration();
+ var newRangeX = (startTime - qmlProfilerDataModel.traceStartTime()) * width / qmlProfilerDataModel.traceDuration();
if (rangeMover.x !== newRangeX)
rangeMover.x = newRangeX;
- var newWidth = (endTime-startTime) * width / qmlEventList.traceDuration();
+ var newWidth = (endTime-startTime) * width / qmlProfilerDataModel.traceDuration();
if (rangeMover.width !== newWidth)
rangeMover.width = newWidth;
}
@@ -76,10 +76,10 @@ Canvas2D {
}
Connections {
- target: qmlEventList
+ target: qmlProfilerDataModel
onStateChanged: {
// State is "done"
- if (qmlEventList.getCurrentStateFromQml() == 3) {
+ if (qmlProfilerDataModel.getCurrentStateFromQml() == 3) {
dataAvailable = true;
requestRedraw();
}
@@ -88,7 +88,7 @@ Canvas2D {
// ***** slots
onDrawRegion: {
- Plotter.qmlEventList = qmlEventList;
+ Plotter.qmlProfilerDataModel = qmlProfilerDataModel;
if (dataAvailable) {
Plotter.plot(canvas, ctxt, region);
} else {
diff --git a/src/plugins/qmlprofiler/qml/SelectionRange.qml b/src/plugins/qmlprofiler/qml/SelectionRange.qml
index 616fd89598..b8b6507258 100644
--- a/src/plugins/qmlprofiler/qml/SelectionRange.qml
+++ b/src/plugins/qmlprofiler/qml/SelectionRange.qml
@@ -50,7 +50,7 @@ Rectangle {
property string endTimeString: detailedPrintTime(startTime+duration)
property string durationString: detailedPrintTime(duration)
- property variant startTime: x * selectionRange.viewTimePerPixel + qmlEventList.traceStartTime()
+ property variant startTime: x * selectionRange.viewTimePerPixel + qmlProfilerDataModel.traceStartTime()
property variant duration: width * selectionRange.viewTimePerPixel
property variant viewTimePerPixel: 1
property variant creationState : 0
diff --git a/src/plugins/qmlprofiler/qml/StatusDisplay.qml b/src/plugins/qmlprofiler/qml/StatusDisplay.qml
index d4507a37d6..8919d07b13 100644
--- a/src/plugins/qmlprofiler/qml/StatusDisplay.qml
+++ b/src/plugins/qmlprofiler/qml/StatusDisplay.qml
@@ -69,7 +69,7 @@ Item {
states: [
// no data available
State {
- when: (root.eventCount == 0) && !elapsedTimer.running
+ when: (root.eventCount == 0) && !root.recordingEnabled
PropertyChanges {
target: statusDisplay
visible: true
@@ -85,7 +85,7 @@ Item {
},
// running app
State {
- when: elapsedTimer.running
+ when: root.recordingEnabled
PropertyChanges {
target: statusDisplay
visible: true
@@ -99,7 +99,7 @@ Item {
// loading data
State {
name: "loading"
- when: (!root.dataAvailable) && (root.eventCount > 0) && !root.applicationDied
+ when: !root.dataAvailable && (root.eventCount > 0) && !root.appKilled
PropertyChanges {
target: statusDisplay
visible: true
@@ -118,7 +118,7 @@ Item {
// application died
State {
name: "deadApp"
- when: (!root.dataAvailable) && (root.eventCount > 0) && root.applicationDied
+ when: !root.dataAvailable && (root.eventCount > 0) && root.appKilled
PropertyChanges {
target: statusDisplay
visible: true
diff --git a/src/plugins/qmlprofiler/qml/TimeMarks.qml b/src/plugins/qmlprofiler/qml/TimeMarks.qml
index 2cb176c0a1..d38e4de741 100644
--- a/src/plugins/qmlprofiler/qml/TimeMarks.qml
+++ b/src/plugins/qmlprofiler/qml/TimeMarks.qml
@@ -91,15 +91,15 @@ Canvas2D {
// gray off out-of-bounds areas
var rectWidth;
- if (startTime < qmlEventList.traceStartTime()) {
+ if (startTime < qmlProfilerDataModel.traceStartTime()) {
ctxt.fillStyle = "rgba(127,127,127,0.2)";
- rectWidth = (qmlEventList.traceStartTime() - startTime) * spacing;
+ rectWidth = (qmlProfilerDataModel.traceStartTime() - startTime) * spacing;
ctxt.fillRect(0, 0, rectWidth, height);
}
- if (endTime > qmlEventList.traceEndTime()) {
+ if (endTime > qmlProfilerDataModel.traceEndTime()) {
ctxt.fillStyle = "rgba(127,127,127,0.2)";
- var rectX = (qmlEventList.traceEndTime() - startTime) * spacing;
- rectWidth = (endTime - qmlEventList.traceEndTime()) * spacing;
+ var rectX = (qmlProfilerDataModel.traceEndTime() - startTime) * spacing;
+ rectWidth = (endTime - qmlProfilerDataModel.traceEndTime()) * spacing;
ctxt.fillRect(rectX, 0, rectWidth, height);
}
}
@@ -126,8 +126,8 @@ Canvas2D {
var cumulatedHeight = 0;
for (var i=0; i<labels.rowCount; i++) {
cumulatedHeight += root.singleRowHeight + (labels.rowExpanded[i] ?
- qmlEventList.uniqueEventsOfType(i) * root.singleRowHeight :
- qmlEventList.maxNestingForType(i) * root.singleRowHeight);
+ qmlProfilerDataModel.uniqueEventsOfType(i) * root.singleRowHeight :
+ qmlProfilerDataModel.maxNestingForType(i) * root.singleRowHeight);
ctxt.strokeStyle = "#B0B0B0";
ctxt.beginPath();
diff --git a/src/plugins/qmlprofiler/qmlprofiler.pro b/src/plugins/qmlprofiler/qmlprofiler.pro
index 7403b1fb68..4dc919bef2 100644
--- a/src/plugins/qmlprofiler/qmlprofiler.pro
+++ b/src/plugins/qmlprofiler/qmlprofiler.pro
@@ -24,14 +24,19 @@ SOURCES += \
qmlprofilerplugin.cpp \
qmlprofilertool.cpp \
qmlprofilerengine.cpp \
- tracewindow.cpp \
- timelineview.cpp \
qmlprofilerattachdialog.cpp \
localqmlprofilerrunner.cpp \
codaqmlprofilerrunner.cpp \
remotelinuxqmlprofilerrunner.cpp \
qmlprofilereventview.cpp \
- qmlprofilerdetailsrewriter.cpp
+ qmlprofilerdetailsrewriter.cpp \
+ qmlprofilertraceview.cpp \
+ timelinerenderer.cpp \
+ qmlprofilerstatemanager.cpp \
+ qv8profilerdatamodel.cpp \
+ qmlprofilerdatamodel.cpp \
+ qmlprofilerclientmanager.cpp \
+ qmlprofilerviewmanager.cpp
HEADERS += \
qmlprofilerconstants.h \
@@ -39,22 +44,26 @@ HEADERS += \
qmlprofilerplugin.h \
qmlprofilertool.h \
qmlprofilerengine.h \
- tracewindow.h \
- timelineview.h \
qmlprofilerattachdialog.h \
abstractqmlprofilerrunner.h \
localqmlprofilerrunner.h \
codaqmlprofilerrunner.h \
remotelinuxqmlprofilerrunner.h \
qmlprofilereventview.h \
- qmlprofilerdetailsrewriter.h
+ qmlprofilerdetailsrewriter.h \
+ qmlprofilertraceview.h \
+ timelinerenderer.h \
+ qmlprofilerstatemanager.h \
+ qv8profilerdatamodel.h \
+ qmlprofilerdatamodel.h \
+ qmlprofilerclientmanager.h \
+ qmlprofilerviewmanager.h
RESOURCES += \
qml/qmlprofiler.qrc
OTHER_FILES += \
qml/Detail.qml \
- qml/Elapsed.qml \
qml/Label.qml \
qml/MainView.qml \
qml/RangeDetails.qml \
@@ -64,8 +73,7 @@ OTHER_FILES += \
qml/StatusDisplay.qml \
qml/SelectionRange.qml \
qml/SelectionRangeDetails.qml \
- qml/Overview.qml \
- qml/Overview.js
+ qml/Overview.qml
FORMS += \
qmlprofilerattachdialog.ui
diff --git a/src/plugins/qmlprofiler/qmlprofiler.qbs b/src/plugins/qmlprofiler/qmlprofiler.qbs
index 13cbca838c..f4af4e4366 100644
--- a/src/plugins/qmlprofiler/qmlprofiler.qbs
+++ b/src/plugins/qmlprofiler/qmlprofiler.qbs
@@ -35,27 +35,37 @@ QtcPlugin {
"codaqmlprofilerrunner.h",
"localqmlprofilerrunner.cpp",
"localqmlprofilerrunner.h",
- "qmlprofiler_global.h",
"qmlprofilerattachdialog.cpp",
"qmlprofilerattachdialog.h",
"qmlprofilerattachdialog.ui",
+ "qmlprofilerclientmanager.cpp",
+ "qmlprofilerclientmanager.h",
"qmlprofilerconstants.h",
+ "qmlprofilerdatamodel.cpp",
+ "qmlprofilerdatamodel.h",
"qmlprofilerdetailsrewriter.cpp",
"qmlprofilerdetailsrewriter.h",
"qmlprofilerengine.cpp",
"qmlprofilerengine.h",
"qmlprofilereventview.cpp",
"qmlprofilereventview.h",
+ "qmlprofiler_global.h",
"qmlprofilerplugin.cpp",
"qmlprofilerplugin.h",
+ "qmlprofilerstatemanager.cpp",
+ "qmlprofilerstatemanager.h",
"qmlprofilertool.cpp",
"qmlprofilertool.h",
+ "qmlprofilertraceview.cpp",
+ "qmlprofilertraceview.h",
+ "qmlprofilerviewmanager.cpp",
+ "qmlprofilerviewmanager.h",
+ "qv8profilerdatamodel.cpp",
+ "qv8profilerdatamodel.h",
"remotelinuxqmlprofilerrunner.cpp",
"remotelinuxqmlprofilerrunner.h",
- "timelineview.cpp",
- "timelineview.h",
- "tracewindow.cpp",
- "tracewindow.h",
+ "timelinerenderer.cpp",
+ "timelinerenderer.h",
"canvas/qdeclarativecanvas.cpp",
"canvas/qdeclarativecanvas_p.h",
"canvas/qdeclarativecanvastimer.cpp",
@@ -75,7 +85,8 @@ QtcPlugin {
"qml/StatusDisplay.qml",
"qml/TimeDisplay.qml",
"qml/TimeMarks.qml",
- "qml/qmlprofiler.qrc"
+ "qml/qmlprofiler.qrc",
+ "qml/Overview.js"
]
}
diff --git a/src/plugins/qmlprofiler/qmlprofilerclientmanager.cpp b/src/plugins/qmlprofiler/qmlprofilerclientmanager.cpp
new file mode 100644
index 0000000000..01a0d787a2
--- /dev/null
+++ b/src/plugins/qmlprofiler/qmlprofilerclientmanager.cpp
@@ -0,0 +1,426 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** 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 qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "qmlprofilerclientmanager.h"
+#include "qmlprofilertool.h"
+#include "qmlprofilerplugin.h"
+
+#include <qmljsdebugclient/qdeclarativedebugclient.h>
+#include <qmljsdebugclient/qmlprofilertraceclient.h>
+#include <qmljsdebugclient/qv8profilerclient.h>
+
+#include <utils/qtcassert.h>
+#include <QWeakPointer>
+#include <QTimer>
+#include <QMessageBox>
+
+using namespace QmlJsDebugClient;
+using namespace Core;
+
+namespace QmlProfiler {
+namespace Internal {
+
+class QmlProfilerClientManager::QmlProfilerClientManagerPrivate {
+public:
+ QmlProfilerClientManagerPrivate(QmlProfilerClientManager *qq) { Q_UNUSED(qq); }
+
+ QmlProfilerStateManager* profilerState;
+
+ QDeclarativeDebugConnection *connection;
+ QWeakPointer<QmlProfilerTraceClient> qmlclientplugin;
+ QWeakPointer<QV8ProfilerClient> v8clientplugin;
+
+ QTimer connectionTimer;
+ int connectionAttempts;
+
+ enum ConnectMode {
+ TcpConnection, OstConnection
+ };
+ ConnectMode connectMode;
+ QString tcpHost;
+ quint64 tcpPort;
+ QString ostDevice;
+ QString sysroot;
+
+ bool v8DataReady;
+ bool qmlDataReady;
+};
+
+QmlProfilerClientManager::QmlProfilerClientManager(QObject *parent) :
+ QObject(parent), d(new QmlProfilerClientManagerPrivate(this))
+{
+ setObjectName("QML Profiler Connections");
+
+ d->profilerState = 0;
+
+ d->connection = 0;
+ d->connectionAttempts = 0;
+ d->v8DataReady = false;
+ d->qmlDataReady = false;
+
+ d->connectionTimer.setInterval(200);
+ connect(&d->connectionTimer, SIGNAL(timeout()), SLOT(tryToConnect()));
+}
+
+QmlProfilerClientManager::~QmlProfilerClientManager()
+{
+ disconnectClientSignals();
+ delete d->connection;
+ delete d->qmlclientplugin.data();
+ delete d->v8clientplugin.data();
+
+ delete d;
+}
+////////////////////////////////////////////////////////////////
+// Interface
+void QmlProfilerClientManager::setTcpConnection(QString host, quint64 port)
+{
+ d->connectMode = QmlProfilerClientManagerPrivate::TcpConnection;
+ d->tcpHost = host;
+ d->tcpPort = port;
+}
+
+void QmlProfilerClientManager::setOstConnection(QString ostDevice)
+{
+ d->connectMode = QmlProfilerClientManagerPrivate::OstConnection;
+ d->ostDevice = ostDevice;
+}
+
+void QmlProfilerClientManager::clearBufferedData()
+{
+ if (d->qmlclientplugin)
+ d->qmlclientplugin.data()->clearData();
+ if (d->v8clientplugin)
+ d->v8clientplugin.data()->clearData();
+}
+
+////////////////////////////////////////////////////////////////
+// Internal
+void QmlProfilerClientManager::connectClient(quint16 port)
+{
+ if (d->connection)
+ delete d->connection;
+ d->connection = new QDeclarativeDebugConnection;
+ enableServices();
+ connect(d->connection, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
+ this, SLOT(connectionStateChanged()));
+ d->connectionTimer.start();
+ d->tcpPort = port;
+}
+
+void QmlProfilerClientManager::enableServices()
+{
+ QTC_ASSERT(d->profilerState, return);
+
+ disconnectClientSignals();
+ d->profilerState->setServerRecording(false); // false by default (will be set to true when connected)
+ delete d->qmlclientplugin.data();
+ d->qmlclientplugin = new QmlProfilerTraceClient(d->connection);
+ delete d->v8clientplugin.data();
+ d->v8clientplugin = new QV8ProfilerClient(d->connection);
+ connectClientSignals();
+}
+
+void QmlProfilerClientManager::connectClientSignals()
+{
+ QTC_ASSERT(d->profilerState, return);
+ if (d->qmlclientplugin) {
+ connect(d->qmlclientplugin.data(), SIGNAL(complete()),
+ this, SLOT(qmlComplete()));
+ connect(d->qmlclientplugin.data(),
+ SIGNAL(range(int,qint64,qint64,QStringList,QmlJsDebugClient::QmlEventLocation)),
+ this,
+ SIGNAL(addRangedEvent(int,qint64,qint64,QStringList,QmlJsDebugClient::QmlEventLocation)));
+ connect(d->qmlclientplugin.data(), SIGNAL(traceFinished(qint64)),
+ this, SIGNAL(traceFinished(qint64)));
+ connect(d->qmlclientplugin.data(), SIGNAL(traceStarted(qint64)),
+ this, SIGNAL(traceStarted(qint64)));
+ connect(d->qmlclientplugin.data(), SIGNAL(frame(qint64,int,int)),
+ this, SIGNAL(addFrameEvent(qint64,int,int)));
+ connect(d->qmlclientplugin.data(), SIGNAL(enabledChanged()),
+ d->qmlclientplugin.data(), SLOT(sendRecordingStatus()));
+ // fixme: this should be unified for both clients
+ connect(d->qmlclientplugin.data(), SIGNAL(recordingChanged(bool)),
+ d->profilerState, SLOT(setServerRecording(bool)));
+ }
+ if (d->v8clientplugin) {
+ connect(d->v8clientplugin.data(), SIGNAL(complete()), this, SLOT(v8Complete()));
+ connect(d->v8clientplugin.data(),
+ SIGNAL(v8range(int,QString,QString,int,double,double)),
+ this,
+ SIGNAL(addV8Event(int,QString,QString,int,double,double)));
+ connect(d->v8clientplugin.data(), SIGNAL(enabledChanged()),
+ d->v8clientplugin.data(), SLOT(sendRecordingStatus()));
+ }
+}
+
+void QmlProfilerClientManager::disconnectClientSignals()
+{
+ if (d->qmlclientplugin) {
+ disconnect(d->qmlclientplugin.data(), SIGNAL(complete()),
+ this, SLOT(qmlComplete()));
+ disconnect(d->qmlclientplugin.data(),
+ SIGNAL(range(int,qint64,qint64,QStringList,QmlJsDebugClient::QmlEventLocation)),
+ this,
+ SIGNAL(addRangedEvent(int,qint64,qint64,QStringList,QmlJsDebugClient::QmlEventLocation)));
+ disconnect(d->qmlclientplugin.data(), SIGNAL(traceFinished(qint64)),
+ this, SIGNAL(traceFinished(qint64)));
+ disconnect(d->qmlclientplugin.data(), SIGNAL(traceStarted(qint64)),
+ this, SIGNAL(traceStarted(qint64)));
+ disconnect(d->qmlclientplugin.data(), SIGNAL(frame(qint64,int,int)),
+ this, SIGNAL(addFrameEvent(qint64,int,int)));
+ disconnect(d->qmlclientplugin.data(), SIGNAL(enabledChanged()),
+ d->qmlclientplugin.data(), SLOT(sendRecordingStatus()));
+ // fixme: this should be unified for both clients
+ disconnect(d->qmlclientplugin.data(), SIGNAL(recordingChanged(bool)),
+ d->profilerState, SLOT(setServerRecording(bool)));
+ }
+ if (d->v8clientplugin) {
+ disconnect(d->v8clientplugin.data(), SIGNAL(complete()), this, SLOT(v8Complete()));
+ disconnect(d->v8clientplugin.data(),
+ SIGNAL(v8range(int,QString,QString,int,double,double)),
+ this,
+ SIGNAL(addV8Event(int,QString,QString,int,double,double)));
+ disconnect(d->v8clientplugin.data(), SIGNAL(enabledChanged()),
+ d->v8clientplugin.data(), SLOT(sendRecordingStatus()));
+ }
+}
+
+void QmlProfilerClientManager::connectToClient()
+{
+ if (!d->connection || d->connection->state() != QAbstractSocket::UnconnectedState)
+ return;
+
+ if (d->connectMode == QmlProfilerClientManagerPrivate::TcpConnection) {
+ QmlProfilerTool::logStatus(QString("QML Profiler: Connecting to %1:%2 ...").arg(d->tcpHost, QString::number(d->tcpPort)));
+ d->connection->connectToHost(d->tcpHost, d->tcpPort);
+ } else {
+ QmlProfilerTool::logStatus(QString("QML Profiler: Connecting to %1 ...").arg(d->tcpHost));
+ d->connection->connectToOst(d->ostDevice);
+ }
+}
+
+void QmlProfilerClientManager::disconnectClient()
+{
+ // this might be actually be called indirectly by QDDConnectionPrivate::readyRead(), therefore allow
+ // method to complete before deleting object
+ if (d->connection) {
+ d->connection->deleteLater();
+ d->connection = 0;
+ }
+}
+
+void QmlProfilerClientManager::tryToConnect()
+{
+ ++d->connectionAttempts;
+
+ if (d->connection && d->connection->isConnected()) {
+ d->connectionTimer.stop();
+ d->connectionAttempts = 0;
+ } else if (d->connectionAttempts == 50) {
+ d->connectionTimer.stop();
+ d->connectionAttempts = 0;
+
+ QMessageBox *infoBox = QmlProfilerTool::requestMessageBox();
+ infoBox->setIcon(QMessageBox::Critical);
+ infoBox->setWindowTitle(tr("Qt Creator"));
+ infoBox->setText(tr("Could not connect to the in-process QML profiler.\n"
+ "Do you want to retry?"));
+ infoBox->setStandardButtons(QMessageBox::Retry |
+ QMessageBox::Cancel |
+ QMessageBox::Help);
+ infoBox->setDefaultButton(QMessageBox::Retry);
+ infoBox->setModal(true);
+
+ connect(infoBox, SIGNAL(finished(int)),
+ this, SLOT(retryMessageBoxFinished(int)));
+
+ infoBox->show();
+ } else {
+ connectToClient();
+ }
+}
+
+void QmlProfilerClientManager::connectionStateChanged()
+{
+ if (!d->connection)
+ return;
+ switch (d->connection->state()) {
+ case QAbstractSocket::UnconnectedState:
+ {
+ if (QmlProfilerPlugin::debugOutput)
+ qWarning("QML Profiler: disconnected");
+ break;
+ }
+ case QAbstractSocket::HostLookupState:
+ break;
+ case QAbstractSocket::ConnectingState: {
+ if (QmlProfilerPlugin::debugOutput)
+ qWarning("QML Profiler: Connecting to debug server ...");
+ break;
+ }
+ case QAbstractSocket::ConnectedState:
+ {
+ if (QmlProfilerPlugin::debugOutput)
+ qWarning("QML Profiler: connected and running");
+ // notify the client recording status
+ clientRecordingChanged();
+ break;
+ }
+ case QAbstractSocket::ClosingState:
+ if (QmlProfilerPlugin::debugOutput)
+ qWarning("QML Profiler: closing ...");
+ break;
+ case QAbstractSocket::BoundState:
+ case QAbstractSocket::ListeningState:
+ break;
+ }
+}
+
+void QmlProfilerClientManager::retryMessageBoxFinished(int result)
+{
+ switch (result) {
+ case QMessageBox::Retry: {
+ d->connectionAttempts = 0;
+ d->connectionTimer.start();
+ break;
+ }
+ case QMessageBox::Help: {
+ QmlProfilerTool::handleHelpRequest(QString("qthelp://com.nokia.qtcreator/doc/creator-debugging-qml.html"));
+ // fall through
+ }
+ default: {
+ if (d->connection) {
+ QmlProfilerTool::logStatus("QML Profiler: Failed to connect! " + d->connection->errorString());
+ } else {
+ QmlProfilerTool::logStatus("QML Profiler: Failed to connect!");
+ }
+
+ emit connectionFailed();
+ break;
+ }
+ }
+}
+
+void QmlProfilerClientManager::qmlComplete()
+{
+ d->qmlDataReady = true;
+ if (!d->v8clientplugin || d->v8clientplugin.data()->status() != QDeclarativeDebugClient::Enabled || d->v8DataReady) {
+ emit dataReadyForProcessing();
+ // once complete is sent, reset the flags
+ d->qmlDataReady = false;
+ d->v8DataReady = false;
+ }
+}
+
+void QmlProfilerClientManager::v8Complete()
+{
+ d->v8DataReady = true;
+ if (!d->qmlclientplugin || d->qmlclientplugin.data()->status() != QDeclarativeDebugClient::Enabled || d->qmlDataReady) {
+ emit dataReadyForProcessing();
+ // once complete is sent, reset the flags
+ d->v8DataReady = false;
+ d->qmlDataReady = false;
+ }
+}
+
+void QmlProfilerClientManager::stopClientsRecording()
+{
+ if (d->qmlclientplugin)
+ d->qmlclientplugin.data()->setRecording(false);
+ if (d->v8clientplugin)
+ d->v8clientplugin.data()->setRecording(false);
+}
+
+////////////////////////////////////////////////////////////////
+// Profiler State
+void QmlProfilerClientManager::registerProfilerStateManager( QmlProfilerStateManager *profilerState )
+{
+ if (d->profilerState) {
+ disconnect(d->profilerState, SIGNAL(stateChanged()),
+ this, SLOT(profilerStateChanged()));
+ disconnect(d->profilerState, SIGNAL(clientRecordingChanged()),
+ this, SLOT(clientRecordingChanged()));
+ disconnect(d->profilerState, SIGNAL(serverRecordingChanged()),
+ this, SLOT(serverRecordingChanged()));
+ }
+
+ d->profilerState = profilerState;
+
+ // connect
+ if (d->profilerState) {
+ connect(d->profilerState, SIGNAL(stateChanged()),
+ this, SLOT(profilerStateChanged()));
+ connect(d->profilerState, SIGNAL(clientRecordingChanged()),
+ this, SLOT(clientRecordingChanged()));
+ connect(d->profilerState, SIGNAL(serverRecordingChanged()),
+ this, SLOT(serverRecordingChanged()));
+ }
+}
+
+void QmlProfilerClientManager::profilerStateChanged()
+{
+ QTC_ASSERT(d->profilerState, return);
+ switch (d->profilerState->currentState()) {
+ case QmlProfilerStateManager::AppStopRequested :
+ if (d->profilerState->serverRecording()) {
+ stopClientsRecording();
+ }
+ else
+ d->profilerState->setCurrentState(QmlProfilerStateManager::AppReadyToStop);
+ break;
+ default:
+ break;
+ }
+}
+
+void QmlProfilerClientManager::clientRecordingChanged()
+{
+ QTC_ASSERT(d->profilerState, return);
+ if (d->profilerState->currentState() == QmlProfilerStateManager::AppRunning) {
+ if (d->qmlclientplugin)
+ d->qmlclientplugin.data()->setRecording(d->profilerState->clientRecording());
+ if (d->v8clientplugin)
+ d->v8clientplugin.data()->setRecording(d->profilerState->clientRecording());
+ }
+}
+
+void QmlProfilerClientManager::serverRecordingChanged()
+{
+ if (d->profilerState->serverRecording()) {
+ d->v8DataReady = false;
+ d->qmlDataReady = false;
+ }
+}
+
+} // namespace Internal
+} // namespace QmlProfiler
diff --git a/src/plugins/qmlprofiler/qmlprofilerclientmanager.h b/src/plugins/qmlprofiler/qmlprofilerclientmanager.h
new file mode 100644
index 0000000000..35766e7a5a
--- /dev/null
+++ b/src/plugins/qmlprofiler/qmlprofilerclientmanager.h
@@ -0,0 +1,102 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** 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 qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef QMLPROFILERCLIENTMANAGER_H
+#define QMLPROFILERCLIENTMANAGER_H
+
+#include <QObject>
+#include <QStringList>
+
+#include "qmlprofilerstatemanager.h"
+#include <qmljsdebugclient/qmlprofilereventlocation.h>
+
+namespace QmlProfiler {
+namespace Internal {
+
+class QmlProfilerClientManager : public QObject
+{
+ Q_OBJECT
+public:
+ explicit QmlProfilerClientManager(QObject *parent = 0);
+ ~QmlProfilerClientManager();
+
+ void registerProfilerStateManager(QmlProfilerStateManager *profilerState);
+
+ void setTcpConnection(QString host, quint64 port);
+ void setOstConnection(QString ostDevice);
+
+ void clearBufferedData();
+
+signals:
+ void connectionFailed();
+
+ // data
+ void addRangedEvent(int,qint64,qint64,QStringList,QmlJsDebugClient::QmlEventLocation);
+ void addV8Event(int,QString,QString,int,double,double);
+ void addFrameEvent(qint64,int,int);
+ void traceStarted(qint64);
+ void traceFinished(qint64);
+ void dataReadyForProcessing();
+
+public slots:
+ void connectClient(quint16 port);
+ void disconnectClient();
+
+private slots:
+ void tryToConnect();
+ void connectionStateChanged();
+ void retryMessageBoxFinished(int result);
+
+ void qmlComplete();
+ void v8Complete();
+
+ void profilerStateChanged();
+ void clientRecordingChanged();
+ void serverRecordingChanged();
+
+private:
+ class QmlProfilerClientManagerPrivate;
+ QmlProfilerClientManagerPrivate *d;
+
+ void connectToClient();
+
+ void enableServices();
+ void connectClientSignals();
+ void disconnectClientSignals();
+
+ void stopClientsRecording();
+};
+
+}
+}
+
+#endif // QMLPROFILERCLIENTMANAGER_H
diff --git a/src/plugins/qmlprofiler/qmlprofilerconstants.h b/src/plugins/qmlprofiler/qmlprofilerconstants.h
index 38a47c11f1..fec1b6b539 100644
--- a/src/plugins/qmlprofiler/qmlprofilerconstants.h
+++ b/src/plugins/qmlprofiler/qmlprofilerconstants.h
@@ -37,6 +37,7 @@ namespace QmlProfiler {
namespace Constants {
const char ATTACH[] = "Menu.Analyzer.Attach";
+const char TraceFileExtension[] = ".qtd";
} // namespace Constants
} // namespace QmlProfiler
diff --git a/src/plugins/qmlprofiler/qmlprofilerdatamodel.cpp b/src/plugins/qmlprofiler/qmlprofilerdatamodel.cpp
new file mode 100644
index 0000000000..750bbb0f0d
--- /dev/null
+++ b/src/plugins/qmlprofiler/qmlprofilerdatamodel.cpp
@@ -0,0 +1,1652 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** 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 qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "qmlprofilerdatamodel.h"
+
+#include <QUrl>
+#include <QHash>
+#include <QtAlgorithms>
+#include <QString>
+#include <QStringList>
+
+#include <QFile>
+#include <QXmlStreamReader>
+#include <QXmlStreamWriter>
+
+#include <QTimer>
+#include <utils/qtcassert.h>
+
+using namespace QmlJsDebugClient;
+
+namespace QmlProfiler {
+namespace Internal {
+
+///////////////////////////////////////////////////////////
+QmlRangeEventData::QmlRangeEventData()
+{
+ eventType = MaximumQmlEventType;
+ eventId = -1;
+ duration = 0;
+ calls = 0;
+ minTime = 0;
+ maxTime = 0;
+ timePerCall = 0;
+ percentOfTime = 0;
+ medianTime = 0;
+ isBindingLoop = false;
+}
+
+QmlRangeEventData::~QmlRangeEventData()
+{
+ qDeleteAll(parentHash.values());
+ parentHash.clear();
+ qDeleteAll(childrenHash.values());
+ childrenHash.clear();
+}
+
+QmlRangeEventData &QmlRangeEventData::operator=(const QmlRangeEventData &ref)
+{
+ if (this == &ref)
+ return *this;
+
+ displayName = ref.displayName;
+ location = ref.location;
+ eventHashStr = ref.eventHashStr;
+ details = ref.details;
+ eventType = ref.eventType;
+ duration = ref.duration;
+ calls = ref.calls;
+ minTime = ref.minTime;
+ maxTime = ref.maxTime;
+ timePerCall = ref.timePerCall;
+ percentOfTime = ref.percentOfTime;
+ medianTime = ref.medianTime;
+ eventId = ref.eventId;
+ isBindingLoop = ref.isBindingLoop;
+
+ qDeleteAll(parentHash.values());
+ parentHash.clear();
+ foreach (const QString &key, ref.parentHash.keys()) {
+ parentHash.insert(key, new QmlRangeEventRelative(ref.parentHash.value(key)));
+ }
+
+ qDeleteAll(childrenHash.values());
+ childrenHash.clear();
+ foreach (const QString &key, ref.childrenHash.keys()) {
+ childrenHash.insert(key, new QmlRangeEventRelative(ref.childrenHash.value(key)));
+ }
+
+ return *this;
+}
+
+///////////////////////////////////////////////////////////
+
+// endtimedata
+struct QmlRangeEventEndInstance {
+ qint64 endTime;
+ int startTimeIndex;
+ QmlRangeEventData *description;
+};
+
+// starttimedata
+struct QmlRangeEventStartInstance {
+ qint64 startTime;
+ qint64 duration;
+ qint64 level;
+ int endTimeIndex;
+ qint64 nestingLevel;
+ qint64 nestingDepth;
+ QmlRangeEventData *statsInfo;
+
+ // animation-related data
+ int frameRate;
+ int animationCount;
+
+ int bindingLoopHead;
+};
+
+struct QmlRangeEventTypeCount {
+ QVector<int> eventIds;
+ int nestingCount;
+};
+
+// used by quicksort
+bool compareEndTimes(const QmlRangeEventEndInstance &t1, const QmlRangeEventEndInstance &t2)
+{
+ return t1.endTime < t2.endTime;
+}
+
+bool compareStartTimes(const QmlRangeEventStartInstance &t1, const QmlRangeEventStartInstance &t2)
+{
+ return t1.startTime < t2.startTime;
+}
+
+bool compareStartIndexes(const QmlRangeEventEndInstance &t1, const QmlRangeEventEndInstance &t2)
+{
+ return t1.startTimeIndex < t2.startTimeIndex;
+}
+
+//////////////////////////////////////////////////////////////
+class QmlProfilerDataModel::QmlProfilerDataModelPrivate
+{
+public:
+ QmlProfilerDataModelPrivate(QmlProfilerDataModel *qq) : q(qq) {}
+
+ QmlProfilerDataModel *q;
+
+ // convenience functions
+ void clearQmlRootEvent();
+ void insertQmlRootEvent();
+ void postProcess();
+ void sortEndTimes();
+ void findAnimationLimits();
+ void sortStartTimes();
+ void computeNestingLevels();
+ void computeNestingDepth();
+ void prepareForDisplay();
+ void linkStartsToEnds();
+ void linkEndsToStarts();
+ bool checkBindingLoop(QmlRangeEventData *from, QmlRangeEventData *current, QList<QmlRangeEventData *>visited);
+
+
+ // stats
+ void clearStatistics();
+ void redoTree(qint64 fromTime, qint64 toTime);
+ void computeMedianTime(qint64 fromTime, qint64 toTime);
+ void findBindingLoops(qint64 fromTime, qint64 toTime);
+
+ QmlProfilerDataModel::State listState;
+
+ // Stored data
+ QHash<QString, QmlRangeEventData *> rangeEventDictionary;
+ QVector<QmlRangeEventEndInstance> endInstanceList;
+ QVector<QmlRangeEventStartInstance> startInstanceList;
+
+ QmlRangeEventData qmlRootEvent;
+
+ QV8ProfilerDataModel *v8DataModel;
+
+ QHash<int, QmlRangeEventTypeCount *> typeCounts;
+
+ qint64 traceEndTime;
+ qint64 traceStartTime;
+ qint64 qmlMeasuredTime;
+
+ QmlRangeEventStartInstance *lastFrameEvent;
+ qint64 maxAnimationCount;
+ qint64 minAnimationCount;
+
+ // file to load
+ QString fileName;
+};
+
+
+////////////////////////////////////////////////////////////////////////////////////
+
+
+QmlProfilerDataModel::QmlProfilerDataModel(QObject *parent) :
+ QObject(parent), d(new QmlProfilerDataModelPrivate(this))
+{
+ setObjectName("QmlProfilerDataModel");
+
+ d->listState = Empty;
+
+ d->traceEndTime = 0;
+ d->traceStartTime = -1;
+ d->qmlMeasuredTime = 0;
+ d->clearQmlRootEvent();
+ d->lastFrameEvent = 0;
+ d->maxAnimationCount = 0;
+ d->minAnimationCount = 0;
+ d->v8DataModel = new QV8ProfilerDataModel(this, this);
+}
+
+QmlProfilerDataModel::~QmlProfilerDataModel()
+{
+ clear();
+ delete d;
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+QList<QmlRangeEventData *> QmlProfilerDataModel::getEventDescriptions() const
+{
+ return d->rangeEventDictionary.values();
+}
+
+QmlRangeEventData *QmlProfilerDataModel::eventDescription(int eventId) const
+{
+ foreach (QmlRangeEventData *event, d->rangeEventDictionary.values()) {
+ if (event->eventId == eventId)
+ return event;
+ }
+ return 0;
+}
+
+QList<QV8EventData *> QmlProfilerDataModel::getV8Events() const
+{
+ return d->v8DataModel->getV8Events();
+}
+
+QV8EventData *QmlProfilerDataModel::v8EventDescription(int eventId) const
+{
+ return d->v8DataModel->v8EventDescription(eventId);
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+
+void QmlProfilerDataModel::clear()
+{
+ qDeleteAll(d->rangeEventDictionary.values());
+ d->rangeEventDictionary.clear();
+
+ d->endInstanceList.clear();
+ d->startInstanceList.clear();
+
+ d->clearQmlRootEvent();
+
+ foreach (QmlRangeEventTypeCount *typeCount, d->typeCounts.values())
+ delete typeCount;
+ d->typeCounts.clear();
+
+ d->traceEndTime = 0;
+ d->traceStartTime = -1;
+ d->qmlMeasuredTime = 0;
+
+ d->lastFrameEvent = 0;
+ d->maxAnimationCount = 0;
+ d->minAnimationCount = 0;
+
+ d->v8DataModel->clear();
+
+ emit countChanged();
+ setState(Empty);
+}
+
+void QmlProfilerDataModel::addRangedEvent(int type, qint64 startTime, qint64 length,
+ const QStringList &data,
+ const QmlJsDebugClient::QmlEventLocation &location)
+{
+ const QChar colon = QLatin1Char(':');
+ QString displayName, eventHashStr, details;
+ QmlJsDebugClient::QmlEventLocation eventLocation = location;
+
+ setState(AcquiringData);
+
+ // generate details string
+ if (data.isEmpty())
+ details = tr("Source code not available");
+ else {
+ details = data.join(" ").replace('\n'," ").simplified();
+ QRegExp rewrite("\\(function \\$(\\w+)\\(\\) \\{ (return |)(.+) \\}\\)");
+ bool match = rewrite.exactMatch(details);
+ if (match) {
+ details = rewrite.cap(1) + ": " + rewrite.cap(3);
+ }
+ if (details.startsWith(QString("file://")))
+ details = details.mid(details.lastIndexOf(QChar('/')) + 1);
+ }
+
+ // backwards compatibility: "compiling" events don't have a proper location in older
+ // version of the protocol, but the filename is passed in the details string
+ if (type == QmlJsDebugClient::Compiling && eventLocation.filename.isEmpty()) {
+ eventLocation.filename = details;
+ eventLocation.line = 1;
+ eventLocation.column = 1;
+ }
+
+ // generate hash
+ if (eventLocation.filename.isEmpty()) {
+ displayName = tr("<bytecode>");
+ eventHashStr = getHashStringForQmlEvent(eventLocation, type);
+ } else {
+ const QString filePath = QUrl(eventLocation.filename).path();
+ displayName = filePath.mid(filePath.lastIndexOf(QChar('/')) + 1) + colon +
+ QString::number(eventLocation.line);
+ eventHashStr = getHashStringForQmlEvent(eventLocation, type);
+ }
+
+ QmlRangeEventData *newEvent;
+ if (d->rangeEventDictionary.contains(eventHashStr)) {
+ newEvent = d->rangeEventDictionary[eventHashStr];
+ } else {
+ newEvent = new QmlRangeEventData;
+ newEvent->displayName = displayName;
+ newEvent->location = eventLocation;
+ newEvent->eventHashStr = eventHashStr;
+ newEvent->eventType = (QmlJsDebugClient::QmlEventType)type;
+ newEvent->details = details;
+ d->rangeEventDictionary.insert(eventHashStr, newEvent);
+ }
+
+ QmlRangeEventEndInstance endTimeData;
+ endTimeData.endTime = startTime + length;
+ endTimeData.description = newEvent;
+ endTimeData.startTimeIndex = d->startInstanceList.count();
+
+ QmlRangeEventStartInstance startTimeData;
+ startTimeData.startTime = startTime;
+ startTimeData.duration = length;
+ startTimeData.statsInfo = newEvent;
+ startTimeData.endTimeIndex = d->endInstanceList.count();
+ startTimeData.animationCount = -1;
+ startTimeData.frameRate = 1e9/length;
+
+ d->endInstanceList << endTimeData;
+ d->startInstanceList << startTimeData;
+
+ emit countChanged();
+}
+
+void QmlProfilerDataModel::addV8Event(int depth, const QString &function,
+ const QString &filename, int lineNumber,
+ double totalTime, double selfTime)
+{
+ d->v8DataModel->addV8Event(depth, function, filename, lineNumber, totalTime, selfTime);
+}
+
+void QmlProfilerDataModel::addFrameEvent(qint64 time, int framerate, int animationcount)
+{
+ QString displayName, eventHashStr, details;
+
+ setState(AcquiringData);
+
+ details = tr("Animation Timer Update");
+ displayName = tr("<Animation Update>");
+ eventHashStr = displayName;
+
+ QmlRangeEventData *newEvent;
+ if (d->rangeEventDictionary.contains(eventHashStr)) {
+ newEvent = d->rangeEventDictionary[eventHashStr];
+ } else {
+ newEvent = new QmlRangeEventData;
+ newEvent->displayName = displayName;
+ newEvent->eventHashStr = eventHashStr;
+ newEvent->eventType = QmlJsDebugClient::Painting;
+ newEvent->details = details;
+ d->rangeEventDictionary.insert(eventHashStr, newEvent);
+ }
+
+ qint64 length = 1e9/framerate;
+ // avoid overlap
+ if (d->lastFrameEvent &&
+ d->lastFrameEvent->startTime + d->lastFrameEvent->duration >= time) {
+ d->lastFrameEvent->duration = time - 1 - d->lastFrameEvent->startTime;
+ d->endInstanceList[d->lastFrameEvent->endTimeIndex].endTime =
+ d->lastFrameEvent->startTime + d->lastFrameEvent->duration;
+ }
+
+ QmlRangeEventEndInstance endTimeData;
+ endTimeData.endTime = time + length;
+ endTimeData.description = newEvent;
+ endTimeData.startTimeIndex = d->startInstanceList.count();
+
+ QmlRangeEventStartInstance startTimeData;
+ startTimeData.startTime = time;
+ startTimeData.duration = length;
+ startTimeData.statsInfo = newEvent;
+ startTimeData.endTimeIndex = d->endInstanceList.count();
+ startTimeData.animationCount = animationcount;
+ startTimeData.frameRate = framerate;
+
+ d->endInstanceList << endTimeData;
+ d->startInstanceList << startTimeData;
+
+ d->lastFrameEvent = &d->startInstanceList.last();
+
+ emit countChanged();
+}
+
+void QmlProfilerDataModel::setTraceEndTime(qint64 time)
+{
+ d->traceEndTime = time;
+}
+
+void QmlProfilerDataModel::setTraceStartTime(qint64 time)
+{
+ d->traceStartTime = time;
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+
+QString QmlProfilerDataModel::getHashStringForQmlEvent(
+ const QmlJsDebugClient::QmlEventLocation &location, int eventType)
+{
+ return QString("%1:%2:%3:%4").arg(location.filename,
+ QString::number(location.line),
+ QString::number(location.column),
+ QString::number(eventType));
+}
+
+QString QmlProfilerDataModel::getHashStringForV8Event(const QString &displayName,
+ const QString &function)
+{
+ return QString("%1:%2").arg(displayName, function);
+}
+
+QString QmlProfilerDataModel::rootEventName()
+{
+ return tr("<program>");
+}
+
+QString QmlProfilerDataModel::rootEventDescription()
+{
+ return tr("Main Program");
+}
+
+QString QmlProfilerDataModel::qmlEventTypeAsString(QmlEventType typeEnum)
+{
+ switch (typeEnum) {
+ case Painting:
+ return QLatin1String(Constants::TYPE_PAINTING_STR);
+ break;
+ case Compiling:
+ return QLatin1String(Constants::TYPE_COMPILING_STR);
+ break;
+ case Creating:
+ return QLatin1String(Constants::TYPE_CREATING_STR);
+ break;
+ case Binding:
+ return QLatin1String(Constants::TYPE_BINDING_STR);
+ break;
+ case HandlingSignal:
+ return QLatin1String(Constants::TYPE_HANDLINGSIGNAL_STR);
+ break;
+ default:
+ return QString::number((int)typeEnum);
+ }
+}
+
+QmlEventType QmlProfilerDataModel::qmlEventTypeAsEnum(const QString &typeString)
+{
+ if (typeString == QLatin1String(Constants::TYPE_PAINTING_STR)) {
+ return Painting;
+ } else if (typeString == QLatin1String(Constants::TYPE_COMPILING_STR)) {
+ return Compiling;
+ } else if (typeString == QLatin1String(Constants::TYPE_CREATING_STR)) {
+ return Creating;
+ } else if (typeString == QLatin1String(Constants::TYPE_BINDING_STR)) {
+ return Binding;
+ } else if (typeString == QLatin1String(Constants::TYPE_HANDLINGSIGNAL_STR)) {
+ return HandlingSignal;
+ } else {
+ bool isNumber = false;
+ int type = typeString.toUInt(&isNumber);
+ if (isNumber) {
+ return (QmlEventType)type;
+ } else {
+ return MaximumQmlEventType;
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+
+int QmlProfilerDataModel::findFirstIndex(qint64 startTime) const
+{
+ int candidate = -1;
+ // in the "endtime" list, find the first event that ends after startTime
+ if (d->endInstanceList.isEmpty())
+ return 0; // -1
+ if (d->endInstanceList.count() == 1 || d->endInstanceList.first().endTime >= startTime)
+ candidate = 0;
+ else
+ if (d->endInstanceList.last().endTime <= startTime)
+ return 0; // -1
+
+ if (candidate == -1)
+ {
+ int fromIndex = 0;
+ int toIndex = d->endInstanceList.count()-1;
+ while (toIndex - fromIndex > 1) {
+ int midIndex = (fromIndex + toIndex)/2;
+ if (d->endInstanceList[midIndex].endTime < startTime)
+ fromIndex = midIndex;
+ else
+ toIndex = midIndex;
+ }
+
+ candidate = toIndex;
+ }
+
+ int ndx = d->endInstanceList[candidate].startTimeIndex;
+
+ // and then go to the parent
+ while (ndx > 0 && d->startInstanceList[ndx].level > d->startInstanceList[ndx-1].level )
+ ndx--;
+
+ return ndx;
+}
+
+int QmlProfilerDataModel::findFirstIndexNoParents(qint64 startTime) const
+{
+ int candidate = -1;
+ // in the "endtime" list, find the first event that ends after startTime
+ if (d->endInstanceList.isEmpty())
+ return 0; // -1
+ if (d->endInstanceList.count() == 1 || d->endInstanceList.first().endTime >= startTime)
+ candidate = 0;
+ else
+ if (d->endInstanceList.last().endTime <= startTime)
+ return 0; // -1
+
+ if (candidate == -1) {
+ int fromIndex = 0;
+ int toIndex = d->endInstanceList.count()-1;
+ while (toIndex - fromIndex > 1) {
+ int midIndex = (fromIndex + toIndex)/2;
+ if (d->endInstanceList[midIndex].endTime < startTime)
+ fromIndex = midIndex;
+ else
+ toIndex = midIndex;
+ }
+
+ candidate = toIndex;
+ }
+
+ int ndx = d->endInstanceList[candidate].startTimeIndex;
+
+ return ndx;
+}
+
+int QmlProfilerDataModel::findLastIndex(qint64 endTime) const
+{
+ // in the "starttime" list, find the last event that starts before endtime
+ if (d->startInstanceList.isEmpty())
+ return 0; // -1
+ if (d->startInstanceList.first().startTime >= endTime)
+ return 0; // -1
+ if (d->startInstanceList.count() == 1)
+ return 0;
+ if (d->startInstanceList.last().startTime <= endTime)
+ return d->startInstanceList.count()-1;
+
+ int fromIndex = 0;
+ int toIndex = d->startInstanceList.count()-1;
+ while (toIndex - fromIndex > 1) {
+ int midIndex = (fromIndex + toIndex)/2;
+ if (d->startInstanceList[midIndex].startTime < endTime)
+ fromIndex = midIndex;
+ else
+ toIndex = midIndex;
+ }
+
+ return fromIndex;
+}
+
+qint64 QmlProfilerDataModel::firstTimeMark() const
+{
+ if (d->startInstanceList.isEmpty())
+ return 0;
+ else {
+ return d->startInstanceList[0].startTime;
+ }
+}
+
+qint64 QmlProfilerDataModel::lastTimeMark() const
+{
+ if (d->endInstanceList.isEmpty())
+ return 0;
+ else {
+ return d->endInstanceList.last().endTime;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+
+int QmlProfilerDataModel::count() const
+{
+ return d->startInstanceList.count();
+}
+
+bool QmlProfilerDataModel::isEmpty() const
+{
+ return d->startInstanceList.isEmpty() && d->v8DataModel->isEmpty();
+}
+
+qint64 QmlProfilerDataModel::getStartTime(int index) const
+{
+ return d->startInstanceList[index].startTime;
+}
+
+qint64 QmlProfilerDataModel::getEndTime(int index) const
+{
+ return d->startInstanceList[index].startTime + d->startInstanceList[index].duration;
+}
+
+qint64 QmlProfilerDataModel::getDuration(int index) const
+{
+ return d->startInstanceList[index].duration;
+}
+
+int QmlProfilerDataModel::getType(int index) const
+{
+ return d->startInstanceList[index].statsInfo->eventType;
+}
+
+int QmlProfilerDataModel::getNestingLevel(int index) const
+{
+ return d->startInstanceList[index].nestingLevel;
+}
+
+int QmlProfilerDataModel::getNestingDepth(int index) const
+{
+ return d->startInstanceList[index].nestingDepth;
+}
+
+QString QmlProfilerDataModel::getFilename(int index) const
+{
+ return d->startInstanceList[index].statsInfo->location.filename;
+}
+
+int QmlProfilerDataModel::getLine(int index) const
+{
+ return d->startInstanceList[index].statsInfo->location.line;
+}
+
+int QmlProfilerDataModel::getColumn(int index) const
+{
+ return d->startInstanceList[index].statsInfo->location.column;
+}
+
+QString QmlProfilerDataModel::getDetails(int index) const
+{
+ // special: animations
+ if (d->startInstanceList[index].statsInfo->eventType == QmlJsDebugClient::Painting &&
+ d->startInstanceList[index].animationCount >= 0)
+ return tr("%1 animations at %2 FPS").arg(
+ QString::number(d->startInstanceList[index].animationCount),
+ QString::number(d->startInstanceList[index].frameRate));
+ return d->startInstanceList[index].statsInfo->details;
+}
+
+int QmlProfilerDataModel::getEventId(int index) const
+{
+ return d->startInstanceList[index].statsInfo->eventId;
+}
+
+int QmlProfilerDataModel::getBindingLoopDest(int index) const
+{
+ return d->startInstanceList[index].bindingLoopHead;
+}
+
+int QmlProfilerDataModel::getFramerate(int index) const
+{
+ return d->startInstanceList[index].frameRate;
+}
+
+int QmlProfilerDataModel::getAnimationCount(int index) const
+{
+ return d->startInstanceList[index].animationCount;
+}
+
+int QmlProfilerDataModel::getMaximumAnimationCount() const
+{
+ return d->maxAnimationCount;
+}
+
+int QmlProfilerDataModel::getMinimumAnimationCount() const
+{
+ return d->minAnimationCount;
+}
+
+/////////////////////////////////////////
+
+int QmlProfilerDataModel::uniqueEventsOfType(int type) const
+{
+ if (!d->typeCounts.contains(type))
+ return 0;
+ return d->typeCounts[type]->eventIds.count();
+}
+
+int QmlProfilerDataModel::maxNestingForType(int type) const
+{
+ if (!d->typeCounts.contains(type))
+ return 0;
+ return d->typeCounts[type]->nestingCount;
+}
+
+QString QmlProfilerDataModel::eventTextForType(int type, int index) const
+{
+ if (!d->typeCounts.contains(type))
+ return QString();
+ return d->rangeEventDictionary.values().at(d->typeCounts[type]->eventIds[index])->details;
+}
+
+QString QmlProfilerDataModel::eventDisplayNameForType(int type, int index) const
+{
+ if (!d->typeCounts.contains(type))
+ return QString();
+ return d->rangeEventDictionary.values().at(d->typeCounts[type]->eventIds[index])->displayName;
+}
+
+int QmlProfilerDataModel::eventIdForType(int type, int index) const
+{
+ if (!d->typeCounts.contains(type))
+ return -1;
+ return d->typeCounts[type]->eventIds[index];
+}
+
+int QmlProfilerDataModel::eventPosInType(int index) const
+{
+ int eventType = d->startInstanceList[index].statsInfo->eventType;
+ return d->typeCounts[eventType]->eventIds.indexOf(d->startInstanceList[index].statsInfo->eventId);
+}
+
+/////////////////////////////////////////
+
+qint64 QmlProfilerDataModel::traceStartTime() const
+{
+ return d->traceStartTime != -1? d->traceStartTime : firstTimeMark();
+}
+
+qint64 QmlProfilerDataModel::traceEndTime() const
+{
+ return d->traceEndTime ? d->traceEndTime : lastTimeMark();
+}
+
+qint64 QmlProfilerDataModel::traceDuration() const
+{
+ return traceEndTime() - traceStartTime();
+}
+
+qint64 QmlProfilerDataModel::qmlMeasuredTime() const
+{
+ return d->qmlMeasuredTime;
+}
+qint64 QmlProfilerDataModel::v8MeasuredTime() const
+{
+ return d->v8DataModel->v8MeasuredTime();
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+
+void QmlProfilerDataModel::complete()
+{
+ if (currentState() == AcquiringData) {
+ setState(ProcessingData);
+ d->v8DataModel->collectV8Statistics();
+ d->postProcess();
+ } else
+ if (currentState() == Empty) {
+ setState(Done);
+ } else {
+ emit error("Unexpected complete signal in data model");
+ }
+}
+
+void QmlProfilerDataModel::QmlProfilerDataModelPrivate::postProcess()
+{
+ if (q->count() != 0) {
+ sortStartTimes();
+ sortEndTimes();
+ findAnimationLimits();
+ computeNestingLevels();
+ computeNestingDepth();
+ linkEndsToStarts();
+ insertQmlRootEvent();
+ q->reloadDetails();
+ prepareForDisplay();
+ q->compileStatistics(q->traceStartTime(), q->traceEndTime());
+
+ }
+ q->setState(Done);
+}
+
+
+void QmlProfilerDataModel::QmlProfilerDataModelPrivate::prepareForDisplay()
+{
+ // generate numeric ids
+ int ndx = 0;
+ foreach (QmlRangeEventData *binding, rangeEventDictionary.values()) {
+ binding->eventId = ndx++;
+ }
+
+ // collect type counts
+ foreach (const QmlRangeEventStartInstance &eventStartData, startInstanceList) {
+ int typeNumber = eventStartData.statsInfo->eventType;
+ if (!typeCounts.contains(typeNumber)) {
+ typeCounts[typeNumber] = new QmlRangeEventTypeCount;
+ typeCounts[typeNumber]->nestingCount = 0;
+ }
+ if (eventStartData.nestingLevel > typeCounts[typeNumber]->nestingCount) {
+ typeCounts[typeNumber]->nestingCount = eventStartData.nestingLevel;
+ }
+ if (!typeCounts[typeNumber]->eventIds.contains(eventStartData.statsInfo->eventId))
+ typeCounts[typeNumber]->eventIds << eventStartData.statsInfo->eventId;
+ }
+}
+
+void QmlProfilerDataModel::QmlProfilerDataModelPrivate::sortStartTimes()
+{
+ if (startInstanceList.count() < 2)
+ return;
+
+ // assuming startTimes is partially sorted
+ // identify blocks of events and sort them with quicksort
+ QVector<QmlRangeEventStartInstance>::iterator itFrom = startInstanceList.end() - 2;
+ QVector<QmlRangeEventStartInstance>::iterator itTo = startInstanceList.end() - 1;
+
+ while (itFrom != startInstanceList.begin() && itTo != startInstanceList.begin()) {
+ // find block to sort
+ while (itFrom != startInstanceList.begin()
+ && itTo->startTime > itFrom->startTime) {
+ itTo--;
+ itFrom = itTo - 1;
+ }
+
+ // if we're at the end of the list
+ if (itFrom == startInstanceList.begin())
+ break;
+
+ // find block length
+ while (itFrom != startInstanceList.begin()
+ && itTo->startTime <= itFrom->startTime)
+ itFrom--;
+
+ if (itTo->startTime <= itFrom->startTime)
+ qSort(itFrom, itTo + 1, compareStartTimes);
+ else
+ qSort(itFrom + 1, itTo + 1, compareStartTimes);
+
+ // move to next block
+ itTo = itFrom;
+ itFrom = itTo - 1;
+ }
+
+ // link back the endTimes
+ linkEndsToStarts();
+}
+
+void QmlProfilerDataModel::QmlProfilerDataModelPrivate::sortEndTimes()
+{
+ // assuming endTimes is partially sorted
+ // identify blocks of events and sort them with quicksort
+
+ if (endInstanceList.count() < 2)
+ return;
+
+ QVector<QmlRangeEventEndInstance>::iterator itFrom = endInstanceList.begin();
+ QVector<QmlRangeEventEndInstance>::iterator itTo = endInstanceList.begin() + 1;
+
+ while (itTo != endInstanceList.end() && itFrom != endInstanceList.end()) {
+ // find block to sort
+ while (itTo != endInstanceList.end()
+ && startInstanceList[itTo->startTimeIndex].startTime >
+ startInstanceList[itFrom->startTimeIndex].startTime +
+ startInstanceList[itFrom->startTimeIndex].duration) {
+ itFrom++;
+ itTo = itFrom+1;
+ }
+
+ // if we're at the end of the list
+ if (itTo == endInstanceList.end())
+ break;
+
+ // find block length
+ while (itTo != endInstanceList.end()
+ && startInstanceList[itTo->startTimeIndex].startTime <=
+ startInstanceList[itFrom->startTimeIndex].startTime +
+ startInstanceList[itFrom->startTimeIndex].duration)
+ itTo++;
+
+ // sort block
+ qSort(itFrom, itTo, compareEndTimes);
+
+ // move to next block
+ itFrom = itTo;
+ itTo = itFrom+1;
+
+ }
+
+ // link back the startTimes
+ linkStartsToEnds();
+}
+
+void QmlProfilerDataModel::QmlProfilerDataModelPrivate::linkStartsToEnds()
+{
+ for (int i = 0; i < endInstanceList.count(); i++)
+ startInstanceList[endInstanceList[i].startTimeIndex].endTimeIndex = i;
+}
+
+void QmlProfilerDataModel::QmlProfilerDataModelPrivate::findAnimationLimits()
+{
+ maxAnimationCount = 0;
+ minAnimationCount = 0;
+ lastFrameEvent = 0;
+
+ for (int i = 0; i < startInstanceList.count(); i++) {
+ if (startInstanceList[i].statsInfo->eventType == QmlJsDebugClient::Painting &&
+ startInstanceList[i].animationCount >= 0) {
+ int animationcount = startInstanceList[i].animationCount;
+ if (lastFrameEvent) {
+ if (animationcount > maxAnimationCount)
+ maxAnimationCount = animationcount;
+ if (animationcount < minAnimationCount)
+ minAnimationCount = animationcount;
+ } else {
+ maxAnimationCount = animationcount;
+ minAnimationCount = animationcount;
+ }
+ lastFrameEvent = &startInstanceList[i];
+ }
+ }
+}
+
+void QmlProfilerDataModel::QmlProfilerDataModelPrivate::computeNestingLevels()
+{
+ // compute levels
+ QHash<int, qint64> endtimesPerLevel;
+ QList<int> nestingLevels;
+ QList< QHash<int, qint64> > endtimesPerNestingLevel;
+ int level = Constants::QML_MIN_LEVEL;
+ endtimesPerLevel[Constants::QML_MIN_LEVEL] = 0;
+
+ for (int i = 0; i < QmlJsDebugClient::MaximumQmlEventType; i++) {
+ nestingLevels << Constants::QML_MIN_LEVEL;
+ QHash<int, qint64> dummyHash;
+ dummyHash[Constants::QML_MIN_LEVEL] = 0;
+ endtimesPerNestingLevel << dummyHash;
+ }
+
+ for (int i=0; i<startInstanceList.count(); i++) {
+ qint64 st = startInstanceList[i].startTime;
+ int type = startInstanceList[i].statsInfo->eventType;
+
+ if (type == QmlJsDebugClient::Painting) {
+ // animation/paint events have level 0 by definition (same as "mainprogram"),
+ // but are not considered parents of other events for statistical purposes
+ startInstanceList[i].level = Constants::QML_MIN_LEVEL - 1;
+ startInstanceList[i].nestingLevel = Constants::QML_MIN_LEVEL;
+ continue;
+ }
+
+ // general level
+ if (endtimesPerLevel[level] > st) {
+ level++;
+ } else {
+ while (level > Constants::QML_MIN_LEVEL && endtimesPerLevel[level-1] <= st)
+ level--;
+ }
+ endtimesPerLevel[level] = st + startInstanceList[i].duration;
+
+ // per type
+ if (endtimesPerNestingLevel[type][nestingLevels[type]] > st) {
+ nestingLevels[type]++;
+ } else {
+ while (nestingLevels[type] > Constants::QML_MIN_LEVEL &&
+ endtimesPerNestingLevel[type][nestingLevels[type]-1] <= st)
+ nestingLevels[type]--;
+ }
+ endtimesPerNestingLevel[type][nestingLevels[type]] =
+ st + startInstanceList[i].duration;
+
+ startInstanceList[i].level = level;
+ startInstanceList[i].nestingLevel = nestingLevels[type];
+
+ if (level == Constants::QML_MIN_LEVEL) {
+ qmlMeasuredTime += startInstanceList[i].duration;
+ }
+ }
+}
+
+void QmlProfilerDataModel::QmlProfilerDataModelPrivate::computeNestingDepth()
+{
+ QHash<int, int> nestingDepth;
+ for (int i = 0; i < endInstanceList.count(); i++) {
+ int type = endInstanceList[i].description->eventType;
+ int nestingInType = startInstanceList[endInstanceList[i].startTimeIndex].nestingLevel;
+ if (!nestingDepth.contains(type))
+ nestingDepth[type] = nestingInType;
+ else {
+ int nd = nestingDepth[type];
+ nestingDepth[type] = nd > nestingInType ? nd : nestingInType;
+ }
+
+ startInstanceList[endInstanceList[i].startTimeIndex].nestingDepth = nestingDepth[type];
+ if (nestingInType == Constants::QML_MIN_LEVEL)
+ nestingDepth[type] = Constants::QML_MIN_LEVEL;
+ }
+}
+
+void QmlProfilerDataModel::QmlProfilerDataModelPrivate::linkEndsToStarts()
+{
+ for (int i = 0; i < startInstanceList.count(); i++)
+ endInstanceList[startInstanceList[i].endTimeIndex].startTimeIndex = i;
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+
+void QmlProfilerDataModel::compileStatistics(qint64 startTime, qint64 endTime)
+{
+ d->clearStatistics();
+ d->redoTree(startTime, endTime);
+ d->computeMedianTime(startTime, endTime);
+ d->findBindingLoops(startTime, endTime);
+}
+
+void QmlProfilerDataModel::QmlProfilerDataModelPrivate::clearStatistics()
+{
+ // clear existing statistics
+ foreach (QmlRangeEventData *eventDescription, rangeEventDictionary.values()) {
+ eventDescription->calls = 0;
+ // maximum possible value
+ eventDescription->minTime = endInstanceList.last().endTime;
+ eventDescription->maxTime = 0;
+ eventDescription->medianTime = 0;
+ eventDescription->duration = 0;
+ qDeleteAll(eventDescription->parentHash);
+ qDeleteAll(eventDescription->childrenHash);
+ eventDescription->parentHash.clear();
+ eventDescription->childrenHash.clear();
+ }
+}
+
+void QmlProfilerDataModel::QmlProfilerDataModelPrivate::redoTree(qint64 fromTime,
+ qint64 toTime)
+{
+ double totalTime = 0;
+ int fromIndex = q->findFirstIndex(fromTime);
+ int toIndex = q->findLastIndex(toTime);
+
+ QmlRangeEventData *listedRootEvent = rangeEventDictionary.value(rootEventName());
+ QTC_ASSERT(listedRootEvent, /**/);
+
+ // compute parent-child relationship and call count
+ QHash<int, QmlRangeEventData*> lastParent;
+ for (int index = fromIndex; index <= toIndex; index++) {
+ QmlRangeEventData *eventDescription = startInstanceList[index].statsInfo;
+
+ if (startInstanceList[index].startTime > toTime ||
+ startInstanceList[index].startTime+startInstanceList[index].duration < fromTime) {
+ continue;
+ }
+
+ if (eventDescription->eventType == QmlJsDebugClient::Painting) {
+ // skip animation/paint events
+ continue;
+ }
+
+ eventDescription->calls++;
+ qint64 duration = startInstanceList[index].duration;
+ eventDescription->duration += duration;
+ if (eventDescription->maxTime < duration)
+ eventDescription->maxTime = duration;
+ if (eventDescription->minTime > duration)
+ eventDescription->minTime = duration;
+
+ int level = startInstanceList[index].level;
+
+ QmlRangeEventData *parentEvent = listedRootEvent;
+ if (level > Constants::QML_MIN_LEVEL && lastParent.contains(level-1)) {
+ parentEvent = lastParent[level-1];
+ }
+
+ if (!eventDescription->parentHash.contains(parentEvent->eventHashStr)) {
+ QmlRangeEventRelative *newParentEvent = new QmlRangeEventRelative(parentEvent);
+ newParentEvent->calls = 1;
+ newParentEvent->duration = duration;
+
+ eventDescription->parentHash.insert(parentEvent->eventHashStr, newParentEvent);
+ } else {
+ QmlRangeEventRelative *newParentEvent =
+ eventDescription->parentHash.value(parentEvent->eventHashStr);
+ newParentEvent->duration += duration;
+ newParentEvent->calls++;
+ }
+
+ if (!parentEvent->childrenHash.contains(eventDescription->eventHashStr)) {
+ QmlRangeEventRelative *newChildEvent = new QmlRangeEventRelative(eventDescription);
+ newChildEvent->calls = 1;
+ newChildEvent->duration = duration;
+
+ parentEvent->childrenHash.insert(eventDescription->eventHashStr, newChildEvent);
+ } else {
+ QmlRangeEventRelative *newChildEvent =
+ parentEvent->childrenHash.value(eventDescription->eventHashStr);
+ newChildEvent->duration += duration;
+ newChildEvent->calls++;
+ }
+
+ lastParent[level] = eventDescription;
+
+ if (level == Constants::QML_MIN_LEVEL) {
+ totalTime += duration;
+ }
+ }
+
+ // fake rootEvent statistics
+ // the +1 nanosecond is to force it to be on top of the sorted list
+ listedRootEvent->duration = totalTime+1;
+ listedRootEvent->minTime = totalTime+1;
+ listedRootEvent->maxTime = totalTime+1;
+ listedRootEvent->medianTime = totalTime+1;
+ if (totalTime > 0)
+ listedRootEvent->calls = 1;
+
+ // copy to the global root reference
+ qmlRootEvent = *listedRootEvent;
+
+ // compute percentages
+ foreach (QmlRangeEventData *binding, rangeEventDictionary.values()) {
+ binding->percentOfTime = binding->duration * 100.0 / totalTime;
+ binding->timePerCall = binding->calls > 0 ? double(binding->duration) / binding->calls : 0;
+ }
+}
+
+void QmlProfilerDataModel::QmlProfilerDataModelPrivate::computeMedianTime(qint64 fromTime,
+ qint64 toTime)
+{
+ int fromIndex = q->findFirstIndex(fromTime);
+ int toIndex = q->findLastIndex(toTime);
+
+ // compute median time
+ QHash< QmlRangeEventData* , QList<qint64> > durationLists;
+ for (int index = fromIndex; index <= toIndex; index++) {
+ QmlRangeEventData *desc = startInstanceList[index].statsInfo;
+ qint64 len = startInstanceList[index].duration;
+ durationLists[desc].append(len);
+ }
+ QMutableHashIterator < QmlRangeEventData* , QList<qint64> > iter(durationLists);
+ while (iter.hasNext()) {
+ iter.next();
+ if (!iter.value().isEmpty()) {
+ qSort(iter.value());
+ iter.key()->medianTime = iter.value().at(iter.value().count()/2);
+ }
+ }
+}
+
+void QmlProfilerDataModel::QmlProfilerDataModelPrivate::findBindingLoops(qint64 fromTime,
+ qint64 toTime)
+{
+ int fromIndex = q->findFirstIndex(fromTime);
+ int toIndex = q->findLastIndex(toTime);
+
+ // first clear existing data
+ foreach (QmlRangeEventData *event, rangeEventDictionary.values()) {
+ event->isBindingLoop = false;
+ foreach (QmlRangeEventRelative *parentEvent, event->parentHash.values())
+ parentEvent->inLoopPath = false;
+ foreach (QmlRangeEventRelative *childEvent, event->childrenHash.values())
+ childEvent->inLoopPath = false;
+ }
+
+ QList<QmlRangeEventData *> stackRefs;
+ QList<QmlRangeEventStartInstance *> stack;
+
+ for (int i = 0; i < startInstanceList.count(); i++) {
+ QmlRangeEventData *currentEvent = startInstanceList[i].statsInfo;
+ QmlRangeEventStartInstance *inTimeEvent = &startInstanceList[i];
+ inTimeEvent->bindingLoopHead = -1;
+
+ // managing call stack
+ for (int j = stack.count() - 1; j >= 0; j--) {
+ if (stack[j]->startTime + stack[j]->duration <= inTimeEvent->startTime) {
+ stack.removeAt(j);
+ stackRefs.removeAt(j);
+ }
+ }
+
+ bool loopDetected = stackRefs.contains(currentEvent);
+ stack << inTimeEvent;
+ stackRefs << currentEvent;
+
+ if (loopDetected) {
+ if (i >= fromIndex && i <= toIndex) {
+ // for the statistics
+ currentEvent->isBindingLoop = true;
+ for (int j = stackRefs.indexOf(currentEvent); j < stackRefs.count()-1; j++) {
+ QmlRangeEventRelative *nextEventSub =
+ stackRefs[j]->childrenHash.value(stackRefs[j+1]->eventHashStr);
+ nextEventSub->inLoopPath = true;
+ QmlRangeEventRelative *prevEventSub =
+ stackRefs[j+1]->parentHash.value(stackRefs[j]->eventHashStr);
+ prevEventSub->inLoopPath = true;
+ }
+ }
+
+ // use crossed references to find index in starttimesortedlist
+ QmlRangeEventStartInstance *head = stack[stackRefs.indexOf(currentEvent)];
+ inTimeEvent->bindingLoopHead = endInstanceList[head->endTimeIndex].startTimeIndex;
+ startInstanceList[inTimeEvent->bindingLoopHead].bindingLoopHead = i;
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+
+void QmlProfilerDataModel::QmlProfilerDataModelPrivate::clearQmlRootEvent()
+{
+ qmlRootEvent.displayName = rootEventName();
+ qmlRootEvent.location = QmlEventLocation();
+ qmlRootEvent.eventHashStr = rootEventName();
+ qmlRootEvent.details = rootEventDescription();
+ qmlRootEvent.eventType = QmlJsDebugClient::Binding;
+ qmlRootEvent.duration = 0;
+ qmlRootEvent.calls = 0;
+ qmlRootEvent.minTime = 0;
+ qmlRootEvent.maxTime = 0;
+ qmlRootEvent.timePerCall = 0;
+ qmlRootEvent.percentOfTime = 0;
+ qmlRootEvent.medianTime = 0;
+ qmlRootEvent.eventId = -1;
+
+ qDeleteAll(qmlRootEvent.parentHash.values());
+ qDeleteAll(qmlRootEvent.childrenHash.values());
+ qmlRootEvent.parentHash.clear();
+ qmlRootEvent.childrenHash.clear();
+}
+
+void QmlProfilerDataModel::QmlProfilerDataModelPrivate::insertQmlRootEvent()
+{
+ // create root event for statistics & insert into list
+ clearQmlRootEvent();
+ QmlRangeEventData *listedRootEvent = rangeEventDictionary.value(rootEventName());
+ if (!listedRootEvent) {
+ listedRootEvent = new QmlRangeEventData;
+ rangeEventDictionary.insert(rootEventName(), listedRootEvent);
+ }
+ *listedRootEvent = qmlRootEvent;
+}
+
+void QmlProfilerDataModel::reloadDetails()
+{
+ // request binding/signal details from the AST
+ foreach (QmlRangeEventData *event, d->rangeEventDictionary.values()) {
+ if (event->eventType != Binding && event->eventType != HandlingSignal)
+ continue;
+
+ // This skips anonymous bindings in Qt4.8 (we don't have valid location data for them)
+ if (event->location.filename.isEmpty())
+ continue;
+
+ // Skip non-anonymous bindings from Qt4.8 (we already have correct details for them)
+ if (event->location.column == -1)
+ continue;
+
+ emit requestDetailsForLocation(event->eventType, event->location);
+ }
+ emit reloadDocumentsForDetails();
+}
+
+void QmlProfilerDataModel::rewriteDetailsString(int eventType,
+ const QmlJsDebugClient::QmlEventLocation &location,
+ const QString &newString)
+{
+ QString eventHashStr = getHashStringForQmlEvent(location, eventType);
+ QTC_ASSERT(d->rangeEventDictionary.contains(eventHashStr), return);
+ d->rangeEventDictionary.value(eventHashStr)->details = newString;
+ emit detailsChanged(d->rangeEventDictionary.value(eventHashStr)->eventId, newString);
+}
+
+void QmlProfilerDataModel::finishedRewritingDetails()
+{
+ emit reloadDetailLabels();
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+
+bool QmlProfilerDataModel::save(const QString &filename)
+{
+ if (isEmpty()) {
+ emit error(tr("No data to save"));
+ return false;
+ }
+
+ QFile file(filename);
+ if (!file.open(QIODevice::WriteOnly)) {
+ emit error(tr("Could not open %1 for writing").arg(filename));
+ return false;
+ }
+
+ QXmlStreamWriter stream(&file);
+ stream.setAutoFormatting(true);
+ stream.writeStartDocument();
+
+ stream.writeStartElement("trace");
+ stream.writeAttribute("version", Constants::PROFILER_FILE_VERSION);
+
+ stream.writeAttribute("traceStart", QString::number(traceStartTime()));
+ stream.writeAttribute("traceEnd", QString::number(traceEndTime()));
+
+ stream.writeStartElement("eventData");
+ stream.writeAttribute("totalTime", QString::number(d->qmlMeasuredTime));
+
+ foreach (const QmlRangeEventData *eventData, d->rangeEventDictionary.values()) {
+ stream.writeStartElement("event");
+ stream.writeAttribute("index", QString::number(d->rangeEventDictionary.keys().indexOf(eventData->eventHashStr)));
+ stream.writeTextElement("displayname", eventData->displayName);
+ stream.writeTextElement("type", qmlEventTypeAsString(eventData->eventType));
+ if (!eventData->location.filename.isEmpty()) {
+ stream.writeTextElement("filename", eventData->location.filename);
+ stream.writeTextElement("line", QString::number(eventData->location.line));
+ stream.writeTextElement("column", QString::number(eventData->location.column));
+ }
+ stream.writeTextElement("details", eventData->details);
+ stream.writeEndElement();
+ }
+ stream.writeEndElement(); // eventData
+
+ stream.writeStartElement("profilerDataModel");
+ foreach (const QmlRangeEventStartInstance &rangedEvent, d->startInstanceList) {
+ stream.writeStartElement("range");
+ stream.writeAttribute("startTime", QString::number(rangedEvent.startTime));
+ stream.writeAttribute("duration", QString::number(rangedEvent.duration));
+ stream.writeAttribute("eventIndex", QString::number(d->rangeEventDictionary.keys().indexOf(rangedEvent.statsInfo->eventHashStr)));
+ if (rangedEvent.statsInfo->eventType == QmlJsDebugClient::Painting && rangedEvent.animationCount >= 0) {
+ // animation frame
+ stream.writeAttribute("framerate", QString::number(rangedEvent.frameRate));
+ stream.writeAttribute("animationcount", QString::number(rangedEvent.animationCount));
+ }
+ stream.writeEndElement();
+ }
+ stream.writeEndElement(); // profilerDataModel
+
+ d->v8DataModel->save(stream);
+
+ stream.writeEndElement(); // trace
+ stream.writeEndDocument();
+
+ file.close();
+ return true;
+}
+
+void QmlProfilerDataModel::setFilename(const QString &filename)
+{
+ d->fileName = filename;
+}
+
+void QmlProfilerDataModel::load(const QString &filename)
+{
+ setFilename(filename);
+ load();
+}
+
+// "be strict in your output but tolerant in your inputs"
+void QmlProfilerDataModel::load()
+{
+ QString filename = d->fileName;
+
+ QFile file(filename);
+
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ emit error(tr("Could not open %1 for reading").arg(filename));
+ return;
+ }
+
+ // erase current
+ clear();
+
+ setState(AcquiringData);
+
+ bool readingQmlEvents = false;
+ QHash<int, QmlRangeEventData *> descriptionBuffer;
+ QmlRangeEventData *currentEvent = 0;
+ bool startTimesAreSorted = true;
+ bool validVersion = true;
+
+ // time computation
+ d->qmlMeasuredTime = 0;
+
+ QXmlStreamReader stream(&file);
+
+ while (validVersion && !stream.atEnd() && !stream.hasError()) {
+ QXmlStreamReader::TokenType token = stream.readNext();
+ QString elementName = stream.name().toString();
+ switch (token) {
+ case QXmlStreamReader::StartDocument : continue;
+ case QXmlStreamReader::StartElement : {
+ if (elementName == "trace") {
+ QXmlStreamAttributes attributes = stream.attributes();
+ if (attributes.hasAttribute("version"))
+ validVersion = attributes.value("version").toString() == Constants::PROFILER_FILE_VERSION;
+ else
+ validVersion = false;
+ if (attributes.hasAttribute("traceStart"))
+ setTraceStartTime(attributes.value("traceStart").toString().toLongLong());
+ if (attributes.hasAttribute("traceEnd"))
+ setTraceEndTime(attributes.value("traceEnd").toString().toLongLong());
+ }
+ if (elementName == "eventData") {
+ readingQmlEvents = true;
+ QXmlStreamAttributes attributes = stream.attributes();
+ if (attributes.hasAttribute("totalTime"))
+ d->qmlMeasuredTime = attributes.value("totalTime").toString().toDouble();
+ break;
+ }
+ if (elementName == "v8profile" && !readingQmlEvents) {
+ d->v8DataModel->load(stream);
+ break;
+ }
+
+ if (elementName == "trace") {
+ QXmlStreamAttributes attributes = stream.attributes();
+ if (attributes.hasAttribute("traceStart"))
+ setTraceStartTime(attributes.value("traceStart").toString().toLongLong());
+ if (attributes.hasAttribute("traceEnd"))
+ setTraceEndTime(attributes.value("traceEnd").toString().toLongLong());
+ }
+
+ if (elementName == "range") {
+ QmlRangeEventStartInstance rangedEvent;
+ QXmlStreamAttributes attributes = stream.attributes();
+ if (attributes.hasAttribute("startTime"))
+ rangedEvent.startTime = attributes.value("startTime").toString().toLongLong();
+ if (attributes.hasAttribute("duration"))
+ rangedEvent.duration = attributes.value("duration").toString().toLongLong();
+ if (attributes.hasAttribute("framerate"))
+ rangedEvent.frameRate = attributes.value("framerate").toString().toInt();
+ if (attributes.hasAttribute("animationcount"))
+ rangedEvent.animationCount = attributes.value("animationcount").toString().toInt();
+ else
+ rangedEvent.animationCount = -1;
+ if (attributes.hasAttribute("eventIndex")) {
+ int ndx = attributes.value("eventIndex").toString().toInt();
+ if (!descriptionBuffer.value(ndx))
+ descriptionBuffer[ndx] = new QmlRangeEventData;
+ rangedEvent.statsInfo = descriptionBuffer.value(ndx);
+ }
+ rangedEvent.endTimeIndex = d->endInstanceList.count();
+
+ if (!d->startInstanceList.isEmpty()
+ && rangedEvent.startTime < d->startInstanceList.last().startTime)
+ startTimesAreSorted = false;
+ d->startInstanceList << rangedEvent;
+
+ QmlRangeEventEndInstance endTimeEvent;
+ endTimeEvent.endTime = rangedEvent.startTime + rangedEvent.duration;
+ endTimeEvent.startTimeIndex = d->startInstanceList.count()-1;
+ endTimeEvent.description = rangedEvent.statsInfo;
+ d->endInstanceList << endTimeEvent;
+ 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 QmlRangeEventData;
+ currentEvent = descriptionBuffer[ndx];
+ } else {
+ currentEvent = 0;
+ }
+ break;
+ }
+
+ // the remaining are eventdata or v8eventdata elements
+ if (!currentEvent)
+ 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 = qmlEventTypeAsEnum(readData);
+ break;
+ }
+ if (elementName == "filename") {
+ currentEvent->location.filename = readData;
+ break;
+ }
+ if (elementName == "line") {
+ currentEvent->location.line = readData.toInt();
+ break;
+ }
+ if (elementName == "column") {
+ currentEvent->location.column = readData.toInt();
+ }
+ if (elementName == "details") {
+ currentEvent->details = readData;
+ break;
+ }
+ }
+ break;
+ }
+ case QXmlStreamReader::EndElement : {
+ if (elementName == "event") {
+ currentEvent = 0;
+ break;
+ }
+ if (elementName == "eventData") {
+ readingQmlEvents = false;
+ break;
+ }
+ }
+ default: break;
+ }
+ }
+
+ file.close();
+
+ if (stream.hasError()) {
+ emit error(tr("Error while parsing %1").arg(filename));
+ clear();
+ return;
+ }
+
+ stream.clear();
+
+ if (!validVersion) {
+ clear();
+ emit error(tr("Invalid version of QML Trace file."));
+ return;
+ }
+
+ // move the buffered data to the details cache
+ foreach (QmlRangeEventData *desc, descriptionBuffer.values()) {
+ desc->eventHashStr = getHashStringForQmlEvent(desc->location, desc->eventType);;
+ d->rangeEventDictionary[desc->eventHashStr] = desc;
+ }
+
+ // sort startTimeSortedList
+ if (!startTimesAreSorted) {
+ qSort(d->startInstanceList.begin(), d->startInstanceList.end(), compareStartTimes);
+ for (int i = 0; i< d->startInstanceList.count(); i++) {
+ QmlRangeEventStartInstance startTimeData = d->startInstanceList[i];
+ d->endInstanceList[startTimeData.endTimeIndex].startTimeIndex = i;
+ }
+ qSort(d->endInstanceList.begin(), d->endInstanceList.end(), compareStartIndexes);
+ }
+
+ emit countChanged();
+
+ descriptionBuffer.clear();
+
+ setState(ProcessingData);
+ d->postProcess();
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+
+QmlProfilerDataModel::State QmlProfilerDataModel::currentState() const
+{
+ return d->listState;
+}
+
+int QmlProfilerDataModel::getCurrentStateFromQml() const
+{
+ return (int)d->listState;
+}
+
+void QmlProfilerDataModel::setState(QmlProfilerDataModel::State state)
+{
+ // It's not an error, we are continuously calling "AcquiringData" for example
+ if (d->listState == state)
+ return;
+
+ switch (state) {
+ case Empty:
+ // if it's not empty, complain but go on
+ QTC_ASSERT(isEmpty(), /**/);
+ break;
+ case AcquiringData:
+ // we're not supposed to receive new data while processing older data
+ QTC_ASSERT(d->listState != ProcessingData, return);
+ break;
+ case ProcessingData:
+ QTC_ASSERT(d->listState == AcquiringData, return);
+ break;
+ case Done:
+ QTC_ASSERT(d->listState == ProcessingData || d->listState == Empty, return);
+ break;
+ default:
+ emit error("Trying to set unknown state in events list");
+ break;
+ }
+
+ d->listState = state;
+ emit stateChanged();
+
+ // special: if we were done with an empty list, clean internal data and go back to empty
+ if (d->listState == Done && isEmpty()) {
+ clear();
+ }
+ return;
+}
+
+} // namespace Internal
+} // namespace QmlProfiler
+
+QT_BEGIN_NAMESPACE
+Q_DECLARE_TYPEINFO(QmlProfiler::Internal::QmlRangeEventData, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QmlProfiler::Internal::QmlRangeEventStartInstance, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QmlProfiler::Internal::QmlRangeEventEndInstance, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QmlProfiler::Internal::QmlRangeEventRelative, Q_MOVABLE_TYPE);
+QT_END_NAMESPACE
diff --git a/src/libs/qmljsdebugclient/qmlprofilereventlist.h b/src/plugins/qmlprofiler/qmlprofilerdatamodel.h
index 32f5203c2a..7e2ab03470 100644
--- a/src/libs/qmljsdebugclient/qmlprofilereventlist.h
+++ b/src/plugins/qmlprofiler/qmlprofilerdatamodel.h
@@ -30,33 +30,37 @@
**
**************************************************************************/
-#ifndef QMLPROFILEREVENTLIST_H
-#define QMLPROFILEREVENTLIST_H
+#ifndef QMLPROFILERDATAMODEL_H
+#define QMLPROFILERDATAMODEL_H
-#include "qmlprofilereventtypes.h"
-#include "qmlprofilereventlocation.h"
-#include "qmljsdebugclient_global.h"
+#include <qmljsdebugclient/qmlprofilereventtypes.h>
+#include <qmljsdebugclient/qmlprofilereventlocation.h>
+#include "qv8profilerdatamodel.h"
#include <QHash>
#include <QObject>
-namespace QmlJsDebugClient {
+namespace QmlProfiler {
+namespace Internal {
-struct QmlEventSub;
-struct QV8EventSub;
+// used for parents and children
+struct QmlRangeEventRelative;
-struct QMLJSDEBUGCLIENT_EXPORT QmlEventData
+struct QmlRangeEventData
{
- QmlEventData();
- ~QmlEventData();
+ QmlRangeEventData();
+ ~QmlRangeEventData();
- QString displayname;
+ int eventId;
+ QString displayName;
QString eventHashStr;
QString details;
- QmlEventLocation location;
+ QmlJsDebugClient::QmlEventLocation location;
QmlJsDebugClient::QmlEventType eventType;
- QHash <QString, QmlEventSub *> parentHash;
- QHash <QString, QmlEventSub *> childrenHash;
+
+ QHash <QString, QmlRangeEventRelative *> parentHash;
+ QHash <QString, QmlRangeEventRelative *> childrenHash;
+
qint64 duration;
qint64 calls;
qint64 minTime;
@@ -64,54 +68,22 @@ struct QMLJSDEBUGCLIENT_EXPORT QmlEventData
double timePerCall;
double percentOfTime;
qint64 medianTime;
- int eventId;
+
bool isBindingLoop;
- QmlEventData &operator=(const QmlEventData &ref);
+ QmlRangeEventData &operator=(const QmlRangeEventData &ref);
};
-struct QMLJSDEBUGCLIENT_EXPORT QmlEventSub {
- QmlEventSub(QmlEventData *from) : reference(from), duration(0), calls(0), inLoopPath(false) {}
- QmlEventSub(QmlEventSub *from) : reference(from->reference), duration(from->duration), calls(from->calls), inLoopPath(from->inLoopPath) {}
- QmlEventData *reference;
+struct QmlRangeEventRelative {
+ QmlRangeEventRelative(QmlRangeEventData *from) : reference(from), duration(0), calls(0), inLoopPath(false) {}
+ QmlRangeEventRelative(QmlRangeEventRelative *from) : reference(from->reference), duration(from->duration), calls(from->calls), inLoopPath(from->inLoopPath) {}
+ QmlRangeEventData *reference;
qint64 duration;
qint64 calls;
bool inLoopPath;
};
-struct QMLJSDEBUGCLIENT_EXPORT QV8EventData
-{
- QV8EventData();
- ~QV8EventData();
-
- QString displayName;
- QString filename;
- QString functionName;
- int line;
- double totalTime; // given in milliseconds
- double totalPercent;
- double selfTime;
- double selfPercent;
- QHash <QString, QV8EventSub *> parentHash;
- QHash <QString, QV8EventSub *> childrenHash;
- int eventId;
-
- QV8EventData &operator=(const QV8EventData &ref);
-};
-
-struct QMLJSDEBUGCLIENT_EXPORT QV8EventSub {
- QV8EventSub(QV8EventData *from) : reference(from), totalTime(0) {}
- QV8EventSub(QV8EventSub *from) : reference(from->reference), totalTime(from->totalTime) {}
-
- QV8EventData *reference;
- qint64 totalTime;
-};
-
-typedef QHash<QString, QmlEventData *> QmlEventHash;
-typedef QList<QmlEventData *> QmlEventDescriptions;
-typedef QList<QV8EventData *> QV8EventDescriptions;
-
-class QMLJSDEBUGCLIENT_EXPORT QmlProfilerEventList : public QObject
+class QmlProfilerDataModel : public QObject
{
Q_OBJECT
public:
@@ -122,23 +94,30 @@ public:
Done
};
- explicit QmlProfilerEventList(QObject *parent = 0);
- ~QmlProfilerEventList();
+ explicit QmlProfilerDataModel(QObject *parent = 0);
+ ~QmlProfilerDataModel();
- QmlEventDescriptions getEventDescriptions() const;
- QmlEventData *eventDescription(int eventId) const;
- const QV8EventDescriptions& getV8Events() const;
+ QList<QmlRangeEventData *> getEventDescriptions() const;
+ QmlRangeEventData *eventDescription(int eventId) const;
+ QList<QV8EventData *> getV8Events() const;
QV8EventData *v8EventDescription(int eventId) const;
+ static QString getHashStringForQmlEvent(const QmlJsDebugClient::QmlEventLocation &location, int eventType);
+ static QString getHashStringForV8Event(const QString &displayName, const QString &function);
+ static QString rootEventName();
+ static QString rootEventDescription();
+ static QString qmlEventTypeAsString(QmlJsDebugClient::QmlEventType typeEnum);
+ static QmlJsDebugClient::QmlEventType qmlEventTypeAsEnum(const QString &typeString);
+
int findFirstIndex(qint64 startTime) const;
int findFirstIndexNoParents(qint64 startTime) const;
int findLastIndex(qint64 endTime) const;
Q_INVOKABLE qint64 firstTimeMark() const;
Q_INVOKABLE qint64 lastTimeMark() const;
- Q_INVOKABLE int count() const;
-
// data access
+ Q_INVOKABLE int count() const;
+ Q_INVOKABLE bool isEmpty() const;
Q_INVOKABLE qint64 getStartTime(int index) const;
Q_INVOKABLE qint64 getEndTime(int index) const;
Q_INVOKABLE qint64 getDuration(int index) const;
@@ -171,7 +150,6 @@ public:
Q_INVOKABLE qint64 qmlMeasuredTime() const;
Q_INVOKABLE qint64 v8MeasuredTime() const;
- void showErrorDialog(const QString &st ) const;
void compileStatistics(qint64 startTime, qint64 endTime);
State currentState() const;
Q_INVOKABLE int getCurrentStateFromQml() const;
@@ -188,44 +166,37 @@ signals:
public slots:
void clear();
+
void addRangedEvent(int type, qint64 startTime, qint64 length,
const QStringList &data, const QmlJsDebugClient::QmlEventLocation &location);
- void complete();
-
void addV8Event(int depth,const QString &function,const QString &filename, int lineNumber, double totalTime, double selfTime);
void addFrameEvent(qint64 time, int framerate, int animationcount);
+ void setTraceStartTime(qint64 time);
+ void setTraceEndTime(qint64 time);
+
+ void complete();
+
bool save(const QString &filename);
void load(const QString &filename);
void setFilename(const QString &filename);
void load();
- void setTraceEndTime( qint64 time );
- void setTraceStartTime( qint64 time );
-
void rewriteDetailsString(int eventType, const QmlJsDebugClient::QmlEventLocation &location, const QString &newString);
void finishedRewritingDetails();
private:
- void postProcess();
- void sortEndTimes();
- void findAnimationLimits();
- void sortStartTimes();
- void computeLevels();
- void computeNestingLevels();
- void computeNestingDepth();
- void prepareForDisplay();
- void linkEndsToStarts();
- void reloadDetails();
- void findBindingLoops(qint64 startTime, qint64 endTime);
- bool checkBindingLoop(QmlEventData *from, QmlEventData *current, QList<QmlEventData *>visited);
void setState(State state);
+ void reloadDetails();
private:
- class QmlProfilerEventListPrivate;
- QmlProfilerEventListPrivate *d;
+ class QmlProfilerDataModelPrivate;
+ QmlProfilerDataModelPrivate *d;
+
+ friend class QV8ProfilerDataModel;
};
-} // namespace QmlJsDebugClient
+} // namespace Internal
+} // namespace QmlProfiler
-#endif // QMLPROFILEREVENTLIST_H
+#endif // QMLPROFILERDATAMODEL_H
diff --git a/src/plugins/qmlprofiler/qmlprofilerdetailsrewriter.h b/src/plugins/qmlprofiler/qmlprofilerdetailsrewriter.h
index 561717c890..4f61f8a640 100644
--- a/src/plugins/qmlprofiler/qmlprofilerdetailsrewriter.h
+++ b/src/plugins/qmlprofiler/qmlprofilerdetailsrewriter.h
@@ -35,7 +35,7 @@
#include <QObject>
-#include "qmljsdebugclient/qmlprofilereventlocation.h"
+#include <qmljsdebugclient/qmlprofilereventlocation.h>
#include <qmljs/qmljsdocument.h>
namespace QmlProfiler {
diff --git a/src/plugins/qmlprofiler/qmlprofilerengine.cpp b/src/plugins/qmlprofiler/qmlprofilerengine.cpp
index 5487711e68..5990df7c4c 100644
--- a/src/plugins/qmlprofiler/qmlprofilerengine.cpp
+++ b/src/plugins/qmlprofiler/qmlprofilerengine.cpp
@@ -35,8 +35,6 @@
#include "codaqmlprofilerrunner.h"
#include "localqmlprofilerrunner.h"
#include "remotelinuxqmlprofilerrunner.h"
-#include "qmlprofilerplugin.h"
-#include "qmlprofilertool.h"
#include <analyzerbase/analyzermanager.h>
#include <coreplugin/icore.h>
@@ -76,16 +74,11 @@ public:
QmlProfilerEngine *q;
- //AnalyzerStartParameters m_params;
+ QmlProfilerStateManager *m_profilerState;
+
AbstractQmlProfilerRunner *m_runner;
- bool m_running;
- bool m_fetchingData;
- bool m_hasData;
- bool m_fetchDataFromStart;
- bool m_delayedDelete;
QTimer m_noDebugOutputTimer;
QmlJsDebugClient::QDeclarativeOutputParser m_outputParser;
- QTimer m_runningTimer;
};
AbstractQmlProfilerRunner *
@@ -137,10 +130,7 @@ QmlProfilerEngine::QmlProfilerEngine(IAnalyzerTool *tool,
: IAnalyzerEngine(tool, sp, runConfiguration)
, d(new QmlProfilerEnginePrivate(this))
{
- d->m_running = false;
- d->m_fetchingData = false;
- d->m_fetchDataFromStart = false;
- d->m_delayedDelete = false;
+ d->m_profilerState = 0;
// Only wait 4 seconds for the 'Waiting for connection' on application ouput, then just try to connect
// (application output might be redirected / blocked)
@@ -157,29 +147,31 @@ QmlProfilerEngine::QmlProfilerEngine(IAnalyzerTool *tool,
this, SLOT(processIsRunning()));
connect(&d->m_outputParser, SIGNAL(errorMessage(QString)),
this, SLOT(wrongSetupMessageBox(QString)));
-
- d->m_runningTimer.setInterval(100); // ten times per second
- connect(&d->m_runningTimer, SIGNAL(timeout()), this, SIGNAL(timeUpdate()));
}
QmlProfilerEngine::~QmlProfilerEngine()
{
- if (d->m_running)
+ if (d->m_profilerState && d->m_profilerState->currentState() == QmlProfilerStateManager::AppRunning)
stop();
delete d;
}
bool QmlProfilerEngine::start()
{
+ QTC_ASSERT(d->m_profilerState, return false);
+
if (d->m_runner) {
delete d->m_runner;
d->m_runner = 0;
}
+ d->m_profilerState->setCurrentState(QmlProfilerStateManager::AppStarting);
+
if (QmlProjectManager::QmlProjectRunConfiguration *rc =
qobject_cast<QmlProjectManager::QmlProjectRunConfiguration *>(runConfiguration())) {
if (rc->observerPath().isEmpty()) {
QmlProjectManager::QmlProjectPlugin::showQmlObserverToolWarning();
+ d->m_profilerState->setCurrentState(QmlProfilerStateManager::Idle);
AnalyzerManager::stopTool();
return false;
}
@@ -190,13 +182,14 @@ bool QmlProfilerEngine::start()
if (LocalQmlProfilerRunner *qmlRunner = qobject_cast<LocalQmlProfilerRunner *>(d->m_runner)) {
if (!qmlRunner->hasExecutable()) {
showNonmodalWarning(tr("No executable file to launch."));
+ d->m_profilerState->setCurrentState(QmlProfilerStateManager::Idle);
AnalyzerManager::stopTool();
return false;
}
}
if (d->m_runner) {
- connect(d->m_runner, SIGNAL(stopped()), this, SLOT(stopped()));
+ connect(d->m_runner, SIGNAL(stopped()), this, SLOT(processEnded()));
connect(d->m_runner, SIGNAL(appendMessage(QString,Utils::OutputFormat)),
this, SLOT(logApplicationMessage(QString,Utils::OutputFormat)));
d->m_runner->start();
@@ -205,81 +198,80 @@ bool QmlProfilerEngine::start()
emit processRunning(startParameters().connParams.port);
}
-
- d->m_running = true;
- d->m_delayedDelete = false;
- d->m_runningTimer.start();
-
- if (d->m_fetchDataFromStart) {
- d->m_fetchingData = true;
- d->m_hasData = false;
- }
-
+ d->m_profilerState->setCurrentState(QmlProfilerStateManager::AppRunning);
emit starting(this);
return true;
}
void QmlProfilerEngine::stop()
{
- if (d->m_fetchingData) {
- if (d->m_running)
- d->m_delayedDelete = true;
- // will result in dataReceived() call
- emit stopRecording();
- d->m_fetchDataFromStart = true;
- } else {
- finishProcess();
- d->m_fetchDataFromStart = false;
+ QTC_ASSERT(d->m_profilerState, return);
+
+ switch (d->m_profilerState->currentState()) {
+ case QmlProfilerStateManager::AppRunning : {
+ d->m_profilerState->setCurrentState(QmlProfilerStateManager::AppStopRequested);
+ break;
+ }
+ case QmlProfilerStateManager::AppReadyToStop : {
+ cancelProcess();
+ break;
+ }
+ case QmlProfilerStateManager::AppKilled : {
+ d->m_profilerState->setCurrentState(QmlProfilerStateManager::Idle);
+ break;
+ }
+ default:
+ qDebug() << tr("Unexpected engine stop from state %1 in %2:%3").arg(d->m_profilerState->currentStateAsString(), QString(__FILE__), QString::number(__LINE__));
+ break;
}
}
-void QmlProfilerEngine::stopped()
+void QmlProfilerEngine::processEnded()
{
- // if it was killed, preserve recording flag
- if (d->m_running)
- d->m_fetchDataFromStart = d->m_fetchingData;
-
- // user feedback
- if (d->m_running && d->m_fetchingData && !d->m_hasData) {
- showNonmodalWarning(tr("Application finished before loading profiled data.\n Please use the stop button instead."));
- emit applicationDied();
- }
+ QTC_ASSERT(d->m_profilerState, return);
- d->m_running = false;
- d->m_runningTimer.stop();
- AnalyzerManager::stopTool();
- emit finished();
- emit recordingChanged(d->m_fetchDataFromStart);
-}
+ switch (d->m_profilerState->currentState()) {
+ case QmlProfilerStateManager::AppRunning : {
+ d->m_profilerState->setCurrentState(QmlProfilerStateManager::AppKilled);
+ AnalyzerManager::stopTool();
-void QmlProfilerEngine::setFetchingData(bool b)
-{
- d->m_fetchingData = b;
- if (d->m_running && b)
- d->m_hasData = false;
- if (!d->m_running)
- d->m_fetchDataFromStart = b;
+ emit finished();
+ break;
+ }
+ case QmlProfilerStateManager::AppStopped :
+ // fallthrough
+ case QmlProfilerStateManager::AppKilled : {
+ d->m_profilerState->setCurrentState(QmlProfilerStateManager::Idle);
+ break;
+ }
+ default:
+ qDebug() << tr("Process died unexpectedly from state %1 in %2:%3").arg(d->m_profilerState->currentStateAsString(), QString(__FILE__), QString::number(__LINE__));
+ break;
+ }
}
-void QmlProfilerEngine::dataReceived()
+void QmlProfilerEngine::cancelProcess()
{
- if (d->m_delayedDelete)
- finishProcess();
- d->m_delayedDelete = false;
- d->m_hasData = true;
-}
+ QTC_ASSERT(d->m_profilerState, return);
-void QmlProfilerEngine::finishProcess()
-{
- // user stop?
- if (d->m_running) {
- d->m_running = false;
- d->m_runningTimer.stop();
- if (d->m_runner)
- d->m_runner->stop();
- emit finished();
- emit recordingChanged(d->m_fetchDataFromStart);
+ switch (d->m_profilerState->currentState()) {
+ case QmlProfilerStateManager::AppReadyToStop : {
+ d->m_profilerState->setCurrentState(QmlProfilerStateManager::AppStopped);
+ break;
+ }
+ case QmlProfilerStateManager::AppRunning : {
+ d->m_profilerState->setCurrentState(QmlProfilerStateManager::AppKilled);
+ break;
+ }
+ default: {
+ qDebug() << tr("Unexpected process termination requested with state %1 in %2:%3").arg(d->m_profilerState->currentStateAsString(), QString(__FILE__), QString::number(__LINE__));
+ return;
+ }
}
+
+ if (d->m_runner)
+ d->m_runner->stop();
+ emit finished();
}
void QmlProfilerEngine::logApplicationMessage(const QString &msg, Utils::OutputFormat format)
@@ -305,11 +297,10 @@ void QmlProfilerEngine::wrongSetupMessageBox(const QString &errorMessage)
infoBox->show();
- d->m_running = false;
- d->m_runningTimer.stop();
+ // KILL
+ d->m_profilerState->setCurrentState(QmlProfilerStateManager::AppKilled);
AnalyzerManager::stopTool();
emit finished();
- emit recordingChanged(d->m_fetchDataFromStart);
}
void QmlProfilerEngine::wrongSetupMessageBoxFinished(int button)
@@ -348,5 +339,44 @@ void QmlProfilerEngine::processIsRunning(quint16 port)
emit processRunning(d->m_runner->debugPort());
}
+////////////////////////////////////////////////////////////////
+// Profiler State
+void QmlProfilerEngine::registerProfilerStateManager( QmlProfilerStateManager *profilerState )
+{
+ // disconnect old
+ if (d->m_profilerState) {
+ disconnect(d->m_profilerState, SIGNAL(stateChanged()), this, SLOT(profilerStateChanged()));
+ }
+
+ d->m_profilerState = profilerState;
+
+ // connect
+ if (d->m_profilerState) {
+ connect(d->m_profilerState, SIGNAL(stateChanged()), this, SLOT(profilerStateChanged()));
+ }
+}
+
+void QmlProfilerEngine::profilerStateChanged()
+{
+ switch (d->m_profilerState->currentState()) {
+ case QmlProfilerStateManager::AppReadyToStop : {
+ cancelProcess();
+ break;
+ }
+ case QmlProfilerStateManager::Idle : {
+ // for some reason the engine is not deleted when it goes to idle
+ // a new one will be created on the next run, and this one will
+ // be only deleted if the new one is running the same app
+
+ // we need to explictly disconnect it here without expecting a deletion
+ // as it will not be run any more, otherwise we will get funny side effects
+ registerProfilerStateManager(0);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
} // namespace Internal
} // namespace QmlProfiler
diff --git a/src/plugins/qmlprofiler/qmlprofilerengine.h b/src/plugins/qmlprofiler/qmlprofilerengine.h
index aade80edb7..c438e69329 100644
--- a/src/plugins/qmlprofiler/qmlprofilerengine.h
+++ b/src/plugins/qmlprofiler/qmlprofilerengine.h
@@ -34,6 +34,7 @@
#define QMLPROFILERENGINE_H
#include <analyzerbase/ianalyzerengine.h>
+#include "qmlprofilerstatemanager.h"
#include <utils/outputformat.h>
namespace QmlProfiler {
@@ -49,29 +50,29 @@ public:
ProjectExplorer::RunConfiguration *runConfiguration);
~QmlProfilerEngine();
+ void registerProfilerStateManager( QmlProfilerStateManager *profilerState );
+
static void showNonmodalWarning(const QString &warningMsg);
signals:
void processRunning(quint16 port);
- void stopRecording();
void timeUpdate();
- void recordingChanged(bool recording);
- void applicationDied();
public slots:
bool start();
void stop();
private slots:
- void stopped();
+ void processEnded();
- void setFetchingData(bool);
- void dataReceived();
- void finishProcess();
+ void cancelProcess();
void logApplicationMessage(const QString &msg, Utils::OutputFormat format);
void wrongSetupMessageBox(const QString &errorMessage);
void wrongSetupMessageBoxFinished(int);
void processIsRunning(quint16 port = 0);
+private slots:
+ void profilerStateChanged();
+
private:
class QmlProfilerEnginePrivate;
QmlProfilerEnginePrivate *d;
diff --git a/src/plugins/qmlprofiler/qmlprofilereventview.cpp b/src/plugins/qmlprofiler/qmlprofilereventview.cpp
index b59fdc4c7a..3630626cac 100644
--- a/src/plugins/qmlprofiler/qmlprofilereventview.cpp
+++ b/src/plugins/qmlprofiler/qmlprofilereventview.cpp
@@ -48,6 +48,11 @@
#include <QVBoxLayout>
#include <QHBoxLayout>
+#include "qmlprofilerviewmanager.h"
+#include "qmlprofilertool.h"
+#include <QMenu>
+
+#include <utils/qtcassert.h>
using namespace QmlJsDebugClient;
@@ -90,31 +95,63 @@ public:
////////////////////////////////////////////////////////////////////////////////////
-QmlProfilerEventsWidget::QmlProfilerEventsWidget(QmlJsDebugClient::QmlProfilerEventList *model, QWidget *parent) : QWidget(parent)
+class QmlProfilerEventsWidget::QmlProfilerEventsWidgetPrivate
{
- setObjectName("QmlProfilerEventsView");
+public:
+ QmlProfilerEventsWidgetPrivate(QmlProfilerEventsWidget *qq):q(qq) {}
+ ~QmlProfilerEventsWidgetPrivate() {}
- m_eventTree = new QmlProfilerEventsMainView(model, this);
- m_eventTree->setViewType(QmlProfilerEventsMainView::EventsView);
- connect(m_eventTree, SIGNAL(gotoSourceLocation(QString,int,int)), this, SIGNAL(gotoSourceLocation(QString,int,int)));
- connect(m_eventTree, SIGNAL(showEventInTimeline(int)), this, SIGNAL(showEventInTimeline(int)));
+ QmlProfilerEventsWidget *q;
- m_eventChildren = new QmlProfilerEventsParentsAndChildrenView(model, QmlProfilerEventsParentsAndChildrenView::ChildrenView, this);
- m_eventParents = new QmlProfilerEventsParentsAndChildrenView(model, QmlProfilerEventsParentsAndChildrenView::ParentsView, this);
- connect(m_eventTree, SIGNAL(eventSelected(int)), m_eventChildren, SLOT(displayEvent(int)));
- connect(m_eventTree, SIGNAL(eventSelected(int)), m_eventParents, SLOT(displayEvent(int)));
- connect(m_eventChildren, SIGNAL(eventClicked(int)), m_eventTree, SLOT(selectEvent(int)));
- connect(m_eventParents, SIGNAL(eventClicked(int)), m_eventTree, SLOT(selectEvent(int)));
+ Analyzer::IAnalyzerTool *m_profilerTool;
+ QmlProfilerViewManager *m_viewContainer;
+
+ QmlProfilerEventsMainView *m_eventTree;
+ QmlProfilerEventsParentsAndChildrenView *m_eventChildren;
+ QmlProfilerEventsParentsAndChildrenView *m_eventParents;
+ QmlProfilerDataModel *m_profilerDataModel;
+
+ bool m_globalStatsEnabled;
+};
+
+QmlProfilerEventsWidget::QmlProfilerEventsWidget(QWidget *parent,
+ Analyzer::IAnalyzerTool *profilerTool,
+ QmlProfilerViewManager *container,
+ QmlProfilerDataModel *profilerDataModel )
+ : QWidget(parent), d(new QmlProfilerEventsWidgetPrivate(this))
+{
+ setObjectName("QmlProfilerEventsView");
+
+ d->m_profilerDataModel = profilerDataModel;
+ connect(d->m_profilerDataModel, SIGNAL(stateChanged()),
+ this, SLOT(profilerDataModelStateChanged()));
+
+ d->m_eventTree = new QmlProfilerEventsMainView(QmlProfilerEventsMainView::EventsView, this, d->m_profilerDataModel);
+ connect(d->m_eventTree, SIGNAL(gotoSourceLocation(QString,int,int)), this, SIGNAL(gotoSourceLocation(QString,int,int)));
+ connect(d->m_eventTree, SIGNAL(showEventInTimeline(int)), this, SIGNAL(showEventInTimeline(int)));
+
+ d->m_eventChildren = new QmlProfilerEventsParentsAndChildrenView(
+ QmlProfilerEventsParentsAndChildrenView::ChildrenView,
+ this,
+ d->m_profilerDataModel);
+ d->m_eventParents = new QmlProfilerEventsParentsAndChildrenView(
+ QmlProfilerEventsParentsAndChildrenView::ParentsView,
+ this,
+ d->m_profilerDataModel);
+ connect(d->m_eventTree, SIGNAL(eventSelected(int)), d->m_eventChildren, SLOT(displayEvent(int)));
+ connect(d->m_eventTree, SIGNAL(eventSelected(int)), d->m_eventParents, SLOT(displayEvent(int)));
+ connect(d->m_eventChildren, SIGNAL(eventClicked(int)), d->m_eventTree, SLOT(selectEvent(int)));
+ connect(d->m_eventParents, SIGNAL(eventClicked(int)), d->m_eventTree, SLOT(selectEvent(int)));
// widget arrangement
QVBoxLayout *groupLayout = new QVBoxLayout;
groupLayout->setContentsMargins(0,0,0,0);
groupLayout->setSpacing(0);
Core::MiniSplitter *splitterVertical = new Core::MiniSplitter;
- splitterVertical->addWidget(m_eventTree);
+ splitterVertical->addWidget(d->m_eventTree);
Core::MiniSplitter *splitterHorizontal = new Core::MiniSplitter;
- splitterHorizontal->addWidget(m_eventParents);
- splitterHorizontal->addWidget(m_eventChildren);
+ splitterHorizontal->addWidget(d->m_eventParents);
+ splitterHorizontal->addWidget(d->m_eventChildren);
splitterHorizontal->setOrientation(Qt::Horizontal);
splitterVertical->addWidget(splitterHorizontal);
splitterVertical->setOrientation(Qt::Vertical);
@@ -123,23 +160,21 @@ QmlProfilerEventsWidget::QmlProfilerEventsWidget(QmlJsDebugClient::QmlProfilerEv
groupLayout->addWidget(splitterVertical);
setLayout(groupLayout);
- m_eventStatistics = model;
- if (model) {
- connect(model, SIGNAL(stateChanged()), this, SLOT(eventListStateChanged()));
- }
-
- m_globalStatsEnabled = true;
+ d->m_profilerTool = profilerTool;
+ d->m_viewContainer = container;
+ d->m_globalStatsEnabled = true;
}
QmlProfilerEventsWidget::~QmlProfilerEventsWidget()
{
+ delete d;
}
-void QmlProfilerEventsWidget::eventListStateChanged()
+void QmlProfilerEventsWidget::profilerDataModelStateChanged()
{
- if (m_eventStatistics) {
- QmlProfilerEventList::State newState = m_eventStatistics->currentState();
- if (newState == QmlProfilerEventList::Empty) {
+ if (d->m_profilerDataModel) {
+ QmlProfilerDataModel::State newState = d->m_profilerDataModel->currentState();
+ if (newState == QmlProfilerDataModel::Empty) {
clear();
}
}
@@ -148,57 +183,119 @@ void QmlProfilerEventsWidget::eventListStateChanged()
void QmlProfilerEventsWidget::switchToV8View()
{
setObjectName("QmlProfilerV8ProfileView");
- m_eventTree->setViewType(QmlProfilerEventsMainView::V8ProfileView);
- m_eventParents->setViewType(QmlProfilerEventsParentsAndChildrenView::V8ParentsView);
- m_eventChildren->setViewType(QmlProfilerEventsParentsAndChildrenView::V8ChildrenView);
+ d->m_eventTree->setViewType(QmlProfilerEventsMainView::V8ProfileView);
+ d->m_eventParents->setViewType(QmlProfilerEventsParentsAndChildrenView::V8ParentsView);
+ d->m_eventChildren->setViewType(QmlProfilerEventsParentsAndChildrenView::V8ChildrenView);
setToolTip(tr("Trace information from the v8 JavaScript engine. Available only in Qt5 based applications"));
}
void QmlProfilerEventsWidget::clear()
{
- m_eventTree->clear();
- m_eventChildren->clear();
- m_eventParents->clear();
+ d->m_eventTree->clear();
+ d->m_eventChildren->clear();
+ d->m_eventParents->clear();
}
void QmlProfilerEventsWidget::getStatisticsInRange(qint64 rangeStart, qint64 rangeEnd)
{
clear();
- m_eventTree->getStatisticsInRange(rangeStart, rangeEnd);
- m_globalStatsEnabled = m_eventTree->isRangeGlobal(rangeStart, rangeEnd);
+ d->m_eventTree->getStatisticsInRange(rangeStart, rangeEnd);
+ d->m_globalStatsEnabled = d->m_eventTree->isRangeGlobal(rangeStart, rangeEnd);
}
QModelIndex QmlProfilerEventsWidget::selectedItem() const
{
- return m_eventTree->selectedItem();
+ return d->m_eventTree->selectedItem();
}
void QmlProfilerEventsWidget::contextMenuEvent(QContextMenuEvent *ev)
{
- emit contextMenuRequested(ev->globalPos());
+ QTC_ASSERT(d->m_viewContainer, return;);
+
+ QMenu menu;
+ QAction *copyRowAction = 0;
+ QAction *copyTableAction = 0;
+ QAction *showExtendedStatsAction = 0;
+ QAction *getLocalStatsAction = 0;
+ QAction *getGlobalStatsAction = 0;
+
+ QmlProfilerTool *profilerTool = qobject_cast<QmlProfilerTool *>(d->m_profilerTool);
+ QPoint position = ev->globalPos();
+
+ if (profilerTool) {
+ QList <QAction *> commonActions = profilerTool->profilerContextMenuActions();
+ foreach (QAction *act, commonActions) {
+ menu.addAction(act);
+ }
+ }
+
+ if (mouseOnTable(position)) {
+ menu.addSeparator();
+ if (selectedItem().isValid())
+ copyRowAction = menu.addAction(tr("Copy Row"));
+ copyTableAction = menu.addAction(tr("Copy Table"));
+
+ if (isQml()) {
+ // only for qml events view, not for v8
+ showExtendedStatsAction = menu.addAction(tr("Extended Event Statistics"));
+ showExtendedStatsAction->setCheckable(true);
+ showExtendedStatsAction->setChecked(showExtendedStatistics());
+ }
+ }
+
+ if (isQml()) {
+ menu.addSeparator();
+ getLocalStatsAction = menu.addAction(tr("Limit Events Pane to Current Range"));
+ if (!d->m_viewContainer->hasValidSelection())
+ getLocalStatsAction->setEnabled(false);
+ getGlobalStatsAction = menu.addAction(tr("Reset Events Pane"));
+ if (hasGlobalStats())
+ getGlobalStatsAction->setEnabled(false);
+ }
+
+ QAction *selectedAction = menu.exec(position);
+
+ if (selectedAction) {
+ if (selectedAction == copyRowAction)
+ copyRowToClipboard();
+ if (selectedAction == copyTableAction)
+ copyTableToClipboard();
+ if (selectedAction == getLocalStatsAction) {
+ getStatisticsInRange(d->m_viewContainer->selectionStart(),
+ d->m_viewContainer->selectionEnd());
+ }
+ if (selectedAction == getGlobalStatsAction) {
+ if (d->m_profilerDataModel) {
+ getStatisticsInRange(d->m_profilerDataModel->traceStartTime(),
+ d->m_profilerDataModel->traceEndTime());
+ }
+ }
+ if (selectedAction == showExtendedStatsAction)
+ setShowExtendedStatistics(!showExtendedStatistics());
+ }
}
bool QmlProfilerEventsWidget::mouseOnTable(const QPoint &position) const
{
- QPoint tableTopLeft = m_eventTree->mapToGlobal(QPoint(0,0));
- QPoint tableBottomRight = m_eventTree->mapToGlobal(QPoint(m_eventTree->width(), m_eventTree->height()));
+ QPoint tableTopLeft = d->m_eventTree->mapToGlobal(QPoint(0,0));
+ QPoint tableBottomRight = d->m_eventTree->mapToGlobal(QPoint(d->m_eventTree->width(), d->m_eventTree->height()));
return (position.x() >= tableTopLeft.x() && position.x() <= tableBottomRight.x() && position.y() >= tableTopLeft.y() && position.y() <= tableBottomRight.y());
}
void QmlProfilerEventsWidget::copyTableToClipboard() const
{
- m_eventTree->copyTableToClipboard();
+ d->m_eventTree->copyTableToClipboard();
}
void QmlProfilerEventsWidget::copyRowToClipboard() const
{
- m_eventTree->copyRowToClipboard();
+ d->m_eventTree->copyRowToClipboard();
}
void QmlProfilerEventsWidget::updateSelectedEvent(int eventId) const
{
- if (m_eventTree->selectedEventId() != eventId)
- m_eventTree->selectEvent(eventId);
+ if (d->m_eventTree->selectedEventId() != eventId)
+ d->m_eventTree->selectEvent(eventId);
}
void QmlProfilerEventsWidget::selectBySourceLocation(const QString &filename, int line, int column)
@@ -207,22 +304,31 @@ void QmlProfilerEventsWidget::selectBySourceLocation(const QString &filename, in
// Our javascript trace data does not store column information
// thus we ignore it here
Q_UNUSED(column);
- m_eventTree->selectEventByLocation(filename, line);
+ d->m_eventTree->selectEventByLocation(filename, line);
}
bool QmlProfilerEventsWidget::hasGlobalStats() const
{
- return m_globalStatsEnabled;
+ return d->m_globalStatsEnabled;
}
void QmlProfilerEventsWidget::setShowExtendedStatistics(bool show)
{
- m_eventTree->setShowExtendedStatistics(show);
+ d->m_eventTree->setShowExtendedStatistics(show);
}
bool QmlProfilerEventsWidget::showExtendedStatistics() const
{
- return m_eventTree->showExtendedStatistics();
+ return d->m_eventTree->showExtendedStatistics();
+}
+
+bool QmlProfilerEventsWidget::isQml() const
+{
+ return d->m_eventTree->viewType() == QmlProfilerEventsMainView::EventsView;
+}
+bool QmlProfilerEventsWidget::isV8() const
+{
+ return d->m_eventTree->viewType() == QmlProfilerEventsMainView::V8ProfileView;
}
////////////////////////////////////////////////////////////////////////////////////
@@ -232,8 +338,8 @@ class QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate
public:
QmlProfilerEventsMainViewPrivate(QmlProfilerEventsMainView *qq) : q(qq) {}
- void buildModelFromList(const QmlEventDescriptions &list, QStandardItem *parentItem, const QmlEventDescriptions &visitedFunctionsList = QmlEventDescriptions() );
- void buildV8ModelFromList( const QV8EventDescriptions &list );
+ void buildModelFromList(const QList<QmlRangeEventData *> &list, QStandardItem *parentItem );
+ void buildV8ModelFromList( const QList<QV8EventData *> &list );
int getFieldCount();
QString textForItem(QStandardItem *item, bool recursive) const;
@@ -242,7 +348,7 @@ public:
QmlProfilerEventsMainView *q;
QmlProfilerEventsMainView::ViewTypes m_viewType;
- QmlProfilerEventList *m_eventStatistics;
+ QmlProfilerDataModel *m_profilerDataModel;
QStandardItemModel *m_model;
QList<bool> m_fieldShown;
QHash<int, int> m_columnIndex; // maps field enum to column index
@@ -254,8 +360,10 @@ public:
////////////////////////////////////////////////////////////////////////////////////
-QmlProfilerEventsMainView::QmlProfilerEventsMainView(QmlProfilerEventList *model, QWidget *parent) :
- QTreeView(parent), d(new QmlProfilerEventsMainViewPrivate(this))
+QmlProfilerEventsMainView::QmlProfilerEventsMainView(ViewTypes viewType,
+ QWidget *parent,
+ QmlProfilerDataModel *dataModel)
+ : QTreeView(parent), d(new QmlProfilerEventsMainViewPrivate(this))
{
setObjectName("QmlProfilerEventsTable");
header()->setResizeMode(QHeaderView::Interactive);
@@ -268,15 +376,17 @@ QmlProfilerEventsMainView::QmlProfilerEventsMainView(QmlProfilerEventList *model
setModel(d->m_model);
connect(this,SIGNAL(clicked(QModelIndex)), this,SLOT(jumpToItem(QModelIndex)));
- d->m_eventStatistics = 0;
- setEventStatisticsModel(model);
+ d->m_profilerDataModel = dataModel;
+ connect(d->m_profilerDataModel,SIGNAL(stateChanged()),
+ this,SLOT(profilerDataModelStateChanged()));
+ connect(d->m_profilerDataModel,SIGNAL(detailsChanged(int,QString)),
+ this,SLOT(changeDetailsForEvent(int,QString)));
d->m_firstNumericColumn = 0;
d->m_preventSelectBounce = false;
d->m_showExtendedStatistics = false;
- // default view
- setViewType(EventsView);
+ setViewType(viewType);
}
QmlProfilerEventsMainView::~QmlProfilerEventsMainView()
@@ -285,24 +395,11 @@ QmlProfilerEventsMainView::~QmlProfilerEventsMainView()
delete d->m_model;
}
-void QmlProfilerEventsMainView::setEventStatisticsModel( QmlProfilerEventList *model )
-{
- if (d->m_eventStatistics) {
- disconnect(d->m_eventStatistics,SIGNAL(stateChanged()),this,SLOT(eventListStateChanged()));
- disconnect(d->m_eventStatistics,SIGNAL(detailsChanged(int,QString)),this,SLOT(changeDetailsForEvent(int,QString)));
- }
- d->m_eventStatistics = model;
- if (model) {
- connect(d->m_eventStatistics,SIGNAL(stateChanged()),this,SLOT(eventListStateChanged()));
- connect(d->m_eventStatistics,SIGNAL(detailsChanged(int,QString)),this,SLOT(changeDetailsForEvent(int,QString)));
- }
-}
-
-void QmlProfilerEventsMainView::eventListStateChanged()
+void QmlProfilerEventsMainView::profilerDataModelStateChanged()
{
- if (d->m_eventStatistics) {
- QmlProfilerEventList::State newState = d->m_eventStatistics->currentState();
- if (newState == QmlProfilerEventList::Done)
+ if (d->m_profilerDataModel) {
+ QmlProfilerDataModel::State newState = d->m_profilerDataModel->currentState();
+ if (newState == QmlProfilerDataModel::Done)
buildModel();
}
}
@@ -319,6 +416,11 @@ void QmlProfilerEventsMainView::setFieldViewable(Fields field, bool show)
}
}
+QmlProfilerEventsMainView::ViewTypes QmlProfilerEventsMainView::viewType() const
+{
+ return d->m_viewType;
+}
+
void QmlProfilerEventsMainView::setViewType(ViewTypes type)
{
d->m_viewType = type;
@@ -465,12 +567,12 @@ int QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate::getFieldCount()
void QmlProfilerEventsMainView::buildModel()
{
- if (d->m_eventStatistics) {
+ if (d->m_profilerDataModel) {
clear();
if (d->m_viewType == V8ProfileView)
- d->buildV8ModelFromList( d->m_eventStatistics->getV8Events() );
+ d->buildV8ModelFromList( d->m_profilerDataModel->getV8Events() );
else
- d->buildModelFromList( d->m_eventStatistics->getEventDescriptions(), d->m_model->invisibleRootItem() );
+ d->buildModelFromList( d->m_profilerDataModel->getEventDescriptions(), d->m_model->invisibleRootItem() );
setShowExtendedStatistics(d->m_showExtendedStatistics);
@@ -488,18 +590,15 @@ void QmlProfilerEventsMainView::buildModel()
}
}
-void QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate::buildModelFromList( const QmlEventDescriptions &list, QStandardItem *parentItem, const QmlEventDescriptions &visitedFunctionsList )
+void QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate::buildModelFromList( const QList<QmlRangeEventData *> &list, QStandardItem *parentItem)
{
- foreach (QmlEventData *binding, list) {
- if (visitedFunctionsList.contains(binding))
- continue;
-
+ foreach (QmlRangeEventData *binding, list) {
if (binding->calls == 0)
continue;
QList<QStandardItem *> newRow;
if (m_fieldShown[Name]) {
- newRow << new EventsViewItem(binding->displayname);
+ newRow << new EventsViewItem(binding->displayName);
}
if (m_fieldShown[Type]) {
@@ -572,7 +671,7 @@ void QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate::buildModelFrom
}
}
-void QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate::buildV8ModelFromList(const QV8EventDescriptions &list)
+void QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate::buildV8ModelFromList(const QList<QV8EventData *> &list)
{
for (int index = 0; index < list.count(); index++) {
QV8EventData *v8event = list.at(index);
@@ -649,13 +748,17 @@ QString QmlProfilerEventsMainView::nameForType(int typeNumber)
void QmlProfilerEventsMainView::getStatisticsInRange(qint64 rangeStart, qint64 rangeEnd)
{
- d->m_eventStatistics->compileStatistics(rangeStart, rangeEnd);
+ if (d->m_profilerDataModel)
+ d->m_profilerDataModel->compileStatistics(rangeStart, rangeEnd);
buildModel();
}
bool QmlProfilerEventsMainView::isRangeGlobal(qint64 rangeStart, qint64 rangeEnd) const
{
- return d->m_eventStatistics->traceStartTime() == rangeStart && d->m_eventStatistics->traceEndTime() == rangeEnd;
+ if (d->m_profilerDataModel)
+ return d->m_profilerDataModel->traceStartTime() == rangeStart && d->m_profilerDataModel->traceEndTime() == rangeEnd;
+ else
+ return true;
}
int QmlProfilerEventsMainView::selectedEventId() const
@@ -691,7 +794,7 @@ void QmlProfilerEventsMainView::jumpToItem(const QModelIndex &index)
// show in callers/callees subwindow
emit eventSelected(infoItem->data(EventIdRole).toInt());
- // show in timelineview
+ // show in timelinerenderer
if (d->m_viewType == EventsView) {
emit showEventInTimeline(infoItem->data(EventIdRole).toInt());
}
@@ -819,9 +922,11 @@ void QmlProfilerEventsMainView::copyRowToClipboard() const
////////////////////////////////////////////////////////////////////////////////////
-QmlProfilerEventsParentsAndChildrenView::QmlProfilerEventsParentsAndChildrenView(QmlJsDebugClient::QmlProfilerEventList *model, SubViewType subtableType, QWidget *parent):QTreeView(parent)
+QmlProfilerEventsParentsAndChildrenView::QmlProfilerEventsParentsAndChildrenView(
+ SubViewType subtableType, QWidget *parent, QmlProfilerDataModel *model)
+ : QTreeView(parent)
{
- m_eventList = model;
+ m_profilerDataModel = model;
setModel(new QStandardItemModel(this));
setRootIsDecorated(false);
setFrameStyle(QFrame::NoFrame);
@@ -843,30 +948,33 @@ void QmlProfilerEventsParentsAndChildrenView::setViewType(SubViewType type)
void QmlProfilerEventsParentsAndChildrenView::displayEvent(int eventId)
{
+ if (!m_profilerDataModel)
+ return;
+
bool isV8 = m_subtableType == V8ParentsView || m_subtableType == V8ChildrenView;
bool isChildren = m_subtableType == ChildrenView || m_subtableType == V8ChildrenView;
if (isV8) {
- QmlJsDebugClient::QV8EventData *v8event = m_eventList->v8EventDescription(eventId);
+ QV8EventData *v8event = m_profilerDataModel->v8EventDescription(eventId);
if (v8event) {
if (isChildren) {
- QList <QmlJsDebugClient::QV8EventSub *> childrenList = v8event->childrenHash.values();
+ QList <QV8EventSub *> childrenList = v8event->childrenHash.values();
rebuildTree((QObject *)&childrenList);
}
else {
- QList <QmlJsDebugClient::QV8EventSub *> parentList = v8event->parentHash.values();
+ QList <QV8EventSub *> parentList = v8event->parentHash.values();
rebuildTree((QObject *)&parentList);
}
}
} else {
- QmlJsDebugClient::QmlEventData *qmlEvent = m_eventList->eventDescription(eventId);
+ QmlRangeEventData *qmlEvent = m_profilerDataModel->eventDescription(eventId);
if (qmlEvent) {
if (isChildren) {
- QList <QmlJsDebugClient::QmlEventSub *> childrenList = qmlEvent->childrenHash.values();
+ QList <QmlRangeEventRelative *> childrenList = qmlEvent->childrenHash.values();
rebuildTree((QObject *)&childrenList);
}
else {
- QList <QmlJsDebugClient::QmlEventSub *> parentList = qmlEvent->parentHash.values();
+ QList <QmlRangeEventRelative *> parentList = qmlEvent->parentHash.values();
rebuildTree((QObject *)&parentList);
}
}
@@ -881,7 +989,7 @@ void QmlProfilerEventsParentsAndChildrenView::displayEvent(int eventId)
sortByColumn(2);
}
-void QmlProfilerEventsParentsAndChildrenView::rebuildTree(void *eventList)
+void QmlProfilerEventsParentsAndChildrenView::rebuildTree(void *profilerDataModel)
{
Q_ASSERT(treeModel());
treeModel()->clear();
@@ -889,8 +997,8 @@ void QmlProfilerEventsParentsAndChildrenView::rebuildTree(void *eventList)
QStandardItem *topLevelItem = treeModel()->invisibleRootItem();
bool isV8 = m_subtableType == V8ParentsView || m_subtableType == V8ChildrenView;
- QList <QmlEventSub *> *qmlList = static_cast< QList <QmlEventSub *> *>(eventList);
- QList <QV8EventSub*> *v8List = static_cast< QList <QV8EventSub *> *>(eventList);
+ QList <QmlRangeEventRelative *> *qmlList = static_cast< QList <QmlRangeEventRelative *> *>(profilerDataModel);
+ QList <QV8EventSub*> *v8List = static_cast< QList <QV8EventSub *> *>(profilerDataModel);
int listLength;
if (!isV8)
@@ -901,9 +1009,9 @@ void QmlProfilerEventsParentsAndChildrenView::rebuildTree(void *eventList)
for (int index=0; index < listLength; index++) {
QList<QStandardItem *> newRow;
if (!isV8) {
- QmlEventSub *event = qmlList->at(index);
+ QmlRangeEventRelative *event = qmlList->at(index);
- newRow << new EventsViewItem(event->reference->displayname);
+ newRow << new EventsViewItem(event->reference->displayName);
newRow << new EventsViewItem(QmlProfilerEventsMainView::nameForType(event->reference->eventType));
newRow << new EventsViewItem(QmlProfilerEventsMainView::displayTime(event->duration));
newRow << new EventsViewItem(QString::number(event->calls));
diff --git a/src/plugins/qmlprofiler/qmlprofilereventview.h b/src/plugins/qmlprofiler/qmlprofilereventview.h
index 1c7c87d7f2..409ea4dca0 100644
--- a/src/plugins/qmlprofiler/qmlprofilereventview.h
+++ b/src/plugins/qmlprofiler/qmlprofilereventview.h
@@ -34,9 +34,13 @@
#define QMLPROFILEREVENTVIEW_H
#include <QTreeView>
-#include <qmljsdebugclient/qmlprofilereventtypes.h>
-#include <qmljsdebugclient/qmlprofilereventlist.h>
#include <QStandardItemModel>
+#include <qmljsdebugclient/qmlprofilereventtypes.h>
+#include "qmlprofilerdatamodel.h"
+
+#include <analyzerbase/ianalyzertool.h>
+
+#include "qmlprofilerviewmanager.h"
namespace QmlProfiler {
namespace Internal {
@@ -44,9 +48,6 @@ namespace Internal {
class QmlProfilerEventsMainView;
class QmlProfilerEventsParentsAndChildrenView;
-typedef QHash<QString, QmlJsDebugClient::QmlEventData *> QmlEventHash;
-typedef QList<QmlJsDebugClient::QmlEventData *> QmlEventList;
-
enum ItemRole {
EventHashStrRole = Qt::UserRole+1,
FilenameRole = Qt::UserRole+2,
@@ -59,7 +60,10 @@ class QmlProfilerEventsWidget : public QWidget
{
Q_OBJECT
public:
- explicit QmlProfilerEventsWidget(QmlJsDebugClient::QmlProfilerEventList *model, QWidget *parent);
+ explicit QmlProfilerEventsWidget(QWidget *parent,
+ Analyzer::IAnalyzerTool *profilerTool,
+ QmlProfilerViewManager *container,
+ QmlProfilerDataModel *profilerDataModel );
~QmlProfilerEventsWidget();
void switchToV8View();
@@ -75,9 +79,11 @@ public:
void setShowExtendedStatistics(bool show);
bool showExtendedStatistics() const;
+ bool isQml() const;
+ bool isV8() const;
+
signals:
void gotoSourceLocation(const QString &fileName, int lineNumber, int columnNumber);
- void contextMenuRequested(const QPoint &position);
void showEventInTimeline(int eventId);
public slots:
@@ -85,18 +91,14 @@ public slots:
void selectBySourceLocation(const QString &filename, int line, int column);
private slots:
- void eventListStateChanged();
+ void profilerDataModelStateChanged();
protected:
void contextMenuEvent(QContextMenuEvent *ev);
private:
- QmlProfilerEventsMainView *m_eventTree;
- QmlProfilerEventsParentsAndChildrenView *m_eventChildren;
- QmlProfilerEventsParentsAndChildrenView *m_eventParents;
- QmlJsDebugClient::QmlProfilerEventList *m_eventStatistics;
-
- bool m_globalStatsEnabled;
+ class QmlProfilerEventsWidgetPrivate;
+ QmlProfilerEventsWidgetPrivate *d;
};
class QmlProfilerEventsMainView : public QTreeView
@@ -129,12 +131,14 @@ public:
MaxViewTypes
};
- explicit QmlProfilerEventsMainView(QmlJsDebugClient::QmlProfilerEventList *model, QWidget *parent);
+ explicit QmlProfilerEventsMainView(ViewTypes viewType,
+ QWidget *parent,
+ QmlProfilerDataModel *dataModel);
~QmlProfilerEventsMainView();
- void setEventStatisticsModel(QmlJsDebugClient::QmlProfilerEventList *model);
void setFieldViewable(Fields field, bool show);
void setViewType(ViewTypes type);
+ ViewTypes viewType() const;
void setShowAnonymousEvents( bool showThem );
QModelIndex selectedItem() const;
@@ -157,7 +161,6 @@ signals:
void showEventInTimeline(int eventId);
public slots:
- void eventListStateChanged();
void clear();
void jumpToItem(const QModelIndex &index);
void selectEvent(int eventId);
@@ -165,6 +168,9 @@ public slots:
void buildModel();
void changeDetailsForEvent(int eventId, const QString &newString);
+private slots:
+ void profilerDataModelStateChanged();
+
private:
void setHeaderLabels();
@@ -186,7 +192,9 @@ public:
MaxSubtableTypes
};
- explicit QmlProfilerEventsParentsAndChildrenView(QmlJsDebugClient::QmlProfilerEventList *model, SubViewType subtableType, QWidget *parent);
+ explicit QmlProfilerEventsParentsAndChildrenView(SubViewType subtableType,
+ QWidget *parent,
+ QmlProfilerDataModel *model);
~QmlProfilerEventsParentsAndChildrenView();
void setViewType(SubViewType type);
@@ -200,10 +208,10 @@ public slots:
void clear();
private:
- void rebuildTree(void *eventList);
+ void rebuildTree(void *profilerDataModel);
void updateHeader();
QStandardItemModel *treeModel();
- QmlJsDebugClient::QmlProfilerEventList *m_eventList;
+ QmlProfilerDataModel *m_profilerDataModel;
SubViewType m_subtableType;
};
diff --git a/src/plugins/qmlprofiler/qmlprofilerstatemanager.cpp b/src/plugins/qmlprofiler/qmlprofilerstatemanager.cpp
new file mode 100644
index 0000000000..194e2cdf1c
--- /dev/null
+++ b/src/plugins/qmlprofiler/qmlprofilerstatemanager.cpp
@@ -0,0 +1,163 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** 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 qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "qmlprofilerstatemanager.h"
+
+#include <QDebug>
+#include <utils/qtcassert.h>
+
+// uncomment for printing the state changes to debug output
+//#define _DEBUG_PROFILERSTATE_
+
+namespace QmlProfiler {
+namespace Internal {
+
+inline QString stringForState(int state) {
+ switch (state) {
+ case QmlProfilerStateManager::Idle: return QString("Idle");
+ case QmlProfilerStateManager::AppStarting: return QString("AppStarting");
+ case QmlProfilerStateManager::AppRunning: return QString("AppRunning");
+ case QmlProfilerStateManager::AppStopRequested: return QString("AppStopRequested");
+ case QmlProfilerStateManager::AppReadyToStop: return QString("AppReadyToStop");
+ case QmlProfilerStateManager::AppStopped: return QString("AppStopped");
+ case QmlProfilerStateManager::AppKilled: return QString("AppKilled");
+ default: break;
+ }
+ return QString();
+}
+
+class QmlProfilerStateManager::QmlProfilerStateManagerPrivate
+{
+public:
+ QmlProfilerStateManagerPrivate(QmlProfilerStateManager *qq) : q(qq) {}
+ ~QmlProfilerStateManagerPrivate() {}
+
+ QmlProfilerStateManager *q;
+
+ QmlProfilerStateManager::QmlProfilerState m_currentState;
+ bool m_clientRecording;
+ bool m_serverRecording;
+};
+QmlProfilerStateManager::QmlProfilerStateManager(QObject *parent) :
+ QObject(parent),d(new QmlProfilerStateManagerPrivate(this))
+{
+ d->m_currentState = Idle;
+ d->m_clientRecording = true;
+ d->m_serverRecording = false;
+}
+
+QmlProfilerStateManager::~QmlProfilerStateManager()
+{
+ delete d;
+}
+
+QmlProfilerStateManager::QmlProfilerState QmlProfilerStateManager::currentState()
+{
+ return d->m_currentState;
+}
+
+bool QmlProfilerStateManager::clientRecording()
+{
+ return d->m_clientRecording;
+}
+
+bool QmlProfilerStateManager::serverRecording()
+{
+ return d->m_serverRecording;
+}
+
+QString QmlProfilerStateManager::currentStateAsString()
+{
+ return stringForState(d->m_currentState);
+}
+
+void QmlProfilerStateManager::setCurrentState(QmlProfilerState newState)
+{
+#ifdef _DEBUG_PROFILERSTATE_
+ qDebug() << "Profiler state change request from" << stringForState(d->m_currentState) << "to" << stringForState(newState);
+#endif
+ QTC_ASSERT(d->m_currentState != newState, /**/);
+ switch (newState) {
+ case Idle:
+ QTC_ASSERT(d->m_currentState == AppStarting || d->m_currentState == AppStopped || d->m_currentState == AppKilled, /**/);
+ break;
+ case AppStarting:
+ QTC_ASSERT(d->m_currentState == Idle, /**/);
+ break;
+ case AppRunning:
+ QTC_ASSERT(d->m_currentState == AppStarting, /**/);
+ break;
+ case AppStopRequested:
+ QTC_ASSERT(d->m_currentState == AppRunning, /**/);
+ break;
+ case AppReadyToStop:
+ QTC_ASSERT(d->m_currentState == AppStopRequested, /**/);
+ break;
+ case AppStopped:
+ QTC_ASSERT(d->m_currentState == AppReadyToStop, /**/);
+ break;
+ case AppKilled:
+ QTC_ASSERT(d->m_currentState == AppRunning, /**/);
+ break;
+ default:
+ qDebug() << tr("Switching to unknown state in %1:%2").arg(QString(__FILE__), QString::number(__LINE__));
+ break;
+ }
+
+ d->m_currentState = newState;
+ emit stateChanged();
+}
+
+void QmlProfilerStateManager::setClientRecording(bool recording)
+{
+#ifdef _DEBUG_PROFILERSTATE_
+ qDebug() << "Setting client recording flag from" << d->m_serverRecording << "to" << recording;
+#endif
+ if (d->m_clientRecording != recording) {
+ d->m_clientRecording = recording;
+ emit clientRecordingChanged();
+ }
+}
+
+void QmlProfilerStateManager::setServerRecording(bool recording)
+{
+#ifdef _DEBUG_PROFILERSTATE_
+ qDebug() << "Setting server recording flag from" << d->m_serverRecording << "to" << recording;
+#endif
+ if (d->m_serverRecording != recording) {
+ d->m_serverRecording = recording;
+ emit serverRecordingChanged();
+ }
+}
+
+}
+}
diff --git a/src/tools/qmlprofilertool/commandlistener.h b/src/plugins/qmlprofiler/qmlprofilerstatemanager.h
index 4b8df747ff..188b0b5be3 100644
--- a/src/tools/qmlprofilertool/commandlistener.h
+++ b/src/plugins/qmlprofiler/qmlprofilerstatemanager.h
@@ -30,25 +30,53 @@
**
**************************************************************************/
-#ifndef COMMANDLISTENER_H
-#define COMMANDLISTENER_H
+#ifndef QMLPROFILERSTATEMANAGER_H
+#define QMLPROFILERSTATEMANAGER_H
-#include <QThread>
+#include <QObject>
-class CommandListener : public QThread
+namespace QmlProfiler {
+namespace Internal {
+
+class QmlProfilerStateManager : public QObject
{
Q_OBJECT
public:
- CommandListener(QObject *parent = 0);
+ enum QmlProfilerState {
+ Idle,
+ AppStarting,
+ AppRunning,
+ AppStopRequested,
+ AppReadyToStop,
+ AppStopped,
+ AppKilled
+ };
+
+ explicit QmlProfilerStateManager(QObject *parent = 0);
+ ~QmlProfilerStateManager();
+
+ QmlProfilerState currentState();
+ bool clientRecording();
+ bool serverRecording();
- void run();
+ QString currentStateAsString();
- void requestStop() { m_stopRequested = true; }
signals:
- void command(const QString &command);
+ void stateChanged();
+ void clientRecordingChanged();
+ void serverRecordingChanged();
+
+public slots:
+ void setCurrentState(QmlProfilerState newState);
+ void setClientRecording(bool recording);
+ void setServerRecording(bool recording);
private:
- bool m_stopRequested;
+ class QmlProfilerStateManagerPrivate;
+ QmlProfilerStateManagerPrivate *d;
};
-#endif // COMMANDLISTENER_H
+}
+}
+
+#endif // QMLPROFILERSTATEMANAGER_H
diff --git a/src/plugins/qmlprofiler/qmlprofilertool.cpp b/src/plugins/qmlprofiler/qmlprofilertool.cpp
index 76359684c9..46bf8b6085 100644
--- a/src/plugins/qmlprofiler/qmlprofilertool.cpp
+++ b/src/plugins/qmlprofiler/qmlprofilertool.cpp
@@ -31,24 +31,20 @@
**************************************************************************/
#include "qmlprofilertool.h"
+#include "qmlprofilerstatemanager.h"
#include "qmlprofilerengine.h"
-#include "qmlprofilerplugin.h"
#include "qmlprofilerconstants.h"
#include "qmlprofilerattachdialog.h"
-#include "qmlprofilereventview.h"
-
-#include "tracewindow.h"
-#include "timelineview.h"
-
-#include <qmljsdebugclient/qmlprofilereventlist.h>
-#include <qmljsdebugclient/qdeclarativedebugclient.h>
+#include "qmlprofilerviewmanager.h"
+#include "qmlprofilerclientmanager.h"
+#include "qmlprofilerdatamodel.h"
+#include "qmlprofilerdetailsrewriter.h"
+#include "timelinerenderer.h"
#include <analyzerbase/analyzermanager.h>
-#include <analyzerbase/analyzerconstants.h>
#include <analyzerbase/analyzerruncontrol.h>
-#include "canvas/qdeclarativecanvas_p.h"
-#include "canvas/qdeclarativecanvastimer_p.h"
+#include "canvas/qdeclarativecontext2d_p.h"
#include "canvas/qmlprofilercanvas.h"
#include <qmlprojectmanager/qmlprojectrunconfiguration.h>
@@ -56,7 +52,6 @@
#include <utils/fileinprojectfinder.h>
#include <utils/qtcassert.h>
#include <projectexplorer/projectexplorer.h>
-#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/project.h>
#include <projectexplorer/target.h>
#include <projectexplorer/session.h>
@@ -65,7 +60,6 @@
#include <remotelinux/remotelinuxrunconfiguration.h>
#include <remotelinux/linuxdeviceconfiguration.h>
-#include <texteditor/itexteditor.h>
#include <coreplugin/coreconstants.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/icore.h>
@@ -82,23 +76,22 @@
#include <qt4projectmanager/qt-s60/s60devicerunconfiguration.h>
#include <qt4projectmanager/qt-s60/s60deployconfiguration.h>
-#include <QFile>
-
#include <QApplication>
#include <QHBoxLayout>
#include <QLabel>
-#include <QTabWidget>
#include <QToolButton>
#include <QMessageBox>
-#include <QDockWidget>
#include <QFileDialog>
#include <QMenu>
+#include <QTimer>
+#include <QTime>
using namespace Core;
using namespace Core::Constants;
using namespace Analyzer;
using namespace Analyzer::Constants;
using namespace QmlProfiler::Internal;
+using namespace QmlProfiler::Constants;
using namespace QmlJsDebugClient;
using namespace ProjectExplorer;
using namespace QmlProjectManager;
@@ -112,59 +105,67 @@ public:
QmlProfilerTool *q;
- QDeclarativeDebugConnection *m_client;
- QTimer m_connectionTimer;
- int m_connectionAttempts;
- TraceWindow *m_traceWindow;
- QmlProfilerEventsWidget *m_eventsView;
- QmlProfilerEventsWidget *m_v8profilerView;
+ QmlProfilerStateManager *m_profilerState;
+ QmlProfilerClientManager *m_profilerConnections;
+ QmlProfilerDataModel *m_profilerDataModel;
+ QmlProfilerDetailsRewriter *m_detailsRewriter;
+
+ QmlProfilerViewManager *m_viewContainer;
Utils::FileInProjectFinder m_projectFinder;
RunConfiguration *m_runConfiguration;
- bool m_isAttached;
QToolButton *m_recordButton;
QToolButton *m_clearButton;
- bool m_recordingEnabled;
- bool m_appIsRunning;
- bool m_qmlActive;
- bool m_v8Active;
- QTime m_appTimer;
- qint64 m_appRunningTime;
-
- enum ConnectMode {
- TcpConnection, OstConnection
- };
-
- ConnectMode m_connectMode;
- QString m_tcpHost;
- quint16 m_tcpPort;
- QString m_ostDevice;
- QString m_sysroot;
+
+ // elapsed time display
+ QTimer m_recordingTimer;
+ QTime m_recordingElapsedTime;
+ QLabel *m_timeLabel;
+
+ // save and load actions
QAction *m_saveQmlTrace;
+ QAction *m_loadQmlTrace;
};
QmlProfilerTool::QmlProfilerTool(QObject *parent)
: IAnalyzerTool(parent), d(new QmlProfilerToolPrivate(this))
{
setObjectName("QmlProfilerTool");
- d->m_client = 0;
- d->m_connectionAttempts = 0;
- d->m_traceWindow = 0;
- d->m_runConfiguration = 0;
- d->m_isAttached = false;
- d->m_recordingEnabled = true;
- d->m_appIsRunning = false;
- d->m_appTimer.start();
- d->m_appRunningTime = 0;
- d->m_connectionTimer.setInterval(200);
- connect(&d->m_connectionTimer, SIGNAL(timeout()), SLOT(tryToConnect()));
+ d->m_profilerState = 0;
+ d->m_viewContainer = 0;
+ d->m_runConfiguration = 0;
- qmlRegisterType<Canvas>("Monitor", 1, 0, "Canvas");
qmlRegisterType<QmlProfilerCanvas>("Monitor", 1, 0, "Canvas2D");
qmlRegisterType<Context2D>();
- qmlRegisterType<CanvasImage>();
qmlRegisterType<CanvasGradient>();
- qmlRegisterType<TimelineView>("Monitor", 1, 0,"TimelineView");
+ qmlRegisterType<TimelineRenderer>("Monitor", 1, 0,"TimelineRenderer");
+
+ d->m_profilerState = new QmlProfilerStateManager(this);
+ connect(d->m_profilerState, SIGNAL(stateChanged()), this, SLOT(profilerStateChanged()));
+ connect(d->m_profilerState, SIGNAL(clientRecordingChanged()), this, SLOT(clientRecordingChanged()));
+ connect(d->m_profilerState, SIGNAL(serverRecordingChanged()), this, SLOT(serverRecordingChanged()));
+
+ d->m_profilerConnections = new QmlProfilerClientManager(this);
+ d->m_profilerConnections->registerProfilerStateManager(d->m_profilerState);
+
+ d->m_profilerDataModel = new QmlProfilerDataModel(this);
+ connect(d->m_profilerDataModel, SIGNAL(stateChanged()), this, SLOT(profilerDataModelStateChanged()));
+ connect(d->m_profilerDataModel, SIGNAL(error(QString)), this, SLOT(showErrorDialog(QString)));
+ connect(d->m_profilerConnections, SIGNAL(addRangedEvent(int,qint64,qint64,QStringList,QmlJsDebugClient::QmlEventLocation)), d->m_profilerDataModel, SLOT(addRangedEvent(int,qint64,qint64,QStringList,QmlJsDebugClient::QmlEventLocation)));
+ connect(d->m_profilerConnections, SIGNAL(addV8Event(int,QString,QString,int,double,double)), d->m_profilerDataModel, SLOT(addV8Event(int,QString,QString,int,double,double)));
+ connect(d->m_profilerConnections, SIGNAL(addFrameEvent(qint64,int,int)), d->m_profilerDataModel, SLOT(addFrameEvent(qint64,int,int)));
+ connect(d->m_profilerConnections, SIGNAL(traceStarted(qint64)), d->m_profilerDataModel, SLOT(setTraceStartTime(qint64)));
+ connect(d->m_profilerConnections, SIGNAL(traceFinished(qint64)), d->m_profilerDataModel, SLOT(setTraceEndTime(qint64)));
+ connect(d->m_profilerConnections, SIGNAL(dataReadyForProcessing()), d->m_profilerDataModel, SLOT(complete()));
+
+
+ d->m_detailsRewriter = new QmlProfilerDetailsRewriter(this);
+ connect(d->m_profilerDataModel, SIGNAL(requestDetailsForLocation(int,QmlJsDebugClient::QmlEventLocation)),
+ d->m_detailsRewriter, SLOT(requestDetailsForLocation(int,QmlJsDebugClient::QmlEventLocation)));
+ connect(d->m_detailsRewriter, SIGNAL(rewriteDetailsString(int,QmlJsDebugClient::QmlEventLocation,QString)),
+ d->m_profilerDataModel, SLOT(rewriteDetailsString(int,QmlJsDebugClient::QmlEventLocation,QString)));
+ connect(d->m_detailsRewriter, SIGNAL(eventDetailsChanged()), d->m_profilerDataModel, SLOT(finishedRewritingDetails()));
+ connect(d->m_profilerDataModel, SIGNAL(reloadDocumentsForDetails()), d->m_detailsRewriter, SLOT(reloadDocuments()));
Command *command = 0;
const Context globalContext(C_GLOBAL);
@@ -176,7 +177,7 @@ QmlProfilerTool::QmlProfilerTool(QObject *parent)
menu->addMenu(options, G_ANALYZER_OPTIONS);
options->menu()->setEnabled(true);
- QAction *act = new QAction(tr("Load QML Trace"), options);
+ QAction *act = d->m_loadQmlTrace = new QAction(tr("Load QML Trace"), options);
command = am->registerAction(act, "Analyzer.Menu.StartAnalyzer.QMLProfilerOptions.LoadQMLTrace", globalContext);
connect(act, SIGNAL(triggered()), this, SLOT(showLoadDialog()));
options->addAction(command);
@@ -186,11 +187,13 @@ QmlProfilerTool::QmlProfilerTool(QObject *parent)
command = am->registerAction(act, "Analyzer.Menu.StartAnalyzer.QMLProfilerOptions.SaveQMLTrace", globalContext);
connect(act, SIGNAL(triggered()), this, SLOT(showSaveDialog()));
options->addAction(command);
+
+ d->m_recordingTimer.setInterval(100);
+ connect(&d->m_recordingTimer, SIGNAL(timeout()), this, SLOT(updateTimeDisplay()));
}
QmlProfilerTool::~QmlProfilerTool()
{
- delete d->m_client;
delete d;
}
@@ -220,86 +223,14 @@ IAnalyzerTool::ToolMode QmlProfilerTool::toolMode() const
return AnyMode;
}
-void QmlProfilerTool::showContextMenu(const QPoint &position)
-{
- QmlProfilerEventsWidget *eventView = qobject_cast<QmlProfilerEventsWidget *>(sender());
- TraceWindow *traceView = qobject_cast<TraceWindow *>(sender());
-
- QMenu menu;
- QAction *loadAction = menu.addAction(tr("Load QML Trace"));
- QAction *saveAction = menu.addAction(tr("Save QML Trace"));
- QAction *copyRowAction = 0;
- QAction *copyTableAction = 0;
- QAction *showExtendedStatsAction = 0;
- QAction *viewAllAction = 0;
- QAction *getLocalStatsAction = 0;
- QAction *getGlobalStatsAction = 0;
-
- if (eventView && eventView->mouseOnTable(position)) {
- menu.addSeparator();
- if (eventView->selectedItem().isValid())
- copyRowAction = menu.addAction(tr("Copy Row"));
- copyTableAction = menu.addAction(tr("Copy Table"));
-
- if (eventView == d->m_eventsView) {
- // only for qml events view, not for v8
- showExtendedStatsAction = menu.addAction(tr("Extended Event Statistics"));
- showExtendedStatsAction->setCheckable(true);
- showExtendedStatsAction->setChecked(eventView->showExtendedStatistics());
- }
- }
-
- if (sender() == d->m_traceWindow || sender() == d->m_eventsView) {
- menu.addSeparator();
- getLocalStatsAction = menu.addAction(tr("Limit Events Pane to Current Range"));
- if (!d->m_traceWindow->hasValidSelection())
- getLocalStatsAction->setEnabled(false);
- getGlobalStatsAction = menu.addAction(tr("Reset Events Pane"));
- if (d->m_eventsView->hasGlobalStats())
- getGlobalStatsAction->setEnabled(false);
- }
-
- if (traceView) {
- if (traceView->getEventList()->count() > 0) {
- menu.addSeparator();
- viewAllAction = menu.addAction(tr("Reset Zoom"));
- }
- }
-
- QAction *selectedAction = menu.exec(position);
-
- if (selectedAction) {
- if (selectedAction == loadAction)
- showLoadDialog();
- if (selectedAction == saveAction)
- showSaveDialog();
- if (selectedAction == copyRowAction)
- eventView->copyRowToClipboard();
- if (selectedAction == copyTableAction)
- eventView->copyTableToClipboard();
- if (selectedAction == viewAllAction)
- traceView->viewAll();
- if (selectedAction == getLocalStatsAction) {
- d->m_eventsView->getStatisticsInRange(
- d->m_traceWindow->selectionStart(),
- d->m_traceWindow->selectionEnd());
- }
- if (selectedAction == getGlobalStatsAction) {
- d->m_eventsView->getStatisticsInRange(
- d->m_traceWindow->getEventList()->traceStartTime(),
- d->m_traceWindow->getEventList()->traceEndTime());
- }
- if (selectedAction == showExtendedStatsAction)
- eventView->setShowExtendedStatistics(!eventView->showExtendedStatistics());
- }
-}
-
IAnalyzerEngine *QmlProfilerTool::createEngine(const AnalyzerStartParameters &sp,
RunConfiguration *runConfiguration)
{
QmlProfilerEngine *engine = new QmlProfilerEngine(this, sp, runConfiguration);
- d->m_connectMode = QmlProfilerToolPrivate::TcpConnection;
+ engine->registerProfilerStateManager(d->m_profilerState);
+
+ bool isTcpConnection = true;
if (runConfiguration) {
// Check minimum Qt Version. We cannot really be sure what the Qt version
@@ -324,16 +255,15 @@ IAnalyzerEngine *QmlProfilerTool::createEngine(const AnalyzerStartParameters &sp
runConfiguration->target()->activeDeployConfiguration())) {
if (deployConfig->communicationChannel()
== Qt4ProjectManager::S60DeployConfiguration::CommunicationCodaSerialConnection) {
- d->m_connectMode = QmlProfilerToolPrivate::OstConnection;
- d->m_ostDevice = deployConfig->serialPortName();
+ d->m_profilerConnections->setOstConnection(deployConfig->serialPortName());
+ isTcpConnection = false;
}
}
}
// FIXME: Check that there's something sensible in sp.connParams
- if (d->m_connectMode == QmlProfilerToolPrivate::TcpConnection) {
- d->m_tcpHost = sp.connParams.host;
- d->m_tcpPort = sp.connParams.port;
+ if (isTcpConnection) {
+ d->m_profilerConnections->setTcpConnection(sp.connParams.host, sp.connParams.port);
}
d->m_runConfiguration = runConfiguration;
@@ -364,20 +294,9 @@ IAnalyzerEngine *QmlProfilerTool::createEngine(const AnalyzerStartParameters &sp
d->m_projectFinder.setProjectFiles(sourceFiles);
d->m_projectFinder.setSysroot(sp.sysroot);
- connect(engine, SIGNAL(processRunning(quint16)), this, SLOT(connectClient(quint16)));
- connect(engine, SIGNAL(finished()), this, SLOT(disconnectClient()));
- connect(engine, SIGNAL(finished()), this, SLOT(updateTimers()));
- connect(engine, SIGNAL(stopRecording()), this, SLOT(stopRecording()));
- connect(engine, SIGNAL(recordingChanged(bool)), this, SLOT(setRecording(bool)));
- connect(engine, SIGNAL(timeUpdate()), this, SLOT(updateTimers()));
- connect(d->m_traceWindow, SIGNAL(viewUpdated()), engine, SLOT(dataReceived()));
- connect(this, SIGNAL(connectionFailed()), engine, SLOT(finishProcess()));
- connect(this, SIGNAL(fetchingData(bool)), engine, SLOT(setFetchingData(bool)));
- connect(engine, SIGNAL(starting(const Analyzer::IAnalyzerEngine*)), this, SLOT(setAppIsRunning()));
- connect(engine, SIGNAL(finished()), this, SLOT(setAppIsStopped()));
- connect(this, SIGNAL(cancelRun()), engine, SLOT(finishProcess()));
- connect(engine, SIGNAL(applicationDied()), d->m_traceWindow, SLOT(applicationDied()));
- emit fetchingData(d->m_recordButton->isChecked());
+ connect(engine, SIGNAL(processRunning(quint16)), d->m_profilerConnections, SLOT(connectClient(quint16)));
+ connect(engine, SIGNAL(finished()), d->m_profilerConnections, SLOT(disconnectClient()));
+ connect(d->m_profilerConnections, SIGNAL(connectionFailed()), engine, SLOT(cancelProcess()));
return engine;
}
@@ -444,53 +363,15 @@ AnalyzerStartParameters QmlProfilerTool::createStartParameters(RunConfiguration
QWidget *QmlProfilerTool::createWidgets()
{
- QTC_ASSERT(!d->m_traceWindow, return 0);
-
- //
- // DockWidgets
- //
-
- Utils::FancyMainWindow *mw = AnalyzerManager::mainWindow();
-
- d->m_traceWindow = new TraceWindow(mw);
- d->m_traceWindow->reset(d->m_client);
-
- connect(d->m_traceWindow, SIGNAL(clearViewsFromTool()), this, SLOT(clearDisplay()));
- connect(d->m_traceWindow, SIGNAL(gotoSourceLocation(QString,int,int)),this, SLOT(gotoSourceLocation(QString,int,int)));
- connect(d->m_traceWindow, SIGNAL(contextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint)));
- connect(d->m_traceWindow->getEventList(), SIGNAL(error(QString)), this, SLOT(showErrorDialog(QString)));
- connect(d->m_traceWindow->getEventList(), SIGNAL(stateChanged()), this, SLOT(eventListStateChanged()));
- connect(d->m_traceWindow, SIGNAL(profilerStateChanged(bool,bool)), this, SLOT(profilerStateChanged(bool,bool)));
- connect(d->m_traceWindow, SIGNAL(recordingChanged(bool)), this, SLOT(setRecording(bool)));
-
- d->m_eventsView = new QmlProfilerEventsWidget(d->m_traceWindow->getEventList(), mw);
- connect(d->m_eventsView, SIGNAL(gotoSourceLocation(QString,int,int)), this, SLOT(gotoSourceLocation(QString,int,int)));
- connect(d->m_eventsView, SIGNAL(contextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint)));
- connect(d->m_eventsView, SIGNAL(showEventInTimeline(int)), d->m_traceWindow, SLOT(selectNextEvent(int)));
- connect(d->m_traceWindow, SIGNAL(selectedEventIdChanged(int)), d->m_eventsView, SLOT(updateSelectedEvent(int)));
+ QTC_ASSERT(!d->m_viewContainer, return 0);
- d->m_v8profilerView = new QmlProfilerEventsWidget(d->m_traceWindow->getEventList(), mw);
- d->m_v8profilerView->switchToV8View();
- connect(d->m_v8profilerView, SIGNAL(gotoSourceLocation(QString,int,int)), this, SLOT(gotoSourceLocation(QString,int,int)));
- connect(d->m_v8profilerView, SIGNAL(contextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint)));
- connect(d->m_v8profilerView, SIGNAL(gotoSourceLocation(QString,int,int)), d->m_eventsView, SLOT(selectBySourceLocation(QString,int,int)));
- connect(d->m_eventsView, SIGNAL(gotoSourceLocation(QString,int,int)), d->m_v8profilerView, SLOT(selectBySourceLocation(QString,int,int)));
-
- QDockWidget *eventsDock = AnalyzerManager::createDockWidget
- (this, tr("Events"), d->m_eventsView, Qt::BottomDockWidgetArea);
- QDockWidget *timelineDock = AnalyzerManager::createDockWidget
- (this, tr("Timeline"), d->m_traceWindow, Qt::BottomDockWidgetArea);
- QDockWidget *v8profilerDock = AnalyzerManager::createDockWidget
- (this, tr("JavaScript"), d->m_v8profilerView, Qt::BottomDockWidgetArea);
-
- eventsDock->show();
- timelineDock->show();
- v8profilerDock->show();
-
- mw->splitDockWidget(mw->toolBarDockWidget(), eventsDock, Qt::Vertical);
- mw->tabifyDockWidget(eventsDock, timelineDock);
- mw->tabifyDockWidget(timelineDock, v8profilerDock);
+ d->m_viewContainer = new QmlProfilerViewManager(this,
+ this,
+ d->m_profilerDataModel,
+ d->m_profilerState);
+ connect(d->m_viewContainer, SIGNAL(gotoSourceLocation(QString,int,int)),
+ this, SLOT(gotoSourceLocation(QString,int,int)));
//
// Toolbar
@@ -507,118 +388,53 @@ QWidget *QmlProfilerTool::createWidgets()
connect(d->m_recordButton,SIGNAL(clicked(bool)), this, SLOT(recordingButtonChanged(bool)));
d->m_recordButton->setChecked(true);
- setRecording(d->m_recordingEnabled);
+ setRecording(d->m_profilerState->clientRecording());
layout->addWidget(d->m_recordButton);
d->m_clearButton = new QToolButton(toolbarWidget);
d->m_clearButton->setIcon(QIcon(QLatin1String(":/qmlprofiler/clean_pane_small.png")));
d->m_clearButton->setToolTip(tr("Discard data"));
- connect(d->m_clearButton,SIGNAL(clicked()), this, SLOT(clearDisplay()));
+
+ connect(d->m_clearButton,SIGNAL(clicked()), this, SLOT(clearData()));
+
layout->addWidget(d->m_clearButton);
- QLabel *timeLabel = new QLabel();
- QPalette palette = timeLabel->palette();
+ d->m_timeLabel = new QLabel();
+ QPalette palette = d->m_timeLabel->palette();
palette.setColor(QPalette::WindowText, Qt::white);
- timeLabel->setPalette(palette);
- timeLabel->setIndent(10);
- connect(d->m_traceWindow, SIGNAL(viewUpdated()), this, SLOT(updateTimers()));
- connect(this, SIGNAL(setTimeLabel(QString)), timeLabel, SLOT(setText(QString)));
- updateTimers();
- layout->addWidget(timeLabel);
+ d->m_timeLabel->setPalette(palette);
+ d->m_timeLabel->setIndent(10);
+ updateTimeDisplay();
+ layout->addWidget(d->m_timeLabel);
toolbarWidget->setLayout(layout);
return toolbarWidget;
}
-void QmlProfilerTool::connectClient(quint16 port)
-{
- if (d->m_client)
- delete d->m_client;
- d->m_client = new QDeclarativeDebugConnection;
- d->m_traceWindow->reset(d->m_client);
- connect(d->m_client, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
- this, SLOT(connectionStateChanged()));
- d->m_connectionTimer.start();
- d->m_appTimer.start();
- d->m_tcpPort = port;
-}
-
-void QmlProfilerTool::connectToClient()
-{
- if (!d->m_client || d->m_client->state() != QAbstractSocket::UnconnectedState)
- return;
-
- if (d->m_connectMode == QmlProfilerToolPrivate::TcpConnection) {
- logStatus(QString("QML Profiler: Connecting to %1:%2...").arg(d->m_tcpHost, QString::number(d->m_tcpPort)));
- d->m_client->connectToHost(d->m_tcpHost, d->m_tcpPort);
- } else {
- logStatus(QString("QML Profiler: Connecting to %1...").arg(d->m_tcpHost));
- d->m_client->connectToOst(d->m_ostDevice);
- }
-}
-
-void QmlProfilerTool::disconnectClient()
-{
- // this might be actually be called indirectly by QDDConnectionPrivate::readyRead(), therefore allow
- // method to complete before deleting object
- if (d->m_client) {
- d->m_client->deleteLater();
- d->m_client = 0;
- }
-}
-
-void QmlProfilerTool::startRecording()
-{
- if (d->m_client && d->m_client->isConnected()) {
- clearDisplay();
- d->m_traceWindow->setRecording(true);
- }
- emit fetchingData(true);
-}
-
-void QmlProfilerTool::stopRecording()
-{
- d->m_traceWindow->setRecording(false);
- emit fetchingData(false);
-
- // manage early stop
- if (d->m_client && !d->m_client->isConnected() && d->m_appIsRunning)
- emit cancelRun();
-}
-
void QmlProfilerTool::recordingButtonChanged(bool recording)
{
- if (recording)
- startRecording();
- else
- stopRecording();
-
- setRecording(recording);
+ d->m_profilerState->setClientRecording(recording);
}
void QmlProfilerTool::setRecording(bool recording)
{
- // update record button
- d->m_recordingEnabled = recording;
+ // update display
d->m_recordButton->setToolTip( recording ? tr("Disable profiling") : tr("Enable profiling"));
d->m_recordButton->setIcon(QIcon(recording ? QLatin1String(":/qmlprofiler/recordOn.png") :
QLatin1String(":/qmlprofiler/recordOff.png")));
d->m_recordButton->setChecked(recording);
- updateTimers();
-}
-void QmlProfilerTool::setAppIsRunning()
-{
- d->m_appIsRunning = true;
- updateTimers();
-}
-
-void QmlProfilerTool::setAppIsStopped()
-{
- d->m_appIsRunning = false;
- updateTimers();
+ // manage timer
+ if (d->m_profilerState->currentState() == QmlProfilerStateManager::AppRunning) {
+ if (recording) {
+ d->m_recordingTimer.start();
+ d->m_recordingElapsedTime.start();
+ } else {
+ d->m_recordingTimer.stop();
+ }
+ }
}
void QmlProfilerTool::gotoSourceLocation(const QString &fileUrl, int lineNumber, int columnNumber)
@@ -643,33 +459,30 @@ void QmlProfilerTool::gotoSourceLocation(const QString &fileUrl, int lineNumber,
}
}
-inline QString stringifyTime(double seconds)
+void QmlProfilerTool::updateTimeDisplay()
{
+ double seconds = 0;
+ if (d->m_profilerState->serverRecording() &&
+ d->m_profilerState->currentState() == QmlProfilerStateManager::AppRunning) {
+ seconds = d->m_recordingElapsedTime.elapsed() / 1000.0;
+ } else if (d->m_profilerDataModel->currentState() != QmlProfilerDataModel::Empty ) {
+ seconds = (d->m_profilerDataModel->traceEndTime() - d->m_profilerDataModel->traceStartTime()) / 1.0e9;
+ }
QString timeString = QString::number(seconds,'f',1);
- return QmlProfilerTool::tr("%1 s").arg(timeString, 6);
+ QString profilerTimeStr = QmlProfilerTool::tr("%1 s").arg(timeString, 6);
+ d->m_timeLabel->setText(tr("Elapsed: %1").arg(profilerTimeStr));
}
-void QmlProfilerTool::updateTimers()
+void QmlProfilerTool::clearData()
{
- // prof time
- QString profilerTimeStr = stringifyTime(d->m_traceWindow->profiledTime());
- emit setTimeLabel(tr("Elapsed: %1").arg(profilerTimeStr));
-}
-
-void QmlProfilerTool::profilerStateChanged(bool qmlActive, bool v8active)
-{
- d->m_v8Active = v8active;
- d->m_qmlActive = qmlActive;
- updateTimers();
+ d->m_profilerDataModel->clear();
}
void QmlProfilerTool::clearDisplay()
{
- d->m_appRunningTime = 0;
- d->m_traceWindow->clearDisplay();
- d->m_eventsView->clear();
- d->m_v8profilerView->clear();
- updateTimers();
+ d->m_profilerConnections->clearBufferedData();
+ d->m_viewContainer->clear();
+ updateTimeDisplay();
}
static void startRemoteTool(IAnalyzerTool *tool, StartMode mode)
@@ -718,84 +531,6 @@ static void startRemoteTool(IAnalyzerTool *tool, StartMode mode)
ProjectExplorerPlugin::instance()->startRunControl(rc, tool->runMode());
}
-void QmlProfilerTool::tryToConnect()
-{
- ++d->m_connectionAttempts;
-
- if (d->m_client && d->m_client->isConnected()) {
- d->m_connectionTimer.stop();
- d->m_connectionAttempts = 0;
- } else if (d->m_connectionAttempts == 50) {
- d->m_connectionTimer.stop();
- d->m_connectionAttempts = 0;
-
- QMessageBox *infoBox = new QMessageBox(Core::ICore::mainWindow());
- infoBox->setIcon(QMessageBox::Critical);
- infoBox->setWindowTitle(tr("Qt Creator"));
- infoBox->setText(tr("Could not connect to the in-process QML profiler.\n"
- "Do you want to retry?"));
- infoBox->setStandardButtons(QMessageBox::Retry | QMessageBox::Cancel | QMessageBox::Help);
- infoBox->setDefaultButton(QMessageBox::Retry);
- infoBox->setModal(true);
-
- connect(infoBox, SIGNAL(finished(int)),
- this, SLOT(retryMessageBoxFinished(int)));
-
- infoBox->show();
- } else {
- connectToClient();
- }
-}
-
-void QmlProfilerTool::connectionStateChanged()
-{
- if (!d->m_client)
- return;
- switch (d->m_client->state()) {
- case QAbstractSocket::UnconnectedState:
- {
- if (QmlProfilerPlugin::debugOutput)
- qWarning("QML Profiler: disconnected");
- break;
- }
- case QAbstractSocket::HostLookupState:
- break;
- case QAbstractSocket::ConnectingState: {
- if (QmlProfilerPlugin::debugOutput)
- qWarning("QML Profiler: Connecting to debug server ...");
- break;
- }
- case QAbstractSocket::ConnectedState:
- {
- if (QmlProfilerPlugin::debugOutput)
- qWarning("QML Profiler: connected and running");
- updateRecordingState();
- break;
- }
- case QAbstractSocket::ClosingState:
- if (QmlProfilerPlugin::debugOutput)
- qWarning("QML Profiler: closing ...");
- break;
- case QAbstractSocket::BoundState:
- case QAbstractSocket::ListeningState:
- break;
- }
-}
-
-void QmlProfilerTool::updateRecordingState()
-{
- if (d->m_client->isConnected()) {
- d->m_traceWindow->setRecording(d->m_recordingEnabled);
- } else {
- d->m_traceWindow->setRecording(false);
- }
-
- if (d->m_traceWindow->isRecording())
- clearDisplay();
-
- updateTimers();
-}
-
void QmlProfilerTool::startTool(StartMode mode)
{
using namespace ProjectExplorer;
@@ -821,14 +556,25 @@ void QmlProfilerTool::logStatus(const QString &msg)
void QmlProfilerTool::logError(const QString &msg)
{
- // TODO: Rather show errors in the application ouput
MessageManager *messageManager = MessageManager::instance();
messageManager->printToOutputPane(msg, true);
}
+void QmlProfilerTool::showErrorDialog(const QString &error)
+{
+ QMessageBox *errorDialog = new QMessageBox(Core::ICore::mainWindow());
+ errorDialog->setIcon(QMessageBox::Warning);
+ errorDialog->setWindowTitle(tr("QML Profiler"));
+ errorDialog->setText(error);
+ errorDialog->setStandardButtons(QMessageBox::Ok);
+ errorDialog->setDefaultButton(QMessageBox::Ok);
+ errorDialog->setModal(false);
+ errorDialog->show();
+}
+
void QmlProfilerTool::showSaveOption()
{
- d->m_saveQmlTrace->setEnabled(d->m_traceWindow->getEventList()->count());
+ d->m_saveQmlTrace->setEnabled(!d->m_profilerDataModel->isEmpty());
}
void QmlProfilerTool::showSaveDialog()
@@ -837,7 +583,7 @@ void QmlProfilerTool::showSaveDialog()
if (!filename.isEmpty()) {
if (!filename.endsWith(QLatin1String(TraceFileExtension)))
filename += QLatin1String(TraceFileExtension);
- d->m_traceWindow->getEventList()->save(filename);
+ d->m_profilerDataModel->save(filename);
}
}
@@ -853,53 +599,98 @@ void QmlProfilerTool::showLoadDialog()
if (!filename.isEmpty()) {
// delayed load (prevent graphical artifacts due to long load time)
- d->m_traceWindow->getEventList()->setFilename(filename);
- QTimer::singleShot(100, d->m_traceWindow->getEventList(), SLOT(load()));
+ d->m_profilerDataModel->setFilename(filename);
+ QTimer::singleShot(100, d->m_profilerDataModel, SLOT(load()));
}
}
-void QmlProfilerTool::showErrorDialog(const QString &error)
+void QmlProfilerTool::profilerDataModelStateChanged()
{
- QMessageBox *errorDialog = new QMessageBox(Core::ICore::mainWindow());
- errorDialog->setIcon(QMessageBox::Warning);
- errorDialog->setWindowTitle(tr("QML Profiler"));
- errorDialog->setText(error);
- errorDialog->setStandardButtons(QMessageBox::Ok);
- errorDialog->setDefaultButton(QMessageBox::Ok);
- errorDialog->setModal(false);
- errorDialog->show();
+ switch (d->m_profilerDataModel->currentState()) {
+ case QmlProfilerDataModel::Empty :
+ clearDisplay();
+ break;
+ case QmlProfilerDataModel::AcquiringData :
+ case QmlProfilerDataModel::ProcessingData :
+ // nothing to be done for these two
+ break;
+ case QmlProfilerDataModel::Done :
+ if (d->m_profilerState->currentState() == QmlProfilerStateManager::AppStopRequested)
+ d->m_profilerState->setCurrentState(QmlProfilerStateManager::AppReadyToStop);
+ showSaveOption();
+ updateTimeDisplay();
+ break;
+ default:
+ break;
+ }
+}
+
+QList <QAction *> QmlProfilerTool::profilerContextMenuActions() const
+{
+ QList <QAction *> commonActions;
+ commonActions << d->m_loadQmlTrace << d->m_saveQmlTrace;
+ return commonActions;
+}
+
+void QmlProfilerTool::showNonmodalWarning(const QString &warningMsg)
+{
+ QMessageBox *noExecWarning = new QMessageBox(Core::ICore::mainWindow());
+ noExecWarning->setIcon(QMessageBox::Warning);
+ noExecWarning->setWindowTitle(tr("QML Profiler"));
+ noExecWarning->setText(warningMsg);
+ noExecWarning->setStandardButtons(QMessageBox::Ok);
+ noExecWarning->setDefaultButton(QMessageBox::Ok);
+ noExecWarning->setModal(false);
+ noExecWarning->show();
+}
+
+QMessageBox *QmlProfilerTool::requestMessageBox()
+{
+ return new QMessageBox(Core::ICore::mainWindow());
+}
+
+void QmlProfilerTool::handleHelpRequest(const QString &link)
+{
+ HelpManager *helpManager = HelpManager::instance();
+ helpManager->handleHelpRequest(link);
}
-void QmlProfilerTool::retryMessageBoxFinished(int result)
+void QmlProfilerTool::profilerStateChanged()
{
- switch (result) {
- case QMessageBox::Retry: {
- d->m_connectionAttempts = 0;
- d->m_connectionTimer.start();
+ switch (d->m_profilerState->currentState()) {
+ case QmlProfilerStateManager::AppKilled : {
+ if (d->m_profilerDataModel->currentState() == QmlProfilerDataModel::AcquiringData) {
+ showNonmodalWarning(tr("Application finished before loading profiled data.\n Please use the stop button instead."));
+ }
break;
}
- case QMessageBox::Help: {
- HelpManager *helpManager = HelpManager::instance();
- helpManager->handleHelpRequest("qthelp://com.nokia.qtcreator/doc/creator-debugging-qml.html");
- // fall through
- }
- default: {
- if (d->m_client) {
- logStatus("QML Profiler: Failed to connect! " + d->m_client->errorString());
- } else {
- logStatus("QML Profiler: Failed to connect!");
- }
-
- emit connectionFailed();
+ case QmlProfilerStateManager::Idle :
+ // when the app finishes, set recording display to client status
+ setRecording(d->m_profilerState->clientRecording());
+ break;
+ default:
+ // no special action needed for other states
break;
}
+}
+
+void QmlProfilerTool::clientRecordingChanged()
+{
+ // if application is running, display server record changes
+ // if application is stopped, display client record changes
+ if (d->m_profilerState->currentState() != QmlProfilerStateManager::AppRunning) {
+ setRecording(d->m_profilerState->clientRecording());
}
}
-void QmlProfilerTool::eventListStateChanged()
+void QmlProfilerTool::serverRecordingChanged()
{
- if (d->m_traceWindow->getEventList()->currentState() == QmlProfilerEventList::Done) {
- showSaveOption();
- updateTimers();
+ if (d->m_profilerState->currentState() == QmlProfilerStateManager::AppRunning) {
+ setRecording(d->m_profilerState->serverRecording());
+ // clear the old data each time we start a new profiling session
+ if (d->m_profilerState->serverRecording()) {
+ clearData();
+ }
}
}
+
diff --git a/src/plugins/qmlprofiler/qmlprofilertool.h b/src/plugins/qmlprofiler/qmlprofilertool.h
index 32f46105f0..bb3f2b1cec 100644
--- a/src/plugins/qmlprofiler/qmlprofilertool.h
+++ b/src/plugins/qmlprofiler/qmlprofilertool.h
@@ -36,13 +36,11 @@
#include <analyzerbase/ianalyzertool.h>
#include <analyzerbase/ianalyzerengine.h>
-#include <QPoint>
+class QMessageBox;
namespace QmlProfiler {
namespace Internal {
-#define TraceFileExtension ".qtd"
-
class QmlProfilerTool : public Analyzer::IAnalyzerTool
{
Q_OBJECT
@@ -72,49 +70,37 @@ public:
QWidget *createWidgets();
void startTool(Analyzer::StartMode mode);
+ QList <QAction *> profilerContextMenuActions() const;
+
+ // display dialogs / log output
+ static QMessageBox *requestMessageBox();
+ static void handleHelpRequest(const QString &link);
+ static void logStatus(const QString &msg);
+ static void logError(const QString &msg);
+ static void showNonmodalWarning(const QString &warningMsg);
+
public slots:
- void connectClient(quint16 port);
- void disconnectClient();
+ void profilerStateChanged();
+ void clientRecordingChanged();
+ void serverRecordingChanged();
- void startRecording();
- void stopRecording();
void recordingButtonChanged(bool recording);
void setRecording(bool recording);
- void setAppIsRunning();
- void setAppIsStopped();
-
void gotoSourceLocation(const QString &fileUrl, int lineNumber, int columnNumber);
- void updateTimers();
- void profilerStateChanged(bool qmlActive, bool v8active);
-
- void clearDisplay();
-
- void showContextMenu(const QPoint &position);
-
-signals:
- void setTimeLabel(const QString &);
- void setStatusLabel(const QString &);
- void fetchingData(bool);
- void connectionFailed();
- void cancelRun();
private slots:
- void tryToConnect();
- void connectionStateChanged();
+ void clearData();
+ void showErrorDialog(const QString &error);
+ void profilerDataModelStateChanged();
+ void updateTimeDisplay();
+
void showSaveOption();
void showSaveDialog();
void showLoadDialog();
- void showErrorDialog(const QString &error);
- void retryMessageBoxFinished(int result);
- void eventListStateChanged();
private:
- void connectToClient();
- void updateRecordingState();
- void ensureWidgets();
- void logStatus(const QString &msg);
- void logError(const QString &msg);
+ void clearDisplay();
class QmlProfilerToolPrivate;
QmlProfilerToolPrivate *d;
diff --git a/src/plugins/qmlprofiler/qmlprofilertraceview.cpp b/src/plugins/qmlprofiler/qmlprofilertraceview.cpp
new file mode 100644
index 0000000000..ab02f45b08
--- /dev/null
+++ b/src/plugins/qmlprofiler/qmlprofilertraceview.cpp
@@ -0,0 +1,601 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** 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 qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "qmlprofilertraceview.h"
+#include "qmlprofilertool.h"
+#include "qmlprofilerstatemanager.h"
+#include "qmlprofilerdatamodel.h"
+
+// Needed for the load&save actions in the context menu
+#include <analyzerbase/ianalyzertool.h>
+
+// Comunication with the other views (limit events to range)
+#include "qmlprofilerviewmanager.h"
+
+#include <utils/styledbar.h>
+
+#include <QDeclarativeContext>
+#include <QToolButton>
+#include <QEvent>
+#include <QVBoxLayout>
+#include <QGraphicsObject>
+#include <QScrollBar>
+#include <QSlider>
+#include <QMenu>
+
+#include <math.h>
+
+using namespace QmlJsDebugClient;
+
+namespace QmlProfiler {
+namespace Internal {
+
+const int sliderTicks = 10000;
+const qreal sliderExp = 3;
+
+
+/////////////////////////////////////////////////////////
+bool MouseWheelResizer::eventFilter(QObject *obj, QEvent *event)
+{
+ if (event->type() == QEvent::Wheel) {
+ QWheelEvent *ev = static_cast<QWheelEvent *>(event);
+ if (ev->modifiers() & Qt::ControlModifier) {
+ emit mouseWheelMoved(ev->pos().x(), ev->pos().y(), ev->delta());
+ return true;
+ }
+ }
+ return QObject::eventFilter(obj, event);
+}
+
+/////////////////////////////////////////////////////////
+void ZoomControl::setRange(qint64 startTime, qint64 endTime)
+{
+ if (m_startTime != startTime || m_endTime != endTime) {
+ m_startTime = startTime;
+ m_endTime = endTime;
+ emit rangeChanged();
+ }
+}
+
+/////////////////////////////////////////////////////////
+ScrollableDeclarativeView::ScrollableDeclarativeView(QWidget *parent)
+ : QDeclarativeView(parent)
+{
+}
+
+ScrollableDeclarativeView::~ScrollableDeclarativeView()
+{
+}
+
+void ScrollableDeclarativeView::scrollContentsBy(int dx, int dy)
+{
+ // special workaround to track the scrollbar
+ if (rootObject()) {
+ int scrollY = rootObject()->property("scrollY").toInt();
+ rootObject()->setProperty("scrollY", QVariant(scrollY - dy));
+ }
+ QDeclarativeView::scrollContentsBy(dx,dy);
+}
+
+/////////////////////////////////////////////////////////
+class QmlProfilerTraceView::QmlProfilerTraceViewPrivate
+{
+public:
+ QmlProfilerTraceViewPrivate(QmlProfilerTraceView *qq) : q(qq) {}
+ QmlProfilerTraceView *q;
+
+ QmlProfilerStateManager *m_profilerState;
+ Analyzer::IAnalyzerTool *m_profilerTool;
+ QmlProfilerViewManager *m_viewContainer;
+
+ QSize m_sizeHint;
+
+ ScrollableDeclarativeView *m_mainView;
+ QDeclarativeView *m_timebar;
+ QDeclarativeView *m_overview;
+ QmlProfilerDataModel *m_profilerDataModel;
+
+ ZoomControl *m_zoomControl;
+
+ QToolButton *m_buttonRange;
+ QToolButton *m_buttonLock;
+ QWidget *m_zoomToolbar;
+ int m_currentZoomLevel;
+};
+
+QmlProfilerTraceView::QmlProfilerTraceView(QWidget *parent, Analyzer::IAnalyzerTool *profilerTool, QmlProfilerViewManager *container, QmlProfilerDataModel *model, QmlProfilerStateManager *profilerState)
+ : QWidget(parent), d(new QmlProfilerTraceViewPrivate(this))
+{
+ setObjectName("QML Profiler");
+
+ d->m_zoomControl = new ZoomControl(this);
+ connect(d->m_zoomControl, SIGNAL(rangeChanged()), this, SLOT(updateRange()));
+
+ QVBoxLayout *groupLayout = new QVBoxLayout;
+ groupLayout->setContentsMargins(0, 0, 0, 0);
+ groupLayout->setSpacing(0);
+
+ d->m_mainView = new ScrollableDeclarativeView(this);
+ d->m_mainView->setResizeMode(QDeclarativeView::SizeViewToRootObject);
+ d->m_mainView->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+ d->m_mainView->setBackgroundBrush(QBrush(Qt::white));
+ d->m_mainView->setAlignment(Qt::AlignLeft | Qt::AlignTop);
+ d->m_mainView->setFocus();
+
+ MouseWheelResizer *resizer = new MouseWheelResizer(this);
+ connect(resizer,SIGNAL(mouseWheelMoved(int,int,int)), this, SLOT(mouseWheelMoved(int,int,int)));
+ d->m_mainView->viewport()->installEventFilter(resizer);
+
+ QHBoxLayout *toolsLayout = new QHBoxLayout;
+
+ d->m_timebar = new QDeclarativeView(this);
+ d->m_timebar->setResizeMode(QDeclarativeView::SizeRootObjectToView);
+ d->m_timebar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+ d->m_timebar->setFixedHeight(24);
+
+ d->m_overview = new QDeclarativeView(this);
+ d->m_overview->setResizeMode(QDeclarativeView::SizeRootObjectToView);
+ d->m_overview->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+ d->m_overview->setMaximumHeight(50);
+
+ d->m_zoomToolbar = createZoomToolbar();
+ d->m_zoomToolbar->move(0, d->m_timebar->height());
+ d->m_zoomToolbar->setVisible(false);
+
+ toolsLayout->addWidget(createToolbar());
+ toolsLayout->addWidget(d->m_timebar);
+ emit enableToolbar(false);
+
+ groupLayout->addLayout(toolsLayout);
+ groupLayout->addWidget(d->m_mainView);
+ groupLayout->addWidget(d->m_overview);
+
+ setLayout(groupLayout);
+
+ d->m_profilerTool = profilerTool;
+ d->m_viewContainer = container;
+ d->m_profilerDataModel = model;
+ connect(d->m_profilerDataModel, SIGNAL(stateChanged()),
+ this, SLOT(profilerDataModelStateChanged()));
+ d->m_mainView->rootContext()->setContextProperty("qmlProfilerDataModel",
+ d->m_profilerDataModel);
+ d->m_overview->rootContext()->setContextProperty("qmlProfilerDataModel",
+ d->m_profilerDataModel);
+
+ d->m_profilerState = profilerState;
+ connect(d->m_profilerState, SIGNAL(stateChanged()),
+ this, SLOT(profilerStateChanged()));
+ connect(d->m_profilerState, SIGNAL(clientRecordingChanged()),
+ this, SLOT(clientRecordingChanged()));
+ connect(d->m_profilerState, SIGNAL(serverRecordingChanged()),
+ this, SLOT(serverRecordingChanged()));
+
+ // Minimum height: 5 rows of 20 pixels + scrollbar of 50 pixels + 20 pixels margin
+ setMinimumHeight(170);
+ d->m_currentZoomLevel = 0;
+}
+
+QmlProfilerTraceView::~QmlProfilerTraceView()
+{
+ delete d;
+}
+
+/////////////////////////////////////////////////////////
+// Initialize widgets
+void QmlProfilerTraceView::reset()
+{
+ d->m_mainView->rootContext()->setContextProperty("zoomControl", d->m_zoomControl);
+ d->m_timebar->rootContext()->setContextProperty("zoomControl", d->m_zoomControl);
+ d->m_overview->rootContext()->setContextProperty("zoomControl", d->m_zoomControl);
+
+ d->m_timebar->setSource(QUrl("qrc:/qmlprofiler/TimeDisplay.qml"));
+ d->m_overview->setSource(QUrl("qrc:/qmlprofiler/Overview.qml"));
+
+ d->m_mainView->setSource(QUrl("qrc:/qmlprofiler/MainView.qml"));
+ d->m_mainView->rootObject()->setProperty("width", QVariant(width()));
+ d->m_mainView->rootObject()->setProperty("candidateHeight", QVariant(height() - d->m_timebar->height() - d->m_overview->height()));
+
+ connect(d->m_mainView->rootObject(), SIGNAL(updateCursorPosition()), this, SLOT(updateCursorPosition()));
+ connect(d->m_mainView->rootObject(), SIGNAL(updateRangeButton()), this, SLOT(updateRangeButton()));
+ connect(d->m_mainView->rootObject(), SIGNAL(updateLockButton()), this, SLOT(updateLockButton()));
+ connect(this, SIGNAL(jumpToPrev()), d->m_mainView->rootObject(), SLOT(prevEvent()));
+ connect(this, SIGNAL(jumpToNext()), d->m_mainView->rootObject(), SLOT(nextEvent()));
+ connect(d->m_mainView->rootObject(), SIGNAL(selectedEventChanged(int)), this, SIGNAL(selectedEventChanged(int)));
+ connect(d->m_mainView->rootObject(), SIGNAL(changeToolTip(QString)), this, SLOT(updateToolTip(QString)));
+ connect(d->m_mainView->rootObject(), SIGNAL(updateVerticalScroll(int)), this, SLOT(updateVerticalScroll(int)));
+}
+
+QWidget *QmlProfilerTraceView::createToolbar()
+{
+ Utils::StyledBar *bar = new Utils::StyledBar(this);
+ bar->setSingleRow(true);
+ bar->setFixedWidth(150);
+ bar->setFixedHeight(24);
+
+ QHBoxLayout *toolBarLayout = new QHBoxLayout(bar);
+ toolBarLayout->setMargin(0);
+ toolBarLayout->setSpacing(0);
+
+ QToolButton *buttonPrev= new QToolButton;
+ buttonPrev->setIcon(QIcon(":/qmlprofiler/ico_prev.png"));
+ buttonPrev->setToolTip(tr("Jump to previous event"));
+ connect(buttonPrev, SIGNAL(clicked()), this, SIGNAL(jumpToPrev()));
+ connect(this, SIGNAL(enableToolbar(bool)), buttonPrev, SLOT(setEnabled(bool)));
+
+ QToolButton *buttonNext= new QToolButton;
+ buttonNext->setIcon(QIcon(":/qmlprofiler/ico_next.png"));
+ buttonNext->setToolTip(tr("Jump to next event"));
+ connect(buttonNext, SIGNAL(clicked()), this, SIGNAL(jumpToNext()));
+ connect(this, SIGNAL(enableToolbar(bool)), buttonNext, SLOT(setEnabled(bool)));
+
+ QToolButton *buttonZoomControls = new QToolButton;
+ buttonZoomControls->setIcon(QIcon(":/qmlprofiler/ico_zoom.png"));
+ buttonZoomControls->setToolTip(tr("Show zoom slider"));
+ buttonZoomControls->setCheckable(true);
+ buttonZoomControls->setChecked(false);
+ connect(buttonZoomControls, SIGNAL(toggled(bool)), d->m_zoomToolbar, SLOT(setVisible(bool)));
+ connect(this, SIGNAL(enableToolbar(bool)), buttonZoomControls, SLOT(setEnabled(bool)));
+
+ d->m_buttonRange = new QToolButton;
+ d->m_buttonRange->setIcon(QIcon(":/qmlprofiler/ico_rangeselection.png"));
+ d->m_buttonRange->setToolTip(tr("Select range"));
+ d->m_buttonRange->setCheckable(true);
+ d->m_buttonRange->setChecked(false);
+ connect(d->m_buttonRange, SIGNAL(clicked(bool)), this, SLOT(toggleRangeMode(bool)));
+ connect(this, SIGNAL(enableToolbar(bool)), d->m_buttonRange, SLOT(setEnabled(bool)));
+ connect(this, SIGNAL(rangeModeChanged(bool)), d->m_buttonRange, SLOT(setChecked(bool)));
+
+ d->m_buttonLock = new QToolButton;
+ d->m_buttonLock->setIcon(QIcon(":/qmlprofiler/ico_selectionmode.png"));
+ d->m_buttonLock->setToolTip(tr("View event information on mouseover"));
+ d->m_buttonLock->setCheckable(true);
+ d->m_buttonLock->setChecked(false);
+ connect(d->m_buttonLock, SIGNAL(clicked(bool)), this, SLOT(toggleLockMode(bool)));
+ connect(this, SIGNAL(enableToolbar(bool)), d->m_buttonLock, SLOT(setEnabled(bool)));
+ connect(this, SIGNAL(lockModeChanged(bool)), d->m_buttonLock, SLOT(setChecked(bool)));
+
+ toolBarLayout->addWidget(buttonPrev);
+ toolBarLayout->addWidget(buttonNext);
+ toolBarLayout->addWidget(new Utils::StyledSeparator());
+ toolBarLayout->addWidget(buttonZoomControls);
+ toolBarLayout->addWidget(new Utils::StyledSeparator());
+ toolBarLayout->addWidget(d->m_buttonRange);
+ toolBarLayout->addWidget(d->m_buttonLock);
+
+ return bar;
+}
+
+
+QWidget *QmlProfilerTraceView::createZoomToolbar()
+{
+ Utils::StyledBar *bar = new Utils::StyledBar(this);
+ bar->setSingleRow(true);
+ bar->setFixedWidth(150);
+ bar->setFixedHeight(24);
+
+ QHBoxLayout *toolBarLayout = new QHBoxLayout(bar);
+ toolBarLayout->setMargin(0);
+ toolBarLayout->setSpacing(0);
+
+ QSlider *zoomSlider = new QSlider(Qt::Horizontal);
+ zoomSlider->setFocusPolicy(Qt::NoFocus);
+ zoomSlider->setRange(1, sliderTicks);
+ zoomSlider->setInvertedAppearance(true);
+ zoomSlider->setPageStep(sliderTicks/100);
+ connect(this, SIGNAL(enableToolbar(bool)), zoomSlider, SLOT(setEnabled(bool)));
+ connect(zoomSlider, SIGNAL(valueChanged(int)), this, SLOT(setZoomLevel(int)));
+ connect(this, SIGNAL(zoomLevelChanged(int)), zoomSlider, SLOT(setValue(int)));
+ zoomSlider->setStyleSheet("\
+ QSlider:horizontal {\
+ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #444444, stop: 1 #5a5a5a);\
+ border: 1px #313131;\
+ height: 20px;\
+ margin: 0px 0px 0px 0px;\
+ }\
+ QSlider::add-page:horizontal {\
+ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #5a5a5a, stop: 1 #444444);\
+ border: 1px #313131;\
+ }\
+ QSlider::sub-page:horizontal {\
+ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #5a5a5a, stop: 1 #444444);\
+ border: 1px #313131;\
+ }\
+ ");
+
+ toolBarLayout->addWidget(zoomSlider);
+
+ return bar;
+}
+
+/////////////////////////////////////////////////////////
+bool QmlProfilerTraceView::hasValidSelection() const
+{
+ if (d->m_mainView->rootObject()) {
+ return d->m_mainView->rootObject()->property("selectionRangeReady").toBool();
+ }
+ return false;
+}
+
+qint64 QmlProfilerTraceView::selectionStart() const
+{
+ if (d->m_mainView->rootObject()) {
+ return d->m_mainView->rootObject()->property("selectionRangeStart").toLongLong();
+ }
+ return 0;
+}
+
+qint64 QmlProfilerTraceView::selectionEnd() const
+{
+ if (d->m_mainView->rootObject()) {
+ return d->m_mainView->rootObject()->property("selectionRangeEnd").toLongLong();
+ }
+ return 0;
+}
+
+void QmlProfilerTraceView::clearDisplay()
+{
+ d->m_zoomControl->setRange(0,0);
+
+ updateVerticalScroll(0);
+ d->m_mainView->rootObject()->setProperty("scrollY", QVariant(0));
+
+ QMetaObject::invokeMethod(d->m_mainView->rootObject(), "clearAll");
+ QMetaObject::invokeMethod(d->m_overview->rootObject(), "clearDisplay");
+}
+
+void QmlProfilerTraceView::selectNextEventWithId(int eventId)
+{
+ if (d->m_mainView->rootObject())
+ QMetaObject::invokeMethod(d->m_mainView->rootObject(), "selectNextWithId",
+ Q_ARG(QVariant,QVariant(eventId)));
+}
+
+/////////////////////////////////////////////////////////
+// Goto source location
+void QmlProfilerTraceView::updateCursorPosition()
+{
+ emit gotoSourceLocation(d->m_mainView->rootObject()->property("fileName").toString(),
+ d->m_mainView->rootObject()->property("lineNumber").toInt(),
+ d->m_mainView->rootObject()->property("columnNumber").toInt());
+}
+
+/////////////////////////////////////////////////////////
+// Toolbar buttons
+void QmlProfilerTraceView::toggleRangeMode(bool active)
+{
+ bool rangeMode = d->m_mainView->rootObject()->property("selectionRangeMode").toBool();
+ if (active != rangeMode) {
+ if (active)
+ d->m_buttonRange->setIcon(QIcon(":/qmlprofiler/ico_rangeselected.png"));
+ else
+ d->m_buttonRange->setIcon(QIcon(":/qmlprofiler/ico_rangeselection.png"));
+ d->m_mainView->rootObject()->setProperty("selectionRangeMode", QVariant(active));
+ }
+}
+
+void QmlProfilerTraceView::updateRangeButton()
+{
+ bool rangeMode = d->m_mainView->rootObject()->property("selectionRangeMode").toBool();
+ if (rangeMode)
+ d->m_buttonRange->setIcon(QIcon(":/qmlprofiler/ico_rangeselected.png"));
+ else
+ d->m_buttonRange->setIcon(QIcon(":/qmlprofiler/ico_rangeselection.png"));
+ emit rangeModeChanged(rangeMode);
+}
+
+void QmlProfilerTraceView::toggleLockMode(bool active)
+{
+ bool lockMode = !d->m_mainView->rootObject()->property("selectionLocked").toBool();
+ if (active != lockMode) {
+ d->m_mainView->rootObject()->setProperty("selectionLocked", QVariant(!active));
+ d->m_mainView->rootObject()->setProperty("selectedItem", QVariant(-1));
+ }
+}
+
+void QmlProfilerTraceView::updateLockButton()
+{
+ bool lockMode = !d->m_mainView->rootObject()->property("selectionLocked").toBool();
+ emit lockModeChanged(lockMode);
+}
+
+////////////////////////////////////////////////////////
+// Zoom control
+void QmlProfilerTraceView::setZoomLevel(int zoomLevel)
+{
+ if (d->m_currentZoomLevel != zoomLevel && d->m_mainView->rootObject()) {
+ QVariant newFactor = pow(qreal(zoomLevel) / qreal(sliderTicks), sliderExp);
+ d->m_currentZoomLevel = zoomLevel;
+ QMetaObject::invokeMethod(d->m_mainView->rootObject(), "updateWindowLength", Q_ARG(QVariant, newFactor));
+ }
+}
+
+void QmlProfilerTraceView::updateRange()
+{
+ if (!d->m_profilerDataModel)
+ return;
+ qreal duration = d->m_zoomControl->endTime() - d->m_zoomControl->startTime();
+ if (duration <= 0)
+ return;
+ if (d->m_profilerDataModel->traceDuration() <= 0)
+ return;
+ int newLevel = pow(duration / d->m_profilerDataModel->traceDuration(), 1/sliderExp) * sliderTicks;
+ if (d->m_currentZoomLevel != newLevel) {
+ d->m_currentZoomLevel = newLevel;
+ emit zoomLevelChanged(newLevel);
+ }
+}
+
+void QmlProfilerTraceView::mouseWheelMoved(int mouseX, int mouseY, int wheelDelta)
+{
+ Q_UNUSED(mouseY);
+ if (d->m_mainView->rootObject()) {
+ QMetaObject::invokeMethod(d->m_mainView->rootObject(), "wheelZoom",
+ Q_ARG(QVariant, QVariant(mouseX)),
+ Q_ARG(QVariant, QVariant(wheelDelta)));
+ }
+}
+////////////////////////////////////////////////////////
+void QmlProfilerTraceView::updateToolTip(const QString &text)
+{
+ setToolTip(text);
+}
+
+void QmlProfilerTraceView::updateVerticalScroll(int newPosition)
+{
+ d->m_mainView->verticalScrollBar()->setValue(newPosition);
+}
+
+void QmlProfilerTraceView::resizeEvent(QResizeEvent *event)
+{
+ if (d->m_mainView->rootObject()) {
+ d->m_mainView->rootObject()->setProperty("width", QVariant(event->size().width()));
+ int newHeight = event->size().height() - d->m_timebar->height() - d->m_overview->height();
+ d->m_mainView->rootObject()->setProperty("candidateHeight", QVariant(newHeight));
+ }
+}
+
+////////////////////////////////////////////////////////////////
+// Context menu
+void QmlProfilerTraceView::contextMenuEvent(QContextMenuEvent *ev)
+{
+ QMenu menu;
+ QAction *viewAllAction = 0;
+ QAction *getLocalStatsAction = 0;
+ QAction *getGlobalStatsAction = 0;
+
+ QmlProfilerTool *profilerTool = qobject_cast<QmlProfilerTool *>(d->m_profilerTool);
+ QPoint position = ev->globalPos();
+
+ if (profilerTool) {
+ QList <QAction *> commonActions = profilerTool->profilerContextMenuActions();
+ foreach (QAction *act, commonActions) {
+ menu.addAction(act);
+ }
+ }
+
+ menu.addSeparator();
+ getLocalStatsAction = menu.addAction(tr("Limit Events Pane to Current Range"));
+ if (!d->m_viewContainer->hasValidSelection())
+ getLocalStatsAction->setEnabled(false);
+ getGlobalStatsAction = menu.addAction(tr("Reset Events Pane"));
+ if (d->m_viewContainer->hasGlobalStats())
+ getGlobalStatsAction->setEnabled(false);
+
+
+ if (d->m_profilerDataModel->count() > 0) {
+ menu.addSeparator();
+ viewAllAction = menu.addAction(tr("Reset Zoom"));
+ }
+
+
+ QAction *selectedAction = menu.exec(position);
+
+ if (selectedAction) {
+ if (selectedAction == viewAllAction) {
+ d->m_zoomControl->setRange(
+ d->m_profilerDataModel->traceStartTime(),
+ d->m_profilerDataModel->traceEndTime());
+ }
+ if (selectedAction == getLocalStatsAction) {
+ d->m_viewContainer->getStatisticsInRange(
+ d->m_viewContainer->selectionStart(),
+ d->m_viewContainer->selectionEnd());
+ }
+ if (selectedAction == getGlobalStatsAction) {
+ d->m_viewContainer->getStatisticsInRange(
+ d->m_profilerDataModel->traceStartTime(),
+ d->m_profilerDataModel->traceEndTime());
+ }
+ }
+}
+
+/////////////////////////////////////////////////
+// Tell QML the state of the profiler
+void QmlProfilerTraceView::setRecording(bool recording)
+{
+ if (d->m_mainView->rootObject())
+ d->m_mainView->rootObject()->setProperty("recordingEnabled", QVariant(recording));
+}
+
+void QmlProfilerTraceView::setAppKilled()
+{
+ if (d->m_mainView->rootObject())
+ d->m_mainView->rootObject()->setProperty("appKilled",QVariant(true));
+}
+////////////////////////////////////////////////////////////////
+// Profiler State
+void QmlProfilerTraceView::profilerDataModelStateChanged()
+{
+ switch (d->m_profilerDataModel->currentState()) {
+ case QmlProfilerDataModel::Empty :
+ emit enableToolbar(false);
+ break;
+ case QmlProfilerDataModel::AcquiringData :
+ // nothing to be done
+ break;
+ case QmlProfilerDataModel::ProcessingData :
+ // nothing to be done
+ break;
+ case QmlProfilerDataModel::Done :
+ emit enableToolbar(true);
+ break;
+ default:
+ break;
+ }
+}
+
+void QmlProfilerTraceView::profilerStateChanged()
+{
+ switch (d->m_profilerState->currentState()) {
+ case QmlProfilerStateManager::AppKilled : {
+ if (d->m_profilerDataModel->currentState() == QmlProfilerDataModel::AcquiringData)
+ setAppKilled();
+ break;
+ }
+ default:
+ // no special action needed for other states
+ break;
+ }
+}
+
+void QmlProfilerTraceView::clientRecordingChanged()
+{
+ // nothing yet
+}
+
+void QmlProfilerTraceView::serverRecordingChanged()
+{
+ setRecording(d->m_profilerState->serverRecording());
+}
+
+} // namespace Internal
+} // namespace QmlProfiler
diff --git a/src/plugins/qmlprofiler/tracewindow.h b/src/plugins/qmlprofiler/qmlprofilertraceview.h
index 2959720db6..6b61380882 100644
--- a/src/plugins/qmlprofiler/tracewindow.h
+++ b/src/plugins/qmlprofiler/qmlprofilertraceview.h
@@ -30,24 +30,23 @@
**
**************************************************************************/
-#ifndef TRACEWINDOW_H
-#define TRACEWINDOW_H
+#ifndef QMLPROFILERTRACEVIEW_H
+#define QMLPROFILERTRACEVIEW_H
-#include <qmljsdebugclient/qmlprofilertraceclient.h>
-#include <qmljsdebugclient/qmlprofilereventlist.h>
-#include "qmlprofilerdetailsrewriter.h"
-#include <qmljsdebugclient/qv8profilerclient.h>
-
-#include <QPointer>
-#include <QWidget>
-#include <QToolButton>
-
-#include <QEvent>
#include <QDeclarativeView>
+namespace Analyzer {
+class IAnalyzerTool;
+}
+
namespace QmlProfiler {
namespace Internal {
+class QmlProfilerStateManager;
+class QmlProfilerViewManager;
+class QmlProfilerDataModel;
+
+// capture mouse wheel events
class MouseWheelResizer : public QObject {
Q_OBJECT
public:
@@ -87,117 +86,73 @@ protected:
void scrollContentsBy(int dx, int dy);
};
-class TraceWindow : public QWidget
+class QmlProfilerTraceView : public QWidget
{
Q_OBJECT
public:
- TraceWindow(QWidget *parent = 0);
- ~TraceWindow();
-
- void reset(QmlJsDebugClient::QDeclarativeDebugConnection *conn);
+ explicit QmlProfilerTraceView(QWidget *parent, Analyzer::IAnalyzerTool *profilerTool, QmlProfilerViewManager *container, QmlProfilerDataModel *model, QmlProfilerStateManager *profilerState);
+ ~QmlProfilerTraceView();
- QmlJsDebugClient::QmlProfilerEventList *getEventList() const;
- ZoomControl *rangeTimes() const;
-
- void setRecording(bool recording);
- bool isRecording() const;
- void viewAll();
+ void reset();
bool hasValidSelection() const;
qint64 selectionStart() const;
qint64 selectionEnd() const;
- double profiledTime() const;
public slots:
void clearDisplay();
- void selectNextEvent(int eventId);
- void applicationDied();
+ void selectNextEventWithId(int eventId);
private slots:
void updateCursorPosition();
- void updateTimer();
- void updateToolbar();
void toggleRangeMode(bool);
- void toggleLockMode(bool);
void updateRangeButton();
+ void toggleLockMode(bool);
void updateLockButton();
+
void setZoomLevel(int zoomLevel);
void updateRange();
- void mouseWheelMoved(int x, int y, int delta);
+ void mouseWheelMoved(int mouseX, int mouseY, int wheelDelta);
- void qmlComplete();
- void v8Complete();
- void updateProfilerState();
void updateToolTip(const QString &text);
void updateVerticalScroll(int newPosition);
- void eventListStateChanged();
- void manageTraceStart(qint64 traceStart);
- void firstDataReceived();
- void correctTimer();
+ void profilerDataModelStateChanged();
+
+protected:
+ virtual void resizeEvent(QResizeEvent *event);
+
+private slots:
+ void profilerStateChanged();
+ void clientRecordingChanged();
+ void serverRecordingChanged();
signals:
- void viewUpdated();
- void profilerStateChanged(bool qmlActive, bool v8active);
void gotoSourceLocation(const QString &fileUrl, int lineNumber, int columNumber);
- void range(int type, qint64 startTime, qint64 length, const QStringList &data, const QmlJsDebugClient::QmlEventLocation &location);
- void v8range(int depth,const QString &function,const QString &filename,
- int lineNumber, double totalTime, double selfTime);
- void traceFinished(qint64);
- void traceStarted(qint64);
- void frameEvent(qint64, int, int);
- void recordingChanged(bool);
-
- void internalClearDisplay();
- void clearViewsFromTool();
+ void selectedEventChanged(int eventId);
+
void jumpToPrev();
void jumpToNext();
void rangeModeChanged(bool);
void lockModeChanged(bool);
void enableToolbar(bool);
void zoomLevelChanged(int);
- void updateViewZoom(QVariant zoomLevel);
- void wheelZoom(QVariant wheelCenter, QVariant wheelDelta);
- void globalZoom();
-
- void contextMenuRequested(const QPoint& position);
- void selectNextEventInDisplay(QVariant eventId);
- void selectedEventIdChanged(int eventId);
private:
void contextMenuEvent(QContextMenuEvent *);
QWidget *createToolbar();
QWidget *createZoomToolbar();
- void connectClientSignals();
- void disconnectClientSignals();
-protected:
- virtual void resizeEvent(QResizeEvent *event);
+ void setRecording(bool recording);
+ void setAppKilled();
private:
- QWeakPointer<QmlJsDebugClient::QmlProfilerTraceClient> m_plugin;
- QWeakPointer<QmlJsDebugClient::QV8ProfilerClient> m_v8plugin;
- QSize m_sizeHint;
-
- ScrollableDeclarativeView *m_mainView;
- QDeclarativeView *m_timebar;
- QDeclarativeView *m_overview;
- QmlJsDebugClient::QmlProfilerEventList *m_eventList;
- QmlProfilerDetailsRewriter *m_rewriter;
- bool m_qmlDataReady;
- bool m_v8DataReady;
- double m_profiledTime;
-
- QWeakPointer<ZoomControl> m_zoomControl;
-
- QToolButton *m_buttonRange;
- QToolButton *m_buttonLock;
- QWidget *m_zoomToolbar;
- int m_currentZoomLevel;
+ class QmlProfilerTraceViewPrivate;
+ QmlProfilerTraceViewPrivate *d;
};
} // namespace Internal
} // namespace QmlProfiler
-#endif // TRACEWINDOW_H
+#endif // QMLPROFILERTRACEVIEW_H
diff --git a/src/plugins/qmlprofiler/qmlprofilerviewmanager.cpp b/src/plugins/qmlprofiler/qmlprofilerviewmanager.cpp
new file mode 100644
index 0000000000..a12a6ccda5
--- /dev/null
+++ b/src/plugins/qmlprofiler/qmlprofilerviewmanager.cpp
@@ -0,0 +1,172 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** 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 qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "qmlprofilerviewmanager.h"
+
+#include "qmlprofilertraceview.h"
+#include "qmlprofilereventview.h"
+#include "qmlprofilertool.h"
+#include "qmlprofilerstatemanager.h"
+#include "qmlprofilerdatamodel.h"
+
+#include <utils/qtcassert.h>
+#include <utils/fancymainwindow.h>
+#include <analyzerbase/analyzermanager.h>
+
+
+#include <QDockWidget>
+
+using namespace Analyzer;
+
+namespace QmlProfiler {
+namespace Internal {
+
+class QmlProfilerViewManager::QmlProfilerViewManagerPrivate {
+public:
+ QmlProfilerViewManagerPrivate(QmlProfilerViewManager *qq) { Q_UNUSED(qq); }
+
+ QmlProfilerTraceView *traceView;
+ QmlProfilerEventsWidget *eventsView;
+ QmlProfilerEventsWidget *v8profilerView;
+ QmlProfilerStateManager *profilerState;
+ QmlProfilerDataModel *profilerDataModel;
+ QmlProfilerTool *profilerTool;
+};
+
+QmlProfilerViewManager::QmlProfilerViewManager(QObject *parent,
+ QmlProfilerTool *profilerTool,
+ QmlProfilerDataModel *model,
+ QmlProfilerStateManager *profilerState)
+ : QObject(parent), d(new QmlProfilerViewManagerPrivate(this))
+{
+ setObjectName("QML Profiler View Manager");
+ d->traceView = 0;
+ d->eventsView = 0;
+ d->v8profilerView = 0;
+ d->profilerState = profilerState;
+ d->profilerDataModel = model;
+ d->profilerTool = profilerTool;
+ createViews();
+}
+
+QmlProfilerViewManager::~QmlProfilerViewManager()
+{
+ delete d;
+}
+
+////////////////////////////////////////////////////////////
+// Views
+void QmlProfilerViewManager::createViews()
+{
+ QTC_ASSERT(d->profilerDataModel, return);
+ QTC_ASSERT(d->profilerState, return);
+
+ Utils::FancyMainWindow *mw = AnalyzerManager::mainWindow();
+
+ d->traceView = new QmlProfilerTraceView(mw,
+ d->profilerTool,
+ this,
+ d->profilerDataModel,
+ d->profilerState);
+ connect(d->traceView, SIGNAL(gotoSourceLocation(QString,int,int)),
+ this, SIGNAL(gotoSourceLocation(QString,int,int)));
+
+ d->traceView->reset();
+
+ d->eventsView = new QmlProfilerEventsWidget(mw, d->profilerTool, this, d->profilerDataModel);
+ connect(d->eventsView, SIGNAL(gotoSourceLocation(QString,int,int)), this,
+ SIGNAL(gotoSourceLocation(QString,int,int)));
+ connect(d->eventsView, SIGNAL(showEventInTimeline(int)), d->traceView,
+ SLOT(selectNextEventWithId(int)));
+ connect(d->traceView, SIGNAL(selectedEventChanged(int)), d->eventsView,
+ SLOT(updateSelectedEvent(int)));
+
+ d->v8profilerView = new QmlProfilerEventsWidget(mw, d->profilerTool,
+ this, d->profilerDataModel);
+ d->v8profilerView->switchToV8View();
+ connect(d->v8profilerView, SIGNAL(gotoSourceLocation(QString,int,int)),
+ this, SIGNAL(gotoSourceLocation(QString,int,int)));
+ connect(d->v8profilerView, SIGNAL(gotoSourceLocation(QString,int,int)),
+ d->eventsView, SLOT(selectBySourceLocation(QString,int,int)));
+ connect(d->eventsView, SIGNAL(gotoSourceLocation(QString,int,int)),
+ d->v8profilerView, SLOT(selectBySourceLocation(QString,int,int)));
+
+ QDockWidget *eventsDock = AnalyzerManager::createDockWidget
+ (d->profilerTool, tr("Events"), d->eventsView, Qt::BottomDockWidgetArea);
+ QDockWidget *timelineDock = AnalyzerManager::createDockWidget
+ (d->profilerTool, tr("Timeline"), d->traceView, Qt::BottomDockWidgetArea);
+ QDockWidget *v8profilerDock = AnalyzerManager::createDockWidget
+ (d->profilerTool, tr("JavaScript"), d->v8profilerView, Qt::BottomDockWidgetArea);
+
+ eventsDock->show();
+ timelineDock->show();
+ v8profilerDock->show();
+
+ mw->splitDockWidget(mw->toolBarDockWidget(), eventsDock, Qt::Vertical);
+ mw->tabifyDockWidget(eventsDock, timelineDock);
+ mw->tabifyDockWidget(timelineDock, v8profilerDock);
+}
+
+bool QmlProfilerViewManager::hasValidSelection() const
+{
+ return d->traceView->hasValidSelection();
+}
+
+qint64 QmlProfilerViewManager::selectionStart() const
+{
+ return d->traceView->selectionStart();
+}
+
+qint64 QmlProfilerViewManager::selectionEnd() const
+{
+ return d->traceView->selectionEnd();
+}
+
+bool QmlProfilerViewManager::hasGlobalStats() const
+{
+ return d->eventsView->hasGlobalStats();
+}
+
+void QmlProfilerViewManager::getStatisticsInRange(qint64 rangeStart, qint64 rangeEnd)
+{
+ d->eventsView->getStatisticsInRange(rangeStart, rangeEnd);
+}
+
+void QmlProfilerViewManager::clear()
+{
+ d->traceView->clearDisplay();
+ d->eventsView->clear();
+ d->v8profilerView->clear();
+}
+
+} // namespace Internal
+} // namespace QmlProfiler
diff --git a/src/tools/qmlprofilertool/main.cpp b/src/plugins/qmlprofiler/qmlprofilerviewmanager.h
index bb5c2bd328..4e492918ee 100644
--- a/src/tools/qmlprofilertool/main.cpp
+++ b/src/plugins/qmlprofiler/qmlprofilerviewmanager.h
@@ -30,26 +30,50 @@
**
**************************************************************************/
-#include "qmlprofilerapplication.h"
-#include "commandlistener.h"
+#ifndef QMLPROFILERVIEWMANAGER_H
+#define QMLPROFILERVIEWMANAGER_H
-int main(int argc, char *argv[])
+#include <QObject>
+
+namespace QmlProfiler {
+namespace Internal {
+
+class QmlProfilerTool;
+class QmlProfilerDataModel;
+class QmlProfilerStateManager;
+
+class QmlProfilerViewManager : public QObject
{
- QmlProfilerApplication app(argc, argv);
+ Q_OBJECT
+public:
+ explicit QmlProfilerViewManager(QObject *parent,
+ QmlProfilerTool *profilerTool,
+ QmlProfilerDataModel *model,
+ QmlProfilerStateManager *profilerState);
+ ~QmlProfilerViewManager();
+
+ void createViews();
+
+ // used by the options "limit events to range"
+ bool hasValidSelection() const;
+ qint64 selectionStart() const;
+ qint64 selectionEnd() const;
+ bool hasGlobalStats() const;
+ void getStatisticsInRange(qint64 rangeStart, qint64 rangeEnd);
+
+public slots:
+ void clear();
- if (!app.parseArguments()) {
- app.printUsage();
- return 1;
- }
+signals:
+ void gotoSourceLocation(QString,int,int);
- CommandListener listener;
- QObject::connect(&listener, SIGNAL(command(QString)), &app, SLOT(userCommand(QString)));
- listener.start();
+private:
+ class QmlProfilerViewManagerPrivate;
+ QmlProfilerViewManagerPrivate *d;
+};
- int exitValue = app.exec();
- // wait for listener to exit
- listener.wait();
+} // namespace Internal
+} // namespace QmlProfiler
- return exitValue;
-}
+#endif // QMLPROFILERVIEWMANAGER_H
diff --git a/src/plugins/qmlprofiler/qv8profilerdatamodel.cpp b/src/plugins/qmlprofiler/qv8profilerdatamodel.cpp
new file mode 100644
index 0000000000..d58f198be6
--- /dev/null
+++ b/src/plugins/qmlprofiler/qv8profilerdatamodel.cpp
@@ -0,0 +1,445 @@
+#include "qv8profilerdatamodel.h"
+#include "qmlprofilerdatamodel.h"
+
+#include <QStringList>
+
+using namespace QmlJsDebugClient;
+
+QT_BEGIN_NAMESPACE
+Q_DECLARE_TYPEINFO(QmlProfiler::Internal::QV8EventData, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QmlProfiler::Internal::QV8EventSub, Q_MOVABLE_TYPE);
+QT_END_NAMESPACE
+
+namespace QmlProfiler {
+namespace Internal {
+
+QV8EventData &QV8EventData::operator=(const QV8EventData &ref)
+{
+ if (this == &ref)
+ return *this;
+
+ displayName = ref.displayName;
+ eventHashStr = ref.eventHashStr;
+ filename = ref.filename;
+ functionName = ref.functionName;
+ line = ref.line;
+ totalTime = ref.totalTime;
+ totalPercent = ref.totalPercent;
+ selfTime = ref.selfTime;
+ selfPercent = ref.selfPercent;
+ eventId = ref.eventId;
+
+ qDeleteAll(parentHash.values());
+ parentHash.clear();
+ foreach (const QString &key, ref.parentHash.keys()) {
+ parentHash.insert(key, new QV8EventSub(ref.parentHash.value(key)));
+ }
+
+ qDeleteAll(childrenHash.values());
+ childrenHash.clear();
+ foreach (const QString &key, ref.childrenHash.keys()) {
+ childrenHash.insert(key, new QV8EventSub(ref.childrenHash.value(key)));
+ }
+ return *this;
+}
+
+QV8EventData::QV8EventData()
+{
+ line = -1;
+ eventId = -1;
+ totalTime = 0;
+ selfTime = 0;
+ totalPercent = 0;
+ selfPercent = 0;
+}
+
+QV8EventData::~QV8EventData()
+{
+ qDeleteAll(parentHash.values());
+ parentHash.clear();
+ qDeleteAll(childrenHash.values());
+ childrenHash.clear();
+}
+
+class QV8ProfilerDataModel::QV8ProfilerDataModelPrivate
+{
+public:
+ QV8ProfilerDataModelPrivate(QV8ProfilerDataModel *qq) {Q_UNUSED(qq);}
+
+ QmlProfilerDataModel *qmlProfilerDataModel;
+
+ void clearV8RootEvent();
+ void collectV8Statistics();
+
+ QHash<QString, QV8EventData *> v8EventHash;
+ QHash<int, QV8EventData *> v8parents;
+ QV8EventData v8RootEvent;
+ qint64 v8MeasuredTime;
+};
+
+QV8ProfilerDataModel::QV8ProfilerDataModel(QObject *parent,
+ QmlProfilerDataModel *profilerDataModel)
+ : QObject(parent), d(new QV8ProfilerDataModelPrivate(this))
+{
+ d->v8MeasuredTime = 0;
+ d->clearV8RootEvent();
+ d->qmlProfilerDataModel = profilerDataModel;
+}
+
+QV8ProfilerDataModel::~QV8ProfilerDataModel()
+{
+ delete d;
+}
+
+void QV8ProfilerDataModel::clear()
+{
+ qDeleteAll(d->v8EventHash.values());
+ d->v8EventHash.clear();
+ d->v8parents.clear();
+ d->clearV8RootEvent();
+ d->v8MeasuredTime = 0;
+}
+
+bool QV8ProfilerDataModel::isEmpty() const
+{
+ return d->v8EventHash.isEmpty();
+}
+
+QV8EventData *QV8ProfilerDataModel::v8EventDescription(int eventId) const
+{
+ foreach (QV8EventData *event, d->v8EventHash.values()) {
+ if (event->eventId == eventId)
+ return event;
+ }
+ return 0;
+}
+
+qint64 QV8ProfilerDataModel::v8MeasuredTime() const
+{
+ return d->v8MeasuredTime;
+}
+
+QList<QV8EventData *> QV8ProfilerDataModel::getV8Events() const
+{
+ return d->v8EventHash.values();
+}
+
+void QV8ProfilerDataModel::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);
+ QString hashStr = QmlProfilerDataModel::getHashStringForV8Event(displayName, function);
+
+ d->qmlProfilerDataModel->setState(QmlProfilerDataModel::AcquiringData);
+
+ // time is given in milliseconds, but internally we store it in microseconds
+ totalTime *= 1e6;
+ selfTime *= 1e6;
+
+ // accumulate information
+ QV8EventData *eventData = d->v8EventHash[hashStr];
+ if (!eventData) {
+ eventData = new QV8EventData;
+ eventData->displayName = displayName;
+ eventData->eventHashStr = hashStr;
+ eventData->filename = filename;
+ eventData->functionName = function;
+ eventData->line = lineNumber;
+ eventData->totalTime = totalTime;
+ eventData->selfTime = selfTime;
+ d->v8EventHash[hashStr] = eventData;
+ } else {
+ eventData->totalTime += totalTime;
+ eventData->selfTime += selfTime;
+ }
+ d->v8parents[depth] = eventData;
+
+ QV8EventData *parentEvent = 0;
+ if (depth == 0) {
+ parentEvent = &d->v8RootEvent;
+ d->v8MeasuredTime += totalTime;
+ }
+ if (depth > 0 && d->v8parents.contains(depth-1)) {
+ parentEvent = d->v8parents.value(depth-1);
+ }
+
+ if (parentEvent != 0) {
+ if (!eventData->parentHash.contains(parentEvent->eventHashStr)) {
+ QV8EventSub *newParentSub = new QV8EventSub(parentEvent);
+ newParentSub->totalTime = totalTime;
+
+ eventData->parentHash.insert(parentEvent->eventHashStr, newParentSub);
+ } else {
+ QV8EventSub *newParentSub = eventData->parentHash.value(parentEvent->eventHashStr);
+ newParentSub->totalTime += totalTime;
+ }
+
+ if (!parentEvent->childrenHash.contains(eventData->eventHashStr)) {
+ QV8EventSub *newChildSub = new QV8EventSub(eventData);
+ newChildSub->totalTime = totalTime;
+
+ parentEvent->childrenHash.insert(eventData->eventHashStr, newChildSub);
+ } else {
+ QV8EventSub *newChildSub = parentEvent->childrenHash.value(eventData->eventHashStr);
+ newChildSub->totalTime += totalTime;
+ }
+ }
+}
+
+void QV8ProfilerDataModel::collectV8Statistics()
+{
+ d->collectV8Statistics();
+}
+
+void QV8ProfilerDataModel::QV8ProfilerDataModelPrivate::collectV8Statistics()
+{
+ if (!v8EventHash.isEmpty()) {
+ double totalTimes = v8MeasuredTime;
+ double selfTimes = 0;
+ foreach (QV8EventData *v8event, v8EventHash.values()) {
+ selfTimes += v8event->selfTime;
+ }
+
+ // prevent divisions by 0
+ if (totalTimes == 0)
+ totalTimes = 1;
+ if (selfTimes == 0)
+ selfTimes = 1;
+
+ // insert root event in eventlist
+ // the +1 ns is to get it on top of the sorted list
+ v8RootEvent.totalTime = v8MeasuredTime + 1;
+ v8RootEvent.selfTime = 0;
+
+ QString rootEventHash = QmlProfilerDataModel::getHashStringForV8Event(
+ QmlProfilerDataModel::rootEventName(),
+ QmlProfilerDataModel::rootEventDescription());
+ QV8EventData *v8RootEventPointer = v8EventHash[rootEventHash];
+ if (v8RootEventPointer) {
+ v8RootEvent = *v8RootEventPointer;
+ } else {
+ v8EventHash[rootEventHash] = new QV8EventData;
+ *v8EventHash[rootEventHash] = v8RootEvent;
+ }
+
+ foreach (QV8EventData *v8event, v8EventHash.values()) {
+ v8event->totalPercent = v8event->totalTime * 100.0 / totalTimes;
+ v8event->selfPercent = v8event->selfTime * 100.0 / selfTimes;
+ }
+
+ int index = 0;
+ foreach (QV8EventData *v8event, v8EventHash.values()) {
+ v8event->eventId = index++;
+ }
+ v8RootEvent.eventId = v8EventHash[rootEventHash]->eventId;
+ }
+}
+
+void QV8ProfilerDataModel::QV8ProfilerDataModelPrivate::clearV8RootEvent()
+{
+ v8RootEvent.displayName = QmlProfilerDataModel::rootEventName();
+ v8RootEvent.eventHashStr = QmlProfilerDataModel::rootEventName();
+ v8RootEvent.functionName = QmlProfilerDataModel::rootEventDescription();
+ v8RootEvent.line = -1;
+ v8RootEvent.totalTime = 0;
+ v8RootEvent.totalPercent = 0;
+ v8RootEvent.selfTime = 0;
+ v8RootEvent.selfPercent = 0;
+ v8RootEvent.eventId = -1;
+
+ qDeleteAll(v8RootEvent.parentHash.values());
+ qDeleteAll(v8RootEvent.childrenHash.values());
+ v8RootEvent.parentHash.clear();
+ v8RootEvent.childrenHash.clear();
+}
+
+void QV8ProfilerDataModel::save(QXmlStreamWriter &stream)
+{
+ stream.writeStartElement("v8profile"); // v8 profiler output
+ stream.writeAttribute("totalTime", QString::number(d->v8MeasuredTime));
+ foreach (QV8EventData *v8event, d->v8EventHash.values()) {
+ stream.writeStartElement("event");
+ stream.writeAttribute("index",
+ QString::number(
+ d->v8EventHash.keys().indexOf(
+ v8event->eventHashStr)));
+ 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->childrenHash.isEmpty()) {
+ stream.writeStartElement("childrenEvents");
+ QStringList childrenIndexes;
+ QStringList childrenTimes;
+ QStringList parentTimes;
+ foreach (QV8EventSub *v8child, v8event->childrenHash.values()) {
+ childrenIndexes << QString::number(v8child->reference->eventId);
+ childrenTimes << QString::number(v8child->totalTime);
+ parentTimes << QString::number(v8child->totalTime);
+ }
+
+ stream.writeAttribute("list", childrenIndexes.join(QString(", ")));
+ stream.writeAttribute("childrenTimes", childrenTimes.join(QString(", ")));
+ stream.writeAttribute("parentTimes", parentTimes.join(QString(", ")));
+ stream.writeEndElement();
+ }
+ stream.writeEndElement();
+ }
+ stream.writeEndElement(); // v8 profiler output
+}
+
+void QV8ProfilerDataModel::load(QXmlStreamReader &stream)
+{
+ QHash <int, QV8EventData *> v8eventBuffer;
+ QHash <int, QString> childrenIndexes;
+ QHash <int, QString> childrenTimes;
+ QHash <int, QString> parentTimes;
+ QV8EventData *v8event = 0;
+
+ // time computation
+ d->v8MeasuredTime = 0;
+ double cumulatedV8Time = 0;
+
+ // get the v8 time
+ QXmlStreamAttributes attributes = stream.attributes();
+ if (attributes.hasAttribute("totalTime"))
+ d->v8MeasuredTime = attributes.value("totalTime").toString().toDouble();
+
+ while (!stream.atEnd() && !stream.hasError()) {
+ QXmlStreamReader::TokenType token = stream.readNext();
+ QString elementName = stream.name().toString();
+ 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 (!v8eventBuffer.value(ndx))
+ v8eventBuffer[ndx] = new QV8EventData;
+ v8event = v8eventBuffer[ndx];
+ } else {
+ v8event = 0;
+ }
+ break;
+ }
+
+ if (!v8event)
+ break;
+
+ if (elementName == "childrenEvents") {
+ QXmlStreamAttributes attributes = stream.attributes();
+ int eventIndex = v8eventBuffer.key(v8event);
+ if (attributes.hasAttribute("list")) {
+ // store for later parsing (we haven't read all the events yet)
+ childrenIndexes[eventIndex] = attributes.value("list").toString();
+ }
+ if (attributes.hasAttribute("childrenTimes")) {
+ childrenTimes[eventIndex] =
+ attributes.value("childrenTimes").toString();
+ }
+ if (attributes.hasAttribute("parentTimes")) {
+ parentTimes[eventIndex] = attributes.value("parentTimes").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();
+ cumulatedV8Time += v8event->totalTime;
+ break;
+ }
+
+ if (elementName == "selfTime") {
+ v8event->selfTime = readData.toDouble();
+ break;
+ }
+ break;
+ }
+ case QXmlStreamReader::EndElement : {
+ if (elementName == "v8profile") {
+ // done reading the v8 profile data
+ break;
+ }
+ }
+ default: break;
+ }
+}
+
+ // backwards compatibility
+ if (d->v8MeasuredTime == 0)
+ d->v8MeasuredTime = cumulatedV8Time;
+
+ // find v8events' children and parents
+ foreach (int parentIndex, childrenIndexes.keys()) {
+ QStringList childrenStrings = childrenIndexes.value(parentIndex).split(",");
+ QStringList childrenTimesStrings = childrenTimes.value(parentIndex).split(", ");
+ QStringList parentTimesStrings = parentTimes.value(parentIndex).split(", ");
+ for (int ndx = 0; ndx < childrenStrings.count(); ndx++) {
+ int childIndex = childrenStrings[ndx].toInt();
+ if (v8eventBuffer.value(childIndex)) {
+ QV8EventSub *newChild = new QV8EventSub(v8eventBuffer[childIndex]);
+ QV8EventSub *newParent = new QV8EventSub(v8eventBuffer[parentIndex]);
+ if (childrenTimesStrings.count() > ndx) {
+ newChild->totalTime = childrenTimesStrings[ndx].toDouble();
+ }
+ if (parentTimesStrings.count() > ndx) {
+ newParent->totalTime = parentTimesStrings[ndx].toDouble();
+ }
+ v8eventBuffer[parentIndex]->childrenHash.insert(
+ newChild->reference->displayName,
+ newChild);
+ v8eventBuffer[childIndex]->parentHash.insert(
+ newParent->reference->displayName,
+ newParent);
+ }
+ }
+ }
+
+ // store v8 events
+ foreach (QV8EventData *storedV8Event, v8eventBuffer.values()) {
+ storedV8Event->eventHashStr =
+ QmlProfilerDataModel::getHashStringForV8Event(
+ storedV8Event->displayName, storedV8Event->functionName);
+ d->v8EventHash[storedV8Event->eventHashStr] = storedV8Event;
+ }
+
+ d->collectV8Statistics();
+
+}
+
+} // namespace Internal
+} // namespace QmlProfiler
diff --git a/src/plugins/qmlprofiler/qv8profilerdatamodel.h b/src/plugins/qmlprofiler/qv8profilerdatamodel.h
new file mode 100644
index 0000000000..6441a24702
--- /dev/null
+++ b/src/plugins/qmlprofiler/qv8profilerdatamodel.h
@@ -0,0 +1,79 @@
+#ifndef QV8PROFILERDATAMODEL_H
+#define QV8PROFILERDATAMODEL_H
+
+#include <QObject>
+#include <QHash>
+
+#include <QXmlStreamReader>
+#include <QXmlStreamWriter>
+
+namespace QmlProfiler {
+namespace Internal {
+
+class QmlProfilerDataModel;
+struct QV8EventSub;
+
+struct QV8EventData
+{
+ QV8EventData();
+ ~QV8EventData();
+
+ QString displayName;
+ QString eventHashStr;
+ QString filename;
+ QString functionName;
+ int line;
+ double totalTime; // given in milliseconds
+ double totalPercent;
+ double selfTime;
+ double selfPercent;
+ QHash <QString, QV8EventSub *> parentHash;
+ QHash <QString, QV8EventSub *> childrenHash;
+ int eventId;
+
+ QV8EventData &operator=(const QV8EventData &ref);
+};
+
+struct QV8EventSub {
+ QV8EventSub(QV8EventData *from) : reference(from), totalTime(0) {}
+ QV8EventSub(QV8EventSub *from) : reference(from->reference), totalTime(from->totalTime) {}
+
+ QV8EventData *reference;
+ qint64 totalTime;
+};
+
+class QV8ProfilerDataModel : public QObject
+{
+ Q_OBJECT
+public:
+ explicit QV8ProfilerDataModel(QObject *parent, QmlProfilerDataModel *profilerDataModel);
+ ~QV8ProfilerDataModel();
+
+ void clear();
+ bool isEmpty() const;
+ QList<QV8EventData *> getV8Events() const;
+ QV8EventData *v8EventDescription(int eventId) const;
+
+ qint64 v8MeasuredTime() const;
+ void collectV8Statistics();
+
+ void save(QXmlStreamWriter &stream);
+ void load(QXmlStreamReader &stream);
+
+public slots:
+ void addV8Event(int depth,
+ const QString &function,
+ const QString &filename,
+ int lineNumber,
+ double totalTime,
+ double selfTime);
+
+private:
+ class QV8ProfilerDataModelPrivate;
+ QV8ProfilerDataModelPrivate *d;
+};
+
+} // namespace Internal
+} // namespace QmlProfiler
+
+#endif // QV8PROFILERDATAMODEL_H
diff --git a/src/plugins/qmlprofiler/timelineview.cpp b/src/plugins/qmlprofiler/timelinerenderer.cpp
index 1e10eb0075..a078d31f84 100644
--- a/src/plugins/qmlprofiler/timelineview.cpp
+++ b/src/plugins/qmlprofiler/timelinerenderer.cpp
@@ -30,7 +30,7 @@
**
**************************************************************************/
-#include "timelineview.h"
+#include "timelinerenderer.h"
#include <qdeclarativecontext.h>
#include <qdeclarativeproperty.h>
@@ -45,9 +45,9 @@ using namespace QmlProfiler::Internal;
const int DefaultRowHeight = 30;
-TimelineView::TimelineView(QDeclarativeItem *parent) :
+TimelineRenderer::TimelineRenderer(QDeclarativeItem *parent) :
QDeclarativeItem(parent), m_startTime(0), m_endTime(0), m_spacing(0),
- m_lastStartTime(0), m_lastEndTime(0), m_eventList(0)
+ m_lastStartTime(0), m_lastEndTime(0), m_profilerDataModel(0)
{
clearData();
setFlag(QGraphicsItem::ItemHasNoContents, false);
@@ -57,12 +57,12 @@ TimelineView::TimelineView(QDeclarativeItem *parent) :
m_rowsExpanded << false;
}
-void TimelineView::componentComplete()
+void TimelineRenderer::componentComplete()
{
const QMetaObject *metaObject = this->metaObject();
int propertyCount = metaObject->propertyCount();
int requestPaintMethod = metaObject->indexOfMethod("requestPaint()");
- for (int ii = TimelineView::staticMetaObject.propertyCount(); ii < propertyCount; ++ii) {
+ for (int ii = TimelineRenderer::staticMetaObject.propertyCount(); ii < propertyCount; ++ii) {
QMetaProperty p = metaObject->property(ii);
if (p.hasNotifySignal())
QMetaObject::connect(this, p.notifySignalIndex(), this, requestPaintMethod, 0, 0);
@@ -70,12 +70,12 @@ void TimelineView::componentComplete()
QDeclarativeItem::componentComplete();
}
-void TimelineView::requestPaint()
+void TimelineRenderer::requestPaint()
{
update();
}
-void TimelineView::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *)
+void TimelineRenderer::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *)
{
qint64 windowDuration = m_endTime - m_startTime;
if (windowDuration <= 0)
@@ -86,7 +86,8 @@ void TimelineView::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget
m_rowWidths.clear();
// The "1+" is because the reference screenshot features an empty row per type, in order to leave space for the title
for (int i=0; i<QmlJsDebugClient::MaximumQmlEventType; i++) {
- m_rowWidths << 1 + (m_rowsExpanded[i] ? m_eventList->uniqueEventsOfType(i) : m_eventList->maxNestingForType(i));
+ m_rowWidths << 1 + (m_rowsExpanded[i] ? m_profilerDataModel->uniqueEventsOfType(i) :
+ m_profilerDataModel->maxNestingForType(i));
}
// event rows
@@ -105,8 +106,8 @@ void TimelineView::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget
for (int j=0; j<m_rowWidths[i]; j++)
m_rowLastX << -m_startTime * m_spacing;
- int firstIndex = m_eventList->findFirstIndex(m_startTime);
- int lastIndex = m_eventList->findLastIndex(m_endTime);
+ int firstIndex = m_profilerDataModel->findFirstIndex(m_startTime);
+ int lastIndex = m_profilerDataModel->findLastIndex(m_endTime);
drawItemsToPainter(p, firstIndex, lastIndex);
drawSelectionBoxes(p, firstIndex, lastIndex);
@@ -116,25 +117,27 @@ void TimelineView::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget
m_lastEndTime = m_endTime;
}
-QColor TimelineView::colorForItem(int itemIndex)
+QColor TimelineRenderer::colorForItem(int itemIndex)
{
- int ndx = m_eventList->getEventId(itemIndex);
+ int ndx = m_profilerDataModel->getEventId(itemIndex);
return QColor::fromHsl((ndx*25)%360, 76, 166);
}
-void TimelineView::drawItemsToPainter(QPainter *p, int fromIndex, int toIndex)
+void TimelineRenderer::drawItemsToPainter(QPainter *p, int fromIndex, int toIndex)
{
int x, y, width, height, rowNumber, eventType;
for (int i = fromIndex; i <= toIndex; i++) {
- x = (m_eventList->getStartTime(i) - m_startTime) * m_spacing;
+ x = (m_profilerDataModel->getStartTime(i) - m_startTime) * m_spacing;
- eventType = m_eventList->getType(i);
+ eventType = m_profilerDataModel->getType(i);
if (m_rowsExpanded[eventType])
- y = m_rowStarts[eventType] + DefaultRowHeight*(m_eventList->eventPosInType(i) + 1);
+ y = m_rowStarts[eventType] + DefaultRowHeight *
+ (m_profilerDataModel->eventPosInType(i) + 1);
else
- y = m_rowStarts[eventType] + DefaultRowHeight*m_eventList->getNestingLevel(i);
+ y = m_rowStarts[eventType] + DefaultRowHeight *
+ m_profilerDataModel->getNestingLevel(i);
- width = m_eventList->getDuration(i)*m_spacing;
+ width = m_profilerDataModel->getDuration(i)*m_spacing;
if (width<1)
width = 1;
@@ -144,17 +147,19 @@ void TimelineView::drawItemsToPainter(QPainter *p, int fromIndex, int toIndex)
m_rowLastX[rowNumber] = x+width;
// special: animations
- if (eventType == 0 && m_eventList->getAnimationCount(i) >= 0) {
- double scale = m_eventList->getMaximumAnimationCount() - m_eventList->getMinimumAnimationCount();
+ if (eventType == 0 && m_profilerDataModel->getAnimationCount(i) >= 0) {
+ double scale = m_profilerDataModel->getMaximumAnimationCount() -
+ m_profilerDataModel->getMinimumAnimationCount();
double fraction;
if (scale > 1)
- fraction = (double)(m_eventList->getAnimationCount(i) - m_eventList->getMinimumAnimationCount()) / scale;
+ fraction = (double)(m_profilerDataModel->getAnimationCount(i) -
+ m_profilerDataModel->getMinimumAnimationCount()) / scale;
else
fraction = 1.0;
height = DefaultRowHeight * (fraction * 0.85 + 0.15);
y += DefaultRowHeight - height;
- double fpsFraction = m_eventList->getFramerate(i) / 60.0;
+ double fpsFraction = m_profilerDataModel->getFramerate(i) / 60.0;
if (fpsFraction > 1.0)
fpsFraction = 1.0;
p->setBrush(QColor::fromHsl((fpsFraction*96)+10, 76, 166));
@@ -167,12 +172,12 @@ void TimelineView::drawItemsToPainter(QPainter *p, int fromIndex, int toIndex)
}
}
-void TimelineView::drawSelectionBoxes(QPainter *p, int fromIndex, int toIndex)
+void TimelineRenderer::drawSelectionBoxes(QPainter *p, int fromIndex, int toIndex)
{
if (m_selectedItem == -1)
return;
- int id = m_eventList->getEventId(m_selectedItem);
+ int id = m_profilerDataModel->getEventId(m_selectedItem);
p->setBrush(Qt::transparent);
QColor selectionColor = Qt::blue;
@@ -188,17 +193,19 @@ void TimelineView::drawSelectionBoxes(QPainter *p, int fromIndex, int toIndex)
QRect selectedItemRect(0,0,0,0);
for (int i = fromIndex; i <= toIndex; i++) {
- if (m_eventList->getEventId(i) != id)
+ if (m_profilerDataModel->getEventId(i) != id)
continue;
- x = (m_eventList->getStartTime(i) - m_startTime) * m_spacing;
- eventType = m_eventList->getType(i);
+ x = (m_profilerDataModel->getStartTime(i) - m_startTime) * m_spacing;
+ eventType = m_profilerDataModel->getType(i);
if (m_rowsExpanded[eventType])
- y = m_rowStarts[eventType] + DefaultRowHeight*(m_eventList->eventPosInType(i) + 1);
+ y = m_rowStarts[eventType] + DefaultRowHeight *
+ (m_profilerDataModel->eventPosInType(i) + 1);
else
- y = m_rowStarts[eventType] + DefaultRowHeight*m_eventList->getNestingLevel(i);
+ y = m_rowStarts[eventType] + DefaultRowHeight *
+ m_profilerDataModel->getNestingLevel(i);
- width = m_eventList->getDuration(i)*m_spacing;
+ width = m_profilerDataModel->getDuration(i)*m_spacing;
if (width<1)
width = 1;
@@ -215,7 +222,7 @@ void TimelineView::drawSelectionBoxes(QPainter *p, int fromIndex, int toIndex)
}
}
-void TimelineView::drawBindingLoopMarkers(QPainter *p, int fromIndex, int toIndex)
+void TimelineRenderer::drawBindingLoopMarkers(QPainter *p, int fromIndex, int toIndex)
{
int destindex;
int xfrom, xto, eventType;
@@ -228,35 +235,38 @@ void TimelineView::drawBindingLoopMarkers(QPainter *p, int fromIndex, int toInde
p->save();
for (int i = fromIndex; i <= toIndex; i++) {
- destindex = m_eventList->getBindingLoopDest(i);
+ destindex = m_profilerDataModel->getBindingLoopDest(i);
if (destindex >= 0) {
// from
- xfrom = (m_eventList->getStartTime(i) + m_eventList->getDuration(i)/2 -
+ xfrom = (m_profilerDataModel->getStartTime(i) +
+ m_profilerDataModel->getDuration(i)/2 -
m_startTime) * m_spacing;
- eventType = m_eventList->getType(i);
+ eventType = m_profilerDataModel->getType(i);
if (m_rowsExpanded[eventType])
yfrom = m_rowStarts[eventType] + DefaultRowHeight*
- (m_eventList->eventPosInType(i) + 1);
+ (m_profilerDataModel->eventPosInType(i) + 1);
else
- yfrom = m_rowStarts[eventType] + DefaultRowHeight*m_eventList->getNestingLevel(i);
+ yfrom = m_rowStarts[eventType] + DefaultRowHeight *
+ m_profilerDataModel->getNestingLevel(i);
yfrom += DefaultRowHeight / 2;
// to
- xto = (m_eventList->getStartTime(destindex) + m_eventList->getDuration(destindex)/2 -
+ xto = (m_profilerDataModel->getStartTime(destindex) +
+ m_profilerDataModel->getDuration(destindex)/2 -
m_startTime) * m_spacing;
- eventType = m_eventList->getType(destindex);
+ eventType = m_profilerDataModel->getType(destindex);
if (m_rowsExpanded[eventType])
- yto = m_rowStarts[eventType] + DefaultRowHeight*
- (m_eventList->eventPosInType(destindex) + 1);
+ yto = m_rowStarts[eventType] + DefaultRowHeight *
+ (m_profilerDataModel->eventPosInType(destindex) + 1);
else
yto = m_rowStarts[eventType] + DefaultRowHeight *
- m_eventList->getNestingLevel(destindex);
+ m_profilerDataModel->getNestingLevel(destindex);
yto += DefaultRowHeight / 2;
// radius
- int eventWidth = m_eventList->getDuration(i) * m_spacing;
+ int eventWidth = m_profilerDataModel->getDuration(i) * m_spacing;
radius = 5;
if (radius * 2 > eventWidth)
radius = eventWidth / 2;
@@ -283,7 +293,7 @@ void TimelineView::drawBindingLoopMarkers(QPainter *p, int fromIndex, int toInde
p->restore();
}
-void TimelineView::mousePressEvent(QGraphicsSceneMouseEvent *event)
+void TimelineRenderer::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
// special case: if there is a drag area below me, don't accept the
// events unless I'm actually clicking inside an item
@@ -294,19 +304,19 @@ void TimelineView::mousePressEvent(QGraphicsSceneMouseEvent *event)
}
-void TimelineView::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
+void TimelineRenderer::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
Q_UNUSED(event);
manageClicked();
}
-void TimelineView::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
+void TimelineRenderer::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
event->setAccepted(false);
}
-void TimelineView::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
+void TimelineRenderer::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
{
Q_UNUSED(event);
manageHovered(event->pos().x(), event->pos().y());
@@ -314,7 +324,7 @@ void TimelineView::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
event->setAccepted(false);
}
-void TimelineView::manageClicked()
+void TimelineRenderer::manageClicked()
{
if (m_currentSelection.eventIndex != -1) {
if (m_currentSelection.eventIndex == m_selectedItem)
@@ -328,7 +338,7 @@ void TimelineView::manageClicked()
setSelectedItem(m_currentSelection.eventIndex);
}
-void TimelineView::manageHovered(int x, int y)
+void TimelineRenderer::manageHovered(int x, int y)
{
if (m_endTime - m_startTime <=0 || m_lastEndTime - m_lastStartTime <= 0)
return;
@@ -337,13 +347,16 @@ void TimelineView::manageHovered(int x, int y)
int row = y / DefaultRowHeight;
// already covered? nothing to do
- if (m_currentSelection.eventIndex != -1 && time >= m_currentSelection.startTime && time <= m_currentSelection.endTime && row == m_currentSelection.row) {
+ if (m_currentSelection.eventIndex != -1 &&
+ time >= m_currentSelection.startTime &&
+ time <= m_currentSelection.endTime &&
+ row == m_currentSelection.row) {
return;
}
// find if there's items in the time range
- int eventFrom = m_eventList->findFirstIndex(time);
- int eventTo = m_eventList->findLastIndex(time);
+ int eventFrom = m_profilerDataModel->findFirstIndex(time);
+ int eventTo = m_profilerDataModel->findLastIndex(time);
if (eventTo < eventFrom) {
m_currentSelection.eventIndex = -1;
return;
@@ -352,19 +365,21 @@ void TimelineView::manageHovered(int x, int y)
// find if we are in the right column
int itemRow, eventType;
for (int i=eventTo; i>=eventFrom; --i) {
- if (ceil(m_eventList->getEndTime(i)*m_spacing) < floor(time*m_spacing))
+ if (ceil(m_profilerDataModel->getEndTime(i)*m_spacing) < floor(time*m_spacing))
continue;
- eventType = m_eventList->getType(i);
+ eventType = m_profilerDataModel->getType(i);
if (m_rowsExpanded[eventType])
- itemRow = m_rowStarts[eventType]/DefaultRowHeight + m_eventList->eventPosInType(i) + 1;
+ itemRow = m_rowStarts[eventType]/DefaultRowHeight +
+ m_profilerDataModel->eventPosInType(i) + 1;
else
- itemRow = m_rowStarts[eventType]/DefaultRowHeight + m_eventList->getNestingLevel(i);
+ itemRow = m_rowStarts[eventType]/DefaultRowHeight +
+ m_profilerDataModel->getNestingLevel(i);
if (itemRow == row) {
// match
m_currentSelection.eventIndex = i;
- m_currentSelection.startTime = m_eventList->getStartTime(i);
- m_currentSelection.endTime = m_eventList->getEndTime(i);
+ m_currentSelection.startTime = m_profilerDataModel->getStartTime(i);
+ m_currentSelection.endTime = m_profilerDataModel->getEndTime(i);
m_currentSelection.row = row;
if (!m_selectionLocked)
setSelectedItem(i);
@@ -376,7 +391,7 @@ void TimelineView::manageHovered(int x, int y)
return;
}
-void TimelineView::clearData()
+void TimelineRenderer::clearData()
{
m_startTime = 0;
m_endTime = 0;
@@ -390,122 +405,125 @@ void TimelineView::clearData()
m_selectionLocked = true;
}
-qint64 TimelineView::getDuration(int index) const
+qint64 TimelineRenderer::getDuration(int index) const
{
- Q_ASSERT(m_eventList);
- return m_eventList->getEndTime(index) - m_eventList->getStartTime(index);
+ Q_ASSERT(m_profilerDataModel);
+ return m_profilerDataModel->getEndTime(index) -
+ m_profilerDataModel->getStartTime(index);
}
-QString TimelineView::getFilename(int index) const
+QString TimelineRenderer::getFilename(int index) const
{
- Q_ASSERT(m_eventList);
- return m_eventList->getFilename(index);
+ Q_ASSERT(m_profilerDataModel);
+ return m_profilerDataModel->getFilename(index);
}
-int TimelineView::getLine(int index) const
+int TimelineRenderer::getLine(int index) const
{
- Q_ASSERT(m_eventList);
- return m_eventList->getLine(index);
+ Q_ASSERT(m_profilerDataModel);
+ return m_profilerDataModel->getLine(index);
}
-QString TimelineView::getDetails(int index) const
+QString TimelineRenderer::getDetails(int index) const
{
- Q_ASSERT(m_eventList);
- return m_eventList->getDetails(index);
+ Q_ASSERT(m_profilerDataModel);
+ return m_profilerDataModel->getDetails(index);
}
-int TimelineView::getYPosition(int index) const
+int TimelineRenderer::getYPosition(int index) const
{
- Q_ASSERT(m_eventList);
- if (index >= m_eventList->count() || m_rowStarts.isEmpty())
+ Q_ASSERT(m_profilerDataModel);
+ if (index >= m_profilerDataModel->count() || m_rowStarts.isEmpty())
return 0;
- int y, eventType = m_eventList->getType(index);
+ int y, eventType = m_profilerDataModel->getType(index);
if (m_rowsExpanded[eventType])
- y = m_rowStarts[eventType] + DefaultRowHeight*(m_eventList->eventPosInType(index) + 1);
+ y = m_rowStarts[eventType] + DefaultRowHeight *
+ (m_profilerDataModel->eventPosInType(index) + 1);
else
- y = m_rowStarts[eventType] + DefaultRowHeight*m_eventList->getNestingLevel(index);
+ y = m_rowStarts[eventType] + DefaultRowHeight *
+ m_profilerDataModel->getNestingLevel(index);
return y;
}
-void TimelineView::setRowExpanded(int rowIndex, bool expanded)
+void TimelineRenderer::setRowExpanded(int rowIndex, bool expanded)
{
m_rowsExpanded[rowIndex] = expanded;
update();
}
-void TimelineView::selectNext()
+void TimelineRenderer::selectNext()
{
- if (m_eventList->count() == 0)
+ if (m_profilerDataModel->count() == 0)
return;
// select next in view or after
int newIndex = m_selectedItem+1;
- if (newIndex >= m_eventList->count())
+ if (newIndex >= m_profilerDataModel->count())
newIndex = 0;
- if (m_eventList->getEndTime(newIndex) < m_startTime)
- newIndex = m_eventList->findFirstIndexNoParents(m_startTime);
+ if (m_profilerDataModel->getEndTime(newIndex) < m_startTime)
+ newIndex = m_profilerDataModel->findFirstIndexNoParents(m_startTime);
setSelectedItem(newIndex);
}
-void TimelineView::selectPrev()
+void TimelineRenderer::selectPrev()
{
- if (m_eventList->count() == 0)
+ if (m_profilerDataModel->count() == 0)
return;
// select last in view or before
int newIndex = m_selectedItem-1;
if (newIndex < 0)
- newIndex = m_eventList->count()-1;
- if (m_eventList->getStartTime(newIndex) > m_endTime)
- newIndex = m_eventList->findLastIndex(m_endTime);
+ newIndex = m_profilerDataModel->count()-1;
+ if (m_profilerDataModel->getStartTime(newIndex) > m_endTime)
+ newIndex = m_profilerDataModel->findLastIndex(m_endTime);
setSelectedItem(newIndex);
}
-int TimelineView::nextItemFromId(int eventId) const
+int TimelineRenderer::nextItemFromId(int eventId) const
{
int ndx = -1;
if (m_selectedItem == -1)
- ndx = m_eventList->findFirstIndexNoParents(m_startTime);
+ ndx = m_profilerDataModel->findFirstIndexNoParents(m_startTime);
else
ndx = m_selectedItem + 1;
- if (ndx >= m_eventList->count())
+ if (ndx >= m_profilerDataModel->count())
ndx = 0;
int startIndex = ndx;
do {
- if (m_eventList->getEventId(ndx) == eventId)
+ if (m_profilerDataModel->getEventId(ndx) == eventId)
return ndx;
- ndx = (ndx + 1) % m_eventList->count();
+ ndx = (ndx + 1) % m_profilerDataModel->count();
} while (ndx != startIndex);
return -1;
}
-int TimelineView::prevItemFromId(int eventId) const
+int TimelineRenderer::prevItemFromId(int eventId) const
{
int ndx = -1;
if (m_selectedItem == -1)
- ndx = m_eventList->findFirstIndexNoParents(m_startTime);
+ ndx = m_profilerDataModel->findFirstIndexNoParents(m_startTime);
else
ndx = m_selectedItem - 1;
if (ndx < 0)
- ndx = m_eventList->count() - 1;
+ ndx = m_profilerDataModel->count() - 1;
int startIndex = ndx;
do {
- if (m_eventList->getEventId(ndx) == eventId)
+ if (m_profilerDataModel->getEventId(ndx) == eventId)
return ndx;
if (--ndx < 0)
- ndx = m_eventList->count()-1;
+ ndx = m_profilerDataModel->count()-1;
} while (ndx != startIndex);
return -1;
}
-void TimelineView::selectNextFromId(int eventId)
+void TimelineRenderer::selectNextFromId(int eventId)
{
int eventIndex = nextItemFromId(eventId);
if (eventIndex != -1)
setSelectedItem(eventIndex);
}
-void TimelineView::selectPrevFromId(int eventId)
+void TimelineRenderer::selectPrevFromId(int eventId)
{
int eventIndex = prevItemFromId(eventId);
if (eventIndex != -1)
diff --git a/src/plugins/qmlprofiler/timelineview.h b/src/plugins/qmlprofiler/timelinerenderer.h
index 357662ea3e..6b776b4c04 100644
--- a/src/plugins/qmlprofiler/timelineview.h
+++ b/src/plugins/qmlprofiler/timelinerenderer.h
@@ -30,29 +30,29 @@
**
**************************************************************************/
-#ifndef TIMELINEVIEW_H
-#define TIMELINEVIEW_H
+#ifndef TIMELINERENDERER_H
+#define TIMELINERENDERER_H
#include <QDeclarativeItem>
#include <QScriptValue>
-#include <qmljsdebugclient/qmlprofilereventlist.h>
+#include "qmlprofilerdatamodel.h"
namespace QmlProfiler {
namespace Internal {
-class TimelineView : public QDeclarativeItem
+class TimelineRenderer : public QDeclarativeItem
{
Q_OBJECT
Q_PROPERTY(qint64 startTime READ startTime WRITE setStartTime NOTIFY startTimeChanged)
Q_PROPERTY(qint64 endTime READ endTime WRITE setEndTime NOTIFY endTimeChanged)
- Q_PROPERTY(QObject* eventList READ eventList WRITE setEventList NOTIFY eventListChanged)
+ Q_PROPERTY(QObject* profilerDataModel READ profilerDataModel WRITE setProfilerDataModel NOTIFY profilerDataModelChanged)
Q_PROPERTY(bool selectionLocked READ selectionLocked WRITE setSelectionLocked NOTIFY selectionLockedChanged)
Q_PROPERTY(int selectedItem READ selectedItem WRITE setSelectedItem NOTIFY selectedItemChanged)
Q_PROPERTY(int startDragArea READ startDragArea WRITE setStartDragArea NOTIFY startDragAreaChanged)
Q_PROPERTY(int endDragArea READ endDragArea WRITE setEndDragArea NOTIFY endDragAreaChanged)
public:
- explicit TimelineView(QDeclarativeItem *parent = 0);
+ explicit TimelineRenderer(QDeclarativeItem *parent = 0);
qint64 startTime() const
{
@@ -84,11 +84,11 @@ public:
return m_endDragArea;
}
- QmlJsDebugClient::QmlProfilerEventList *eventList() const { return m_eventList; }
- void setEventList(QObject *eventList)
+ QmlProfilerDataModel *profilerDataModel() const { return m_profilerDataModel; }
+ void setProfilerDataModel(QObject *profilerDataModel)
{
- m_eventList = qobject_cast<QmlJsDebugClient::QmlProfilerEventList *>(eventList);
- emit eventListChanged(m_eventList);
+ m_profilerDataModel = qobject_cast<QmlProfilerDataModel *>(profilerDataModel);
+ emit profilerDataModelChanged(m_profilerDataModel);
}
Q_INVOKABLE qint64 getDuration(int index) const;
@@ -109,7 +109,7 @@ public:
signals:
void startTimeChanged(qint64 arg);
void endTimeChanged(qint64 arg);
- void eventListChanged(QmlJsDebugClient::QmlProfilerEventList *list);
+ void profilerDataModelChanged(QmlProfilerDataModel *list);
void selectionLockedChanged(bool locked);
void selectedItemChanged(int itemIndex);
void startDragAreaChanged(int startDragArea);
@@ -195,7 +195,7 @@ private:
qint64 m_lastStartTime;
qint64 m_lastEndTime;
- QmlJsDebugClient::QmlProfilerEventList *m_eventList;
+ QmlProfilerDataModel *m_profilerDataModel;
QList<int> m_rowLastX;
QList<int> m_rowStarts;
@@ -218,6 +218,6 @@ private:
} // namespace Internal
} // namespace QmlProfiler
-QML_DECLARE_TYPE(QmlProfiler::Internal::TimelineView)
+QML_DECLARE_TYPE(QmlProfiler::Internal::TimelineRenderer)
-#endif // TIMELINEVIEW_H
+#endif // TIMELINERENDERER_H
diff --git a/src/plugins/qmlprofiler/tracewindow.cpp b/src/plugins/qmlprofiler/tracewindow.cpp
deleted file mode 100644
index 6a4f3817fb..0000000000
--- a/src/plugins/qmlprofiler/tracewindow.cpp
+++ /dev/null
@@ -1,649 +0,0 @@
-/**************************************************************************
-**
-** This file is part of Qt Creator
-**
-** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
-**
-** Contact: Nokia Corporation (qt-info@nokia.com)
-**
-**
-** 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 qt-info@nokia.com.
-**
-**************************************************************************/
-
-#include "tracewindow.h"
-
-#include "qmlprofilerplugin.h"
-
-#include <qmljsdebugclient/qmlprofilereventlist.h>
-#include <qmljsdebugclient/qmlprofilertraceclient.h>
-#include <utils/styledbar.h>
-
-#include <QDeclarativeView>
-#include <QDeclarativeContext>
-#include <QVBoxLayout>
-#include <QGraphicsObject>
-#include <QContextMenuEvent>
-#include <QScrollBar>
-#include <QSlider>
-#include <QWidget>
-
-#include <math.h>
-
-using namespace QmlJsDebugClient;
-
-namespace QmlProfiler {
-namespace Internal {
-
-const int sliderTicks = 10000;
-const qreal sliderExp = 3;
-
-void ZoomControl::setRange(qint64 startTime, qint64 endTime)
-{
- if (m_startTime != startTime || m_endTime != endTime) {
- m_startTime = startTime;
- m_endTime = endTime;
- emit rangeChanged();
- }
-}
-
-ScrollableDeclarativeView::ScrollableDeclarativeView(QWidget *parent)
- : QDeclarativeView(parent)
-{
-}
-
-ScrollableDeclarativeView::~ScrollableDeclarativeView()
-{
-}
-
-void ScrollableDeclarativeView::scrollContentsBy(int dx, int dy)
-{
- // special workaround to track the scrollbar
- if (rootObject()) {
- int scrollY = rootObject()->property("scrollY").toInt();
- rootObject()->setProperty("scrollY", QVariant(scrollY - dy));
- }
- QDeclarativeView::scrollContentsBy(dx,dy);
-}
-
-TraceWindow::TraceWindow(QWidget *parent)
- : QWidget(parent)
-{
- setObjectName("QML Profiler");
-
- m_zoomControl = new ZoomControl(this);
- connect(m_zoomControl.data(), SIGNAL(rangeChanged()), this, SLOT(updateRange()));
-
- QVBoxLayout *groupLayout = new QVBoxLayout;
- groupLayout->setContentsMargins(0, 0, 0, 0);
- groupLayout->setSpacing(0);
-
- m_mainView = new ScrollableDeclarativeView(this);
- m_mainView->setResizeMode(QDeclarativeView::SizeViewToRootObject);
- m_mainView->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
- m_mainView->setBackgroundBrush(QBrush(Qt::white));
- m_mainView->setAlignment(Qt::AlignLeft | Qt::AlignTop);
- m_mainView->setFocus();
-
- MouseWheelResizer *resizer = new MouseWheelResizer(this);
- connect(resizer,SIGNAL(mouseWheelMoved(int,int,int)), this, SLOT(mouseWheelMoved(int,int,int)));
- m_mainView->viewport()->installEventFilter(resizer);
-
- QHBoxLayout *toolsLayout = new QHBoxLayout;
-
- m_timebar = new QDeclarativeView(this);
- m_timebar->setResizeMode(QDeclarativeView::SizeRootObjectToView);
- m_timebar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
- m_timebar->setFixedHeight(24);
-
- m_overview = new QDeclarativeView(this);
- m_overview->setResizeMode(QDeclarativeView::SizeRootObjectToView);
- m_overview->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
- m_overview->setMaximumHeight(50);
-
- m_zoomToolbar = createZoomToolbar();
- m_zoomToolbar->move(0, m_timebar->height());
- m_zoomToolbar->setVisible(false);
-
- toolsLayout->addWidget(createToolbar());
- toolsLayout->addWidget(m_timebar);
-
- groupLayout->addLayout(toolsLayout);
- groupLayout->addWidget(m_mainView);
- groupLayout->addWidget(m_overview);
-
- setLayout(groupLayout);
-
- m_eventList = new QmlProfilerEventList(this);
- connect(this,SIGNAL(range(int,qint64,qint64,QStringList,QmlJsDebugClient::QmlEventLocation)), m_eventList, SLOT(addRangedEvent(int,qint64,qint64,QStringList,QmlJsDebugClient::QmlEventLocation)));
- connect(this, SIGNAL(traceFinished(qint64)), m_eventList, SLOT(setTraceEndTime(qint64)));
- connect(this, SIGNAL(traceStarted(qint64)), m_eventList, SLOT(setTraceStartTime(qint64)));
- connect(this, SIGNAL(frameEvent(qint64,int,int)), m_eventList, SLOT(addFrameEvent(qint64,int,int)));
- connect(m_eventList, SIGNAL(stateChanged()), this, SLOT(eventListStateChanged()));
- m_mainView->rootContext()->setContextProperty("qmlEventList", m_eventList);
- m_overview->rootContext()->setContextProperty("qmlEventList", m_eventList);
-
- m_rewriter = new QmlProfilerDetailsRewriter(this);
- connect(m_eventList, SIGNAL(requestDetailsForLocation(int,QmlJsDebugClient::QmlEventLocation)), m_rewriter, SLOT(requestDetailsForLocation(int,QmlJsDebugClient::QmlEventLocation)));
- connect(m_rewriter, SIGNAL(rewriteDetailsString(int,QmlJsDebugClient::QmlEventLocation,QString)), m_eventList, SLOT(rewriteDetailsString(int,QmlJsDebugClient::QmlEventLocation,QString)));
- connect(m_rewriter, SIGNAL(eventDetailsChanged()), m_eventList, SLOT(finishedRewritingDetails()));
- connect(m_eventList, SIGNAL(reloadDocumentsForDetails()), m_rewriter, SLOT(reloadDocuments()));
-
- 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);
- m_currentZoomLevel = 0;
- m_profiledTime = 0;
-}
-
-TraceWindow::~TraceWindow()
-{
- disconnectClientSignals();
- delete m_plugin.data();
- delete m_v8plugin.data();
-}
-
-QWidget *TraceWindow::createToolbar()
-{
- Utils::StyledBar *bar = new Utils::StyledBar(this);
- bar->setSingleRow(true);
- bar->setFixedWidth(150);
- bar->setFixedHeight(24);
-
- QHBoxLayout *toolBarLayout = new QHBoxLayout(bar);
- toolBarLayout->setMargin(0);
- toolBarLayout->setSpacing(0);
-
- QToolButton *buttonPrev= new QToolButton;
- buttonPrev->setIcon(QIcon(":/qmlprofiler/ico_prev.png"));
- buttonPrev->setToolTip(tr("Jump to previous event"));
- connect(buttonPrev, SIGNAL(clicked()), this, SIGNAL(jumpToPrev()));
- connect(this, SIGNAL(enableToolbar(bool)), buttonPrev, SLOT(setEnabled(bool)));
-
- QToolButton *buttonNext= new QToolButton;
- buttonNext->setIcon(QIcon(":/qmlprofiler/ico_next.png"));
- buttonNext->setToolTip(tr("Jump to next event"));
- connect(buttonNext, SIGNAL(clicked()), this, SIGNAL(jumpToNext()));
- connect(this, SIGNAL(enableToolbar(bool)), buttonNext, SLOT(setEnabled(bool)));
-
- QToolButton *buttonZoomControls = new QToolButton;
- buttonZoomControls->setIcon(QIcon(":/qmlprofiler/ico_zoom.png"));
- buttonZoomControls->setToolTip(tr("Show zoom slider"));
- buttonZoomControls->setCheckable(true);
- buttonZoomControls->setChecked(false);
- connect(buttonZoomControls, SIGNAL(toggled(bool)), m_zoomToolbar, SLOT(setVisible(bool)));
- connect(this, SIGNAL(enableToolbar(bool)), buttonZoomControls, SLOT(setEnabled(bool)));
-
- m_buttonRange = new QToolButton;
- m_buttonRange->setIcon(QIcon(":/qmlprofiler/ico_rangeselection.png"));
- m_buttonRange->setToolTip(tr("Select range"));
- m_buttonRange->setCheckable(true);
- m_buttonRange->setChecked(false);
- connect(m_buttonRange, SIGNAL(clicked(bool)), this, SLOT(toggleRangeMode(bool)));
- connect(this, SIGNAL(enableToolbar(bool)), m_buttonRange, SLOT(setEnabled(bool)));
- connect(this, SIGNAL(rangeModeChanged(bool)), m_buttonRange, SLOT(setChecked(bool)));
-
- m_buttonLock = new QToolButton;
- m_buttonLock->setIcon(QIcon(":/qmlprofiler/ico_selectionmode.png"));
- m_buttonLock->setToolTip(tr("View event information on mouseover"));
- m_buttonLock->setCheckable(true);
- m_buttonLock->setChecked(false);
- connect(m_buttonLock, SIGNAL(clicked(bool)), this, SLOT(toggleLockMode(bool)));
- connect(this, SIGNAL(enableToolbar(bool)), m_buttonLock, SLOT(setEnabled(bool)));
- connect(this, SIGNAL(lockModeChanged(bool)), m_buttonLock, SLOT(setChecked(bool)));
-
- toolBarLayout->addWidget(buttonPrev);
- toolBarLayout->addWidget(buttonNext);
- toolBarLayout->addWidget(new Utils::StyledSeparator());
- toolBarLayout->addWidget(buttonZoomControls);
- toolBarLayout->addWidget(new Utils::StyledSeparator());
- toolBarLayout->addWidget(m_buttonRange);
- toolBarLayout->addWidget(m_buttonLock);
-
- return bar;
-}
-
-
-QWidget *TraceWindow::createZoomToolbar()
-{
- Utils::StyledBar *bar = new Utils::StyledBar(this);
- bar->setSingleRow(true);
- bar->setFixedWidth(150);
- bar->setFixedHeight(24);
-
- QHBoxLayout *toolBarLayout = new QHBoxLayout(bar);
- toolBarLayout->setMargin(0);
- toolBarLayout->setSpacing(0);
-
- QSlider *zoomSlider = new QSlider(Qt::Horizontal);
- zoomSlider->setFocusPolicy(Qt::NoFocus);
- zoomSlider->setRange(1, sliderTicks);
- zoomSlider->setInvertedAppearance(true);
- zoomSlider->setPageStep(sliderTicks/100);
- connect(this, SIGNAL(enableToolbar(bool)), zoomSlider, SLOT(setEnabled(bool)));
- connect(zoomSlider, SIGNAL(valueChanged(int)), this, SLOT(setZoomLevel(int)));
- connect(this, SIGNAL(zoomLevelChanged(int)), zoomSlider, SLOT(setValue(int)));
- zoomSlider->setStyleSheet("\
- QSlider:horizontal {\
- background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #444444, stop: 1 #5a5a5a);\
- border: 1px #313131;\
- height: 20px;\
- margin: 0px 0px 0px 0px;\
- }\
- QSlider::add-page:horizontal {\
- background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #5a5a5a, stop: 1 #444444);\
- border: 1px #313131;\
- }\
- QSlider::sub-page:horizontal {\
- background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #5a5a5a, stop: 1 #444444);\
- border: 1px #313131;\
- }\
- ");
-
- toolBarLayout->addWidget(zoomSlider);
-
- return bar;
-}
-
-void TraceWindow::reset(QDeclarativeDebugConnection *conn)
-{
- disconnectClientSignals();
-
- delete m_plugin.data();
- m_plugin = new QmlProfilerTraceClient(conn);
- delete m_v8plugin.data();
- m_v8plugin = new QV8ProfilerClient(conn);
-
- connectClientSignals();
-
- m_mainView->rootContext()->setContextProperty("connection", m_plugin.data());
- m_mainView->rootContext()->setContextProperty("zoomControl", m_zoomControl.data());
- m_timebar->rootContext()->setContextProperty("zoomControl", m_zoomControl.data());
- m_overview->rootContext()->setContextProperty("zoomControl", m_zoomControl.data());
-
- m_timebar->setSource(QUrl("qrc:/qmlprofiler/TimeDisplay.qml"));
- m_overview->setSource(QUrl("qrc:/qmlprofiler/Overview.qml"));
-
- m_mainView->setSource(QUrl("qrc:/qmlprofiler/MainView.qml"));
- m_mainView->rootObject()->setProperty("width", QVariant(width()));
- m_mainView->rootObject()->setProperty("candidateHeight", QVariant(height() - m_timebar->height() - m_overview->height()));
-
- updateToolbar();
-
- connect(m_mainView->rootObject(), SIGNAL(updateCursorPosition()), this, SLOT(updateCursorPosition()));
- connect(m_mainView->rootObject(), SIGNAL(updateTimer()), this, SLOT(updateTimer()));
- connect(m_mainView->rootObject(), SIGNAL(updateRangeButton()), this, SLOT(updateRangeButton()));
- connect(m_mainView->rootObject(), SIGNAL(updateLockButton()), this, SLOT(updateLockButton()));
- connect(m_eventList, SIGNAL(countChanged()), this, SLOT(updateToolbar()));
- connect(this, SIGNAL(jumpToPrev()), m_mainView->rootObject(), SLOT(prevEvent()));
- connect(this, SIGNAL(jumpToNext()), m_mainView->rootObject(), SLOT(nextEvent()));
- connect(this, SIGNAL(updateViewZoom(QVariant)), m_mainView->rootObject(), SLOT(updateWindowLength(QVariant)));
- connect(this, SIGNAL(wheelZoom(QVariant,QVariant)), m_mainView->rootObject(), SLOT(wheelZoom(QVariant,QVariant)));
- connect(this, SIGNAL(globalZoom()), m_mainView->rootObject(), SLOT(globalZoom()));
- connect(this, SIGNAL(selectNextEventInDisplay(QVariant)), m_mainView->rootObject(), SLOT(selectNextWithId(QVariant)));
- connect(m_mainView->rootObject(), SIGNAL(selectedEventIdChanged(int)), this, SIGNAL(selectedEventIdChanged(int)));
- connect(m_mainView->rootObject(), SIGNAL(changeToolTip(QString)), this, SLOT(updateToolTip(QString)));
- connect(m_mainView->rootObject(), SIGNAL(updateVerticalScroll(int)), this, SLOT(updateVerticalScroll(int)));
-
- connect(this, SIGNAL(internalClearDisplay()), m_mainView->rootObject(), SLOT(clearAll()));
- connect(this,SIGNAL(internalClearDisplay()), m_overview->rootObject(), SLOT(clearDisplay()));
-
- m_v8DataReady = false;
- m_qmlDataReady = false;
-}
-
-void TraceWindow::connectClientSignals()
-{
- if (m_plugin) {
- connect(m_plugin.data(), SIGNAL(complete()), this, SLOT(qmlComplete()));
- connect(m_plugin.data(), SIGNAL(range(int,qint64,qint64,QStringList,QmlJsDebugClient::QmlEventLocation)),
- this, SIGNAL(range(int,qint64,qint64,QStringList,QmlJsDebugClient::QmlEventLocation)));
- connect(m_plugin.data(), SIGNAL(traceFinished(qint64)), this, SIGNAL(traceFinished(qint64)));
- connect(m_plugin.data(), SIGNAL(traceStarted(qint64)), this, SLOT(manageTraceStart(qint64)));
- connect(m_plugin.data(), SIGNAL(frame(qint64,int,int)), this, SIGNAL(frameEvent(qint64,int,int)));
- connect(m_plugin.data(), SIGNAL(enabledChanged()), this, SLOT(updateProfilerState()));
- connect(m_plugin.data(), SIGNAL(enabledChanged()), m_plugin.data(), SLOT(sendRecordingStatus()));
- connect(m_plugin.data(), SIGNAL(recordingChanged(bool)), this, SIGNAL(recordingChanged(bool)));
- }
- if (m_v8plugin) {
- 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)));
- connect(m_v8plugin.data(), SIGNAL(enabledChanged()), this, SLOT(updateProfilerState()));
- connect(m_v8plugin.data(), SIGNAL(enabledChanged()), m_v8plugin.data(), SLOT(sendRecordingStatus()));
- }
-}
-
-void TraceWindow::disconnectClientSignals()
-{
- if (m_plugin) {
- disconnect(m_plugin.data(), SIGNAL(complete()), this, SLOT(qmlComplete()));
- disconnect(m_plugin.data(), SIGNAL(range(int,qint64,qint64,QStringList,QmlJsDebugClient::QmlEventLocation)),
- this, SIGNAL(range(int,qint64,qint64,QStringList,QmlJsDebugClient::QmlEventLocation)));
- disconnect(m_plugin.data(), SIGNAL(traceFinished(qint64)), this, SIGNAL(traceFinished(qint64)));
- disconnect(m_plugin.data(), SIGNAL(traceStarted(qint64)), this, SLOT(manageTraceStart(qint64)));
- disconnect(m_plugin.data(), SIGNAL(enabledChanged()), this, SLOT(updateProfilerState()));
- disconnect(m_plugin.data(), SIGNAL(enabledChanged()), m_plugin.data(), SLOT(sendRecordingStatus()));
- disconnect(m_plugin.data(), SIGNAL(recordingChanged(bool)), this, SIGNAL(recordingChanged(bool)));
- }
- 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)));
- disconnect(m_v8plugin.data(), SIGNAL(enabledChanged()), this, SLOT(updateProfilerState()));
- disconnect(m_v8plugin.data(), SIGNAL(enabledChanged()), m_v8plugin.data(), SLOT(sendRecordingStatus()));
- }
-}
-
-QmlProfilerEventList *TraceWindow::getEventList() const
-{
- return m_eventList;
-}
-
-ZoomControl *TraceWindow::rangeTimes() const
-{
- return m_zoomControl.data();
-}
-
-void TraceWindow::contextMenuEvent(QContextMenuEvent *ev)
-{
- emit contextMenuRequested(ev->globalPos());
-}
-
-void TraceWindow::updateCursorPosition()
-{
- emit gotoSourceLocation(m_mainView->rootObject()->property("fileName").toString(),
- m_mainView->rootObject()->property("lineNumber").toInt(),
- m_mainView->rootObject()->property("columnNumber").toInt());
-}
-
-void TraceWindow::updateTimer()
-{
- m_profiledTime = m_mainView->rootObject()->property("elapsedTime").toDouble();
-}
-
-void TraceWindow::correctTimer()
-{
- // once the data is post-processed, use the eventlist time instead of the qml timer
- m_profiledTime = (m_eventList->traceEndTime() - m_eventList->traceStartTime()) / 1.0e9;
- if (m_profiledTime < 0)
- m_profiledTime = 0;
- emit viewUpdated();
-}
-
-double TraceWindow::profiledTime() const
-{
- return m_profiledTime;
-}
-
-void TraceWindow::clearDisplay()
-{
- m_eventList->clear();
-
- if (m_plugin)
- m_plugin.data()->clearData();
- if (m_v8plugin)
- m_v8plugin.data()->clearData();
-
- m_zoomControl.data()->setRange(0,0);
- m_profiledTime = 0;
-
- updateVerticalScroll(0);
- m_mainView->rootObject()->setProperty("scrollY", QVariant(0));
-
- emit internalClearDisplay();
-}
-
-void TraceWindow::updateToolbar()
-{
- emit enableToolbar(m_eventList && m_eventList->count()>0);
-}
-
-void TraceWindow::toggleRangeMode(bool active)
-{
- bool rangeMode = m_mainView->rootObject()->property("selectionRangeMode").toBool();
- if (active != rangeMode) {
- if (active)
- m_buttonRange->setIcon(QIcon(":/qmlprofiler/ico_rangeselected.png"));
- else
- m_buttonRange->setIcon(QIcon(":/qmlprofiler/ico_rangeselection.png"));
- m_mainView->rootObject()->setProperty("selectionRangeMode", QVariant(active));
- }
-}
-
-void TraceWindow::updateRangeButton()
-{
- bool rangeMode = m_mainView->rootObject()->property("selectionRangeMode").toBool();
- if (rangeMode)
- m_buttonRange->setIcon(QIcon(":/qmlprofiler/ico_rangeselected.png"));
- else
- m_buttonRange->setIcon(QIcon(":/qmlprofiler/ico_rangeselection.png"));
- emit rangeModeChanged(rangeMode);
-}
-
-void TraceWindow::toggleLockMode(bool active)
-{
- bool lockMode = !m_mainView->rootObject()->property("selectionLocked").toBool();
- if (active != lockMode) {
- m_mainView->rootObject()->setProperty("selectionLocked", QVariant(!active));
- m_mainView->rootObject()->setProperty("selectedItem", QVariant(-1));
- }
-}
-
-void TraceWindow::updateLockButton()
-{
- bool lockMode = !m_mainView->rootObject()->property("selectionLocked").toBool();
- emit lockModeChanged(lockMode);
-}
-
-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
-{
- return m_plugin.data()->isRecording();
-}
-
-void TraceWindow::qmlComplete()
-{
- m_qmlDataReady = true;
- if (!m_v8plugin || m_v8plugin.data()->status() != QDeclarativeDebugClient::Enabled || m_v8DataReady) {
- m_eventList->complete();
- // once complete is sent, reset the flags
- m_qmlDataReady = false;
- m_v8DataReady = false;
- }
-}
-
-void TraceWindow::v8Complete()
-{
- m_v8DataReady = true;
- if (!m_plugin || m_plugin.data()->status() != QDeclarativeDebugClient::Enabled || m_qmlDataReady) {
- m_eventList->complete();
- // once complete is sent, reset the flags
- m_v8DataReady = false;
- m_qmlDataReady = false;
- }
-}
-
-void TraceWindow::resizeEvent(QResizeEvent *event)
-{
- if (m_mainView->rootObject()) {
- m_mainView->rootObject()->setProperty("width", QVariant(event->size().width()));
- int newHeight = event->size().height() - m_timebar->height() - m_overview->height();
- m_mainView->rootObject()->setProperty("candidateHeight", QVariant(newHeight));
- }
-}
-
-bool MouseWheelResizer::eventFilter(QObject *obj, QEvent *event)
-{
- if (event->type() == QEvent::Wheel) {
- QWheelEvent *ev = static_cast<QWheelEvent *>(event);
- if (ev->modifiers() & Qt::ControlModifier) {
- emit mouseWheelMoved(ev->pos().x(), ev->pos().y(), ev->delta());
- return true;
- }
- }
- return QObject::eventFilter(obj, event);
-}
-
-void TraceWindow::mouseWheelMoved(int x, int y, int delta)
-{
- Q_UNUSED(y);
- if (m_mainView->rootObject()) {
- emit wheelZoom(QVariant(x), QVariant(delta));
- }
-}
-
-void TraceWindow::viewAll()
-{
- emit globalZoom();
-}
-
-bool TraceWindow::hasValidSelection() const
-{
- if (m_mainView->rootObject()) {
- return m_mainView->rootObject()->property("selectionRangeReady").toBool();
- }
- return false;
-}
-
-qint64 TraceWindow::selectionStart() const
-{
- if (m_mainView->rootObject()) {
- return m_mainView->rootObject()->property("selectionRangeStart").toLongLong();
- }
- return 0;
-}
-
-qint64 TraceWindow::selectionEnd() const
-{
- if (m_mainView->rootObject()) {
- return m_mainView->rootObject()->property("selectionRangeEnd").toLongLong();
- }
- return 0;
-}
-
-void TraceWindow::setZoomLevel(int zoomLevel)
-{
- if (m_currentZoomLevel != zoomLevel && m_mainView->rootObject()) {
- qreal newFactor = pow(qreal(zoomLevel) / qreal(sliderTicks), sliderExp);
- m_currentZoomLevel = zoomLevel;
- emit updateViewZoom(QVariant(newFactor));
- }
-}
-
-void TraceWindow::updateRange()
-{
- if (!m_eventList)
- return;
- qreal duration = m_zoomControl.data()->endTime() - m_zoomControl.data()->startTime();
- if (duration <= 0)
- return;
- if (m_eventList->traceDuration() <= 0)
- return;
- int newLevel = pow(duration / m_eventList->traceDuration(), 1/sliderExp) * sliderTicks;
- if (m_currentZoomLevel != newLevel) {
- m_currentZoomLevel = newLevel;
- emit zoomLevelChanged(newLevel);
- }
-}
-
-void TraceWindow::selectNextEvent(int eventId)
-{
- emit selectNextEventInDisplay(QVariant(eventId));
-}
-
-void TraceWindow::updateProfilerState()
-{
- bool qmlActive = false;
- bool v8Active = false;
- if (m_plugin)
- qmlActive = m_plugin.data()->isEnabled();
- if (m_v8plugin)
- v8Active = m_v8plugin.data()->isEnabled();
-
- emit profilerStateChanged(qmlActive, v8Active);
-}
-
-void TraceWindow::updateToolTip(const QString &text)
-{
- setToolTip(text);
-}
-
-void TraceWindow::updateVerticalScroll(int newPosition)
-{
- m_mainView->verticalScrollBar()->setValue(newPosition);
-}
-
-void TraceWindow::eventListStateChanged()
-{
- switch (m_eventList->currentState()) {
- case QmlProfilerEventList::Empty :
- clearDisplay();
- break;
- case QmlProfilerEventList::AcquiringData :
- firstDataReceived();
- break;
- case QmlProfilerEventList::ProcessingData :
- // nothing to be done
- break;
- case QmlProfilerEventList::Done :
- correctTimer();
- break;
- default:
- break;
- }
-}
-
-void TraceWindow::manageTraceStart(qint64 traceStart)
-{
- // new trace started
- emit clearViewsFromTool();
-
- emit traceStarted(traceStart);
-}
-
-void TraceWindow::firstDataReceived()
-{
- if (m_plugin && m_plugin.data()->isRecording()) {
- // serverside recording disabled
- m_plugin.data()->setRecordingFromServer(false);
- }
-}
-
-void TraceWindow::applicationDied()
-{
- if (m_mainView->rootObject())
- m_mainView->rootObject()->setProperty("applicationDied",QVariant(true));
-}
-
-} // namespace Internal
-} // namespace QmlProfiler
diff --git a/src/tools/qmlprofilertool/commandlistener.cpp b/src/tools/qmlprofilertool/commandlistener.cpp
deleted file mode 100644
index 57754a6dd0..0000000000
--- a/src/tools/qmlprofilertool/commandlistener.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-/**************************************************************************
-**
-** This file is part of Qt Creator
-**
-** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
-**
-** Contact: Nokia Corporation (qt-info@nokia.com)
-**
-**
-** 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 qt-info@nokia.com.
-**
-**************************************************************************/
-
-#include "commandlistener.h"
-#include "constants.h"
-#include <QTextStream>
-
-CommandListener::CommandListener(QObject *parent)
- : QThread(parent)
- , m_stopRequested(false)
-{
-}
-
-void CommandListener::run()
-{
- QString line;
- QTextStream in(stdin, QIODevice::ReadOnly);
- do {
- line = in.readLine();
- line = line.trimmed();
- if (!line.isEmpty()) {
- emit command(line);
- if (line == QLatin1String(Constants::CMD_QUIT)
- || line == QLatin1String(Constants::CMD_QUIT2))
- return;
- }
- } while (!m_stopRequested && !line.isNull());
-}
diff --git a/src/tools/qmlprofilertool/constants.h b/src/tools/qmlprofilertool/constants.h
deleted file mode 100644
index 24d2702751..0000000000
--- a/src/tools/qmlprofilertool/constants.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/**************************************************************************
-**
-** This file is part of Qt Creator
-**
-** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
-**
-** Contact: Nokia Corporation (qt-info@nokia.com)
-**
-**
-** 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 qt-info@nokia.com.
-**
-**************************************************************************/
-
-#ifndef CONSTANTS_H
-#define CONSTANTS_H
-
-namespace Constants {
-
-const char CMD_HELP[] ="help";
-const char CMD_HELP2[] = "h";
-const char CMD_HELP3[] = "?";
-
-const char CMD_RECORD[] ="record";
-const char CMD_RECORD2[] ="r";
-
-const char CMD_QUIT[] ="quit";
-const char CMD_QUIT2[] = "q";
-
-} // Contants
-
-#endif // CONSTANTS_H
diff --git a/src/tools/qmlprofilertool/qmlprofilerapplication.h b/src/tools/qmlprofilertool/qmlprofilerapplication.h
deleted file mode 100644
index 9414895631..0000000000
--- a/src/tools/qmlprofilertool/qmlprofilerapplication.h
+++ /dev/null
@@ -1,113 +0,0 @@
-/**************************************************************************
-**
-** This file is part of Qt Creator
-**
-** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
-**
-** Contact: Nokia Corporation (qt-info@nokia.com)
-**
-**
-** 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 qt-info@nokia.com.
-**
-**************************************************************************/
-
-#ifndef QMLPROFILERAPPLICATION_H
-#define QMLPROFILERAPPLICATION_H
-
-#include <QCoreApplication>
-#include <QStringList>
-#include <QTimer>
-
-#include <qdeclarativedebugclient.h>
-#include <qmlprofilertraceclient.h>
-#include <qmlprofilereventlist.h>
-#include <qv8profilerclient.h>
-
-QT_FORWARD_DECLARE_CLASS(QProcess)
-
-class QmlProfilerApplication : public QCoreApplication
-{
- Q_OBJECT
-public:
- QmlProfilerApplication(int &argc, char **argv);
- ~QmlProfilerApplication();
-
- bool parseArguments();
- void printUsage();
- int exec();
-
-public slots:
- void userCommand(const QString &command);
-
-private slots:
- void run();
- void tryToConnect();
- void connected();
- void connectionStateChanged(QAbstractSocket::SocketState state);
- void connectionError(QAbstractSocket::SocketError error);
- void processHasOutput();
- void processFinished();
-
- void traceClientEnabled();
- void profilerClientEnabled();
- void traceFinished();
- void recordingChanged();
-
- void print(const QString &line);
- void logError(const QString &error);
- void logStatus(const QString &status);
-
- void qmlComplete();
- void v8Complete();
-
-private:
- void printCommands();
- QString traceFileName() const;
-
- enum ApplicationMode {
- LaunchMode,
- AttachMode
- } m_runMode;
-
- // LaunchMode
- QString m_programPath;
- QStringList m_programArguments;
- QProcess *m_process;
- QString m_tracePrefix;
-
- QString m_hostName;
- quint16 m_port;
- bool m_verbose;
- bool m_quitAfterSave;
-
- QmlJsDebugClient::QDeclarativeDebugConnection m_connection;
- QmlJsDebugClient::QmlProfilerTraceClient m_qmlProfilerClient;
- QmlJsDebugClient::QV8ProfilerClient m_v8profilerClient;
- QmlJsDebugClient::QmlProfilerEventList m_eventList;
- QTimer m_connectTimer;
- uint m_connectionAttempts;
-
- bool m_qmlDataReady;
- bool m_v8DataReady;
-};
-
-#endif // QMLPROFILERAPPLICATION_H
diff --git a/src/tools/qmlprofilertool/qmlprofilertool.pro b/src/tools/qmlprofilertool/qmlprofilertool.pro
deleted file mode 100644
index ca582eeb76..0000000000
--- a/src/tools/qmlprofilertool/qmlprofilertool.pro
+++ /dev/null
@@ -1,28 +0,0 @@
-QT = core
-include(../../../qtcreator.pri)
-include(../../rpath.pri)
-
-TEMPLATE = app
-TARGET = qmlprofiler
-DESTDIR = $$IDE_BIN_PATH
-
-CONFIG += console
-CONFIG -= app_bundle
-
-include(../../shared/symbianutils/symbianutils.pri)
-include(../../libs/qmljsdebugclient/qmljsdebugclient-lib.pri)
-
-INCLUDEPATH += ../../libs/qmljsdebugclient
-
-SOURCES += main.cpp \
- qmlprofilerapplication.cpp \
- commandlistener.cpp
-
-HEADERS += \
- qmlprofilerapplication.h \
- commandlistener.h \
- constants.h
-
-target.path=/bin
-INSTALLS+=target
-
diff --git a/src/tools/tools.pro b/src/tools/tools.pro
index bd5bac6c8f..b6b159f591 100644
--- a/src/tools/tools.pro
+++ b/src/tools/tools.pro
@@ -1,7 +1,6 @@
TEMPLATE = subdirs
SUBDIRS = qtpromaker \
- qmlprofilertool \
qmlpuppet
win32 {