QmlProfilerTool: add tool to QDeclarative

qmlprofiler is a standalone tool used to record
profiling data of QML apps. The data is stored
in a file which can be loaded in QtCreator for
investigation.

Change-Id: I308f4c40bc3876933bd0d32c336cef6cd6f5fb4a
Reviewed-by: Kai Koehne <kai.koehne@nokia.com>
This commit is contained in:
Aurindam Jana 2012-02-09 17:04:43 +01:00 committed by Qt by Nokia
parent 045e593291
commit 6c24d2feb4
12 changed files with 3426 additions and 1 deletions

View File

@ -0,0 +1,66 @@
/****************************************************************************
**
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/
**
** This file is part of the QtDeclarative module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** 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, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "commandlistener.h"
#include "constants.h"
#include <QtCore/QTextStream>
CommandListener::CommandListener(QObject *parent)
: QThread(parent)
, m_stopRequested(false)
{
}
void CommandListener::run()
{
QString line;
QTextStream in(stdin, QIODevice::ReadOnly);
do {
line = in.readLine();
line = line.trimmed();
if (!line.isEmpty()) {
emit command(line);
if (line == QLatin1String(Constants::CMD_QUIT)
|| line == QLatin1String(Constants::CMD_QUIT2))
return;
}
} while (!m_stopRequested && !line.isNull());
}

View File

@ -0,0 +1,63 @@
/****************************************************************************
**
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/
**
** This file is part of the QtDeclarative module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** 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, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef COMMANDLISTENER_H
#define COMMANDLISTENER_H
#include <QtCore/QThread>
class CommandListener : public QThread
{
Q_OBJECT
public:
CommandListener(QObject *parent = 0);
void run();
void requestStop() { m_stopRequested = true; }
signals:
void command(const QString &command);
private:
bool m_stopRequested;
};
#endif // COMMANDLISTENER_H

View File

@ -0,0 +1,68 @@
/****************************************************************************
**
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/
**
** This file is part of the QtDeclarative module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** 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, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef CONSTANTS_H
#define CONSTANTS_H
namespace Constants {
const char CMD_HELP[] ="help";
const char CMD_HELP2[] = "h";
const char CMD_HELP3[] = "?";
const char CMD_RECORD[] ="record";
const char CMD_RECORD2[] ="r";
const char CMD_QUIT[] ="quit";
const char CMD_QUIT2[] = "q";
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 PROFILER_FILE_VERSION[] = "1.02";
const int MIN_LEVEL = 1;
} // Contants
#endif // CONSTANTS_H

View File

@ -0,0 +1,64 @@
/****************************************************************************
**
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/
**
** This file is part of the tools applications of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** 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, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "commandlistener.h"
#include "qmlprofilerapplication.h"
int main(int argc, char *argv[])
{
QmlProfilerApplication app(argc, argv);
if (!app.parseArguments()) {
app.printUsage();
return 1;
}
CommandListener listener;
QObject::connect(&listener, SIGNAL(command(QString)), &app, SLOT(userCommand(QString)));
listener.start();
int exitValue = app.exec();
// wait for listener to exit
listener.wait();
return exitValue;
}

View File

@ -0,0 +1,300 @@
/****************************************************************************
**
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/
**
** This file is part of the QtDeclarative module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** 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, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "profileclient.h"
#include <QtCore/QStack>
#include <QtCore/QStringList>
ProfileClient::ProfileClient(const QString &clientName,
QDeclarativeDebugConnection *client)
: QDeclarativeDebugClient(clientName, client),
m_recording(false),
m_enabled(false)
{
}
ProfileClient::~ProfileClient()
{
//Disable profiling if started by client
//Profiling data will be lost!!
if (isRecording())
setRecording(false);
}
void ProfileClient::clearData()
{
emit cleared();
}
bool ProfileClient::isEnabled() const
{
return m_enabled;
}
void ProfileClient::sendRecordingStatus()
{
}
bool ProfileClient::isRecording() const
{
return m_recording;
}
void ProfileClient::setRecording(bool v)
{
if (v == m_recording)
return;
m_recording = v;
if (state() == Enabled) {
sendRecordingStatus();
}
emit recordingChanged(v);
}
void ProfileClient::stateChanged(State status)
{
if ((m_enabled && status != Enabled) ||
(!m_enabled && status == Enabled))
emit enabledChanged();
m_enabled = status == Enabled;
}
class DeclarativeProfileClientPrivate
{
public:
DeclarativeProfileClientPrivate()
: inProgressRanges(0)
, maximumTime(0)
{
::memset(rangeCount, 0,
QDeclarativeProfilerService::MaximumRangeType * sizeof(int));
}
qint64 inProgressRanges;
QStack<qint64> rangeStartTimes[QDeclarativeProfilerService::MaximumRangeType];
QStack<QStringList> rangeDatas[QDeclarativeProfilerService::MaximumRangeType];
QStack<EventLocation> rangeLocations[QDeclarativeProfilerService::MaximumRangeType];
int rangeCount[QDeclarativeProfilerService::MaximumRangeType];
qint64 maximumTime;
};
DeclarativeProfileClient::DeclarativeProfileClient(
QDeclarativeDebugConnection *client)
: ProfileClient(QLatin1String("CanvasFrameRate"), client),
d(new DeclarativeProfileClientPrivate)
{
}
DeclarativeProfileClient::~DeclarativeProfileClient()
{
delete d;
}
void DeclarativeProfileClient::clearData()
{
::memset(d->rangeCount, 0,
QDeclarativeProfilerService::MaximumRangeType * sizeof(int));
ProfileClient::clearData();
}
void DeclarativeProfileClient::sendRecordingStatus()
{
QByteArray ba;
QDataStream stream(&ba, QIODevice::WriteOnly);
stream << isRecording();
sendMessage(ba);
}
void DeclarativeProfileClient::messageReceived(const QByteArray &data)
{
QByteArray rwData = data;
QDataStream stream(&rwData, QIODevice::ReadOnly);
qint64 time;
int messageType;
stream >> time >> messageType;
if (messageType >= QDeclarativeProfilerService::MaximumMessage)
return;
if (messageType == QDeclarativeProfilerService::Event) {
int event;
stream >> event;
if (event == QDeclarativeProfilerService::EndTrace) {
emit this->traceFinished(time);
d->maximumTime = time;
d->maximumTime = qMax(time, d->maximumTime);
} else if (event == QDeclarativeProfilerService::AnimationFrame) {
int frameRate, animationCount;
stream >> frameRate >> animationCount;
emit this->frame(time, frameRate, animationCount);
d->maximumTime = qMax(time, d->maximumTime);
} else if (event == QDeclarativeProfilerService::StartTrace) {
emit this->traceStarted(time);
d->maximumTime = time;
} else if (event < QDeclarativeProfilerService::MaximumEventType) {
d->maximumTime = qMax(time, d->maximumTime);
}
} else if (messageType == QDeclarativeProfilerService::Complete) {
emit complete();
} else {
int range;
stream >> range;
if (range >= QDeclarativeProfilerService::MaximumRangeType)
return;
if (messageType == QDeclarativeProfilerService::RangeStart) {
d->rangeStartTimes[range].push(time);
d->inProgressRanges |= (static_cast<qint64>(1) << range);
++d->rangeCount[range];
} else if (messageType == QDeclarativeProfilerService::RangeData) {
QString data;
stream >> data;
int count = d->rangeCount[range];
if (count > 0) {
while (d->rangeDatas[range].count() < count)
d->rangeDatas[range].push(QStringList());
d->rangeDatas[range][count-1] << data;
}
} else if (messageType == QDeclarativeProfilerService::RangeLocation) {
QString fileName;
int line;
int column = -1;
stream >> fileName >> line;
if (!stream.atEnd())
stream >> column;
if (d->rangeCount[range] > 0) {
d->rangeLocations[range].push(EventLocation(fileName, line,
column));
}
} else {
if (d->rangeCount[range] > 0) {
--d->rangeCount[range];
if (d->inProgressRanges & (static_cast<qint64>(1) << range))
d->inProgressRanges &= ~(static_cast<qint64>(1) << range);
d->maximumTime = qMax(time, d->maximumTime);
QStringList data = d->rangeDatas[range].count() ?
d->rangeDatas[range].pop() : QStringList();
EventLocation location = d->rangeLocations[range].count() ?
d->rangeLocations[range].pop() : EventLocation();
qint64 startTime = d->rangeStartTimes[range].pop();
emit this->range((QDeclarativeProfilerService::RangeType)range,
startTime, time - startTime, data, location);
if (d->rangeCount[range] == 0) {
int count = d->rangeDatas[range].count() +
d->rangeStartTimes[range].count() +
d->rangeLocations[range].count();
if (count != 0)
qWarning() << "incorrectly nested data";
}
}
}
}
}
V8ProfileClient::V8ProfileClient(QDeclarativeDebugConnection *client)
: ProfileClient(QLatin1String("V8Profiler"), client)
{
}
V8ProfileClient::~V8ProfileClient()
{
}
void V8ProfileClient::sendRecordingStatus()
{
QByteArray ba;
QDataStream stream(&ba, QIODevice::WriteOnly);
QByteArray cmd("V8PROFILER");
QByteArray option("");
QByteArray title("");
if (m_recording) {
option = "start";
} else {
option = "stop";
}
stream << cmd << option << title;
sendMessage(ba);
}
void V8ProfileClient::messageReceived(const QByteArray &data)
{
QByteArray rwData = data;
QDataStream stream(&rwData, QIODevice::ReadOnly);
int messageType;
stream >> messageType;
if (messageType == V8Complete) {
emit complete();
} else if (messageType == V8Entry) {
QString filename;
QString function;
int lineNumber;
double totalTime;
double selfTime;
int depth;
stream >> filename >> function >> lineNumber >> totalTime >>
selfTime >> depth;
emit this->range(depth, function, filename, lineNumber, totalTime,
selfTime);
}
}

View File

@ -0,0 +1,139 @@
/****************************************************************************
**
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/
**
** This file is part of the QtDeclarative module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** 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, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef PROFILECLIENT_H
#define PROFILECLIENT_H
#include "profiledata.h"
#include <QtDeclarative/private/qdeclarativedebugclient_p.h>
#include <QtDeclarative/private/qdeclarativeprofilerservice_p.h>
class ProfileClientPrivate;
class ProfileClient : public QDeclarativeDebugClient
{
Q_OBJECT
Q_PROPERTY(bool enabled READ isEnabled NOTIFY enabledChanged)
Q_PROPERTY(bool recording READ isRecording WRITE setRecording
NOTIFY recordingChanged)
public:
ProfileClient(const QString & clientName,
QDeclarativeDebugConnection *client);
~ProfileClient();
bool isEnabled() const;
bool isRecording() const;
public slots:
void setRecording(bool);
virtual void clearData();
virtual void sendRecordingStatus();
signals:
void complete();
void recordingChanged(bool arg);
void enabledChanged();
void cleared();
protected:
virtual void stateChanged(State);
protected:
bool m_recording;
bool m_enabled;
};
class DeclarativeProfileClient : public ProfileClient
{
Q_OBJECT
public:
DeclarativeProfileClient(QDeclarativeDebugConnection *client);
~DeclarativeProfileClient();
public slots:
void clearData();
void sendRecordingStatus();
signals:
void traceFinished( qint64 time );
void traceStarted( qint64 time );
void range(QDeclarativeProfilerService::RangeType type, qint64 startTime,
qint64 length, const QStringList &data,
const EventLocation &location);
void frame(qint64 time, int frameRate, int animationCount);
protected:
virtual void messageReceived(const QByteArray &);
private:
class DeclarativeProfileClientPrivate *d;
};
class V8ProfileClient : public ProfileClient
{
Q_OBJECT
public:
enum Message {
V8Entry,
V8Complete,
V8MaximumMessage
};
V8ProfileClient(QDeclarativeDebugConnection *client);
~V8ProfileClient();
public slots:
void sendRecordingStatus();
signals:
void range(int depth, const QString &function, const QString &filename,
int lineNumber, double totalTime, double selfTime);
protected:
virtual void messageReceived(const QByteArray &);
};
#endif // PROFILECLIENT_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,247 @@
/****************************************************************************
**
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/
**
** This file is part of the QtDeclarative module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** 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, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef PROFILEDATA_H
#define PROFILEDATA_H
#include <QtDeclarative/private/qdeclarativeprofilerservice_p.h>
#include <QtCore/QObject>
#include <QtCore/QHash>
struct DeclarativeEvent;
struct V8Event;
typedef QHash<QString, DeclarativeEvent *> DeclarativeEventHash;
typedef QList<DeclarativeEvent *> DeclarativeEvents;
typedef QList<V8Event *> V8Events;
struct EventLocation
{
EventLocation() : line(-1),column(-1) {}
EventLocation(const QString &file, int lineNumber, int columnNumber)
: filename(file), line(lineNumber), column(columnNumber) {}
QString filename;
int line;
int column;
};
struct DeclarativeEventSub {
DeclarativeEventSub(DeclarativeEvent *from)
: reference(from), duration(0), calls(0), inLoopPath(false)
{}
DeclarativeEventSub(DeclarativeEventSub *from)
: reference(from->reference), duration(from->duration),
calls(from->calls), inLoopPath(from->inLoopPath)
{}
DeclarativeEvent *reference;
qint64 duration;
qint64 calls;
bool inLoopPath;
};
struct DeclarativeEvent
{
DeclarativeEvent();
~DeclarativeEvent();
QString displayname;
QString eventHashStr;
QString details;
EventLocation location;
QDeclarativeProfilerService::RangeType eventType;
QHash <QString, DeclarativeEventSub *> parentHash;
QHash <QString, DeclarativeEventSub *> childrenHash;
qint64 duration;
qint64 calls;
qint64 minTime;
qint64 maxTime;
double timePerCall;
double percentOfTime;
qint64 medianTime;
int eventId;
bool isBindingLoop;
DeclarativeEvent &operator=(const DeclarativeEvent &ref);
};
struct V8EventSub {
V8EventSub(V8Event *from)
: reference(from), totalTime(0)
{}
V8EventSub(V8EventSub *from)
: reference(from->reference), totalTime(from->totalTime)
{}
V8Event *reference;
qint64 totalTime;
};
struct V8Event
{
V8Event();
~V8Event();
QString displayName;
QString filename;
QString functionName;
int line;
double totalTime; // given in milliseconds
double totalPercent;
double selfTime;
double selfPercent;
QHash <QString, V8EventSub *> parentHash;
QHash <QString, V8EventSub *> childrenHash;
int eventId;
V8Event &operator=(const V8Event &ref);
};
class ProfileData : public QObject
{
Q_OBJECT
public:
explicit ProfileData(QObject *parent = 0);
~ProfileData();
DeclarativeEvents getDeclarativeEvents() const;
DeclarativeEvent *declarativeEvent(int eventId) const;
const V8Events& getV8Events() const;
V8Event *v8Event(int eventId) const;
int findFirstIndex(qint64 startTime) const;
int findFirstIndexNoParents(qint64 startTime) const;
int findLastIndex(qint64 endTime) const;
Q_INVOKABLE qint64 firstTimeMark() const;
Q_INVOKABLE qint64 lastTimeMark() const;
Q_INVOKABLE int count() const;
// data access
Q_INVOKABLE qint64 getStartTime(int index) const;
Q_INVOKABLE qint64 getEndTime(int index) const;
Q_INVOKABLE qint64 getDuration(int index) const;
Q_INVOKABLE int getType(int index) const;
Q_INVOKABLE int getNestingLevel(int index) const;
Q_INVOKABLE int getNestingDepth(int index) const;
Q_INVOKABLE QString getFilename(int index) const;
Q_INVOKABLE int getLine(int index) const;
Q_INVOKABLE int getColumn(int index) const;
Q_INVOKABLE QString getDetails(int index) const;
Q_INVOKABLE int getEventId(int index) const;
Q_INVOKABLE int getFramerate(int index) const;
Q_INVOKABLE int getAnimationCount(int index) const;
Q_INVOKABLE int getMaximumAnimationCount() const;
Q_INVOKABLE int getMinimumAnimationCount() const;
// per-type data
Q_INVOKABLE int uniqueEventsOfType(int type) const;
Q_INVOKABLE int maxNestingForType(int type) const;
Q_INVOKABLE QString eventTextForType(int type, int index) const;
Q_INVOKABLE QString eventDisplayNameForType(int type, int index) const;
Q_INVOKABLE int eventIdForType(int type, int index) const;
Q_INVOKABLE int eventPosInType(int index) const;
Q_INVOKABLE qint64 traceStartTime() const;
Q_INVOKABLE qint64 traceEndTime() const;
Q_INVOKABLE qint64 traceDuration() const;
Q_INVOKABLE qint64 declarativeMeasuredTime() const;
Q_INVOKABLE qint64 v8MeasuredTime() const;
void showErrorDialog(const QString &st ) const;
void compileStatistics(qint64 startTime, qint64 endTime);
signals:
void dataReady();
void countChanged();
void error(const QString &error);
void dataClear();
void processingData();
void postProcessing();
void requestDetailsForLocation(int eventType, const EventLocation &location);
void detailsChanged(int eventId, const QString &newString);
void reloadDetailLabels();
void reloadDocumentsForDetails();
public slots:
void clear();
void addDeclarativeEvent(QDeclarativeProfilerService::RangeType type,
qint64 startTime, qint64 length,
const QStringList &data,
const EventLocation &location);
void complete();
void addV8Event(int depth,const QString &function,const QString &filename,
int lineNumber, double totalTime, double selfTime);
void addFrameEvent(qint64 time, int framerate, int animationcount);
bool save(const QString &filename);
void load(const QString &filename);
void setFilename(const QString &filename);
void load();
void setTraceEndTime( qint64 time );
void setTraceStartTime( qint64 time );
void rewriteDetailsString(QDeclarativeProfilerService::RangeType eventType,
const EventLocation &location,
const QString &newString);
void finishedRewritingDetails();
private:
void postProcess();
void sortEndTimes();
void findAnimationLimits();
void sortStartTimes();
void computeLevels();
void computeNestingLevels();
void computeNestingDepth();
void prepareForDisplay();
void linkEndsToStarts();
void reloadDetails();
void findBindingLoops(qint64 startTime, qint64 endTime);
private:
class ProfileDataPrivate *d;
};
#endif // PROFILEDATA_H

View File

@ -0,0 +1,25 @@
TEMPLATE = app
TARGET = qmlprofiler
DESTDIR = $$QT.declarative.bins
QT += declarative declarative-private network
target.path = $$[QT_INSTALL_BINS]
INSTALLS += target
macx: CONFIG -= app_bundle
CONFIG += console declarative_debug
SOURCES += main.cpp \
qmlprofilerapplication.cpp \
commandlistener.cpp \
profileclient.cpp \
profiledata.cpp
HEADERS += \
qmlprofilerapplication.h \
commandlistener.h \
constants.h \
profileclient.h \
profiledata.h

View File

@ -0,0 +1,434 @@
/****************************************************************************
**
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/
**
** This file is part of the QtDeclarative module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** 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, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qmlprofilerapplication.h"
#include "constants.h"
#include <QtCore/QStringList>
#include <QtCore/QTextStream>
#include <QtCore/QProcess>
#include <QtCore/QTimer>
#include <QtCore/QDateTime>
#include <QtCore/QFileInfo>
#include <QtCore/QDebug>
static const char usageTextC[] =
"Usage:\n"
" qmlprofiler [options] [program] [program-options]\n"
" qmlprofiler [options] -attach [hostname]\n"
"\n"
"QML Profiler retrieves QML tracing data from a running application.\n"
"The data collected can then be visualized in Qt Creator.\n"
"\n"
"The application to be profiled has to enable QML debugging. See the Qt Creator\n"
"documentation on how to do this for different Qt versions.\n"
"\n"
"Options:\n"
" -help Show this information and exit.\n"
" -fromStart\n"
" Record as soon as the engine is started, default is false.\n"
" -p <number>, -port <number>\n"
" TCP/IP port to use, default is 3768.\n"
" -v, -verbose\n"
" Print debugging output.\n"
" -version\n"
" Show the version of qmlprofiler and exit.\n";
static const char commandTextC[] =
"Commands:\n"
" r, record\n"
" Switch recording on or off.\n"
" q, quit\n"
" Terminate program.";
static const char TraceFileExtension[] = ".qtd";
QmlProfilerApplication::QmlProfilerApplication(int &argc, char **argv) :
QCoreApplication(argc, argv),
m_runMode(LaunchMode),
m_process(0),
m_tracePrefix(QLatin1String("trace")),
m_hostName(QLatin1String("127.0.0.1")),
m_port(3768),
m_verbose(false),
m_quitAfterSave(false),
m_declarativeProfilerClient(&m_connection),
m_v8profilerClient(&m_connection),
m_connectionAttempts(0),
m_declarativeDataReady(false),
m_v8DataReady(false)
{
m_connectTimer.setInterval(1000);
connect(&m_connectTimer, SIGNAL(timeout()), this, SLOT(tryToConnect()));
connect(&m_connection, SIGNAL(connected()), this, SLOT(connected()));
connect(&m_connection, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(connectionStateChanged(QAbstractSocket::SocketState)));
connect(&m_connection, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(connectionError(QAbstractSocket::SocketError)));
connect(&m_declarativeProfilerClient, SIGNAL(enabledChanged()), this, SLOT(traceClientEnabled()));
connect(&m_declarativeProfilerClient, SIGNAL(recordingChanged(bool)), this, SLOT(recordingChanged()));
connect(&m_declarativeProfilerClient, SIGNAL(range(QDeclarativeProfilerService::RangeType,qint64,qint64,QStringList,EventLocation)),
&m_profileData, SLOT(addDeclarativeEvent(QDeclarativeProfilerService::RangeType,qint64,qint64,QStringList,EventLocation)));
connect(&m_declarativeProfilerClient, SIGNAL(traceFinished(qint64)), &m_profileData, SLOT(setTraceEndTime(qint64)));
connect(&m_declarativeProfilerClient, SIGNAL(traceStarted(qint64)), &m_profileData, SLOT(setTraceStartTime(qint64)));
connect(&m_declarativeProfilerClient, SIGNAL(frame(qint64,int,int)), &m_profileData, SLOT(addFrameEvent(qint64,int,int)));
connect(&m_declarativeProfilerClient, SIGNAL(complete()), this, SLOT(declarativeComplete()));
connect(&m_v8profilerClient, SIGNAL(enabledChanged()), this, SLOT(profilerClientEnabled()));
connect(&m_v8profilerClient, SIGNAL(range(int,QString,QString,int,double,double)),
&m_profileData, SLOT(addV8Event(int,QString,QString,int,double,double)));
connect(&m_v8profilerClient, SIGNAL(complete()), this, SLOT(v8Complete()));
connect(&m_profileData, SIGNAL(error(QString)), this, SLOT(logError(QString)));
connect(&m_profileData, SIGNAL(dataReady()), this, SLOT(traceFinished()));
}
QmlProfilerApplication::~QmlProfilerApplication()
{
if (!m_process)
return;
logStatus("Terminating process ...");
m_process->disconnect();
m_process->terminate();
if (!m_process->waitForFinished(1000)) {
logStatus("Killing process ...");
m_process->kill();
}
delete m_process;
}
bool QmlProfilerApplication::parseArguments()
{
for (int argPos = 1; argPos < arguments().size(); ++argPos) {
const QString arg = arguments().at(argPos);
if (arg == QLatin1String("-attach") || arg == QLatin1String("-a")) {
if (argPos + 1 == arguments().size()) {
return false;
}
m_hostName = arguments().at(++argPos);
m_runMode = AttachMode;
} else if (arg == QLatin1String("-port") || arg == QLatin1String("-p")) {
if (argPos + 1 == arguments().size()) {
return false;
}
const QString portStr = arguments().at(++argPos);
bool isNumber;
m_port = portStr.toUShort(&isNumber);
if (!isNumber) {
logError(QString("'%1' is not a valid port").arg(portStr));
return false;
}
} else if (arg == QLatin1String("-fromStart")) {
m_declarativeProfilerClient.setRecording(true);
m_v8profilerClient.setRecording(true);
} else if (arg == QLatin1String("-help") || arg == QLatin1String("-h") || arg == QLatin1String("/h") || arg == QLatin1String("/?")) {
return false;
} else if (arg == QLatin1String("-verbose") || arg == QLatin1String("-v")) {
m_verbose = true;
} else if (arg == QLatin1String("-version")) {
print(QString("QML Profiler based on Qt %1.").arg(qVersion()));
::exit(1);
return false;
} else {
if (m_programPath.isEmpty()) {
m_programPath = arg;
m_tracePrefix = QFileInfo(m_programPath).fileName();
} else {
m_programArguments << arg;
}
}
}
if (m_runMode == LaunchMode
&& m_programPath.isEmpty())
return false;
if (m_runMode == AttachMode
&& !m_programPath.isEmpty())
return false;
return true;
}
void QmlProfilerApplication::printUsage()
{
print(QLatin1String(usageTextC));
print(QLatin1String(commandTextC));
}
int QmlProfilerApplication::exec()
{
QTimer::singleShot(0, this, SLOT(run()));
return QCoreApplication::exec();
}
void QmlProfilerApplication::printCommands()
{
print(QLatin1String(commandTextC));
}
QString QmlProfilerApplication::traceFileName() const
{
QString fileName = m_tracePrefix + "_" +
QDateTime::currentDateTime().toString(QLatin1String("yyMMdd_hhmmss")) +
TraceFileExtension;
if (QFileInfo(fileName).exists()) {
QString baseName;
int suffixIndex = 0;
do {
baseName = QFileInfo(fileName).baseName()
+ QString::number(suffixIndex++);
} while (QFileInfo(baseName + TraceFileExtension).exists());
fileName = baseName + TraceFileExtension;
}
return QFileInfo(fileName).absoluteFilePath();
}
void QmlProfilerApplication::userCommand(const QString &command)
{
QString cmd = command.trimmed();
if (cmd == Constants::CMD_HELP
|| cmd == Constants::CMD_HELP2
|| cmd == Constants::CMD_HELP3) {
printCommands();
} else if (cmd == Constants::CMD_RECORD
|| cmd == Constants::CMD_RECORD2) {
m_declarativeProfilerClient.setRecording(
!m_declarativeProfilerClient.isRecording());
m_v8profilerClient.setRecording(!m_v8profilerClient.isRecording());
m_declarativeDataReady = false;
m_v8DataReady = false;
} else if (cmd == Constants::CMD_QUIT
|| cmd == Constants::CMD_QUIT2) {
print(QLatin1String("Quit"));
if (m_declarativeProfilerClient.isRecording()) {
m_quitAfterSave = true;
m_declarativeDataReady = false;
m_v8DataReady = false;
m_declarativeProfilerClient.setRecording(false);
m_v8profilerClient.setRecording(false);
} else {
quit();
}
}
}
void QmlProfilerApplication::run()
{
if (m_runMode == LaunchMode) {
m_process = new QProcess(this);
QStringList arguments;
arguments << QString::fromLatin1("-qmljsdebugger=port:%1,block").arg(m_port);
arguments << m_programArguments;
m_process->setProcessChannelMode(QProcess::MergedChannels);
connect(m_process, SIGNAL(readyRead()), this, SLOT(processHasOutput()));
connect(m_process, SIGNAL(finished(int,QProcess::ExitStatus)), this,
SLOT(processFinished()));
logStatus(QString("Starting '%1 %2' ...").arg(m_programPath,
arguments.join(" ")));
m_process->start(m_programPath, arguments);
if (!m_process->waitForStarted()) {
logError(QString("Could not run '%1': %2").arg(m_programPath,
m_process->errorString()));
exit(1);
}
}
m_connectTimer.start();
}
void QmlProfilerApplication::tryToConnect()
{
Q_ASSERT(!m_connection.isConnected());
++ m_connectionAttempts;
if (!m_verbose && !(m_connectionAttempts % 5)) {// print every 5 seconds
if (!m_verbose)
logError(QString("Could not connect to %1:%2 for %3 seconds ...").arg(
m_hostName, QString::number(m_port),
QString::number(m_connectionAttempts)));
}
if (m_connection.state() == QAbstractSocket::UnconnectedState) {
logStatus(QString("Connecting to %1:%2 ...").arg(m_hostName,
QString::number(m_port)));
m_connection.connectToHost(m_hostName, m_port);
}
}
void QmlProfilerApplication::connected()
{
m_connectTimer.stop();
print(QString(QLatin1String("Connected to host:port %1:%2."
"Wait for profile data or type a command"
"(type 'help'' to show list of commands).")
).arg(m_hostName).arg((m_port)));
QString recordingStatus(QLatin1String("Recording Status: %1"));
if (!m_declarativeProfilerClient.isRecording() &&
!m_v8profilerClient.isRecording())
recordingStatus = recordingStatus.arg(QLatin1String("Off"));
else
recordingStatus = recordingStatus.arg(QLatin1String("On"));
print(recordingStatus);
}
void QmlProfilerApplication::connectionStateChanged(
QAbstractSocket::SocketState state)
{
if (m_verbose)
qDebug() << state;
}
void QmlProfilerApplication::connectionError(QAbstractSocket::SocketError error)
{
if (m_verbose)
qDebug() << error;
}
void QmlProfilerApplication::processHasOutput()
{
Q_ASSERT(m_process);
while (m_process->bytesAvailable()) {
QTextStream out(stdout);
out << m_process->readAll();
}
}
void QmlProfilerApplication::processFinished()
{
Q_ASSERT(m_process);
if (m_process->exitStatus() == QProcess::NormalExit) {
logStatus(QString("Process exited (%1).").arg(m_process->exitCode()));
if (m_declarativeProfilerClient.isRecording()) {
logError("Process exited while recording, last trace is lost!");
exit(2);
} else {
exit(0);
}
} else {
logError("Process crashed! Exiting ...");
exit(3);
}
}
void QmlProfilerApplication::traceClientEnabled()
{
logStatus("Trace client is attached.");
// blocked server is waiting for recording message from both clients
// once the last one is connected, both messages should be sent
m_declarativeProfilerClient.sendRecordingStatus();
m_v8profilerClient.sendRecordingStatus();
}
void QmlProfilerApplication::profilerClientEnabled()
{
logStatus("Profiler client is attached.");
// blocked server is waiting for recording message from both clients
// once the last one is connected, both messages should be sent
m_declarativeProfilerClient.sendRecordingStatus();
m_v8profilerClient.sendRecordingStatus();
}
void QmlProfilerApplication::traceFinished()
{
const QString fileName = traceFileName();
if (m_profileData.save(fileName))
print(QString("Saving trace to %1.").arg(fileName));
if (m_quitAfterSave)
quit();
}
void QmlProfilerApplication::recordingChanged()
{
if (m_declarativeProfilerClient.isRecording()) {
print(QLatin1String("Recording is on."));
} else {
print(QLatin1String("Recording is off."));
}
}
void QmlProfilerApplication::print(const QString &line)
{
QTextStream err(stderr);
err << line << endl;
}
void QmlProfilerApplication::logError(const QString &error)
{
QTextStream err(stderr);
err << "Error: " << error << endl;
}
void QmlProfilerApplication::logStatus(const QString &status)
{
if (!m_verbose)
return;
QTextStream err(stderr);
err << status << endl;
}
void QmlProfilerApplication::declarativeComplete()
{
m_declarativeDataReady = true;
if (m_v8profilerClient.state() != QDeclarativeDebugClient::Enabled ||
m_v8DataReady) {
m_profileData.complete();
// once complete is sent, reset the flag
m_declarativeDataReady = false;
}
}
void QmlProfilerApplication::v8Complete()
{
m_v8DataReady = true;
if (m_declarativeProfilerClient.state() != QDeclarativeDebugClient::Enabled ||
m_declarativeDataReady) {
m_profileData.complete();
// once complete is sent, reset the flag
m_v8DataReady = false;
}
}

View File

@ -0,0 +1,117 @@
/****************************************************************************
**
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/
**
** This file is part of the QtDeclarative module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** 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, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QMLPROFILERAPPLICATION_H
#define QMLPROFILERAPPLICATION_H
#include <QtCore/QCoreApplication>
#include <QtCore/QProcess>
#include <QtCore/QTimer>
#include "profileclient.h"
class QmlProfilerApplication : public QCoreApplication
{
Q_OBJECT
public:
QmlProfilerApplication(int &argc, char **argv);
~QmlProfilerApplication();
bool parseArguments();
void printUsage();
int exec();
public slots:
void userCommand(const QString &command);
private slots:
void run();
void tryToConnect();
void connected();
void connectionStateChanged(QAbstractSocket::SocketState state);
void connectionError(QAbstractSocket::SocketError error);
void processHasOutput();
void processFinished();
void traceClientEnabled();
void profilerClientEnabled();
void traceFinished();
void recordingChanged();
void print(const QString &line);
void logError(const QString &error);
void logStatus(const QString &status);
void declarativeComplete();
void v8Complete();
private:
void printCommands();
QString traceFileName() const;
enum ApplicationMode {
LaunchMode,
AttachMode
} m_runMode;
// LaunchMode
QString m_programPath;
QStringList m_programArguments;
QProcess *m_process;
QString m_tracePrefix;
QString m_hostName;
quint16 m_port;
bool m_verbose;
bool m_quitAfterSave;
QDeclarativeDebugConnection m_connection;
DeclarativeProfileClient m_declarativeProfilerClient;
V8ProfileClient m_v8profilerClient;
ProfileData m_profileData;
QTimer m_connectTimer;
uint m_connectionAttempts;
bool m_declarativeDataReady;
bool m_v8DataReady;
};
#endif // QMLPROFILERAPPLICATION_H

View File

@ -1,5 +1,5 @@
TEMPLATE = subdirs
SUBDIRS += qmlscene qmlplugindump qmlmin qmleasing
SUBDIRS += qmlscene qmlplugindump qmlmin qmleasing qmlprofiler
contains(QT_CONFIG, qmltest): SUBDIRS += qmltestrunner