2013-08-08 13:28:08 +02:00
|
|
|
/****************************************************************************
|
|
|
|
|
**
|
2014-01-07 13:27:11 +01:00
|
|
|
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
2013-08-08 13:28:08 +02:00
|
|
|
** 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";
|
|
|
|
|
|
2014-06-03 16:57:32 +02:00
|
|
|
static const char *RANGE_TYPE_STRINGS[] = {
|
|
|
|
|
"Painting",
|
|
|
|
|
"Compiling",
|
|
|
|
|
"Creating",
|
|
|
|
|
"Binding",
|
|
|
|
|
"HandlingSignal",
|
|
|
|
|
"Javascript"
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Q_STATIC_ASSERT(sizeof(RANGE_TYPE_STRINGS) == QmlDebug::MaximumRangeType * sizeof(const char *));
|
|
|
|
|
|
|
|
|
|
static const char *MESSAGE_STRINGS[] = {
|
|
|
|
|
// So far only pixmap and scenegraph are used. The others are padding.
|
|
|
|
|
"Event",
|
|
|
|
|
"RangeStart",
|
|
|
|
|
"RangeData",
|
|
|
|
|
"RangeLocation",
|
|
|
|
|
"RangeEnd",
|
|
|
|
|
"Complete",
|
|
|
|
|
"PixmapCache",
|
2014-05-27 19:19:03 +02:00
|
|
|
"SceneGraph",
|
|
|
|
|
"MemoryAllocation"
|
2014-06-03 16:57:32 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Q_STATIC_ASSERT(sizeof(MESSAGE_STRINGS) == QmlDebug::MaximumMessage * sizeof(const char *));
|
2013-08-08 13:28:08 +02:00
|
|
|
|
|
|
|
|
#define _(X) QLatin1String(X)
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// "be strict in your output but tolerant in your inputs"
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
namespace QmlProfiler {
|
|
|
|
|
namespace Internal {
|
|
|
|
|
|
2014-06-03 16:57:32 +02:00
|
|
|
static QPair<QmlDebug::Message, QmlDebug::RangeType> qmlTypeAsEnum(const QString &typeString)
|
2013-08-08 13:28:08 +02:00
|
|
|
{
|
2014-06-03 16:57:32 +02:00
|
|
|
QPair<QmlDebug::Message, QmlDebug::RangeType> ret(QmlDebug::MaximumMessage,
|
|
|
|
|
QmlDebug::MaximumRangeType);
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < QmlDebug::MaximumMessage; ++i) {
|
|
|
|
|
if (typeString == _(MESSAGE_STRINGS[i])) {
|
|
|
|
|
ret.first = static_cast<QmlDebug::Message>(i);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < QmlDebug::MaximumRangeType; ++i) {
|
|
|
|
|
if (typeString == _(RANGE_TYPE_STRINGS[i])) {
|
|
|
|
|
ret.second = static_cast<QmlDebug::RangeType>(i);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ret.first == QmlDebug::MaximumMessage && ret.second == QmlDebug::MaximumRangeType) {
|
2013-08-08 13:28:08 +02:00
|
|
|
bool isNumber = false;
|
|
|
|
|
int type = typeString.toUInt(&isNumber);
|
2014-06-03 16:57:32 +02:00
|
|
|
if (isNumber && type < QmlDebug::MaximumRangeType)
|
|
|
|
|
// Allow saving ranges as numbers, but not messages.
|
|
|
|
|
ret.second = static_cast<QmlDebug::RangeType>(type);
|
2013-08-08 13:28:08 +02:00
|
|
|
}
|
2014-06-03 16:57:32 +02:00
|
|
|
|
|
|
|
|
return ret;
|
2013-08-08 13:28:08 +02:00
|
|
|
}
|
|
|
|
|
|
2014-06-03 16:57:32 +02:00
|
|
|
static QString qmlTypeAsString(QmlDebug::Message message, QmlDebug::RangeType rangeType)
|
2013-08-08 13:28:08 +02:00
|
|
|
{
|
2014-06-03 16:57:32 +02:00
|
|
|
if (rangeType < QmlDebug::MaximumRangeType)
|
|
|
|
|
return _(RANGE_TYPE_STRINGS[rangeType]);
|
|
|
|
|
else if (message != QmlDebug::MaximumMessage)
|
|
|
|
|
return _(MESSAGE_STRINGS[message]);
|
|
|
|
|
else
|
|
|
|
|
return QString::number((int)rangeType);
|
2013-08-08 13:28:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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();
|
2013-08-29 11:04:09 +02:00
|
|
|
const QStringRef elementName = stream.name();
|
2013-08-08 13:28:08 +02:00
|
|
|
switch (token) {
|
|
|
|
|
case QXmlStreamReader::StartDocument : continue;
|
|
|
|
|
case QXmlStreamReader::StartElement : {
|
|
|
|
|
if (elementName == _("trace")) {
|
|
|
|
|
QXmlStreamAttributes attributes = stream.attributes();
|
|
|
|
|
if (attributes.hasAttribute(_("version")))
|
2013-08-29 11:04:09 +02:00
|
|
|
validVersion = attributes.value(_("version")) == _(PROFILER_FILE_VERSION);
|
2013-08-08 13:28:08 +02:00
|
|
|
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)
|
|
|
|
|
{
|
2013-08-29 11:04:09 +02:00
|
|
|
QTC_ASSERT(stream.name() == _("eventData"), return);
|
2013-08-08 13:28:08 +02:00
|
|
|
|
|
|
|
|
QXmlStreamAttributes attributes = stream.attributes();
|
|
|
|
|
|
|
|
|
|
int eventIndex = -1;
|
2014-06-13 16:34:30 +02:00
|
|
|
QmlProfilerDataModel::QmlEventTypeData event = {
|
2013-08-08 13:28:08 +02:00
|
|
|
QString(), // displayname
|
2014-06-13 16:34:30 +02:00
|
|
|
QmlEventLocation(),
|
2014-06-03 16:57:32 +02:00
|
|
|
MaximumMessage,
|
2013-08-08 13:28:08 +02:00
|
|
|
Painting, // type
|
|
|
|
|
QmlBinding, // bindingType, set for backwards compatibility
|
2014-06-13 16:34:30 +02:00
|
|
|
QString(), // details
|
2013-08-08 13:28:08 +02:00
|
|
|
};
|
2014-06-13 16:34:30 +02:00
|
|
|
const QmlProfilerDataModel::QmlEventTypeData defaultEvent = event;
|
2013-08-08 13:28:08 +02:00
|
|
|
|
|
|
|
|
while (!stream.atEnd() && !stream.hasError()) {
|
|
|
|
|
QXmlStreamReader::TokenType token = stream.readNext();
|
2013-08-29 11:04:09 +02:00
|
|
|
const QStringRef elementName = stream.name();
|
2013-08-08 13:28:08 +02:00
|
|
|
|
|
|
|
|
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")) {
|
2014-06-03 16:57:32 +02:00
|
|
|
QPair<QmlDebug::Message, QmlDebug::RangeType> enums = qmlTypeAsEnum(readData);
|
|
|
|
|
event.message = enums.first;
|
|
|
|
|
event.rangeType = enums.second;
|
2013-08-08 13:28:08 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (elementName == _("filename")) {
|
2014-06-13 16:34:30 +02:00
|
|
|
event.location.filename = readData;
|
2013-08-08 13:28:08 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (elementName == _("line")) {
|
2014-06-13 16:34:30 +02:00
|
|
|
event.location.line = readData.toInt();
|
2013-08-08 13:28:08 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (elementName == _("column")) {
|
2014-06-13 16:34:30 +02:00
|
|
|
event.location.column = readData.toInt();
|
2013-08-08 13:28:08 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (elementName == _("details")) {
|
2014-06-13 16:34:30 +02:00
|
|
|
event.data = readData;
|
2013-08-08 13:28:08 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-03 16:57:32 +02:00
|
|
|
if (elementName == _("animationFrame")) {
|
|
|
|
|
event.detailType = readData.toInt();
|
|
|
|
|
// new animation frames used to be saved as ranges of range type Painting with
|
|
|
|
|
// binding type 4 (which was called "AnimationFrame" to make everything even more
|
|
|
|
|
// confusing), even though they clearly aren't ranges. Convert that to something
|
|
|
|
|
// sane here.
|
|
|
|
|
if (event.detailType == 4) {
|
|
|
|
|
event.message = Event;
|
|
|
|
|
event.rangeType = MaximumRangeType;
|
|
|
|
|
event.detailType = AnimationFrame;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-08 13:28:08 +02:00
|
|
|
if (elementName == _("bindingType") ||
|
|
|
|
|
elementName == _("cacheEventType") ||
|
|
|
|
|
elementName == _("sgEventType")) {
|
2014-06-03 16:57:32 +02:00
|
|
|
event.detailType = readData.toInt();
|
2013-08-08 13:28:08 +02:00
|
|
|
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)
|
|
|
|
|
{
|
2013-08-29 11:04:09 +02:00
|
|
|
QTC_ASSERT(stream.name() == _("profilerDataModel"), return);
|
2013-08-08 13:28:08 +02:00
|
|
|
|
|
|
|
|
while (!stream.atEnd() && !stream.hasError()) {
|
|
|
|
|
QXmlStreamReader::TokenType token = stream.readNext();
|
2013-08-29 11:04:09 +02:00
|
|
|
const QStringRef elementName = stream.name();
|
2013-08-08 13:28:08 +02:00
|
|
|
|
|
|
|
|
switch (token) {
|
|
|
|
|
case QXmlStreamReader::StartElement: {
|
|
|
|
|
if (elementName == _("range")) {
|
2014-06-13 16:34:30 +02:00
|
|
|
QmlProfilerDataModel::QmlEventData range = { -1, 0, 0, 0, 0, 0, 0, 0 };
|
2013-08-08 13:28:08 +02:00
|
|
|
|
|
|
|
|
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();
|
2014-03-17 12:04:23 +01:00
|
|
|
if (attributes.hasAttribute(_("thread")))
|
|
|
|
|
range.numericData3 = attributes.value(_("thread")).toString().toLongLong();
|
2013-08-08 13:28:08 +02:00
|
|
|
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();
|
|
|
|
|
|
2014-06-13 16:34:30 +02:00
|
|
|
range.typeIndex = attributes.value(_("eventIndex")).toString().toInt();
|
2013-08-08 13:28:08 +02:00
|
|
|
|
2014-06-13 16:34:30 +02:00
|
|
|
m_ranges.append(range);
|
2013-08-08 13:28:08 +02:00
|
|
|
}
|
|
|
|
|
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) {
|
2014-06-13 16:34:30 +02:00
|
|
|
const QmlProfilerDataModel::QmlEventData &range = m_ranges[i];
|
|
|
|
|
int eventIndex = range.typeIndex;
|
2013-08-08 13:28:08 +02:00
|
|
|
|
|
|
|
|
if (eventIndex < 0 || eventIndex >= m_qmlEvents.size()) {
|
|
|
|
|
qWarning() << ".qtd file - range index" << eventIndex
|
|
|
|
|
<< "is outside of bounds (0, " << m_qmlEvents.size() << ")";
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-13 16:34:30 +02:00
|
|
|
const QmlProfilerDataModel::QmlEventTypeData &event = m_qmlEvents[eventIndex];
|
2013-08-08 13:28:08 +02:00
|
|
|
|
2014-06-03 16:57:32 +02:00
|
|
|
emit rangedEvent(event.message, event.rangeType, event.detailType, range.startTime,
|
2014-06-13 16:34:30 +02:00
|
|
|
range.duration, event.data, event.location,
|
2014-06-03 16:57:32 +02:00
|
|
|
range.numericData1,range.numericData2, range.numericData3,
|
|
|
|
|
range.numericData4, range.numericData5);
|
2013-08-08 13:28:08 +02:00
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QmlProfilerFileWriter::QmlProfilerFileWriter(QObject *parent) :
|
|
|
|
|
QObject(parent),
|
|
|
|
|
m_startTime(0),
|
|
|
|
|
m_endTime(0),
|
|
|
|
|
m_measuredTime(0),
|
|
|
|
|
m_v8Model(0)
|
|
|
|
|
{
|
2014-06-03 16:57:32 +02:00
|
|
|
m_acceptedRangeTypes << QmlDebug::Compiling << QmlDebug::Creating << QmlDebug::Binding
|
|
|
|
|
<< QmlDebug::HandlingSignal << QmlDebug::Javascript;
|
|
|
|
|
m_acceptedMessages << QmlDebug::SceneGraphFrame << QmlDebug::PixmapCacheEvent;
|
2013-08-08 13:28:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-13 16:34:30 +02:00
|
|
|
void QmlProfilerFileWriter::setQmlEvents(const QVector<QmlProfilerDataModel::QmlEventTypeData> &types,
|
|
|
|
|
const QVector<QmlProfilerDataModel::QmlEventData> &events)
|
2013-08-08 13:28:08 +02:00
|
|
|
{
|
2014-06-13 16:34:30 +02:00
|
|
|
m_qmlEvents = types;
|
|
|
|
|
m_ranges = events;
|
|
|
|
|
calculateMeasuredTime();
|
2013-08-08 13:28:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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));
|
|
|
|
|
|
2014-06-13 16:34:30 +02:00
|
|
|
for (int typeIndex = 0; typeIndex < m_qmlEvents.size(); ++typeIndex) {
|
|
|
|
|
const QmlProfilerDataModel::QmlEventTypeData &event = m_qmlEvents[typeIndex];
|
2013-08-08 13:28:08 +02:00
|
|
|
|
|
|
|
|
stream.writeStartElement(_("event"));
|
2014-06-13 16:34:30 +02:00
|
|
|
stream.writeAttribute(_("index"), QString::number(typeIndex));
|
2013-08-08 13:28:08 +02:00
|
|
|
stream.writeTextElement(_("displayname"), event.displayName);
|
2014-06-03 16:57:32 +02:00
|
|
|
stream.writeTextElement(_("type"), qmlTypeAsString(event.message, event.rangeType));
|
2014-06-13 16:34:30 +02:00
|
|
|
if (!event.location.filename.isEmpty()) {
|
|
|
|
|
stream.writeTextElement(_("filename"), event.location.filename);
|
|
|
|
|
stream.writeTextElement(_("line"), QString::number(event.location.line));
|
|
|
|
|
stream.writeTextElement(_("column"), QString::number(event.location.column));
|
2013-08-08 13:28:08 +02:00
|
|
|
}
|
2014-06-13 16:34:30 +02:00
|
|
|
stream.writeTextElement(_("details"), event.data);
|
2014-06-03 16:57:32 +02:00
|
|
|
if (event.rangeType == Binding)
|
|
|
|
|
stream.writeTextElement(_("bindingType"), QString::number(event.detailType));
|
|
|
|
|
if (event.message == Event && event.detailType == AnimationFrame)
|
|
|
|
|
stream.writeTextElement(_("animationFrame"), QString::number(event.detailType));
|
|
|
|
|
if (event.message == PixmapCacheEvent)
|
|
|
|
|
stream.writeTextElement(_("cacheEventType"), QString::number(event.detailType));
|
|
|
|
|
if (event.message == SceneGraphFrame)
|
|
|
|
|
stream.writeTextElement(_("sgEventType"), QString::number(event.detailType));
|
2013-08-08 13:28:08 +02:00
|
|
|
stream.writeEndElement();
|
|
|
|
|
}
|
|
|
|
|
stream.writeEndElement(); // eventData
|
|
|
|
|
|
|
|
|
|
stream.writeStartElement(_("profilerDataModel"));
|
|
|
|
|
|
2014-06-13 16:34:30 +02:00
|
|
|
for (int rangeIndex = 0; rangeIndex < m_ranges.size(); ++rangeIndex) {
|
|
|
|
|
const QmlProfilerDataModel::QmlEventData &range = m_ranges[rangeIndex];
|
2013-08-08 13:28:08 +02:00
|
|
|
|
|
|
|
|
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));
|
2014-06-13 16:34:30 +02:00
|
|
|
stream.writeAttribute(_("eventIndex"), QString::number(range.typeIndex));
|
2013-08-08 13:28:08 +02:00
|
|
|
|
2014-06-13 16:34:30 +02:00
|
|
|
const QmlProfilerDataModel::QmlEventTypeData &event = m_qmlEvents[range.typeIndex];
|
2013-08-08 13:28:08 +02:00
|
|
|
|
|
|
|
|
// special: animation event
|
2014-06-03 16:57:32 +02:00
|
|
|
if (event.message == QmlDebug::Event && event.detailType == QmlDebug::AnimationFrame) {
|
2013-08-08 13:28:08 +02:00
|
|
|
stream.writeAttribute(_("framerate"), QString::number(range.numericData1));
|
|
|
|
|
stream.writeAttribute(_("animationcount"), QString::number(range.numericData2));
|
2014-03-17 12:04:23 +01:00
|
|
|
stream.writeAttribute(_("thread"), QString::number(range.numericData3));
|
2013-08-08 13:28:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// special: pixmap cache event
|
2014-06-03 16:57:32 +02:00
|
|
|
if (event.message == QmlDebug::PixmapCacheEvent) {
|
|
|
|
|
if (event.detailType == PixmapSizeKnown) {
|
2013-08-08 13:28:08 +02:00
|
|
|
stream.writeAttribute(_("width"), QString::number(range.numericData1));
|
|
|
|
|
stream.writeAttribute(_("height"), QString::number(range.numericData2));
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-03 16:57:32 +02:00
|
|
|
if (event.detailType == PixmapReferenceCountChanged ||
|
|
|
|
|
event.detailType == PixmapCacheCountChanged)
|
2013-08-08 13:28:08 +02:00
|
|
|
stream.writeAttribute(_("refCount"), QString::number(range.numericData3));
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-03 16:57:32 +02:00
|
|
|
if (event.message == QmlDebug::SceneGraphFrame) {
|
2013-08-08 13:28:08 +02:00
|
|
|
// 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();
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-13 16:34:30 +02:00
|
|
|
void QmlProfilerFileWriter::calculateMeasuredTime()
|
2013-08-08 13:28:08 +02:00
|
|
|
{
|
|
|
|
|
// 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;
|
|
|
|
|
|
2014-06-13 16:34:30 +02:00
|
|
|
foreach (const QmlProfilerDataModel::QmlEventData &event, m_ranges) {
|
2013-08-08 13:28:08 +02:00
|
|
|
// whitelist
|
2014-06-13 16:34:30 +02:00
|
|
|
const QmlProfilerDataModel::QmlEventTypeData &type = m_qmlEvents[event.typeIndex];
|
|
|
|
|
if (!m_acceptedRangeTypes.contains(type.rangeType) &&
|
|
|
|
|
!m_acceptedMessages.contains(type.message))
|
2013-08-08 13:28:08 +02:00
|
|
|
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;
|
2013-11-11 22:20:47 +02:00
|
|
|
if (level == QmlDebug::Constants::QML_MIN_LEVEL)
|
2013-08-08 13:28:08 +02:00
|
|
|
duration += event.duration;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_measuredTime = duration;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace Internal
|
|
|
|
|
} // namespace QmlProfiler
|