aboutsummaryrefslogtreecommitdiffstats
path: root/tools/qmlprofiler
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@theqtcompany.com>2015-05-21 17:13:03 +0200
committerUlf Hermann <ulf.hermann@theqtcompany.com>2015-06-06 21:13:25 +0000
commitcd0b001ec8153515b4af549e4b111bada14ebce7 (patch)
treec0a38bc233eff945670dca2d43d1575458493d3a /tools/qmlprofiler
parentc402f593595f2705f8d0f73a13a08cb8bbda9421 (diff)
qmlprofiler: Improve options for trace output
* Remove the auto-generation of file names. * Accept file names as parameters or from interactive commands * Output to stdout by default for better scripting and quick preview * Decouple output from clearing of data so that you can write the same data multiple times. Task-number: QTBUG-43066 Change-Id: Ia4cc3701cbac7c6f8948b11307130a5d6a2ff44c Reviewed-by: Joerg Bornemann <joerg.bornemann@theqtcompany.com>
Diffstat (limited to 'tools/qmlprofiler')
-rw-r--r--tools/qmlprofiler/constants.h15
-rw-r--r--tools/qmlprofiler/qmlprofilerapplication.cpp209
-rw-r--r--tools/qmlprofiler/qmlprofilerapplication.h21
-rw-r--r--tools/qmlprofiler/qmlprofilerdata.cpp16
4 files changed, 211 insertions, 50 deletions
diff --git a/tools/qmlprofiler/constants.h b/tools/qmlprofiler/constants.h
index b925ba00e8..5b4b52515f 100644
--- a/tools/qmlprofiler/constants.h
+++ b/tools/qmlprofiler/constants.h
@@ -39,9 +39,24 @@ namespace Constants {
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/qmlprofilerapplication.cpp b/tools/qmlprofiler/qmlprofilerapplication.cpp
index 23483b2d16..a493214ced 100644
--- a/tools/qmlprofiler/qmlprofilerapplication.cpp
+++ b/tools/qmlprofiler/qmlprofilerapplication.cpp
@@ -44,8 +44,20 @@
static const char commandTextC[] =
"The following commands are available:\n"
- "\"r\", \"record\" Switch recording on or off.\n"
- "\"q\", \"quit\" Terminate program.";
+ "'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 TraceFileExtension[] = ".qtd";
@@ -56,8 +68,8 @@ QmlProfilerApplication::QmlProfilerApplication(int &argc, char **argv) :
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),
@@ -145,6 +157,11 @@ void QmlProfilerApplication::parseArguments()
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 "
@@ -189,6 +206,8 @@ void QmlProfilerApplication::parseArguments()
}
}
+ m_outputFile = parser.value(output);
+
m_recording = (parser.value(record) == QLatin1String("on"));
m_interactive = parser.isSet(interactive);
@@ -223,49 +242,141 @@ bool QmlProfilerApplication::isInteractive() const
return m_interactive;
}
-void QmlProfilerApplication::printCommands()
+void QmlProfilerApplication::flush()
{
- print(tr(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();
+ 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);
- emit readyForCommand();
- } else if (cmd == Constants::CMD_QUIT
- || cmd == Constants::CMD_QUIT2) {
- print(QLatin1String("Quit"));
+ } else if (cmd == Constants::CMD_QUIT || cmd == Constants::CMD_QUIT2) {
+ m_pendingRequest = REQUEST_QUIT;
if (m_recording) {
- m_quitAfterSave = true;
- m_qmlProfilerClient.sendRecordingStatus(false);
- m_v8profilerClient.sendRecordingStatus(false);
+ 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 {
- printCommands();
- emit readyForCommand();
+ prompt(tr(commandTextC));
}
}
@@ -274,6 +385,13 @@ 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::run()
@@ -299,7 +417,6 @@ void QmlProfilerApplication::run()
}
m_connectTimer.start();
- emit readyForCommand();
}
void QmlProfilerApplication::tryToConnect()
@@ -324,9 +441,9 @@ void QmlProfilerApplication::tryToConnect()
void QmlProfilerApplication::connected()
{
m_connectTimer.stop();
- print(tr("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)));
- print(tr("Recording Status: %1").arg(m_recording ? tr("on") : tr("off")));
+ 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(
@@ -391,23 +508,31 @@ void QmlProfilerApplication::profilerClientEnabled()
void QmlProfilerApplication::traceFinished()
{
m_recording = false; // only on "Complete" we know that the trace is really finished.
- const QString fileName = traceFileName();
- if (m_profilerData.save(fileName))
- print(QString("Saving trace to %1.").arg(fileName));
-
- // after saving, reset the flags
+ // after receiving both notifications, reset the flags
m_qmlDataReady = false;
m_v8DataReady = false;
- if (m_quitAfterSave)
- quit();
+ if (m_pendingRequest == REQUEST_FLUSH) {
+ flush();
+ } else if (m_pendingRequest == REQUEST_TOGGLE_RECORDING) {
+ m_pendingRequest = REQUEST_NONE;
+ prompt(tr("Recording stopped."));
+ } else {
+ 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)
diff --git a/tools/qmlprofiler/qmlprofilerapplication.h b/tools/qmlprofiler/qmlprofilerapplication.h
index 8d2cbffe7b..8f5be179b6 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
@@ -72,7 +81,7 @@ private slots:
void profilerClientEnabled();
void traceFinished();
- void print(const QString &line);
+ void prompt(const QString &line = QString(), bool ready = true);
void logError(const QString &error);
void logStatus(const QString &status);
@@ -80,8 +89,9 @@ private slots:
void v8Complete();
private:
- void printCommands();
- QString traceFileName() const;
+ bool checkOutputFile(PendingRequest pending);
+ void flush();
+ void output();
enum ApplicationMode {
LaunchMode,
@@ -96,8 +106,11 @@ private:
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;
diff --git a/tools/qmlprofiler/qmlprofilerdata.cpp b/tools/qmlprofiler/qmlprofilerdata.cpp
index eaa7cc0e7e..72aeafcb0f 100644
--- a/tools/qmlprofiler/qmlprofilerdata.cpp
+++ b/tools/qmlprofiler/qmlprofilerdata.cpp
@@ -553,10 +553,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);