qmlprofilertool.cpp 26.6 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
Kai Koehne's avatar
Kai Koehne committed
2
**
3
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
hjk's avatar
hjk committed
4
** Contact: http://www.qt-project.org/legal
Kai Koehne's avatar
Kai Koehne committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
Kai Koehne's avatar
Kai Koehne committed
7
**
hjk's avatar
hjk committed
8
9
10
11
12
** 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
Eike Ziller's avatar
Eike Ziller committed
13
14
** conditions see http://www.qt.io/licensing.  For further information
** use the contact form at http://www.qt.io/contact-us.
Kai Koehne's avatar
Kai Koehne committed
15
16
**
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17
** Alternatively, this file may be used under the terms of the GNU Lesser
Eike Ziller's avatar
Eike Ziller committed
18
19
20
21
22
23
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file.  Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
hjk's avatar
hjk committed
24
25
26
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
Kai Koehne's avatar
Kai Koehne committed
27
28
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
29
****************************************************************************/
Kai Koehne's avatar
Kai Koehne committed
30

Christiaan Janssen's avatar
Christiaan Janssen committed
31
#include "qmlprofilertool.h"
Christiaan Janssen's avatar
Christiaan Janssen committed
32
#include "qmlprofilerstatemanager.h"
Christiaan Janssen's avatar
Christiaan Janssen committed
33
#include "qmlprofilerengine.h"
34
35
#include "qmlprofilerconstants.h"
#include "qmlprofilerattachdialog.h"
Christiaan Janssen's avatar
Christiaan Janssen committed
36
37
#include "qmlprofilerviewmanager.h"
#include "qmlprofilerclientmanager.h"
Christiaan Janssen's avatar
Christiaan Janssen committed
38
#include "qmlprofilermodelmanager.h"
Christiaan Janssen's avatar
Christiaan Janssen committed
39
40
#include "qmlprofilerdetailsrewriter.h"
#include "timelinerenderer.h"
41
#include "notesmodel.h"
Christiaan Janssen's avatar
Christiaan Janssen committed
42
43

#include <analyzerbase/analyzermanager.h>
44
#include <analyzerbase/analyzerruncontrol.h>
Christiaan Janssen's avatar
Christiaan Janssen committed
45

46
#include <utils/fancymainwindow.h>
Christiaan Janssen's avatar
Christiaan Janssen committed
47
#include <utils/fileinprojectfinder.h>
48
#include <utils/qtcassert.h>
49
#include <projectexplorer/environmentaspect.h>
Christiaan Janssen's avatar
Christiaan Janssen committed
50
51
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/project.h>
52
#include <projectexplorer/target.h>
53
#include <projectexplorer/session.h>
54
#include <projectexplorer/kitinformation.h>
55
#include <projectexplorer/localapplicationrunconfiguration.h>
56
#include <texteditor/texteditor.h>
57

58
#include <coreplugin/coreconstants.h>
Christiaan Janssen's avatar
Christiaan Janssen committed
59
#include <coreplugin/editormanager/editormanager.h>
60
#include <coreplugin/icore.h>
61
#include <coreplugin/messagemanager.h>
62
#include <coreplugin/helpmanager.h>
63
64
65
66
67
#include <coreplugin/modemanager.h>
#include <coreplugin/imode.h>
#include <coreplugin/actionmanager/command.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/actionmanager/actioncontainer.h>
Christiaan Janssen's avatar
Christiaan Janssen committed
68

Tobias Hunger's avatar
Tobias Hunger committed
69
#include <qtsupport/qtkitinformation.h>
Tobias Hunger's avatar
Tobias Hunger committed
70

71
#include <QApplication>
hjk's avatar
hjk committed
72
#include <QFileDialog>
73
74
75
#include <QHBoxLayout>
#include <QLabel>
#include <QMenu>
hjk's avatar
hjk committed
76
#include <QMessageBox>
77
#include <QTcpServer>
hjk's avatar
hjk committed
78
79
80
#include <QTime>
#include <QTimer>
#include <QToolButton>
81

82
83
using namespace Core;
using namespace Core::Constants;
Christiaan Janssen's avatar
Christiaan Janssen committed
84
using namespace Analyzer;
85
using namespace Analyzer::Constants;
Christiaan Janssen's avatar
Christiaan Janssen committed
86
using namespace QmlProfiler::Constants;
87
using namespace QmlDebug;
88
using namespace ProjectExplorer;
Christiaan Janssen's avatar
Christiaan Janssen committed
89

90
91
92
namespace QmlProfiler {
namespace Internal {

Christiaan Janssen's avatar
Christiaan Janssen committed
93
94
95
class QmlProfilerTool::QmlProfilerToolPrivate
{
public:
Christiaan Janssen's avatar
Christiaan Janssen committed
96
97
    QmlProfilerStateManager *m_profilerState;
    QmlProfilerClientManager *m_profilerConnections;
Christiaan Janssen's avatar
Christiaan Janssen committed
98
    QmlProfilerModelManager *m_profilerModelManager;
Christiaan Janssen's avatar
Christiaan Janssen committed
99
100

    QmlProfilerViewManager *m_viewContainer;
101
    Utils::FileInProjectFinder m_projectFinder;
102
    QToolButton *m_recordButton;
103
104
    QMenu *m_featuresMenu;

105
    QToolButton *m_clearButton;
Christiaan Janssen's avatar
Christiaan Janssen committed
106
107
108
109
110
111
112

    // elapsed time display
    QTimer m_recordingTimer;
    QTime m_recordingElapsedTime;
    QLabel *m_timeLabel;

    // save and load actions
113
    QAction *m_saveQmlTrace;
Christiaan Janssen's avatar
Christiaan Janssen committed
114
    QAction *m_loadQmlTrace;
Christiaan Janssen's avatar
Christiaan Janssen committed
115
116
};

117
QmlProfilerTool::QmlProfilerTool(QObject *parent)
hjk's avatar
hjk committed
118
    : IAnalyzerTool(parent), d(new QmlProfilerToolPrivate)
Christiaan Janssen's avatar
Christiaan Janssen committed
119
{
120
    setObjectName(QLatin1String("QmlProfilerTool"));
121
122
    setRunMode(QmlProfilerRunMode);
    setToolMode(AnyMode);
hjk's avatar
hjk committed
123

Christiaan Janssen's avatar
Christiaan Janssen committed
124
125
    d->m_profilerState = 0;
    d->m_viewContainer = 0;
126
127
128
129
    d->m_recordButton = 0;
    d->m_featuresMenu = 0;
    d->m_clearButton = 0;
    d->m_timeLabel = 0;
130

Christiaan Janssen's avatar
Christiaan Janssen committed
131
132
133
134
135
136
137
138
139
    qmlRegisterType<TimelineRenderer>("Monitor", 1, 0,"TimelineRenderer");

    d->m_profilerState = new QmlProfilerStateManager(this);
    connect(d->m_profilerState, SIGNAL(stateChanged()), this, SLOT(profilerStateChanged()));
    connect(d->m_profilerState, SIGNAL(clientRecordingChanged()), this, SLOT(clientRecordingChanged()));
    connect(d->m_profilerState, SIGNAL(serverRecordingChanged()), this, SLOT(serverRecordingChanged()));

    d->m_profilerConnections = new QmlProfilerClientManager(this);
    d->m_profilerConnections->registerProfilerStateManager(d->m_profilerState);
140
    connect(d->m_profilerConnections, SIGNAL(connectionClosed()), this, SLOT(clientsDisconnected()));
Christiaan Janssen's avatar
Christiaan Janssen committed
141

Christiaan Janssen's avatar
Christiaan Janssen committed
142
143
144
    d->m_profilerModelManager = new QmlProfilerModelManager(&d->m_projectFinder, this);
    connect(d->m_profilerModelManager, SIGNAL(stateChanged()), this, SLOT(profilerDataModelStateChanged()));
    connect(d->m_profilerModelManager, SIGNAL(error(QString)), this, SLOT(showErrorDialog(QString)));
145
146
    connect(d->m_profilerModelManager, SIGNAL(availableFeaturesChanged(quint64)),
            this, SLOT(setAvailableFeatures(quint64)));
147

Christiaan Janssen's avatar
Christiaan Janssen committed
148
    d->m_profilerConnections->setModelManager(d->m_profilerModelManager);
149
150
151
    Command *command = 0;
    const Context globalContext(C_GLOBAL);

hjk's avatar
hjk committed
152
153
    ActionContainer *menu = ActionManager::actionContainer(M_DEBUG_ANALYZER);
    ActionContainer *options = ActionManager::createMenu(M_DEBUG_ANALYZER_QML_OPTIONS);
154
155
156
157
    options->menu()->setTitle(tr("QML Profiler Options"));
    menu->addMenu(options, G_ANALYZER_OPTIONS);
    options->menu()->setEnabled(true);

Christiaan Janssen's avatar
Christiaan Janssen committed
158
    QAction *act = d->m_loadQmlTrace = new QAction(tr("Load QML Trace"), options);
hjk's avatar
hjk committed
159
    command = ActionManager::registerAction(act, "Analyzer.Menu.StartAnalyzer.QMLProfilerOptions.LoadQMLTrace", globalContext);
160
161
162
163
164
    connect(act, SIGNAL(triggered()), this, SLOT(showLoadDialog()));
    options->addAction(command);

    act = d->m_saveQmlTrace = new QAction(tr("Save QML Trace"), options);
    d->m_saveQmlTrace->setEnabled(false);
hjk's avatar
hjk committed
165
    command = ActionManager::registerAction(act, "Analyzer.Menu.StartAnalyzer.QMLProfilerOptions.SaveQMLTrace", globalContext);
166
167
    connect(act, SIGNAL(triggered()), this, SLOT(showSaveDialog()));
    options->addAction(command);
Christiaan Janssen's avatar
Christiaan Janssen committed
168
169
170

    d->m_recordingTimer.setInterval(100);
    connect(&d->m_recordingTimer, SIGNAL(timeout()), this, SLOT(updateTimeDisplay()));
Christiaan Janssen's avatar
Christiaan Janssen committed
171
172
173
174
175
176
177
}

QmlProfilerTool::~QmlProfilerTool()
{
    delete d;
}

178
AnalyzerRunControl *QmlProfilerTool::createRunControl(const AnalyzerStartParameters &sp,
179
    RunConfiguration *runConfiguration)
Christiaan Janssen's avatar
Christiaan Janssen committed
180
{
181
    QmlProfilerRunControl *engine = new QmlProfilerRunControl(sp, runConfiguration);
182

Christiaan Janssen's avatar
Christiaan Janssen committed
183
184
185
    engine->registerProfilerStateManager(d->m_profilerState);

    bool isTcpConnection = true;
186

187
188
189
190
    if (runConfiguration) {
        // Check minimum Qt Version. We cannot really be sure what the Qt version
        // at runtime is, but guess that the active build configuraiton has been used.
        QtSupport::QtVersionNumber minimumVersion(4, 7, 4);
Tobias Hunger's avatar
Tobias Hunger committed
191
        QtSupport::BaseQtVersion *version = QtSupport::QtKitInformation::qtVersion(runConfiguration->target()->kit());
Tobias Hunger's avatar
Tobias Hunger committed
192
193
        if (version) {
            if (version->isValid() && version->qtVersion() < minimumVersion) {
194
195
196
197
198
199
200
201
                int result = QMessageBox::warning(QApplication::activeWindow(), tr("QML Profiler"),
                     tr("The QML profiler requires Qt 4.7.4 or newer.\n"
                     "The Qt version configured in your active build configuration is too old.\n"
                     "Do you want to continue?"), QMessageBox::Yes, QMessageBox::No);
                if (result == QMessageBox::No)
                    return 0;
            }
        }
202
203
    }

204
    // FIXME: Check that there's something sensible in sp.connParams
205
    if (isTcpConnection)
206
        d->m_profilerConnections->setTcpConnection(sp.analyzerHost, sp.analyzerPort);
207

208
209
210
211
212
    //
    // Initialize m_projectFinder
    //

    QString projectDirectory;
213
214
    if (runConfiguration) {
        Project *project = runConfiguration->target()->project();
215
        projectDirectory = project->projectDirectory().toString();
216
    }
217

218
    populateFileFinder(projectDirectory, sp.sysroot);
219

Christiaan Janssen's avatar
Christiaan Janssen committed
220
221
    connect(engine, SIGNAL(processRunning(quint16)), d->m_profilerConnections, SLOT(connectClient(quint16)));
    connect(d->m_profilerConnections, SIGNAL(connectionFailed()), engine, SLOT(cancelProcess()));
Christiaan Janssen's avatar
Christiaan Janssen committed
222
223
224
225

    return engine;
}

226
227
228
static QString sysroot(RunConfiguration *runConfig)
{
    QTC_ASSERT(runConfig, return QString());
hjk's avatar
hjk committed
229
230
231
    Kit *k = runConfig->target()->kit();
    if (k && SysRootKitInformation::hasSysRoot(k))
        return SysRootKitInformation::sysRoot(runConfig->target()->kit()).toString();
232
233
234
    return QString();
}

235
236
QWidget *QmlProfilerTool::createWidgets()
{
Christiaan Janssen's avatar
Christiaan Janssen committed
237
    QTC_ASSERT(!d->m_viewContainer, return 0);
238

Christiaan Janssen's avatar
Christiaan Janssen committed
239

Christiaan Janssen's avatar
Christiaan Janssen committed
240
241
    d->m_viewContainer = new QmlProfilerViewManager(this,
                                                    this,
Christiaan Janssen's avatar
Christiaan Janssen committed
242
                                                    d->m_profilerModelManager,
Christiaan Janssen's avatar
Christiaan Janssen committed
243
244
245
                                                    d->m_profilerState);
    connect(d->m_viewContainer, SIGNAL(gotoSourceLocation(QString,int,int)),
            this, SLOT(gotoSourceLocation(QString,int,int)));
246

247
248
249
    //
    // Toolbar
    //
Christiaan Janssen's avatar
Christiaan Janssen committed
250
251
252
253
254
255
256
    QWidget *toolbarWidget = new QWidget;
    toolbarWidget->setObjectName(QLatin1String("QmlProfilerToolBarWidget"));

    QHBoxLayout *layout = new QHBoxLayout;
    layout->setMargin(0);
    layout->setSpacing(0);

257
258
259
    d->m_recordButton = new QToolButton(toolbarWidget);
    d->m_recordButton->setCheckable(true);

260
    connect(d->m_recordButton,SIGNAL(clicked(bool)), this, SLOT(recordingButtonChanged(bool)));
261
    d->m_recordButton->setChecked(true);
262
263
264
265
266
267
268
    d->m_featuresMenu = new QMenu(d->m_recordButton);
    d->m_recordButton->setMenu(d->m_featuresMenu);
    d->m_recordButton->setPopupMode(QToolButton::MenuButtonPopup);
    setAvailableFeatures(d->m_profilerModelManager->availableFeatures());
    connect(d->m_featuresMenu, SIGNAL(triggered(QAction*)),
            this, SLOT(toggleRecordingFeature(QAction*)));

Christiaan Janssen's avatar
Christiaan Janssen committed
269
    setRecording(d->m_profilerState->clientRecording());
270
271
    layout->addWidget(d->m_recordButton);

272
273
    d->m_clearButton = new QToolButton(toolbarWidget);
    d->m_clearButton->setIcon(QIcon(QLatin1String(":/qmlprofiler/clean_pane_small.png")));
Christiaan Janssen's avatar
Christiaan Janssen committed
274
    d->m_clearButton->setToolTip(tr("Discard data"));
Christiaan Janssen's avatar
Christiaan Janssen committed
275

276
277
278
279
    connect(d->m_clearButton, &QAbstractButton::clicked, [this](){
        if (checkForUnsavedNotes())
            clearData();
    });
Christiaan Janssen's avatar
Christiaan Janssen committed
280

281
282
    layout->addWidget(d->m_clearButton);

Christiaan Janssen's avatar
Christiaan Janssen committed
283
    d->m_timeLabel = new QLabel();
Lorenz Haas's avatar
Lorenz Haas committed
284
    QPalette palette;
Christiaan Janssen's avatar
Christiaan Janssen committed
285
    palette.setColor(QPalette::WindowText, Qt::white);
Christiaan Janssen's avatar
Christiaan Janssen committed
286
287
288
289
    d->m_timeLabel->setPalette(palette);
    d->m_timeLabel->setIndent(10);
    updateTimeDisplay();
    layout->addWidget(d->m_timeLabel);
290

Christiaan Janssen's avatar
Christiaan Janssen committed
291
292
    toolbarWidget->setLayout(layout);

293
294
295
296
    // When the widgets are requested we assume that the session data
    // is available, then we can populate the file finder
    populateFileFinder();

Christiaan Janssen's avatar
Christiaan Janssen committed
297
    return toolbarWidget;
Christiaan Janssen's avatar
Christiaan Janssen committed
298
299
}

300
301
302
303
void QmlProfilerTool::populateFileFinder(QString projectDirectory, QString activeSysroot)
{
    // Initialize filefinder with some sensible default
    QStringList sourceFiles;
hjk's avatar
hjk committed
304
305
    QList<Project *> projects = SessionManager::projects();
    if (Project *startupProject = SessionManager::startupProject()) {
306
        // startup project first
hjk's avatar
hjk committed
307
        projects.removeOne(startupProject);
308
309
310
311
312
313
        projects.insert(0, startupProject);
    }
    foreach (Project *project, projects)
        sourceFiles << project->files(Project::ExcludeGeneratedFiles);

    if (!projects.isEmpty()) {
314
        if (projectDirectory.isEmpty())
315
            projectDirectory = projects.first()->projectDirectory().toString();
316
317
318
319
320
321
322
323
324
325
326
327
328

        if (activeSysroot.isEmpty()) {
            if (Target *target = projects.first()->activeTarget())
                if (RunConfiguration *rc = target->activeRunConfiguration())
                    activeSysroot = sysroot(rc);
        }
    }

    d->m_projectFinder.setProjectDirectory(projectDirectory);
    d->m_projectFinder.setProjectFiles(sourceFiles);
    d->m_projectFinder.setSysroot(activeSysroot);
}

329
void QmlProfilerTool::recordingButtonChanged(bool recording)
330
{
331
332
333
334
335
336
337
338
339
340
    if (recording && d->m_profilerState->currentState() == QmlProfilerStateManager::AppRunning) {
        if (checkForUnsavedNotes()) {
            clearData(); // clear right away, before the application starts
            d->m_profilerState->setClientRecording(true);
        } else {
            d->m_recordButton->setChecked(false);
        }
    } else {
        d->m_profilerState->setClientRecording(recording);
    }
341
342
343
344
}

void QmlProfilerTool::setRecording(bool recording)
{
Christiaan Janssen's avatar
Christiaan Janssen committed
345
    // update display
346
347
348
349
350
    d->m_recordButton->setToolTip( recording ? tr("Disable profiling") : tr("Enable profiling"));
    d->m_recordButton->setIcon(QIcon(recording ? QLatin1String(":/qmlprofiler/recordOn.png") :
                                                 QLatin1String(":/qmlprofiler/recordOff.png")));

    d->m_recordButton->setChecked(recording);
351
    d->m_profilerState->setClientRecording(recording);
Christiaan Janssen's avatar
Christiaan Janssen committed
352

Christiaan Janssen's avatar
Christiaan Janssen committed
353
354
355
356
357
358
359
360
    // manage timer
    if (d->m_profilerState->currentState() == QmlProfilerStateManager::AppRunning) {
        if (recording) {
            d->m_recordingTimer.start();
            d->m_recordingElapsedTime.start();
        } else {
            d->m_recordingTimer.stop();
        }
361
362
363
        d->m_recordButton->menu()->setEnabled(!recording);
    } else {
        d->m_recordButton->menu()->setEnabled(true);
Christiaan Janssen's avatar
Christiaan Janssen committed
364
    }
365
366
}

367
void QmlProfilerTool::gotoSourceLocation(const QString &fileUrl, int lineNumber, int columnNumber)
Christiaan Janssen's avatar
Christiaan Janssen committed
368
{
Kai Koehne's avatar
Kai Koehne committed
369
    if (lineNumber < 0 || fileUrl.isEmpty())
Christiaan Janssen's avatar
Christiaan Janssen committed
370
371
        return;

372
    const QString projectFileName = d->m_projectFinder.findFile(fileUrl);
Christiaan Janssen's avatar
Christiaan Janssen committed
373

374
375
376
377
    QFileInfo fileInfo(projectFileName);
    if (!fileInfo.exists() || !fileInfo.isReadable())
        return;

hjk's avatar
hjk committed
378
    IEditor *editor = EditorManager::openEditor(projectFileName);
379
    TextEditor::BaseTextEditor *textEditor = qobject_cast<TextEditor::BaseTextEditor*>(editor);
Christiaan Janssen's avatar
Christiaan Janssen committed
380
381

    if (textEditor) {
382
        EditorManager::addCurrentPositionToNavigationHistory();
383
384
385
        // textEditor counts columns starting with 0, but the ASTs store the
        // location starting with 1, therefore the -1 in the call to gotoLine
        textEditor->gotoLine(lineNumber, columnNumber - 1);
Christiaan Janssen's avatar
Christiaan Janssen committed
386
387
388
        textEditor->widget()->setFocus();
    }
}
Christiaan Janssen's avatar
Christiaan Janssen committed
389

Christiaan Janssen's avatar
Christiaan Janssen committed
390
void QmlProfilerTool::updateTimeDisplay()
391
{
Christiaan Janssen's avatar
Christiaan Janssen committed
392
393
394
395
    double seconds = 0;
    if (d->m_profilerState->serverRecording() &&
        d->m_profilerState->currentState() == QmlProfilerStateManager::AppRunning) {
            seconds = d->m_recordingElapsedTime.elapsed() / 1000.0;
396
397
    } else if (d->m_profilerModelManager->state() != QmlProfilerDataState::Empty &&
               d->m_profilerModelManager->state() != QmlProfilerDataState::ClearingData) {
Christiaan Janssen's avatar
Christiaan Janssen committed
398
        seconds = d->m_profilerModelManager->traceTime()->duration() / 1.0e9;
Christiaan Janssen's avatar
Christiaan Janssen committed
399
    }
400
    QString timeString = QString::number(seconds,'f',1);
Christiaan Janssen's avatar
Christiaan Janssen committed
401
402
    QString profilerTimeStr = QmlProfilerTool::tr("%1 s").arg(timeString, 6);
    d->m_timeLabel->setText(tr("Elapsed: %1").arg(profilerTimeStr));
403
404
}

Christiaan Janssen's avatar
Christiaan Janssen committed
405
void QmlProfilerTool::clearData()
406
{
Christiaan Janssen's avatar
Christiaan Janssen committed
407
    d->m_profilerModelManager->clear();
408
    d->m_profilerConnections->discardPendingData();
Christiaan Janssen's avatar
Christiaan Janssen committed
409
}
410

411
412
void QmlProfilerTool::clearDisplay()
{
Christiaan Janssen's avatar
Christiaan Janssen committed
413
414
415
    d->m_profilerConnections->clearBufferedData();
    d->m_viewContainer->clear();
    updateTimeDisplay();
416
417
}

418
static void startRemoteTool(IAnalyzerTool *tool, StartMode mode)
419
{
420
    Id kitId;
421
    quint16 port;
422
    Kit *kit = 0;
423
424

    {
hjk's avatar
hjk committed
425
        QSettings *settings = ICore::settings();
426

427
428
        kitId = Id::fromSetting(settings->value(QLatin1String("AnalyzerQmlAttachDialog/kitId")));
        port = settings->value(QLatin1String("AnalyzerQmlAttachDialog/port"), 3768).toUInt();
429
430
431

        QmlProfilerAttachDialog dialog;

432
        dialog.setKitId(kitId);
433
434
435
436
437
        dialog.setPort(port);

        if (dialog.exec() != QDialog::Accepted)
            return;

438
        kit = dialog.kit();
439
440
        port = dialog.port();

441
        settings->setValue(QLatin1String("AnalyzerQmlAttachDialog/kitId"), kit->id().toSetting());
442
443
        settings->setValue(QLatin1String("AnalyzerQmlAttachDialog/port"), port);
    }
444

445
446
    AnalyzerStartParameters sp;
    sp.startMode = mode;
447
448
449
450

    IDevice::ConstPtr device = DeviceKitInformation::device(kit);
    if (device) {
        sp.connParams = device->sshParameters();
hjk's avatar
hjk committed
451
        sp.analyzerHost = device->qmlProfilerHost();
452
453
    }
    sp.sysroot = SysRootKitInformation::sysRoot(kit).toString();
454
    sp.analyzerPort = port;
455

456
    AnalyzerRunControl *rc = tool->createRunControl(sp, 0);
457
    QObject::connect(AnalyzerManager::stopAction(), SIGNAL(triggered()), rc, SLOT(stopIt()));
458

459
    ProjectExplorerPlugin::startRunControl(rc, tool->runMode());
460
}
461

462
463
void QmlProfilerTool::startTool(StartMode mode)
{
464
465
466
467
468
469
470
    if (d->m_recordButton->isChecked()) {
        if (!checkForUnsavedNotes())
            return;
        else
            clearData(); // clear right away to suppress second warning on server recording change
    }

471
472
473
    // Make sure mode is shown.
    AnalyzerManager::showMode();

474
475
    if (mode == StartLocal) {
        // ### not sure if we're supposed to check if the RunConFiguration isEnabled
hjk's avatar
hjk committed
476
477
        Project *pro = SessionManager::startupProject();
        ProjectExplorerPlugin::instance()->runProject(pro, runMode());
478
479
480
    } else if (mode == StartRemote) {
        startRemoteTool(this, mode);
    }
481
}
482

483
void QmlProfilerTool::logState(const QString &msg)
484
{
hjk's avatar
hjk committed
485
    MessageManager::write(msg, MessageManager::Flash);
486
487
488
489
}

void QmlProfilerTool::logError(const QString &msg)
{
hjk's avatar
hjk committed
490
    MessageManager::write(msg);
491
}
492

Christiaan Janssen's avatar
Christiaan Janssen committed
493
494
void QmlProfilerTool::showErrorDialog(const QString &error)
{
hjk's avatar
hjk committed
495
    QMessageBox *errorDialog = new QMessageBox(ICore::mainWindow());
Christiaan Janssen's avatar
Christiaan Janssen committed
496
497
498
499
500
501
502
503
504
    errorDialog->setIcon(QMessageBox::Warning);
    errorDialog->setWindowTitle(tr("QML Profiler"));
    errorDialog->setText(error);
    errorDialog->setStandardButtons(QMessageBox::Ok);
    errorDialog->setDefaultButton(QMessageBox::Ok);
    errorDialog->setModal(false);
    errorDialog->show();
}

505
506
507
508
509
void QmlProfilerTool::showLoadOption()
{
    d->m_loadQmlTrace->setEnabled(!d->m_profilerState->serverRecording());
}

510
511
void QmlProfilerTool::showSaveOption()
{
Christiaan Janssen's avatar
Christiaan Janssen committed
512
    d->m_saveQmlTrace->setEnabled(!d->m_profilerModelManager->isEmpty());
513
514
}

515
516
void QmlProfilerTool::showSaveDialog()
{
hjk's avatar
hjk committed
517
    QString filename = QFileDialog::getSaveFileName(ICore::mainWindow(), tr("Save QML Trace"), QString(),
518
                                                    tr("QML traces (*%1)").arg(QLatin1String(TraceFileExtension)));
519
    if (!filename.isEmpty()) {
520
521
        if (!filename.endsWith(QLatin1String(TraceFileExtension)))
            filename += QLatin1String(TraceFileExtension);
Christiaan Janssen's avatar
Christiaan Janssen committed
522
        d->m_profilerModelManager->save(filename);
523
524
525
526
527
    }
}

void QmlProfilerTool::showLoadDialog()
{
528
529
530
    if (!checkForUnsavedNotes())
        return;

531
    if (ModeManager::currentMode()->id() != MODE_ANALYZE)
532
533
        AnalyzerManager::showMode();

534
    AnalyzerManager::selectTool(this, StartRemote);
535

hjk's avatar
hjk committed
536
    QString filename = QFileDialog::getOpenFileName(ICore::mainWindow(), tr("Load QML Trace"), QString(),
537
                                                    tr("QML traces (*%1)").arg(QLatin1String(TraceFileExtension)));
538
539
540

    if (!filename.isEmpty()) {
        // delayed load (prevent graphical artifacts due to long load time)
Christiaan Janssen's avatar
Christiaan Janssen committed
541
542
        d->m_profilerModelManager->setFilename(filename);
        QTimer::singleShot(100, d->m_profilerModelManager, SLOT(load()));
543
544
    }
}
545

546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
/*!
    Checks if we have unsaved notes. If so, shows a warning dialog. Returns true if we can continue
    with a potentially destructive operation and discard the warnings, or false if not. We don't
    want to show a save/discard dialog here because that will often result in a confusing series of
    different dialogs: first "save" and then immediately "load" or "connect".
 */
bool QmlProfilerTool::checkForUnsavedNotes()
{
    if (!d->m_profilerModelManager->notesModel()->isModified())
        return true;
    return QMessageBox::warning(QApplication::activeWindow(), tr("QML Profiler"),
                                tr("You are about to discard the profiling data, including unsaved "
                                   "notes. Do you want to continue?"),
                                QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes;
}

562
563
564
565
void QmlProfilerTool::clientsDisconnected()
{
    // If the application stopped by itself, check if we have all the data
    if (d->m_profilerState->currentState() == QmlProfilerStateManager::AppDying) {
Christiaan Janssen's avatar
Christiaan Janssen committed
566
        if (d->m_profilerModelManager->state() == QmlProfilerDataState::AcquiringData)
567
568
569
570
571
572
573
574
575
576
            d->m_profilerState->setCurrentState(QmlProfilerStateManager::AppKilled);
        else
            d->m_profilerState->setCurrentState(QmlProfilerStateManager::AppStopped);

        // ... and return to the "base" state
        d->m_profilerState->setCurrentState(QmlProfilerStateManager::Idle);
    }
    // If the connection is closed while the app is still running, no special action is needed
}

577
578
579
template<QmlDebug::ProfileFeature feature>
void QmlProfilerTool::updateFeaturesMenu(quint64 features)
{
580
    if (features & (1ULL << (feature))) {
581
582
583
584
        QAction *action = d->m_featuresMenu->addAction(tr(QmlProfilerModelManager::featureName(
                                               static_cast<QmlDebug::ProfileFeature>(feature))));
        action->setCheckable(true);
        action->setData(static_cast<uint>(feature));
585
        action->setChecked(d->m_profilerState->recordingFeatures() & (1ULL << (feature)));
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
    }
    updateFeaturesMenu<static_cast<QmlDebug::ProfileFeature>(feature + 1)>(features);
}

template<>
void QmlProfilerTool::updateFeaturesMenu<QmlDebug::MaximumProfileFeature>(quint64 features)
{
    Q_UNUSED(features);
    return;
}

void QmlProfilerTool::setAvailableFeatures(quint64 features)
{
    if (features != d->m_profilerState->recordingFeatures())
        d->m_profilerState->setRecordingFeatures(features); // by default, enable them all.
    if (d->m_featuresMenu) {
        d->m_featuresMenu->clear();
        updateFeaturesMenu<static_cast<QmlDebug::ProfileFeature>(0)>(features);
    }
}

Christiaan Janssen's avatar
Christiaan Janssen committed
607
void QmlProfilerTool::profilerDataModelStateChanged()
608
{
Christiaan Janssen's avatar
Christiaan Janssen committed
609
610
    switch (d->m_profilerModelManager->state()) {
    case QmlProfilerDataState::Empty :
611
612
        break;
    case QmlProfilerDataState::ClearingData :
Christiaan Janssen's avatar
Christiaan Janssen committed
613
614
        clearDisplay();
        break;
Christiaan Janssen's avatar
Christiaan Janssen committed
615
616
    case QmlProfilerDataState::AcquiringData :
    case QmlProfilerDataState::ProcessingData :
Christiaan Janssen's avatar
Christiaan Janssen committed
617
618
        // nothing to be done for these two
        break;
Christiaan Janssen's avatar
Christiaan Janssen committed
619
    case QmlProfilerDataState::Done :
Christiaan Janssen's avatar
Christiaan Janssen committed
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
        if (d->m_profilerState->currentState() == QmlProfilerStateManager::AppStopRequested)
            d->m_profilerState->setCurrentState(QmlProfilerStateManager::AppReadyToStop);
        showSaveOption();
        updateTimeDisplay();
    break;
    default:
        break;
    }
}

QList <QAction *> QmlProfilerTool::profilerContextMenuActions() const
{
    QList <QAction *> commonActions;
    commonActions << d->m_loadQmlTrace << d->m_saveQmlTrace;
    return commonActions;
}

void QmlProfilerTool::showNonmodalWarning(const QString &warningMsg)
{
hjk's avatar
hjk committed
639
    QMessageBox *noExecWarning = new QMessageBox(ICore::mainWindow());
Christiaan Janssen's avatar
Christiaan Janssen committed
640
641
642
643
644
645
646
647
648
649
650
    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();
}

QMessageBox *QmlProfilerTool::requestMessageBox()
{
hjk's avatar
hjk committed
651
    return new QMessageBox(ICore::mainWindow());
Christiaan Janssen's avatar
Christiaan Janssen committed
652
653
654
655
}

void QmlProfilerTool::handleHelpRequest(const QString &link)
{
656
    HelpManager::handleHelpRequest(link);
657
}
658

Christiaan Janssen's avatar
Christiaan Janssen committed
659
void QmlProfilerTool::profilerStateChanged()
660
{
Christiaan Janssen's avatar
Christiaan Janssen committed
661
    switch (d->m_profilerState->currentState()) {
662
663
664
665
666
667
    case QmlProfilerStateManager::AppDying : {
        // If already disconnected when dying, check again that all data was read
        if (!d->m_profilerConnections->isConnected())
            QTimer::singleShot(0, this, SLOT(clientsDisconnected()));
        break;
    }
Christiaan Janssen's avatar
Christiaan Janssen committed
668
    case QmlProfilerStateManager::AppKilled : {
Jarek Kobus's avatar
Jarek Kobus committed
669
        showNonmodalWarning(tr("Application finished before loading profiled data.\nPlease use the stop button instead."));
Christiaan Janssen's avatar
Christiaan Janssen committed
670
        d->m_profilerModelManager->clear();
671
672
        break;
    }
Christiaan Janssen's avatar
Christiaan Janssen committed
673
674
675
676
677
678
    case QmlProfilerStateManager::Idle :
        // when the app finishes, set recording display to client status
        setRecording(d->m_profilerState->clientRecording());
        break;
    default:
        // no special action needed for other states
679
680
        break;
    }
Christiaan Janssen's avatar
Christiaan Janssen committed
681
682
683
684
685
686
}

void QmlProfilerTool::clientRecordingChanged()
{
    // if application is running, display server record changes
    // if application is stopped, display client record changes
687
    if (d->m_profilerState->currentState() != QmlProfilerStateManager::AppRunning)
Christiaan Janssen's avatar
Christiaan Janssen committed
688
        setRecording(d->m_profilerState->clientRecording());
689
}
690

Christiaan Janssen's avatar
Christiaan Janssen committed
691
void QmlProfilerTool::serverRecordingChanged()
692
{
693
    showLoadOption();
Christiaan Janssen's avatar
Christiaan Janssen committed
694
695
696
    if (d->m_profilerState->currentState() == QmlProfilerStateManager::AppRunning) {
        // clear the old data each time we start a new profiling session
        if (d->m_profilerState->serverRecording()) {
697
698
699
700
701
702
703
704
705
706
707
708
            // We cannot stop it here, so we cannot give the usual yes/no dialog. Show a dialog
            // offering to immediately save the data instead.
            if (d->m_profilerModelManager->notesModel()->isModified() &&
                    QMessageBox::warning(QApplication::activeWindow(), tr("QML Profiler"),
                                         tr("Starting a new profiling session will discard the "
                                            "previous data, including unsaved notes.\nDo you want "
                                            "to save the data first?"),
                                         QMessageBox::Save, QMessageBox::Discard) ==
                    QMessageBox::Save)
                showSaveDialog();

            setRecording(true);
Christiaan Janssen's avatar
Christiaan Janssen committed
709
            d->m_clearButton->setEnabled(false);
Christiaan Janssen's avatar
Christiaan Janssen committed
710
            clearData();
Christiaan Janssen's avatar
Christiaan Janssen committed
711
712
            d->m_profilerModelManager->prepareForWriting();
        } else {
713
            setRecording(false);
Christiaan Janssen's avatar
Christiaan Janssen committed
714
            d->m_clearButton->setEnabled(true);
Christiaan Janssen's avatar
Christiaan Janssen committed
715
        }
Christiaan Janssen's avatar
Christiaan Janssen committed
716
717
    } else {
        d->m_clearButton->setEnabled(true);
718
719
    }
}
720
721
722
723
724
725

void QmlProfilerTool::toggleRecordingFeature(QAction *action)
{
    uint feature = action->data().toUInt();
    if (action->isChecked())
        d->m_profilerState->setRecordingFeatures(
726
                    d->m_profilerState->recordingFeatures() | (1ULL << feature));
727
728
    else
        d->m_profilerState->setRecordingFeatures(
729
                    d->m_profilerState->recordingFeatures() & (~(1ULL << feature)));
730
731
732
733
734
735
736

    // Keep the menu open to allow for more features to be toggled
    d->m_recordButton->showMenu();
}

}
}