summaryrefslogtreecommitdiff
path: root/src/plugins/qmlprofiler/qmlprofilertracefile.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/qmlprofiler/qmlprofilertracefile.cpp')
-rw-r--r--src/plugins/qmlprofiler/qmlprofilertracefile.cpp561
1 files changed, 561 insertions, 0 deletions
diff --git a/src/plugins/qmlprofiler/qmlprofilertracefile.cpp b/src/plugins/qmlprofiler/qmlprofilertracefile.cpp
new file mode 100644
index 0000000000..0da6a4a853
--- /dev/null
+++ b/src/plugins/qmlprofiler/qmlprofilertracefile.cpp
@@ -0,0 +1,561 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "qmlprofilertracefile.h"
+
+#include <utils/qtcassert.h>
+
+#include <QIODevice>
+#include <QStringList>
+#include <QXmlStreamReader>
+#include <QXmlStreamWriter>
+#include <QDebug>
+
+// import QmlEventType, QmlBindingType enums, QmlEventLocation
+using namespace QmlDebug;
+
+
+const char PROFILER_FILE_VERSION[] = "1.02";
+
+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 TYPE_PIXMAPCACHE_STR[] = "PixmapCache";
+const char TYPE_SCENEGRAPH_STR[] = "SceneGraph";
+
+#define _(X) QLatin1String(X)
+
+
+//
+// "be strict in your output but tolerant in your inputs"
+//
+
+namespace QmlProfiler {
+namespace Internal {
+
+static QmlEventType qmlEventTypeAsEnum(const QString &typeString)
+{
+ if (typeString == _(TYPE_PAINTING_STR)) {
+ return Painting;
+ } else if (typeString == _(TYPE_COMPILING_STR)) {
+ return Compiling;
+ } else if (typeString == _(TYPE_CREATING_STR)) {
+ return Creating;
+ } else if (typeString == _(TYPE_BINDING_STR)) {
+ return Binding;
+ } else if (typeString == _(TYPE_HANDLINGSIGNAL_STR)) {
+ return HandlingSignal;
+ } else if (typeString == _(TYPE_PIXMAPCACHE_STR)) {
+ return PixmapCacheEvent;
+ } else if (typeString == _(TYPE_SCENEGRAPH_STR)) {
+ return SceneGraphFrameEvent;
+ } else {
+ bool isNumber = false;
+ int type = typeString.toUInt(&isNumber);
+ if (isNumber)
+ return (QmlEventType)type;
+ else
+ return MaximumQmlEventType;
+ }
+}
+
+static QString qmlEventTypeAsString(QmlEventType typeEnum)
+{
+ switch (typeEnum) {
+ case Painting:
+ return _(TYPE_PAINTING_STR);
+ break;
+ case Compiling:
+ return _(TYPE_COMPILING_STR);
+ break;
+ case Creating:
+ return _(TYPE_CREATING_STR);
+ break;
+ case Binding:
+ return _(TYPE_BINDING_STR);
+ break;
+ case HandlingSignal:
+ return _(TYPE_HANDLINGSIGNAL_STR);
+ break;
+ case PixmapCacheEvent:
+ return _(TYPE_PIXMAPCACHE_STR);
+ break;
+ case SceneGraphFrameEvent:
+ return _(TYPE_SCENEGRAPH_STR);
+ break;
+ default:
+ return QString::number((int)typeEnum);
+ }
+}
+
+
+QmlProfilerFileReader::QmlProfilerFileReader(QObject *parent) :
+ QObject(parent),
+ m_v8Model(0)
+{
+}
+
+void QmlProfilerFileReader::setV8DataModel(QV8ProfilerDataModel *dataModel)
+{
+ m_v8Model = dataModel;
+}
+
+bool QmlProfilerFileReader::load(QIODevice *device)
+{
+ QXmlStreamReader stream(device);
+
+ bool validVersion = true;
+
+ while (validVersion && !stream.atEnd() && !stream.hasError()) {
+ QXmlStreamReader::TokenType token = stream.readNext();
+ const QStringRef elementName = stream.name();
+ switch (token) {
+ case QXmlStreamReader::StartDocument : continue;
+ case QXmlStreamReader::StartElement : {
+ if (elementName == _("trace")) {
+ QXmlStreamAttributes attributes = stream.attributes();
+ if (attributes.hasAttribute(_("version")))
+ validVersion = attributes.value(_("version")) == _(PROFILER_FILE_VERSION);
+ else
+ validVersion = false;
+ if (attributes.hasAttribute(_("traceStart")))
+ emit traceStartTime(attributes.value(_("traceStart")).toString().toLongLong());
+ if (attributes.hasAttribute(_("traceEnd")))
+ emit traceEndTime(attributes.value(_("traceEnd")).toString().toLongLong());
+ }
+
+ if (elementName == _("eventData")) {
+ loadEventData(stream);
+ break;
+ }
+
+ if (elementName == _("profilerDataModel")) {
+ loadProfilerDataModel(stream);
+ break;
+ }
+
+ if (elementName == _("v8profile")) {
+ if (m_v8Model)
+ m_v8Model->load(stream);
+ break;
+ }
+
+ break;
+ }
+ default: break;
+ }
+ }
+
+ if (stream.hasError()) {
+ emit error(tr("Error while parsing trace data file: %1").arg(stream.errorString()));
+ return false;
+ } else {
+ processQmlEvents();
+
+ return true;
+ }
+}
+
+void QmlProfilerFileReader::loadEventData(QXmlStreamReader &stream)
+{
+ QTC_ASSERT(stream.name() == _("eventData"), return);
+
+ QXmlStreamAttributes attributes = stream.attributes();
+ if (attributes.hasAttribute(_("totalTime"))) {
+ // not used any more
+ }
+
+ int eventIndex = -1;
+ QmlEvent event = {
+ QString(), // displayname
+ QString(), // filename
+ QString(), // details
+ Painting, // type
+ QmlBinding, // bindingType, set for backwards compatibility
+ 0, // line
+ 0 // column
+ };
+ const QmlEvent defaultEvent = event;
+
+ while (!stream.atEnd() && !stream.hasError()) {
+ QXmlStreamReader::TokenType token = stream.readNext();
+ const QStringRef elementName = stream.name();
+
+ switch (token) {
+ case QXmlStreamReader::StartElement: {
+ if (elementName == _("event")) {
+ event = defaultEvent;
+
+ const QXmlStreamAttributes attributes = stream.attributes();
+ if (attributes.hasAttribute(_("index"))) {
+ eventIndex = attributes.value(_("index")).toString().toInt();
+ } else {
+ // ignore event
+ eventIndex = -1;
+ }
+ break;
+ }
+
+ stream.readNext();
+ if (stream.tokenType() != QXmlStreamReader::Characters)
+ break;
+
+ const QString readData = stream.text().toString();
+
+ if (elementName == _("displayname")) {
+ event.displayName = readData;
+ break;
+ }
+
+ if (elementName == _("type")) {
+ event.type = qmlEventTypeAsEnum(readData);
+ break;
+ }
+
+ if (elementName == _("filename")) {
+ event.filename = readData;
+ break;
+ }
+
+ if (elementName == _("line")) {
+ event.line = readData.toInt();
+ break;
+ }
+
+ if (elementName == _("column")) {
+ event.column = readData.toInt();
+ break;
+ }
+
+ if (elementName == _("details")) {
+ event.details = readData;
+ break;
+ }
+
+ if (elementName == _("bindingType") ||
+ elementName == _("animationFrame") ||
+ elementName == _("cacheEventType") ||
+ elementName == _("sgEventType")) {
+ event.bindingType = readData.toInt();
+ break;
+ }
+
+ break;
+ }
+ case QXmlStreamReader::EndElement: {
+ if (elementName == _("event")) {
+ if (eventIndex >= 0) {
+ if (eventIndex >= m_qmlEvents.size())
+ m_qmlEvents.resize(eventIndex + 1);
+ m_qmlEvents[eventIndex] = event;
+ }
+ break;
+ }
+
+ if (elementName == _("eventData")) {
+ // done reading eventData
+ return;
+ }
+ break;
+ }
+ default: break;
+ } // switch
+ }
+}
+
+void QmlProfilerFileReader::loadProfilerDataModel(QXmlStreamReader &stream)
+{
+ QTC_ASSERT(stream.name() == _("profilerDataModel"), return);
+
+ while (!stream.atEnd() && !stream.hasError()) {
+ QXmlStreamReader::TokenType token = stream.readNext();
+ const QStringRef elementName = stream.name();
+
+ switch (token) {
+ case QXmlStreamReader::StartElement: {
+ if (elementName == _("range")) {
+ Range range = { 0, 0, 0, 0, 0, 0, 0 };
+
+ const QXmlStreamAttributes attributes = stream.attributes();
+ if (!attributes.hasAttribute(_("startTime"))
+ || !attributes.hasAttribute(_("eventIndex"))) {
+ // ignore incomplete entry
+ continue;
+ }
+
+ range.startTime = attributes.value(_("startTime")).toString().toLongLong();
+ if (attributes.hasAttribute(_("duration")))
+ range.duration = attributes.value(_("duration")).toString().toLongLong();
+
+ // attributes for special events
+ if (attributes.hasAttribute(_("framerate")))
+ range.numericData1 = attributes.value(_("framerate")).toString().toLongLong();
+ if (attributes.hasAttribute(_("animationcount")))
+ range.numericData2 = attributes.value(_("animationcount")).toString().toLongLong();
+ if (attributes.hasAttribute(_("width")))
+ range.numericData1 = attributes.value(_("width")).toString().toLongLong();
+ if (attributes.hasAttribute(_("height")))
+ range.numericData2 = attributes.value(_("height")).toString().toLongLong();
+ if (attributes.hasAttribute(_("refCount")))
+ range.numericData3 = attributes.value(_("refCount")).toString().toLongLong();
+ if (attributes.hasAttribute(_("timing1")))
+ range.numericData1 = attributes.value(_("timing1")).toString().toLongLong();
+ if (attributes.hasAttribute(_("timing2")))
+ range.numericData2 = attributes.value(_("timing2")).toString().toLongLong();
+ if (attributes.hasAttribute(_("timing3")))
+ range.numericData3 = attributes.value(_("timing3")).toString().toLongLong();
+ if (attributes.hasAttribute(_("timing4")))
+ range.numericData4 = attributes.value(_("timing4")).toString().toLongLong();
+ if (attributes.hasAttribute(_("timing5")))
+ range.numericData5 = attributes.value(_("timing5")).toString().toLongLong();
+
+
+ int eventIndex = attributes.value(_("eventIndex")).toString().toInt();
+
+
+ m_ranges.append(QPair<Range,int>(range, eventIndex));
+ }
+ break;
+ }
+ case QXmlStreamReader::EndElement: {
+ if (elementName == _("profilerDataModel")) {
+ // done reading profilerDataModel
+ return;
+ }
+ break;
+ }
+ default: break;
+ } // switch
+ }
+}
+
+void QmlProfilerFileReader::processQmlEvents()
+{
+ for (int i = 0; i < m_ranges.size(); ++i) {
+ Range range = m_ranges[i].first;
+ int eventIndex = m_ranges[i].second;
+
+ if (eventIndex < 0 || eventIndex >= m_qmlEvents.size()) {
+ qWarning() << ".qtd file - range index" << eventIndex
+ << "is outside of bounds (0, " << m_qmlEvents.size() << ")";
+ continue;
+ }
+
+ QmlEvent &event = m_qmlEvents[eventIndex];
+
+ emit rangedEvent(event.type, event.bindingType, range.startTime, range.duration,
+ QStringList(event.displayName),
+ QmlEventLocation(event.filename, event.line, event.column),
+ range.numericData1,range.numericData2, range.numericData3, range.numericData4, range.numericData5);
+
+ }
+}
+
+QmlProfilerFileWriter::QmlProfilerFileWriter(QObject *parent) :
+ QObject(parent),
+ m_startTime(0),
+ m_endTime(0),
+ m_measuredTime(0),
+ m_v8Model(0)
+{
+ m_acceptedTypes << QmlDebug::Compiling << QmlDebug::Creating << QmlDebug::Binding << QmlDebug::HandlingSignal;
+}
+
+void QmlProfilerFileWriter::setTraceTime(qint64 startTime, qint64 endTime, qint64 measuredTime)
+{
+ m_startTime = startTime;
+ m_endTime = endTime;
+ m_measuredTime = measuredTime;
+}
+
+void QmlProfilerFileWriter::setV8DataModel(QV8ProfilerDataModel *dataModel)
+{
+ m_v8Model = dataModel;
+}
+
+void QmlProfilerFileWriter::setQmlEvents(const QVector<QmlProfilerSimpleModel::QmlEventData> &events)
+{
+ foreach (const QmlProfilerSimpleModel::QmlEventData &event, events) {
+ const QString hashStr = QmlProfilerSimpleModel::getHashString(event);
+ if (!m_qmlEvents.contains(hashStr)) {
+ QmlEvent e = { event.displayName,
+ event.location.filename,
+ event.data.join(_("")),
+ static_cast<QmlDebug::QmlEventType>(event.eventType),
+ event.bindingType,
+ event.location.line,
+ event.location.column
+ };
+ m_qmlEvents.insert(hashStr, e);
+ }
+
+ Range r = { event.startTime, event.duration, event.numericData1, event.numericData2, event.numericData3, event.numericData4, event.numericData5 };
+ m_ranges.append(QPair<Range, QString>(r, hashStr));
+ }
+
+ calculateMeasuredTime(events);
+}
+
+void QmlProfilerFileWriter::save(QIODevice *device)
+{
+ QXmlStreamWriter stream(device);
+
+ stream.setAutoFormatting(true);
+ stream.writeStartDocument();
+
+ stream.writeStartElement(_("trace"));
+ stream.writeAttribute(_("version"), _(PROFILER_FILE_VERSION));
+
+ stream.writeAttribute(_("traceStart"), QString::number(m_startTime));
+ stream.writeAttribute(_("traceEnd"), QString::number(m_endTime));
+
+ stream.writeStartElement(_("eventData"));
+ stream.writeAttribute(_("totalTime"), QString::number(m_measuredTime));
+
+ QHash<QString,QmlEvent>::const_iterator eventIter = m_qmlEvents.constBegin();
+ for (; eventIter != m_qmlEvents.constEnd(); ++eventIter) {
+ QmlEvent event = eventIter.value();
+
+ stream.writeStartElement(_("event"));
+ stream.writeAttribute(_("index"), QString::number(m_qmlEvents.keys().indexOf(eventIter.key())));
+ stream.writeTextElement(_("displayname"), event.displayName);
+ stream.writeTextElement(_("type"), qmlEventTypeAsString(event.type));
+ if (!event.filename.isEmpty()) {
+ stream.writeTextElement(_("filename"), event.filename);
+ stream.writeTextElement(_("line"), QString::number(event.line));
+ stream.writeTextElement(_("column"), QString::number(event.column));
+ }
+ stream.writeTextElement(_("details"), event.details);
+ if (event.type == Binding)
+ stream.writeTextElement(_("bindingType"), QString::number(event.bindingType));
+ if (event.type == Painting && event.bindingType == AnimationFrame)
+ stream.writeTextElement(_("animationFrame"), QString::number(event.bindingType));
+ if (event.type == PixmapCacheEvent)
+ stream.writeTextElement(_("cacheEventType"), QString::number(event.bindingType));
+ if (event.type == SceneGraphFrameEvent)
+ stream.writeTextElement(_("sgEventType"), QString::number(event.bindingType));
+ stream.writeEndElement();
+ }
+ stream.writeEndElement(); // eventData
+
+ stream.writeStartElement(_("profilerDataModel"));
+
+ QVector<QPair<Range, QString> >::const_iterator rangeIter = m_ranges.constBegin();
+ for (; rangeIter != m_ranges.constEnd(); ++rangeIter) {
+ Range range = rangeIter->first;
+ QString eventHash = rangeIter->second;
+
+ stream.writeStartElement(_("range"));
+ stream.writeAttribute(_("startTime"), QString::number(range.startTime));
+ if (range.duration > 0) // no need to store duration of instantaneous events
+ stream.writeAttribute(_("duration"), QString::number(range.duration));
+ stream.writeAttribute(_("eventIndex"), QString::number(m_qmlEvents.keys().indexOf(eventHash)));
+
+ QmlEvent event = m_qmlEvents.value(eventHash);
+
+ // special: animation event
+ if (event.type == QmlDebug::Painting && event.bindingType == QmlDebug::AnimationFrame) {
+
+ stream.writeAttribute(_("framerate"), QString::number(range.numericData1));
+ stream.writeAttribute(_("animationcount"), QString::number(range.numericData2));
+ }
+
+ // special: pixmap cache event
+ if (event.type == QmlDebug::PixmapCacheEvent) {
+ // pixmap image size
+ if (event.bindingType == 0) {
+ stream.writeAttribute(_("width"), QString::number(range.numericData1));
+ stream.writeAttribute(_("height"), QString::number(range.numericData2));
+ }
+
+ // reference count (1) / cache size changed (2)
+ if (event.bindingType == 1 || event.bindingType == 2)
+ stream.writeAttribute(_("refCount"), QString::number(range.numericData3));
+ }
+
+ if (event.type == QmlDebug::SceneGraphFrameEvent) {
+ // special: scenegraph frame events
+ if (range.numericData1 > 0)
+ stream.writeAttribute(_("timing1"), QString::number(range.numericData1));
+ if (range.numericData2 > 0)
+ stream.writeAttribute(_("timing2"), QString::number(range.numericData2));
+ if (range.numericData3 > 0)
+ stream.writeAttribute(_("timing3"), QString::number(range.numericData3));
+ if (range.numericData4 > 0)
+ stream.writeAttribute(_("timing4"), QString::number(range.numericData4));
+ if (range.numericData5 > 0)
+ stream.writeAttribute(_("timing5"), QString::number(range.numericData5));
+ }
+ stream.writeEndElement();
+ }
+ stream.writeEndElement(); // profilerDataModel
+
+ m_v8Model->save(stream);
+
+ stream.writeEndElement(); // trace
+ stream.writeEndDocument();
+}
+
+void QmlProfilerFileWriter::calculateMeasuredTime(const QVector<QmlProfilerSimpleModel::QmlEventData> &events)
+{
+ // measured time isn't used, but old clients might still need it
+ // -> we calculate it explicitly
+
+ qint64 duration = 0;
+
+ QHash<int, qint64> endtimesPerLevel;
+ int level = QmlDebug::Constants::QML_MIN_LEVEL;
+ endtimesPerLevel[0] = 0;
+
+ foreach (const QmlProfilerSimpleModel::QmlEventData &event, events) {
+ // whitelist
+ if (!m_acceptedTypes.contains(event.eventType))
+ continue;
+
+ // level computation
+ if (endtimesPerLevel[level] > event.startTime) {
+ level++;
+ } else {
+ while (level > QmlDebug::Constants::QML_MIN_LEVEL && endtimesPerLevel[level-1] <= event.startTime)
+ level--;
+ }
+ endtimesPerLevel[level] = event.startTime + event.duration;
+ if (level == QmlDebug::Constants::QML_MIN_LEVEL) {
+ duration += event.duration;
+ }
+ }
+
+ m_measuredTime = duration;
+}
+
+
+} // namespace Internal
+} // namespace QmlProfiler