2013-08-08 13:28:08 +02:00
|
|
|
/****************************************************************************
|
|
|
|
|
**
|
2016-01-15 14:57:40 +01:00
|
|
|
** Copyright (C) 2016 The Qt Company Ltd.
|
|
|
|
|
** Contact: https://www.qt.io/licensing/
|
2013-08-08 13:28:08 +02:00
|
|
|
**
|
|
|
|
|
** 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
|
2016-01-15 14:57:40 +01:00
|
|
|
** a written agreement between you and The Qt Company. For licensing terms
|
|
|
|
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
|
|
|
|
** information use the contact form at https://www.qt.io/contact-us.
|
2013-08-08 13:28:08 +02:00
|
|
|
**
|
2016-01-15 14:57:40 +01:00
|
|
|
** GNU General Public License Usage
|
|
|
|
|
** Alternatively, this file may be used under the terms of the GNU
|
|
|
|
|
** General Public License version 3 as published by the Free Software
|
|
|
|
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
|
|
|
|
** included in the packaging of this file. Please review the following
|
|
|
|
|
** information to ensure the GNU General Public License requirements will
|
|
|
|
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
2013-08-08 13:28:08 +02:00
|
|
|
**
|
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
|
|
#include "qmlprofilertracefile.h"
|
|
|
|
|
|
|
|
|
|
#include <utils/qtcassert.h>
|
|
|
|
|
|
|
|
|
|
#include <QIODevice>
|
|
|
|
|
#include <QStringList>
|
|
|
|
|
#include <QXmlStreamReader>
|
|
|
|
|
#include <QXmlStreamWriter>
|
2016-04-28 16:13:16 +02:00
|
|
|
#include <QStack>
|
2013-08-08 13:28:08 +02:00
|
|
|
#include <QDebug>
|
|
|
|
|
|
2016-05-02 12:18:57 +02:00
|
|
|
namespace QmlProfiler {
|
|
|
|
|
namespace Internal {
|
2013-08-08 13:28:08 +02:00
|
|
|
|
|
|
|
|
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"
|
|
|
|
|
};
|
|
|
|
|
|
2016-05-02 12:18:57 +02:00
|
|
|
Q_STATIC_ASSERT(sizeof(RANGE_TYPE_STRINGS) == MaximumRangeType * sizeof(const char *));
|
2014-06-03 16:57:32 +02:00
|
|
|
|
|
|
|
|
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",
|
2015-11-13 17:55:58 +01:00
|
|
|
"MemoryAllocation",
|
|
|
|
|
"DebugMessage"
|
2014-06-03 16:57:32 +02:00
|
|
|
};
|
|
|
|
|
|
2016-05-02 12:18:57 +02:00
|
|
|
Q_STATIC_ASSERT(sizeof(MESSAGE_STRINGS) == 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"
|
|
|
|
|
//
|
|
|
|
|
|
2015-02-03 23:48:57 +02:00
|
|
|
static QPair<Message, RangeType> qmlTypeAsEnum(const QString &typeString)
|
2013-08-08 13:28:08 +02:00
|
|
|
{
|
2015-06-30 14:30:56 +02:00
|
|
|
QPair<Message, RangeType> ret(MaximumMessage, MaximumRangeType);
|
2014-06-03 16:57:32 +02:00
|
|
|
|
2015-02-03 23:48:57 +02:00
|
|
|
for (int i = 0; i < MaximumMessage; ++i) {
|
2014-06-03 16:57:32 +02:00
|
|
|
if (typeString == _(MESSAGE_STRINGS[i])) {
|
2015-02-03 23:48:57 +02:00
|
|
|
ret.first = static_cast<Message>(i);
|
2014-06-03 16:57:32 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-03 23:48:57 +02:00
|
|
|
for (int i = 0; i < MaximumRangeType; ++i) {
|
2014-06-03 16:57:32 +02:00
|
|
|
if (typeString == _(RANGE_TYPE_STRINGS[i])) {
|
2015-02-03 23:48:57 +02:00
|
|
|
ret.second = static_cast<RangeType>(i);
|
2014-06-03 16:57:32 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-03 23:48:57 +02:00
|
|
|
if (ret.first == MaximumMessage && ret.second == MaximumRangeType) {
|
2013-08-08 13:28:08 +02:00
|
|
|
bool isNumber = false;
|
|
|
|
|
int type = typeString.toUInt(&isNumber);
|
2015-02-03 23:48:57 +02:00
|
|
|
if (isNumber && type < MaximumRangeType)
|
2014-06-03 16:57:32 +02:00
|
|
|
// Allow saving ranges as numbers, but not messages.
|
2015-02-03 23:48:57 +02:00
|
|
|
ret.second = static_cast<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
|
|
|
}
|
|
|
|
|
|
2015-02-03 23:48:57 +02:00
|
|
|
static QString qmlTypeAsString(Message message, RangeType rangeType)
|
2013-08-08 13:28:08 +02:00
|
|
|
{
|
2015-02-03 23:48:57 +02:00
|
|
|
if (rangeType < MaximumRangeType)
|
2014-06-03 16:57:32 +02:00
|
|
|
return _(RANGE_TYPE_STRINGS[rangeType]);
|
2015-02-03 23:48:57 +02:00
|
|
|
else if (message != MaximumMessage)
|
2014-06-03 16:57:32 +02:00
|
|
|
return _(MESSAGE_STRINGS[message]);
|
|
|
|
|
else
|
|
|
|
|
return QString::number((int)rangeType);
|
2013-08-08 13:28:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
QmlProfilerFileReader::QmlProfilerFileReader(QObject *parent) :
|
|
|
|
|
QObject(parent),
|
2016-02-08 16:51:48 +01:00
|
|
|
m_traceStart(-1),
|
|
|
|
|
m_traceEnd(-1),
|
2015-06-30 15:55:33 +02:00
|
|
|
m_future(0),
|
|
|
|
|
m_loadedFeatures(0)
|
2013-08-08 13:28:08 +02:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-17 14:25:24 +01:00
|
|
|
void QmlProfilerFileReader::setFuture(QFutureInterface<void> *future)
|
|
|
|
|
{
|
|
|
|
|
m_future = future;
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-08 13:28:08 +02:00
|
|
|
bool QmlProfilerFileReader::load(QIODevice *device)
|
|
|
|
|
{
|
2015-02-17 14:25:24 +01:00
|
|
|
if (m_future) {
|
2015-08-31 17:52:15 +02:00
|
|
|
m_future->setProgressRange(0, 1000);
|
2015-02-17 14:25:24 +01:00
|
|
|
m_future->setProgressValue(0);
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-08 13:28:08 +02:00
|
|
|
QXmlStreamReader stream(device);
|
|
|
|
|
|
|
|
|
|
bool validVersion = true;
|
|
|
|
|
|
|
|
|
|
while (validVersion && !stream.atEnd() && !stream.hasError()) {
|
2015-02-17 14:25:24 +01:00
|
|
|
if (isCanceled())
|
|
|
|
|
return false;
|
2015-02-17 14:31:04 +01:00
|
|
|
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")))
|
2016-02-08 16:51:48 +01:00
|
|
|
m_traceStart = attributes.value(_("traceStart")).toLongLong();
|
2015-02-17 14:31:04 +01:00
|
|
|
if (attributes.hasAttribute(_("traceEnd")))
|
2016-02-08 16:51:48 +01:00
|
|
|
m_traceEnd = attributes.value(_("traceEnd")).toLongLong();
|
2015-02-17 14:31:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (elementName == _("eventData")) {
|
2016-04-26 11:50:59 +02:00
|
|
|
loadEventTypes(stream);
|
2015-02-17 14:31:04 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (elementName == _("profilerDataModel")) {
|
2016-04-26 11:50:59 +02:00
|
|
|
loadEvents(stream);
|
2015-02-17 14:31:04 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (elementName == _("noteData")) {
|
2016-04-26 11:50:59 +02:00
|
|
|
loadNotes(stream);
|
2015-02-17 14:31:04 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default: break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (stream.hasError()) {
|
|
|
|
|
emit error(tr("Error while parsing trace data file: %1").arg(stream.errorString()));
|
|
|
|
|
return false;
|
|
|
|
|
} else {
|
2016-04-28 16:13:16 +02:00
|
|
|
std::sort(m_events.begin(), m_events.end(), [](const QmlEvent &a, const QmlEvent &b) {
|
|
|
|
|
return a.timestamp() < b.timestamp();
|
|
|
|
|
});
|
2016-02-08 16:51:48 +01:00
|
|
|
emit success();
|
2015-02-17 14:31:04 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
2013-08-08 13:28:08 +02:00
|
|
|
}
|
|
|
|
|
|
2015-06-30 15:55:33 +02:00
|
|
|
quint64 QmlProfilerFileReader::loadedFeatures() const
|
|
|
|
|
{
|
|
|
|
|
return m_loadedFeatures;
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-26 11:50:59 +02:00
|
|
|
ProfileFeature featureFromType(const QmlEventType &type) {
|
|
|
|
|
if (type.rangeType < MaximumRangeType)
|
|
|
|
|
return featureFromRangeType(type.rangeType);
|
2015-06-30 15:55:33 +02:00
|
|
|
|
2016-04-26 11:50:59 +02:00
|
|
|
switch (type.message) {
|
2015-06-30 15:55:33 +02:00
|
|
|
case Event:
|
2016-04-26 11:50:59 +02:00
|
|
|
switch (type.detailType) {
|
2015-06-30 15:55:33 +02:00
|
|
|
case AnimationFrame:
|
|
|
|
|
return ProfileAnimations;
|
|
|
|
|
case Key:
|
|
|
|
|
case Mouse:
|
|
|
|
|
return ProfileInputEvents;
|
|
|
|
|
default:
|
|
|
|
|
return MaximumProfileFeature;
|
|
|
|
|
}
|
|
|
|
|
case PixmapCacheEvent:
|
|
|
|
|
return ProfilePixmapCache;
|
|
|
|
|
case SceneGraphFrame:
|
|
|
|
|
return ProfileSceneGraph;
|
|
|
|
|
case MemoryAllocation:
|
|
|
|
|
return ProfileMemory;
|
2015-11-13 17:55:58 +01:00
|
|
|
case DebugMessage:
|
|
|
|
|
return ProfileDebugMessages;
|
2015-06-30 15:55:33 +02:00
|
|
|
default:
|
|
|
|
|
return MaximumProfileFeature;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-26 11:50:59 +02:00
|
|
|
void QmlProfilerFileReader::loadEventTypes(QXmlStreamReader &stream)
|
2013-08-08 13:28:08 +02:00
|
|
|
{
|
2013-08-29 11:04:09 +02:00
|
|
|
QTC_ASSERT(stream.name() == _("eventData"), return);
|
2013-08-08 13:28:08 +02:00
|
|
|
|
2016-04-26 11:50:59 +02:00
|
|
|
int typeIndex = -1;
|
|
|
|
|
QmlEventType type = {
|
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
|
|
|
};
|
2016-04-26 11:50:59 +02:00
|
|
|
const QmlEventType defaultEvent = type;
|
2013-08-08 13:28:08 +02:00
|
|
|
|
|
|
|
|
while (!stream.atEnd() && !stream.hasError()) {
|
2015-02-17 14:25:24 +01:00
|
|
|
if (isCanceled())
|
|
|
|
|
return;
|
|
|
|
|
|
2013-08-08 13:28:08 +02:00
|
|
|
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")) {
|
2015-02-17 14:25:24 +01:00
|
|
|
progress(stream.device());
|
2016-04-26 11:50:59 +02:00
|
|
|
type = defaultEvent;
|
2013-08-08 13:28:08 +02:00
|
|
|
|
|
|
|
|
const QXmlStreamAttributes attributes = stream.attributes();
|
|
|
|
|
if (attributes.hasAttribute(_("index"))) {
|
2016-04-26 11:50:59 +02:00
|
|
|
typeIndex = attributes.value(_("index")).toInt();
|
2013-08-08 13:28:08 +02:00
|
|
|
} else {
|
|
|
|
|
// ignore event
|
2016-04-26 11:50:59 +02:00
|
|
|
typeIndex = -1;
|
2013-08-08 13:28:08 +02:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
stream.readNext();
|
|
|
|
|
if (stream.tokenType() != QXmlStreamReader::Characters)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
const QString readData = stream.text().toString();
|
|
|
|
|
|
|
|
|
|
if (elementName == _("displayname")) {
|
2016-04-26 11:50:59 +02:00
|
|
|
type.displayName = readData;
|
2013-08-08 13:28:08 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (elementName == _("type")) {
|
2015-02-03 23:48:57 +02:00
|
|
|
QPair<Message, RangeType> enums = qmlTypeAsEnum(readData);
|
2016-04-26 11:50:59 +02:00
|
|
|
type.message = enums.first;
|
|
|
|
|
type.rangeType = enums.second;
|
2013-08-08 13:28:08 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (elementName == _("filename")) {
|
2016-04-26 11:50:59 +02:00
|
|
|
type.location.filename = readData;
|
2013-08-08 13:28:08 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (elementName == _("line")) {
|
2016-04-26 11:50:59 +02:00
|
|
|
type.location.line = readData.toInt();
|
2013-08-08 13:28:08 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (elementName == _("column")) {
|
2016-04-26 11:50:59 +02:00
|
|
|
type.location.column = readData.toInt();
|
2013-08-08 13:28:08 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (elementName == _("details")) {
|
2016-04-26 11:50:59 +02:00
|
|
|
type.data = readData;
|
2013-08-08 13:28:08 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-03 16:57:32 +02:00
|
|
|
if (elementName == _("animationFrame")) {
|
2016-04-26 11:50:59 +02:00
|
|
|
type.detailType = readData.toInt();
|
2014-06-03 16:57:32 +02:00
|
|
|
// 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.
|
2016-04-26 11:50:59 +02:00
|
|
|
if (type.detailType == 4) {
|
|
|
|
|
type.message = Event;
|
|
|
|
|
type.rangeType = MaximumRangeType;
|
|
|
|
|
type.detailType = AnimationFrame;
|
2014-06-03 16:57:32 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-08 13:28:08 +02:00
|
|
|
if (elementName == _("bindingType") ||
|
|
|
|
|
elementName == _("cacheEventType") ||
|
2014-06-13 19:18:01 +02:00
|
|
|
elementName == _("sgEventType") ||
|
2015-05-21 11:52:27 +02:00
|
|
|
elementName == _("memoryEventType") ||
|
|
|
|
|
elementName == _("mouseEvent") ||
|
2015-11-13 17:55:58 +01:00
|
|
|
elementName == _("keyEvent") ||
|
|
|
|
|
elementName == _("level")) {
|
2016-04-26 11:50:59 +02:00
|
|
|
type.detailType = readData.toInt();
|
2013-08-08 13:28:08 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case QXmlStreamReader::EndElement: {
|
|
|
|
|
if (elementName == _("event")) {
|
2016-04-26 11:50:59 +02:00
|
|
|
if (typeIndex >= 0) {
|
|
|
|
|
if (typeIndex >= m_eventTypes.size())
|
|
|
|
|
m_eventTypes.resize(typeIndex + 1);
|
|
|
|
|
m_eventTypes[typeIndex] = type;
|
|
|
|
|
ProfileFeature feature = featureFromType(type);
|
2015-06-30 15:55:33 +02:00
|
|
|
if (feature != MaximumProfileFeature)
|
|
|
|
|
m_loadedFeatures |= (1ULL << static_cast<uint>(feature));
|
2013-08-08 13:28:08 +02:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (elementName == _("eventData")) {
|
|
|
|
|
// done reading eventData
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default: break;
|
|
|
|
|
} // switch
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-26 11:50:59 +02:00
|
|
|
void QmlProfilerFileReader::loadEvents(QXmlStreamReader &stream)
|
2013-08-08 13:28:08 +02:00
|
|
|
{
|
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()) {
|
2015-02-17 14:25:24 +01:00
|
|
|
if (isCanceled())
|
|
|
|
|
return;
|
|
|
|
|
|
2013-08-08 13:28:08 +02:00
|
|
|
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")) {
|
2015-02-17 14:25:24 +01:00
|
|
|
progress(stream.device());
|
2016-04-28 15:57:12 +02:00
|
|
|
QmlEvent event;
|
2013-08-08 13:28:08 +02:00
|
|
|
|
|
|
|
|
const QXmlStreamAttributes attributes = stream.attributes();
|
|
|
|
|
if (!attributes.hasAttribute(_("startTime"))
|
|
|
|
|
|| !attributes.hasAttribute(_("eventIndex"))) {
|
|
|
|
|
// ignore incomplete entry
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-28 15:40:02 +02:00
|
|
|
event.setTimestamp(attributes.value(_("startTime")).toLongLong());
|
2016-04-26 11:50:59 +02:00
|
|
|
event.setTypeIndex(attributes.value(_("eventIndex")).toInt());
|
2013-08-08 13:28:08 +02:00
|
|
|
|
2016-04-28 16:13:16 +02:00
|
|
|
if (attributes.hasAttribute(_("duration"))) {
|
|
|
|
|
event.setRangeStage(RangeStart);
|
|
|
|
|
m_events.append(event);
|
|
|
|
|
QmlEvent rangeEnd(event);
|
|
|
|
|
rangeEnd.setRangeStage(RangeEnd);
|
|
|
|
|
rangeEnd.setTimestamp(event.timestamp()
|
|
|
|
|
+ attributes.value(_("duration")).toLongLong());
|
|
|
|
|
m_events.append(rangeEnd);
|
|
|
|
|
} else {
|
|
|
|
|
// attributes for special events
|
|
|
|
|
if (attributes.hasAttribute(_("framerate")))
|
|
|
|
|
event.setNumber<qint32>(0, attributes.value(_("framerate")).toInt());
|
|
|
|
|
if (attributes.hasAttribute(_("animationcount")))
|
|
|
|
|
event.setNumber<qint32>(1, attributes.value(_("animationcount")).toInt());
|
|
|
|
|
if (attributes.hasAttribute(_("thread")))
|
|
|
|
|
event.setNumber<qint32>(2, attributes.value(_("thread")).toInt());
|
|
|
|
|
if (attributes.hasAttribute(_("width")))
|
|
|
|
|
event.setNumber<qint32>(0, attributes.value(_("width")).toInt());
|
|
|
|
|
if (attributes.hasAttribute(_("height")))
|
|
|
|
|
event.setNumber<qint32>(1, attributes.value(_("height")).toInt());
|
|
|
|
|
if (attributes.hasAttribute(_("refCount")))
|
|
|
|
|
event.setNumber<qint32>(2, attributes.value(_("refCount")).toInt());
|
|
|
|
|
if (attributes.hasAttribute(_("amount")))
|
|
|
|
|
event.setNumber<qint64>(0, attributes.value(_("amount")).toLongLong());
|
|
|
|
|
if (attributes.hasAttribute(_("timing1")))
|
|
|
|
|
event.setNumber<qint64>(0, attributes.value(_("timing1")).toLongLong());
|
|
|
|
|
if (attributes.hasAttribute(_("timing2")))
|
|
|
|
|
event.setNumber<qint64>(1, attributes.value(_("timing2")).toLongLong());
|
|
|
|
|
if (attributes.hasAttribute(_("timing3")))
|
|
|
|
|
event.setNumber<qint64>(2, attributes.value(_("timing3")).toLongLong());
|
|
|
|
|
if (attributes.hasAttribute(_("timing4")))
|
|
|
|
|
event.setNumber<qint64>(3, attributes.value(_("timing4")).toLongLong());
|
|
|
|
|
if (attributes.hasAttribute(_("timing5")))
|
|
|
|
|
event.setNumber<qint64>(4, attributes.value(_("timing5")).toLongLong());
|
|
|
|
|
if (attributes.hasAttribute(_("type")))
|
|
|
|
|
event.setNumber<qint32>(0, attributes.value(_("type")).toInt());
|
|
|
|
|
if (attributes.hasAttribute(_("data1")))
|
|
|
|
|
event.setNumber<qint32>(1, attributes.value(_("data1")).toInt());
|
|
|
|
|
if (attributes.hasAttribute(_("data2")))
|
|
|
|
|
event.setNumber<qint32>(2, attributes.value(_("data2")).toInt());
|
|
|
|
|
if (attributes.hasAttribute(_("text")))
|
|
|
|
|
event.setString(attributes.value(_("text")).toString());
|
|
|
|
|
|
|
|
|
|
m_events.append(event);
|
|
|
|
|
}
|
2013-08-08 13:28:08 +02:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case QXmlStreamReader::EndElement: {
|
|
|
|
|
if (elementName == _("profilerDataModel")) {
|
|
|
|
|
// done reading profilerDataModel
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default: break;
|
|
|
|
|
} // switch
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-26 11:50:59 +02:00
|
|
|
void QmlProfilerFileReader::loadNotes(QXmlStreamReader &stream)
|
2014-09-24 18:23:01 +02:00
|
|
|
{
|
2016-04-26 11:50:59 +02:00
|
|
|
QmlNote currentNote;
|
2014-09-24 18:23:01 +02:00
|
|
|
while (!stream.atEnd() && !stream.hasError()) {
|
2015-02-17 14:25:24 +01:00
|
|
|
if (isCanceled())
|
|
|
|
|
return;
|
|
|
|
|
|
2014-09-24 18:23:01 +02:00
|
|
|
QXmlStreamReader::TokenType token = stream.readNext();
|
|
|
|
|
const QStringRef elementName = stream.name();
|
|
|
|
|
|
|
|
|
|
switch (token) {
|
|
|
|
|
case QXmlStreamReader::StartElement: {
|
|
|
|
|
if (elementName == _("note")) {
|
2015-02-17 14:25:24 +01:00
|
|
|
progress(stream.device());
|
2014-09-24 18:23:01 +02:00
|
|
|
QXmlStreamAttributes attrs = stream.attributes();
|
2015-11-13 15:31:32 +01:00
|
|
|
currentNote.startTime = attrs.value(_("startTime")).toLongLong();
|
|
|
|
|
currentNote.duration = attrs.value(_("duration")).toLongLong();
|
|
|
|
|
currentNote.typeIndex = attrs.value(_("eventIndex")).toInt();
|
2014-09-24 18:23:01 +02:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case QXmlStreamReader::Characters: {
|
|
|
|
|
currentNote.text = stream.text().toString();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case QXmlStreamReader::EndElement: {
|
|
|
|
|
if (elementName == _("note")) {
|
|
|
|
|
m_notes.append(currentNote);
|
|
|
|
|
} else if (elementName == _("noteData")) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-17 14:25:24 +01:00
|
|
|
void QmlProfilerFileReader::progress(QIODevice *device)
|
|
|
|
|
{
|
|
|
|
|
if (!m_future)
|
|
|
|
|
return;
|
|
|
|
|
|
2015-08-31 17:52:15 +02:00
|
|
|
m_future->setProgressValue(device->pos() * 1000 / device->size());
|
2015-02-17 14:25:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool QmlProfilerFileReader::isCanceled() const
|
|
|
|
|
{
|
|
|
|
|
return m_future && m_future->isCanceled();
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-08 13:28:08 +02:00
|
|
|
QmlProfilerFileWriter::QmlProfilerFileWriter(QObject *parent) :
|
|
|
|
|
QObject(parent),
|
|
|
|
|
m_startTime(0),
|
|
|
|
|
m_endTime(0),
|
|
|
|
|
m_measuredTime(0),
|
2015-02-10 20:29:54 +01:00
|
|
|
m_future(0)
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-26 11:50:59 +02:00
|
|
|
void QmlProfilerFileWriter::setData(const QVector<QmlEventType> &types,
|
|
|
|
|
const QVector<QmlEvent> &events)
|
2013-08-08 13:28:08 +02:00
|
|
|
{
|
2016-04-26 11:50:59 +02:00
|
|
|
m_eventTypes = types;
|
|
|
|
|
m_events = events;
|
2013-08-08 13:28:08 +02:00
|
|
|
}
|
|
|
|
|
|
2016-04-26 11:50:59 +02:00
|
|
|
void QmlProfilerFileWriter::setNotes(const QVector<QmlNote> ¬es)
|
2014-09-24 18:23:01 +02:00
|
|
|
{
|
|
|
|
|
m_notes = notes;
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-10 20:29:54 +01:00
|
|
|
void QmlProfilerFileWriter::setFuture(QFutureInterface<void> *future)
|
|
|
|
|
{
|
|
|
|
|
m_future = future;
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-08 13:28:08 +02:00
|
|
|
void QmlProfilerFileWriter::save(QIODevice *device)
|
|
|
|
|
{
|
2015-02-10 20:29:54 +01:00
|
|
|
if (m_future) {
|
|
|
|
|
m_future->setProgressRange(0,
|
2016-04-26 11:50:59 +02:00
|
|
|
qMax(m_eventTypes.size() + m_events.size() + m_notes.size(), 1));
|
2015-02-10 20:29:54 +01:00
|
|
|
m_future->setProgressValue(0);
|
|
|
|
|
m_newProgressValue = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-08 13:28:08 +02:00
|
|
|
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));
|
|
|
|
|
|
2016-04-26 11:50:59 +02:00
|
|
|
for (int typeIndex = 0; typeIndex < m_eventTypes.size(); ++typeIndex) {
|
2015-02-10 20:29:54 +01:00
|
|
|
if (isCanceled())
|
|
|
|
|
return;
|
|
|
|
|
|
2016-04-26 11:50:59 +02:00
|
|
|
const QmlEventType &type = m_eventTypes[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));
|
2016-04-26 11:50:59 +02:00
|
|
|
stream.writeTextElement(_("displayname"), type.displayName);
|
|
|
|
|
stream.writeTextElement(_("type"), qmlTypeAsString(type.message, type.rangeType));
|
|
|
|
|
if (!type.location.filename.isEmpty()) {
|
|
|
|
|
stream.writeTextElement(_("filename"), type.location.filename);
|
|
|
|
|
stream.writeTextElement(_("line"), QString::number(type.location.line));
|
|
|
|
|
stream.writeTextElement(_("column"), QString::number(type.location.column));
|
2013-08-08 13:28:08 +02:00
|
|
|
}
|
2014-06-13 19:16:35 +02:00
|
|
|
|
2016-04-26 11:50:59 +02:00
|
|
|
if (!type.data.isEmpty())
|
|
|
|
|
stream.writeTextElement(_("details"), type.data);
|
2014-06-13 19:16:35 +02:00
|
|
|
|
2016-04-26 11:50:59 +02:00
|
|
|
if (type.rangeType == Binding) {
|
|
|
|
|
stream.writeTextElement(_("bindingType"), QString::number(type.detailType));
|
|
|
|
|
} else if (type.message == Event) {
|
|
|
|
|
switch (type.detailType) {
|
2015-05-21 11:52:27 +02:00
|
|
|
case AnimationFrame:
|
2016-04-26 11:50:59 +02:00
|
|
|
stream.writeTextElement(_("animationFrame"), QString::number(type.detailType));
|
2015-05-21 11:52:27 +02:00
|
|
|
break;
|
|
|
|
|
case Key:
|
2016-04-26 11:50:59 +02:00
|
|
|
stream.writeTextElement(_("keyEvent"), QString::number(type.detailType));
|
2015-05-21 11:52:27 +02:00
|
|
|
break;
|
|
|
|
|
case Mouse:
|
2016-04-26 11:50:59 +02:00
|
|
|
stream.writeTextElement(_("mouseEvent"), QString::number(type.detailType));
|
2015-05-21 11:52:27 +02:00
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
2016-04-26 11:50:59 +02:00
|
|
|
} else if (type.message == PixmapCacheEvent) {
|
|
|
|
|
stream.writeTextElement(_("cacheEventType"), QString::number(type.detailType));
|
|
|
|
|
} else if (type.message == SceneGraphFrame) {
|
|
|
|
|
stream.writeTextElement(_("sgEventType"), QString::number(type.detailType));
|
|
|
|
|
} else if (type.message == MemoryAllocation) {
|
|
|
|
|
stream.writeTextElement(_("memoryEventType"), QString::number(type.detailType));
|
|
|
|
|
} else if (type.message == DebugMessage) {
|
|
|
|
|
stream.writeTextElement(_("level"), QString::number(type.detailType));
|
2015-05-21 11:52:27 +02:00
|
|
|
}
|
2013-08-08 13:28:08 +02:00
|
|
|
stream.writeEndElement();
|
2015-02-10 20:29:54 +01:00
|
|
|
incrementProgress();
|
2013-08-08 13:28:08 +02:00
|
|
|
}
|
|
|
|
|
stream.writeEndElement(); // eventData
|
|
|
|
|
|
|
|
|
|
stream.writeStartElement(_("profilerDataModel"));
|
|
|
|
|
|
2016-04-28 16:13:16 +02:00
|
|
|
QStack<QmlEvent> stack;
|
2016-04-26 11:50:59 +02:00
|
|
|
for (int rangeIndex = 0; rangeIndex < m_events.size(); ++rangeIndex) {
|
2015-02-10 20:29:54 +01:00
|
|
|
if (isCanceled())
|
|
|
|
|
return;
|
|
|
|
|
|
2016-04-26 11:50:59 +02:00
|
|
|
const QmlEvent &event = m_events[rangeIndex];
|
2016-04-28 16:13:16 +02:00
|
|
|
const QmlEventType &type = m_eventTypes[event.typeIndex()];
|
|
|
|
|
if (type.rangeType != MaximumRangeType && event.rangeStage() == RangeStart) {
|
|
|
|
|
stack.push(event);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2013-08-08 13:28:08 +02:00
|
|
|
|
|
|
|
|
stream.writeStartElement(_("range"));
|
2016-04-28 16:13:16 +02:00
|
|
|
if (type.rangeType != MaximumRangeType && event.rangeStage() == RangeEnd) {
|
|
|
|
|
QmlEvent start = stack.pop();
|
|
|
|
|
stream.writeAttribute(_("startTime"), QString::number(start.timestamp()));
|
|
|
|
|
stream.writeAttribute(_("duration"),
|
|
|
|
|
QString::number(event.timestamp() - start.timestamp()));
|
|
|
|
|
} else {
|
|
|
|
|
stream.writeAttribute(_("startTime"), QString::number(event.timestamp()));
|
|
|
|
|
}
|
2013-08-08 13:28:08 +02:00
|
|
|
|
2016-04-28 16:13:16 +02:00
|
|
|
stream.writeAttribute(_("eventIndex"), QString::number(event.typeIndex()));
|
2013-08-08 13:28:08 +02:00
|
|
|
|
2016-04-26 11:50:59 +02:00
|
|
|
if (type.message == Event) {
|
|
|
|
|
if (type.detailType == AnimationFrame) {
|
2015-10-23 17:46:59 +02:00
|
|
|
// special: animation event
|
2016-04-28 15:57:12 +02:00
|
|
|
stream.writeAttribute(_("framerate"), QString::number(event.number<qint32>(0)));
|
|
|
|
|
stream.writeAttribute(_("animationcount"),
|
|
|
|
|
QString::number(event.number<qint32>(1)));
|
|
|
|
|
stream.writeAttribute(_("thread"), QString::number(event.number<qint32>(2)));
|
2016-04-26 11:50:59 +02:00
|
|
|
} else if (type.detailType == Key || type.detailType == Mouse) {
|
2015-10-23 17:46:59 +02:00
|
|
|
// special: input event
|
2016-04-28 15:57:12 +02:00
|
|
|
stream.writeAttribute(_("type"), QString::number(event.number<qint32>(0)));
|
|
|
|
|
stream.writeAttribute(_("data1"), QString::number(event.number<qint32>(1)));
|
|
|
|
|
stream.writeAttribute(_("data2"), QString::number(event.number<qint32>(2)));
|
2015-10-23 17:46:59 +02:00
|
|
|
}
|
2013-08-08 13:28:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// special: pixmap cache event
|
2016-04-26 11:50:59 +02:00
|
|
|
if (type.message == PixmapCacheEvent) {
|
|
|
|
|
if (type.detailType == PixmapSizeKnown) {
|
2016-04-28 15:57:12 +02:00
|
|
|
stream.writeAttribute(_("width"), QString::number(event.number<qint32>(0)));
|
|
|
|
|
stream.writeAttribute(_("height"), QString::number(event.number<qint32>(1)));
|
2013-08-08 13:28:08 +02:00
|
|
|
}
|
|
|
|
|
|
2016-04-26 11:50:59 +02:00
|
|
|
if (type.detailType == PixmapReferenceCountChanged ||
|
|
|
|
|
type.detailType == PixmapCacheCountChanged)
|
2016-04-28 15:57:12 +02:00
|
|
|
stream.writeAttribute(_("refCount"), QString::number(event.number<qint32>(2)));
|
2013-08-08 13:28:08 +02:00
|
|
|
}
|
|
|
|
|
|
2016-04-26 11:50:59 +02:00
|
|
|
if (type.message == SceneGraphFrame) {
|
2013-08-08 13:28:08 +02:00
|
|
|
// special: scenegraph frame events
|
2016-04-28 15:57:12 +02:00
|
|
|
for (int i = 0; i < 5; ++i) {
|
|
|
|
|
qint64 number = event.number<qint64>(i);
|
|
|
|
|
if (number <= 0)
|
|
|
|
|
continue;
|
|
|
|
|
stream.writeAttribute(QString::fromLatin1("timing%1").arg(i + 1),
|
|
|
|
|
QString::number(number));
|
|
|
|
|
}
|
2013-08-08 13:28:08 +02:00
|
|
|
}
|
2014-06-13 19:18:01 +02:00
|
|
|
|
|
|
|
|
// special: memory allocation event
|
2016-04-26 11:50:59 +02:00
|
|
|
if (type.message == MemoryAllocation)
|
2016-04-28 15:57:12 +02:00
|
|
|
stream.writeAttribute(_("amount"), QString::number(event.number<qint64>(0)));
|
2014-06-13 19:18:01 +02:00
|
|
|
|
2016-04-26 11:50:59 +02:00
|
|
|
if (type.message == DebugMessage)
|
2016-04-28 15:57:12 +02:00
|
|
|
stream.writeAttribute(_("text"), event.string());
|
2015-11-13 17:55:58 +01:00
|
|
|
|
2013-08-08 13:28:08 +02:00
|
|
|
stream.writeEndElement();
|
2015-02-10 20:29:54 +01:00
|
|
|
incrementProgress();
|
2013-08-08 13:28:08 +02:00
|
|
|
}
|
|
|
|
|
stream.writeEndElement(); // profilerDataModel
|
|
|
|
|
|
2014-09-24 18:23:01 +02:00
|
|
|
stream.writeStartElement(_("noteData"));
|
|
|
|
|
for (int noteIndex = 0; noteIndex < m_notes.size(); ++noteIndex) {
|
2015-02-10 20:29:54 +01:00
|
|
|
if (isCanceled())
|
|
|
|
|
return;
|
|
|
|
|
|
2016-04-28 16:02:54 +02:00
|
|
|
const QmlNote ¬e = m_notes[noteIndex];
|
2014-09-24 18:23:01 +02:00
|
|
|
stream.writeStartElement(_("note"));
|
2016-04-28 16:02:54 +02:00
|
|
|
stream.writeAttribute(_("startTime"), QString::number(note.startTime));
|
|
|
|
|
stream.writeAttribute(_("duration"), QString::number(note.duration));
|
|
|
|
|
stream.writeAttribute(_("eventIndex"), QString::number(note.typeIndex));
|
|
|
|
|
stream.writeCharacters(note.text);
|
2014-09-24 18:23:01 +02:00
|
|
|
stream.writeEndElement(); // note
|
2015-02-10 20:29:54 +01:00
|
|
|
incrementProgress();
|
2014-09-24 18:23:01 +02:00
|
|
|
}
|
|
|
|
|
stream.writeEndElement(); // noteData
|
|
|
|
|
|
2015-02-10 20:29:54 +01:00
|
|
|
if (isCanceled())
|
|
|
|
|
return;
|
2013-08-08 13:28:08 +02:00
|
|
|
|
|
|
|
|
stream.writeEndElement(); // trace
|
|
|
|
|
stream.writeEndDocument();
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-10 20:29:54 +01:00
|
|
|
void QmlProfilerFileWriter::incrementProgress()
|
|
|
|
|
{
|
|
|
|
|
if (!m_future)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
m_newProgressValue++;
|
|
|
|
|
if (float(m_newProgressValue - m_future->progressValue())
|
|
|
|
|
/ float(m_future->progressMaximum() - m_future->progressMinimum()) >= 0.01) {
|
|
|
|
|
m_future->setProgressValue(m_newProgressValue);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool QmlProfilerFileWriter::isCanceled() const
|
|
|
|
|
{
|
|
|
|
|
return m_future && m_future->isCanceled();
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-08 13:28:08 +02:00
|
|
|
} // namespace Internal
|
|
|
|
|
} // namespace QmlProfiler
|