diff --git a/src/libs/qmljsdebugclient/qmljsdebugclient-lib.pri b/src/libs/qmljsdebugclient/qmljsdebugclient-lib.pri index f15d920bf902f847f9d288a3eac7566601b367cf..69b0606451a1510fa77247982630f2c6c4e1a0be 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 c1462ecd5e7047cf68339d65dce6cd3c45e3fba9..893ef89d4841dfd9ff7a5c7bfdf4108a73dc035a 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 a975195e444398dcbc58b82fd3c0caf9633f364c..8cb51a197f737b50413874e08e1fd2a4c2c445dc 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 3576b94645fb8bec3febffd82ed1d8f2a74c2ee7..0000000000000000000000000000000000000000 --- 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 45b7df6ae6a1e0be7dac7ccaaf73ba1195beee41..cb317fd2e5bc1d4cffc611052db214bcd7ed1d3e 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 ae1c490002965848adf34f76464eeb70c8162355..02e102f039ed1adbb1c04dbf0458977582934c2d 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 cc130bba736b66abae899bfa879c71227261c5f6..01eb3f56b3ae851290367d7b985d60cc0062abe6 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 4d37c067e36209ba2f37d1ef3580f147f5f46827..3ed2071d7391c8f0696c7bf43158b96ede495bbd 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(); @@ -106,6 +105,9 @@ protected: virtual void statusChanged(Status); 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 432ed6c41c8bb02f7e1ca5a5b606b470d72b4b00..7853efcdb3d90a59b19c5dbe39ef19a839e27ddf 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 25e1328c7350f2b848f35b3c7d7f4e092b40f17a..48d191a45885c29178a603535e75cacf4eca8579 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 0d89c057abd615373c079f1f07365b84e9bcf9cf..21e322d5be59c60c2be46c161f5516731ada7482 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 9fc2e8e44ed67693679d762627f0f4c92cc3cac0..b810a78530365d0e16e1b488e75b6be2b4dfd695 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 4fe625a7fd10e7cc5e7346ae2b5e208358051386..821f3446532cc5e6ddf63bfbcf945b577527cca5 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 552650216903f1187c854df70afb0a05e8f381f1..c82277bc26d5d65bb6169d1037b3d696ef2580ea 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 616fd895986c33abf9f2112a92d1a4634856c5cc..b8b650725823718d340710fc20eb0ce2eb0caea4 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 d4507a37d63c7941e907909e8044675df3303f91..8919d07b1396c2e264b31e10781ab6630410a88d 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 2cb176c0a15fd84e4848aa927a42b590fb08e73d..d38e4de7417161ff47aef485a9b4ab0f0490ba37 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 7403b1fb68aba82b6fc49d4e23a89b2b10f301ee..4dc919bef2560e24586056856efeca308e4ddd36 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 13cbca838c652f47073b7e66e4cafb24d1241f43..f4af4e4366312d718eef27cde31b2a69210ecec9 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 0000000000000000000000000000000000000000..01a0d787a2d74d9ecd9562ea9880adeaf7a09cc0 --- /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 0000000000000000000000000000000000000000..35766e7a5a833a6992648f43d092b47e5864480f --- /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 38a47c11f10325436c1fe469e2630c250d074d3c..fec1b6b539add9a7ae8b0fe7679262d08d9fb6c1 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 0000000000000000000000000000000000000000..750bbb0f0db0b40773f14f3227282b0e6ad04914 --- /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 similarity index 64% rename from src/libs/qmljsdebugclient/qmlprofilereventlist.h rename to src/plugins/qmlprofiler/qmlprofilerdatamodel.h index 32f5203c2a0a275e39355fba6c8063fa212e9028..7e2ab0347094358e3178e5a746831cd60990538c 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 561717c890d6a42dd819c10ddb7f3e4b7054ac5c..4f61f8a64089ca41fa7830844eac82a92af3545e 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 5487711e68d7273a0599a400025c5002a49d4308..5990df7c4c09016d6ddd85ec49c8ada60ed19403 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 aade80edb731c6136c00defcbedd933a6ae18a5b..c438e69329638d31511702431d60da65f5c9f8ed 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 b59fdc4c7a2e74ffe8f7de7150dc7b0a6013097f..3630626cacb3816756570ada6380880d99050639 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 1c7c87d7f243b44ee9eb597d0646a9ecc07bfe21..409ea4dca08041d6c16d6837b3e1ee6e6a92a9f4 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 0000000000000000000000000000000000000000..194e2cdf1c6cf8acf7fceb3c40d44c7e391ed6cd --- /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 similarity index 58% rename from src/tools/qmlprofilertool/commandlistener.h rename to src/plugins/qmlprofiler/qmlprofilerstatemanager.h index 4b8df747ffe2eef48db9e447d47895745309b313..188b0b5be34bf38cc6b83c0cbdf20e81c2581f3d 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 76359684c9e3531b3bb099768845fbeeb986012a..46bf8b6085b82d69e9c939cbd63d3e805fb73596 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 32f46105f064e249b9d833d73ba359b9b78644c1..bb3f2b1cec600fb63145335d7b3718b4e18be123 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 0000000000000000000000000000000000000000..ab02f45b08ea9e8ec083d17f153ee2a1f11fb577 --- /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 similarity index 56% rename from src/plugins/qmlprofiler/tracewindow.h rename to src/plugins/qmlprofiler/qmlprofilertraceview.h index 2959720db6437df0c653caf010c1acc8c4ffb2c6..6b61380882e28df551f529bc9d6a283c38507741 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 0000000000000000000000000000000000000000..a12a6ccda5f8643a6b8a9f79aab92669c10e2f6c --- /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 similarity index 53% rename from src/tools/qmlprofilertool/main.cpp rename to src/plugins/qmlprofiler/qmlprofilerviewmanager.h index bb5c2bd32880187e3f9ce2561578c8d587b64521..4e492918eeedc1348d01240bb1d9f9b41cace3c7 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 0000000000000000000000000000000000000000..d58f198be6bbc841e048c34d61b418b431148878 --- /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 0000000000000000000000000000000000000000..6441a24702aea2db5839fe1a3672daac1ec48495 --- /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 similarity index 60% rename from src/plugins/qmlprofiler/timelineview.cpp rename to src/plugins/qmlprofiler/timelinerenderer.cpp index 1e10eb0075e8474b6c43b5d6438ee392bac0db45..a078d31f8402ee0c4995c9659f42432fac48e706 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 similarity index 87% rename from src/plugins/qmlprofiler/timelineview.h rename to src/plugins/qmlprofiler/timelinerenderer.h index 357662ea3e0ab0f783bd9d5d852731497d53a5dc..6b776b4c047110002e6f2fb521145743b66ede4a 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 6a4f3817fb4bbac5b0e3fba0946954ef25102abb..0000000000000000000000000000000000000000 --- 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 57754a6dd00d654a5d161a8efe50f2b5c33b809b..0000000000000000000000000000000000000000 --- 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 24d2702751cd811bf445c74f9a711e7a744ac1e0..0000000000000000000000000000000000000000 --- 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 9414895631fd77b99e9041860dbad581446670b0..0000000000000000000000000000000000000000 --- 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 ca582eeb768e5e12fa9b3389c81150914327835e..0000000000000000000000000000000000000000 --- 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 bd5bac6c8f042997e896a7d282aa8569d1d9e291..b6b159f591d850cd632d32adb4382430e2ede949 100644 --- a/src/tools/tools.pro +++ b/src/tools/tools.pro @@ -1,7 +1,6 @@ TEMPLATE = subdirs SUBDIRS = qtpromaker \ - qmlprofilertool \ qmlpuppet win32 {