From 1f76525c5f701f612b56a49715cda6597e3727b1 Mon Sep 17 00:00:00 2001 From: Christiaan Janssen Date: Thu, 8 Mar 2012 16:50:14 +0100 Subject: [PATCH] QmlProfiler: updated standalone app Using a stripped-down version of the profiler data structure. Change-Id: I93a0b12462edea0ca8a1d0db42aa892aa2afc919 Reviewed-by: Kai Koehne --- tools/qmlprofiler/constants.h | 11 +- tools/qmlprofiler/profiledata.cpp | 1902 -------------------- tools/qmlprofiler/profiledata.h | 247 --- tools/qmlprofiler/qmlprofiler.pro | 11 +- tools/qmlprofiler/qmlprofilerapplication.cpp | 22 +- tools/qmlprofiler/qmlprofilerapplication.h | 9 +- .../{profileclient.cpp => qmlprofilerclient.cpp} | 56 +- .../{profileclient.h => qmlprofilerclient.h} | 33 +- tools/qmlprofiler/qmlprofilerdata.cpp | 602 +++++++ tools/qmlprofiler/qmlprofilerdata.h | 104 ++ tools/qmlprofiler/qmlprofilereventlocation.h | 61 + 11 files changed, 834 insertions(+), 2224 deletions(-) delete mode 100644 tools/qmlprofiler/profiledata.cpp delete mode 100644 tools/qmlprofiler/profiledata.h rename tools/qmlprofiler/{profileclient.cpp => qmlprofilerclient.cpp} (84%) rename tools/qmlprofiler/{profileclient.h => qmlprofilerclient.h} (84%) create mode 100644 tools/qmlprofiler/qmlprofilerdata.cpp create mode 100644 tools/qmlprofiler/qmlprofilerdata.h create mode 100644 tools/qmlprofiler/qmlprofilereventlocation.h diff --git a/tools/qmlprofiler/constants.h b/tools/qmlprofiler/constants.h index e5a1f80..d87bad1 100644 --- a/tools/qmlprofiler/constants.h +++ b/tools/qmlprofiler/constants.h @@ -54,15 +54,6 @@ const char CMD_RECORD2[] ="r"; const char CMD_QUIT[] ="quit"; const char CMD_QUIT2[] = "q"; -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 MIN_LEVEL = 1; - -} // Contants +} // Constants #endif // CONSTANTS_H diff --git a/tools/qmlprofiler/profiledata.cpp b/tools/qmlprofiler/profiledata.cpp deleted file mode 100644 index 6082f4a..0000000 --- a/tools/qmlprofiler/profiledata.cpp +++ /dev/null @@ -1,1902 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** 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. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU General -** Public License version 3.0 as published by the Free Software Foundation -** and appearing in the file LICENSE.GPL included in the packaging of this -** file. Please review the following information to ensure the GNU General -** Public License version 3.0 requirements will be met: -** http://www.gnu.org/copyleft/gpl.html. -** -** 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. -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "profiledata.h" -#include "constants.h" - -#include -#include -#include -#include -#include - -using namespace Constants; - -QmlEvent::QmlEvent() -{ - eventType = QQmlProfilerService::MaximumRangeType; - eventId = -1; - duration = 0; - calls = 0; - minTime = 0; - maxTime = 0; - timePerCall = 0; - percentOfTime = 0; - medianTime = 0; - isBindingLoop = false; -} - -QmlEvent::~QmlEvent() -{ - qDeleteAll(parentHash.values()); - parentHash.clear(); - qDeleteAll(childrenHash.values()); - childrenHash.clear(); -} - -QmlEvent &QmlEvent::operator=(const QmlEvent &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; -} - -V8Event::V8Event() -{ - line = -1; - eventId = -1; - totalTime = 0; - selfTime = 0; - totalPercent = 0; - selfPercent = 0; -} - -V8Event::~V8Event() -{ - qDeleteAll(parentHash.values()); - parentHash.clear(); - qDeleteAll(childrenHash.values()); - childrenHash.clear(); -} - -V8Event &V8Event::operator=(const V8Event &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 V8EventSub(ref.parentHash.value(key))); - } - - qDeleteAll(childrenHash.values()); - childrenHash.clear(); - foreach (const QString &key, ref.childrenHash.keys()) { - childrenHash.insert(key, new V8EventSub(ref.childrenHash.value(key))); - } - return *this; -} - -// endtimedata -struct QmlEventEndTime { - qint64 endTime; - int startTimeIndex; - QmlEvent *description; -}; - -// starttimedata -struct QmlEventStartTime{ - qint64 startTime; - qint64 length; - qint64 level; - int endTimeIndex; - qint64 nestingLevel; - qint64 nestingDepth; - QmlEvent *description; - - // animation-related data - int frameRate; - int animationCount; - - int bindingLoopHead; -}; - -struct QmlEventTypeCount { - QList eventIds; - int nestingCount; -}; - -// used by quicksort -bool compareEndTimes(const QmlEventEndTime &t1, - const QmlEventEndTime &t2) -{ - return t1.endTime < t2.endTime; -} - -bool compareStartTimes(const QmlEventStartTime &t1, - const QmlEventStartTime &t2) -{ - return t1.startTime < t2.startTime; -} - -bool compareStartIndexes(const QmlEventEndTime &t1, - const QmlEventEndTime &t2) -{ - return t1.startTimeIndex < t2.startTimeIndex; -} - -QString qmlEventType(QQmlProfilerService::RangeType typeEnum) -{ - switch (typeEnum) { - case QQmlProfilerService::Painting: - return QLatin1String(TYPE_PAINTING_STR); - break; - case QQmlProfilerService::Compiling: - return QLatin1String(TYPE_COMPILING_STR); - break; - case QQmlProfilerService::Creating: - return QLatin1String(TYPE_CREATING_STR); - break; - case QQmlProfilerService::Binding: - return QLatin1String(TYPE_BINDING_STR); - break; - case QQmlProfilerService::HandlingSignal: - return QLatin1String(TYPE_HANDLINGSIGNAL_STR); - break; - default: - return QString::number((int)typeEnum); - } -} - -QQmlProfilerService::RangeType qmlEventType(const QString &typeString) -{ - if (typeString == QLatin1String(TYPE_PAINTING_STR)) { - return QQmlProfilerService::Painting; - } else if (typeString == QLatin1String(TYPE_COMPILING_STR)) { - return QQmlProfilerService::Compiling; - } else if (typeString == QLatin1String(TYPE_CREATING_STR)) { - return QQmlProfilerService::Creating; - } else if (typeString == QLatin1String(TYPE_BINDING_STR)) { - return QQmlProfilerService::Binding; - } else if (typeString == QLatin1String(TYPE_HANDLINGSIGNAL_STR)) { - return QQmlProfilerService::HandlingSignal; - } else { - bool isNumber = false; - int type = typeString.toUInt(&isNumber); - if (isNumber) { - return (QQmlProfilerService::RangeType)type; - } else { - return QQmlProfilerService::MaximumRangeType; - } - } -} - -QString getHashStringForQmlEvent( - EventLocation location, - QQmlProfilerService::RangeType eventType) -{ - return QString("%1:%2:%3:%4").arg(location.filename, - QString::number(location.line), - QString::number(location.column), - QString::number(eventType)); -} - -class ProfileDataPrivate -{ -public: - - // convenience functions - void clearQmlRootEvent(); - void clearV8RootEvent(); - - // Stored data - QmlEventHash m_eventDescriptions; - QList m_endTimeSortedList; - QList m_startTimeSortedList; - - void collectV8Statistics(); - V8Events m_v8EventList; - QHash m_v8parents; - - QmlEvent m_qmlRootEvent; - V8Event m_v8RootEvent; - QString m_rootEventName; - QString m_rootEventDesc; - - QHash m_typeCounts; - - qint64 m_traceEndTime; - qint64 m_traceStartTime; - qint64 m_qmlMeasuredTime; - qint64 m_v8MeasuredTime; - - QmlEventStartTime *m_lastFrameEvent; - qint64 m_maximumAnimationCount; - qint64 m_minimumAnimationCount; - - // file to load - QString m_filename; -}; - -ProfileData::ProfileData(QObject *parent) : - QObject(parent), - d(new ProfileDataPrivate) -{ - setObjectName("ProfileData"); - - d->m_traceEndTime = 0; - d->m_traceStartTime = -1; - d->m_qmlMeasuredTime = 0; - d->m_v8MeasuredTime = 0; - d->m_rootEventName = tr(""); - d->m_rootEventDesc = tr("Main Program"); - d->clearQmlRootEvent(); - d->clearV8RootEvent(); - d->m_lastFrameEvent = 0; - d->m_maximumAnimationCount = 0; - d->m_minimumAnimationCount = 0; -} - -ProfileData::~ProfileData() -{ - clear(); -} - -void ProfileData::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(); - emit dataClear(); -} - -QmlEvents ProfileData::getQmlEvents() const -{ - return d->m_eventDescriptions.values(); -} - -QmlEvent *ProfileData::qmlEvent(int eventId) const -{ - foreach (QmlEvent *event, d->m_eventDescriptions.values()) { - if (event->eventId == eventId) - return event; - } - return 0; -} - -V8Event *ProfileData::v8Event(int eventId) const -{ - foreach (V8Event *event, d->m_v8EventList) { - if (event->eventId == eventId) - return event; - } - return 0; -} - -const V8Events& ProfileData::getV8Events() const -{ - return d->m_v8EventList; -} - -void ProfileData::addQmlEvent( - QQmlProfilerService::RangeType type, qint64 startTime, qint64 length, - const QStringList &data, const EventLocation &location) -{ - const QChar colon = QLatin1Char(':'); - QString displayName, eventHashStr, details; - EventLocation eventLocation = location; - - emit processingData(); - - // 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 == QQmlProfilerService::Compiling && eventLocation.filename.isEmpty()) { - eventLocation.filename = details; - eventLocation.line = 1; - eventLocation.column = 1; - } - - // generate hash - if (eventLocation.filename.isEmpty()) { - displayName = tr(""); - 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); - } - - QmlEvent *newEvent; - if (d->m_eventDescriptions.contains(eventHashStr)) { - newEvent = d->m_eventDescriptions[eventHashStr]; - } else { - newEvent = new QmlEvent; - newEvent->displayname = displayName; - newEvent->location = eventLocation; - newEvent->eventHashStr = eventHashStr; - newEvent->eventType = type; - newEvent->details = details; - d->m_eventDescriptions.insert(eventHashStr, newEvent); - } - - QmlEventEndTime endTimeData; - endTimeData.endTime = startTime + length; - endTimeData.description = newEvent; - endTimeData.startTimeIndex = d->m_startTimeSortedList.count(); - - QmlEventStartTime 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 ProfileData::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); - V8Event *eventData = 0; - - // time is given in milliseconds, but internally we store it in microseconds - totalTime *= 1e6; - selfTime *= 1e6; - - // cumulate information - foreach (V8Event *v8event, d->m_v8EventList) { - if (v8event->displayName == displayName && - v8event->functionName == function) { - eventData = v8event; - break; - } - } - - if (!eventData) { - eventData = new V8Event; - 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; - - V8Event *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)) { - V8EventSub *newParentSub = new V8EventSub(parentEvent); - newParentSub->totalTime = totalTime; - - eventData->parentHash.insert(parentEvent->displayName, newParentSub ); - } else { - V8EventSub *newParentSub = - eventData->parentHash.value(parentEvent->displayName); - newParentSub->totalTime += totalTime; - } - - if (!parentEvent->childrenHash.contains(eventData->displayName)) { - V8EventSub *newChildSub = new V8EventSub(eventData); - newChildSub->totalTime = totalTime; - - parentEvent->childrenHash.insert(eventData->displayName, newChildSub); - } else { - V8EventSub *newChildSub = - parentEvent->childrenHash.value(eventData->displayName); - newChildSub->totalTime += totalTime; - } - } -} - -void ProfileData::addFrameEvent(qint64 time, int framerate, int animationcount) -{ - QString displayName, eventHashStr, details; - - emit processingData(); - - details = tr("Animation Timer Update"); - displayName = tr(""); - eventHashStr = displayName; - - QmlEvent *newEvent; - if (d->m_eventDescriptions.contains(eventHashStr)) { - newEvent = d->m_eventDescriptions[eventHashStr]; - } else { - newEvent = new QmlEvent; - newEvent->displayname = displayName; - newEvent->eventHashStr = eventHashStr; - newEvent->eventType = QQmlProfilerService::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; - } - - QmlEventEndTime endTimeData; - endTimeData.endTime = time + length; - endTimeData.description = newEvent; - endTimeData.startTimeIndex = d->m_startTimeSortedList.count(); - - QmlEventStartTime 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 ProfileDataPrivate::collectV8Statistics() -{ - if (!m_v8EventList.isEmpty()) { - double totalTimes = m_v8MeasuredTime; - double selfTimes = 0; - foreach (V8Event *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(); - V8Event *newRootEvent = new V8Event; - *newRootEvent = m_v8RootEvent; - m_v8EventList << newRootEvent; - } - - foreach (V8Event *v8event, m_v8EventList) { - v8event->totalPercent = v8event->totalTime * 100.0 / totalTimes; - v8event->selfPercent = v8event->selfTime * 100.0 / selfTimes; - } - - int index = 0; - foreach (V8Event *v8event, m_v8EventList) { - v8event->eventId = index++; - } - m_v8RootEvent.eventId = m_v8EventList[rootEventIndex]->eventId; - } -} - -void ProfileData::setTraceEndTime( qint64 time ) -{ - d->m_traceEndTime = time; -} - -void ProfileData::setTraceStartTime( qint64 time ) -{ - d->m_traceStartTime = time; -} - -void ProfileData::complete() -{ - emit postProcessing(); - d->collectV8Statistics(); - postProcess(); -} - -void ProfileDataPrivate::clearQmlRootEvent() -{ - m_qmlRootEvent.displayname = m_rootEventName; - m_qmlRootEvent.location = EventLocation(); - m_qmlRootEvent.eventHashStr = m_rootEventName; - m_qmlRootEvent.details = m_rootEventDesc; - m_qmlRootEvent.eventType = QQmlProfilerService::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 ProfileDataPrivate::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 ProfileData::compileStatistics(qint64 startTime, qint64 endTime) -{ - int index; - int fromIndex = findFirstIndex(startTime); - int toIndex = findLastIndex(endTime); - double totalTime = 0; - - // clear existing statistics - foreach (QmlEvent *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 - d->clearQmlRootEvent(); - - // compute parent-child relationship and call count - QHash lastParent; - for (index = fromIndex; index <= toIndex; index++) { - QmlEvent *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 == QQmlProfilerService::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; - - QmlEvent *parentEvent = &d->m_qmlRootEvent; - 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 - d->m_qmlRootEvent.duration = totalTime+1; - d->m_qmlRootEvent.minTime = totalTime+1; - d->m_qmlRootEvent.maxTime = totalTime+1; - d->m_qmlRootEvent.medianTime = totalTime+1; - if (totalTime > 0) - d->m_qmlRootEvent.calls = 1; - - // insert into list - QmlEvent *listedRootEvent = - d->m_eventDescriptions.value(d->m_rootEventName); - if (!listedRootEvent) { - listedRootEvent = new QmlEvent; - d->m_eventDescriptions.insert(d->m_rootEventName, listedRootEvent); - } - *listedRootEvent = d->m_qmlRootEvent; - - // compute percentages - foreach (QmlEvent *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 < QmlEvent* , QList > durationLists; - for (index = fromIndex; index <= toIndex; index++) { - QmlEvent *desc = d->m_startTimeSortedList[index].description; - qint64 len = d->m_startTimeSortedList[index].length; - durationLists[desc].append(len); - } - QMutableHashIterator < QmlEvent* , QList > 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 ProfileData::prepareForDisplay() -{ - // generate numeric ids - int ndx = 0; - foreach (QmlEvent *binding, d->m_eventDescriptions.values()) { - binding->eventId = ndx++; - } - - // collect type counts - foreach (const QmlEventStartTime &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 ProfileData::sortStartTimes() -{ - if (d->m_startTimeSortedList.count() < 2) - return; - - // assuming startTimes is partially sorted - // identify blocks of events and sort them with quicksort - QList::iterator itFrom = - d->m_startTimeSortedList.end() - 2; - QList::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 ProfileData::sortEndTimes() -{ - // assuming endTimes is partially sorted - // identify blocks of events and sort them with quicksort - - if (d->m_endTimeSortedList.count() < 2) - return; - - QList::iterator itFrom = - d->m_endTimeSortedList.begin(); - QList::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 ProfileData::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 == - QQmlProfilerService::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 ProfileData::computeNestingLevels() -{ - // compute levels - QHash endtimesPerLevel; - QList nestingLevels; - QList < QHash > endtimesPerNestingLevel; - int level = MIN_LEVEL; - endtimesPerLevel[MIN_LEVEL] = 0; - - for (int i = 0; i < QQmlProfilerService::MaximumRangeType; i++) { - nestingLevels << MIN_LEVEL; - QHash dummyHash; - dummyHash[MIN_LEVEL] = 0; - endtimesPerNestingLevel << dummyHash; - } - - for (int i=0; im_startTimeSortedList.count(); i++) { - qint64 st = d->m_startTimeSortedList[i].startTime; - int type = d->m_startTimeSortedList[i].description->eventType; - - if (type == QQmlProfilerService::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 ProfileData::computeNestingDepth() -{ - QHash 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 ProfileData::postProcess() -{ - if (count() != 0) { - sortStartTimes(); - sortEndTimes(); - findAnimationLimits(); - computeLevels(); - linkEndsToStarts(); - reloadDetails(); - compileStatistics(traceStartTime(), traceEndTime()); - prepareForDisplay(); - } - // data is ready even when there's no data - emit dataReady(); -} - -void ProfileData::linkEndsToStarts() -{ - for (int i = 0; i < d->m_startTimeSortedList.count(); i++) - d->m_endTimeSortedList[d->m_startTimeSortedList[i].endTimeIndex].startTimeIndex = i; -} - -void ProfileData::computeLevels() -{ - computeNestingLevels(); - computeNestingDepth(); -} - -void ProfileData::reloadDetails() -{ - // request binding/signal details from the AST - foreach (QmlEvent *event, d->m_eventDescriptions.values()) { - if (event->eventType != QQmlProfilerService::Binding && - event->eventType != QQmlProfilerService::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 ProfileData::findBindingLoops(qint64 startTime, qint64 endTime) -{ - // first clear existing data - foreach (QmlEvent *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 stackRefs; - QList stack; - int fromIndex = findFirstIndex(startTime); - int toIndex = findLastIndex(endTime); - - for (int i = 0; i < d->m_startTimeSortedList.count(); i++) { - QmlEvent *currentEvent = d->m_startTimeSortedList[i].description; - QmlEventStartTime *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 - QmlEventStartTime *head = stack[stackRefs.indexOf(currentEvent)]; - inTimeEvent->bindingLoopHead = d->m_endTimeSortedList[head->endTimeIndex].startTimeIndex; - d->m_startTimeSortedList[inTimeEvent->bindingLoopHead].bindingLoopHead = i; - } - } -} - -void ProfileData::rewriteDetailsString( - QQmlProfilerService::RangeType eventType, - const EventLocation &location, const QString &newString) -{ - QString eventHashStr = getHashStringForQmlEvent(location, - eventType); - Q_ASSERT(d->m_eventDescriptions.contains(eventHashStr)); - d->m_eventDescriptions.value(eventHashStr)->details = newString; - emit detailsChanged(d->m_eventDescriptions.value(eventHashStr)->eventId, - newString); -} - -void ProfileData::finishedRewritingDetails() -{ - emit reloadDetailLabels(); -} - -// 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 ProfileData::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 ProfileData::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 ProfileData::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 ProfileData::firstTimeMark() const -{ - if (d->m_startTimeSortedList.isEmpty()) - return 0; - else { - return d->m_startTimeSortedList[0].startTime; - } -} - -qint64 ProfileData::lastTimeMark() const -{ - if (d->m_endTimeSortedList.isEmpty()) - return 0; - else { - return d->m_endTimeSortedList.last().endTime; - } -} - -qint64 ProfileData::traceStartTime() const -{ - return d->m_traceStartTime != -1? d->m_traceStartTime : firstTimeMark(); -} - -qint64 ProfileData::traceEndTime() const -{ - return d->m_traceEndTime ? d->m_traceEndTime : lastTimeMark(); -} - -qint64 ProfileData::traceDuration() const -{ - return traceEndTime() - traceStartTime(); -} - -qint64 ProfileData::qmlMeasuredTime() const -{ - return d->m_qmlMeasuredTime; -} -qint64 ProfileData::v8MeasuredTime() const -{ - return d->m_v8MeasuredTime; -} - -int ProfileData::count() const -{ - return d->m_startTimeSortedList.count(); -} - -//////////////////////////////////////////////////////////////////////////////// - - -bool ProfileData::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", 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 QmlEvent *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 QmlEventStartTime &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 == - QQmlProfilerService::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 (V8Event *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 (V8EventSub *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 ProfileData::setFilename(const QString &filename) -{ - d->m_filename = filename; -} - -void ProfileData::load(const QString &filename) -{ - setFilename(filename); - load(); -} - -// "be strict in your output but tolerant in your inputs" -void ProfileData::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; - } - - emit processingData(); - - // erase current - clear(); - - bool readingQmlEvents = false; - bool readingV8Events = false; - QHash descriptionBuffer; - QmlEvent *currentEvent = 0; - QHash v8eventBuffer; - QHash childrenIndexes; - QHash childrenTimes; - QHash parentTimes; - V8Event *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() == - 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") { - QmlEventStartTime 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 QmlEvent; - 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; - - QmlEventEndTime 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 QmlEvent; - 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 V8Event; - 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 countChanged(); - emit dataReady(); - 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 (QmlEvent *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++) { - QmlEventStartTime 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)) { - V8EventSub *newChild = new V8EventSub(v8eventBuffer[childIndex]); - V8EventSub *newParent = new V8EventSub(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(); - - emit postProcessing(); - d->collectV8Statistics(); - postProcess(); -} - -/////////////////////////////////////////////// -qint64 ProfileData::getStartTime(int index) const -{ - return d->m_startTimeSortedList[index].startTime; -} - -qint64 ProfileData::getEndTime(int index) const -{ - return d->m_startTimeSortedList[index].startTime + - d->m_startTimeSortedList[index].length; -} - -qint64 ProfileData::getDuration(int index) const -{ - return d->m_startTimeSortedList[index].length; -} - -int ProfileData::getType(int index) const -{ - return d->m_startTimeSortedList[index].description->eventType; -} - -int ProfileData::getNestingLevel(int index) const -{ - return d->m_startTimeSortedList[index].nestingLevel; -} - -int ProfileData::getNestingDepth(int index) const -{ - return d->m_startTimeSortedList[index].nestingDepth; -} - -QString ProfileData::getFilename(int index) const -{ - return d->m_startTimeSortedList[index].description->location.filename; -} - -int ProfileData::getLine(int index) const -{ - return d->m_startTimeSortedList[index].description->location.line; -} - -int ProfileData::getColumn(int index) const -{ - return d->m_startTimeSortedList[index].description->location.column; -} - -QString ProfileData::getDetails(int index) const -{ - // special: animations - if (d->m_startTimeSortedList[index].description->eventType == - QQmlProfilerService::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 ProfileData::getEventId(int index) const { - return d->m_startTimeSortedList[index].description->eventId; -} - -int ProfileData::getFramerate(int index) const -{ - return d->m_startTimeSortedList[index].frameRate; -} - -int ProfileData::getAnimationCount(int index) const -{ - return d->m_startTimeSortedList[index].animationCount; -} - -int ProfileData::getMaximumAnimationCount() const -{ - return d->m_maximumAnimationCount; -} - -int ProfileData::getMinimumAnimationCount() const -{ - return d->m_minimumAnimationCount; -} - -int ProfileData::uniqueEventsOfType(int type) const -{ - if (!d->m_typeCounts.contains(type)) - return 0; - return d->m_typeCounts[type]->eventIds.count(); -} - -int ProfileData::maxNestingForType(int type) const -{ - if (!d->m_typeCounts.contains(type)) - return 0; - return d->m_typeCounts[type]->nestingCount; -} - -QString ProfileData::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 ProfileData::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 ProfileData::eventIdForType(int type, int index) const -{ - if (!d->m_typeCounts.contains(type)) - return -1; - return d->m_typeCounts[type]->eventIds[index]; -} - -int ProfileData::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); -} diff --git a/tools/qmlprofiler/profiledata.h b/tools/qmlprofiler/profiledata.h deleted file mode 100644 index f5d7269..0000000 --- a/tools/qmlprofiler/profiledata.h +++ /dev/null @@ -1,247 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** 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. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU General -** Public License version 3.0 as published by the Free Software Foundation -** and appearing in the file LICENSE.GPL included in the packaging of this -** file. Please review the following information to ensure the GNU General -** Public License version 3.0 requirements will be met: -** http://www.gnu.org/copyleft/gpl.html. -** -** 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. -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef PROFILEDATA_H -#define PROFILEDATA_H - -#include - -#include -#include - -struct QmlEvent; -struct V8Event; - -typedef QHash QmlEventHash; -typedef QList QmlEvents; -typedef QList V8Events; - -struct EventLocation -{ - EventLocation() : line(-1),column(-1) {} - EventLocation(const QString &file, int lineNumber, int columnNumber) - : filename(file), line(lineNumber), column(columnNumber) {} - QString filename; - int line; - int column; -}; - -struct QmlEventSub { - QmlEventSub(QmlEvent *from) - : reference(from), duration(0), calls(0), inLoopPath(false) - {} - QmlEventSub(QmlEventSub *from) - : reference(from->reference), duration(from->duration), - calls(from->calls), inLoopPath(from->inLoopPath) - {} - QmlEvent *reference; - qint64 duration; - qint64 calls; - bool inLoopPath; -}; - -struct QmlEvent -{ - QmlEvent(); - ~QmlEvent(); - - QString displayname; - QString eventHashStr; - QString details; - EventLocation location; - QQmlProfilerService::RangeType eventType; - QHash parentHash; - QHash childrenHash; - qint64 duration; - qint64 calls; - qint64 minTime; - qint64 maxTime; - double timePerCall; - double percentOfTime; - qint64 medianTime; - int eventId; - bool isBindingLoop; - - QmlEvent &operator=(const QmlEvent &ref); -}; - -struct V8EventSub { - V8EventSub(V8Event *from) - : reference(from), totalTime(0) - {} - V8EventSub(V8EventSub *from) - : reference(from->reference), totalTime(from->totalTime) - {} - - V8Event *reference; - qint64 totalTime; -}; - -struct V8Event -{ - V8Event(); - ~V8Event(); - - QString displayName; - QString filename; - QString functionName; - int line; - double totalTime; // given in milliseconds - double totalPercent; - double selfTime; - double selfPercent; - QHash parentHash; - QHash childrenHash; - int eventId; - - V8Event &operator=(const V8Event &ref); -}; - -class ProfileData : public QObject -{ - Q_OBJECT - -public: - explicit ProfileData(QObject *parent = 0); - ~ProfileData(); - - QmlEvents getQmlEvents() const; - QmlEvent *qmlEvent(int eventId) const; - const V8Events& getV8Events() const; - V8Event *v8Event(int eventId) const; - - 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 qint64 getStartTime(int index) const; - Q_INVOKABLE qint64 getEndTime(int index) const; - Q_INVOKABLE qint64 getDuration(int index) const; - Q_INVOKABLE int getType(int index) const; - Q_INVOKABLE int getNestingLevel(int index) const; - Q_INVOKABLE int getNestingDepth(int index) const; - Q_INVOKABLE QString getFilename(int index) const; - Q_INVOKABLE int getLine(int index) const; - Q_INVOKABLE int getColumn(int index) const; - Q_INVOKABLE QString getDetails(int index) const; - Q_INVOKABLE int getEventId(int index) const; - Q_INVOKABLE int getFramerate(int index) const; - Q_INVOKABLE int getAnimationCount(int index) const; - Q_INVOKABLE int getMaximumAnimationCount() const; - Q_INVOKABLE int getMinimumAnimationCount() const; - - // per-type data - Q_INVOKABLE int uniqueEventsOfType(int type) const; - Q_INVOKABLE int maxNestingForType(int type) const; - Q_INVOKABLE QString eventTextForType(int type, int index) const; - Q_INVOKABLE QString eventDisplayNameForType(int type, int index) const; - Q_INVOKABLE int eventIdForType(int type, int index) const; - Q_INVOKABLE int eventPosInType(int index) const; - - Q_INVOKABLE qint64 traceStartTime() const; - Q_INVOKABLE qint64 traceEndTime() const; - Q_INVOKABLE qint64 traceDuration() const; - Q_INVOKABLE qint64 qmlMeasuredTime() const; - Q_INVOKABLE qint64 v8MeasuredTime() const; - - void showErrorDialog(const QString &st ) const; - void compileStatistics(qint64 startTime, qint64 endTime); - -signals: - void dataReady(); - void countChanged(); - void error(const QString &error); - void dataClear(); - void processingData(); - void postProcessing(); - - void requestDetailsForLocation(int eventType, const EventLocation &location); - void detailsChanged(int eventId, const QString &newString); - void reloadDetailLabels(); - void reloadDocumentsForDetails(); - -public slots: - void clear(); - void addQmlEvent(QQmlProfilerService::RangeType type, - qint64 startTime, qint64 length, - const QStringList &data, - const EventLocation &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); - 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(QQmlProfilerService::RangeType eventType, - const EventLocation &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); - -private: - class ProfileDataPrivate *d; -}; - -#endif // PROFILEDATA_H diff --git a/tools/qmlprofiler/qmlprofiler.pro b/tools/qmlprofiler/qmlprofiler.pro index db7e357..c35c487 100644 --- a/tools/qmlprofiler/qmlprofiler.pro +++ b/tools/qmlprofiler/qmlprofiler.pro @@ -14,14 +14,15 @@ CONFIG += console declarative_debug SOURCES += main.cpp \ qmlprofilerapplication.cpp \ commandlistener.cpp \ - profileclient.cpp \ - profiledata.cpp \ - qqmldebugclient.cpp + qqmldebugclient.cpp \ + qmlprofilerdata.cpp \ + qmlprofilerclient.cpp HEADERS += \ qmlprofilerapplication.h \ commandlistener.h \ constants.h \ - profileclient.h \ - profiledata.h \ + qmlprofilerdata.h \ + qmlprofilerclient.h \ + qmlprofilereventlocation.h \ qqmldebugclient.h diff --git a/tools/qmlprofiler/qmlprofilerapplication.cpp b/tools/qmlprofiler/qmlprofilerapplication.cpp index 0b10ec0..05518dd 100644 --- a/tools/qmlprofiler/qmlprofilerapplication.cpp +++ b/tools/qmlprofiler/qmlprofilerapplication.cpp @@ -104,20 +104,20 @@ QmlProfilerApplication::QmlProfilerApplication(int &argc, char **argv) : connect(&m_qmlProfilerClient, SIGNAL(enabledChanged()), this, SLOT(traceClientEnabled())); connect(&m_qmlProfilerClient, SIGNAL(recordingChanged(bool)), this, SLOT(recordingChanged())); - connect(&m_qmlProfilerClient, SIGNAL(range(QQmlProfilerService::RangeType,qint64,qint64,QStringList,EventLocation)), - &m_profileData, SLOT(addQmlEvent(QQmlProfilerService::RangeType,qint64,qint64,QStringList,EventLocation))); - connect(&m_qmlProfilerClient, SIGNAL(traceFinished(qint64)), &m_profileData, SLOT(setTraceEndTime(qint64))); - connect(&m_qmlProfilerClient, SIGNAL(traceStarted(qint64)), &m_profileData, SLOT(setTraceStartTime(qint64))); - connect(&m_qmlProfilerClient, SIGNAL(frame(qint64,int,int)), &m_profileData, SLOT(addFrameEvent(qint64,int,int))); + connect(&m_qmlProfilerClient, SIGNAL(range(QQmlProfilerService::RangeType,qint64,qint64,QStringList,QmlEventLocation)), + &m_profilerData, SLOT(addQmlEvent(QQmlProfilerService::RangeType,qint64,qint64,QStringList,QmlEventLocation))); + connect(&m_qmlProfilerClient, SIGNAL(traceFinished(qint64)), &m_profilerData, SLOT(setTraceEndTime(qint64))); + connect(&m_qmlProfilerClient, SIGNAL(traceStarted(qint64)), &m_profilerData, SLOT(setTraceStartTime(qint64))); + connect(&m_qmlProfilerClient, SIGNAL(frame(qint64,int,int)), &m_profilerData, SLOT(addFrameEvent(qint64,int,int))); connect(&m_qmlProfilerClient, SIGNAL(complete()), this, SLOT(qmlComplete())); connect(&m_v8profilerClient, SIGNAL(enabledChanged()), this, SLOT(profilerClientEnabled())); connect(&m_v8profilerClient, SIGNAL(range(int,QString,QString,int,double,double)), - &m_profileData, SLOT(addV8Event(int,QString,QString,int,double,double))); + &m_profilerData, SLOT(addV8Event(int,QString,QString,int,double,double))); connect(&m_v8profilerClient, SIGNAL(complete()), this, SLOT(v8Complete())); - connect(&m_profileData, SIGNAL(error(QString)), this, SLOT(logError(QString))); - connect(&m_profileData, SIGNAL(dataReady()), this, SLOT(traceFinished())); + connect(&m_profilerData, SIGNAL(error(QString)), this, SLOT(logError(QString))); + connect(&m_profilerData, SIGNAL(dataReady()), this, SLOT(traceFinished())); } @@ -375,7 +375,7 @@ void QmlProfilerApplication::traceFinished() { const QString fileName = traceFileName(); - if (m_profileData.save(fileName)) + if (m_profilerData.save(fileName)) print(QString("Saving trace to %1.").arg(fileName)); if (m_quitAfterSave) @@ -416,7 +416,7 @@ void QmlProfilerApplication::qmlComplete() m_qmlDataReady = true; if (m_v8profilerClient.state() != QQmlDebugClient::Enabled || m_v8DataReady) { - m_profileData.complete(); + m_profilerData.complete(); // once complete is sent, reset the flag m_qmlDataReady = false; } @@ -427,7 +427,7 @@ void QmlProfilerApplication::v8Complete() m_v8DataReady = true; if (m_qmlProfilerClient.state() != QQmlDebugClient::Enabled || m_qmlDataReady) { - m_profileData.complete(); + m_profilerData.complete(); // once complete is sent, reset the flag m_v8DataReady = false; } diff --git a/tools/qmlprofiler/qmlprofilerapplication.h b/tools/qmlprofiler/qmlprofilerapplication.h index 3937db7..c714888 100644 --- a/tools/qmlprofiler/qmlprofilerapplication.h +++ b/tools/qmlprofiler/qmlprofilerapplication.h @@ -46,7 +46,8 @@ #include #include -#include "profileclient.h" +#include "qmlprofilerclient.h" +#include "qmlprofilerdata.h" class QmlProfilerApplication : public QCoreApplication { @@ -104,9 +105,9 @@ private: bool m_quitAfterSave; QQmlDebugConnection m_connection; - QmlProfileClient m_qmlProfilerClient; - V8ProfileClient m_v8profilerClient; - ProfileData m_profileData; + QmlProfilerClient m_qmlProfilerClient; + V8ProfilerClient m_v8profilerClient; + QmlProfilerData m_profilerData; QTimer m_connectTimer; uint m_connectionAttempts; diff --git a/tools/qmlprofiler/profileclient.cpp b/tools/qmlprofiler/qmlprofilerclient.cpp similarity index 84% rename from tools/qmlprofiler/profileclient.cpp rename to tools/qmlprofiler/qmlprofilerclient.cpp index 8528746..97ed90e 100644 --- a/tools/qmlprofiler/profileclient.cpp +++ b/tools/qmlprofiler/qmlprofilerclient.cpp @@ -39,12 +39,12 @@ ** ****************************************************************************/ -#include "profileclient.h" +#include "qmlprofilerclient.h" #include #include -ProfileClient::ProfileClient(const QString &clientName, +ProfilerClient::ProfilerClient(const QString &clientName, QQmlDebugConnection *client) : QQmlDebugClient(clientName, client), m_recording(false), @@ -52,7 +52,7 @@ ProfileClient::ProfileClient(const QString &clientName, { } -ProfileClient::~ProfileClient() +ProfilerClient::~ProfilerClient() { //Disable profiling if started by client //Profiling data will be lost!! @@ -60,26 +60,26 @@ ProfileClient::~ProfileClient() setRecording(false); } -void ProfileClient::clearData() +void ProfilerClient::clearData() { emit cleared(); } -bool ProfileClient::isEnabled() const +bool ProfilerClient::isEnabled() const { return m_enabled; } -void ProfileClient::sendRecordingStatus() +void ProfilerClient::sendRecordingStatus() { } -bool ProfileClient::isRecording() const +bool ProfilerClient::isRecording() const { return m_recording; } -void ProfileClient::setRecording(bool v) +void ProfilerClient::setRecording(bool v) { if (v == m_recording) return; @@ -93,7 +93,7 @@ void ProfileClient::setRecording(bool v) emit recordingChanged(v); } -void ProfileClient::stateChanged(State status) +void ProfilerClient::stateChanged(State status) { if ((m_enabled && status != Enabled) || (!m_enabled && status == Enabled)) @@ -103,10 +103,10 @@ void ProfileClient::stateChanged(State status) } -class QmlProfileClientPrivate +class QmlProfilerClientPrivate { public: - QmlProfileClientPrivate() + QmlProfilerClientPrivate() : inProgressRanges(0) , maximumTime(0) { @@ -117,31 +117,31 @@ public: qint64 inProgressRanges; QStack rangeStartTimes[QQmlProfilerService::MaximumRangeType]; QStack rangeDatas[QQmlProfilerService::MaximumRangeType]; - QStack rangeLocations[QQmlProfilerService::MaximumRangeType]; + QStack rangeLocations[QQmlProfilerService::MaximumRangeType]; int rangeCount[QQmlProfilerService::MaximumRangeType]; qint64 maximumTime; }; -QmlProfileClient::QmlProfileClient( +QmlProfilerClient::QmlProfilerClient( QQmlDebugConnection *client) - : ProfileClient(QLatin1String("CanvasFrameRate"), client), - d(new QmlProfileClientPrivate) + : ProfilerClient(QStringLiteral("CanvasFrameRate"), client), + d(new QmlProfilerClientPrivate) { } -QmlProfileClient::~QmlProfileClient() +QmlProfilerClient::~QmlProfilerClient() { delete d; } -void QmlProfileClient::clearData() +void QmlProfilerClient::clearData() { ::memset(d->rangeCount, 0, QQmlProfilerService::MaximumRangeType * sizeof(int)); - ProfileClient::clearData(); + ProfilerClient::clearData(); } -void QmlProfileClient::sendRecordingStatus() +void QmlProfilerClient::sendRecordingStatus() { QByteArray ba; QDataStream stream(&ba, QIODevice::WriteOnly); @@ -149,7 +149,7 @@ void QmlProfileClient::sendRecordingStatus() sendMessage(ba); } -void QmlProfileClient::messageReceived(const QByteArray &data) +void QmlProfilerClient::messageReceived(const QByteArray &data) { QByteArray rwData = data; QDataStream stream(&rwData, QIODevice::ReadOnly); @@ -216,7 +216,7 @@ void QmlProfileClient::messageReceived(const QByteArray &data) stream >> column; if (d->rangeCount[range] > 0) { - d->rangeLocations[range].push(EventLocation(fileName, line, + d->rangeLocations[range].push(QmlEventLocation(fileName, line, column)); } } else { @@ -228,8 +228,8 @@ void QmlProfileClient::messageReceived(const QByteArray &data) d->maximumTime = qMax(time, d->maximumTime); QStringList data = d->rangeDatas[range].count() ? d->rangeDatas[range].pop() : QStringList(); - EventLocation location = d->rangeLocations[range].count() ? - d->rangeLocations[range].pop() : EventLocation(); + QmlEventLocation location = d->rangeLocations[range].count() ? + d->rangeLocations[range].pop() : QmlEventLocation(); qint64 startTime = d->rangeStartTimes[range].pop(); emit this->range((QQmlProfilerService::RangeType)range, @@ -246,16 +246,16 @@ void QmlProfileClient::messageReceived(const QByteArray &data) } } -V8ProfileClient::V8ProfileClient(QQmlDebugConnection *client) - : ProfileClient(QLatin1String("V8Profiler"), client) +V8ProfilerClient::V8ProfilerClient(QQmlDebugConnection *client) + : ProfilerClient(QStringLiteral("V8Profiler"), client) { } -V8ProfileClient::~V8ProfileClient() +V8ProfilerClient::~V8ProfilerClient() { } -void V8ProfileClient::sendRecordingStatus() +void V8ProfilerClient::sendRecordingStatus() { QByteArray ba; QDataStream stream(&ba, QIODevice::WriteOnly); @@ -272,7 +272,7 @@ void V8ProfileClient::sendRecordingStatus() sendMessage(ba); } -void V8ProfileClient::messageReceived(const QByteArray &data) +void V8ProfilerClient::messageReceived(const QByteArray &data) { QByteArray rwData = data; QDataStream stream(&rwData, QIODevice::ReadOnly); diff --git a/tools/qmlprofiler/profileclient.h b/tools/qmlprofiler/qmlprofilerclient.h similarity index 84% rename from tools/qmlprofiler/profileclient.h rename to tools/qmlprofiler/qmlprofilerclient.h index 54826a2..2a8629d 100644 --- a/tools/qmlprofiler/profileclient.h +++ b/tools/qmlprofiler/qmlprofilerclient.h @@ -39,16 +39,15 @@ ** ****************************************************************************/ -#ifndef PROFILECLIENT_H -#define PROFILECLIENT_H - -#include "profiledata.h" +#ifndef QMLPROFILERCLIENT_H +#define QMLPROFILERCLIENT_H #include "qqmldebugclient.h" #include +#include "qmlprofilereventlocation.h" -class ProfileClientPrivate; -class ProfileClient : public QQmlDebugClient +class ProfilerClientPrivate; +class ProfilerClient : public QQmlDebugClient { Q_OBJECT @@ -57,9 +56,9 @@ class ProfileClient : public QQmlDebugClient NOTIFY recordingChanged) public: - ProfileClient(const QString & clientName, + ProfilerClient(const QString &clientName, QQmlDebugConnection *client); - ~ProfileClient(); + ~ProfilerClient(); bool isEnabled() const; bool isRecording() const; @@ -83,13 +82,13 @@ protected: bool m_enabled; }; -class QmlProfileClient : public ProfileClient +class QmlProfilerClient : public ProfilerClient { Q_OBJECT public: - QmlProfileClient(QQmlDebugConnection *client); - ~QmlProfileClient(); + QmlProfilerClient(QQmlDebugConnection *client); + ~QmlProfilerClient(); public slots: void clearData(); @@ -100,17 +99,17 @@ signals: void traceStarted( qint64 time ); void range(QQmlProfilerService::RangeType type, qint64 startTime, qint64 length, const QStringList &data, - const EventLocation &location); + const QmlEventLocation &location); void frame(qint64 time, int frameRate, int animationCount); protected: virtual void messageReceived(const QByteArray &); private: - class QmlProfileClientPrivate *d; + class QmlProfilerClientPrivate *d; }; -class V8ProfileClient : public ProfileClient +class V8ProfilerClient : public ProfilerClient { Q_OBJECT @@ -122,8 +121,8 @@ public: V8MaximumMessage }; - V8ProfileClient(QQmlDebugConnection *client); - ~V8ProfileClient(); + V8ProfilerClient(QQmlDebugConnection *client); + ~V8ProfilerClient(); public slots: void sendRecordingStatus(); @@ -136,4 +135,4 @@ protected: virtual void messageReceived(const QByteArray &); }; -#endif // PROFILECLIENT_H +#endif // QMLPROFILERCLIENT_H diff --git a/tools/qmlprofiler/qmlprofilerdata.cpp b/tools/qmlprofiler/qmlprofilerdata.cpp new file mode 100644 index 0000000..38e64ff --- /dev/null +++ b/tools/qmlprofiler/qmlprofilerdata.cpp @@ -0,0 +1,602 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmlprofilerdata.h" + +#include +#include +#include +#include +#include + +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"; +} + +struct QmlRangeEventData { + QmlRangeEventData() {} // never called + QmlRangeEventData(const QString &_displayName, + const QString &_eventHashStr, + const QmlEventLocation &_location, + const QString &_details, + const QQmlProfilerService::RangeType &_eventType) + : displayName(_displayName),eventHashStr(_eventHashStr),location(_location), + details(_details),eventType(_eventType) {} + QString displayName; + QString eventHashStr; + QmlEventLocation location; + QString details; + QQmlProfilerService::RangeType eventType; +}; + +struct QmlRangeEventStartInstance { + QmlRangeEventStartInstance() {} // never called + QmlRangeEventStartInstance(qint64 _startTime, qint64 _duration, int _frameRate, + int _animationCount, QmlRangeEventData *_data) + : startTime(_startTime), duration(_duration), frameRate(_frameRate), + animationCount(_animationCount), data(_data) + { } + qint64 startTime; + qint64 duration; + int frameRate; + int animationCount; + QmlRangeEventData *data; +}; + +QT_BEGIN_NAMESPACE +Q_DECLARE_TYPEINFO(QmlRangeEventData, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(QmlRangeEventStartInstance, Q_MOVABLE_TYPE); +QT_END_NAMESPACE + +struct QV8EventInfo { + QString displayName; + QString eventHashStr; + QString functionName; + QString fileName; + int line; + qint64 totalTime; + qint64 selfTime; + + QHash v8children; +}; + +///////////////////////////////////////////////////////////////// +class QmlProfilerDataPrivate +{ +public: + QmlProfilerDataPrivate(QmlProfilerData *qq){ Q_UNUSED(qq); } + + // data storage + QHash eventDescriptions; + QVector startInstanceList; + QHash v8EventHash; + + qint64 traceStartTime; + qint64 traceEndTime; + + // internal state while collecting events + QmlRangeEventStartInstance *lastFrameEvent; + qint64 qmlMeasuredTime; + qint64 v8MeasuredTime; + QHash v8parents; + void clearV8RootEvent(); + QV8EventInfo v8RootEvent; + + QmlProfilerData::State state; +}; + +///////////////////////////////////////////////////////////////// +QmlProfilerData::QmlProfilerData(QObject *parent) : + QObject(parent),d(new QmlProfilerDataPrivate(this)) +{ + d->state = Empty; + clear(); +} + +QmlProfilerData::~QmlProfilerData() +{ + clear(); + delete d; +} + +void QmlProfilerData::clear() +{ + qDeleteAll(d->eventDescriptions.values()); + d->eventDescriptions.clear(); + d->startInstanceList.clear(); + + qDeleteAll(d->v8EventHash.values()); + d->v8EventHash.clear(); + d->v8parents.clear(); + d->clearV8RootEvent(); + d->v8MeasuredTime = 0; + + d->traceEndTime = 0; + d->traceStartTime = -1; + d->qmlMeasuredTime = 0; + + d->lastFrameEvent = 0; + + setState(Empty); +} + +QString QmlProfilerData::getHashStringForQmlEvent(const QmlEventLocation &location, int eventType) +{ + return QString(QStringLiteral("%1:%2:%3:%4")).arg( + location.filename, + QString::number(location.line), + QString::number(location.column), + QString::number(eventType)); +} + +QString QmlProfilerData::getHashStringForV8Event(const QString &displayName, const QString &function) +{ + return QString(QStringLiteral("%1:%2")).arg(displayName, function); +} + +QString QmlProfilerData::qmlRangeTypeAsString(QQmlProfilerService::RangeType typeEnum) +{ + switch (typeEnum) { + case QQmlProfilerService::Painting: + return QLatin1String(Constants::TYPE_PAINTING_STR); + break; + case QQmlProfilerService::Compiling: + return QLatin1String(Constants::TYPE_COMPILING_STR); + break; + case QQmlProfilerService::Creating: + return QLatin1String(Constants::TYPE_CREATING_STR); + break; + case QQmlProfilerService::Binding: + return QLatin1String(Constants::TYPE_BINDING_STR); + break; + case QQmlProfilerService::HandlingSignal: + return QLatin1String(Constants::TYPE_HANDLINGSIGNAL_STR); + break; + default: + return QString::number((int)typeEnum); + } +} + +void QmlProfilerData::setTraceStartTime(qint64 time) +{ + d->traceStartTime = time; +} + +void QmlProfilerData::setTraceEndTime(qint64 time) +{ + d->traceEndTime = time; +} + +qint64 QmlProfilerData::traceStartTime() const +{ + return d->traceStartTime; +} + +qint64 QmlProfilerData::traceEndTime() const +{ + return d->traceEndTime; +} + +void QmlProfilerData::addQmlEvent(QQmlProfilerService::RangeType type, + qint64 startTime, + qint64 duration, + const QStringList &data, + const QmlEventLocation &location) +{ + setState(AcquiringData); + + QString details; + // generate details string + if (data.isEmpty()) + details = tr("Source code not available"); + else { + details = data.join(QStringLiteral(" ")).replace( + QLatin1Char('\n'),QStringLiteral(" ")).simplified(); + QRegExp rewrite(QStringLiteral("\\(function \\$(\\w+)\\(\\) \\{ (return |)(.+) \\}\\)")); + bool match = rewrite.exactMatch(details); + if (match) { + details = rewrite.cap(1) +QLatin1String(": ") + rewrite.cap(3); + } + if (details.startsWith(QLatin1String("file://"))) + details = details.mid(details.lastIndexOf(QLatin1Char('/')) + 1); + } + + QmlEventLocation eventLocation = location; + QString displayName, eventHashStr; + // generate hash + if (eventLocation.filename.isEmpty()) { + displayName = tr(""); + eventHashStr = getHashStringForQmlEvent(eventLocation, type); + } else { + const QString filePath = QUrl(eventLocation.filename).path(); + displayName = filePath.mid( + filePath.lastIndexOf(QLatin1Char('/')) + 1) + + QLatin1Char(':') + QString::number(eventLocation.line); + eventHashStr = getHashStringForQmlEvent(eventLocation, type); + } + + QmlRangeEventData *newEvent; + if (d->eventDescriptions.contains(eventHashStr)) { + newEvent = d->eventDescriptions[eventHashStr]; + } else { + newEvent = new QmlRangeEventData(displayName, eventHashStr, location, details, type); + d->eventDescriptions.insert(eventHashStr, newEvent); + } + + QmlRangeEventStartInstance rangeEventStartInstance(startTime, duration, 1e9/duration, -1, newEvent); + + d->startInstanceList.append(rangeEventStartInstance); +} + +void QmlProfilerData::addFrameEvent(qint64 time, int framerate, int animationcount) +{ + setState(AcquiringData); + + QString details = tr("Animation Timer Update"); + QString displayName = tr(""); + QString eventHashStr = displayName; + + QmlRangeEventData *newEvent; + if (d->eventDescriptions.contains(eventHashStr)) { + newEvent = d->eventDescriptions[eventHashStr]; + } else { + newEvent = new QmlRangeEventData(displayName, eventHashStr, QmlEventLocation(), details, QQmlProfilerService::Painting); + d->eventDescriptions.insert(eventHashStr, newEvent); + } + + qint64 duration = 1e9/framerate; + // avoid overlap + if (d->lastFrameEvent && + d->lastFrameEvent->startTime + d->lastFrameEvent->duration >= time) { + d->lastFrameEvent->duration = time - 1 - d->lastFrameEvent->startTime; + } + + QmlRangeEventStartInstance rangeEventStartInstance(time, duration, framerate, animationcount, newEvent); + + d->startInstanceList.append(rangeEventStartInstance); + + d->lastFrameEvent = &d->startInstanceList.last(); +} + +QString QmlProfilerData::rootEventName() +{ + return tr(""); +} + +QString QmlProfilerData::rootEventDescription() +{ + return tr("Main Program"); +} + +void QmlProfilerDataPrivate::clearV8RootEvent() +{ + v8RootEvent.displayName = QmlProfilerData::rootEventName(); + v8RootEvent.eventHashStr = QmlProfilerData::rootEventName(); + v8RootEvent.functionName = QmlProfilerData::rootEventDescription(); + v8RootEvent.line = -1; + v8RootEvent.totalTime = 0; + v8RootEvent.selfTime = 0; + v8RootEvent.v8children.clear(); +} + +void QmlProfilerData::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 = getHashStringForV8Event(displayName, function); + + setState(AcquiringData); + + // time is given in milliseconds, but internally we store it in microseconds + totalTime *= 1e6; + selfTime *= 1e6; + + // accumulate information + QV8EventInfo *eventData = d->v8EventHash[hashStr]; + if (!eventData) { + eventData = new QV8EventInfo; + 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; + + QV8EventInfo *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 (!parentEvent->v8children.contains(eventData->eventHashStr)) { + parentEvent->v8children[eventData->eventHashStr] = totalTime; + } else { + parentEvent->v8children[eventData->eventHashStr] += totalTime; + } + } +} + +void QmlProfilerData::computeQmlTime() +{ + // compute levels + QHash endtimesPerLevel; + int minimumLevel = 1; + int level = minimumLevel; + + for (int i = 0; i < d->startInstanceList.count(); i++) { + qint64 st = d->startInstanceList[i].startTime; + int type = d->startInstanceList[i].data->eventType; + + if (type == QQmlProfilerService::Painting) { + continue; + } + + // general level + if (endtimesPerLevel[level] > st) { + level++; + } else { + while (level > minimumLevel && endtimesPerLevel[level-1] <= st) + level--; + } + endtimesPerLevel[level] = st + d->startInstanceList[i].duration; + + if (level == minimumLevel) { + d->qmlMeasuredTime += d->startInstanceList[i].duration; + } + } +} + +bool compareStartTimes(const QmlRangeEventStartInstance &t1, const QmlRangeEventStartInstance &t2) +{ + return t1.startTime < t2.startTime; +} + +void QmlProfilerData::sortStartTimes() +{ + if (d->startInstanceList.count() < 2) + return; + + // assuming startTimes is partially sorted + // identify blocks of events and sort them with quicksort + QVector::iterator itFrom = d->startInstanceList.end() - 2; + QVector::iterator itTo = d->startInstanceList.end() - 1; + + while (itFrom != d->startInstanceList.begin() && itTo != d->startInstanceList.begin()) { + // find block to sort + while ( itFrom != d->startInstanceList.begin() + && itTo->startTime > itFrom->startTime ) { + itTo--; + itFrom = itTo - 1; + } + + // if we're at the end of the list + if (itFrom == d->startInstanceList.begin()) + break; + + // find block length + while ( itFrom != d->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; + } +} + +void QmlProfilerData::complete() +{ + setState(ProcessingData); + sortStartTimes(); + computeQmlTime(); + setState(Done); + emit dataReady(); +} + +bool QmlProfilerData::isEmpty() const +{ + return d->startInstanceList.isEmpty() && d->v8EventHash.isEmpty(); +} + +bool QmlProfilerData::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(QStringLiteral("trace")); + stream.writeAttribute(QStringLiteral("version"), QLatin1String(Constants::PROFILER_FILE_VERSION)); + + stream.writeAttribute(QStringLiteral("traceStart"), QString::number(traceStartTime())); + stream.writeAttribute(QStringLiteral("traceEnd"), QString::number(traceEndTime())); + + stream.writeStartElement(QStringLiteral("eventData")); + stream.writeAttribute(QStringLiteral("totalTime"), QString::number(d->qmlMeasuredTime)); + + foreach (const QmlRangeEventData *eventData, d->eventDescriptions.values()) { + stream.writeStartElement(QStringLiteral("event")); + stream.writeAttribute(QStringLiteral("index"), QString::number(d->eventDescriptions.keys().indexOf(eventData->eventHashStr))); + stream.writeTextElement(QStringLiteral("displayname"), eventData->displayName); + stream.writeTextElement(QStringLiteral("type"), qmlRangeTypeAsString(eventData->eventType)); + if (!eventData->location.filename.isEmpty()) { + stream.writeTextElement(QStringLiteral("filename"), eventData->location.filename); + stream.writeTextElement(QStringLiteral("line"), QString::number(eventData->location.line)); + stream.writeTextElement(QStringLiteral("column"), QString::number(eventData->location.column)); + } + stream.writeTextElement(QStringLiteral("details"), eventData->details); + stream.writeEndElement(); + } + stream.writeEndElement(); // eventData + + stream.writeStartElement(QStringLiteral("profilerDataModel")); + foreach (const QmlRangeEventStartInstance &rangedEvent, d->startInstanceList) { + stream.writeStartElement(QStringLiteral("range")); + stream.writeAttribute(QStringLiteral("startTime"), QString::number(rangedEvent.startTime)); + stream.writeAttribute(QStringLiteral("duration"), QString::number(rangedEvent.duration)); + stream.writeAttribute(QStringLiteral("eventIndex"), QString::number(d->eventDescriptions.keys().indexOf(rangedEvent.data->eventHashStr))); + if (rangedEvent.data->eventType == QQmlProfilerService::Painting && rangedEvent.animationCount >= 0) { + // animation frame + stream.writeAttribute(QStringLiteral("framerate"), QString::number(rangedEvent.frameRate)); + stream.writeAttribute(QStringLiteral("animationcount"), QString::number(rangedEvent.animationCount)); + } + stream.writeEndElement(); + } + stream.writeEndElement(); // profilerDataModel + + stream.writeStartElement(QStringLiteral("v8profile")); // v8 profiler output + stream.writeAttribute(QStringLiteral("totalTime"), QString::number(d->v8MeasuredTime)); + foreach (QV8EventInfo *v8event, d->v8EventHash.values()) { + stream.writeStartElement(QStringLiteral("event")); + stream.writeAttribute(QStringLiteral("index"), QString::number(d->v8EventHash.keys().indexOf(v8event->eventHashStr))); + stream.writeTextElement(QStringLiteral("displayname"), v8event->displayName); + stream.writeTextElement(QStringLiteral("functionname"), v8event->functionName); + if (!v8event->fileName.isEmpty()) { + stream.writeTextElement(QStringLiteral("filename"), v8event->fileName); + stream.writeTextElement(QStringLiteral("line"), QString::number(v8event->line)); + } + stream.writeTextElement(QStringLiteral("totalTime"), QString::number(v8event->totalTime)); + stream.writeTextElement(QStringLiteral("selfTime"), QString::number(v8event->selfTime)); + if (!v8event->v8children.isEmpty()) { + stream.writeStartElement(QStringLiteral("childrenEvents")); + QStringList childrenIndexes; + QStringList childrenTimes; + foreach (const QString &childHash, v8event->v8children.keys()) { + childrenIndexes << QString::number(v8EventIndex(childHash)); + childrenTimes << QString::number(v8event->v8children[childHash]); + } + + stream.writeAttribute(QStringLiteral("list"), childrenIndexes.join(QString(", "))); + stream.writeAttribute(QStringLiteral("childrenTimes"), childrenTimes.join(QString(", "))); + stream.writeEndElement(); + } + stream.writeEndElement(); + } + stream.writeEndElement(); // v8 profiler output + + stream.writeEndElement(); // trace + stream.writeEndDocument(); + + file.close(); + return true; +} + +int QmlProfilerData::v8EventIndex(const QString &hashStr) +{ + if (!d->v8EventHash.contains(hashStr)) { + emit error("Trying to index nonexisting v8 event"); + return -1; + } + return d->v8EventHash.keys().indexOf( hashStr ); +} + +void QmlProfilerData::setState(QmlProfilerData::State state) +{ + // It's not an error, we are continuously calling "AcquiringData" for example + if (d->state == state) + return; + + switch (state) { + case Empty: + // if it's not empty, complain but go on + if (!isEmpty()) + emit error("Invalid qmlprofiler state change (Empty)"); + break; + case AcquiringData: + // we're not supposed to receive new data while processing older data + if (d->state == ProcessingData) + emit error("Invalid qmlprofiler state change (AcquiringData)"); + break; + case ProcessingData: + if (d->state != AcquiringData) + emit error("Invalid qmlprofiler state change (ProcessingData)"); + break; + case Done: + if (d->state != ProcessingData && d->state != Empty) + emit error("Invalid qmlprofiler state change (Done)"); + break; + default: + emit error("Trying to set unknown state in events list"); + break; + } + + d->state = state; + emit stateChanged(); + + // special: if we were done with an empty list, clean internal data and go back to empty + if (d->state == Done && isEmpty()) { + clear(); + } + return; +} + diff --git a/tools/qmlprofiler/qmlprofilerdata.h b/tools/qmlprofiler/qmlprofilerdata.h new file mode 100644 index 0000000..66209a1 --- /dev/null +++ b/tools/qmlprofiler/qmlprofilerdata.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMLPROFILERDATA_H +#define QMLPROFILERDATA_H + +#include +#include "qmlprofilereventlocation.h" + +#include + +class QmlProfilerDataPrivate; +class QmlProfilerData : public QObject +{ + Q_OBJECT +public: + enum State { + Empty, + AcquiringData, + ProcessingData, + Done + }; + + explicit QmlProfilerData(QObject *parent = 0); + ~QmlProfilerData(); + + static QString getHashStringForQmlEvent(const QmlEventLocation &location, int eventType); + static QString getHashStringForV8Event(const QString &displayName, const QString &function); + static QString qmlRangeTypeAsString(QQmlProfilerService::RangeType typeEnum); + static QString rootEventName(); + static QString rootEventDescription(); + + qint64 traceStartTime() const; + qint64 traceEndTime() const; + + bool isEmpty() const; + +signals: + void error(QString); + void stateChanged(); + void dataReady(); + +public slots: + void clear(); + void setTraceEndTime(qint64 time); + void setTraceStartTime(qint64 time); + void addQmlEvent(QQmlProfilerService::RangeType type, qint64 startTime, qint64 duration, + const QStringList &data, const QmlEventLocation &location); + 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 complete(); + bool save(const QString &filename); + +private: + void sortStartTimes(); + int v8EventIndex(const QString &hashStr); + void computeQmlTime(); + void setState(QmlProfilerData::State state); + +private: + QmlProfilerDataPrivate *d; +}; + +#endif // QMLPROFILERDATA_H diff --git a/tools/qmlprofiler/qmlprofilereventlocation.h b/tools/qmlprofiler/qmlprofilereventlocation.h new file mode 100644 index 0000000..a34b6ab --- /dev/null +++ b/tools/qmlprofiler/qmlprofilereventlocation.h @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMLPROFILEREVENTLOCATION_H +#define QMLPROFILEREVENTLOCATION_H + +#include + +struct QmlEventLocation +{ + QmlEventLocation() : line(-1), column(-1) {} + QmlEventLocation(const QString &file, int lineNumber, int columnNumber) + : filename(file), line(lineNumber), column(columnNumber) {} + QString filename; + int line; + int column; +}; + +QT_BEGIN_NAMESPACE +Q_DECLARE_TYPEINFO(QmlEventLocation, Q_MOVABLE_TYPE); +QT_END_NAMESPACE + +#endif // QMLPROFILEREVENTLOCATION_H -- 2.7.4