aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/qml/main.cpp37
-rw-r--r--tools/qmleasing/splineeditor.cpp5
-rw-r--r--tools/qmlimportscanner/main.cpp2
-rw-r--r--tools/qmljs/qmljs.cpp14
-rw-r--r--tools/qmllint/main.cpp2
-rw-r--r--tools/qmlplugindump/main.cpp4
-rw-r--r--tools/qmlprofiler/commandlistener.cpp21
-rw-r--r--tools/qmlprofiler/commandlistener.h13
-rw-r--r--tools/qmlprofiler/constants.h19
-rw-r--r--tools/qmlprofiler/main.cpp32
-rw-r--r--tools/qmlprofiler/qmlprofilerapplication.cpp528
-rw-r--r--tools/qmlprofiler/qmlprofilerapplication.h35
-rw-r--r--tools/qmlprofiler/qmlprofilerclient.cpp102
-rw-r--r--tools/qmlprofiler/qmlprofilerclient.h15
-rw-r--r--tools/qmlprofiler/qmlprofilerdata.cpp70
-rw-r--r--tools/qmlprofiler/qmlprofilerdata.h1
16 files changed, 586 insertions, 314 deletions
diff --git a/tools/qml/main.cpp b/tools/qml/main.cpp
index 0553553e85..206788c061 100644
--- a/tools/qml/main.cpp
+++ b/tools/qml/main.cpp
@@ -53,6 +53,7 @@
#include <QFileInfo>
#include <QRegularExpression>
#include <QStringList>
+#include <QScopedPointer>
#include <QDebug>
#include <QStandardPaths>
#include <QTranslator>
@@ -67,8 +68,8 @@
#include <cstdlib>
#define VERSION_MAJ 1
-#define VERSION_MIN 0
-#define VERSION_STR "1.0"
+#define VERSION_MIN 1
+#define VERSION_STR "1.1"
#define FILE_OPEN_EVENT_WAIT_TIME 3000 // ms
@@ -163,13 +164,20 @@ class LoadWatcher : public QObject
public:
LoadWatcher(QQmlApplicationEngine *e, int expected)
: QObject(e)
+ , earlyExit(false)
, expect(expected)
, haveOne(false)
{
connect(e, SIGNAL(objectCreated(QObject*,QUrl)),
this, SLOT(checkFinished(QObject*)));
+ // QQmlApplicationEngine also connects quit() to QCoreApplication::quit
+ // but if called before exec() then QCoreApplication::quit does nothing
+ connect(e, SIGNAL(quit()),
+ this, SLOT(quit()));
}
+ bool earlyExit;
+
private:
void contain(QObject *o, const QUrl &containPath);
void checkForWindow(QObject *o);
@@ -196,6 +204,11 @@ public Q_SLOTS:
exit(2);//Different return code from qFatal
}
}
+
+ void quit() {
+ //Will be checked before calling exec()
+ earlyExit = true;
+ }
#ifdef QT_GUI_LIB
void onOpenGlContextCreated(QOpenGLContext *context);
#endif
@@ -293,11 +306,10 @@ void printVersion()
void printUsage()
{
- printf("Usage: qml [options] [files]\n");
+ printf("Usage: qml [options] [files] [-- args]\n");
printf("\n");
- printf("Any argument ending in .qml will be treated as a QML file to be loaded.\n");
+ printf("Any unknown argument before '--' will be treated as a QML file to be loaded.\n");
printf("Any number of QML files can be loaded. They will share the same engine.\n");
- printf("Any argument which is not a recognized option and which does not end in .qml will be ignored.\n");
printf("'gui' application type is only available if the QtGui module is available.\n");
printf("'widget' application type is only available if the QtWidgets module is available.\n");
printf("\n");
@@ -434,8 +446,8 @@ int main(int argc, char *argv[])
app->setOrganizationName("QtProject");
app->setOrganizationDomain("qt-project.org");
- qmlRegisterType<Config>("QmlRuntime.Config", VERSION_MAJ, VERSION_MIN, "Configuration");
- qmlRegisterType<PartialScene>("QmlRuntime.Config", VERSION_MAJ, VERSION_MIN, "PartialScene");
+ qmlRegisterType<Config>("QmlRuntime.Config", 1, 0, "Configuration");
+ qmlRegisterType<PartialScene>("QmlRuntime.Config", 1, 0, "PartialScene");
QQmlApplicationEngine e;
QStringList files;
QString confFile;
@@ -444,7 +456,7 @@ int main(int argc, char *argv[])
//Handle main arguments
QStringList argList = app->arguments();
- for (int i = 0; i < argList.count(); i++) {
+ for (int i = 1; i < argList.count(); i++) {
const QString &arg = argList[i];
if (arg == QLatin1String("-quiet"))
quietMode = true;
@@ -492,9 +504,7 @@ int main(int argc, char *argv[])
} else if (arg == QLatin1String("-desktop")) {
QCoreApplication::setAttribute(Qt::AA_UseDesktopOpenGL);
} else {
- //If it ends in .qml, treat it as a file. Else ignore it
- if (arg.endsWith(".qml"))
- files << arg;
+ files << arg;
}
}
@@ -538,7 +548,7 @@ int main(int argc, char *argv[])
loadConf(confFile, !verboseMode);
//Load files
- LoadWatcher lw(&e, files.count());
+ QScopedPointer<LoadWatcher> lw(new LoadWatcher(&e, files.count()));
// Load dummy data before loading QML-files
if (!dummyDir.isEmpty() && QFileInfo (dummyDir).isDir())
@@ -570,6 +580,9 @@ int main(int argc, char *argv[])
}
}
+ if (lw->earlyExit)
+ return 0;
+
return app->exec();
}
diff --git a/tools/qmleasing/splineeditor.cpp b/tools/qmleasing/splineeditor.cpp
index a2ddc056df..b957cab4fe 100644
--- a/tools/qmleasing/splineeditor.cpp
+++ b/tools/qmleasing/splineeditor.cpp
@@ -680,6 +680,7 @@ void SplineEditor::setEasingCurve(const QString &code)
const QStringList stringList = cleanCode.split(QLatin1Char(','), QString::SkipEmptyParts);
if (stringList.count() >= 6 && (stringList.count() % 6 == 0)) {
QList<qreal> realList;
+ realList.reserve(stringList.count());
foreach (const QString &string, stringList) {
bool ok;
realList.append(string.toDouble(&ok));
@@ -687,7 +688,9 @@ void SplineEditor::setEasingCurve(const QString &code)
return;
}
QList<QPointF> points;
- for (int i = 0; i < realList.count() / 2; ++i)
+ const int count = realList.count() / 2;
+ points.reserve(count);
+ for (int i = 0; i < count; ++i)
points.append(QPointF(realList.at(i * 2), realList.at(i * 2 + 1)));
if (points.last() == QPointF(1.0, 1.0)) {
QEasingCurve easingCurve(QEasingCurve::BezierSpline);
diff --git a/tools/qmlimportscanner/main.cpp b/tools/qmlimportscanner/main.cpp
index 37f2962a14..beb47e43c1 100644
--- a/tools/qmlimportscanner/main.cpp
+++ b/tools/qmlimportscanner/main.cpp
@@ -35,7 +35,7 @@
#include <private/qqmljsparser_p.h>
#include <private/qqmljsast_p.h>
#include <private/qv4codegen_p.h>
-#include <private/qv4value_inl_p.h>
+#include <private/qv4value_p.h>
#include <private/qqmlpool_p.h>
#include <private/qqmlirbuilder_p.h>
diff --git a/tools/qmljs/qmljs.cpp b/tools/qmljs/qmljs.cpp
index db9d1b9cda..53e520cd1f 100644
--- a/tools/qmljs/qmljs.cpp
+++ b/tools/qmljs/qmljs.cpp
@@ -44,6 +44,7 @@
#include "private/qv4mm_p.h"
#include "private/qv4context_p.h"
#include "private/qv4script_p.h"
+#include "private/qv4string_p.h"
#ifdef V4_ENABLE_JIT
# include "private/qv4isel_masm_p.h"
@@ -72,7 +73,7 @@ struct Print: FunctionObject
};
V4_OBJECT(FunctionObject)
- static ReturnedValue call(Managed *, CallData *callData)
+ static ReturnedValue call(const Managed *, CallData *callData)
{
for (int i = 0; i < callData->argc; ++i) {
QString s = callData->args[i].toQStringNoThrow();
@@ -98,9 +99,9 @@ struct GC: public FunctionObject
};
V4_OBJECT(FunctionObject)
- static ReturnedValue call(Managed *m, CallData *)
+ static ReturnedValue call(const Managed *m, CallData *)
{
- static_cast<GC *>(m)->engine()->memoryManager->runGC();
+ static_cast<const GC *>(m)->engine()->memoryManager->runGC();
return Encode::undefined();
}
};
@@ -113,7 +114,7 @@ static void showException(QV4::ExecutionContext *ctx, const QV4::Value &exceptio
{
QV4::Scope scope(ctx);
QV4::ScopedValue ex(scope, exception);
- QV4::ErrorObject *e = ex->asErrorObject();
+ QV4::ErrorObject *e = ex->as<QV4::ErrorObject>();
if (!e) {
std::cerr << "Uncaught exception: " << qPrintable(ex->toQString()) << std::endl;
} else {
@@ -187,11 +188,10 @@ int main(int argc, char *argv[])
QV4::Scope scope(&vm);
QV4::ScopedContext ctx(scope, vm.rootContext());
- QV4::ScopedObject globalObject(scope, vm.globalObject());
QV4::ScopedObject print(scope, vm.memoryManager->alloc<builtins::Print>(ctx));
- globalObject->put(QV4::ScopedString(scope, vm.newIdentifier(QStringLiteral("print"))).getPointer(), print);
+ vm.globalObject->put(QV4::ScopedString(scope, vm.newIdentifier(QStringLiteral("print"))).getPointer(), print);
QV4::ScopedObject gc(scope, vm.memoryManager->alloc<builtins::GC>(ctx));
- globalObject->put(QV4::ScopedString(scope, vm.newIdentifier(QStringLiteral("gc"))).getPointer(), gc);
+ vm.globalObject->put(QV4::ScopedString(scope, vm.newIdentifier(QStringLiteral("gc"))).getPointer(), gc);
foreach (const QString &fn, args) {
QFile file(fn);
diff --git a/tools/qmllint/main.cpp b/tools/qmllint/main.cpp
index cd81a5bad6..a1161c635a 100644
--- a/tools/qmllint/main.cpp
+++ b/tools/qmllint/main.cpp
@@ -37,7 +37,7 @@
#include <QCommandLineParser>
#include <QCoreApplication>
-#include <private/qv4value_inl_p.h>
+#include <private/qv4value_p.h>
#include <private/qqmljslexer_p.h>
#include <private/qqmljsparser_p.h>
#include <private/qqmljsengine_p.h>
diff --git a/tools/qmlplugindump/main.cpp b/tools/qmlplugindump/main.cpp
index 96c23ae268..c7539ac874 100644
--- a/tools/qmlplugindump/main.cpp
+++ b/tools/qmlplugindump/main.cpp
@@ -685,7 +685,9 @@ private:
qml->writeScriptBinding(QLatin1String("name"), enquote(QString::fromUtf8(e.name())));
QList<QPair<QString, QString> > namesValues;
- for (int index = 0; index < e.keyCount(); ++index) {
+ const int keyCount = e.keyCount();
+ namesValues.reserve(keyCount);
+ for (int index = 0; index < keyCount; ++index) {
namesValues.append(qMakePair(enquote(QString::fromUtf8(e.key(index))), QString::number(e.value(index))));
}
diff --git a/tools/qmlprofiler/commandlistener.cpp b/tools/qmlprofiler/commandlistener.cpp
index 1d538d8a3d..369d095725 100644
--- a/tools/qmlprofiler/commandlistener.cpp
+++ b/tools/qmlprofiler/commandlistener.cpp
@@ -35,24 +35,7 @@
#include "constants.h"
#include <QtCore/QTextStream>
-CommandListener::CommandListener(QObject *parent)
- : QThread(parent)
- , m_stopRequested(false)
+void CommandListener::readCommand()
{
-}
-
-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());
+ emit command(QTextStream(stdin).readLine());
}
diff --git a/tools/qmlprofiler/commandlistener.h b/tools/qmlprofiler/commandlistener.h
index 7d4d43d727..e74d5323c8 100644
--- a/tools/qmlprofiler/commandlistener.h
+++ b/tools/qmlprofiler/commandlistener.h
@@ -36,20 +36,13 @@
#include <QtCore/QThread>
-class CommandListener : public QThread
-{
+class CommandListener : public QObject {
Q_OBJECT
-public:
- CommandListener(QObject *parent = 0);
+public slots:
+ void readCommand();
- void run();
-
- void requestStop() { m_stopRequested = true; }
signals:
void command(const QString &command);
-
-private:
- bool m_stopRequested;
};
#endif // COMMANDLISTENER_H
diff --git a/tools/qmlprofiler/constants.h b/tools/qmlprofiler/constants.h
index 4e5aac7e32..5b4b52515f 100644
--- a/tools/qmlprofiler/constants.h
+++ b/tools/qmlprofiler/constants.h
@@ -36,16 +36,27 @@
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_OUTPUT[] = "output";
+const char CMD_OUTPUT2[] = "o";
+
+const char CMD_FLUSH[] = "flush";
+const char CMD_FLUSH2[] = "f";
+
+const char CMD_CLEAR[] = "clear";
+const char CMD_CLEAR2[] = "c";
+
const char CMD_QUIT[] ="quit";
const char CMD_QUIT2[] = "q";
+const char CMD_YES[] = "yes";
+const char CMD_YES2[] = "y";
+
+const char CMD_NO[] = "no";
+const char CMD_NO2[] = "n";
+
} // Constants
#endif // CONSTANTS_H
diff --git a/tools/qmlprofiler/main.cpp b/tools/qmlprofiler/main.cpp
index a1bd1b38b4..12fcc79efa 100644
--- a/tools/qmlprofiler/main.cpp
+++ b/tools/qmlprofiler/main.cpp
@@ -38,19 +38,23 @@ 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();
+ app.parseArguments();
-
- return exitValue;
+ if (app.isInteractive()) {
+ QThread listenerThread;
+ CommandListener listener;
+ listener.moveToThread(&listenerThread);
+ QObject::connect(&listener, SIGNAL(command(QString)), &app, SLOT(userCommand(QString)));
+ QObject::connect(&app, SIGNAL(readyForCommand()), &listener, SLOT(readCommand()));
+ listenerThread.start();
+ int exitValue = app.exec();
+ listenerThread.quit();
+ // wait for listener to exit
+ listenerThread.wait();
+ return exitValue;
+ } else {
+ int exitValue = app.exec();
+ app.outputData();
+ return exitValue;
+ }
}
diff --git a/tools/qmlprofiler/qmlprofilerapplication.cpp b/tools/qmlprofiler/qmlprofilerapplication.cpp
index a51f67164f..8633810790 100644
--- a/tools/qmlprofiler/qmlprofilerapplication.cpp
+++ b/tools/qmlprofiler/qmlprofilerapplication.cpp
@@ -40,47 +40,49 @@
#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";
+#include <QtCore/QCommandLineParser>
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";
+ "The following commands are available:\n"
+ "'r', 'record'\n"
+ " Switch recording on or off.\n"
+ "'o [file]', 'output [file]'\n"
+ " Output profiling data to <file>. If no <file>\n"
+ " parameter is given, output to whatever was given\n"
+ " with --output, or standard output.\n"
+ "'c', 'clear'\n"
+ " Clear profiling data recorded so far from memory.\n"
+ "'f [file]', 'flush [file]'\n"
+ " Stop recording if it is running, then output the\n"
+ " data, and finally clear it from memory.\n"
+ "'q', 'quit'\n"
+ " Terminate the program if started from qmlprofiler,\n"
+ " and qmlprofiler itself.";
+
+static const char *features[] = {
+ "javascript",
+ "memory",
+ "pixmapcache",
+ "scenegraph",
+ "animations",
+ "painting",
+ "compiling",
+ "creating",
+ "binding",
+ "handlingsignal",
+ "inputevents"
+};
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_pendingRequest(REQUEST_NONE),
m_verbose(false),
- m_quitAfterSave(false),
+ m_recording(true),
+ m_interactive(false),
m_qmlProfilerClient(&m_connection),
m_v8profilerClient(&m_connection),
m_connectionAttempts(0),
@@ -95,11 +97,11 @@ QmlProfilerApplication::QmlProfilerApplication(int &argc, char **argv) :
connect(&m_connection, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(connectionError(QAbstractSocket::SocketError)));
connect(&m_qmlProfilerClient, SIGNAL(enabledChanged()), this, SLOT(traceClientEnabled()));
- connect(&m_qmlProfilerClient, SIGNAL(recordingChanged(bool)), this, SLOT(recordingChanged()));
connect(&m_qmlProfilerClient, SIGNAL(range(QQmlProfilerService::RangeType,QQmlProfilerService::BindingType,qint64,qint64,QStringList,QmlEventLocation)),
&m_profilerData, SLOT(addQmlEvent(QQmlProfilerService::RangeType,QQmlProfilerService::BindingType,qint64,qint64,QStringList,QmlEventLocation)));
connect(&m_qmlProfilerClient, SIGNAL(traceFinished(qint64)), &m_profilerData, SLOT(setTraceEndTime(qint64)));
connect(&m_qmlProfilerClient, SIGNAL(traceStarted(qint64)), &m_profilerData, SLOT(setTraceStartTime(qint64)));
+ connect(&m_qmlProfilerClient, SIGNAL(traceStarted(qint64)), this, SLOT(notifyTraceStarted()));
connect(&m_qmlProfilerClient, SIGNAL(frame(qint64,int,int,int)), &m_profilerData, SLOT(addFrameEvent(qint64,int,int,int)));
connect(&m_qmlProfilerClient, SIGNAL(sceneGraphFrame(QQmlProfilerService::SceneGraphFrameType,
qint64,qint64,qint64,qint64,qint64,qint64)),
@@ -113,6 +115,8 @@ QmlProfilerApplication::QmlProfilerApplication(int &argc, char **argv) :
qint64)),
&m_profilerData, SLOT(addMemoryEvent(QQmlProfilerService::MemoryType,qint64,
qint64)));
+ connect(&m_qmlProfilerClient, SIGNAL(inputEvent(QQmlProfilerService::EventType,qint64)),
+ &m_profilerData, SLOT(addInputEvent(QQmlProfilerService::EventType,qint64)));
connect(&m_qmlProfilerClient, SIGNAL(complete()), this, SLOT(qmlComplete()));
@@ -140,63 +144,139 @@ QmlProfilerApplication::~QmlProfilerApplication()
delete m_process;
}
-bool QmlProfilerApplication::parseArguments()
+void 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_qmlProfilerClient.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;
- }
+ setApplicationName(QLatin1String("qmlprofiler"));
+ setApplicationVersion(QLatin1String(qVersion()));
+
+ QCommandLineParser parser;
+ parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
+ parser.setOptionsAfterPositionalArgumentsMode(QCommandLineParser::ParseAsPositionalArguments);
+
+ parser.setApplicationDescription(QChar::LineFeed + tr(
+ "The QML Profiler retrieves QML tracing data from an application. The data\n"
+ "collected can then be visualized in Qt Creator. The application to be profiled\n"
+ "has to enable QML debugging. See the Qt Creator documentation on how to do\n"
+ "this for different Qt versions."));
+
+ QCommandLineOption attach(QStringList() << QLatin1String("a") << QLatin1String("attach"),
+ tr("Attach to an application already running on <hostname>, "
+ "instead of starting it locally."),
+ QLatin1String("hostname"));
+ parser.addOption(attach);
+
+ QCommandLineOption port(QStringList() << QLatin1String("p") << QLatin1String("port"),
+ tr("Connect to the TCP port <port>. The default is 3768."),
+ QLatin1String("port"), QLatin1String("3768"));
+ parser.addOption(port);
+
+ QCommandLineOption output(QStringList() << QLatin1String("o") << QLatin1String("output"),
+ tr("Save tracing data in <file>. By default the data is sent to the "
+ "standard output."), QLatin1String("file"), QString());
+ parser.addOption(output);
+
+ QCommandLineOption record(QLatin1String("record"),
+ tr("If set to 'off', don't immediately start recording data when the "
+ "QML engine starts, but instead either start the recording "
+ "interactively or with the JavaScript console.profile() function. "
+ "By default the recording starts immediately."),
+ QLatin1String("on|off"), QLatin1String("on"));
+ parser.addOption(record);
+
+ QStringList featureList;
+ for (int i = 0; i < QQmlProfilerService::MaximumProfileFeature; ++i)
+ featureList << QLatin1String(features[i]);
+
+ QCommandLineOption include(QLatin1String("include"),
+ tr("Comma-separated list of features to record. By default all "
+ "features supported by the QML engine are recorded. If --include "
+ "is specified, only the given features will be recorded. "
+ "The following features are unserstood by qmlprofiler: %1").arg(
+ featureList.join(", ")),
+ QLatin1String("feature,..."));
+ parser.addOption(include);
+
+ QCommandLineOption exclude(QLatin1String("exclude"),
+ tr("Comma-separated list of features to exclude when recording. By "
+ "default all features supported by the QML engine are recorded. "
+ "See --include for the features understood by qmlprofiler."),
+ QLatin1String("feature,..."));
+ parser.addOption(exclude);
+
+ QCommandLineOption interactive(QLatin1String("interactive"),
+ tr("Manually control the recording from the command line. The "
+ "profiler will not terminate itself when the application "
+ "does so in this case.") + QChar::Space + tr(commandTextC));
+ parser.addOption(interactive);
+
+ QCommandLineOption verbose(QStringList() << QLatin1String("verbose"),
+ tr("Print debugging output."));
+ parser.addOption(verbose);
+
+ parser.addHelpOption();
+ parser.addVersionOption();
+
+ parser.addPositionalArgument(QLatin1String("program"),
+ tr("The program to be started and profiled."),
+ QLatin1String("[program]"));
+ parser.addPositionalArgument(QLatin1String("parameters"),
+ tr("Parameters for the program to be started."),
+ QLatin1String("[parameters...]"));
+
+ parser.process(*this);
+
+ if (parser.isSet(attach)) {
+ m_hostName = parser.value(attach);
+ m_runMode = AttachMode;
+ }
+
+ if (parser.isSet(port)) {
+ bool isNumber;
+ m_port = parser.value(port).toUShort(&isNumber);
+ if (!isNumber) {
+ logError(tr("'%1' is not a valid port.").arg(parser.value(port)));
+ parser.showHelp(1);
}
}
- if (m_runMode == LaunchMode
- && m_programPath.isEmpty())
- return false;
+ m_outputFile = parser.value(output);
- if (m_runMode == AttachMode
- && !m_programPath.isEmpty())
- return false;
+ m_recording = (parser.value(record) == QLatin1String("on"));
+ m_interactive = parser.isSet(interactive);
- return true;
-}
+ quint64 features = std::numeric_limits<quint64>::max();
+ if (parser.isSet(include)) {
+ if (parser.isSet(exclude)) {
+ logError(tr("qmlprofiler can only process either --include or --exclude, not both."));
+ parser.showHelp(4);
+ }
+ features = parseFeatures(featureList, parser.value(include), false);
+ }
-void QmlProfilerApplication::printUsage()
-{
- print(QLatin1String(usageTextC));
- print(QLatin1String(commandTextC));
+ if (parser.isSet(exclude))
+ features = parseFeatures(featureList, parser.value(exclude), true);
+
+ if (features == 0)
+ parser.showHelp(4);
+
+ m_qmlProfilerClient.setFeatures(features);
+
+ if (parser.isSet(verbose))
+ m_verbose = true;
+
+ m_programArguments = parser.positionalArguments();
+ if (!m_programArguments.isEmpty())
+ m_programPath = m_programArguments.takeFirst();
+
+ if (m_runMode == LaunchMode && m_programPath.isEmpty()) {
+ logError(tr("You have to specify either --attach or a program to start."));
+ parser.showHelp(2);
+ }
+
+ if (m_runMode == AttachMode && !m_programPath.isEmpty()) {
+ logError(tr("--attach cannot be used when starting a program."));
+ parser.showHelp(3);
+ }
}
int QmlProfilerApplication::exec()
@@ -205,55 +285,189 @@ int QmlProfilerApplication::exec()
return QCoreApplication::exec();
}
-void QmlProfilerApplication::printCommands()
+bool QmlProfilerApplication::isInteractive() const
+{
+ return m_interactive;
+}
+
+quint64 QmlProfilerApplication::parseFeatures(const QStringList &featureList, const QString &values,
+ bool exclude)
+{
+ quint64 features = exclude ? std::numeric_limits<quint64>::max() : 0;
+ QStringList givenFeatures = values.split(QLatin1Char(','));
+ foreach (const QString &f, givenFeatures) {
+ int index = featureList.indexOf(f);
+ if (index < 0) {
+ logError(tr("Unknown feature '%1'").arg(f));
+ return 0;
+ }
+ quint64 flag = static_cast<quint64>(1) << index;
+ features = (exclude ? (features ^ flag) : (features | flag));
+ }
+ if (features == 0) {
+ logError(exclude ? tr("No features remaining to record after processing --exclude.") :
+ tr("No features specified for --include."));
+ }
+ return features;
+}
+
+void QmlProfilerApplication::flush()
{
- print(QLatin1String(commandTextC));
+ if (m_recording) {
+ m_pendingRequest = REQUEST_FLUSH;
+ m_qmlProfilerClient.sendRecordingStatus(false);
+ m_v8profilerClient.sendRecordingStatus(false);
+ } else {
+ if (m_profilerData.save(m_interactiveOutputFile)) {
+ m_profilerData.clear();
+ if (!m_interactiveOutputFile.isEmpty())
+ prompt(tr("Data written to %1.").arg(m_interactiveOutputFile));
+ else
+ prompt();
+ } else {
+ prompt(tr("Saving failed."));
+ }
+ m_interactiveOutputFile.clear();
+ m_pendingRequest = REQUEST_NONE;
+ }
}
-QString QmlProfilerApplication::traceFileName() const
+void QmlProfilerApplication::output()
{
- 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;
+ if (m_profilerData.save(m_interactiveOutputFile)) {
+ if (!m_interactiveOutputFile.isEmpty())
+ prompt(tr("Data written to %1.").arg(m_interactiveOutputFile));
+ else
+ prompt();
+ } else {
+ prompt(tr("Saving failed"));
}
- return QFileInfo(fileName).absoluteFilePath();
+ m_interactiveOutputFile.clear();
+ m_pendingRequest = REQUEST_NONE;
+}
+
+bool QmlProfilerApplication::checkOutputFile(PendingRequest pending)
+{
+ if (m_interactiveOutputFile.isEmpty())
+ return true;
+ QFileInfo file(m_interactiveOutputFile);
+ if (file.exists()) {
+ if (!file.isFile()) {
+ prompt(tr("Cannot overwrite %1.").arg(m_interactiveOutputFile));
+ m_interactiveOutputFile.clear();
+ } else {
+ prompt(tr("%1 exists. Overwrite (y/n)?").arg(m_interactiveOutputFile));
+ m_pendingRequest = pending;
+ }
+ return false;
+ } else {
+ return true;
+ }
}
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_qmlProfilerClient.setRecording(
- !m_qmlProfilerClient.isRecording());
- m_v8profilerClient.setRecording(!m_v8profilerClient.isRecording());
- m_qmlDataReady = false;
- m_v8DataReady = false;
- } else if (cmd == Constants::CMD_QUIT
- || cmd == Constants::CMD_QUIT2) {
- print(QLatin1String("Quit"));
- if (m_qmlProfilerClient.isRecording()) {
- m_quitAfterSave = true;
- m_qmlDataReady = false;
- m_v8DataReady = false;
- m_qmlProfilerClient.setRecording(false);
- m_v8profilerClient.setRecording(false);
+ QStringList args = command.split(QChar::Space, QString::SkipEmptyParts);
+ if (args.isEmpty()) {
+ prompt();
+ return;
+ }
+
+ QByteArray cmd = args.takeFirst().trimmed().toLatin1();
+
+ if (m_pendingRequest == REQUEST_QUIT) {
+ if (cmd == Constants::CMD_YES || cmd == Constants::CMD_YES2) {
+ quit();
+ } else if (cmd == Constants::CMD_NO || cmd == Constants::CMD_NO2) {
+ m_pendingRequest = REQUEST_NONE;
+ prompt();
+ } else {
+ prompt(tr("The application is still generating data. Really quit (y/n)?"));
+ }
+ return;
+ }
+
+ if (m_pendingRequest == REQUEST_OUTPUT_FILE || m_pendingRequest == REQUEST_FLUSH_FILE) {
+ if (cmd == Constants::CMD_YES || cmd == Constants::CMD_YES2) {
+ if (m_pendingRequest == REQUEST_OUTPUT_FILE)
+ output();
+ else
+ flush();
+ } else if (cmd == Constants::CMD_NO || cmd == Constants::CMD_NO2) {
+ m_pendingRequest = REQUEST_NONE;
+ m_interactiveOutputFile.clear();
+ prompt();
+ } else {
+ prompt(tr("%1 exists. Overwrite (y/n)?"));
+ }
+ return;
+ }
+
+ if (cmd == Constants::CMD_RECORD || cmd == Constants::CMD_RECORD2) {
+ m_pendingRequest = REQUEST_TOGGLE_RECORDING;
+ m_qmlProfilerClient.sendRecordingStatus(!m_recording);
+ m_v8profilerClient.sendRecordingStatus(!m_recording);
+ } else if (cmd == Constants::CMD_QUIT || cmd == Constants::CMD_QUIT2) {
+ m_pendingRequest = REQUEST_QUIT;
+ if (m_recording) {
+ prompt(tr("The application is still generating data. Really quit (y/n)?"));
+ } else if (!m_profilerData.isEmpty()) {
+ prompt(tr("There is still trace data in memory. Really quit (y/n)?"));
} else {
quit();
}
+ } else if (cmd == Constants::CMD_OUTPUT || cmd == Constants::CMD_OUTPUT2) {
+ if (m_recording) {
+ prompt(tr("Cannot output while recording data."));
+ } else if (m_profilerData.isEmpty()) {
+ prompt(tr("No data was recorded so far."));
+ } else {
+ m_interactiveOutputFile = args.length() > 0 ? args[0] : m_outputFile;
+ if (checkOutputFile(REQUEST_OUTPUT_FILE))
+ output();
+ }
+ } else if (cmd == Constants::CMD_CLEAR || cmd == Constants::CMD_CLEAR2) {
+ if (m_recording) {
+ prompt(tr("Cannot clear data while recording."));
+ } else if (m_profilerData.isEmpty()) {
+ prompt(tr("No data was recorded so far."));
+ } else {
+ m_profilerData.clear();
+ prompt(tr("Trace data cleared."));
+ }
+ } else if (cmd == Constants::CMD_FLUSH || cmd == Constants::CMD_FLUSH2) {
+ if (!m_recording && m_profilerData.isEmpty()) {
+ prompt(tr("No data was recorded so far."));
+ } else {
+ m_interactiveOutputFile = args.length() > 0 ? args[0] : m_outputFile;
+ if (checkOutputFile(REQUEST_FLUSH_FILE))
+ flush();
+ }
+ } else {
+ prompt(tr(commandTextC));
+ }
+}
+
+void QmlProfilerApplication::notifyTraceStarted()
+{
+ // Synchronize to server state. It doesn't hurt to do this multiple times in a row for
+ // different traces. There is no symmetric event to "Complete" after all.
+ m_recording = true;
+
+ if (m_pendingRequest == REQUEST_TOGGLE_RECORDING) {
+ m_pendingRequest = REQUEST_NONE;
+ prompt(tr("Recording started"));
+ } else {
+ prompt(tr("Application started recording"), false);
+ }
+}
+
+void QmlProfilerApplication::outputData()
+{
+ if (!m_profilerData.isEmpty()) {
+ m_profilerData.save(m_outputFile);
+ m_profilerData.clear();
}
}
@@ -304,17 +518,9 @@ void QmlProfilerApplication::tryToConnect()
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_qmlProfilerClient.isRecording() &&
- !m_v8profilerClient.isRecording())
- recordingStatus = recordingStatus.arg(QLatin1String("Off"));
- else
- recordingStatus = recordingStatus.arg(QLatin1String("On"));
- print(recordingStatus);
+ prompt(tr("Connected to host:port %1:%2. Wait for profile data or type a command (type 'help' "
+ "to show list of commands).\nRecording Status: %3")
+ .arg(m_hostName).arg((m_port)).arg(m_recording ? tr("on") : tr("off")));
}
void QmlProfilerApplication::connectionStateChanged(
@@ -334,7 +540,7 @@ void QmlProfilerApplication::processHasOutput()
{
Q_ASSERT(m_process);
while (m_process->bytesAvailable()) {
- QTextStream out(stdout);
+ QTextStream out(stderr);
out << m_process->readAll();
}
}
@@ -342,19 +548,19 @@ void QmlProfilerApplication::processHasOutput()
void QmlProfilerApplication::processFinished()
{
Q_ASSERT(m_process);
+ int exitCode = 0;
if (m_process->exitStatus() == QProcess::NormalExit) {
logStatus(QString("Process exited (%1).").arg(m_process->exitCode()));
-
- if (m_qmlProfilerClient.isRecording()) {
- logError("Process exited while recording, last trace is lost!");
- exit(2);
- } else {
- exit(0);
+ if (m_recording) {
+ logError("Process exited while recording, last trace is damaged!");
+ exitCode = 2;
}
} else {
- logError("Process crashed! Exiting ...");
- exit(3);
+ logError("Process crashed!");
+ exitCode = 3;
}
+ if (!m_interactive)
+ exit(exitCode);
}
void QmlProfilerApplication::traceClientEnabled()
@@ -362,8 +568,8 @@ 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_qmlProfilerClient.sendRecordingStatus();
- m_v8profilerClient.sendRecordingStatus();
+ m_qmlProfilerClient.sendRecordingStatus(m_recording);
+ m_v8profilerClient.sendRecordingStatus(m_recording);
}
void QmlProfilerApplication::profilerClientEnabled()
@@ -372,34 +578,38 @@ void QmlProfilerApplication::profilerClientEnabled()
// blocked server is waiting for recording message from both clients
// once the last one is connected, both messages should be sent
- m_qmlProfilerClient.sendRecordingStatus();
- m_v8profilerClient.sendRecordingStatus();
+ m_qmlProfilerClient.sendRecordingStatus(m_recording);
+ m_v8profilerClient.sendRecordingStatus(m_recording);
}
void QmlProfilerApplication::traceFinished()
{
- const QString fileName = traceFileName();
+ m_recording = false; // only on "Complete" we know that the trace is really finished.
- if (m_profilerData.save(fileName))
- print(QString("Saving trace to %1.").arg(fileName));
+ // after receiving both notifications, reset the flags
+ m_qmlDataReady = false;
+ m_v8DataReady = false;
- if (m_quitAfterSave)
- quit();
-}
-
-void QmlProfilerApplication::recordingChanged()
-{
- if (m_qmlProfilerClient.isRecording()) {
- print(QLatin1String("Recording is on."));
+ if (m_pendingRequest == REQUEST_FLUSH) {
+ flush();
+ } else if (m_pendingRequest == REQUEST_TOGGLE_RECORDING) {
+ m_pendingRequest = REQUEST_NONE;
+ prompt(tr("Recording stopped."));
} else {
- print(QLatin1String("Recording is off."));
+ prompt(tr("Application stopped recording."), false);
}
}
-void QmlProfilerApplication::print(const QString &line)
+void QmlProfilerApplication::prompt(const QString &line, bool ready)
{
- QTextStream err(stderr);
- err << line << endl;
+ if (m_interactive) {
+ QTextStream err(stderr);
+ if (!line.isEmpty())
+ err << line << endl;
+ err << QLatin1String("> ");
+ if (ready)
+ emit readyForCommand();
+ }
}
void QmlProfilerApplication::logError(const QString &error)
@@ -422,8 +632,6 @@ void QmlProfilerApplication::qmlComplete()
if (m_v8profilerClient.state() != QQmlDebugClient::Enabled ||
m_v8DataReady) {
m_profilerData.complete();
- // once complete is sent, reset the flag
- m_qmlDataReady = false;
}
}
@@ -433,7 +641,5 @@ void QmlProfilerApplication::v8Complete()
if (m_qmlProfilerClient.state() != QQmlDebugClient::Enabled ||
m_qmlDataReady) {
m_profilerData.complete();
- // once complete is sent, reset the flag
- m_v8DataReady = false;
}
}
diff --git a/tools/qmlprofiler/qmlprofilerapplication.h b/tools/qmlprofiler/qmlprofilerapplication.h
index 7e7cebfcf1..f1bf6c3e93 100644
--- a/tools/qmlprofiler/qmlprofilerapplication.h
+++ b/tools/qmlprofiler/qmlprofilerapplication.h
@@ -41,6 +41,15 @@
#include "qmlprofilerclient.h"
#include "qmlprofilerdata.h"
+enum PendingRequest {
+ REQUEST_QUIT,
+ REQUEST_FLUSH_FILE,
+ REQUEST_FLUSH,
+ REQUEST_OUTPUT_FILE,
+ REQUEST_TOGGLE_RECORDING,
+ REQUEST_NONE
+};
+
class QmlProfilerApplication : public QCoreApplication
{
Q_OBJECT
@@ -48,12 +57,17 @@ public:
QmlProfilerApplication(int &argc, char **argv);
~QmlProfilerApplication();
- bool parseArguments();
- void printUsage();
+ void parseArguments();
int exec();
+ bool isInteractive() const;
+
+signals:
+ void readyForCommand();
public slots:
void userCommand(const QString &command);
+ void notifyTraceStarted();
+ void outputData();
private slots:
void run();
@@ -67,9 +81,8 @@ private slots:
void traceClientEnabled();
void profilerClientEnabled();
void traceFinished();
- void recordingChanged();
- void print(const QString &line);
+ void prompt(const QString &line = QString(), bool ready = true);
void logError(const QString &error);
void logStatus(const QString &status);
@@ -77,8 +90,10 @@ private slots:
void v8Complete();
private:
- void printCommands();
- QString traceFileName() const;
+ quint64 parseFeatures(const QStringList &featureList, const QString &values, bool exclude);
+ bool checkOutputFile(PendingRequest pending);
+ void flush();
+ void output();
enum ApplicationMode {
LaunchMode,
@@ -89,12 +104,16 @@ private:
QString m_programPath;
QStringList m_programArguments;
QProcess *m_process;
- QString m_tracePrefix;
QString m_hostName;
quint16 m_port;
+ QString m_outputFile;
+ QString m_interactiveOutputFile;
+
+ PendingRequest m_pendingRequest;
bool m_verbose;
- bool m_quitAfterSave;
+ bool m_recording;
+ bool m_interactive;
QQmlDebugConnection m_connection;
QmlProfilerClient m_qmlProfilerClient;
diff --git a/tools/qmlprofiler/qmlprofilerclient.cpp b/tools/qmlprofiler/qmlprofilerclient.cpp
index 711ddbd862..4976a5a0d9 100644
--- a/tools/qmlprofiler/qmlprofilerclient.cpp
+++ b/tools/qmlprofiler/qmlprofilerclient.cpp
@@ -39,17 +39,12 @@
ProfilerClient::ProfilerClient(const QString &clientName,
QQmlDebugConnection *client)
: QQmlDebugClient(clientName, client),
- m_recording(false),
m_enabled(false)
{
}
ProfilerClient::~ProfilerClient()
{
- //Disable profiling if started by client
- //Profiling data will be lost!!
- if (isRecording())
- setRecording(false);
}
void ProfilerClient::clearData()
@@ -62,29 +57,6 @@ bool ProfilerClient::isEnabled() const
return m_enabled;
}
-void ProfilerClient::sendRecordingStatus()
-{
-}
-
-bool ProfilerClient::isRecording() const
-{
- return m_recording;
-}
-
-void ProfilerClient::setRecording(bool v)
-{
- if (v == m_recording)
- return;
-
- m_recording = v;
-
- if (state() == Enabled) {
- sendRecordingStatus();
- }
-
- emit recordingChanged(v);
-}
-
void ProfilerClient::stateChanged(State status)
{
if ((m_enabled && status != Enabled) ||
@@ -99,8 +71,7 @@ class QmlProfilerClientPrivate
{
public:
QmlProfilerClientPrivate()
- : inProgressRanges(0)
- , maximumTime(0)
+ : inProgressRanges(0) , features(std::numeric_limits<quint64>::max())
{
::memset(rangeCount, 0,
QQmlProfilerService::MaximumRangeType * sizeof(int));
@@ -112,7 +83,8 @@ public:
QStack<QmlEventLocation> rangeLocations[QQmlProfilerService::MaximumRangeType];
QStack<QQmlProfilerService::BindingType> bindingTypes;
int rangeCount[QQmlProfilerService::MaximumRangeType];
- qint64 maximumTime;
+
+ quint64 features;
};
QmlProfilerClient::QmlProfilerClient(
@@ -127,6 +99,11 @@ QmlProfilerClient::~QmlProfilerClient()
delete d;
}
+void QmlProfilerClient::setFeatures(quint64 features)
+{
+ d->features = features;
+}
+
void QmlProfilerClient::clearData()
{
::memset(d->rangeCount, 0,
@@ -135,19 +112,43 @@ void QmlProfilerClient::clearData()
ProfilerClient::clearData();
}
-void QmlProfilerClient::sendRecordingStatus()
+void QmlProfilerClient::sendRecordingStatus(bool record)
{
QByteArray ba;
QDataStream stream(&ba, QIODevice::WriteOnly);
- stream << isRecording();
+ stream << record << -1 << d->features;
sendMessage(ba);
}
+inline QQmlProfilerService::ProfileFeature featureFromRangeType(
+ QQmlProfilerService::RangeType range)
+{
+ switch (range) {
+ case QQmlProfilerService::Painting:
+ return QQmlProfilerService::ProfilePainting;
+ case QQmlProfilerService::Compiling:
+ return QQmlProfilerService::ProfileCompiling;
+ case QQmlProfilerService::Creating:
+ return QQmlProfilerService::ProfileCreating;
+ case QQmlProfilerService::Binding:
+ return QQmlProfilerService::ProfileBinding;
+ case QQmlProfilerService::HandlingSignal:
+ return QQmlProfilerService::ProfileHandlingSignal;
+ case QQmlProfilerService::Javascript:
+ return QQmlProfilerService::ProfileJavaScript;
+ default:
+ return QQmlProfilerService::MaximumProfileFeature;
+ }
+}
+
void QmlProfilerClient::messageReceived(const QByteArray &data)
{
QByteArray rwData = data;
QDataStream stream(&rwData, QIODevice::ReadOnly);
+ // Force all the 1 << <FLAG> expressions to be done in 64 bit, to silence some warnings
+ const quint64 one = static_cast<quint64>(1);
+
qint64 time;
int messageType;
@@ -162,25 +163,27 @@ void QmlProfilerClient::messageReceived(const QByteArray &data)
if (event == QQmlProfilerService::EndTrace) {
emit this->traceFinished(time);
- d->maximumTime = time;
- d->maximumTime = qMax(time, d->maximumTime);
} else if (event == QQmlProfilerService::AnimationFrame) {
+ if (!(d->features & one << QQmlProfilerService::ProfileAnimations))
+ return;
int frameRate, animationCount;
int threadId = 0;
stream >> frameRate >> animationCount;
if (!stream.atEnd())
stream >> threadId;
emit this->frame(time, frameRate, animationCount, threadId);
- d->maximumTime = qMax(time, d->maximumTime);
} else if (event == QQmlProfilerService::StartTrace) {
emit this->traceStarted(time);
- d->maximumTime = time;
- } else if (event < QQmlProfilerService::MaximumEventType) {
- d->maximumTime = qMax(time, d->maximumTime);
+ } else if (event == QQmlProfilerService::Key || event == QQmlProfilerService::Mouse) {
+ if (!(d->features & one << QQmlProfilerService::ProfileInputEvents))
+ return;
+ emit this->inputEvent((QQmlProfilerService::EventType)event, time);
}
} else if (messageType == QQmlProfilerService::Complete) {
emit complete();
} else if (messageType == QQmlProfilerService::SceneGraphFrame) {
+ if (!(d->features & one << QQmlProfilerService::ProfileSceneGraph))
+ return;
int sgEventType;
int count = 0;
qint64 params[5];
@@ -193,8 +196,9 @@ void QmlProfilerClient::messageReceived(const QByteArray &data)
params[count++] = 0;
emit sceneGraphFrame((QQmlProfilerService::SceneGraphFrameType)sgEventType, time,
params[0], params[1], params[2], params[3], params[4]);
- d->maximumTime = qMax(time, d->maximumTime);
} else if (messageType == QQmlProfilerService::PixmapCacheEvent) {
+ if (!(d->features & one << QQmlProfilerService::ProfilePixmapCache))
+ return;
int pixEvTy, width = 0, height = 0, refcount = 0;
QString pixUrl;
stream >> pixEvTy >> pixUrl;
@@ -207,13 +211,13 @@ void QmlProfilerClient::messageReceived(const QByteArray &data)
}
emit pixmapCache((QQmlProfilerService::PixmapEventType)pixEvTy, time,
QmlEventLocation(pixUrl,0,0), width, height, refcount);
- d->maximumTime = qMax(time, d->maximumTime);
} else if (messageType == QQmlProfilerService::MemoryAllocation) {
+ if (!(d->features & one << QQmlProfilerService::ProfileMemory))
+ return;
int type;
qint64 delta;
stream >> type >> delta;
emit memoryAllocation((QQmlProfilerService::MemoryType)type, time, delta);
- d->maximumTime = qMax(time, d->maximumTime);
} else {
int range;
stream >> range;
@@ -221,6 +225,10 @@ void QmlProfilerClient::messageReceived(const QByteArray &data)
if (range >= QQmlProfilerService::MaximumRangeType)
return;
+ if (!(d->features & one << featureFromRangeType(
+ static_cast<QQmlProfilerService::RangeType>(range))))
+ return;
+
if (messageType == QQmlProfilerService::RangeStart) {
d->rangeStartTimes[range].push(time);
d->inProgressRanges |= (static_cast<qint64>(1) << range);
@@ -263,7 +271,6 @@ void QmlProfilerClient::messageReceived(const QByteArray &data)
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();
QmlEventLocation location = d->rangeLocations[range].count() ?
@@ -296,19 +303,14 @@ V8ProfilerClient::~V8ProfilerClient()
{
}
-void V8ProfilerClient::sendRecordingStatus()
+void V8ProfilerClient::sendRecordingStatus(bool record)
{
QByteArray ba;
QDataStream stream(&ba, QIODevice::WriteOnly);
QByteArray cmd("V8PROFILER");
- QByteArray option("");
+ QByteArray option(record ? "start" : "stop");
QByteArray title("");
- if (m_recording) {
- option = "start";
- } else {
- option = "stop";
- }
stream << cmd << option << title;
sendMessage(ba);
}
diff --git a/tools/qmlprofiler/qmlprofilerclient.h b/tools/qmlprofiler/qmlprofilerclient.h
index 2d9c382ff6..f41ebbd4c9 100644
--- a/tools/qmlprofiler/qmlprofilerclient.h
+++ b/tools/qmlprofiler/qmlprofilerclient.h
@@ -44,25 +44,18 @@ class ProfilerClient : public QQmlDebugClient
Q_OBJECT
Q_PROPERTY(bool enabled READ isEnabled NOTIFY enabledChanged)
- Q_PROPERTY(bool recording READ isRecording WRITE setRecording
- NOTIFY recordingChanged)
-
public:
ProfilerClient(const QString &clientName,
QQmlDebugConnection *client);
~ProfilerClient();
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();
@@ -70,7 +63,6 @@ protected:
virtual void stateChanged(State);
protected:
- bool m_recording;
bool m_enabled;
};
@@ -82,9 +74,11 @@ public:
QmlProfilerClient(QQmlDebugConnection *client);
~QmlProfilerClient();
+ void setFeatures(quint64 features);
+
public slots:
void clearData();
- void sendRecordingStatus();
+ void sendRecordingStatus(bool record);
signals:
void traceFinished( qint64 time );
@@ -101,6 +95,7 @@ signals:
void pixmapCache(QQmlProfilerService::PixmapEventType, qint64 time,
const QmlEventLocation &location, int width, int height, int refCount);
void memoryAllocation(QQmlProfilerService::MemoryType type, qint64 time, qint64 amount);
+ void inputEvent(QQmlProfilerService::EventType, qint64 time);
protected:
virtual void messageReceived(const QByteArray &);
@@ -125,7 +120,7 @@ public:
~V8ProfilerClient();
public slots:
- void sendRecordingStatus();
+ void sendRecordingStatus(bool record);
signals:
void range(int depth, const QString &function, const QString &filename,
diff --git a/tools/qmlprofiler/qmlprofilerdata.cpp b/tools/qmlprofiler/qmlprofilerdata.cpp
index c5992652f8..347c51c13b 100644
--- a/tools/qmlprofiler/qmlprofilerdata.cpp
+++ b/tools/qmlprofiler/qmlprofilerdata.cpp
@@ -177,18 +177,18 @@ QmlProfilerData::~QmlProfilerData()
void QmlProfilerData::clear()
{
- qDeleteAll(d->eventDescriptions.values());
+ qDeleteAll(d->eventDescriptions);
d->eventDescriptions.clear();
d->startInstanceList.clear();
- qDeleteAll(d->v8EventHash.values());
+ qDeleteAll(d->v8EventHash);
d->v8EventHash.clear();
d->v8parents.clear();
d->clearV8RootEvent();
d->v8MeasuredTime = 0;
- d->traceEndTime = 0;
- d->traceStartTime = -1;
+ d->traceEndTime = std::numeric_limits<qint64>::min();
+ d->traceStartTime = std::numeric_limits<qint64>::max();
d->qmlMeasuredTime = 0;
setState(Empty);
@@ -226,12 +226,14 @@ QString QmlProfilerData::qmlMessageAsString(QQmlProfilerService::Message type)
void QmlProfilerData::setTraceStartTime(qint64 time)
{
- d->traceStartTime = time;
+ if (time < d->traceStartTime)
+ d->traceStartTime = time;
}
void QmlProfilerData::setTraceEndTime(qint64 time)
{
- d->traceEndTime = time;
+ if (time > d->traceEndTime)
+ d->traceEndTime = time;
}
qint64 QmlProfilerData::traceStartTime() const
@@ -388,6 +390,25 @@ void QmlProfilerData::addMemoryEvent(QQmlProfilerService::MemoryType type, qint6
d->startInstanceList.append(rangeEventStartInstance);
}
+void QmlProfilerData::addInputEvent(QQmlProfilerDefinitions::EventType type, qint64 time)
+{
+ setState(AcquiringData);
+
+ QString eventHashStr = QString::fromLatin1("Input:%1").arg(type);
+
+ QmlRangeEventData *newEvent;
+ if (d->eventDescriptions.contains(eventHashStr)) {
+ newEvent = d->eventDescriptions[eventHashStr];
+ } else {
+ newEvent = new QmlRangeEventData(QString(), type, eventHashStr, QmlEventLocation(),
+ QString(), QQmlProfilerService::Event,
+ QQmlProfilerService::MaximumRangeType);
+ d->eventDescriptions.insert(eventHashStr, newEvent);
+ }
+
+ d->startInstanceList.append(QmlRangeEventStartInstance(time, -1, 0, 0, 0, newEvent));
+}
+
QString QmlProfilerData::rootEventName()
{
return tr("<program>");
@@ -551,10 +572,18 @@ bool QmlProfilerData::save(const QString &filename)
return false;
}
- QFile file(filename);
- if (!file.open(QIODevice::WriteOnly)) {
- emit error(tr("Could not open %1 for writing").arg(filename));
- return false;
+ QFile file;
+ if (!filename.isEmpty()) {
+ file.setFileName(filename);
+ if (!file.open(QIODevice::WriteOnly)) {
+ emit error(tr("Could not open %1 for writing").arg(filename));
+ return false;
+ }
+ } else {
+ if (!file.open(stdout, QIODevice::WriteOnly)) {
+ emit error(tr("Could not open stdout for writing"));
+ return false;
+ }
}
QXmlStreamWriter stream(&file);
@@ -589,11 +618,22 @@ bool QmlProfilerData::save(const QString &filename)
if (eventData->rangeType == QQmlProfilerService::Binding)
stream.writeTextElement(QStringLiteral("bindingType"),
QString::number((int)eventData->detailType));
- else if (eventData->message == QQmlProfilerService::Event &&
- eventData->detailType == QQmlProfilerService::AnimationFrame)
- stream.writeTextElement(QStringLiteral("animationFrame"),
- QString::number((int)eventData->detailType));
- else if (eventData->message == QQmlProfilerService::PixmapCacheEvent)
+ else if (eventData->message == QQmlProfilerService::Event) {
+ switch (eventData->detailType) {
+ case QQmlProfilerService::AnimationFrame:
+ stream.writeTextElement(QStringLiteral("animationFrame"),
+ QString::number((int)eventData->detailType));
+ break;
+ case QQmlProfilerService::Key:
+ stream.writeTextElement(QStringLiteral("keyEvent"),
+ QString::number((int)eventData->detailType));
+ break;
+ case QQmlProfilerService::Mouse:
+ stream.writeTextElement(QStringLiteral("mouseEvent"),
+ QString::number((int)eventData->detailType));
+ break;
+ }
+ } else if (eventData->message == QQmlProfilerService::PixmapCacheEvent)
stream.writeTextElement(QStringLiteral("cacheEventType"),
QString::number((int)eventData->detailType));
else if (eventData->message == QQmlProfilerService::SceneGraphFrame)
diff --git a/tools/qmlprofiler/qmlprofilerdata.h b/tools/qmlprofiler/qmlprofilerdata.h
index d420275d5e..ef8ef10929 100644
--- a/tools/qmlprofiler/qmlprofilerdata.h
+++ b/tools/qmlprofiler/qmlprofilerdata.h
@@ -88,6 +88,7 @@ public slots:
void addPixmapCacheEvent(QQmlProfilerService::PixmapEventType type, qint64 time,
const QmlEventLocation &location, int width, int height, int refcount);
void addMemoryEvent(QQmlProfilerService::MemoryType type, qint64 time, qint64 size);
+ void addInputEvent(QQmlProfilerService::EventType type, qint64 time);
void complete();
bool save(const QString &filename);