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:
parent
045e593291
commit
6c24d2feb4
|
@ -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());
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -1,5 +1,5 @@
|
|||
TEMPLATE = subdirs
|
||||
SUBDIRS += qmlscene qmlplugindump qmlmin qmleasing
|
||||
SUBDIRS += qmlscene qmlplugindump qmlmin qmleasing qmlprofiler
|
||||
|
||||
contains(QT_CONFIG, qmltest): SUBDIRS += qmltestrunner
|
||||
|
||||
|
|
Loading…
Reference in New Issue