diff --git a/src/libs/qmljsdebugclient/qmlprofilereventlist.cpp b/src/libs/qmljsdebugclient/qmlprofilereventlist.cpp index 6146f69907e..56dbd4e5641 100644 --- a/src/libs/qmljsdebugclient/qmlprofilereventlist.cpp +++ b/src/libs/qmljsdebugclient/qmlprofilereventlist.cpp @@ -54,10 +54,115 @@ const char *const TYPE_COMPILING_STR = "Compiling"; const char *const TYPE_CREATING_STR = "Creating"; const char *const TYPE_BINDING_STR = "Binding"; const char *const TYPE_HANDLINGSIGNAL_STR = "HandlingSignal"; +const char *const PROFILER_FILE_VERSION = "1.01"; } #define MIN_LEVEL 1 +QmlEventData::QmlEventData() +{ + line = -1; + eventType = MaximumQmlEventType; + eventId = -1; + duration = 0; + calls = 0; + minTime = 0; + maxTime = 0; + timePerCall = 0; + percentOfTime = 0; + medianTime = 0; +} + +QmlEventData::~QmlEventData() +{ + qDeleteAll(parentHash.values()); + parentHash.clear(); + qDeleteAll(childrenHash.values()); + childrenHash.clear(); +} + +QmlEventData &QmlEventData::operator=(const QmlEventData &ref) +{ + if (this == &ref) + return *this; + + displayname = ref.displayname; + filename = ref.filename; + eventHashStr = ref.eventHashStr; + details = ref.details; + line = ref.line; + 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; + + qDeleteAll(parentHash.values()); + parentHash.clear(); + foreach (const QString &key, ref.parentHash.keys()) { + parentHash.insert(key, new QmlEventSub(ref.parentHash.value(key))); + } + + qDeleteAll(childrenHash.values()); + childrenHash.clear(); + foreach (const QString &key, ref.childrenHash.keys()) { + childrenHash.insert(key, new QmlEventSub(ref.childrenHash.value(key))); + } + + return *this; +} + +QV8EventData::QV8EventData() +{ + line = -1; + eventId = -1; + totalTime = 0; + selfTime = 0; + totalPercent = 0; + selfPercent = 0; +} + +QV8EventData::~QV8EventData() +{ + qDeleteAll(parentHash.values()); + parentHash.clear(); + qDeleteAll(childrenHash.values()); + childrenHash.clear(); +} + +QV8EventData &QV8EventData::operator=(const QV8EventData &ref) +{ + if (this == &ref) + return *this; + + displayName = ref.displayName; + filename = ref.filename; + functionName = ref.functionName; + line = ref.line; + totalTime = ref.totalTime; + totalPercent = ref.totalPercent; + selfTime = ref.selfTime; + selfPercent = ref.selfPercent; + eventId = ref.eventId; + + qDeleteAll(parentHash.values()); + parentHash.clear(); + foreach (const QString &key, ref.parentHash.keys()) { + parentHash.insert(key, new QV8EventSub(ref.parentHash.value(key))); + } + + qDeleteAll(childrenHash.values()); + childrenHash.clear(); + foreach (const QString &key, ref.childrenHash.keys()) { + childrenHash.insert(key, new QV8EventSub(ref.childrenHash.value(key))); + } + return *this; +} + // endtimedata struct QmlEventEndTimeData { qint64 endTime; @@ -151,6 +256,10 @@ public: QmlProfilerEventList *q; + // convenience functions + void clearQmlRootEvent(); + void clearV8RootEvent(); + // Stored data QmlEventHash m_eventDescriptions; QList m_endTimeSortedList; @@ -160,6 +269,11 @@ public: QV8EventDescriptions m_v8EventList; QHash m_v8parents; + QmlEventData m_qmlRootEvent; + QV8EventData m_v8RootEvent; + QString m_rootEventName; + QString m_rootEventDesc; + QHash m_typeCounts; qint64 m_traceEndTime; @@ -184,6 +298,11 @@ QmlProfilerEventList::QmlProfilerEventList(QObject *parent) : 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(); + } QmlProfilerEventList::~QmlProfilerEventList() @@ -193,17 +312,20 @@ QmlProfilerEventList::~QmlProfilerEventList() void QmlProfilerEventList::clear() { - foreach (QmlEventData *binding, d->m_eventDescriptions.values()) - delete binding; + 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(); - qDeleteAll(d->m_v8EventList); - d->m_v8EventList.clear(); d->m_v8parents.clear(); + d->clearQmlRootEvent(); + d->clearV8RootEvent(); + foreach (QmlEventTypeCount *typeCount, d->m_typeCounts.values()) delete typeCount; d->m_typeCounts.clear(); @@ -249,46 +371,46 @@ void QmlProfilerEventList::addRangedEvent(int type, qint64 startTime, qint64 len const QStringList &data, const QString &fileName, int line) { const QChar colon = QLatin1Char(':'); - QString displayName, location, details; + QString displayName, eventHashStr, details; 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); + } + + // generate hash if (fileName.isEmpty()) { displayName = tr(""); - location = QString("--:%1:%2").arg(QString::number(type), data.join(" ")); + eventHashStr = QString("--:%1:%2").arg(QString::number(type), details); } else { const QString filePath = QUrl(fileName).path(); displayName = filePath.mid(filePath.lastIndexOf(QChar('/')) + 1) + colon + QString::number(line); - location = fileName+colon+QString::number(line); + eventHashStr = QString("%1:%2:%3:%4").arg(fileName, QString::number(line), QString::number(type), details); } QmlEventData *newEvent; - if (d->m_eventDescriptions.contains(location)) { - newEvent = d->m_eventDescriptions[location]; + if (d->m_eventDescriptions.contains(eventHashStr)) { + newEvent = d->m_eventDescriptions[eventHashStr]; } else { - - // 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); - } - - newEvent = new QmlEventData(); + newEvent = new QmlEventData; newEvent->displayname = displayName; newEvent->filename = fileName; - newEvent->location = location; + newEvent->eventHashStr = eventHashStr; newEvent->line = line; newEvent->eventType = (QmlJsDebugClient::QmlEventType)type; newEvent->details = details; - d->m_eventDescriptions.insert(location, newEvent); + d->m_eventDescriptions.insert(eventHashStr, newEvent); } QmlEventEndTimeData endTimeData; @@ -311,7 +433,7 @@ void QmlProfilerEventList::addRangedEvent(int type, qint64 startTime, qint64 len void QmlProfilerEventList::addV8Event(int depth, const QString &function, const QString &filename, int lineNumber, double totalTime, double selfTime) { QString displayName = filename.mid(filename.lastIndexOf(QLatin1Char('/')) + 1) + QLatin1Char(':') + QString::number(lineNumber); - QV8EventData *newData = 0; + QV8EventData *eventData = 0; // time is given in milliseconds, but internally we store it in microseconds totalTime *= 1e6; @@ -319,45 +441,65 @@ void QmlProfilerEventList::addV8Event(int depth, const QString &function, const // cumulate information foreach (QV8EventData *v8event, d->m_v8EventList) { - if (v8event->displayName == displayName && v8event->functionName == function) - newData = v8event; - } - - if (!newData) { - newData = new QV8EventData(); - newData->displayName = displayName; - newData->filename = filename; - newData->functionName = function; - newData->line = lineNumber; - newData->totalTime = totalTime; - newData->selfTime = selfTime; - d->m_v8EventList << newData; - } else { - newData->totalTime += totalTime; - newData->selfTime += selfTime; - } - d->m_v8parents[depth] = newData; - - if (depth > 0) { - QV8EventData* parentEvent = d->m_v8parents.value(depth-1); - if (parentEvent) { - if (!newData->parentList.contains(parentEvent)) - newData->parentList << parentEvent; - if (!parentEvent->childrenList.contains(newData)) - parentEvent->childrenList << newData; + if (v8event->displayName == displayName && v8event->functionName == function) { + eventData = v8event; + break; } + } + + if (!eventData) { + eventData = new QV8EventData; + eventData->displayName = displayName; + eventData->filename = filename; + eventData->functionName = function; + eventData->line = lineNumber; + eventData->totalTime = totalTime; + eventData->selfTime = selfTime; + d->m_v8EventList << eventData; } else { + eventData->totalTime += totalTime; + eventData->selfTime += selfTime; + } + d->m_v8parents[depth] = eventData; + + QV8EventData *parentEvent = 0; + if (depth == 0) { + parentEvent = &d->m_v8RootEvent; d->m_v8MeasuredTime += totalTime; } + if (depth > 0 && d->m_v8parents.contains(depth-1)) { + parentEvent = d->m_v8parents.value(depth-1); + } + + if (parentEvent != 0) { + if (!eventData->parentHash.contains(parentEvent->displayName)) { + QV8EventSub *newParentSub = new QV8EventSub(parentEvent); + newParentSub->totalTime = totalTime; + + eventData->parentHash.insert(parentEvent->displayName, newParentSub ); + } else { + QV8EventSub *newParentSub = eventData->parentHash.value(parentEvent->displayName); + newParentSub->totalTime += totalTime; + } + + if (!parentEvent->childrenHash.contains(eventData->displayName)) { + QV8EventSub *newChildSub = new QV8EventSub(eventData); + newChildSub->totalTime = totalTime; + + parentEvent->childrenHash.insert(eventData->displayName, newChildSub); + } else { + QV8EventSub *newChildSub = parentEvent->childrenHash.value(eventData->displayName); + newChildSub->totalTime += totalTime; + } + } } void QmlProfilerEventList::QmlProfilerEventListPrivate::collectV8Statistics() { if (!m_v8EventList.isEmpty()) { - double totalTimes = 0; + double totalTimes = m_v8MeasuredTime; double selfTimes = 0; foreach (QV8EventData *v8event, m_v8EventList) { - totalTimes += v8event->totalTime; selfTimes += v8event->selfTime; } @@ -367,14 +509,37 @@ void QmlProfilerEventList::QmlProfilerEventListPrivate::collectV8Statistics() if (selfTimes == 0) selfTimes = 1; + // insert root event in eventlist + // the +1 ns is to get it on top of the sorted list + m_v8RootEvent.totalTime = m_v8MeasuredTime + 1; + m_v8RootEvent.selfTime = 0; + + int rootEventIndex = -1; + for (int ndx = 0; ndx < m_v8EventList.count(); ndx++) + { + if (m_v8EventList.at(ndx)->displayName == m_rootEventName) { + m_v8RootEvent = *m_v8EventList.at(ndx); + rootEventIndex = ndx; + break; + } + } + if (rootEventIndex == -1) { + rootEventIndex = m_v8EventList.count(); + QV8EventData *newRootEvent = new QV8EventData; + *newRootEvent = m_v8RootEvent; + m_v8EventList << newRootEvent; + } + foreach (QV8EventData *v8event, m_v8EventList) { v8event->totalPercent = v8event->totalTime * 100.0 / totalTimes; v8event->selfPercent = v8event->selfTime * 100.0 / selfTimes; } int index = 0; - foreach (QV8EventData *v8event, m_v8EventList) + foreach (QV8EventData *v8event, m_v8EventList) { v8event->eventId = index++; + } + m_v8RootEvent.eventId = m_v8EventList[rootEventIndex]->eventId; } } @@ -395,11 +560,52 @@ void QmlProfilerEventList::complete() postProcess(); } +void QmlProfilerEventList::QmlProfilerEventListPrivate::clearQmlRootEvent() +{ + m_qmlRootEvent.displayname = m_rootEventName; + m_qmlRootEvent.filename = QString(); + m_qmlRootEvent.eventHashStr = m_rootEventName; + m_qmlRootEvent.details = m_rootEventDesc; + m_qmlRootEvent.line = 01; + m_qmlRootEvent.eventType = QmlJsDebugClient::Binding; + m_qmlRootEvent.duration = 0; + m_qmlRootEvent.calls = 0; + m_qmlRootEvent.minTime = 0; + m_qmlRootEvent.maxTime = 0; + m_qmlRootEvent.timePerCall = 0; + m_qmlRootEvent.percentOfTime = 0; + m_qmlRootEvent.medianTime = 0; + m_qmlRootEvent.eventId = -1; + + qDeleteAll(m_qmlRootEvent.parentHash.values()); + qDeleteAll(m_qmlRootEvent.childrenHash.values()); + m_qmlRootEvent.parentHash.clear(); + m_qmlRootEvent.childrenHash.clear(); +} + +void QmlProfilerEventList::QmlProfilerEventListPrivate::clearV8RootEvent() +{ + m_v8RootEvent.displayName = m_rootEventName; + m_v8RootEvent.functionName = m_rootEventDesc; + m_v8RootEvent.line = -1; + m_v8RootEvent.totalTime = 0; + m_v8RootEvent.totalPercent = 0; + m_v8RootEvent.selfTime = 0; + m_v8RootEvent.selfPercent = 0; + m_v8RootEvent.eventId = -1; + + qDeleteAll(m_v8RootEvent.parentHash.values()); + qDeleteAll(m_v8RootEvent.childrenHash.values()); + m_v8RootEvent.parentHash.clear(); + m_v8RootEvent.childrenHash.clear(); +} + void QmlProfilerEventList::compileStatistics(qint64 startTime, qint64 endTime) { int index; int fromIndex = findFirstIndex(startTime); int toIndex = findLastIndex(endTime); + double totalTime = 0; // clear existing statistics foreach (QmlEventData *eventDescription, d->m_eventDescriptions.values()) { @@ -408,11 +614,16 @@ void QmlProfilerEventList::compileStatistics(qint64 startTime, qint64 endTime) eventDescription->minTime = d->m_endTimeSortedList.last().endTime; eventDescription->maxTime = 0; eventDescription->medianTime = 0; - eventDescription->cumulatedDuration = 0; - eventDescription->parentList.clear(); - eventDescription->childrenList.clear(); + 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++) { @@ -425,35 +636,71 @@ void QmlProfilerEventList::compileStatistics(qint64 startTime, qint64 endTime) eventDescription->calls++; qint64 duration = d->m_startTimeSortedList[index].length; - eventDescription->cumulatedDuration += duration; + eventDescription->duration += duration; if (eventDescription->maxTime < duration) eventDescription->maxTime = duration; if (eventDescription->minTime > duration) eventDescription->minTime = duration; int level = d->m_startTimeSortedList[index].level; - if (level > MIN_LEVEL) { - if (lastParent.contains(level-1)) { - QmlEventData *parentEvent = lastParent[level-1]; - if (!eventDescription->parentList.contains(parentEvent)) - eventDescription->parentList.append(parentEvent); - if (!parentEvent->childrenList.contains(eventDescription)) - parentEvent->childrenList.append(eventDescription); - } + + QmlEventData *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 + QmlEventData *listedRootEvent = d->m_eventDescriptions.value(d->m_rootEventName); + if (!listedRootEvent) { + listedRootEvent = new QmlEventData; + d->m_eventDescriptions.insert(d->m_rootEventName, listedRootEvent); + } + *listedRootEvent = d->m_qmlRootEvent; + // compute percentages - double totalTime = 0; foreach (QmlEventData *binding, d->m_eventDescriptions.values()) { - totalTime += binding->cumulatedDuration; - } - - foreach (QmlEventData *binding, d->m_eventDescriptions.values()) { - binding->percentOfTime = binding->cumulatedDuration * 100.0 / totalTime; - binding->timePerCall = binding->calls > 0 ? double(binding->cumulatedDuration) / binding->calls : 0; + binding->percentOfTime = binding->duration * 100.0 / totalTime; + binding->timePerCall = binding->calls > 0 ? double(binding->duration) / binding->calls : 0; } // compute median time @@ -841,14 +1088,17 @@ bool QmlProfilerEventList::save(const QString &filename) stream.writeStartDocument(); stream.writeStartElement("trace"); + stream.writeAttribute("version", Constants::PROFILER_FILE_VERSION); stream.writeAttribute("traceStart", QString::number(traceStartTime())); stream.writeAttribute("traceEnd", QString::number(traceEndTime())); stream.writeStartElement("eventData"); + stream.writeAttribute("totalTime", QString::number(d->m_qmlMeasuredTime)); + foreach (const QmlEventData *eventData, d->m_eventDescriptions.values()) { stream.writeStartElement("event"); - stream.writeAttribute("index", QString::number(d->m_eventDescriptions.keys().indexOf(eventData->location))); + 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->filename.isEmpty()) { @@ -865,12 +1115,13 @@ bool QmlProfilerEventList::save(const QString &filename) 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->location))); + stream.writeAttribute("eventIndex", QString::number(d->m_eventDescriptions.keys().indexOf(rangedEvent.description->eventHashStr))); stream.writeEndElement(); } stream.writeEndElement(); // eventList stream.writeStartElement("v8profile"); // v8 profiler output + stream.writeAttribute("totalTime", QString::number(d->m_v8MeasuredTime)); foreach (QV8EventData *v8event, d->m_v8EventList) { stream.writeStartElement("event"); stream.writeAttribute("index", QString::number(d->m_v8EventList.indexOf(v8event))); @@ -882,13 +1133,20 @@ bool QmlProfilerEventList::save(const QString &filename) } stream.writeTextElement("totalTime", QString::number(v8event->totalTime)); stream.writeTextElement("selfTime", QString::number(v8event->selfTime)); - if (!v8event->childrenList.isEmpty()) { + if (!v8event->childrenHash.isEmpty()) { stream.writeStartElement("childrenEvents"); QStringList childrenIndexes; - foreach (QV8EventData *v8child, v8event->childrenList) { - childrenIndexes << QString::number(d->m_v8EventList.indexOf(v8child)); + QStringList childrenTimes; + QStringList parentTimes; + foreach (QV8EventSub *v8child, v8event->childrenHash.values()) { + childrenIndexes << QString::number(v8child->reference->eventId); + childrenTimes << QString::number(v8child->totalTime); + parentTimes << QString::number(d->m_v8EventList[v8child->reference->eventId]->parentHash[v8event->displayName]->totalTime); } + stream.writeAttribute("list", childrenIndexes.join(QString(", "))); + stream.writeAttribute("childrenTimes", childrenTimes.join(QString(", "))); + stream.writeAttribute("parentTimes", parentTimes.join(QString(", "))); stream.writeEndElement(); } stream.writeEndElement(); @@ -936,12 +1194,20 @@ void QmlProfilerEventList::load() QmlEventData *currentEvent = 0; QHash v8eventBuffer; QHash childrenIndexes; + QHash childrenTimes; + QHash parentTimes; QV8EventData *v8event = 0; bool startTimesAreSorted = true; + bool validVersion = true; + + // time computation + d->m_v8MeasuredTime = 0; + d->m_qmlMeasuredTime = 0; + double cumulatedV8Time = 0; QXmlStreamReader stream(&file); - while (!stream.atEnd() && !stream.hasError()) { + while (validVersion && !stream.atEnd() && !stream.hasError()) { QXmlStreamReader::TokenType token = stream.readNext(); QString elementName = stream.name().toString(); switch (token) { @@ -949,6 +1215,10 @@ void QmlProfilerEventList::load() case QXmlStreamReader::StartElement : { if (elementName == "trace") { QXmlStreamAttributes attributes = stream.attributes(); + if (attributes.hasAttribute("version")) + validVersion = attributes.value("version").toString() == Constants::PROFILER_FILE_VERSION; + else + validVersion = false; if (attributes.hasAttribute("traceStart")) setTraceStartTime(attributes.value("traceStart").toString().toLongLong()); if (attributes.hasAttribute("traceEnd")) @@ -956,10 +1226,25 @@ void QmlProfilerEventList::load() } 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") { @@ -972,7 +1257,7 @@ void QmlProfilerEventList::load() if (attributes.hasAttribute("eventIndex")) { int ndx = attributes.value("eventIndex").toString().toInt(); if (!descriptionBuffer.value(ndx)) - descriptionBuffer[ndx] = new QmlEventData(); + descriptionBuffer[ndx] = new QmlEventData; rangedEvent.description = descriptionBuffer.value(ndx); } rangedEvent.endTimeIndex = d->m_endTimeSortedList.length(); @@ -996,7 +1281,7 @@ void QmlProfilerEventList::load() if (attributes.hasAttribute("index")) { int ndx = attributes.value("index").toString().toInt(); if (!descriptionBuffer.value(ndx)) - descriptionBuffer[ndx] = new QmlEventData(); + descriptionBuffer[ndx] = new QmlEventData; currentEvent = descriptionBuffer[ndx]; } else { currentEvent = 0; @@ -1055,9 +1340,16 @@ void QmlProfilerEventList::load() 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[v8eventBuffer.key(v8event)] = attributes.value("list").toString(); + 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(); } } @@ -1088,6 +1380,7 @@ void QmlProfilerEventList::load() if (elementName == "totalTime") { v8event->totalTime = readData.toDouble(); + cumulatedV8Time += v8event->totalTime; break; } @@ -1126,10 +1419,22 @@ void QmlProfilerEventList::load() stream.clear(); + if (!validVersion) { + clear(); + emit countChanged(); + emit dataReady(); + emit error(tr("Invalid version of QmlProfiler trace file.")); + return; + } + + // backwards compatibility + if (d->m_v8MeasuredTime == 0) + d->m_v8MeasuredTime = cumulatedV8Time; + // move the buffered data to the details cache foreach (QmlEventData *desc, descriptionBuffer.values()) { QString location = QString("%1:%2:%3").arg(QString::number(desc->eventType), desc->displayname, desc->details); - desc->location = location; + desc->eventHashStr = location; d->m_eventDescriptions[location] = desc; } @@ -1145,12 +1450,20 @@ void QmlProfilerEventList::load() // find v8events' children and parents foreach (int parentIndex, childrenIndexes.keys()) { - QStringList childrenStrings = childrenIndexes.value(parentIndex).split((",")); - foreach (const QString &childString, childrenStrings) { - int childIndex = childString.toInt(); + 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)) { - v8eventBuffer[parentIndex]->childrenList << v8eventBuffer[childIndex]; - v8eventBuffer[childIndex]->parentList << v8eventBuffer[parentIndex]; + QV8EventSub *newChild = new QV8EventSub(v8eventBuffer[childIndex]); + QV8EventSub *newParent = new QV8EventSub(v8eventBuffer[parentIndex]); + if (childrenTimesStrings.count() > ndx) + newChild->totalTime = childrenTimesStrings[ndx].toDouble(); + if (parentTimesStrings.count() > ndx) + newParent->totalTime = parentTimesStrings[ndx].toDouble(); + v8eventBuffer[parentIndex]->childrenHash.insert(newChild->reference->displayName, newChild); + v8eventBuffer[childIndex]->parentHash.insert(newParent->reference->displayName, newParent); } } } diff --git a/src/libs/qmljsdebugclient/qmlprofilereventlist.h b/src/libs/qmljsdebugclient/qmlprofilereventlist.h index 2a61739288b..6c61c811395 100644 --- a/src/libs/qmljsdebugclient/qmlprofilereventlist.h +++ b/src/libs/qmljsdebugclient/qmlprofilereventlist.h @@ -41,18 +41,23 @@ namespace QmlJsDebugClient { +struct QmlEventSub; +struct QV8EventSub; + struct QMLJSDEBUGCLIENT_EXPORT QmlEventData { - QmlEventData():line(-1),cumulatedDuration(0),calls(0),eventId(-1){} + QmlEventData(); + ~QmlEventData(); + QString displayname; QString filename; - QString location; + QString eventHashStr; QString details; int line; QmlJsDebugClient::QmlEventType eventType; - QList< QmlEventData *> parentList; - QList< QmlEventData *> childrenList; - qint64 cumulatedDuration; + QHash parentHash; + QHash childrenHash; + qint64 duration; qint64 calls; qint64 minTime; qint64 maxTime; @@ -60,10 +65,23 @@ struct QMLJSDEBUGCLIENT_EXPORT QmlEventData double percentOfTime; qint64 medianTime; int eventId; + + QmlEventData &operator=(const QmlEventData &ref); +}; + +struct QMLJSDEBUGCLIENT_EXPORT QmlEventSub { + QmlEventSub(QmlEventData *from) : reference(from), duration(0), calls(0) {} + QmlEventSub(QmlEventSub *from) : reference(from->reference), duration(from->duration), calls(from->calls) {} + QmlEventData *reference; + qint64 duration; + qint64 calls; }; struct QMLJSDEBUGCLIENT_EXPORT QV8EventData { + QV8EventData(); + ~QV8EventData(); + QString displayName; QString filename; QString functionName; @@ -72,9 +90,19 @@ struct QMLJSDEBUGCLIENT_EXPORT QV8EventData double totalPercent; double selfTime; double selfPercent; - QList< QV8EventData *> parentList; - QList< QV8EventData *> childrenList; + QHash parentHash; + QHash childrenHash; int eventId; + + QV8EventData &operator=(const QV8EventData &ref); +}; + +struct QMLJSDEBUGCLIENT_EXPORT QV8EventSub { + QV8EventSub(QV8EventData *from) : reference(from), totalTime(0) {} + QV8EventSub(QV8EventSub *from) : reference(from->reference), totalTime(from->totalTime) {} + + QV8EventData *reference; + qint64 totalTime; }; typedef QHash QmlEventHash; diff --git a/src/plugins/qmlprofiler/qml/Overview.js b/src/plugins/qmlprofiler/qml/Overview.js index 5cfe715bf1e..317b22f9e76 100644 --- a/src/plugins/qmlprofiler/qml/Overview.js +++ b/src/plugins/qmlprofiler/qml/Overview.js @@ -83,6 +83,9 @@ function drawData(canvas, ctxt, region) function drawTimeBar(canvas, ctxt, region) { + if (!qmlEventList) + return; + var width = canvas.width; var height = 10; var startTime = qmlEventList.traceStartTime(); diff --git a/src/plugins/qmlprofiler/qmlprofilereventview.cpp b/src/plugins/qmlprofiler/qmlprofilereventview.cpp index e139da01c47..a99c1531cc3 100644 --- a/src/plugins/qmlprofiler/qmlprofilereventview.cpp +++ b/src/plugins/qmlprofiler/qmlprofilereventview.cpp @@ -70,7 +70,7 @@ public: data(LineRole).toInt() < other.data(LineRole).toInt() : data(FilenameRole).toString() < other.data(FilenameRole).toString(); } else { - return data().toString() < other.data().toString(); + return data().toString().toLower() < other.data().toString().toLower(); } } @@ -196,8 +196,6 @@ public: void buildModelFromList(const QmlEventDescriptions &list, QStandardItem *parentItem, const QmlEventDescriptions &visitedFunctionsList = QmlEventDescriptions() ); void buildV8ModelFromList( const QV8EventDescriptions &list ); int getFieldCount(); - QString displayTime(double time) const; - QString nameForType(int typeNumber) const; QString textForItem(QStandardItem *item, bool recursive) const; @@ -282,8 +280,6 @@ void QmlProfilerEventsMainView::setViewType(ViewTypes type) setFieldViewable(MinTime, true); setFieldViewable(MedianTime, true); setFieldViewable(Details, true); - setFieldViewable(Parents, false); - setFieldViewable(Children, false); break; } case V8ProfileView: { @@ -300,8 +296,6 @@ void QmlProfilerEventsMainView::setViewType(ViewTypes type) setFieldViewable(MinTime, false); setFieldViewable(MedianTime, false); setFieldViewable(Details, true); - setFieldViewable(Parents, false); - setFieldViewable(Children, false); break; } default: break; @@ -358,7 +352,7 @@ int QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate::getFieldCount() { int count = 0; for (int i=0; i < m_fieldShown.count(); ++i) - if (m_fieldShown[i] && i != Parents && i != Children) + if (m_fieldShown[i]) count++; return count; } @@ -372,13 +366,9 @@ void QmlProfilerEventsMainView::buildModel() else d->buildModelFromList( d->m_eventStatistics->getEventDescriptions(), d->m_model->invisibleRootItem() ); - bool hasBranches = d->m_fieldShown[Parents] || d->m_fieldShown[Children]; - setRootIsDecorated(hasBranches); - + setRootIsDecorated(false); setSortingEnabled(true); - - if (!hasBranches) - sortByColumn(d->m_firstNumericColumn,Qt::DescendingOrder); + sortByColumn(d->m_firstNumericColumn,Qt::DescendingOrder); expandAll(); if (d->m_fieldShown[Name]) @@ -415,8 +405,8 @@ void QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate::buildModelFrom } if (m_fieldShown[TotalDuration]) { - newRow << new EventsViewItem(displayTime(binding->cumulatedDuration)); - newRow.last()->setData(QVariant(binding->cumulatedDuration)); + newRow << new EventsViewItem(displayTime(binding->duration)); + newRow.last()->setData(QVariant(binding->duration)); } if (m_fieldShown[CallCount]) { @@ -457,27 +447,13 @@ void QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate::buildModelFrom item->setEditable(false); // metadata - newRow.at(0)->setData(QVariant(binding->location),LocationRole); + newRow.at(0)->setData(QVariant(binding->eventHashStr),EventHashStrRole); newRow.at(0)->setData(QVariant(binding->filename),FilenameRole); newRow.at(0)->setData(QVariant(binding->line),LineRole); newRow.at(0)->setData(QVariant(binding->eventId),EventIdRole); // append parentItem->appendRow(newRow); - - if (m_fieldShown[Parents] && !binding->parentList.isEmpty()) { - QmlEventDescriptions newParentList(visitedFunctionsList); - newParentList.append(binding); - - buildModelFromList(binding->parentList, newRow.at(0), newParentList); - } - - if (m_fieldShown[Children] && !binding->childrenList.isEmpty()) { - QmlEventDescriptions newChildrenList(visitedFunctionsList); - newChildrenList.append(binding); - - buildModelFromList(binding->childrenList, newRow.at(0), newChildrenList); - } } } } @@ -523,9 +499,10 @@ void QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate::buildV8ModelFr item->setEditable(false); // metadata - newRow.at(0)->setData(QVariant(v8event->filename),FilenameRole); - newRow.at(0)->setData(QVariant(v8event->line),LineRole); - newRow.at(0)->setData(QVariant(v8event->eventId),EventIdRole); + newRow.at(0)->setData(QString("%1:%2").arg(v8event->filename, QString::number(v8event->line)), EventHashStrRole); + newRow.at(0)->setData(QVariant(v8event->filename), FilenameRole); + newRow.at(0)->setData(QVariant(v8event->line), LineRole); + newRow.at(0)->setData(QVariant(v8event->eventId), EventIdRole); // append m_model->invisibleRootItem()->appendRow(newRow); @@ -533,14 +510,14 @@ void QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate::buildV8ModelFr } } -QString QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate::displayTime(double time) const +QString QmlProfilerEventsMainView::displayTime(double time) { if (time < 1e6) - return QString::number(time/1e3,'f',3) + QString::fromWCharArray(L" \u03BCs"); + return QString::number(time/1e3,'f',3) + trUtf8(" \u03BCs"); if (time < 1e9) - return QString::number(time/1e6,'f',3) + QLatin1String(" ms"); + return QString::number(time/1e6,'f',3) + tr(" ms"); - return QString::number(time/1e9,'f',3) + QLatin1String(" s"); + return QString::number(time/1e9,'f',3) + tr(" s"); } QString QmlProfilerEventsMainView::nameForType(int typeNumber) @@ -719,24 +696,36 @@ void QmlProfilerEventsParentsAndChildrenView::displayEvent(int eventId) if (isV8) { QmlJsDebugClient::QV8EventData *v8event = m_eventList->v8EventDescription(eventId); if (v8event) { - if (isChildren) - rebuildTree((QObject *)&v8event->childrenList); - else - rebuildTree((QObject *)&v8event->parentList); + if (isChildren) { + QList childrenList = v8event->childrenHash.values(); + rebuildTree((QObject *)&childrenList); + } + else { + QList parentList = v8event->parentHash.values(); + rebuildTree((QObject *)&parentList); + } } } else { QmlJsDebugClient::QmlEventData *qmlEvent = m_eventList->eventDescription(eventId); if (qmlEvent) { - if (isChildren) - rebuildTree((QObject *)&qmlEvent->childrenList); - else - rebuildTree((QObject *)&qmlEvent->parentList); + if (isChildren) { + QList childrenList = qmlEvent->childrenHash.values(); + rebuildTree((QObject *)&childrenList); + } + else { + QList parentList = qmlEvent->parentHash.values(); + rebuildTree((QObject *)&parentList); + } } } updateHeader(); resizeColumnToContents(0); setSortingEnabled(true); + if (isV8) + sortByColumn(1); + else + sortByColumn(2); } void QmlProfilerEventsParentsAndChildrenView::rebuildTree(void *eventList) @@ -747,8 +736,8 @@ void QmlProfilerEventsParentsAndChildrenView::rebuildTree(void *eventList) QStandardItem *topLevelItem = treeModel()->invisibleRootItem(); bool isV8 = m_subtableType == V8ParentsView || m_subtableType == V8ChildrenView; - QList *qmlList = static_cast< QList *>(eventList); - QList *v8List = static_cast< QList *>(eventList); + QList *qmlList = static_cast< QList *>(eventList); + QList *v8List = static_cast< QList *>(eventList); int listLength; if (!isV8) @@ -759,17 +748,23 @@ void QmlProfilerEventsParentsAndChildrenView::rebuildTree(void *eventList) for (int index=0; index < listLength; index++) { QList newRow; if (!isV8) { - QmlEventData *event = qmlList->at(index); + QmlEventSub *event = qmlList->at(index); - newRow << new QStandardItem(event->displayname); - newRow << new QStandardItem(QmlProfilerEventsMainView::nameForType(event->eventType)); - newRow << new QStandardItem(event->details); - newRow.at(0)->setData(QVariant(event->eventId), EventIdRole); + newRow << new EventsViewItem(event->reference->displayname); + newRow << new EventsViewItem(QmlProfilerEventsMainView::nameForType(event->reference->eventType)); + newRow << new EventsViewItem(QmlProfilerEventsMainView::displayTime(event->duration)); + newRow << new EventsViewItem(QString::number(event->calls)); + newRow << new EventsViewItem(event->reference->details); + newRow.at(0)->setData(QVariant(event->reference->eventId), EventIdRole); + newRow.at(2)->setData(QVariant(event->duration)); + newRow.at(3)->setData(QVariant(event->calls)); } else { - QV8EventData *event = v8List->at(index); - newRow << new QStandardItem(event->displayName); - newRow << new QStandardItem(event->functionName); - newRow.at(0)->setData(QVariant(event->eventId), EventIdRole); + QV8EventSub *event = v8List->at(index); + newRow << new EventsViewItem(event->reference->displayName); + newRow << new EventsViewItem(QmlProfilerEventsMainView::displayTime(event->totalTime)); + newRow << new EventsViewItem(event->reference->functionName); + newRow.at(0)->setData(QVariant(event->reference->eventId), EventIdRole); + newRow.at(1)->setData(QVariant(event->totalTime)); } foreach (QStandardItem *item, newRow) item->setEditable(false); @@ -797,9 +792,9 @@ void QmlProfilerEventsParentsAndChildrenView::updateHeader() if (treeModel()) { if (isV8) - treeModel()->setColumnCount(2); - else treeModel()->setColumnCount(3); + else + treeModel()->setColumnCount(5); int columnIndex = 0; if (isChildren) @@ -810,6 +805,11 @@ void QmlProfilerEventsParentsAndChildrenView::updateHeader() if (!isV8) treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Type"))); + treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Total Time"))); + + if (!isV8) + treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Calls"))); + if (isChildren) treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Callee Description"))); else diff --git a/src/plugins/qmlprofiler/qmlprofilereventview.h b/src/plugins/qmlprofiler/qmlprofilereventview.h index c533380dd07..48f37f2d5d1 100644 --- a/src/plugins/qmlprofiler/qmlprofilereventview.h +++ b/src/plugins/qmlprofiler/qmlprofilereventview.h @@ -48,7 +48,7 @@ typedef QHash QmlEventHash; typedef QList QmlEventList; enum ItemRole { - LocationRole = Qt::UserRole+1, + EventHashStrRole = Qt::UserRole+1, FilenameRole = Qt::UserRole+2, LineRole = Qt::UserRole+3, EventIdRole = Qt::UserRole+4 @@ -108,8 +108,6 @@ public: MinTime, MedianTime, Details, - Parents, - Children, MaxFields }; @@ -135,6 +133,7 @@ public: void copyTableToClipboard() const; void copyRowToClipboard() const; + static QString displayTime(double time); static QString nameForType(int typeNumber); void getStatisticsInRange(qint64 rangeStart, qint64 rangeEnd);