1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQml module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "profiledata.h"
43 #include "constants.h"
45 #include <QtCore/QStringList>
46 #include <QtCore/QRegExp>
47 #include <QtCore/QUrl>
48 #include <QtCore/QFile>
49 #include <QtCore/QXmlStreamReader>
51 using namespace Constants;
55 eventType = QQmlProfilerService::MaximumRangeType;
64 isBindingLoop = false;
69 qDeleteAll(parentHash.values());
71 qDeleteAll(childrenHash.values());
75 QmlEvent &QmlEvent::operator=(const QmlEvent &ref)
80 displayname = ref.displayname;
81 location = ref.location;
82 eventHashStr = ref.eventHashStr;
83 details = ref.details;
84 eventType = ref.eventType;
85 duration = ref.duration;
87 minTime = ref.minTime;
88 maxTime = ref.maxTime;
89 timePerCall = ref.timePerCall;
90 percentOfTime = ref.percentOfTime;
91 medianTime = ref.medianTime;
92 eventId = ref.eventId;
93 isBindingLoop = ref.isBindingLoop;
95 qDeleteAll(parentHash.values());
97 foreach (const QString &key, ref.parentHash.keys()) {
98 parentHash.insert(key,
99 new QmlEventSub(ref.parentHash.value(key)));
102 qDeleteAll(childrenHash.values());
103 childrenHash.clear();
104 foreach (const QString &key, ref.childrenHash.keys()) {
105 childrenHash.insert(key,
106 new QmlEventSub(ref.childrenHash.value(key)));
124 qDeleteAll(parentHash.values());
126 qDeleteAll(childrenHash.values());
127 childrenHash.clear();
130 V8Event &V8Event::operator=(const V8Event &ref)
135 displayName = ref.displayName;
136 filename = ref.filename;
137 functionName = ref.functionName;
139 totalTime = ref.totalTime;
140 totalPercent = ref.totalPercent;
141 selfTime = ref.selfTime;
142 selfPercent = ref.selfPercent;
143 eventId = ref.eventId;
145 qDeleteAll(parentHash.values());
147 foreach (const QString &key, ref.parentHash.keys()) {
148 parentHash.insert(key, new V8EventSub(ref.parentHash.value(key)));
151 qDeleteAll(childrenHash.values());
152 childrenHash.clear();
153 foreach (const QString &key, ref.childrenHash.keys()) {
154 childrenHash.insert(key, new V8EventSub(ref.childrenHash.value(key)));
160 struct QmlEventEndTime {
163 QmlEvent *description;
167 struct QmlEventStartTime{
174 QmlEvent *description;
176 // animation-related data
183 struct QmlEventTypeCount {
184 QList <int> eventIds;
189 bool compareEndTimes(const QmlEventEndTime &t1,
190 const QmlEventEndTime &t2)
192 return t1.endTime < t2.endTime;
195 bool compareStartTimes(const QmlEventStartTime &t1,
196 const QmlEventStartTime &t2)
198 return t1.startTime < t2.startTime;
201 bool compareStartIndexes(const QmlEventEndTime &t1,
202 const QmlEventEndTime &t2)
204 return t1.startTimeIndex < t2.startTimeIndex;
207 QString qmlEventType(QQmlProfilerService::RangeType typeEnum)
210 case QQmlProfilerService::Painting:
211 return QLatin1String(TYPE_PAINTING_STR);
213 case QQmlProfilerService::Compiling:
214 return QLatin1String(TYPE_COMPILING_STR);
216 case QQmlProfilerService::Creating:
217 return QLatin1String(TYPE_CREATING_STR);
219 case QQmlProfilerService::Binding:
220 return QLatin1String(TYPE_BINDING_STR);
222 case QQmlProfilerService::HandlingSignal:
223 return QLatin1String(TYPE_HANDLINGSIGNAL_STR);
226 return QString::number((int)typeEnum);
230 QQmlProfilerService::RangeType qmlEventType(const QString &typeString)
232 if (typeString == QLatin1String(TYPE_PAINTING_STR)) {
233 return QQmlProfilerService::Painting;
234 } else if (typeString == QLatin1String(TYPE_COMPILING_STR)) {
235 return QQmlProfilerService::Compiling;
236 } else if (typeString == QLatin1String(TYPE_CREATING_STR)) {
237 return QQmlProfilerService::Creating;
238 } else if (typeString == QLatin1String(TYPE_BINDING_STR)) {
239 return QQmlProfilerService::Binding;
240 } else if (typeString == QLatin1String(TYPE_HANDLINGSIGNAL_STR)) {
241 return QQmlProfilerService::HandlingSignal;
243 bool isNumber = false;
244 int type = typeString.toUInt(&isNumber);
246 return (QQmlProfilerService::RangeType)type;
248 return QQmlProfilerService::MaximumRangeType;
253 QString getHashStringForQmlEvent(
254 EventLocation location,
255 QQmlProfilerService::RangeType eventType)
257 return QString("%1:%2:%3:%4").arg(location.filename,
258 QString::number(location.line),
259 QString::number(location.column),
260 QString::number(eventType));
263 class ProfileDataPrivate
267 // convenience functions
268 void clearQmlRootEvent();
269 void clearV8RootEvent();
272 QmlEventHash m_eventDescriptions;
273 QList<QmlEventEndTime> m_endTimeSortedList;
274 QList<QmlEventStartTime> m_startTimeSortedList;
276 void collectV8Statistics();
277 V8Events m_v8EventList;
278 QHash<int, V8Event *> m_v8parents;
280 QmlEvent m_qmlRootEvent;
281 V8Event m_v8RootEvent;
282 QString m_rootEventName;
283 QString m_rootEventDesc;
285 QHash<int, QmlEventTypeCount *> m_typeCounts;
287 qint64 m_traceEndTime;
288 qint64 m_traceStartTime;
289 qint64 m_qmlMeasuredTime;
290 qint64 m_v8MeasuredTime;
292 QmlEventStartTime *m_lastFrameEvent;
293 qint64 m_maximumAnimationCount;
294 qint64 m_minimumAnimationCount;
300 ProfileData::ProfileData(QObject *parent) :
302 d(new ProfileDataPrivate)
304 setObjectName("ProfileData");
306 d->m_traceEndTime = 0;
307 d->m_traceStartTime = -1;
308 d->m_qmlMeasuredTime = 0;
309 d->m_v8MeasuredTime = 0;
310 d->m_rootEventName = tr("<program>");
311 d->m_rootEventDesc = tr("Main Program");
312 d->clearQmlRootEvent();
313 d->clearV8RootEvent();
314 d->m_lastFrameEvent = 0;
315 d->m_maximumAnimationCount = 0;
316 d->m_minimumAnimationCount = 0;
319 ProfileData::~ProfileData()
324 void ProfileData::clear()
326 qDeleteAll(d->m_eventDescriptions.values());
327 d->m_eventDescriptions.clear();
329 qDeleteAll(d->m_v8EventList);
330 d->m_v8EventList.clear();
332 d->m_endTimeSortedList.clear();
333 d->m_startTimeSortedList.clear();
335 d->m_v8parents.clear();
337 d->clearQmlRootEvent();
338 d->clearV8RootEvent();
340 foreach (QmlEventTypeCount *typeCount, d->m_typeCounts.values())
342 d->m_typeCounts.clear();
344 d->m_traceEndTime = 0;
345 d->m_traceStartTime = -1;
346 d->m_qmlMeasuredTime = 0;
347 d->m_v8MeasuredTime = 0;
349 d->m_lastFrameEvent = 0;
350 d->m_maximumAnimationCount = 0;
351 d->m_minimumAnimationCount = 0;
357 QmlEvents ProfileData::getQmlEvents() const
359 return d->m_eventDescriptions.values();
362 QmlEvent *ProfileData::qmlEvent(int eventId) const
364 foreach (QmlEvent *event, d->m_eventDescriptions.values()) {
365 if (event->eventId == eventId)
371 V8Event *ProfileData::v8Event(int eventId) const
373 foreach (V8Event *event, d->m_v8EventList) {
374 if (event->eventId == eventId)
380 const V8Events& ProfileData::getV8Events() const
382 return d->m_v8EventList;
385 void ProfileData::addQmlEvent(
386 QQmlProfilerService::RangeType type, qint64 startTime, qint64 length,
387 const QStringList &data, const EventLocation &location)
389 const QChar colon = QLatin1Char(':');
390 QString displayName, eventHashStr, details;
391 EventLocation eventLocation = location;
393 emit processingData();
395 // generate details string
397 details = tr("Source code not available");
399 details = data.join(" ").replace('\n'," ").simplified();
400 QRegExp rewrite("\\(function \\$(\\w+)\\(\\) \\{ (return |)(.+) \\}\\)");
401 bool match = rewrite.exactMatch(details);
403 details = rewrite.cap(1) + ": " + rewrite.cap(3);
405 if (details.startsWith(QString("file://")))
406 details = details.mid(details.lastIndexOf(QChar('/')) + 1);
409 // backwards compatibility: "compiling" events don't have a proper location in older
410 // version of the protocol, but the filename is passed in the details string
411 if (type == QQmlProfilerService::Compiling && eventLocation.filename.isEmpty()) {
412 eventLocation.filename = details;
413 eventLocation.line = 1;
414 eventLocation.column = 1;
418 if (eventLocation.filename.isEmpty()) {
419 displayName = tr("<bytecode>");
420 eventHashStr = getHashStringForQmlEvent(eventLocation, type);
422 const QString filePath = QUrl(eventLocation.filename).path();
423 displayName = filePath.mid(filePath.lastIndexOf(QChar('/')) + 1) + colon + QString::number(eventLocation.line);
424 eventHashStr = getHashStringForQmlEvent(eventLocation, type);
428 if (d->m_eventDescriptions.contains(eventHashStr)) {
429 newEvent = d->m_eventDescriptions[eventHashStr];
431 newEvent = new QmlEvent;
432 newEvent->displayname = displayName;
433 newEvent->location = eventLocation;
434 newEvent->eventHashStr = eventHashStr;
435 newEvent->eventType = type;
436 newEvent->details = details;
437 d->m_eventDescriptions.insert(eventHashStr, newEvent);
440 QmlEventEndTime endTimeData;
441 endTimeData.endTime = startTime + length;
442 endTimeData.description = newEvent;
443 endTimeData.startTimeIndex = d->m_startTimeSortedList.count();
445 QmlEventStartTime startTimeData;
446 startTimeData.startTime = startTime;
447 startTimeData.length = length;
448 startTimeData.description = newEvent;
449 startTimeData.endTimeIndex = d->m_endTimeSortedList.count();
450 startTimeData.animationCount = -1;
451 startTimeData.frameRate = 1e9/length;
453 d->m_endTimeSortedList << endTimeData;
454 d->m_startTimeSortedList << startTimeData;
459 void ProfileData::addV8Event(int depth, const QString &function,
460 const QString &filename, int lineNumber,
461 double totalTime, double selfTime)
463 QString displayName = filename.mid(
464 filename.lastIndexOf(QLatin1Char('/')) + 1) + QLatin1Char(':') +
465 QString::number(lineNumber);
466 V8Event *eventData = 0;
468 // time is given in milliseconds, but internally we store it in microseconds
472 // cumulate information
473 foreach (V8Event *v8event, d->m_v8EventList) {
474 if (v8event->displayName == displayName &&
475 v8event->functionName == function) {
482 eventData = new V8Event;
483 eventData->displayName = displayName;
484 eventData->filename = filename;
485 eventData->functionName = function;
486 eventData->line = lineNumber;
487 eventData->totalTime = totalTime;
488 eventData->selfTime = selfTime;
489 d->m_v8EventList << eventData;
491 eventData->totalTime += totalTime;
492 eventData->selfTime += selfTime;
494 d->m_v8parents[depth] = eventData;
496 V8Event *parentEvent = 0;
498 parentEvent = &d->m_v8RootEvent;
499 d->m_v8MeasuredTime += totalTime;
501 if (depth > 0 && d->m_v8parents.contains(depth-1)) {
502 parentEvent = d->m_v8parents.value(depth-1);
505 if (parentEvent != 0) {
506 if (!eventData->parentHash.contains(parentEvent->displayName)) {
507 V8EventSub *newParentSub = new V8EventSub(parentEvent);
508 newParentSub->totalTime = totalTime;
510 eventData->parentHash.insert(parentEvent->displayName, newParentSub );
512 V8EventSub *newParentSub =
513 eventData->parentHash.value(parentEvent->displayName);
514 newParentSub->totalTime += totalTime;
517 if (!parentEvent->childrenHash.contains(eventData->displayName)) {
518 V8EventSub *newChildSub = new V8EventSub(eventData);
519 newChildSub->totalTime = totalTime;
521 parentEvent->childrenHash.insert(eventData->displayName, newChildSub);
523 V8EventSub *newChildSub =
524 parentEvent->childrenHash.value(eventData->displayName);
525 newChildSub->totalTime += totalTime;
530 void ProfileData::addFrameEvent(qint64 time, int framerate, int animationcount)
532 QString displayName, eventHashStr, details;
534 emit processingData();
536 details = tr("Animation Timer Update");
537 displayName = tr("<Animation Update>");
538 eventHashStr = displayName;
541 if (d->m_eventDescriptions.contains(eventHashStr)) {
542 newEvent = d->m_eventDescriptions[eventHashStr];
544 newEvent = new QmlEvent;
545 newEvent->displayname = displayName;
546 newEvent->eventHashStr = eventHashStr;
547 newEvent->eventType = QQmlProfilerService::Painting;
548 newEvent->details = details;
549 d->m_eventDescriptions.insert(eventHashStr, newEvent);
552 qint64 length = 1e9/framerate;
554 if (d->m_lastFrameEvent &&
555 d->m_lastFrameEvent->startTime + d->m_lastFrameEvent->length >= time) {
556 d->m_lastFrameEvent->length = time - 1 - d->m_lastFrameEvent->startTime;
557 d->m_endTimeSortedList[d->m_lastFrameEvent->endTimeIndex].endTime =
558 d->m_lastFrameEvent->startTime + d->m_lastFrameEvent->length;
561 QmlEventEndTime endTimeData;
562 endTimeData.endTime = time + length;
563 endTimeData.description = newEvent;
564 endTimeData.startTimeIndex = d->m_startTimeSortedList.count();
566 QmlEventStartTime startTimeData;
567 startTimeData.startTime = time;
568 startTimeData.length = length;
569 startTimeData.description = newEvent;
570 startTimeData.endTimeIndex = d->m_endTimeSortedList.count();
571 startTimeData.animationCount = animationcount;
572 startTimeData.frameRate = framerate;
574 d->m_endTimeSortedList << endTimeData;
575 d->m_startTimeSortedList << startTimeData;
577 d->m_lastFrameEvent = &d->m_startTimeSortedList.last();
582 void ProfileDataPrivate::collectV8Statistics()
584 if (!m_v8EventList.isEmpty()) {
585 double totalTimes = m_v8MeasuredTime;
586 double selfTimes = 0;
587 foreach (V8Event *v8event, m_v8EventList) {
588 selfTimes += v8event->selfTime;
591 // prevent divisions by 0
597 // insert root event in eventlist
598 // the +1 ns is to get it on top of the sorted list
599 m_v8RootEvent.totalTime = m_v8MeasuredTime + 1;
600 m_v8RootEvent.selfTime = 0;
602 int rootEventIndex = -1;
603 for (int ndx = 0; ndx < m_v8EventList.count(); ndx++)
605 if (m_v8EventList.at(ndx)->displayName == m_rootEventName) {
606 m_v8RootEvent = *m_v8EventList.at(ndx);
607 rootEventIndex = ndx;
611 if (rootEventIndex == -1) {
612 rootEventIndex = m_v8EventList.count();
613 V8Event *newRootEvent = new V8Event;
614 *newRootEvent = m_v8RootEvent;
615 m_v8EventList << newRootEvent;
618 foreach (V8Event *v8event, m_v8EventList) {
619 v8event->totalPercent = v8event->totalTime * 100.0 / totalTimes;
620 v8event->selfPercent = v8event->selfTime * 100.0 / selfTimes;
624 foreach (V8Event *v8event, m_v8EventList) {
625 v8event->eventId = index++;
627 m_v8RootEvent.eventId = m_v8EventList[rootEventIndex]->eventId;
631 void ProfileData::setTraceEndTime( qint64 time )
633 d->m_traceEndTime = time;
636 void ProfileData::setTraceStartTime( qint64 time )
638 d->m_traceStartTime = time;
641 void ProfileData::complete()
643 emit postProcessing();
644 d->collectV8Statistics();
648 void ProfileDataPrivate::clearQmlRootEvent()
650 m_qmlRootEvent.displayname = m_rootEventName;
651 m_qmlRootEvent.location = EventLocation();
652 m_qmlRootEvent.eventHashStr = m_rootEventName;
653 m_qmlRootEvent.details = m_rootEventDesc;
654 m_qmlRootEvent.eventType = QQmlProfilerService::Binding;
655 m_qmlRootEvent.duration = 0;
656 m_qmlRootEvent.calls = 0;
657 m_qmlRootEvent.minTime = 0;
658 m_qmlRootEvent.maxTime = 0;
659 m_qmlRootEvent.timePerCall = 0;
660 m_qmlRootEvent.percentOfTime = 0;
661 m_qmlRootEvent.medianTime = 0;
662 m_qmlRootEvent.eventId = -1;
664 qDeleteAll(m_qmlRootEvent.parentHash.values());
665 qDeleteAll(m_qmlRootEvent.childrenHash.values());
666 m_qmlRootEvent.parentHash.clear();
667 m_qmlRootEvent.childrenHash.clear();
670 void ProfileDataPrivate::clearV8RootEvent()
672 m_v8RootEvent.displayName = m_rootEventName;
673 m_v8RootEvent.functionName = m_rootEventDesc;
674 m_v8RootEvent.line = -1;
675 m_v8RootEvent.totalTime = 0;
676 m_v8RootEvent.totalPercent = 0;
677 m_v8RootEvent.selfTime = 0;
678 m_v8RootEvent.selfPercent = 0;
679 m_v8RootEvent.eventId = -1;
681 qDeleteAll(m_v8RootEvent.parentHash.values());
682 qDeleteAll(m_v8RootEvent.childrenHash.values());
683 m_v8RootEvent.parentHash.clear();
684 m_v8RootEvent.childrenHash.clear();
687 void ProfileData::compileStatistics(qint64 startTime, qint64 endTime)
690 int fromIndex = findFirstIndex(startTime);
691 int toIndex = findLastIndex(endTime);
692 double totalTime = 0;
694 // clear existing statistics
695 foreach (QmlEvent *eventDescription,
696 d->m_eventDescriptions.values()) {
697 eventDescription->calls = 0;
698 // maximum possible value
699 eventDescription->minTime = d->m_endTimeSortedList.last().endTime;
700 eventDescription->maxTime = 0;
701 eventDescription->medianTime = 0;
702 eventDescription->duration = 0;
703 qDeleteAll(eventDescription->parentHash);
704 qDeleteAll(eventDescription->childrenHash);
705 eventDescription->parentHash.clear();
706 eventDescription->childrenHash.clear();
709 // create root event for statistics
710 d->clearQmlRootEvent();
712 // compute parent-child relationship and call count
713 QHash<int, QmlEvent*> lastParent;
714 for (index = fromIndex; index <= toIndex; index++) {
715 QmlEvent *eventDescription =
716 d->m_startTimeSortedList[index].description;
718 if (d->m_startTimeSortedList[index].startTime > endTime ||
719 d->m_startTimeSortedList[index].startTime +
720 d->m_startTimeSortedList[index].length < startTime) {
724 if (eventDescription->eventType == QQmlProfilerService::Painting) {
725 // skip animation/paint events
729 eventDescription->calls++;
730 qint64 duration = d->m_startTimeSortedList[index].length;
731 eventDescription->duration += duration;
732 if (eventDescription->maxTime < duration)
733 eventDescription->maxTime = duration;
734 if (eventDescription->minTime > duration)
735 eventDescription->minTime = duration;
737 int level = d->m_startTimeSortedList[index].level;
739 QmlEvent *parentEvent = &d->m_qmlRootEvent;
740 if (level > MIN_LEVEL && lastParent.contains(level-1)) {
741 parentEvent = lastParent[level-1];
744 if (!eventDescription->parentHash.contains(parentEvent->eventHashStr)) {
745 QmlEventSub *newParentEvent =
746 new QmlEventSub(parentEvent);
747 newParentEvent->calls = 1;
748 newParentEvent->duration = duration;
750 eventDescription->parentHash.insert(parentEvent->eventHashStr,
753 QmlEventSub *newParentEvent =
754 eventDescription->parentHash.value(parentEvent->eventHashStr);
755 newParentEvent->duration += duration;
756 newParentEvent->calls++;
759 if (!parentEvent->childrenHash.contains(eventDescription->eventHashStr)) {
760 QmlEventSub *newChildEvent =
761 new QmlEventSub(eventDescription);
762 newChildEvent->calls = 1;
763 newChildEvent->duration = duration;
765 parentEvent->childrenHash.insert(eventDescription->eventHashStr,
768 QmlEventSub *newChildEvent =
769 parentEvent->childrenHash.value(eventDescription->eventHashStr);
770 newChildEvent->duration += duration;
771 newChildEvent->calls++;
774 lastParent[level] = eventDescription;
776 if (level == MIN_LEVEL) {
777 totalTime += duration;
781 // fake rootEvent statistics
782 // the +1 nanosecond is to force it to be on top of the sorted list
783 d->m_qmlRootEvent.duration = totalTime+1;
784 d->m_qmlRootEvent.minTime = totalTime+1;
785 d->m_qmlRootEvent.maxTime = totalTime+1;
786 d->m_qmlRootEvent.medianTime = totalTime+1;
788 d->m_qmlRootEvent.calls = 1;
791 QmlEvent *listedRootEvent =
792 d->m_eventDescriptions.value(d->m_rootEventName);
793 if (!listedRootEvent) {
794 listedRootEvent = new QmlEvent;
795 d->m_eventDescriptions.insert(d->m_rootEventName, listedRootEvent);
797 *listedRootEvent = d->m_qmlRootEvent;
799 // compute percentages
800 foreach (QmlEvent *binding, d->m_eventDescriptions.values()) {
801 binding->percentOfTime = binding->duration * 100.0 / totalTime;
802 binding->timePerCall = binding->calls > 0 ?
803 double(binding->duration) / binding->calls : 0;
806 // compute median time
807 QHash < QmlEvent* , QList<qint64> > durationLists;
808 for (index = fromIndex; index <= toIndex; index++) {
809 QmlEvent *desc = d->m_startTimeSortedList[index].description;
810 qint64 len = d->m_startTimeSortedList[index].length;
811 durationLists[desc].append(len);
813 QMutableHashIterator < QmlEvent* , QList<qint64> > iter(durationLists);
814 while (iter.hasNext()) {
816 if (!iter.value().isEmpty()) {
818 iter.key()->medianTime = iter.value().at(iter.value().count()/2);
823 void ProfileData::prepareForDisplay()
825 // generate numeric ids
827 foreach (QmlEvent *binding, d->m_eventDescriptions.values()) {
828 binding->eventId = ndx++;
831 // collect type counts
832 foreach (const QmlEventStartTime &eventStartData,
833 d->m_startTimeSortedList) {
834 int typeNumber = eventStartData.description->eventType;
835 if (!d->m_typeCounts.contains(typeNumber)) {
836 d->m_typeCounts[typeNumber] = new QmlEventTypeCount;
837 d->m_typeCounts[typeNumber]->nestingCount = 0;
839 if (eventStartData.nestingLevel >
840 d->m_typeCounts[typeNumber]->nestingCount) {
841 d->m_typeCounts[typeNumber]->nestingCount = eventStartData.nestingLevel;
843 if (!d->m_typeCounts[typeNumber]->eventIds.contains(
844 eventStartData.description->eventId))
845 d->m_typeCounts[typeNumber]->eventIds << eventStartData.description->eventId;
849 void ProfileData::sortStartTimes()
851 if (d->m_startTimeSortedList.count() < 2)
854 // assuming startTimes is partially sorted
855 // identify blocks of events and sort them with quicksort
856 QList<QmlEventStartTime>::iterator itFrom =
857 d->m_startTimeSortedList.end() - 2;
858 QList<QmlEventStartTime>::iterator itTo =
859 d->m_startTimeSortedList.end() - 1;
861 while (itFrom != d->m_startTimeSortedList.begin() &&
862 itTo != d->m_startTimeSortedList.begin()) {
863 // find block to sort
864 while ( itFrom != d->m_startTimeSortedList.begin()
865 && itTo->startTime > itFrom->startTime ) {
870 // if we're at the end of the list
871 if (itFrom == d->m_startTimeSortedList.begin())
875 while ( itFrom != d->m_startTimeSortedList.begin()
876 && itTo->startTime <= itFrom->startTime )
879 if (itTo->startTime <= itFrom->startTime)
880 qSort(itFrom, itTo + 1, compareStartTimes);
882 qSort(itFrom + 1, itTo + 1, compareStartTimes);
884 // move to next block
889 // link back the endTimes
890 for (int i = 0; i < d->m_startTimeSortedList.length(); i++)
891 d->m_endTimeSortedList[d->m_startTimeSortedList[i].endTimeIndex].startTimeIndex = i;
894 void ProfileData::sortEndTimes()
896 // assuming endTimes is partially sorted
897 // identify blocks of events and sort them with quicksort
899 if (d->m_endTimeSortedList.count() < 2)
902 QList<QmlEventEndTime>::iterator itFrom =
903 d->m_endTimeSortedList.begin();
904 QList<QmlEventEndTime>::iterator itTo =
905 d->m_endTimeSortedList.begin() + 1;
907 while (itTo != d->m_endTimeSortedList.end() &&
908 itFrom != d->m_endTimeSortedList.end()) {
909 // find block to sort
910 while ( itTo != d->m_endTimeSortedList.end()
911 && d->m_startTimeSortedList[itTo->startTimeIndex].startTime >
912 d->m_startTimeSortedList[itFrom->startTimeIndex].startTime +
913 d->m_startTimeSortedList[itFrom->startTimeIndex].length ) {
918 // if we're at the end of the list
919 if (itTo == d->m_endTimeSortedList.end())
923 while ( itTo != d->m_endTimeSortedList.end()
924 && d->m_startTimeSortedList[itTo->startTimeIndex].startTime <=
925 d->m_startTimeSortedList[itFrom->startTimeIndex].startTime +
926 d->m_startTimeSortedList[itFrom->startTimeIndex].length )
930 qSort(itFrom, itTo, compareEndTimes);
932 // move to next block
938 // link back the startTimes
939 for (int i = 0; i < d->m_endTimeSortedList.length(); i++)
940 d->m_startTimeSortedList[d->m_endTimeSortedList[i].startTimeIndex].endTimeIndex = i;
943 void ProfileData::findAnimationLimits()
945 d->m_maximumAnimationCount = 0;
946 d->m_minimumAnimationCount = 0;
947 d->m_lastFrameEvent = 0;
949 for (int i = 0; i < d->m_startTimeSortedList.count(); i++) {
950 if (d->m_startTimeSortedList[i].description->eventType ==
951 QQmlProfilerService::Painting &&
952 d->m_startTimeSortedList[i].animationCount >= 0) {
953 int animationcount = d->m_startTimeSortedList[i].animationCount;
954 if (d->m_lastFrameEvent) {
955 if (animationcount > d->m_maximumAnimationCount)
956 d->m_maximumAnimationCount = animationcount;
957 if (animationcount < d->m_minimumAnimationCount)
958 d->m_minimumAnimationCount = animationcount;
960 d->m_maximumAnimationCount = animationcount;
961 d->m_minimumAnimationCount = animationcount;
963 d->m_lastFrameEvent = &d->m_startTimeSortedList[i];
968 void ProfileData::computeNestingLevels()
971 QHash <int, qint64> endtimesPerLevel;
972 QList <int> nestingLevels;
973 QList < QHash <int, qint64> > endtimesPerNestingLevel;
974 int level = MIN_LEVEL;
975 endtimesPerLevel[MIN_LEVEL] = 0;
977 for (int i = 0; i < QQmlProfilerService::MaximumRangeType; i++) {
978 nestingLevels << MIN_LEVEL;
979 QHash <int, qint64> dummyHash;
980 dummyHash[MIN_LEVEL] = 0;
981 endtimesPerNestingLevel << dummyHash;
984 for (int i=0; i<d->m_startTimeSortedList.count(); i++) {
985 qint64 st = d->m_startTimeSortedList[i].startTime;
986 int type = d->m_startTimeSortedList[i].description->eventType;
988 if (type == QQmlProfilerService::Painting) {
989 // animation/paint events have level 1 by definition,
990 // but are not considered parents of other events for
991 // statistical purposes
992 d->m_startTimeSortedList[i].level = MIN_LEVEL;
993 d->m_startTimeSortedList[i].nestingLevel = MIN_LEVEL;
998 if (endtimesPerLevel[level] > st) {
1001 while (level > MIN_LEVEL && endtimesPerLevel[level-1] <= st)
1004 endtimesPerLevel[level] = st + d->m_startTimeSortedList[i].length;
1007 if (endtimesPerNestingLevel[type][nestingLevels[type]] > st) {
1008 nestingLevels[type]++;
1010 while (nestingLevels[type] > MIN_LEVEL &&
1011 endtimesPerNestingLevel[type][nestingLevels[type]-1] <= st)
1012 nestingLevels[type]--;
1014 endtimesPerNestingLevel[type][nestingLevels[type]] = st +
1015 d->m_startTimeSortedList[i].length;
1017 d->m_startTimeSortedList[i].level = level;
1018 d->m_startTimeSortedList[i].nestingLevel = nestingLevels[type];
1020 if (level == MIN_LEVEL) {
1021 d->m_qmlMeasuredTime += d->m_startTimeSortedList[i].length;
1026 void ProfileData::computeNestingDepth()
1028 QHash <int, int> nestingDepth;
1029 for (int i = 0; i < d->m_endTimeSortedList.count(); i++) {
1030 int type = d->m_endTimeSortedList[i].description->eventType;
1031 int nestingInType = d->m_startTimeSortedList[d->m_endTimeSortedList[i].startTimeIndex].nestingLevel;
1032 if (!nestingDepth.contains(type))
1033 nestingDepth[type] = nestingInType;
1035 int nd = nestingDepth[type];
1036 nestingDepth[type] = nd > nestingInType ? nd : nestingInType;
1039 d->m_startTimeSortedList[d->m_endTimeSortedList[i].startTimeIndex].nestingDepth
1040 = nestingDepth[type];
1041 if (nestingInType == MIN_LEVEL)
1042 nestingDepth[type] = MIN_LEVEL;
1046 void ProfileData::postProcess()
1051 findAnimationLimits();
1055 compileStatistics(traceStartTime(), traceEndTime());
1056 prepareForDisplay();
1058 // data is ready even when there's no data
1062 void ProfileData::linkEndsToStarts()
1064 for (int i = 0; i < d->m_startTimeSortedList.count(); i++)
1065 d->m_endTimeSortedList[d->m_startTimeSortedList[i].endTimeIndex].startTimeIndex = i;
1068 void ProfileData::computeLevels()
1070 computeNestingLevels();
1071 computeNestingDepth();
1074 void ProfileData::reloadDetails()
1076 // request binding/signal details from the AST
1077 foreach (QmlEvent *event, d->m_eventDescriptions.values()) {
1078 if (event->eventType != QQmlProfilerService::Binding &&
1079 event->eventType != QQmlProfilerService::HandlingSignal)
1082 // This skips anonymous bindings in Qt4.8 (we don't have valid location data for them)
1083 if (event->location.filename.isEmpty())
1086 // Skip non-anonymous bindings from Qt4.8 (we already have correct details for them)
1087 if (event->location.column == -1)
1090 emit requestDetailsForLocation(event->eventType, event->location);
1092 emit reloadDocumentsForDetails();
1095 void ProfileData::findBindingLoops(qint64 startTime, qint64 endTime)
1097 // first clear existing data
1098 foreach (QmlEvent *event, d->m_eventDescriptions.values()) {
1099 event->isBindingLoop = false;
1100 foreach (QmlEventSub *parentEvent, event->parentHash.values())
1101 parentEvent->inLoopPath = false;
1102 foreach (QmlEventSub *childEvent, event->childrenHash.values())
1103 childEvent->inLoopPath = false;
1106 QList <QmlEvent *> stackRefs;
1107 QList <QmlEventStartTime *> stack;
1108 int fromIndex = findFirstIndex(startTime);
1109 int toIndex = findLastIndex(endTime);
1111 for (int i = 0; i < d->m_startTimeSortedList.count(); i++) {
1112 QmlEvent *currentEvent = d->m_startTimeSortedList[i].description;
1113 QmlEventStartTime *inTimeEvent = &d->m_startTimeSortedList[i];
1114 inTimeEvent->bindingLoopHead = -1;
1116 // managing call stack
1117 for (int j = stack.count() - 1; j >= 0; j--) {
1118 if (stack[j]->startTime + stack[j]->length <= inTimeEvent->startTime) {
1120 stackRefs.removeAt(j);
1124 bool loopDetected = stackRefs.contains(currentEvent);
1125 stack << inTimeEvent;
1126 stackRefs << currentEvent;
1129 if (i >= fromIndex && i <= toIndex) {
1130 // for the statistics
1131 currentEvent->isBindingLoop = true;
1132 for (int j = stackRefs.indexOf(currentEvent); j < stackRefs.count()-1; j++) {
1133 QmlEventSub *nextEventSub = stackRefs[j]->childrenHash.value(stackRefs[j+1]->eventHashStr);
1134 nextEventSub->inLoopPath = true;
1135 QmlEventSub *prevEventSub = stackRefs[j+1]->parentHash.value(stackRefs[j]->eventHashStr);
1136 prevEventSub->inLoopPath = true;
1140 // use crossed references to find index in starttimesortedlist
1141 QmlEventStartTime *head = stack[stackRefs.indexOf(currentEvent)];
1142 inTimeEvent->bindingLoopHead = d->m_endTimeSortedList[head->endTimeIndex].startTimeIndex;
1143 d->m_startTimeSortedList[inTimeEvent->bindingLoopHead].bindingLoopHead = i;
1148 void ProfileData::rewriteDetailsString(
1149 QQmlProfilerService::RangeType eventType,
1150 const EventLocation &location, const QString &newString)
1152 QString eventHashStr = getHashStringForQmlEvent(location,
1154 Q_ASSERT(d->m_eventDescriptions.contains(eventHashStr));
1155 d->m_eventDescriptions.value(eventHashStr)->details = newString;
1156 emit detailsChanged(d->m_eventDescriptions.value(eventHashStr)->eventId,
1160 void ProfileData::finishedRewritingDetails()
1162 emit reloadDetailLabels();
1165 // get list of events between A and B:
1166 // find fist event with endtime after A -> aa
1167 // find last event with starttime before B -> bb
1168 // list is from parent of aa with level=0 to bb, in the "sorted by starttime" list
1169 int ProfileData::findFirstIndex(qint64 startTime) const
1172 // in the "endtime" list, find the first event that ends after startTime
1173 if (d->m_endTimeSortedList.isEmpty())
1175 if (d->m_endTimeSortedList.length() == 1 ||
1176 d->m_endTimeSortedList.first().endTime >= startTime)
1179 if (d->m_endTimeSortedList.last().endTime <= startTime)
1182 if (candidate == -1)
1185 int toIndex = d->m_endTimeSortedList.count()-1;
1186 while (toIndex - fromIndex > 1) {
1187 int midIndex = (fromIndex + toIndex)/2;
1188 if (d->m_endTimeSortedList[midIndex].endTime < startTime)
1189 fromIndex = midIndex;
1194 candidate = toIndex;
1197 int ndx = d->m_endTimeSortedList[candidate].startTimeIndex;
1199 // and then go to the parent
1200 while (d->m_startTimeSortedList[ndx].level != MIN_LEVEL && ndx > 0)
1206 int ProfileData::findFirstIndexNoParents(qint64 startTime) const
1209 // in the "endtime" list, find the first event that ends after startTime
1210 if (d->m_endTimeSortedList.isEmpty())
1212 if (d->m_endTimeSortedList.length() == 1 ||
1213 d->m_endTimeSortedList.first().endTime >= startTime)
1216 if (d->m_endTimeSortedList.last().endTime <= startTime)
1219 if (candidate == -1) {
1221 int toIndex = d->m_endTimeSortedList.count()-1;
1222 while (toIndex - fromIndex > 1) {
1223 int midIndex = (fromIndex + toIndex)/2;
1224 if (d->m_endTimeSortedList[midIndex].endTime < startTime)
1225 fromIndex = midIndex;
1230 candidate = toIndex;
1233 int ndx = d->m_endTimeSortedList[candidate].startTimeIndex;
1238 int ProfileData::findLastIndex(qint64 endTime) const
1240 // in the "starttime" list, find the last event that starts before endtime
1241 if (d->m_startTimeSortedList.isEmpty())
1243 if (d->m_startTimeSortedList.first().startTime >= endTime)
1245 if (d->m_startTimeSortedList.length() == 1)
1247 if (d->m_startTimeSortedList.last().startTime <= endTime)
1248 return d->m_startTimeSortedList.count()-1;
1251 int toIndex = d->m_startTimeSortedList.count()-1;
1252 while (toIndex - fromIndex > 1) {
1253 int midIndex = (fromIndex + toIndex)/2;
1254 if (d->m_startTimeSortedList[midIndex].startTime < endTime)
1255 fromIndex = midIndex;
1263 qint64 ProfileData::firstTimeMark() const
1265 if (d->m_startTimeSortedList.isEmpty())
1268 return d->m_startTimeSortedList[0].startTime;
1272 qint64 ProfileData::lastTimeMark() const
1274 if (d->m_endTimeSortedList.isEmpty())
1277 return d->m_endTimeSortedList.last().endTime;
1281 qint64 ProfileData::traceStartTime() const
1283 return d->m_traceStartTime != -1? d->m_traceStartTime : firstTimeMark();
1286 qint64 ProfileData::traceEndTime() const
1288 return d->m_traceEndTime ? d->m_traceEndTime : lastTimeMark();
1291 qint64 ProfileData::traceDuration() const
1293 return traceEndTime() - traceStartTime();
1296 qint64 ProfileData::qmlMeasuredTime() const
1298 return d->m_qmlMeasuredTime;
1300 qint64 ProfileData::v8MeasuredTime() const
1302 return d->m_v8MeasuredTime;
1305 int ProfileData::count() const
1307 return d->m_startTimeSortedList.count();
1310 ////////////////////////////////////////////////////////////////////////////////
1313 bool ProfileData::save(const QString &filename)
1316 emit error(tr("No data to save"));
1320 QFile file(filename);
1321 if (!file.open(QIODevice::WriteOnly)) {
1322 emit error(tr("Could not open %1 for writing").arg(filename));
1326 QXmlStreamWriter stream(&file);
1327 stream.setAutoFormatting(true);
1328 stream.writeStartDocument();
1330 stream.writeStartElement("trace");
1331 stream.writeAttribute("version", PROFILER_FILE_VERSION);
1333 stream.writeAttribute("traceStart", QString::number(traceStartTime()));
1334 stream.writeAttribute("traceEnd", QString::number(traceEndTime()));
1336 stream.writeStartElement("eventData");
1337 stream.writeAttribute("totalTime", QString::number(d->m_qmlMeasuredTime));
1339 foreach (const QmlEvent *eventData, d->m_eventDescriptions.values()) {
1340 stream.writeStartElement("event");
1341 stream.writeAttribute("index",
1343 d->m_eventDescriptions.keys().indexOf(
1344 eventData->eventHashStr)));
1345 stream.writeTextElement("displayname", eventData->displayname);
1346 stream.writeTextElement("type", qmlEventType(eventData->eventType));
1347 if (!eventData->location.filename.isEmpty()) {
1348 stream.writeTextElement("filename", eventData->location.filename);
1349 stream.writeTextElement("line",
1350 QString::number(eventData->location.line));
1351 stream.writeTextElement("column",
1352 QString::number(eventData->location.column));
1354 stream.writeTextElement("details", eventData->details);
1355 stream.writeEndElement();
1357 stream.writeEndElement(); // eventData
1359 stream.writeStartElement("eventList");
1360 foreach (const QmlEventStartTime &rangedEvent,
1361 d->m_startTimeSortedList) {
1362 stream.writeStartElement("range");
1363 stream.writeAttribute("startTime", QString::number(rangedEvent.startTime));
1364 stream.writeAttribute("duration", QString::number(rangedEvent.length));
1365 stream.writeAttribute("eventIndex",
1366 QString::number(d->m_eventDescriptions.keys().indexOf(
1367 rangedEvent.description->eventHashStr)));
1368 if (rangedEvent.description->eventType ==
1369 QQmlProfilerService::Painting && rangedEvent.animationCount >= 0) {
1371 stream.writeAttribute("framerate",
1372 QString::number(rangedEvent.frameRate));
1373 stream.writeAttribute("animationcount",
1374 QString::number(rangedEvent.animationCount));
1376 stream.writeEndElement();
1378 stream.writeEndElement(); // eventList
1380 stream.writeStartElement("v8profile"); // v8 profiler output
1381 stream.writeAttribute("totalTime", QString::number(d->m_v8MeasuredTime));
1382 foreach (V8Event *v8event, d->m_v8EventList) {
1383 stream.writeStartElement("event");
1384 stream.writeAttribute("index",
1385 QString::number(d->m_v8EventList.indexOf(v8event)));
1386 stream.writeTextElement("displayname", v8event->displayName);
1387 stream.writeTextElement("functionname", v8event->functionName);
1388 if (!v8event->filename.isEmpty()) {
1389 stream.writeTextElement("filename", v8event->filename);
1390 stream.writeTextElement("line", QString::number(v8event->line));
1392 stream.writeTextElement("totalTime",
1393 QString::number(v8event->totalTime));
1394 stream.writeTextElement("selfTime", QString::number(v8event->selfTime));
1395 if (!v8event->childrenHash.isEmpty()) {
1396 stream.writeStartElement("childrenEvents");
1397 QStringList childrenIndexes;
1398 QStringList childrenTimes;
1399 QStringList parentTimes;
1400 foreach (V8EventSub *v8child, v8event->childrenHash.values()) {
1401 childrenIndexes << QString::number(v8child->reference->eventId);
1402 childrenTimes << QString::number(v8child->totalTime);
1403 parentTimes << QString::number(
1404 d->m_v8EventList[v8child->reference->eventId]->
1405 parentHash[v8event->displayName]->totalTime);
1408 stream.writeAttribute("list", childrenIndexes.join(QString(", ")));
1409 stream.writeAttribute("childrenTimes",
1410 childrenTimes.join(QString(", ")));
1411 stream.writeAttribute("parentTimes",
1412 parentTimes.join(QString(", ")));
1413 stream.writeEndElement();
1415 stream.writeEndElement();
1417 stream.writeEndElement(); // v8 profiler output
1419 stream.writeEndElement(); // trace
1420 stream.writeEndDocument();
1426 void ProfileData::setFilename(const QString &filename)
1428 d->m_filename = filename;
1431 void ProfileData::load(const QString &filename)
1433 setFilename(filename);
1437 // "be strict in your output but tolerant in your inputs"
1438 void ProfileData::load()
1440 QString filename = d->m_filename;
1442 QFile file(filename);
1444 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
1445 emit error(tr("Could not open %1 for reading").arg(filename));
1449 emit processingData();
1454 bool readingQmlEvents = false;
1455 bool readingV8Events = false;
1456 QHash <int, QmlEvent *> descriptionBuffer;
1457 QmlEvent *currentEvent = 0;
1458 QHash <int, V8Event *> v8eventBuffer;
1459 QHash <int, QString> childrenIndexes;
1460 QHash <int, QString> childrenTimes;
1461 QHash <int, QString> parentTimes;
1462 V8Event *v8event = 0;
1463 bool startTimesAreSorted = true;
1464 bool validVersion = true;
1467 d->m_v8MeasuredTime = 0;
1468 d->m_qmlMeasuredTime = 0;
1469 double cumulatedV8Time = 0;
1471 QXmlStreamReader stream(&file);
1473 while (validVersion && !stream.atEnd() && !stream.hasError()) {
1474 QXmlStreamReader::TokenType token = stream.readNext();
1475 QString elementName = stream.name().toString();
1477 case QXmlStreamReader::StartDocument : continue;
1478 case QXmlStreamReader::StartElement : {
1479 if (elementName == "trace") {
1480 QXmlStreamAttributes attributes = stream.attributes();
1481 if (attributes.hasAttribute("version"))
1483 attributes.value("version").toString() ==
1484 PROFILER_FILE_VERSION;
1486 validVersion = false;
1487 if (attributes.hasAttribute("traceStart"))
1488 setTraceStartTime(attributes.value("traceStart").
1489 toString().toLongLong());
1490 if (attributes.hasAttribute("traceEnd"))
1491 setTraceEndTime(attributes.value("traceEnd").
1492 toString().toLongLong());
1494 if (elementName == "eventData" && !readingV8Events) {
1495 readingQmlEvents = true;
1496 QXmlStreamAttributes attributes = stream.attributes();
1497 if (attributes.hasAttribute("totalTime"))
1498 d->m_qmlMeasuredTime = attributes.value("totalTime").
1499 toString().toDouble();
1502 if (elementName == "v8profile" && !readingQmlEvents) {
1503 readingV8Events = true;
1504 QXmlStreamAttributes attributes = stream.attributes();
1505 if (attributes.hasAttribute("totalTime"))
1506 d->m_v8MeasuredTime = attributes.value("totalTime").
1507 toString().toDouble();
1511 if (elementName == "trace") {
1512 QXmlStreamAttributes attributes = stream.attributes();
1513 if (attributes.hasAttribute("traceStart"))
1514 setTraceStartTime(attributes.value("traceStart").
1515 toString().toLongLong());
1516 if (attributes.hasAttribute("traceEnd"))
1517 setTraceEndTime(attributes.value("traceEnd").
1518 toString().toLongLong());
1521 if (elementName == "range") {
1522 QmlEventStartTime rangedEvent;
1523 QXmlStreamAttributes attributes = stream.attributes();
1524 if (attributes.hasAttribute("startTime"))
1525 rangedEvent.startTime = attributes.value("startTime").
1526 toString().toLongLong();
1527 if (attributes.hasAttribute("duration"))
1528 rangedEvent.length = attributes.value("duration").
1529 toString().toLongLong();
1530 if (attributes.hasAttribute("framerate"))
1531 rangedEvent.frameRate = attributes.value("framerate").
1533 if (attributes.hasAttribute("animationcount"))
1534 rangedEvent.animationCount = attributes.value("animationcount").
1537 rangedEvent.animationCount = -1;
1538 if (attributes.hasAttribute("eventIndex")) {
1539 int ndx = attributes.value("eventIndex").toString().toInt();
1540 if (!descriptionBuffer.value(ndx))
1541 descriptionBuffer[ndx] = new QmlEvent;
1542 rangedEvent.description = descriptionBuffer.value(ndx);
1544 rangedEvent.endTimeIndex = d->m_endTimeSortedList.length();
1546 if (!d->m_startTimeSortedList.isEmpty()
1547 && rangedEvent.startTime <
1548 d->m_startTimeSortedList.last().startTime)
1549 startTimesAreSorted = false;
1550 d->m_startTimeSortedList << rangedEvent;
1552 QmlEventEndTime endTimeEvent;
1553 endTimeEvent.endTime = rangedEvent.startTime + rangedEvent.length;
1554 endTimeEvent.startTimeIndex = d->m_startTimeSortedList.length()-1;
1555 endTimeEvent.description = rangedEvent.description;
1556 d->m_endTimeSortedList << endTimeEvent;
1560 if (readingQmlEvents) {
1561 if (elementName == "event") {
1562 QXmlStreamAttributes attributes = stream.attributes();
1563 if (attributes.hasAttribute("index")) {
1564 int ndx = attributes.value("index").toString().toInt();
1565 if (!descriptionBuffer.value(ndx))
1566 descriptionBuffer[ndx] = new QmlEvent;
1567 currentEvent = descriptionBuffer[ndx];
1574 // the remaining are eventdata or v8eventdata elements
1579 if (stream.tokenType() != QXmlStreamReader::Characters)
1581 QString readData = stream.text().toString();
1583 if (elementName == "displayname") {
1584 currentEvent->displayname = readData;
1587 if (elementName == "type") {
1588 currentEvent->eventType = qmlEventType(readData);
1591 if (elementName == "filename") {
1592 currentEvent->location.filename = readData;
1595 if (elementName == "line") {
1596 currentEvent->location.line = readData.toInt();
1599 if (elementName == "column") {
1600 currentEvent->location.column = readData.toInt();
1602 if (elementName == "details") {
1603 currentEvent->details = readData;
1608 if (readingV8Events) {
1609 if (elementName == "event") {
1610 QXmlStreamAttributes attributes = stream.attributes();
1611 if (attributes.hasAttribute("index")) {
1612 int ndx = attributes.value("index").toString().toInt();
1613 if (!v8eventBuffer.value(ndx))
1614 v8eventBuffer[ndx] = new V8Event;
1615 v8event = v8eventBuffer[ndx];
1622 // the remaining are eventdata or v8eventdata elements
1626 if (elementName == "childrenEvents") {
1627 QXmlStreamAttributes attributes = stream.attributes();
1628 int eventIndex = v8eventBuffer.key(v8event);
1629 if (attributes.hasAttribute("list")) {
1630 // store for later parsing (we haven't read all the events yet)
1631 childrenIndexes[eventIndex] =
1632 attributes.value("list").toString();
1634 if (attributes.hasAttribute("childrenTimes")) {
1635 childrenTimes[eventIndex] =
1636 attributes.value("childrenTimes").toString();
1638 if (attributes.hasAttribute("parentTimes")) {
1639 parentTimes[eventIndex] =
1640 attributes.value("parentTimes").toString();
1645 if (stream.tokenType() != QXmlStreamReader::Characters)
1647 QString readData = stream.text().toString();
1649 if (elementName == "displayname") {
1650 v8event->displayName = readData;
1654 if (elementName == "functionname") {
1655 v8event->functionName = readData;
1659 if (elementName == "filename") {
1660 v8event->filename = readData;
1664 if (elementName == "line") {
1665 v8event->line = readData.toInt();
1669 if (elementName == "totalTime") {
1670 v8event->totalTime = readData.toDouble();
1671 cumulatedV8Time += v8event->totalTime;
1675 if (elementName == "selfTime") {
1676 v8event->selfTime = readData.toDouble();
1683 case QXmlStreamReader::EndElement : {
1684 if (elementName == "event") {
1688 if (elementName == "eventData") {
1689 readingQmlEvents = false;
1692 if (elementName == "v8profile") {
1693 readingV8Events = false;
1702 if (stream.hasError()) {
1703 emit error(tr("Error while parsing %1").arg(filename));
1710 if (!validVersion) {
1712 emit countChanged();
1714 emit error(tr("Invalid version of QML Trace file."));
1718 // backwards compatibility
1719 if (d->m_v8MeasuredTime == 0)
1720 d->m_v8MeasuredTime = cumulatedV8Time;
1722 // move the buffered data to the details cache
1723 foreach (QmlEvent *desc, descriptionBuffer.values()) {
1724 desc->eventHashStr = getHashStringForQmlEvent(
1725 desc->location, desc->eventType);;
1726 d->m_eventDescriptions[desc->eventHashStr] = desc;
1729 // sort startTimeSortedList
1730 if (!startTimesAreSorted) {
1731 qSort(d->m_startTimeSortedList.begin(),
1732 d->m_startTimeSortedList.end(), compareStartTimes);
1733 for (int i = 0; i< d->m_startTimeSortedList.length(); i++) {
1734 QmlEventStartTime startTimeData = d->m_startTimeSortedList[i];
1735 d->m_endTimeSortedList[startTimeData.endTimeIndex].startTimeIndex = i;
1737 qSort(d->m_endTimeSortedList.begin(),
1738 d->m_endTimeSortedList.end(), compareStartIndexes);
1741 // find v8events' children and parents
1742 foreach (int parentIndex, childrenIndexes.keys()) {
1743 QStringList childrenStrings =
1744 childrenIndexes.value(parentIndex).split(",");
1745 QStringList childrenTimesStrings =
1746 childrenTimes.value(parentIndex).split(", ");
1747 QStringList parentTimesStrings =
1748 parentTimes.value(parentIndex).split(", ");
1749 for (int ndx = 0; ndx < childrenStrings.count(); ndx++) {
1750 int childIndex = childrenStrings[ndx].toInt();
1751 if (v8eventBuffer.value(childIndex)) {
1752 V8EventSub *newChild = new V8EventSub(v8eventBuffer[childIndex]);
1753 V8EventSub *newParent = new V8EventSub(v8eventBuffer[parentIndex]);
1754 if (childrenTimesStrings.count() > ndx)
1755 newChild->totalTime = childrenTimesStrings[ndx].toDouble();
1756 if (parentTimesStrings.count() > ndx)
1757 newParent->totalTime = parentTimesStrings[ndx].toDouble();
1758 v8eventBuffer[parentIndex]->childrenHash.insert(
1759 newChild->reference->displayName, newChild);
1760 v8eventBuffer[childIndex]->parentHash.insert(
1761 newParent->reference->displayName, newParent);
1766 d->m_v8EventList = v8eventBuffer.values();
1768 emit countChanged();
1770 descriptionBuffer.clear();
1772 emit postProcessing();
1773 d->collectV8Statistics();
1777 ///////////////////////////////////////////////
1778 qint64 ProfileData::getStartTime(int index) const
1780 return d->m_startTimeSortedList[index].startTime;
1783 qint64 ProfileData::getEndTime(int index) const
1785 return d->m_startTimeSortedList[index].startTime +
1786 d->m_startTimeSortedList[index].length;
1789 qint64 ProfileData::getDuration(int index) const
1791 return d->m_startTimeSortedList[index].length;
1794 int ProfileData::getType(int index) const
1796 return d->m_startTimeSortedList[index].description->eventType;
1799 int ProfileData::getNestingLevel(int index) const
1801 return d->m_startTimeSortedList[index].nestingLevel;
1804 int ProfileData::getNestingDepth(int index) const
1806 return d->m_startTimeSortedList[index].nestingDepth;
1809 QString ProfileData::getFilename(int index) const
1811 return d->m_startTimeSortedList[index].description->location.filename;
1814 int ProfileData::getLine(int index) const
1816 return d->m_startTimeSortedList[index].description->location.line;
1819 int ProfileData::getColumn(int index) const
1821 return d->m_startTimeSortedList[index].description->location.column;
1824 QString ProfileData::getDetails(int index) const
1826 // special: animations
1827 if (d->m_startTimeSortedList[index].description->eventType ==
1828 QQmlProfilerService::Painting &&
1829 d->m_startTimeSortedList[index].animationCount >= 0)
1830 return tr("%1 animations at %2 FPS").arg(
1831 QString::number(d->m_startTimeSortedList[index].animationCount),
1832 QString::number(d->m_startTimeSortedList[index].frameRate));
1833 return d->m_startTimeSortedList[index].description->details;
1836 int ProfileData::getEventId(int index) const {
1837 return d->m_startTimeSortedList[index].description->eventId;
1840 int ProfileData::getFramerate(int index) const
1842 return d->m_startTimeSortedList[index].frameRate;
1845 int ProfileData::getAnimationCount(int index) const
1847 return d->m_startTimeSortedList[index].animationCount;
1850 int ProfileData::getMaximumAnimationCount() const
1852 return d->m_maximumAnimationCount;
1855 int ProfileData::getMinimumAnimationCount() const
1857 return d->m_minimumAnimationCount;
1860 int ProfileData::uniqueEventsOfType(int type) const
1862 if (!d->m_typeCounts.contains(type))
1864 return d->m_typeCounts[type]->eventIds.count();
1867 int ProfileData::maxNestingForType(int type) const
1869 if (!d->m_typeCounts.contains(type))
1871 return d->m_typeCounts[type]->nestingCount;
1874 QString ProfileData::eventTextForType(int type, int index) const
1876 if (!d->m_typeCounts.contains(type))
1878 return d->m_eventDescriptions.values().at(
1879 d->m_typeCounts[type]->eventIds[index])->details;
1882 QString ProfileData::eventDisplayNameForType(int type, int index) const
1884 if (!d->m_typeCounts.contains(type))
1886 return d->m_eventDescriptions.values().at(
1887 d->m_typeCounts[type]->eventIds[index])->displayname;
1890 int ProfileData::eventIdForType(int type, int index) const
1892 if (!d->m_typeCounts.contains(type))
1894 return d->m_typeCounts[type]->eventIds[index];
1897 int ProfileData::eventPosInType(int index) const
1899 int eventType = d->m_startTimeSortedList[index].description->eventType;
1900 return d->m_typeCounts[eventType]->eventIds.indexOf(
1901 d->m_startTimeSortedList[index].description->eventId);