aboutsummaryrefslogtreecommitdiffstats
path: root/plugins/qmlprofiler/qmlprofilerengine.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/qmlprofiler/qmlprofilerengine.cpp')
-rw-r--r--plugins/qmlprofiler/qmlprofilerengine.cpp388
1 files changed, 388 insertions, 0 deletions
diff --git a/plugins/qmlprofiler/qmlprofilerengine.cpp b/plugins/qmlprofiler/qmlprofilerengine.cpp
new file mode 100644
index 0000000000..9b71068c37
--- /dev/null
+++ b/plugins/qmlprofiler/qmlprofilerengine.cpp
@@ -0,0 +1,388 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "qmlprofilerengine.h"
+
+#include "localqmlprofilerrunner.h"
+
+#include <analyzerbase/analyzermanager.h>
+#include <coreplugin/icore.h>
+#include <debugger/debuggerrunconfigurationaspect.h>
+#include <utils/qtcassert.h>
+#include <coreplugin/helpmanager.h>
+#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/kitinformation.h>
+#include <projectexplorer/target.h>
+#include <qmlprojectmanager/qmlprojectrunconfiguration.h>
+#include <qmlprojectmanager/qmlprojectplugin.h>
+#include <projectexplorer/environmentaspect.h>
+#include <projectexplorer/localapplicationruncontrol.h>
+#include <projectexplorer/localapplicationrunconfiguration.h>
+#include <qmldebug/qmloutputparser.h>
+
+#include <QMainWindow>
+#include <QMessageBox>
+#include <QTimer>
+#include <QTcpServer>
+
+using namespace Analyzer;
+using namespace ProjectExplorer;
+
+namespace QmlProfiler {
+namespace Internal {
+
+//
+// QmlProfilerEnginePrivate
+//
+
+class QmlProfilerEngine::QmlProfilerEnginePrivate
+{
+public:
+ QmlProfilerEnginePrivate(QmlProfilerEngine *qq, const AnalyzerStartParameters &sp) : q(qq), m_runner(0), sp(sp) {}
+ ~QmlProfilerEnginePrivate() { delete m_runner; }
+
+ bool attach(const QString &address, uint port);
+ AbstractQmlProfilerRunner *createRunner(ProjectExplorer::RunConfiguration *runConfiguration,
+ QObject *parent);
+
+ QmlProfilerEngine *q;
+
+ QmlProfilerStateManager *m_profilerState;
+
+ AbstractQmlProfilerRunner *m_runner;
+ QTimer m_noDebugOutputTimer;
+ QmlDebug::QmlOutputParser m_outputParser;
+ const AnalyzerStartParameters sp;
+};
+
+AbstractQmlProfilerRunner *
+QmlProfilerEngine::QmlProfilerEnginePrivate::createRunner(ProjectExplorer::RunConfiguration *runConfiguration,
+ QObject *parent)
+{
+ AbstractQmlProfilerRunner *runner = 0;
+ if (!runConfiguration) // attaching
+ return 0;
+
+ QmlProjectManager::QmlProjectRunConfiguration *rc1 =
+ qobject_cast<QmlProjectManager::QmlProjectRunConfiguration *>(runConfiguration);
+ LocalApplicationRunConfiguration *rc2 =
+ qobject_cast<LocalApplicationRunConfiguration *>(runConfiguration);
+ // Supports only local run configurations
+ if (!rc1 && !rc2)
+ return 0;
+
+ ProjectExplorer::EnvironmentAspect *environment
+ = runConfiguration->extraAspect<ProjectExplorer::EnvironmentAspect>();
+ QTC_ASSERT(environment, return 0);
+ LocalQmlProfilerRunner::Configuration conf;
+ if (rc1) {
+ // This is a "plain" .qmlproject.
+ conf.executable = rc1->observerPath();
+ conf.executableArguments = rc1->viewerArguments();
+ conf.workingDirectory = rc1->workingDirectory();
+ conf.environment = environment->environment();
+ } else {
+ // FIXME: Check.
+ conf.executable = rc2->executable();
+ conf.executableArguments = rc2->commandLineArguments();
+ conf.workingDirectory = rc2->workingDirectory();
+ conf.environment = environment->environment();
+ }
+ const ProjectExplorer::IDevice::ConstPtr device =
+ ProjectExplorer::DeviceKitInformation::device(runConfiguration->target()->kit());
+ QTC_ASSERT(device->type() == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE, return 0);
+ conf.port = sp.analyzerPort;
+ runner = new LocalQmlProfilerRunner(conf, parent);
+ return runner;
+}
+
+//
+// QmlProfilerEngine
+//
+
+QmlProfilerEngine::QmlProfilerEngine(IAnalyzerTool *tool,
+ const Analyzer::AnalyzerStartParameters &sp,
+ ProjectExplorer::RunConfiguration *runConfiguration)
+ : IAnalyzerEngine(tool, sp, runConfiguration)
+ , d(new QmlProfilerEnginePrivate(this, sp))
+{
+ d->m_profilerState = 0;
+
+ // Only wait 4 seconds for the 'Waiting for connection' on application output, then just try to connect
+ // (application output might be redirected / blocked)
+ d->m_noDebugOutputTimer.setSingleShot(true);
+ d->m_noDebugOutputTimer.setInterval(4000);
+ connect(&d->m_noDebugOutputTimer, SIGNAL(timeout()), this, SLOT(processIsRunning()));
+
+ d->m_outputParser.setNoOutputText(ApplicationLauncher::msgWinCannotRetrieveDebuggingOutput());
+ connect(&d->m_outputParser, SIGNAL(waitingForConnectionOnPort(quint16)),
+ this, SLOT(processIsRunning(quint16)));
+ connect(&d->m_outputParser, SIGNAL(noOutputMessage()),
+ this, SLOT(processIsRunning()));
+ connect(&d->m_outputParser, SIGNAL(errorMessage(QString)),
+ this, SLOT(wrongSetupMessageBox(QString)));
+}
+
+QmlProfilerEngine::~QmlProfilerEngine()
+{
+ if (d->m_profilerState && d->m_profilerState->currentState() == QmlProfilerStateManager::AppRunning)
+ stop();
+ delete d;
+}
+
+bool QmlProfilerEngine::start()
+{
+ QTC_ASSERT(d->m_profilerState, return false);
+
+ if (d->m_runner) {
+ delete d->m_runner;
+ d->m_runner = 0;
+ }
+
+ d->m_profilerState->setCurrentState(QmlProfilerStateManager::AppStarting);
+
+ if (QmlProjectManager::QmlProjectRunConfiguration *rc =
+ qobject_cast<QmlProjectManager::QmlProjectRunConfiguration *>(runConfiguration())) {
+ if (rc->observerPath().isEmpty()) {
+ QmlProjectManager::QmlProjectPlugin::showQmlObserverToolWarning();
+ d->m_profilerState->setCurrentState(QmlProfilerStateManager::Idle);
+ AnalyzerManager::stopTool();
+ return false;
+ }
+ }
+
+ d->m_runner = d->createRunner(runConfiguration(), this);
+
+ if (LocalQmlProfilerRunner *qmlRunner = qobject_cast<LocalQmlProfilerRunner *>(d->m_runner)) {
+ if (!qmlRunner->hasExecutable()) {
+ showNonmodalWarning(tr("No executable file to launch."));
+ d->m_profilerState->setCurrentState(QmlProfilerStateManager::Idle);
+ AnalyzerManager::stopTool();
+ return false;
+ }
+ }
+
+ if (d->m_runner) {
+ connect(d->m_runner, SIGNAL(stopped()), this, SLOT(processEnded()));
+ connect(d->m_runner, SIGNAL(appendMessage(QString,Utils::OutputFormat)),
+ this, SLOT(logApplicationMessage(QString,Utils::OutputFormat)));
+ d->m_runner->start();
+ d->m_noDebugOutputTimer.start();
+ } else if (d->sp.startMode == StartQmlRemote) {
+ d->m_noDebugOutputTimer.start();
+ } else {
+ emit processRunning(startParameters().analyzerPort);
+ }
+
+ d->m_profilerState->setCurrentState(QmlProfilerStateManager::AppRunning);
+ emit starting(this);
+ return true;
+}
+
+void QmlProfilerEngine::stop()
+{
+ QTC_ASSERT(d->m_profilerState, return);
+
+ switch (d->m_profilerState->currentState()) {
+ case QmlProfilerStateManager::AppRunning : {
+ d->m_profilerState->setCurrentState(QmlProfilerStateManager::AppStopRequested);
+ break;
+ }
+ case QmlProfilerStateManager::AppReadyToStop : {
+ cancelProcess();
+ break;
+ }
+ case QmlProfilerStateManager::AppDying :
+ // valid, but no further action is needed
+ break;
+ default: {
+ const QString message = QString::fromLatin1("Unexpected engine stop from state %1 in %2:%3")
+ .arg(d->m_profilerState->currentStateAsString(), QString::fromLatin1(__FILE__), QString::number(__LINE__));
+ qWarning("%s", qPrintable(message));
+ }
+ break;
+ }
+}
+
+void QmlProfilerEngine::processEnded()
+{
+ QTC_ASSERT(d->m_profilerState, return);
+
+ switch (d->m_profilerState->currentState()) {
+ case QmlProfilerStateManager::AppRunning : {
+ d->m_profilerState->setCurrentState(QmlProfilerStateManager::AppDying);
+ AnalyzerManager::stopTool();
+
+ emit finished();
+ break;
+ }
+ case QmlProfilerStateManager::AppStopped :
+ case QmlProfilerStateManager::AppKilled :
+ d->m_profilerState->setCurrentState(QmlProfilerStateManager::Idle);
+ break;
+ default: {
+ const QString message = QString::fromLatin1("Process died unexpectedly from state %1 in %2:%3")
+ .arg(d->m_profilerState->currentStateAsString(), QString::fromLatin1(__FILE__), QString::number(__LINE__));
+ qWarning("%s", qPrintable(message));
+}
+ break;
+ }
+}
+
+void QmlProfilerEngine::cancelProcess()
+{
+ QTC_ASSERT(d->m_profilerState, return);
+
+ switch (d->m_profilerState->currentState()) {
+ case QmlProfilerStateManager::AppReadyToStop : {
+ d->m_profilerState->setCurrentState(QmlProfilerStateManager::AppStopped);
+ break;
+ }
+ case QmlProfilerStateManager::AppRunning : {
+ d->m_profilerState->setCurrentState(QmlProfilerStateManager::AppDying);
+ break;
+ }
+ default: {
+ const QString message = QString::fromLatin1("Unexpected process termination requested with state %1 in %2:%3")
+ .arg(d->m_profilerState->currentStateAsString(), QString::fromLatin1(__FILE__), QString::number(__LINE__));
+ qWarning("%s", qPrintable(message));
+ return;
+ }
+ }
+
+ if (d->m_runner)
+ d->m_runner->stop();
+ emit finished();
+}
+
+void QmlProfilerEngine::logApplicationMessage(const QString &msg, Utils::OutputFormat format)
+{
+ emit outputReceived(msg, format);
+ d->m_outputParser.processOutput(msg);
+}
+
+void QmlProfilerEngine::wrongSetupMessageBox(const QString &errorMessage)
+{
+ QMessageBox *infoBox = new QMessageBox(Core::ICore::mainWindow());
+ infoBox->setIcon(QMessageBox::Critical);
+ infoBox->setWindowTitle(tr("Qt Creator"));
+ //: %1 is detailed error message
+ infoBox->setText(tr("Could not connect to the in-process QML debugger:\n%1")
+ .arg(errorMessage));
+ infoBox->setStandardButtons(QMessageBox::Ok | QMessageBox::Help);
+ infoBox->setDefaultButton(QMessageBox::Ok);
+ infoBox->setModal(true);
+
+ connect(infoBox, SIGNAL(finished(int)),
+ this, SLOT(wrongSetupMessageBoxFinished(int)));
+
+ infoBox->show();
+
+ // KILL
+ d->m_profilerState->setCurrentState(QmlProfilerStateManager::AppDying);
+ AnalyzerManager::stopTool();
+ emit finished();
+}
+
+void QmlProfilerEngine::wrongSetupMessageBoxFinished(int button)
+{
+ if (button == QMessageBox::Help) {
+ Core::HelpManager *helpManager = Core::HelpManager::instance();
+ helpManager->handleHelpRequest(QLatin1String("qthelp://org.qt-project.qtcreator/doc/creator-debugging-qml.html"
+ "#setting-up-qml-debugging"));
+ }
+}
+
+void QmlProfilerEngine::showNonmodalWarning(const QString &warningMsg)
+{
+ QMessageBox *noExecWarning = new QMessageBox(Core::ICore::mainWindow());
+ noExecWarning->setIcon(QMessageBox::Warning);
+ noExecWarning->setWindowTitle(tr("QML Profiler"));
+ noExecWarning->setText(warningMsg);
+ noExecWarning->setStandardButtons(QMessageBox::Ok);
+ noExecWarning->setDefaultButton(QMessageBox::Ok);
+ noExecWarning->setModal(false);
+ noExecWarning->show();
+}
+
+void QmlProfilerEngine::notifyRemoteSetupDone(quint16 port)
+{
+ d->m_noDebugOutputTimer.stop();
+ emit processRunning(port);
+}
+
+void QmlProfilerEngine::processIsRunning(quint16 port)
+{
+ d->m_noDebugOutputTimer.stop();
+
+ if (port > 0)
+ emit processRunning(port);
+ else if (d->m_runner)
+ emit processRunning(d->m_runner->debugPort());
+}
+
+////////////////////////////////////////////////////////////////
+// Profiler State
+void QmlProfilerEngine::registerProfilerStateManager( QmlProfilerStateManager *profilerState )
+{
+ // disconnect old
+ if (d->m_profilerState)
+ disconnect(d->m_profilerState, SIGNAL(stateChanged()), this, SLOT(profilerStateChanged()));
+
+ d->m_profilerState = profilerState;
+
+ // connect
+ if (d->m_profilerState)
+ connect(d->m_profilerState, SIGNAL(stateChanged()), this, SLOT(profilerStateChanged()));
+}
+
+void QmlProfilerEngine::profilerStateChanged()
+{
+ switch (d->m_profilerState->currentState()) {
+ case QmlProfilerStateManager::AppReadyToStop : {
+ cancelProcess();
+ break;
+ }
+ case QmlProfilerStateManager::Idle : {
+ // When all the profiling is done, delete the profiler runner
+ // (a new one will be created at start)
+ d->m_noDebugOutputTimer.stop();
+ if (d->m_runner) {
+ delete d->m_runner;
+ d->m_runner = 0;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+} // namespace Internal
+} // namespace QmlProfiler