diff options
Diffstat (limited to 'tools')
-rw-r--r-- | tools/qml/main.cpp | 37 | ||||
-rw-r--r-- | tools/qmleasing/splineeditor.cpp | 5 | ||||
-rw-r--r-- | tools/qmlimportscanner/main.cpp | 2 | ||||
-rw-r--r-- | tools/qmljs/qmljs.cpp | 14 | ||||
-rw-r--r-- | tools/qmllint/main.cpp | 2 | ||||
-rw-r--r-- | tools/qmlplugindump/main.cpp | 4 | ||||
-rw-r--r-- | tools/qmlprofiler/commandlistener.cpp | 21 | ||||
-rw-r--r-- | tools/qmlprofiler/commandlistener.h | 13 | ||||
-rw-r--r-- | tools/qmlprofiler/constants.h | 19 | ||||
-rw-r--r-- | tools/qmlprofiler/main.cpp | 32 | ||||
-rw-r--r-- | tools/qmlprofiler/qmlprofilerapplication.cpp | 528 | ||||
-rw-r--r-- | tools/qmlprofiler/qmlprofilerapplication.h | 35 | ||||
-rw-r--r-- | tools/qmlprofiler/qmlprofilerclient.cpp | 102 | ||||
-rw-r--r-- | tools/qmlprofiler/qmlprofilerclient.h | 15 | ||||
-rw-r--r-- | tools/qmlprofiler/qmlprofilerdata.cpp | 70 | ||||
-rw-r--r-- | tools/qmlprofiler/qmlprofilerdata.h | 1 |
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); |