2012-03-08 15:50:14 +00:00
|
|
|
/****************************************************************************
|
|
|
|
**
|
2015-01-28 11:55:39 +00:00
|
|
|
** Copyright (C) 2015 The Qt Company Ltd.
|
|
|
|
** Contact: http://www.qt.io/licensing/
|
2012-03-08 15:50:14 +00:00
|
|
|
**
|
|
|
|
** This file is part of the QtQml module of the Qt Toolkit.
|
|
|
|
**
|
2014-08-22 06:13:59 +00:00
|
|
|
** $QT_BEGIN_LICENSE:LGPL21$
|
2012-09-20 05:21:40 +00:00
|
|
|
** 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
|
2015-01-28 11:55:39 +00:00
|
|
|
** a written agreement between you and The Qt Company. For licensing terms
|
|
|
|
** and conditions see http://www.qt.io/terms-conditions. For further
|
|
|
|
** information use the contact form at http://www.qt.io/contact-us.
|
2012-09-20 05:21:40 +00:00
|
|
|
**
|
2012-03-08 15:50:14 +00:00
|
|
|
** GNU Lesser General Public License Usage
|
2012-09-20 05:21:40 +00:00
|
|
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
2014-08-22 06:13:59 +00:00
|
|
|
** General Public License version 2.1 or version 3 as published by the Free
|
|
|
|
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
|
|
|
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
|
|
|
** following information to ensure the GNU Lesser General Public License
|
|
|
|
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
|
|
|
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
2012-09-20 05:21:40 +00:00
|
|
|
**
|
2015-01-28 11:55:39 +00:00
|
|
|
** As a special exception, The Qt Company gives you certain additional
|
|
|
|
** rights. These rights are described in The Qt Company LGPL Exception
|
2012-03-08 15:50:14 +00:00
|
|
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
|
|
**
|
|
|
|
** $QT_END_LICENSE$
|
|
|
|
**
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#include "qmlprofilerdata.h"
|
|
|
|
|
|
|
|
#include <QStringList>
|
|
|
|
#include <QUrl>
|
|
|
|
#include <QHash>
|
|
|
|
#include <QFile>
|
|
|
|
#include <QXmlStreamReader>
|
|
|
|
|
2014-02-19 17:11:41 +00:00
|
|
|
const char PROFILER_FILE_VERSION[] = "1.02";
|
|
|
|
|
|
|
|
static const char *RANGE_TYPE_STRINGS[] = {
|
|
|
|
"Painting",
|
|
|
|
"Compiling",
|
|
|
|
"Creating",
|
|
|
|
"Binding",
|
|
|
|
"HandlingSignal",
|
|
|
|
"Javascript"
|
|
|
|
};
|
|
|
|
|
|
|
|
Q_STATIC_ASSERT(sizeof(RANGE_TYPE_STRINGS) ==
|
|
|
|
QQmlProfilerDefinitions::MaximumRangeType * sizeof(const char *));
|
|
|
|
|
|
|
|
static const char *MESSAGE_STRINGS[] = {
|
|
|
|
"Event",
|
|
|
|
"RangeStart",
|
|
|
|
"RangeData",
|
|
|
|
"RangeLocation",
|
|
|
|
"RangeEnd",
|
|
|
|
"Complete",
|
|
|
|
"PixmapCache",
|
2014-06-02 16:33:19 +00:00
|
|
|
"SceneGraph",
|
|
|
|
"MemoryAllocation"
|
2014-02-19 17:11:41 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Q_STATIC_ASSERT(sizeof(MESSAGE_STRINGS) ==
|
|
|
|
QQmlProfilerDefinitions::MaximumMessage * sizeof(const char *));
|
2012-03-08 15:50:14 +00:00
|
|
|
|
|
|
|
struct QmlRangeEventData {
|
|
|
|
QmlRangeEventData() {} // never called
|
2014-02-19 17:11:41 +00:00
|
|
|
QmlRangeEventData(const QString &_displayName, int _detailType, const QString &_eventHashStr,
|
|
|
|
const QmlEventLocation &_location, const QString &_details,
|
|
|
|
QQmlProfilerService::Message _message,
|
|
|
|
QQmlProfilerService::RangeType _rangeType)
|
|
|
|
: displayName(_displayName), eventHashStr(_eventHashStr), location(_location),
|
|
|
|
details(_details), message(_message), rangeType(_rangeType), detailType(_detailType) {}
|
2012-03-08 15:50:14 +00:00
|
|
|
QString displayName;
|
|
|
|
QString eventHashStr;
|
|
|
|
QmlEventLocation location;
|
|
|
|
QString details;
|
2014-02-19 17:11:41 +00:00
|
|
|
QQmlProfilerService::Message message;
|
|
|
|
QQmlProfilerService::RangeType rangeType;
|
|
|
|
int detailType; // can be BindingType, PixmapCacheEventType or SceneGraphFrameType
|
2012-03-08 15:50:14 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct QmlRangeEventStartInstance {
|
|
|
|
QmlRangeEventStartInstance() {} // never called
|
|
|
|
QmlRangeEventStartInstance(qint64 _startTime, qint64 _duration, int _frameRate,
|
2014-03-17 13:52:01 +00:00
|
|
|
int _animationCount, int _threadId, QmlRangeEventData *_data)
|
2012-03-08 15:50:14 +00:00
|
|
|
: startTime(_startTime), duration(_duration), frameRate(_frameRate),
|
2014-02-19 17:11:41 +00:00
|
|
|
animationCount(_animationCount), threadId(_threadId), numericData4(-1), numericData5(-1),
|
|
|
|
data(_data)
|
|
|
|
{ }
|
|
|
|
|
|
|
|
QmlRangeEventStartInstance(qint64 _startTime, qint64 _numericData1, qint64 _numericData2,
|
|
|
|
qint64 _numericData3, qint64 _numericData4, qint64 _numericData5,
|
|
|
|
QmlRangeEventData *_data)
|
|
|
|
: startTime(_startTime), duration(-1), numericData1(_numericData1),
|
|
|
|
numericData2(_numericData2), numericData3(_numericData3), numericData4(_numericData4),
|
|
|
|
numericData5(_numericData5), data(_data)
|
2012-03-08 15:50:14 +00:00
|
|
|
{ }
|
|
|
|
qint64 startTime;
|
|
|
|
qint64 duration;
|
2014-02-19 17:11:41 +00:00
|
|
|
union {
|
|
|
|
int frameRate;
|
|
|
|
qint64 numericData1;
|
|
|
|
};
|
|
|
|
union {
|
|
|
|
int animationCount;
|
|
|
|
qint64 numericData2;
|
|
|
|
};
|
|
|
|
union {
|
|
|
|
int threadId;
|
|
|
|
qint64 numericData3;
|
|
|
|
};
|
|
|
|
qint64 numericData4;
|
|
|
|
qint64 numericData5;
|
2012-03-08 15:50:14 +00:00
|
|
|
QmlRangeEventData *data;
|
|
|
|
};
|
|
|
|
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
Q_DECLARE_TYPEINFO(QmlRangeEventData, Q_MOVABLE_TYPE);
|
|
|
|
Q_DECLARE_TYPEINFO(QmlRangeEventStartInstance, Q_MOVABLE_TYPE);
|
|
|
|
QT_END_NAMESPACE
|
|
|
|
|
|
|
|
struct QV8EventInfo {
|
|
|
|
QString displayName;
|
|
|
|
QString eventHashStr;
|
|
|
|
QString functionName;
|
|
|
|
QString fileName;
|
|
|
|
int line;
|
|
|
|
qint64 totalTime;
|
|
|
|
qint64 selfTime;
|
|
|
|
|
|
|
|
QHash<QString, qint64> v8children;
|
|
|
|
};
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
|
|
class QmlProfilerDataPrivate
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
QmlProfilerDataPrivate(QmlProfilerData *qq){ Q_UNUSED(qq); }
|
|
|
|
|
|
|
|
// data storage
|
|
|
|
QHash<QString, QmlRangeEventData *> eventDescriptions;
|
|
|
|
QVector<QmlRangeEventStartInstance> startInstanceList;
|
|
|
|
QHash<QString, QV8EventInfo *> v8EventHash;
|
|
|
|
|
|
|
|
qint64 traceStartTime;
|
|
|
|
qint64 traceEndTime;
|
|
|
|
|
|
|
|
// internal state while collecting events
|
|
|
|
qint64 qmlMeasuredTime;
|
|
|
|
qint64 v8MeasuredTime;
|
|
|
|
QHash<int, QV8EventInfo *> v8parents;
|
|
|
|
void clearV8RootEvent();
|
|
|
|
QV8EventInfo v8RootEvent;
|
|
|
|
|
|
|
|
QmlProfilerData::State state;
|
|
|
|
};
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
|
|
QmlProfilerData::QmlProfilerData(QObject *parent) :
|
|
|
|
QObject(parent),d(new QmlProfilerDataPrivate(this))
|
|
|
|
{
|
|
|
|
d->state = Empty;
|
|
|
|
clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
QmlProfilerData::~QmlProfilerData()
|
|
|
|
{
|
|
|
|
clear();
|
|
|
|
delete d;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QmlProfilerData::clear()
|
|
|
|
{
|
2015-06-19 16:57:54 +00:00
|
|
|
qDeleteAll(d->eventDescriptions);
|
2012-03-08 15:50:14 +00:00
|
|
|
d->eventDescriptions.clear();
|
|
|
|
d->startInstanceList.clear();
|
|
|
|
|
2015-06-19 16:57:54 +00:00
|
|
|
qDeleteAll(d->v8EventHash);
|
2012-03-08 15:50:14 +00:00
|
|
|
d->v8EventHash.clear();
|
|
|
|
d->v8parents.clear();
|
|
|
|
d->clearV8RootEvent();
|
|
|
|
d->v8MeasuredTime = 0;
|
|
|
|
|
2015-05-20 09:35:04 +00:00
|
|
|
d->traceEndTime = std::numeric_limits<qint64>::min();
|
|
|
|
d->traceStartTime = std::numeric_limits<qint64>::max();
|
2012-03-08 15:50:14 +00:00
|
|
|
d->qmlMeasuredTime = 0;
|
|
|
|
|
|
|
|
setState(Empty);
|
|
|
|
}
|
|
|
|
|
|
|
|
QString QmlProfilerData::getHashStringForQmlEvent(const QmlEventLocation &location, int eventType)
|
|
|
|
{
|
|
|
|
return QString(QStringLiteral("%1:%2:%3:%4")).arg(
|
|
|
|
location.filename,
|
|
|
|
QString::number(location.line),
|
|
|
|
QString::number(location.column),
|
|
|
|
QString::number(eventType));
|
|
|
|
}
|
|
|
|
|
|
|
|
QString QmlProfilerData::getHashStringForV8Event(const QString &displayName, const QString &function)
|
|
|
|
{
|
|
|
|
return QString(QStringLiteral("%1:%2")).arg(displayName, function);
|
|
|
|
}
|
|
|
|
|
2014-02-19 17:11:41 +00:00
|
|
|
QString QmlProfilerData::qmlRangeTypeAsString(QQmlProfilerService::RangeType type)
|
2012-03-08 15:50:14 +00:00
|
|
|
{
|
2014-02-19 17:11:41 +00:00
|
|
|
if (type * sizeof(QString) < sizeof(RANGE_TYPE_STRINGS))
|
|
|
|
return QLatin1String(RANGE_TYPE_STRINGS[type]);
|
|
|
|
else
|
|
|
|
return QString::number(type);
|
|
|
|
}
|
|
|
|
|
|
|
|
QString QmlProfilerData::qmlMessageAsString(QQmlProfilerService::Message type)
|
|
|
|
{
|
|
|
|
if (type * sizeof(QString) < sizeof(MESSAGE_STRINGS))
|
|
|
|
return QLatin1String(MESSAGE_STRINGS[type]);
|
|
|
|
else
|
|
|
|
return QString::number(type);
|
2012-03-08 15:50:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QmlProfilerData::setTraceStartTime(qint64 time)
|
|
|
|
{
|
2015-05-20 09:35:04 +00:00
|
|
|
if (time < d->traceStartTime)
|
|
|
|
d->traceStartTime = time;
|
2012-03-08 15:50:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QmlProfilerData::setTraceEndTime(qint64 time)
|
|
|
|
{
|
2015-05-20 09:35:04 +00:00
|
|
|
if (time > d->traceEndTime)
|
|
|
|
d->traceEndTime = time;
|
2012-03-08 15:50:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
qint64 QmlProfilerData::traceStartTime() const
|
|
|
|
{
|
|
|
|
return d->traceStartTime;
|
|
|
|
}
|
|
|
|
|
|
|
|
qint64 QmlProfilerData::traceEndTime() const
|
|
|
|
{
|
|
|
|
return d->traceEndTime;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QmlProfilerData::addQmlEvent(QQmlProfilerService::RangeType type,
|
2012-05-02 15:32:57 +00:00
|
|
|
QQmlProfilerService::BindingType bindingType,
|
2012-03-08 15:50:14 +00:00
|
|
|
qint64 startTime,
|
|
|
|
qint64 duration,
|
|
|
|
const QStringList &data,
|
|
|
|
const QmlEventLocation &location)
|
|
|
|
{
|
|
|
|
setState(AcquiringData);
|
|
|
|
|
|
|
|
QString details;
|
|
|
|
// generate details string
|
|
|
|
if (data.isEmpty())
|
|
|
|
details = tr("Source code not available");
|
|
|
|
else {
|
|
|
|
details = data.join(QStringLiteral(" ")).replace(
|
|
|
|
QLatin1Char('\n'),QStringLiteral(" ")).simplified();
|
|
|
|
QRegExp rewrite(QStringLiteral("\\(function \\$(\\w+)\\(\\) \\{ (return |)(.+) \\}\\)"));
|
|
|
|
bool match = rewrite.exactMatch(details);
|
|
|
|
if (match) {
|
|
|
|
details = rewrite.cap(1) +QLatin1String(": ") + rewrite.cap(3);
|
|
|
|
}
|
|
|
|
if (details.startsWith(QLatin1String("file://")))
|
|
|
|
details = details.mid(details.lastIndexOf(QLatin1Char('/')) + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
QmlEventLocation eventLocation = location;
|
|
|
|
QString displayName, eventHashStr;
|
|
|
|
// generate hash
|
|
|
|
if (eventLocation.filename.isEmpty()) {
|
|
|
|
displayName = tr("<bytecode>");
|
|
|
|
eventHashStr = getHashStringForQmlEvent(eventLocation, type);
|
|
|
|
} else {
|
|
|
|
const QString filePath = QUrl(eventLocation.filename).path();
|
|
|
|
displayName = filePath.mid(
|
|
|
|
filePath.lastIndexOf(QLatin1Char('/')) + 1) +
|
|
|
|
QLatin1Char(':') + QString::number(eventLocation.line);
|
|
|
|
eventHashStr = getHashStringForQmlEvent(eventLocation, type);
|
|
|
|
}
|
|
|
|
|
|
|
|
QmlRangeEventData *newEvent;
|
|
|
|
if (d->eventDescriptions.contains(eventHashStr)) {
|
|
|
|
newEvent = d->eventDescriptions[eventHashStr];
|
|
|
|
} else {
|
2014-02-19 17:11:41 +00:00
|
|
|
newEvent = new QmlRangeEventData(displayName, bindingType, eventHashStr, location, details,
|
|
|
|
QQmlProfilerService::MaximumMessage, type);
|
2012-03-08 15:50:14 +00:00
|
|
|
d->eventDescriptions.insert(eventHashStr, newEvent);
|
|
|
|
}
|
|
|
|
|
2014-03-17 13:52:01 +00:00
|
|
|
QmlRangeEventStartInstance rangeEventStartInstance(startTime, duration, -1, -1, -1, newEvent);
|
2012-03-08 15:50:14 +00:00
|
|
|
|
|
|
|
d->startInstanceList.append(rangeEventStartInstance);
|
|
|
|
}
|
|
|
|
|
2014-03-17 13:52:01 +00:00
|
|
|
void QmlProfilerData::addFrameEvent(qint64 time, int framerate, int animationcount, int threadId)
|
2012-03-08 15:50:14 +00:00
|
|
|
{
|
|
|
|
setState(AcquiringData);
|
|
|
|
|
|
|
|
QString details = tr("Animation Timer Update");
|
|
|
|
QString displayName = tr("<Animation Update>");
|
|
|
|
QString eventHashStr = displayName;
|
|
|
|
|
|
|
|
QmlRangeEventData *newEvent;
|
|
|
|
if (d->eventDescriptions.contains(eventHashStr)) {
|
|
|
|
newEvent = d->eventDescriptions[eventHashStr];
|
|
|
|
} else {
|
2014-02-19 17:11:41 +00:00
|
|
|
newEvent = new QmlRangeEventData(displayName, QQmlProfilerService::AnimationFrame,
|
|
|
|
eventHashStr,
|
|
|
|
QmlEventLocation(), details,
|
|
|
|
QQmlProfilerService::Event,
|
|
|
|
QQmlProfilerService::MaximumRangeType);
|
2012-03-08 15:50:14 +00:00
|
|
|
d->eventDescriptions.insert(eventHashStr, newEvent);
|
|
|
|
}
|
|
|
|
|
2014-03-17 13:52:01 +00:00
|
|
|
QmlRangeEventStartInstance rangeEventStartInstance(time, -1, framerate, animationcount,
|
|
|
|
threadId, newEvent);
|
2012-03-08 15:50:14 +00:00
|
|
|
|
|
|
|
d->startInstanceList.append(rangeEventStartInstance);
|
|
|
|
}
|
|
|
|
|
2014-02-19 17:11:41 +00:00
|
|
|
void QmlProfilerData::addSceneGraphFrameEvent(QQmlProfilerDefinitions::SceneGraphFrameType type, qint64 time, qint64 numericData1, qint64 numericData2, qint64 numericData3, qint64 numericData4, qint64 numericData5)
|
|
|
|
{
|
|
|
|
setState(AcquiringData);
|
|
|
|
|
|
|
|
QString eventHashStr = QString::fromLatin1("SceneGraph:%1").arg(type);
|
|
|
|
QmlRangeEventData *newEvent;
|
|
|
|
if (d->eventDescriptions.contains(eventHashStr)) {
|
|
|
|
newEvent = d->eventDescriptions[eventHashStr];
|
|
|
|
} else {
|
|
|
|
newEvent = new QmlRangeEventData(QStringLiteral("<SceneGraph>"), type, eventHashStr,
|
|
|
|
QmlEventLocation(), QString(),
|
|
|
|
QQmlProfilerService::SceneGraphFrame,
|
|
|
|
QQmlProfilerService::MaximumRangeType);
|
|
|
|
d->eventDescriptions.insert(eventHashStr, newEvent);
|
|
|
|
}
|
|
|
|
|
|
|
|
QmlRangeEventStartInstance rangeEventStartInstance(time, numericData1, numericData2,
|
|
|
|
numericData3, numericData4, numericData5,
|
|
|
|
newEvent);
|
|
|
|
d->startInstanceList.append(rangeEventStartInstance);
|
|
|
|
}
|
|
|
|
|
|
|
|
void QmlProfilerData::addPixmapCacheEvent(QQmlProfilerDefinitions::PixmapEventType type,
|
|
|
|
qint64 time, const QmlEventLocation &location,
|
|
|
|
int width, int height, int refcount)
|
|
|
|
{
|
|
|
|
setState(AcquiringData);
|
|
|
|
|
|
|
|
QString filePath = QUrl(location.filename).path();
|
|
|
|
|
|
|
|
QString eventHashStr = filePath.mid(filePath.lastIndexOf(QLatin1Char('/')) + 1) +
|
|
|
|
QStringLiteral(":") + QString::number(type);
|
|
|
|
QmlRangeEventData *newEvent;
|
|
|
|
if (d->eventDescriptions.contains(eventHashStr)) {
|
|
|
|
newEvent = d->eventDescriptions[eventHashStr];
|
|
|
|
} else {
|
|
|
|
newEvent = new QmlRangeEventData(eventHashStr, type, eventHashStr, location, QString(),
|
|
|
|
QQmlProfilerService::PixmapCacheEvent,
|
|
|
|
QQmlProfilerService::MaximumRangeType);
|
|
|
|
d->eventDescriptions.insert(eventHashStr, newEvent);
|
|
|
|
}
|
|
|
|
|
|
|
|
QmlRangeEventStartInstance rangeEventStartInstance(time, width, height, refcount, 0, 0,
|
|
|
|
newEvent);
|
|
|
|
d->startInstanceList.append(rangeEventStartInstance);
|
|
|
|
}
|
|
|
|
|
2014-06-16 11:33:33 +00:00
|
|
|
void QmlProfilerData::addMemoryEvent(QQmlProfilerService::MemoryType type, qint64 time,
|
|
|
|
qint64 size)
|
|
|
|
{
|
|
|
|
setState(AcquiringData);
|
|
|
|
QString eventHashStr = QString::fromLatin1("MemoryAllocation:%1").arg(type);
|
|
|
|
QmlRangeEventData *newEvent;
|
|
|
|
if (d->eventDescriptions.contains(eventHashStr)) {
|
|
|
|
newEvent = d->eventDescriptions[eventHashStr];
|
|
|
|
} else {
|
|
|
|
newEvent = new QmlRangeEventData(eventHashStr, type, eventHashStr, QmlEventLocation(),
|
|
|
|
QString(), QQmlProfilerService::MemoryAllocation,
|
|
|
|
QQmlProfilerService::MaximumRangeType);
|
|
|
|
d->eventDescriptions.insert(eventHashStr, newEvent);
|
|
|
|
}
|
|
|
|
QmlRangeEventStartInstance rangeEventStartInstance(time, size, 0, 0, 0, 0, newEvent);
|
|
|
|
d->startInstanceList.append(rangeEventStartInstance);
|
|
|
|
}
|
|
|
|
|
2015-05-21 15:48:58 +00:00
|
|
|
void QmlProfilerData::addInputEvent(QQmlProfilerDefinitions::EventType type, qint64 time)
|
|
|
|
{
|
|
|
|
setState(AcquiringData);
|
|
|
|
|
|
|
|
QString eventHashStr = QString::fromLatin1("Input:%1").arg(type);
|
|
|
|
|
|
|
|
QmlRangeEventData *newEvent;
|
|
|
|
if (d->eventDescriptions.contains(eventHashStr)) {
|
|
|
|
newEvent = d->eventDescriptions[eventHashStr];
|
|
|
|
} else {
|
|
|
|
newEvent = new QmlRangeEventData(QString(), type, eventHashStr, QmlEventLocation(),
|
|
|
|
QString(), QQmlProfilerService::Event,
|
|
|
|
QQmlProfilerService::MaximumRangeType);
|
|
|
|
d->eventDescriptions.insert(eventHashStr, newEvent);
|
|
|
|
}
|
|
|
|
|
|
|
|
d->startInstanceList.append(QmlRangeEventStartInstance(time, -1, 0, 0, 0, newEvent));
|
|
|
|
}
|
|
|
|
|
2012-03-08 15:50:14 +00:00
|
|
|
QString QmlProfilerData::rootEventName()
|
|
|
|
{
|
|
|
|
return tr("<program>");
|
|
|
|
}
|
|
|
|
|
|
|
|
QString QmlProfilerData::rootEventDescription()
|
|
|
|
{
|
|
|
|
return tr("Main Program");
|
|
|
|
}
|
|
|
|
|
|
|
|
void QmlProfilerDataPrivate::clearV8RootEvent()
|
|
|
|
{
|
|
|
|
v8RootEvent.displayName = QmlProfilerData::rootEventName();
|
|
|
|
v8RootEvent.eventHashStr = QmlProfilerData::rootEventName();
|
|
|
|
v8RootEvent.functionName = QmlProfilerData::rootEventDescription();
|
|
|
|
v8RootEvent.line = -1;
|
|
|
|
v8RootEvent.totalTime = 0;
|
|
|
|
v8RootEvent.selfTime = 0;
|
|
|
|
v8RootEvent.v8children.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QmlProfilerData::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);
|
|
|
|
QString hashStr = getHashStringForV8Event(displayName, function);
|
|
|
|
|
|
|
|
setState(AcquiringData);
|
|
|
|
|
|
|
|
// time is given in milliseconds, but internally we store it in microseconds
|
|
|
|
totalTime *= 1e6;
|
|
|
|
selfTime *= 1e6;
|
|
|
|
|
|
|
|
// accumulate information
|
|
|
|
QV8EventInfo *eventData = d->v8EventHash[hashStr];
|
|
|
|
if (!eventData) {
|
|
|
|
eventData = new QV8EventInfo;
|
|
|
|
eventData->displayName = displayName;
|
|
|
|
eventData->eventHashStr = hashStr;
|
|
|
|
eventData->fileName = filename;
|
|
|
|
eventData->functionName = function;
|
|
|
|
eventData->line = lineNumber;
|
|
|
|
eventData->totalTime = totalTime;
|
|
|
|
eventData->selfTime = selfTime;
|
|
|
|
d->v8EventHash[hashStr] = eventData;
|
|
|
|
} else {
|
|
|
|
eventData->totalTime += totalTime;
|
|
|
|
eventData->selfTime += selfTime;
|
|
|
|
}
|
|
|
|
d->v8parents[depth] = eventData;
|
|
|
|
|
|
|
|
QV8EventInfo *parentEvent = 0;
|
|
|
|
if (depth == 0) {
|
|
|
|
parentEvent = &d->v8RootEvent;
|
|
|
|
d->v8MeasuredTime += totalTime;
|
|
|
|
}
|
|
|
|
if (depth > 0 && d->v8parents.contains(depth-1)) {
|
|
|
|
parentEvent = d->v8parents.value(depth-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (parentEvent != 0) {
|
|
|
|
if (!parentEvent->v8children.contains(eventData->eventHashStr)) {
|
|
|
|
parentEvent->v8children[eventData->eventHashStr] = totalTime;
|
|
|
|
} else {
|
|
|
|
parentEvent->v8children[eventData->eventHashStr] += totalTime;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void QmlProfilerData::computeQmlTime()
|
|
|
|
{
|
|
|
|
// compute levels
|
|
|
|
QHash<int, qint64> endtimesPerLevel;
|
|
|
|
int minimumLevel = 1;
|
|
|
|
int level = minimumLevel;
|
|
|
|
|
|
|
|
for (int i = 0; i < d->startInstanceList.count(); i++) {
|
|
|
|
qint64 st = d->startInstanceList[i].startTime;
|
|
|
|
|
2014-02-19 17:11:41 +00:00
|
|
|
if (d->startInstanceList[i].data->rangeType == QQmlProfilerService::Painting) {
|
2012-03-08 15:50:14 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// general level
|
|
|
|
if (endtimesPerLevel[level] > st) {
|
|
|
|
level++;
|
|
|
|
} else {
|
|
|
|
while (level > minimumLevel && endtimesPerLevel[level-1] <= st)
|
|
|
|
level--;
|
|
|
|
}
|
|
|
|
endtimesPerLevel[level] = st + d->startInstanceList[i].duration;
|
|
|
|
|
|
|
|
if (level == minimumLevel) {
|
|
|
|
d->qmlMeasuredTime += d->startInstanceList[i].duration;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool compareStartTimes(const QmlRangeEventStartInstance &t1, const QmlRangeEventStartInstance &t2)
|
|
|
|
{
|
|
|
|
return t1.startTime < t2.startTime;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QmlProfilerData::sortStartTimes()
|
|
|
|
{
|
|
|
|
if (d->startInstanceList.count() < 2)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// assuming startTimes is partially sorted
|
|
|
|
// identify blocks of events and sort them with quicksort
|
|
|
|
QVector<QmlRangeEventStartInstance>::iterator itFrom = d->startInstanceList.end() - 2;
|
|
|
|
QVector<QmlRangeEventStartInstance>::iterator itTo = d->startInstanceList.end() - 1;
|
|
|
|
|
|
|
|
while (itFrom != d->startInstanceList.begin() && itTo != d->startInstanceList.begin()) {
|
|
|
|
// find block to sort
|
|
|
|
while ( itFrom != d->startInstanceList.begin()
|
|
|
|
&& itTo->startTime > itFrom->startTime ) {
|
|
|
|
itTo--;
|
|
|
|
itFrom = itTo - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if we're at the end of the list
|
|
|
|
if (itFrom == d->startInstanceList.begin())
|
|
|
|
break;
|
|
|
|
|
|
|
|
// find block length
|
|
|
|
while ( itFrom != d->startInstanceList.begin()
|
|
|
|
&& itTo->startTime <= itFrom->startTime )
|
|
|
|
itFrom--;
|
|
|
|
|
|
|
|
if (itTo->startTime <= itFrom->startTime)
|
2013-09-12 09:06:59 +00:00
|
|
|
std::sort(itFrom, itTo + 1, compareStartTimes);
|
2012-03-08 15:50:14 +00:00
|
|
|
else
|
2013-09-12 09:06:59 +00:00
|
|
|
std::sort(itFrom + 1, itTo + 1, compareStartTimes);
|
2012-03-08 15:50:14 +00:00
|
|
|
|
|
|
|
// move to next block
|
|
|
|
itTo = itFrom;
|
|
|
|
itFrom = itTo - 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void QmlProfilerData::complete()
|
|
|
|
{
|
|
|
|
setState(ProcessingData);
|
|
|
|
sortStartTimes();
|
|
|
|
computeQmlTime();
|
|
|
|
setState(Done);
|
|
|
|
emit dataReady();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QmlProfilerData::isEmpty() const
|
|
|
|
{
|
|
|
|
return d->startInstanceList.isEmpty() && d->v8EventHash.isEmpty();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QmlProfilerData::save(const QString &filename)
|
|
|
|
{
|
|
|
|
if (isEmpty()) {
|
|
|
|
emit error(tr("No data to save"));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-05-21 15:13:03 +00:00
|
|
|
QFile file;
|
|
|
|
if (!filename.isEmpty()) {
|
|
|
|
file.setFileName(filename);
|
|
|
|
if (!file.open(QIODevice::WriteOnly)) {
|
|
|
|
emit error(tr("Could not open %1 for writing").arg(filename));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!file.open(stdout, QIODevice::WriteOnly)) {
|
|
|
|
emit error(tr("Could not open stdout for writing"));
|
|
|
|
return false;
|
|
|
|
}
|
2012-03-08 15:50:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QXmlStreamWriter stream(&file);
|
|
|
|
stream.setAutoFormatting(true);
|
|
|
|
stream.writeStartDocument();
|
|
|
|
|
|
|
|
stream.writeStartElement(QStringLiteral("trace"));
|
2014-02-19 17:11:41 +00:00
|
|
|
stream.writeAttribute(QStringLiteral("version"), PROFILER_FILE_VERSION);
|
2012-03-08 15:50:14 +00:00
|
|
|
|
|
|
|
stream.writeAttribute(QStringLiteral("traceStart"), QString::number(traceStartTime()));
|
|
|
|
stream.writeAttribute(QStringLiteral("traceEnd"), QString::number(traceEndTime()));
|
|
|
|
|
|
|
|
stream.writeStartElement(QStringLiteral("eventData"));
|
|
|
|
stream.writeAttribute(QStringLiteral("totalTime"), QString::number(d->qmlMeasuredTime));
|
|
|
|
|
|
|
|
foreach (const QmlRangeEventData *eventData, d->eventDescriptions.values()) {
|
|
|
|
stream.writeStartElement(QStringLiteral("event"));
|
|
|
|
stream.writeAttribute(QStringLiteral("index"), QString::number(d->eventDescriptions.keys().indexOf(eventData->eventHashStr)));
|
|
|
|
stream.writeTextElement(QStringLiteral("displayname"), eventData->displayName);
|
2014-02-19 17:11:41 +00:00
|
|
|
if (eventData->rangeType != QQmlProfilerService::MaximumRangeType)
|
|
|
|
stream.writeTextElement(QStringLiteral("type"),
|
|
|
|
qmlRangeTypeAsString(eventData->rangeType));
|
|
|
|
else
|
|
|
|
stream.writeTextElement(QStringLiteral("type"),
|
|
|
|
qmlMessageAsString(eventData->message));
|
2012-03-08 15:50:14 +00:00
|
|
|
if (!eventData->location.filename.isEmpty()) {
|
|
|
|
stream.writeTextElement(QStringLiteral("filename"), eventData->location.filename);
|
|
|
|
stream.writeTextElement(QStringLiteral("line"), QString::number(eventData->location.line));
|
|
|
|
stream.writeTextElement(QStringLiteral("column"), QString::number(eventData->location.column));
|
|
|
|
}
|
|
|
|
stream.writeTextElement(QStringLiteral("details"), eventData->details);
|
2014-02-19 17:11:41 +00:00
|
|
|
if (eventData->rangeType == QQmlProfilerService::Binding)
|
|
|
|
stream.writeTextElement(QStringLiteral("bindingType"),
|
|
|
|
QString::number((int)eventData->detailType));
|
2015-05-21 15:48:58 +00:00
|
|
|
else if (eventData->message == QQmlProfilerService::Event) {
|
|
|
|
switch (eventData->detailType) {
|
|
|
|
case QQmlProfilerService::AnimationFrame:
|
|
|
|
stream.writeTextElement(QStringLiteral("animationFrame"),
|
|
|
|
QString::number((int)eventData->detailType));
|
|
|
|
break;
|
|
|
|
case QQmlProfilerService::Key:
|
|
|
|
stream.writeTextElement(QStringLiteral("keyEvent"),
|
|
|
|
QString::number((int)eventData->detailType));
|
|
|
|
break;
|
|
|
|
case QQmlProfilerService::Mouse:
|
|
|
|
stream.writeTextElement(QStringLiteral("mouseEvent"),
|
|
|
|
QString::number((int)eventData->detailType));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else if (eventData->message == QQmlProfilerService::PixmapCacheEvent)
|
2014-02-19 17:11:41 +00:00
|
|
|
stream.writeTextElement(QStringLiteral("cacheEventType"),
|
|
|
|
QString::number((int)eventData->detailType));
|
|
|
|
else if (eventData->message == QQmlProfilerService::SceneGraphFrame)
|
|
|
|
stream.writeTextElement(QStringLiteral("sgEventType"),
|
|
|
|
QString::number((int)eventData->detailType));
|
2014-06-16 11:33:33 +00:00
|
|
|
else if (eventData->message == QQmlProfilerService::MemoryAllocation)
|
|
|
|
stream.writeTextElement(QStringLiteral("memoryEventType"),
|
|
|
|
QString::number((int)eventData->detailType));
|
2012-03-08 15:50:14 +00:00
|
|
|
stream.writeEndElement();
|
|
|
|
}
|
|
|
|
stream.writeEndElement(); // eventData
|
|
|
|
|
|
|
|
stream.writeStartElement(QStringLiteral("profilerDataModel"));
|
2014-02-19 17:11:41 +00:00
|
|
|
foreach (const QmlRangeEventStartInstance &event, d->startInstanceList) {
|
2012-03-08 15:50:14 +00:00
|
|
|
stream.writeStartElement(QStringLiteral("range"));
|
2014-02-19 17:11:41 +00:00
|
|
|
stream.writeAttribute(QStringLiteral("startTime"), QString::number(event.startTime));
|
|
|
|
if (event.duration >= 0)
|
2014-03-17 13:52:01 +00:00
|
|
|
stream.writeAttribute(QStringLiteral("duration"),
|
2014-02-19 17:11:41 +00:00
|
|
|
QString::number(event.duration));
|
|
|
|
stream.writeAttribute(QStringLiteral("eventIndex"), QString::number(d->eventDescriptions.keys().indexOf(event.data->eventHashStr)));
|
|
|
|
if (event.data->message == QQmlProfilerService::Event &&
|
|
|
|
event.data->detailType == QQmlProfilerService::AnimationFrame) {
|
|
|
|
// special: animation frame
|
|
|
|
stream.writeAttribute(QStringLiteral("framerate"), QString::number(event.frameRate));
|
|
|
|
stream.writeAttribute(QStringLiteral("animationcount"), QString::number(event.animationCount));
|
|
|
|
stream.writeAttribute(QStringLiteral("thread"), QString::number(event.threadId));
|
|
|
|
} else if (event.data->message == QQmlProfilerService::PixmapCacheEvent) {
|
|
|
|
// special: pixmap cache event
|
|
|
|
if (event.data->detailType == QQmlProfilerService::PixmapSizeKnown) {
|
|
|
|
stream.writeAttribute(QStringLiteral("width"),
|
|
|
|
QString::number(event.numericData1));
|
|
|
|
stream.writeAttribute(QStringLiteral("height"),
|
|
|
|
QString::number(event.numericData2));
|
|
|
|
} else if (event.data->detailType == QQmlProfilerService::PixmapReferenceCountChanged ||
|
|
|
|
event.data->detailType == QQmlProfilerService::PixmapCacheCountChanged) {
|
|
|
|
stream.writeAttribute(QStringLiteral("refCount"),
|
|
|
|
QString::number(event.numericData3));
|
|
|
|
}
|
|
|
|
} else if (event.data->message == QQmlProfilerService::SceneGraphFrame) {
|
|
|
|
// special: scenegraph frame events
|
|
|
|
if (event.numericData1 > 0)
|
|
|
|
stream.writeAttribute(QStringLiteral("timing1"),
|
|
|
|
QString::number(event.numericData1));
|
|
|
|
if (event.numericData2 > 0)
|
|
|
|
stream.writeAttribute(QStringLiteral("timing2"),
|
|
|
|
QString::number(event.numericData2));
|
|
|
|
if (event.numericData3 > 0)
|
|
|
|
stream.writeAttribute(QStringLiteral("timing3"),
|
|
|
|
QString::number(event.numericData3));
|
|
|
|
if (event.numericData4 > 0)
|
|
|
|
stream.writeAttribute(QStringLiteral("timing4"),
|
|
|
|
QString::number(event.numericData4));
|
|
|
|
if (event.numericData5 > 0)
|
|
|
|
stream.writeAttribute(QStringLiteral("timing5"),
|
|
|
|
QString::number(event.numericData5));
|
2014-06-16 11:33:33 +00:00
|
|
|
} else if (event.data->message == QQmlProfilerService::MemoryAllocation) {
|
|
|
|
stream.writeAttribute(QStringLiteral("amount"), QString::number(event.numericData1));
|
2012-03-08 15:50:14 +00:00
|
|
|
}
|
|
|
|
stream.writeEndElement();
|
|
|
|
}
|
|
|
|
stream.writeEndElement(); // profilerDataModel
|
|
|
|
|
|
|
|
stream.writeStartElement(QStringLiteral("v8profile")); // v8 profiler output
|
|
|
|
stream.writeAttribute(QStringLiteral("totalTime"), QString::number(d->v8MeasuredTime));
|
|
|
|
foreach (QV8EventInfo *v8event, d->v8EventHash.values()) {
|
|
|
|
stream.writeStartElement(QStringLiteral("event"));
|
|
|
|
stream.writeAttribute(QStringLiteral("index"), QString::number(d->v8EventHash.keys().indexOf(v8event->eventHashStr)));
|
|
|
|
stream.writeTextElement(QStringLiteral("displayname"), v8event->displayName);
|
|
|
|
stream.writeTextElement(QStringLiteral("functionname"), v8event->functionName);
|
|
|
|
if (!v8event->fileName.isEmpty()) {
|
|
|
|
stream.writeTextElement(QStringLiteral("filename"), v8event->fileName);
|
|
|
|
stream.writeTextElement(QStringLiteral("line"), QString::number(v8event->line));
|
|
|
|
}
|
|
|
|
stream.writeTextElement(QStringLiteral("totalTime"), QString::number(v8event->totalTime));
|
|
|
|
stream.writeTextElement(QStringLiteral("selfTime"), QString::number(v8event->selfTime));
|
|
|
|
if (!v8event->v8children.isEmpty()) {
|
|
|
|
stream.writeStartElement(QStringLiteral("childrenEvents"));
|
|
|
|
QStringList childrenIndexes;
|
|
|
|
QStringList childrenTimes;
|
|
|
|
foreach (const QString &childHash, v8event->v8children.keys()) {
|
|
|
|
childrenIndexes << QString::number(v8EventIndex(childHash));
|
|
|
|
childrenTimes << QString::number(v8event->v8children[childHash]);
|
|
|
|
}
|
|
|
|
|
|
|
|
stream.writeAttribute(QStringLiteral("list"), childrenIndexes.join(QString(", ")));
|
|
|
|
stream.writeAttribute(QStringLiteral("childrenTimes"), childrenTimes.join(QString(", ")));
|
|
|
|
stream.writeEndElement();
|
|
|
|
}
|
|
|
|
stream.writeEndElement();
|
|
|
|
}
|
|
|
|
stream.writeEndElement(); // v8 profiler output
|
|
|
|
|
|
|
|
stream.writeEndElement(); // trace
|
|
|
|
stream.writeEndDocument();
|
|
|
|
|
|
|
|
file.close();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
int QmlProfilerData::v8EventIndex(const QString &hashStr)
|
|
|
|
{
|
|
|
|
if (!d->v8EventHash.contains(hashStr)) {
|
|
|
|
emit error("Trying to index nonexisting v8 event");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return d->v8EventHash.keys().indexOf( hashStr );
|
|
|
|
}
|
|
|
|
|
|
|
|
void QmlProfilerData::setState(QmlProfilerData::State state)
|
|
|
|
{
|
|
|
|
// It's not an error, we are continuously calling "AcquiringData" for example
|
|
|
|
if (d->state == state)
|
|
|
|
return;
|
|
|
|
|
|
|
|
switch (state) {
|
|
|
|
case Empty:
|
|
|
|
// if it's not empty, complain but go on
|
|
|
|
if (!isEmpty())
|
|
|
|
emit error("Invalid qmlprofiler state change (Empty)");
|
|
|
|
break;
|
|
|
|
case AcquiringData:
|
|
|
|
// we're not supposed to receive new data while processing older data
|
|
|
|
if (d->state == ProcessingData)
|
|
|
|
emit error("Invalid qmlprofiler state change (AcquiringData)");
|
|
|
|
break;
|
|
|
|
case ProcessingData:
|
|
|
|
if (d->state != AcquiringData)
|
|
|
|
emit error("Invalid qmlprofiler state change (ProcessingData)");
|
|
|
|
break;
|
|
|
|
case Done:
|
|
|
|
if (d->state != ProcessingData && d->state != Empty)
|
|
|
|
emit error("Invalid qmlprofiler state change (Done)");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
emit error("Trying to set unknown state in events list");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
d->state = state;
|
|
|
|
emit stateChanged();
|
|
|
|
|
|
|
|
// special: if we were done with an empty list, clean internal data and go back to empty
|
|
|
|
if (d->state == Done && isEmpty()) {
|
|
|
|
clear();
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|