diff options
Diffstat (limited to 'tools/qmlprofiler')
-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 | 549 | ||||
-rw-r--r-- | tools/qmlprofiler/qmlprofilerapplication.h | 35 | ||||
-rw-r--r-- | tools/qmlprofiler/qmlprofilerclient.cpp | 171 | ||||
-rw-r--r-- | tools/qmlprofiler/qmlprofilerclient.h | 27 | ||||
-rw-r--r-- | tools/qmlprofiler/qmlprofilerdata.cpp | 166 | ||||
-rw-r--r-- | tools/qmlprofiler/qmlprofilerdata.h | 17 |
10 files changed, 659 insertions, 391 deletions
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..347e7d3b55 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,24 +97,26 @@ 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(range(QQmlProfilerDefinitions::RangeType,QQmlProfilerDefinitions::BindingType,qint64,qint64,QStringList,QmlEventLocation)), + &m_profilerData, SLOT(addQmlEvent(QQmlProfilerDefinitions::RangeType,QQmlProfilerDefinitions::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, + connect(&m_qmlProfilerClient, SIGNAL(sceneGraphFrame(QQmlProfilerDefinitions::SceneGraphFrameType, qint64,qint64,qint64,qint64,qint64,qint64)), - &m_profilerData, SLOT(addSceneGraphFrameEvent(QQmlProfilerService::SceneGraphFrameType, + &m_profilerData, SLOT(addSceneGraphFrameEvent(QQmlProfilerDefinitions::SceneGraphFrameType, qint64,qint64,qint64,qint64,qint64,qint64))); - connect(&m_qmlProfilerClient, SIGNAL(pixmapCache(QQmlProfilerService::PixmapEventType,qint64, + connect(&m_qmlProfilerClient, SIGNAL(pixmapCache(QQmlProfilerDefinitions::PixmapEventType,qint64, QmlEventLocation,int,int,int)), - &m_profilerData, SLOT(addPixmapCacheEvent(QQmlProfilerService::PixmapEventType,qint64, + &m_profilerData, SLOT(addPixmapCacheEvent(QQmlProfilerDefinitions::PixmapEventType,qint64, QmlEventLocation,int,int,int))); - connect(&m_qmlProfilerClient, SIGNAL(memoryAllocation(QQmlProfilerService::MemoryType,qint64, + connect(&m_qmlProfilerClient, SIGNAL(memoryAllocation(QQmlProfilerDefinitions::MemoryType,qint64, qint64)), - &m_profilerData, SLOT(addMemoryEvent(QQmlProfilerService::MemoryType,qint64, + &m_profilerData, SLOT(addMemoryEvent(QQmlProfilerDefinitions::MemoryType,qint64, qint64))); + connect(&m_qmlProfilerClient, SIGNAL(inputEvent(QQmlProfilerDefinitions::EventType,qint64)), + &m_profilerData, SLOT(addInputEvent(QQmlProfilerDefinitions::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 < QQmlProfilerDefinitions::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(); } } @@ -262,7 +476,8 @@ void QmlProfilerApplication::run() if (m_runMode == LaunchMode) { m_process = new QProcess(this); QStringList arguments; - arguments << QString::fromLatin1("-qmljsdebugger=port:%1,block").arg(m_port); + arguments << QString::fromLatin1("-qmljsdebugger=port:%1,block,services:CanvasFrameRate") + .arg(m_port); arguments << m_programArguments; m_process->setProcessChannelMode(QProcess::MergedChannels); @@ -270,7 +485,7 @@ void QmlProfilerApplication::run() connect(m_process, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(processFinished())); logStatus(QString("Starting '%1 %2' ...").arg(m_programPath, - arguments.join(" "))); + arguments.join(QLatin1Char(' ')))); m_process->start(m_programPath, arguments); if (!m_process->waitForStarted()) { logError(QString("Could not run '%1': %2").arg(m_programPath, @@ -304,17 +519,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 +541,7 @@ void QmlProfilerApplication::processHasOutput() { Q_ASSERT(m_process); while (m_process->bytesAvailable()) { - QTextStream out(stdout); + QTextStream out(stderr); out << m_process->readAll(); } } @@ -342,19 +549,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 +569,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 +579,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 +633,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 +642,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 f6ed4f5723..b4768e6934 100644 --- a/tools/qmlprofiler/qmlprofilerclient.cpp +++ b/tools/qmlprofiler/qmlprofilerclient.cpp @@ -35,23 +35,19 @@ #include <QtCore/QStack> #include <QtCore/QStringList> +#include <QtCore/QDataStream> #include <limits> 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() @@ -64,29 +60,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) || @@ -101,20 +74,19 @@ class QmlProfilerClientPrivate { public: QmlProfilerClientPrivate() - : inProgressRanges(0) - , maximumTime(0) + : inProgressRanges(0) , features(std::numeric_limits<quint64>::max()) { - ::memset(rangeCount, 0, - QQmlProfilerService::MaximumRangeType * sizeof(int)); + ::memset(rangeCount, 0, QQmlProfilerDefinitions::MaximumRangeType * sizeof(int)); } qint64 inProgressRanges; - QStack<qint64> rangeStartTimes[QQmlProfilerService::MaximumRangeType]; - QStack<QStringList> rangeDatas[QQmlProfilerService::MaximumRangeType]; - QStack<QmlEventLocation> rangeLocations[QQmlProfilerService::MaximumRangeType]; - QStack<QQmlProfilerService::BindingType> bindingTypes; - int rangeCount[QQmlProfilerService::MaximumRangeType]; - qint64 maximumTime; + QStack<qint64> rangeStartTimes[QQmlProfilerDefinitions::MaximumRangeType]; + QStack<QStringList> rangeDatas[QQmlProfilerDefinitions::MaximumRangeType]; + QStack<QmlEventLocation> rangeLocations[QQmlProfilerDefinitions::MaximumRangeType]; + QStack<QQmlProfilerDefinitions::BindingType> bindingTypes; + int rangeCount[QQmlProfilerDefinitions::MaximumRangeType]; + + quint64 features; }; QmlProfilerClient::QmlProfilerClient( @@ -129,62 +101,93 @@ QmlProfilerClient::~QmlProfilerClient() delete d; } +void QmlProfilerClient::setFeatures(quint64 features) +{ + d->features = features; +} + void QmlProfilerClient::clearData() { - ::memset(d->rangeCount, 0, - QQmlProfilerService::MaximumRangeType * sizeof(int)); + ::memset(d->rangeCount, 0, QQmlProfilerDefinitions::MaximumRangeType * sizeof(int)); d->bindingTypes.clear(); ProfilerClient::clearData(); } -void QmlProfilerClient::sendRecordingStatus() +void QmlProfilerClient::sendRecordingStatus(bool record) { QByteArray ba; QDataStream stream(&ba, QIODevice::WriteOnly); stream.setVersion(QDataStream::Qt_4_7); - stream << isRecording(); + stream << record << -1 << d->features; sendMessage(ba); } +inline QQmlProfilerDefinitions::ProfileFeature featureFromRangeType( + QQmlProfilerDefinitions::RangeType range) +{ + switch (range) { + case QQmlProfilerDefinitions::Painting: + return QQmlProfilerDefinitions::ProfilePainting; + case QQmlProfilerDefinitions::Compiling: + return QQmlProfilerDefinitions::ProfileCompiling; + case QQmlProfilerDefinitions::Creating: + return QQmlProfilerDefinitions::ProfileCreating; + case QQmlProfilerDefinitions::Binding: + return QQmlProfilerDefinitions::ProfileBinding; + case QQmlProfilerDefinitions::HandlingSignal: + return QQmlProfilerDefinitions::ProfileHandlingSignal; + case QQmlProfilerDefinitions::Javascript: + return QQmlProfilerDefinitions::ProfileJavaScript; + default: + return QQmlProfilerDefinitions::MaximumProfileFeature; + } +} + void QmlProfilerClient::messageReceived(const QByteArray &data) { QByteArray rwData = data; QDataStream stream(&rwData, QIODevice::ReadOnly); stream.setVersion(QDataStream::Qt_4_7); + // 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; stream >> time >> messageType; - if (messageType >= QQmlProfilerService::MaximumMessage) + if (messageType >= QQmlProfilerDefinitions::MaximumMessage) return; - if (messageType == QQmlProfilerService::Event) { + if (messageType == QQmlProfilerDefinitions::Event) { int event; stream >> event; - if (event == QQmlProfilerService::EndTrace) { + if (event == QQmlProfilerDefinitions::EndTrace) { emit this->traceFinished(time); - d->maximumTime = time; - d->maximumTime = qMax(time, d->maximumTime); - } else if (event == QQmlProfilerService::AnimationFrame) { + } else if (event == QQmlProfilerDefinitions::AnimationFrame) { + if (!(d->features & one << QQmlProfilerDefinitions::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) { + } else if (event == QQmlProfilerDefinitions::StartTrace) { emit this->traceStarted(time); - d->maximumTime = time; - } else if (event < QQmlProfilerService::MaximumEventType) { - d->maximumTime = qMax(time, d->maximumTime); + } else if (event == QQmlProfilerDefinitions::Key || + event == QQmlProfilerDefinitions::Mouse) { + if (!(d->features & one << QQmlProfilerDefinitions::ProfileInputEvents)) + return; + emit this->inputEvent((QQmlProfilerDefinitions::EventType)event, time); } - } else if (messageType == QQmlProfilerService::Complete) { + } else if (messageType == QQmlProfilerDefinitions::Complete) { emit complete(); - } else if (messageType == QQmlProfilerService::SceneGraphFrame) { + } else if (messageType == QQmlProfilerDefinitions::SceneGraphFrame) { + if (!(d->features & one << QQmlProfilerDefinitions::ProfileSceneGraph)) + return; int sgEventType; int count = 0; qint64 params[5]; @@ -195,49 +198,54 @@ void QmlProfilerClient::messageReceived(const QByteArray &data) } while (count<5) params[count++] = 0; - emit sceneGraphFrame((QQmlProfilerService::SceneGraphFrameType)sgEventType, time, + emit sceneGraphFrame((QQmlProfilerDefinitions::SceneGraphFrameType)sgEventType, time, params[0], params[1], params[2], params[3], params[4]); - d->maximumTime = qMax(time, d->maximumTime); - } else if (messageType == QQmlProfilerService::PixmapCacheEvent) { + } else if (messageType == QQmlProfilerDefinitions::PixmapCacheEvent) { + if (!(d->features & one << QQmlProfilerDefinitions::ProfilePixmapCache)) + return; int pixEvTy, width = 0, height = 0, refcount = 0; QString pixUrl; stream >> pixEvTy >> pixUrl; - if (pixEvTy == (int)QQmlProfilerService::PixmapReferenceCountChanged || - pixEvTy == (int)QQmlProfilerService::PixmapCacheCountChanged) { + if (pixEvTy == (int)QQmlProfilerDefinitions::PixmapReferenceCountChanged || + pixEvTy == (int)QQmlProfilerDefinitions::PixmapCacheCountChanged) { stream >> refcount; - } else if (pixEvTy == (int)QQmlProfilerService::PixmapSizeKnown) { + } else if (pixEvTy == (int)QQmlProfilerDefinitions::PixmapSizeKnown) { stream >> width >> height; refcount = 1; } - emit pixmapCache((QQmlProfilerService::PixmapEventType)pixEvTy, time, + emit pixmapCache((QQmlProfilerDefinitions::PixmapEventType)pixEvTy, time, QmlEventLocation(pixUrl,0,0), width, height, refcount); - d->maximumTime = qMax(time, d->maximumTime); - } else if (messageType == QQmlProfilerService::MemoryAllocation) { + } else if (messageType == QQmlProfilerDefinitions::MemoryAllocation) { + if (!(d->features & one << QQmlProfilerDefinitions::ProfileMemory)) + return; int type; qint64 delta; stream >> type >> delta; - emit memoryAllocation((QQmlProfilerService::MemoryType)type, time, delta); - d->maximumTime = qMax(time, d->maximumTime); + emit memoryAllocation((QQmlProfilerDefinitions::MemoryType)type, time, delta); } else { int range; stream >> range; - if (range >= QQmlProfilerService::MaximumRangeType) + if (range >= QQmlProfilerDefinitions::MaximumRangeType) return; - if (messageType == QQmlProfilerService::RangeStart) { + if (!(d->features & one << featureFromRangeType( + static_cast<QQmlProfilerDefinitions::RangeType>(range)))) + return; + + if (messageType == QQmlProfilerDefinitions::RangeStart) { d->rangeStartTimes[range].push(time); d->inProgressRanges |= (static_cast<qint64>(1) << range); ++d->rangeCount[range]; // read binding type - if (range == (int)QQmlProfilerService::Binding) { - int bindingType = (int)QQmlProfilerService::QmlBinding; + if (range == (int)QQmlProfilerDefinitions::Binding) { + int bindingType = (int)QQmlProfilerDefinitions::QmlBinding; if (!stream.atEnd()) stream >> bindingType; - d->bindingTypes.push((QQmlProfilerService::BindingType)bindingType); + d->bindingTypes.push((QQmlProfilerDefinitions::BindingType)bindingType); } - } else if (messageType == QQmlProfilerService::RangeData) { + } else if (messageType == QQmlProfilerDefinitions::RangeData) { QString data; stream >> data; @@ -248,7 +256,7 @@ void QmlProfilerClient::messageReceived(const QByteArray &data) d->rangeDatas[range][count-1] << data; } - } else if (messageType == QQmlProfilerService::RangeLocation) { + } else if (messageType == QQmlProfilerDefinitions::RangeLocation) { QString fileName; int line; int column = -1; @@ -267,17 +275,17 @@ 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() ? d->rangeLocations[range].pop() : QmlEventLocation(); qint64 startTime = d->rangeStartTimes[range].pop(); - QQmlProfilerService::BindingType bindingType = QQmlProfilerService::QmlBinding; - if (range == (int)QQmlProfilerService::Binding) + QQmlProfilerDefinitions::BindingType bindingType = + QQmlProfilerDefinitions::QmlBinding; + if (range == (int)QQmlProfilerDefinitions::Binding) bindingType = d->bindingTypes.pop(); - emit this->range((QQmlProfilerService::RangeType)range, + emit this->range((QQmlProfilerDefinitions::RangeType)range, bindingType, startTime, time - startTime, data, location); if (d->rangeCount[range] == 0) { int count = d->rangeDatas[range].count() + @@ -300,19 +308,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..731ab99973 100644 --- a/tools/qmlprofiler/qmlprofilerclient.h +++ b/tools/qmlprofiler/qmlprofilerclient.h @@ -35,8 +35,8 @@ #define QMLPROFILERCLIENT_H #include "qqmldebugclient.h" -#include <QtQml/private/qqmlprofilerservice_p.h> #include "qmlprofilereventlocation.h" +#include <QtQml/private/qqmlprofilerdefinitions_p.h> class ProfilerClientPrivate; class ProfilerClient : public QQmlDebugClient @@ -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,25 +74,28 @@ public: QmlProfilerClient(QQmlDebugConnection *client); ~QmlProfilerClient(); + void setFeatures(quint64 features); + public slots: void clearData(); - void sendRecordingStatus(); + void sendRecordingStatus(bool record); signals: void traceFinished( qint64 time ); void traceStarted( qint64 time ); - void range(QQmlProfilerService::RangeType type, - QQmlProfilerService::BindingType bindingType, + void range(QQmlProfilerDefinitions::RangeType type, + QQmlProfilerDefinitions::BindingType bindingType, qint64 startTime, qint64 length, const QStringList &data, const QmlEventLocation &location); void frame(qint64 time, int frameRate, int animationCount, int threadId); - void sceneGraphFrame(QQmlProfilerService::SceneGraphFrameType type, qint64 time, + void sceneGraphFrame(QQmlProfilerDefinitions::SceneGraphFrameType type, qint64 time, qint64 numericData1, qint64 numericData2, qint64 numericData3, qint64 numericData4, qint64 numericData5); - void pixmapCache(QQmlProfilerService::PixmapEventType, qint64 time, + void pixmapCache(QQmlProfilerDefinitions::PixmapEventType, qint64 time, const QmlEventLocation &location, int width, int height, int refCount); - void memoryAllocation(QQmlProfilerService::MemoryType type, qint64 time, qint64 amount); + void memoryAllocation(QQmlProfilerDefinitions::MemoryType type, qint64 time, qint64 amount); + void inputEvent(QQmlProfilerDefinitions::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 76d06cf564..307b9d3686 100644 --- a/tools/qmlprofiler/qmlprofilerdata.cpp +++ b/tools/qmlprofiler/qmlprofilerdata.cpp @@ -74,16 +74,16 @@ struct QmlRangeEventData { QmlRangeEventData() {} // never called QmlRangeEventData(const QString &_displayName, int _detailType, const QString &_eventHashStr, const QmlEventLocation &_location, const QString &_details, - QQmlProfilerService::Message _message, - QQmlProfilerService::RangeType _rangeType) + QQmlProfilerDefinitions::Message _message, + QQmlProfilerDefinitions::RangeType _rangeType) : displayName(_displayName), eventHashStr(_eventHashStr), location(_location), details(_details), message(_message), rangeType(_rangeType), detailType(_detailType) {} QString displayName; QString eventHashStr; QmlEventLocation location; QString details; - QQmlProfilerService::Message message; - QQmlProfilerService::RangeType rangeType; + QQmlProfilerDefinitions::Message message; + QQmlProfilerDefinitions::RangeType rangeType; int detailType; // can be BindingType, PixmapCacheEventType or SceneGraphFrameType }; @@ -179,18 +179,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); @@ -210,7 +210,7 @@ QString QmlProfilerData::getHashStringForV8Event(const QString &displayName, con return QString(QStringLiteral("%1:%2")).arg(displayName, function); } -QString QmlProfilerData::qmlRangeTypeAsString(QQmlProfilerService::RangeType type) +QString QmlProfilerData::qmlRangeTypeAsString(QQmlProfilerDefinitions::RangeType type) { if (type * sizeof(QString) < sizeof(RANGE_TYPE_STRINGS)) return QLatin1String(RANGE_TYPE_STRINGS[type]); @@ -218,7 +218,7 @@ QString QmlProfilerData::qmlRangeTypeAsString(QQmlProfilerService::RangeType typ return QString::number(type); } -QString QmlProfilerData::qmlMessageAsString(QQmlProfilerService::Message type) +QString QmlProfilerData::qmlMessageAsString(QQmlProfilerDefinitions::Message type) { if (type * sizeof(QString) < sizeof(MESSAGE_STRINGS)) return QLatin1String(MESSAGE_STRINGS[type]); @@ -228,12 +228,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 @@ -246,8 +248,8 @@ qint64 QmlProfilerData::traceEndTime() const return d->traceEndTime; } -void QmlProfilerData::addQmlEvent(QQmlProfilerService::RangeType type, - QQmlProfilerService::BindingType bindingType, +void QmlProfilerData::addQmlEvent(QQmlProfilerDefinitions::RangeType type, + QQmlProfilerDefinitions::BindingType bindingType, qint64 startTime, qint64 duration, const QStringList &data, @@ -260,8 +262,8 @@ void QmlProfilerData::addQmlEvent(QQmlProfilerService::RangeType type, if (data.isEmpty()) details = tr("Source code not available"); else { - details = data.join(QStringLiteral(" ")).replace( - QLatin1Char('\n'),QStringLiteral(" ")).simplified(); + details = data.join(QLatin1Char(' ')).replace( + QLatin1Char('\n'), QLatin1Char(' ')).simplified(); QRegExp rewrite(QStringLiteral("\\(function \\$(\\w+)\\(\\) \\{ (return |)(.+) \\}\\)")); bool match = rewrite.exactMatch(details); if (match) { @@ -290,7 +292,7 @@ void QmlProfilerData::addQmlEvent(QQmlProfilerService::RangeType type, newEvent = d->eventDescriptions[eventHashStr]; } else { newEvent = new QmlRangeEventData(displayName, bindingType, eventHashStr, location, details, - QQmlProfilerService::MaximumMessage, type); + QQmlProfilerDefinitions::MaximumMessage, type); d->eventDescriptions.insert(eventHashStr, newEvent); } @@ -311,11 +313,11 @@ void QmlProfilerData::addFrameEvent(qint64 time, int framerate, int animationcou if (d->eventDescriptions.contains(eventHashStr)) { newEvent = d->eventDescriptions[eventHashStr]; } else { - newEvent = new QmlRangeEventData(displayName, QQmlProfilerService::AnimationFrame, + newEvent = new QmlRangeEventData(displayName, QQmlProfilerDefinitions::AnimationFrame, eventHashStr, QmlEventLocation(), details, - QQmlProfilerService::Event, - QQmlProfilerService::MaximumRangeType); + QQmlProfilerDefinitions::Event, + QQmlProfilerDefinitions::MaximumRangeType); d->eventDescriptions.insert(eventHashStr, newEvent); } @@ -325,7 +327,10 @@ void QmlProfilerData::addFrameEvent(qint64 time, int framerate, int animationcou d->startInstanceList.append(rangeEventStartInstance); } -void QmlProfilerData::addSceneGraphFrameEvent(QQmlProfilerDefinitions::SceneGraphFrameType type, qint64 time, qint64 numericData1, qint64 numericData2, qint64 numericData3, qint64 numericData4, qint64 numericData5) +void QmlProfilerData::addSceneGraphFrameEvent(QQmlProfilerDefinitions::SceneGraphFrameType type, + qint64 time, qint64 numericData1, qint64 numericData2, + qint64 numericData3, qint64 numericData4, + qint64 numericData5) { setState(AcquiringData); @@ -336,8 +341,8 @@ void QmlProfilerData::addSceneGraphFrameEvent(QQmlProfilerDefinitions::SceneGrap } else { newEvent = new QmlRangeEventData(QStringLiteral("<SceneGraph>"), type, eventHashStr, QmlEventLocation(), QString(), - QQmlProfilerService::SceneGraphFrame, - QQmlProfilerService::MaximumRangeType); + QQmlProfilerDefinitions::SceneGraphFrame, + QQmlProfilerDefinitions::MaximumRangeType); d->eventDescriptions.insert(eventHashStr, newEvent); } @@ -362,8 +367,8 @@ void QmlProfilerData::addPixmapCacheEvent(QQmlProfilerDefinitions::PixmapEventTy newEvent = d->eventDescriptions[eventHashStr]; } else { newEvent = new QmlRangeEventData(eventHashStr, type, eventHashStr, location, QString(), - QQmlProfilerService::PixmapCacheEvent, - QQmlProfilerService::MaximumRangeType); + QQmlProfilerDefinitions::PixmapCacheEvent, + QQmlProfilerDefinitions::MaximumRangeType); d->eventDescriptions.insert(eventHashStr, newEvent); } @@ -372,7 +377,7 @@ void QmlProfilerData::addPixmapCacheEvent(QQmlProfilerDefinitions::PixmapEventTy d->startInstanceList.append(rangeEventStartInstance); } -void QmlProfilerData::addMemoryEvent(QQmlProfilerService::MemoryType type, qint64 time, +void QmlProfilerData::addMemoryEvent(QQmlProfilerDefinitions::MemoryType type, qint64 time, qint64 size) { setState(AcquiringData); @@ -382,14 +387,33 @@ void QmlProfilerData::addMemoryEvent(QQmlProfilerService::MemoryType type, qint6 newEvent = d->eventDescriptions[eventHashStr]; } else { newEvent = new QmlRangeEventData(eventHashStr, type, eventHashStr, QmlEventLocation(), - QString(), QQmlProfilerService::MemoryAllocation, - QQmlProfilerService::MaximumRangeType); + QString(), QQmlProfilerDefinitions::MemoryAllocation, + QQmlProfilerDefinitions::MaximumRangeType); d->eventDescriptions.insert(eventHashStr, newEvent); } QmlRangeEventStartInstance rangeEventStartInstance(time, size, 0, 0, 0, 0, newEvent); 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(), QQmlProfilerDefinitions::Event, + QQmlProfilerDefinitions::MaximumRangeType); + d->eventDescriptions.insert(eventHashStr, newEvent); + } + + d->startInstanceList.append(QmlRangeEventStartInstance(time, -1, 0, 0, 0, newEvent)); +} + QString QmlProfilerData::rootEventName() { return tr("<program>"); @@ -470,7 +494,7 @@ void QmlProfilerData::computeQmlTime() for (int i = 0; i < d->startInstanceList.count(); i++) { qint64 st = d->startInstanceList[i].startTime; - if (d->startInstanceList[i].data->rangeType == QQmlProfilerService::Painting) { + if (d->startInstanceList[i].data->rangeType == QQmlProfilerDefinitions::Painting) { continue; } @@ -553,10 +577,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); @@ -574,9 +606,10 @@ bool QmlProfilerData::save(const QString &filename) foreach (const QmlRangeEventData *eventData, d->eventDescriptions.values()) { stream.writeStartElement(QStringLiteral("event")); - stream.writeAttribute(QStringLiteral("index"), QString::number(d->eventDescriptions.keys().indexOf(eventData->eventHashStr))); + stream.writeAttribute(QStringLiteral("index"), QString::number( + d->eventDescriptions.keys().indexOf(eventData->eventHashStr))); stream.writeTextElement(QStringLiteral("displayname"), eventData->displayName); - if (eventData->rangeType != QQmlProfilerService::MaximumRangeType) + if (eventData->rangeType != QQmlProfilerDefinitions::MaximumRangeType) stream.writeTextElement(QStringLiteral("type"), qmlRangeTypeAsString(eventData->rangeType)); else @@ -584,24 +617,37 @@ bool QmlProfilerData::save(const QString &filename) qmlMessageAsString(eventData->message)); if (!eventData->location.filename.isEmpty()) { stream.writeTextElement(QStringLiteral("filename"), eventData->location.filename); - stream.writeTextElement(QStringLiteral("line"), QString::number(eventData->location.line)); - stream.writeTextElement(QStringLiteral("column"), QString::number(eventData->location.column)); + stream.writeTextElement(QStringLiteral("line"), + QString::number(eventData->location.line)); + stream.writeTextElement(QStringLiteral("column"), + QString::number(eventData->location.column)); } stream.writeTextElement(QStringLiteral("details"), eventData->details); - if (eventData->rangeType == QQmlProfilerService::Binding) + if (eventData->rangeType == QQmlProfilerDefinitions::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 == QQmlProfilerDefinitions::Event) { + switch (eventData->detailType) { + case QQmlProfilerDefinitions::AnimationFrame: + stream.writeTextElement(QStringLiteral("animationFrame"), + QString::number((int)eventData->detailType)); + break; + case QQmlProfilerDefinitions::Key: + stream.writeTextElement(QStringLiteral("keyEvent"), + QString::number((int)eventData->detailType)); + break; + case QQmlProfilerDefinitions::Mouse: + stream.writeTextElement(QStringLiteral("mouseEvent"), + QString::number((int)eventData->detailType)); + break; + } + } else if (eventData->message == QQmlProfilerDefinitions::PixmapCacheEvent) stream.writeTextElement(QStringLiteral("cacheEventType"), QString::number((int)eventData->detailType)); - else if (eventData->message == QQmlProfilerService::SceneGraphFrame) + else if (eventData->message == QQmlProfilerDefinitions::SceneGraphFrame) stream.writeTextElement(QStringLiteral("sgEventType"), QString::number((int)eventData->detailType)); - else if (eventData->message == QQmlProfilerService::MemoryAllocation) + else if (eventData->message == QQmlProfilerDefinitions::MemoryAllocation) stream.writeTextElement(QStringLiteral("memoryEventType"), QString::number((int)eventData->detailType)); stream.writeEndElement(); @@ -615,26 +661,30 @@ bool QmlProfilerData::save(const QString &filename) if (event.duration >= 0) stream.writeAttribute(QStringLiteral("duration"), QString::number(event.duration)); - stream.writeAttribute(QStringLiteral("eventIndex"), QString::number(d->eventDescriptions.keys().indexOf(event.data->eventHashStr))); - if (event.data->message == QQmlProfilerService::Event && - event.data->detailType == QQmlProfilerService::AnimationFrame) { + stream.writeAttribute(QStringLiteral("eventIndex"), QString::number( + d->eventDescriptions.keys().indexOf(event.data->eventHashStr))); + if (event.data->message == QQmlProfilerDefinitions::Event && + event.data->detailType == QQmlProfilerDefinitions::AnimationFrame) { // special: animation frame stream.writeAttribute(QStringLiteral("framerate"), QString::number(event.frameRate)); - stream.writeAttribute(QStringLiteral("animationcount"), QString::number(event.animationCount)); + stream.writeAttribute(QStringLiteral("animationcount"), + QString::number(event.animationCount)); stream.writeAttribute(QStringLiteral("thread"), QString::number(event.threadId)); - } else if (event.data->message == QQmlProfilerService::PixmapCacheEvent) { + } else if (event.data->message == QQmlProfilerDefinitions::PixmapCacheEvent) { // special: pixmap cache event - if (event.data->detailType == QQmlProfilerService::PixmapSizeKnown) { + if (event.data->detailType == QQmlProfilerDefinitions::PixmapSizeKnown) { stream.writeAttribute(QStringLiteral("width"), QString::number(event.numericData1)); stream.writeAttribute(QStringLiteral("height"), QString::number(event.numericData2)); - } else if (event.data->detailType == QQmlProfilerService::PixmapReferenceCountChanged || - event.data->detailType == QQmlProfilerService::PixmapCacheCountChanged) { + } else if (event.data->detailType == + QQmlProfilerDefinitions::PixmapReferenceCountChanged || + event.data->detailType == + QQmlProfilerDefinitions::PixmapCacheCountChanged) { stream.writeAttribute(QStringLiteral("refCount"), QString::number(event.numericData3)); } - } else if (event.data->message == QQmlProfilerService::SceneGraphFrame) { + } else if (event.data->message == QQmlProfilerDefinitions::SceneGraphFrame) { // special: scenegraph frame events if (event.numericData1 > 0) stream.writeAttribute(QStringLiteral("timing1"), @@ -651,7 +701,7 @@ bool QmlProfilerData::save(const QString &filename) if (event.numericData5 > 0) stream.writeAttribute(QStringLiteral("timing5"), QString::number(event.numericData5)); - } else if (event.data->message == QQmlProfilerService::MemoryAllocation) { + } else if (event.data->message == QQmlProfilerDefinitions::MemoryAllocation) { stream.writeAttribute(QStringLiteral("amount"), QString::number(event.numericData1)); } stream.writeEndElement(); @@ -662,7 +712,8 @@ bool QmlProfilerData::save(const QString &filename) stream.writeAttribute(QStringLiteral("totalTime"), QString::number(d->v8MeasuredTime)); foreach (QV8EventInfo *v8event, d->v8EventHash.values()) { stream.writeStartElement(QStringLiteral("event")); - stream.writeAttribute(QStringLiteral("index"), QString::number(d->v8EventHash.keys().indexOf(v8event->eventHashStr))); + stream.writeAttribute(QStringLiteral("index"),QString::number( + d->v8EventHash.keys().indexOf(v8event->eventHashStr))); stream.writeTextElement(QStringLiteral("displayname"), v8event->displayName); stream.writeTextElement(QStringLiteral("functionname"), v8event->functionName); if (!v8event->fileName.isEmpty()) { @@ -681,7 +732,8 @@ bool QmlProfilerData::save(const QString &filename) } stream.writeAttribute(QStringLiteral("list"), childrenIndexes.join(QString(", "))); - stream.writeAttribute(QStringLiteral("childrenTimes"), childrenTimes.join(QString(", "))); + stream.writeAttribute(QStringLiteral("childrenTimes"), + childrenTimes.join(QString(", "))); stream.writeEndElement(); } stream.writeEndElement(); diff --git a/tools/qmlprofiler/qmlprofilerdata.h b/tools/qmlprofiler/qmlprofilerdata.h index d420275d5e..91c16c3222 100644 --- a/tools/qmlprofiler/qmlprofilerdata.h +++ b/tools/qmlprofiler/qmlprofilerdata.h @@ -34,9 +34,9 @@ #ifndef QMLPROFILERDATA_H #define QMLPROFILERDATA_H -#include <QtQml/private/qqmlprofilerservice_p.h> #include "qmlprofilereventlocation.h" +#include <QtQml/private/qqmlprofilerdefinitions_p.h> #include <QObject> class QmlProfilerDataPrivate; @@ -56,8 +56,8 @@ public: static QString getHashStringForQmlEvent(const QmlEventLocation &location, int eventType); static QString getHashStringForV8Event(const QString &displayName, const QString &function); - static QString qmlRangeTypeAsString(QQmlProfilerService::RangeType type); - static QString qmlMessageAsString(QQmlProfilerService::Message type); + static QString qmlRangeTypeAsString(QQmlProfilerDefinitions::RangeType type); + static QString qmlMessageAsString(QQmlProfilerDefinitions::Message type); static QString rootEventName(); static QString rootEventDescription(); @@ -75,19 +75,20 @@ public slots: void clear(); void setTraceEndTime(qint64 time); void setTraceStartTime(qint64 time); - void addQmlEvent(QQmlProfilerService::RangeType type, - QQmlProfilerService::BindingType bindingType, + void addQmlEvent(QQmlProfilerDefinitions::RangeType type, + QQmlProfilerDefinitions::BindingType bindingType, qint64 startTime, qint64 duration, const QStringList &data, const QmlEventLocation &location); void addV8Event(int depth, const QString &function, const QString &filename, int lineNumber, double totalTime, double selfTime); void addFrameEvent(qint64 time, int framerate, int animationcount, int threadId); - void addSceneGraphFrameEvent(QQmlProfilerService::SceneGraphFrameType type, qint64 time, + void addSceneGraphFrameEvent(QQmlProfilerDefinitions::SceneGraphFrameType type, qint64 time, qint64 numericData1, qint64 numericData2, qint64 numericData3, qint64 numericData4, qint64 numericData5); - void addPixmapCacheEvent(QQmlProfilerService::PixmapEventType type, qint64 time, + void addPixmapCacheEvent(QQmlProfilerDefinitions::PixmapEventType type, qint64 time, const QmlEventLocation &location, int width, int height, int refcount); - void addMemoryEvent(QQmlProfilerService::MemoryType type, qint64 time, qint64 size); + void addMemoryEvent(QQmlProfilerDefinitions::MemoryType type, qint64 time, qint64 size); + void addInputEvent(QQmlProfilerDefinitions::EventType type, qint64 time); void complete(); bool save(const QString &filename); |