From 7b4e253a1e0d0ba985251ce1fafbf634d95cc744 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Tue, 17 Nov 2015 16:42:21 +0100 Subject: [PATCH] QmlProfiler: Support local connections on Qt >= 5.6 This removes the need to receive messages from the application via stderr. The "Connecting to socket" is still parsed, but only for diagnostic purposes. If it doesn't arrive, the profiling will still work. Change-Id: I022691293da2a1e671ba1263bc76e4044bf1a5b7 Reviewed-by: Joerg Bornemann --- src/libs/qmldebug/qmldebugconstants.h | 1 + src/libs/qmldebug/qmloutputparser.cpp | 3 ++ src/libs/qmldebug/qmloutputparser.h | 1 + .../analyzerbase/analyzerstartparameters.h | 1 + .../qmlprofiler/localqmlprofilerrunner.cpp | 30 +++++++++--- .../qmlprofiler/localqmlprofilerrunner.h | 2 + .../qmlprofiler/qmlprofilerclientmanager.cpp | 46 ++++++++++++++----- .../qmlprofiler/qmlprofilerclientmanager.h | 6 ++- .../qmlprofiler/qmlprofilerruncontrol.cpp | 4 +- .../qmlprofilerruncontrolfactory.cpp | 16 ++++++- src/plugins/qmlprofiler/qmlprofilertool.cpp | 11 +++-- 11 files changed, 94 insertions(+), 27 deletions(-) diff --git a/src/libs/qmldebug/qmldebugconstants.h b/src/libs/qmldebug/qmldebugconstants.h index 0431d707b8b..b1d59e39740 100644 --- a/src/libs/qmldebug/qmldebugconstants.h +++ b/src/libs/qmldebug/qmldebugconstants.h @@ -40,6 +40,7 @@ const char STR_UNABLE_TO_LISTEN[] = "Unable to listen "; const char STR_IGNORING_DEBUGGER[] = "Ignoring \"-qmljsdebugger="; const char STR_IGNORING_DEBUGGER2[] = "Ignoring\"-qmljsdebugger="; // There is (was?) a bug in one of the error strings - safest to handle both const char STR_CONNECTION_ESTABLISHED[] = "Connection established"; +const char STR_CONNECTING_TO_SOCKET[] = "Connecting to socket"; const char QDECLARATIVE_ENGINE[] = "QDeclarativeEngine"; diff --git a/src/libs/qmldebug/qmloutputparser.cpp b/src/libs/qmldebug/qmloutputparser.cpp index ed65274a1dd..e0e2e1d3c63 100644 --- a/src/libs/qmldebug/qmloutputparser.cpp +++ b/src/libs/qmldebug/qmloutputparser.cpp @@ -79,6 +79,7 @@ void QmlOutputParser::processOutput(const QString &output) static QString debuggingNotEnabled = QLatin1String(Constants::STR_IGNORING_DEBUGGER); static QString debuggingNotEnabled2 = QLatin1String(Constants::STR_IGNORING_DEBUGGER2); static QString connectionEstablished = QLatin1String(Constants::STR_CONNECTION_ESTABLISHED); + static QString connectingToSocket = QLatin1String(Constants::STR_CONNECTING_TO_SOCKET); if (status.startsWith(waitingForConnection)) { status.remove(0, waitingForConnection.size()); // chop of 'Waiting for connection ' @@ -100,6 +101,8 @@ void QmlOutputParser::processOutput(const QString &output) emit errorMessage(tr("The application is not set up for QML/JS debugging.")); } else if (status.startsWith(connectionEstablished)) { emit connectionEstablishedMessage(); + } else if (status.startsWith(connectingToSocket)) { + emit connectingToSocketMessage(); } else { emit unknownMessage(status); } diff --git a/src/libs/qmldebug/qmloutputparser.h b/src/libs/qmldebug/qmloutputparser.h index ecac308ef33..5390ecf77cd 100644 --- a/src/libs/qmldebug/qmloutputparser.h +++ b/src/libs/qmldebug/qmloutputparser.h @@ -49,6 +49,7 @@ public: signals: void waitingForConnectionOnPort(quint16 port); void connectionEstablishedMessage(); + void connectingToSocketMessage(); void errorMessage(const QString &detailedError); void unknownMessage(const QString &unknownMessage); void noOutputMessage(); diff --git a/src/plugins/analyzerbase/analyzerstartparameters.h b/src/plugins/analyzerbase/analyzerstartparameters.h index 3eab41f734c..c840095eefa 100644 --- a/src/plugins/analyzerbase/analyzerstartparameters.h +++ b/src/plugins/analyzerbase/analyzerstartparameters.h @@ -62,6 +62,7 @@ public: QString workingDirectory; QString sysroot; QString analyzerHost; + QString analyzerSocket; quint16 analyzerPort = 0; }; diff --git a/src/plugins/qmlprofiler/localqmlprofilerrunner.cpp b/src/plugins/qmlprofiler/localqmlprofilerrunner.cpp index 2d59e4e3a19..9d49420f27e 100644 --- a/src/plugins/qmlprofiler/localqmlprofilerrunner.cpp +++ b/src/plugins/qmlprofiler/localqmlprofilerrunner.cpp @@ -44,6 +44,7 @@ #include #include +#include using namespace QmlProfiler; using namespace ProjectExplorer; @@ -71,7 +72,7 @@ Analyzer::AnalyzerRunControl *LocalQmlProfilerRunner::createLocalRunControl( conf.executableArguments = sp.debuggeeArgs; conf.workingDirectory = sp.workingDirectory; conf.environment = sp.environment; - + conf.socket = sp.analyzerSocket; conf.port = sp.analyzerPort; if (conf.executable.isEmpty()) { @@ -92,6 +93,17 @@ Analyzer::AnalyzerRunControl *LocalQmlProfilerRunner::createLocalRunControl( return rc; } +QString LocalQmlProfilerRunner::findFreeSocket() +{ + QTemporaryFile file; + if (file.open()) { + return file.fileName(); + } else { + qWarning() << "Could not open a temporary file to find a debug socket."; + return QString(); + } +} + quint16 LocalQmlProfilerRunner::findFreePort(QString &host) { QTcpServer server; @@ -121,15 +133,21 @@ LocalQmlProfilerRunner::~LocalQmlProfilerRunner() void LocalQmlProfilerRunner::start() { - QString arguments = QmlDebug::qmlDebugTcpArguments(QmlDebug::QmlProfilerServices, - m_configuration.port); + QString arguments = m_configuration.socket.isEmpty() ? + QmlDebug::qmlDebugTcpArguments(QmlDebug::QmlProfilerServices, + m_configuration.port) : + QmlDebug::qmlDebugLocalArguments(QmlDebug::QmlProfilerServices, + m_configuration.socket); + if (!m_configuration.executableArguments.isEmpty()) arguments += QLatin1Char(' ') + m_configuration.executableArguments; - if (QmlProfilerPlugin::debugOutput) - qWarning("QmlProfiler: Launching %s:%d", qPrintable(m_configuration.executable), - m_configuration.port); + if (QmlProfilerPlugin::debugOutput) { + qWarning("QmlProfiler: Launching %s:%s", qPrintable(m_configuration.executable), + qPrintable(m_configuration.socket.isEmpty() ? + QString::number(m_configuration.port) : m_configuration.socket)); + } m_launcher.setWorkingDirectory(m_configuration.workingDirectory); m_launcher.setEnvironment(m_configuration.environment); diff --git a/src/plugins/qmlprofiler/localqmlprofilerrunner.h b/src/plugins/qmlprofiler/localqmlprofilerrunner.h index 57063e7b4fc..1094ad482bd 100644 --- a/src/plugins/qmlprofiler/localqmlprofilerrunner.h +++ b/src/plugins/qmlprofiler/localqmlprofilerrunner.h @@ -53,6 +53,7 @@ public: QString executable; QString executableArguments; quint16 port; + QString socket; QString workingDirectory; Utils::Environment environment; }; @@ -63,6 +64,7 @@ public: QString *errorMessage); static quint16 findFreePort(QString &host); + static QString findFreeSocket(); ~LocalQmlProfilerRunner(); diff --git a/src/plugins/qmlprofiler/qmlprofilerclientmanager.cpp b/src/plugins/qmlprofiler/qmlprofilerclientmanager.cpp index 887ba0f3b74..00110b060f8 100644 --- a/src/plugins/qmlprofiler/qmlprofilerclientmanager.cpp +++ b/src/plugins/qmlprofiler/qmlprofilerclientmanager.cpp @@ -58,6 +58,7 @@ public: QTimer connectionTimer; int connectionAttempts; + QString localSocket; QString tcpHost; quint64 tcpPort; QString sysroot; @@ -107,6 +108,13 @@ void QmlProfilerClientManager::setTcpConnection(QString host, quint64 port) disconnectClient(); } +void QmlProfilerClientManager::setLocalSocket(QString file) +{ + d->localSocket = file; + d->tcpPort = 0; + connectLocalClient(file); +} + void QmlProfilerClientManager::clearBufferedData() { if (d->qmlclientplugin) @@ -118,7 +126,7 @@ void QmlProfilerClientManager::discardPendingData() clearBufferedData(); } -void QmlProfilerClientManager::connectClient(quint16 port) +void QmlProfilerClientManager::connectTcpClient(quint16 port) { if (d->connection) { if (port == d->tcpPort) { @@ -129,22 +137,28 @@ void QmlProfilerClientManager::connectClient(quint16 port) } } - d->connection = new QmlDebugConnection; - enableServices(); - connect(d->connection, &QmlDebugConnection::connected, - this, &QmlProfilerClientManager::qmlDebugConnectionOpened); - connect(d->connection, &QmlDebugConnection::disconnected, - this, &QmlProfilerClientManager::qmlDebugConnectionClosed); - connect(d->connection, &QmlDebugConnection::socketError, - this, &QmlProfilerClientManager::qmlDebugConnectionError); - connect(d->connection, &QmlDebugConnection::socketStateChanged, - this, &QmlProfilerClientManager::qmlDebugConnectionStateChanged); + createConnection(); d->connectionTimer.start(); d->tcpPort = port; } -void QmlProfilerClientManager::enableServices() +void QmlProfilerClientManager::connectLocalClient(const QString &file) { + if (d->connection) { + if (file == d->localSocket) + return; + else + delete d->connection; + } + + createConnection(); + d->localSocket = file; + d->connection->startLocalServer(file); +} + +void QmlProfilerClientManager::createConnection() +{ + d->connection = new QmlDebugConnection; QTC_ASSERT(d->profilerState, return); disconnectClientSignals(); @@ -155,6 +169,14 @@ void QmlProfilerClientManager::enableServices() d->profilerState->requestedFeatures()); d->qmlclientplugin->setFlushInterval(d->flushInterval); connectClientSignals(); + connect(d->connection, &QmlDebugConnection::connected, + this, &QmlProfilerClientManager::qmlDebugConnectionOpened); + connect(d->connection, &QmlDebugConnection::disconnected, + this, &QmlProfilerClientManager::qmlDebugConnectionClosed); + connect(d->connection, &QmlDebugConnection::socketError, + this, &QmlProfilerClientManager::qmlDebugConnectionError); + connect(d->connection, &QmlDebugConnection::socketStateChanged, + this, &QmlProfilerClientManager::qmlDebugConnectionStateChanged); } void QmlProfilerClientManager::connectClientSignals() diff --git a/src/plugins/qmlprofiler/qmlprofilerclientmanager.h b/src/plugins/qmlprofiler/qmlprofilerclientmanager.h index 9eae3e0cad5..3167f98579d 100644 --- a/src/plugins/qmlprofiler/qmlprofilerclientmanager.h +++ b/src/plugins/qmlprofiler/qmlprofilerclientmanager.h @@ -52,6 +52,7 @@ public: void registerProfilerStateManager(QmlProfilerStateManager *profilerState); void setTcpConnection(QString host, quint64 port); + void setLocalSocket(QString file); void clearBufferedData(); void discardPendingData(); @@ -65,7 +66,8 @@ signals: void connectionClosed(); public slots: - void connectClient(quint16 port); + void connectTcpClient(quint16 port); + void connectLocalClient(const QString &file); void disconnectClient(); private slots: @@ -89,7 +91,7 @@ private: void connectToClient(); - void enableServices(); + void createConnection(); void connectClientSignals(); void disconnectClientSignals(); diff --git a/src/plugins/qmlprofiler/qmlprofilerruncontrol.cpp b/src/plugins/qmlprofiler/qmlprofilerruncontrol.cpp index c6dd5523219..e34687114d7 100644 --- a/src/plugins/qmlprofiler/qmlprofilerruncontrol.cpp +++ b/src/plugins/qmlprofiler/qmlprofilerruncontrol.cpp @@ -96,6 +96,8 @@ QmlProfilerRunControl::QmlProfilerRunControl(const AnalyzerStartParameters &sp, this, &QmlProfilerRunControl::processIsRunning); connect(&d->m_outputParser, &QmlDebug::QmlOutputParser::noOutputMessage, this, [this](){processIsRunning(0);}); + connect(&d->m_outputParser, &QmlDebug::QmlOutputParser::connectingToSocketMessage, + this, [this](){processIsRunning(0);}); connect(&d->m_outputParser, &QmlDebug::QmlOutputParser::errorMessage, this, &QmlProfilerRunControl::wrongSetupMessageBox); } @@ -113,7 +115,7 @@ bool QmlProfilerRunControl::startEngine() if (startParameters().analyzerPort != 0) emit processRunning(startParameters().analyzerPort); - else + else if (startParameters().analyzerSocket.isEmpty()) d->m_noDebugOutputTimer.start(); d->m_profilerState->setCurrentState(QmlProfilerStateManager::AppRunning); diff --git a/src/plugins/qmlprofiler/qmlprofilerruncontrolfactory.cpp b/src/plugins/qmlprofiler/qmlprofilerruncontrolfactory.cpp index a0bf9a6199f..de4d0205cac 100644 --- a/src/plugins/qmlprofiler/qmlprofilerruncontrolfactory.cpp +++ b/src/plugins/qmlprofiler/qmlprofilerruncontrolfactory.cpp @@ -44,6 +44,8 @@ #include #include #include +#include +#include #include @@ -79,7 +81,19 @@ static AnalyzerStartParameters createQmlProfilerStartParameters(RunConfiguration sp.debuggee = rc->executable(); sp.debuggeeArgs = rc->commandLineArguments(); sp.displayName = rc->displayName(); - sp.analyzerPort = LocalQmlProfilerRunner::findFreePort(sp.analyzerHost); + + const QtSupport::BaseQtVersion *version = + QtSupport::QtKitInformation::qtVersion(runConfiguration->target()->kit()); + if (version) { + QtSupport::QtVersionNumber versionNumber = version->qtVersion(); + if (versionNumber.majorVersion >= 5 && versionNumber.minorVersion >= 6) + sp.analyzerSocket = LocalQmlProfilerRunner::findFreeSocket(); + else + sp.analyzerPort = LocalQmlProfilerRunner::findFreePort(sp.analyzerHost); + } else { + qWarning() << "Running QML profiler on Kit without Qt version??"; + sp.analyzerPort = LocalQmlProfilerRunner::findFreePort(sp.analyzerHost); + } return sp; } diff --git a/src/plugins/qmlprofiler/qmlprofilertool.cpp b/src/plugins/qmlprofiler/qmlprofilertool.cpp index d262bb3327b..c80795ebf63 100644 --- a/src/plugins/qmlprofiler/qmlprofilertool.cpp +++ b/src/plugins/qmlprofiler/qmlprofilertool.cpp @@ -214,10 +214,10 @@ AnalyzerRunControl *QmlProfilerTool::createRunControl(const AnalyzerStartParamet engine->registerProfilerStateManager(d->m_profilerState); - bool isTcpConnection = true; - // FIXME: Check that there's something sensible in sp.connParams - if (isTcpConnection) + if (!sp.analyzerSocket.isEmpty()) + d->m_profilerConnections->setLocalSocket(sp.analyzerSocket); + else d->m_profilerConnections->setTcpConnection(sp.analyzerHost, sp.analyzerPort); // @@ -232,8 +232,9 @@ AnalyzerRunControl *QmlProfilerTool::createRunControl(const AnalyzerStartParamet populateFileFinder(projectDirectory, sp.sysroot); - connect(engine, &QmlProfilerRunControl::processRunning, - d->m_profilerConnections, &QmlProfilerClientManager::connectClient); + if (sp.analyzerSocket.isEmpty()) + connect(engine, &QmlProfilerRunControl::processRunning, + d->m_profilerConnections, &QmlProfilerClientManager::connectTcpClient); connect(d->m_profilerConnections, &QmlProfilerClientManager::connectionFailed, engine, &QmlProfilerRunControl::cancelProcess);