From 585e651310edccae8acab8511cccd85e6c2fe74f Mon Sep 17 00:00:00 2001
From: Christiaan Janssen <christiaan.janssen@nokia.com>
Date: Mon, 13 Feb 2012 15:26:57 +0100
Subject: [PATCH] QmlProfiler: manage server-side start tracing message

also
QmlProfiler: refactor eventlist state

If unexpected data is received, assuming server stopped profiling.
Also, introducing a eventlist state instead of relying on
signals sent around.  This is part of a coming bigger patch
where the profiler client is refactored.

Change-Id: Ibed9007903956daf03cc0fcb90f77b5ad2d3cf90
Reviewed-by: Kai Koehne <kai.koehne@nokia.com>
---
 .../qmljsdebugclient/qmlprofilereventlist.cpp | 68 ++++++++++++++++---
 .../qmljsdebugclient/qmlprofilereventlist.h   | 15 ++--
 .../qmlprofilertraceclient.cpp                | 18 ++++-
 .../qmljsdebugclient/qmlprofilertraceclient.h |  1 +
 src/plugins/qmlprofiler/qml/Label.qml         | 18 +++--
 src/plugins/qmlprofiler/qml/MainView.qml      | 28 +++++---
 src/plugins/qmlprofiler/qml/Overview.qml      |  5 +-
 src/plugins/qmlprofiler/qmlprofilerengine.cpp |  9 ++-
 src/plugins/qmlprofiler/qmlprofilerengine.h   |  1 +
 .../qmlprofiler/qmlprofilereventview.cpp      | 27 ++++++--
 .../qmlprofiler/qmlprofilereventview.h        |  5 ++
 src/plugins/qmlprofiler/qmlprofilertool.cpp   | 38 +++++++----
 src/plugins/qmlprofiler/qmlprofilertool.h     |  2 +
 src/plugins/qmlprofiler/tracewindow.cpp       | 64 ++++++++++++++---
 src/plugins/qmlprofiler/tracewindow.h         | 11 ++-
 15 files changed, 246 insertions(+), 64 deletions(-)

diff --git a/src/libs/qmljsdebugclient/qmlprofilereventlist.cpp b/src/libs/qmljsdebugclient/qmlprofilereventlist.cpp
index 3d5327bbc19..b49976cb076 100644
--- a/src/libs/qmljsdebugclient/qmlprofilereventlist.cpp
+++ b/src/libs/qmljsdebugclient/qmlprofilereventlist.cpp
@@ -267,6 +267,8 @@ public:
 
     QmlProfilerEventList *q;
 
+    QmlProfilerEventList::State m_state;
+
     // convenience functions
     void clearQmlRootEvent();
     void clearV8RootEvent();
@@ -309,6 +311,8 @@ QmlProfilerEventList::QmlProfilerEventList(QObject *parent) :
 {
     setObjectName("QmlProfilerEventStatistics");
 
+    d->m_state = Empty;
+
     d->m_traceEndTime = 0;
     d->m_traceStartTime = -1;
     d->m_qmlMeasuredTime = 0;
@@ -357,7 +361,7 @@ void QmlProfilerEventList::clear()
     d->m_minimumAnimationCount = 0;
 
     emit countChanged();
-    emit dataClear();
+    setState(Empty);
 }
 
 QList <QmlEventData *> QmlProfilerEventList::getEventDescriptions() const
@@ -395,7 +399,7 @@ void QmlProfilerEventList::addRangedEvent(int type, qint64 startTime, qint64 len
     QString displayName, eventHashStr, details;
     QmlJsDebugClient::QmlEventLocation eventLocation = location;
 
-    emit processingData();
+    setState(AcquiringData);
 
     // generate details string
     if (data.isEmpty())
@@ -466,11 +470,14 @@ void QmlProfilerEventList::addV8Event(int depth, const QString &function, const
     QString displayName = filename.mid(filename.lastIndexOf(QLatin1Char('/')) + 1) + QLatin1Char(':') + QString::number(lineNumber);
     QV8EventData *eventData = 0;
 
+    setState(AcquiringData);
+
     // time is given in milliseconds, but internally we store it in microseconds
     totalTime *= 1e6;
     selfTime *= 1e6;
 
     // cumulate information
+    // TODO: use hashes
     foreach (QV8EventData *v8event, d->m_v8EventList) {
         if (v8event->displayName == displayName && v8event->functionName == function) {
             eventData = v8event;
@@ -529,7 +536,7 @@ void QmlProfilerEventList::addFrameEvent(qint64 time, int framerate, int animati
 {
     QString displayName, eventHashStr, details;
 
-    emit processingData();
+    setState(AcquiringData);
 
     details = tr("Animation Timer Update");
     displayName = tr("<Animation Update>");
@@ -636,7 +643,7 @@ void QmlProfilerEventList::setTraceStartTime( qint64 time )
 
 void QmlProfilerEventList::complete()
 {
-    emit postProcessing();
+    setState(ProcessingData);
     d->collectV8Statistics();
     postProcess();
 }
@@ -1030,9 +1037,10 @@ void QmlProfilerEventList::postProcess()
         reloadDetails();
         compileStatistics(traceStartTime(), traceEndTime());
         prepareForDisplay();
+        setState(Done);
+    } else {
+        setState(Empty);
     }
-    // data is ready even when there's no data
-    emit dataReady();
 }
 
 void QmlProfilerEventList::linkEndsToStarts()
@@ -1398,7 +1406,7 @@ void QmlProfilerEventList::load()
         return;
     }
 
-    emit processingData();
+    setState(AcquiringData);
 
     // erase current
     clear();
@@ -1645,8 +1653,6 @@ void QmlProfilerEventList::load()
 
     if (!validVersion) {
         clear();
-        emit countChanged();
-        emit dataReady();
         emit error(tr("Invalid version of QML Trace file."));
         return;
     }
@@ -1697,7 +1703,7 @@ void QmlProfilerEventList::load()
 
     descriptionBuffer.clear();
 
-    emit postProcessing();
+    setState(ProcessingData);
     d->collectV8Statistics();
     postProcess();
 }
@@ -1830,4 +1836,46 @@ int QmlProfilerEventList::eventPosInType(int index) const
     return d->m_typeCounts[eventType]->eventIds.indexOf(d->m_startTimeSortedList[index].description->eventId);
 }
 
+/////////////////////////////////////////
+QmlProfilerEventList::State QmlProfilerEventList::currentState() const
+{
+    return d->m_state;
+}
+
+int QmlProfilerEventList::getCurrentStateFromQml() const
+{
+    return (int)d->m_state;
+}
+
+void QmlProfilerEventList::setState(QmlProfilerEventList::State state)
+{
+    // It's not an error, we are continuously calling "AcquiringData" for example
+    if (d->m_state == state)
+        return;
+
+    switch (state) {
+        case Empty:
+            // if it's not empty, complain but go on
+            QTC_ASSERT(count() == 0, /**/);
+        break;
+        case AcquiringData:
+            // we're not supposed to receive new data while processing older data
+            QTC_ASSERT(d->m_state != ProcessingData, return);
+        break;
+        case ProcessingData:
+            QTC_ASSERT(d->m_state == AcquiringData, return);
+        break;
+        case Done:
+            QTC_ASSERT(d->m_state == ProcessingData, return);
+        break;
+        default:
+        qDebug() << "Trying to set unknown state in events list at" << __FILE__ << __LINE__;
+        break;
+    }
+
+    d->m_state = state;
+    emit stateChanged();
+    return;
+}
+
 } // namespace QmlJsDebugClient
diff --git a/src/libs/qmljsdebugclient/qmlprofilereventlist.h b/src/libs/qmljsdebugclient/qmlprofilereventlist.h
index ab44900870c..32f5203c2a0 100644
--- a/src/libs/qmljsdebugclient/qmlprofilereventlist.h
+++ b/src/libs/qmljsdebugclient/qmlprofilereventlist.h
@@ -115,6 +115,12 @@ class QMLJSDEBUGCLIENT_EXPORT QmlProfilerEventList : public QObject
 {
     Q_OBJECT
 public:
+    enum State {
+        Empty,
+        AcquiringData,
+        ProcessingData,
+        Done
+    };
 
     explicit QmlProfilerEventList(QObject *parent = 0);
     ~QmlProfilerEventList();
@@ -167,13 +173,13 @@ public:
 
     void showErrorDialog(const QString &st ) const;
     void compileStatistics(qint64 startTime, qint64 endTime);
+    State currentState() const;
+    Q_INVOKABLE int getCurrentStateFromQml() const;
+
 signals:
-    void dataReady();
+    void stateChanged();
     void countChanged();
     void error(const QString &error);
-    void dataClear();
-    void processingData();
-    void postProcessing();
 
     void requestDetailsForLocation(int eventType, const QmlJsDebugClient::QmlEventLocation &location);
     void detailsChanged(int eventId, const QString &newString);
@@ -212,6 +218,7 @@ private:
     void reloadDetails();
     void findBindingLoops(qint64 startTime, qint64 endTime);
     bool checkBindingLoop(QmlEventData *from, QmlEventData *current, QList<QmlEventData *>visited);
+    void setState(State state);
 
 private:
     class QmlProfilerEventListPrivate;
diff --git a/src/libs/qmljsdebugclient/qmlprofilertraceclient.cpp b/src/libs/qmljsdebugclient/qmlprofilertraceclient.cpp
index 89586779359..cc130bba736 100644
--- a/src/libs/qmljsdebugclient/qmlprofilertraceclient.cpp
+++ b/src/libs/qmljsdebugclient/qmlprofilertraceclient.cpp
@@ -89,6 +89,11 @@ QmlProfilerTraceClient::~QmlProfilerTraceClient()
 void QmlProfilerTraceClient::clearData()
 {
     ::memset(d->rangeCount, 0, MaximumQmlEventType * sizeof(int));
+    for (int eventType = 0; eventType < MaximumQmlEventType; eventType++) {
+        d->rangeDatas[eventType].clear();
+        d->rangeLocations[eventType].clear();
+        d->rangeStartTimes[eventType].clear();
+    }
     emit cleared();
 }
 
@@ -121,6 +126,14 @@ void QmlProfilerTraceClient::setRecording(bool v)
     emit recordingChanged(v);
 }
 
+void QmlProfilerTraceClient::setRecordingFromServer(bool v)
+{
+    if (v == d->recording)
+        return;
+    d->recording = v;
+    emit recordingChanged(v);
+}
+
 void QmlProfilerTraceClient::statusChanged(Status /*status*/)
 {
     emit enabledChanged();
@@ -136,8 +149,6 @@ void QmlProfilerTraceClient::messageReceived(const QByteArray &data)
 
     stream >> time >> messageType;
 
-//    qDebug() << __FUNCTION__ << messageType;
-
     if (messageType >= MaximumMessage)
         return;
 
@@ -158,6 +169,9 @@ void QmlProfilerTraceClient::messageReceived(const QByteArray &data)
             emit this->frame(time, frameRate, animationCount);
             d->maximumTime = qMax(time, d->maximumTime);
         } else if (event == StartTrace) {
+            // special: StartTrace is now asynchronous
+            if (!d->recording)
+                setRecordingFromServer(true);
             emit this->traceStarted(time);
             d->maximumTime = time;
         } else if (event < MaximumEventType) {
diff --git a/src/libs/qmljsdebugclient/qmlprofilertraceclient.h b/src/libs/qmljsdebugclient/qmlprofilertraceclient.h
index 37309ce715e..4d37c067e36 100644
--- a/src/libs/qmljsdebugclient/qmlprofilertraceclient.h
+++ b/src/libs/qmljsdebugclient/qmlprofilertraceclient.h
@@ -83,6 +83,7 @@ public:
 
 public slots:
     void setRecording(bool);
+    void setRecordingFromServer(bool);
     void clearData();
     void sendRecordingStatus();
 
diff --git a/src/plugins/qmlprofiler/qml/Label.qml b/src/plugins/qmlprofiler/qml/Label.qml
index a3d8a1d792f..0d89c057abd 100644
--- a/src/plugins/qmlprofiler/qml/Label.qml
+++ b/src/plugins/qmlprofiler/qml/Label.qml
@@ -81,12 +81,18 @@ Item {
     Connections {
         target: qmlEventList
         onReloadDetailLabels: getDescriptions();
-        onDataReady: getDescriptions();
-        onDataClear: {
-            descriptions = [];
-            eventIds = [];
-            extdescriptions = [];
-            updateHeight();
+        onStateChanged: {
+            // Empty
+            if (qmlEventList.getCurrentStateFromQml() == 0) {
+                descriptions = [];
+                eventIds = [];
+                extdescriptions = [];
+                updateHeight();
+            } else
+            // Done
+            if (qmlEventList.getCurrentStateFromQml() == 3) {
+                getDescriptions();
+            }
         }
     }
 
diff --git a/src/plugins/qmlprofiler/qml/MainView.qml b/src/plugins/qmlprofiler/qml/MainView.qml
index 557d8279e6b..6aa07199669 100644
--- a/src/plugins/qmlprofiler/qml/MainView.qml
+++ b/src/plugins/qmlprofiler/qml/MainView.qml
@@ -111,23 +111,29 @@ Rectangle {
                 root.progress = 0;
             }
         }
-
-        onProcessingData: {
-            root.dataAvailable = false;
-        }
-
-        onPostProcessing: {
-            root.progress = 0.9; // jump to 90%
-        }
-
-        onDataReady: {
-            if (eventCount > 0) {
+        onStateChanged: {
+            switch (qmlEventList.getCurrentStateFromQml()) {
+            case 0: {
+                root.clearAll();
+                break;
+            }
+            case 1: {
+                root.dataAvailable = false;
+                break;
+            }
+            case 2: {
+                root.progress = 0.9; // jump to 90%
+                break;
+            }
+            case 3: {
                 view.clearData();
                 progress = 1.0;
                 dataAvailable = true;
                 view.visible = true;
                 view.requestPaint();
                 zoomControl.setRange(qmlEventList.traceStartTime(), qmlEventList.traceStartTime() + qmlEventList.traceDuration()/10);
+                break;
+            }
             }
         }
     }
diff --git a/src/plugins/qmlprofiler/qml/Overview.qml b/src/plugins/qmlprofiler/qml/Overview.qml
index 5d0a7496feb..55265021690 100644
--- a/src/plugins/qmlprofiler/qml/Overview.qml
+++ b/src/plugins/qmlprofiler/qml/Overview.qml
@@ -77,8 +77,9 @@ Canvas2D {
 
     Connections {
         target: qmlEventList
-        onDataReady: {
-            if (qmlEventList.count() > 0) {
+        onStateChanged: {
+            // State is "done"
+            if (qmlEventList.getCurrentStateFromQml() == 3) {
                 dataAvailable = true;
                 requestRedraw();
             }
diff --git a/src/plugins/qmlprofiler/qmlprofilerengine.cpp b/src/plugins/qmlprofiler/qmlprofilerengine.cpp
index f485dec462f..6a835ac52aa 100644
--- a/src/plugins/qmlprofiler/qmlprofilerengine.cpp
+++ b/src/plugins/qmlprofiler/qmlprofilerengine.cpp
@@ -217,15 +217,15 @@ bool QmlProfilerEngine::start()
 
 void QmlProfilerEngine::stop()
 {
-    // keep the flag for the next restart
-    d->m_fetchDataFromStart = d->m_fetchingData;
     if (d->m_fetchingData) {
         if (d->m_running)
             d->m_delayedDelete = true;
         // will result in dataReceived() call
         emit stopRecording();
+        d->m_fetchDataFromStart = true;
     } else {
         finishProcess();
+        d->m_fetchDataFromStart = false;
     }
 }
 
@@ -242,8 +242,9 @@ void QmlProfilerEngine::stopped()
 
     d->m_running = false;
     d->m_runningTimer.stop();
-    AnalyzerManager::stopTool(); // FIXME: Needed?
+    AnalyzerManager::stopTool();
     emit finished();
+    emit recordingChanged(d->m_fetchDataFromStart);
 }
 
 void QmlProfilerEngine::setFetchingData(bool b)
@@ -269,6 +270,7 @@ void QmlProfilerEngine::finishProcess()
         if (d->m_runner)
             d->m_runner->stop();
         emit finished();
+        emit recordingChanged(d->m_fetchDataFromStart);
     }
 }
 
@@ -299,6 +301,7 @@ void QmlProfilerEngine::wrongSetupMessageBox(const QString &errorMessage)
     d->m_runningTimer.stop();
     AnalyzerManager::stopTool();
     emit finished();
+    emit recordingChanged(d->m_fetchDataFromStart);
 }
 
 void QmlProfilerEngine::wrongSetupMessageBoxFinished(int button)
diff --git a/src/plugins/qmlprofiler/qmlprofilerengine.h b/src/plugins/qmlprofiler/qmlprofilerengine.h
index 68c6e1279b3..017ffd050ba 100644
--- a/src/plugins/qmlprofiler/qmlprofilerengine.h
+++ b/src/plugins/qmlprofiler/qmlprofilerengine.h
@@ -54,6 +54,7 @@ signals:
     void processRunning(int port);
     void stopRecording();
     void timeUpdate();
+    void recordingChanged(bool recording);
 
 public slots:
     bool start();
diff --git a/src/plugins/qmlprofiler/qmlprofilereventview.cpp b/src/plugins/qmlprofiler/qmlprofilereventview.cpp
index 869f0572def..b59fdc4c7a2 100644
--- a/src/plugins/qmlprofiler/qmlprofilereventview.cpp
+++ b/src/plugins/qmlprofiler/qmlprofilereventview.cpp
@@ -123,9 +123,9 @@ QmlProfilerEventsWidget::QmlProfilerEventsWidget(QmlJsDebugClient::QmlProfilerEv
     groupLayout->addWidget(splitterVertical);
     setLayout(groupLayout);
 
+    m_eventStatistics = model;
     if (model) {
-        connect(model,SIGNAL(dataReady()),m_eventChildren,SLOT(clear()));
-        connect(model,SIGNAL(dataReady()),m_eventParents,SLOT(clear()));
+        connect(model, SIGNAL(stateChanged()), this, SLOT(eventListStateChanged()));
     }
 
     m_globalStatsEnabled = true;
@@ -135,6 +135,16 @@ QmlProfilerEventsWidget::~QmlProfilerEventsWidget()
 {
 }
 
+void QmlProfilerEventsWidget::eventListStateChanged()
+{
+    if (m_eventStatistics) {
+        QmlProfilerEventList::State newState = m_eventStatistics->currentState();
+        if (newState == QmlProfilerEventList::Empty) {
+            clear();
+        }
+    }
+}
+
 void QmlProfilerEventsWidget::switchToV8View()
 {
     setObjectName("QmlProfilerV8ProfileView");
@@ -278,16 +288,25 @@ QmlProfilerEventsMainView::~QmlProfilerEventsMainView()
 void QmlProfilerEventsMainView::setEventStatisticsModel( QmlProfilerEventList *model )
 {
     if (d->m_eventStatistics) {
-        disconnect(d->m_eventStatistics,SIGNAL(dataReady()),this,SLOT(buildModel()));
+        disconnect(d->m_eventStatistics,SIGNAL(stateChanged()),this,SLOT(eventListStateChanged()));
         disconnect(d->m_eventStatistics,SIGNAL(detailsChanged(int,QString)),this,SLOT(changeDetailsForEvent(int,QString)));
     }
     d->m_eventStatistics = model;
     if (model) {
-        connect(d->m_eventStatistics,SIGNAL(dataReady()),this,SLOT(buildModel()));
+        connect(d->m_eventStatistics,SIGNAL(stateChanged()),this,SLOT(eventListStateChanged()));
         connect(d->m_eventStatistics,SIGNAL(detailsChanged(int,QString)),this,SLOT(changeDetailsForEvent(int,QString)));
     }
 }
 
+void QmlProfilerEventsMainView::eventListStateChanged()
+{
+    if (d->m_eventStatistics) {
+        QmlProfilerEventList::State newState = d->m_eventStatistics->currentState();
+        if (newState == QmlProfilerEventList::Done)
+            buildModel();
+    }
+}
+
 void QmlProfilerEventsMainView::setFieldViewable(Fields field, bool show)
 {
     if (field < MaxFields) {
diff --git a/src/plugins/qmlprofiler/qmlprofilereventview.h b/src/plugins/qmlprofiler/qmlprofilereventview.h
index 797dc806091..1c7c87d7f24 100644
--- a/src/plugins/qmlprofiler/qmlprofilereventview.h
+++ b/src/plugins/qmlprofiler/qmlprofilereventview.h
@@ -84,6 +84,9 @@ public slots:
     void updateSelectedEvent(int eventId) const;
     void selectBySourceLocation(const QString &filename, int line, int column);
 
+private slots:
+    void eventListStateChanged();
+
 protected:
     void contextMenuEvent(QContextMenuEvent *ev);
 
@@ -91,6 +94,7 @@ private:
     QmlProfilerEventsMainView *m_eventTree;
     QmlProfilerEventsParentsAndChildrenView *m_eventChildren;
     QmlProfilerEventsParentsAndChildrenView *m_eventParents;
+    QmlJsDebugClient::QmlProfilerEventList *m_eventStatistics;
 
     bool m_globalStatsEnabled;
 };
@@ -153,6 +157,7 @@ signals:
     void showEventInTimeline(int eventId);
 
 public slots:
+    void eventListStateChanged();
     void clear();
     void jumpToItem(const QModelIndex &index);
     void selectEvent(int eventId);
diff --git a/src/plugins/qmlprofiler/qmlprofilertool.cpp b/src/plugins/qmlprofiler/qmlprofilertool.cpp
index 0e10042c29d..cabfd6bc418 100644
--- a/src/plugins/qmlprofiler/qmlprofilertool.cpp
+++ b/src/plugins/qmlprofiler/qmlprofilertool.cpp
@@ -368,6 +368,7 @@ IAnalyzerEngine *QmlProfilerTool::createEngine(const AnalyzerStartParameters &sp
     connect(engine, SIGNAL(finished()), this, SLOT(disconnectClient()));
     connect(engine, SIGNAL(finished()), this, SLOT(updateTimers()));
     connect(engine, SIGNAL(stopRecording()), this, SLOT(stopRecording()));
+    connect(engine, SIGNAL(recordingChanged(bool)), this, SLOT(setRecording(bool)));
     connect(engine, SIGNAL(timeUpdate()), this, SLOT(updateTimers()));
     connect(d->m_traceWindow, SIGNAL(viewUpdated()), engine, SLOT(dataReceived()));
     connect(this, SIGNAL(connectionFailed()), engine, SLOT(finishProcess()));
@@ -456,9 +457,9 @@ QWidget *QmlProfilerTool::createWidgets()
     connect(d->m_traceWindow, SIGNAL(gotoSourceLocation(QString,int,int)),this, SLOT(gotoSourceLocation(QString,int,int)));
     connect(d->m_traceWindow, SIGNAL(contextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint)));
     connect(d->m_traceWindow->getEventList(), SIGNAL(error(QString)), this, SLOT(showErrorDialog(QString)));
-    connect(d->m_traceWindow->getEventList(), SIGNAL(dataReady()), this, SLOT(showSaveOption()));
-    connect(d->m_traceWindow->getEventList(), SIGNAL(dataReady()), this, SLOT(updateTimers()));
+    connect(d->m_traceWindow->getEventList(), SIGNAL(stateChanged()), this, SLOT(eventListStateChanged()));
     connect(d->m_traceWindow, SIGNAL(profilerStateChanged(bool,bool)), this, SLOT(profilerStateChanged(bool,bool)));
+    connect(d->m_traceWindow, SIGNAL(recordingChanged(bool)), this, SLOT(setRecording(bool)));
 
     d->m_eventsView = new QmlProfilerEventsWidget(d->m_traceWindow->getEventList(), mw);
     connect(d->m_eventsView, SIGNAL(gotoSourceLocation(QString,int,int)), this, SLOT(gotoSourceLocation(QString,int,int)));
@@ -500,11 +501,11 @@ QWidget *QmlProfilerTool::createWidgets()
     layout->setSpacing(0);
 
     d->m_recordButton = new QToolButton(toolbarWidget);
-    // icon and tooltip set in setRecording(), called later
     d->m_recordButton->setCheckable(true);
 
-    connect(d->m_recordButton,SIGNAL(toggled(bool)), this, SLOT(setRecording(bool)));
+    connect(d->m_recordButton,SIGNAL(clicked(bool)), this, SLOT(recordingButtonChanged(bool)));
     d->m_recordButton->setChecked(true);
+    setRecording(d->m_recordingEnabled);
     layout->addWidget(d->m_recordButton);
 
     d->m_clearButton = new QToolButton(toolbarWidget);
@@ -583,20 +584,25 @@ void QmlProfilerTool::stopRecording()
         emit cancelRun();
 }
 
-void QmlProfilerTool::setRecording(bool recording)
+void QmlProfilerTool::recordingButtonChanged(bool recording)
 {
-    d->m_recordingEnabled = recording;
-
-    // update record button
-    d->m_recordButton->setToolTip( d->m_recordingEnabled ? tr("Disable profiling") : tr("Enable profiling"));
-    d->m_recordButton->setIcon(QIcon(d->m_recordingEnabled ? QLatin1String(":/qmlprofiler/recordOn.png") :
-                                                             QLatin1String(":/qmlprofiler/recordOff.png")));
-
     if (recording)
         startRecording();
     else
         stopRecording();
 
+    setRecording(recording);
+}
+
+void QmlProfilerTool::setRecording(bool recording)
+{
+    // update record button
+    d->m_recordingEnabled = recording;
+    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);
     updateTimers();
 }
 
@@ -886,3 +892,11 @@ void QmlProfilerTool::retryMessageBoxFinished(int result)
     }
     }
 }
+
+void QmlProfilerTool::eventListStateChanged()
+{
+    if (d->m_traceWindow->getEventList()->currentState() == QmlProfilerEventList::Done) {
+        showSaveOption();
+        updateTimers();
+    }
+}
diff --git a/src/plugins/qmlprofiler/qmlprofilertool.h b/src/plugins/qmlprofiler/qmlprofilertool.h
index 728496c6346..3b53e5d7274 100644
--- a/src/plugins/qmlprofiler/qmlprofilertool.h
+++ b/src/plugins/qmlprofiler/qmlprofilertool.h
@@ -78,6 +78,7 @@ public slots:
 
     void startRecording();
     void stopRecording();
+    void recordingButtonChanged(bool recording);
     void setRecording(bool recording);
 
     void setAppIsRunning();
@@ -106,6 +107,7 @@ private slots:
     void showLoadDialog();
     void showErrorDialog(const QString &error);
     void retryMessageBoxFinished(int result);
+    void eventListStateChanged();
 
 private:
     void connectToClient();
diff --git a/src/plugins/qmlprofiler/tracewindow.cpp b/src/plugins/qmlprofiler/tracewindow.cpp
index 22e481e60fd..0e4953180c6 100644
--- a/src/plugins/qmlprofiler/tracewindow.cpp
+++ b/src/plugins/qmlprofiler/tracewindow.cpp
@@ -138,7 +138,7 @@ TraceWindow::TraceWindow(QWidget *parent)
     connect(this, SIGNAL(traceFinished(qint64)), m_eventList, SLOT(setTraceEndTime(qint64)));
     connect(this, SIGNAL(traceStarted(qint64)), m_eventList, SLOT(setTraceStartTime(qint64)));
     connect(this, SIGNAL(frameEvent(qint64,int,int)), m_eventList, SLOT(addFrameEvent(qint64,int,int)));
-    connect(this,SIGNAL(viewUpdated()), m_eventList, SLOT(complete()));
+    connect(m_eventList, SIGNAL(stateChanged()), this, SLOT(eventListStateChanged()));
     m_mainView->rootContext()->setContextProperty("qmlEventList", m_eventList);
     m_overview->rootContext()->setContextProperty("qmlEventList", m_eventList);
 
@@ -319,10 +319,11 @@ void TraceWindow::connectClientSignals()
         connect(m_plugin.data(), SIGNAL(range(int,qint64,qint64,QStringList,QmlJsDebugClient::QmlEventLocation)),
                 this, SIGNAL(range(int,qint64,qint64,QStringList,QmlJsDebugClient::QmlEventLocation)));
         connect(m_plugin.data(), SIGNAL(traceFinished(qint64)), this, SIGNAL(traceFinished(qint64)));
-        connect(m_plugin.data(), SIGNAL(traceStarted(qint64)), this, SIGNAL(traceStarted(qint64)));
+        connect(m_plugin.data(), SIGNAL(traceStarted(qint64)), this, SLOT(manageTraceStart(qint64)));
         connect(m_plugin.data(), SIGNAL(frame(qint64,int,int)), this, SIGNAL(frameEvent(qint64,int,int)));
         connect(m_plugin.data(), SIGNAL(enabledChanged()), this, SLOT(updateProfilerState()));
         connect(m_plugin.data(), SIGNAL(enabledChanged()), m_plugin.data(), SLOT(sendRecordingStatus()));
+        connect(m_plugin.data(), SIGNAL(recordingChanged(bool)), this, SIGNAL(recordingChanged(bool)));
     }
     if (m_v8plugin) {
         connect(m_v8plugin.data(), SIGNAL(complete()), this, SLOT(v8Complete()));
@@ -339,9 +340,10 @@ void TraceWindow::disconnectClientSignals()
         disconnect(m_plugin.data(), SIGNAL(range(int,qint64,qint64,QStringList,QmlJsDebugClient::QmlEventLocation)),
                 this, SIGNAL(range(int,qint64,qint64,QStringList,QmlJsDebugClient::QmlEventLocation)));
         disconnect(m_plugin.data(), SIGNAL(traceFinished(qint64)), this, SIGNAL(traceFinished(qint64)));
-        disconnect(m_plugin.data(), SIGNAL(traceStarted(qint64)), this, SIGNAL(traceStarted(qint64)));
+        disconnect(m_plugin.data(), SIGNAL(traceStarted(qint64)), this, SLOT(manageTraceStart(qint64)));
         disconnect(m_plugin.data(), SIGNAL(enabledChanged()), this, SLOT(updateProfilerState()));
         disconnect(m_plugin.data(), SIGNAL(enabledChanged()), m_plugin.data(), SLOT(sendRecordingStatus()));
+        disconnect(m_plugin.data(), SIGNAL(recordingChanged(bool)), this, SIGNAL(recordingChanged(bool)));
     }
     if (m_v8plugin) {
         disconnect(m_v8plugin.data(), SIGNAL(complete()), this, SLOT(v8Complete()));
@@ -378,6 +380,15 @@ void TraceWindow::updateTimer()
     m_profiledTime = m_mainView->rootObject()->property("elapsedTime").toDouble();
 }
 
+void TraceWindow::correctTimer()
+{
+    // once the data is post-processed, use the eventlist time instead of the qml timer
+    m_profiledTime = (m_eventList->traceEndTime() - m_eventList->traceStartTime()) / 1.0e9;
+    if (m_profiledTime < 0)
+        m_profiledTime = 0;
+    emit viewUpdated();
+}
+
 double TraceWindow::profiledTime() const
 {
     return m_profiledTime;
@@ -463,11 +474,11 @@ bool TraceWindow::isRecording() const
 void TraceWindow::qmlComplete()
 {
     m_qmlDataReady = true;
-
     if (!m_v8plugin || m_v8plugin.data()->status() != QDeclarativeDebugClient::Enabled || m_v8DataReady) {
-        emit viewUpdated();
-        // once complete is sent, reset the flag
+        m_eventList->complete();
+        // once complete is sent, reset the flags
         m_qmlDataReady = false;
+        m_v8DataReady = false;
     }
 }
 
@@ -475,9 +486,10 @@ void TraceWindow::v8Complete()
 {
     m_v8DataReady = true;
     if (!m_plugin || m_plugin.data()->status() != QDeclarativeDebugClient::Enabled || m_qmlDataReady) {
-        emit viewUpdated();
-        // once complete is sent, reset the flag
+        m_eventList->complete();
+        // once complete is sent, reset the flags
         m_v8DataReady = false;
+        m_qmlDataReady = false;
     }
 }
 
@@ -591,5 +603,41 @@ void TraceWindow::updateVerticalScroll(int newPosition)
     m_mainView->verticalScrollBar()->setValue(newPosition);
 }
 
+void TraceWindow::eventListStateChanged()
+{
+    switch (m_eventList->currentState()) {
+    case QmlProfilerEventList::Empty :
+        clearDisplay();
+        break;
+    case QmlProfilerEventList::AcquiringData :
+        firstDataReceived();
+        break;
+    case QmlProfilerEventList::ProcessingData :
+        // nothing to be done
+        break;
+    case QmlProfilerEventList::Done :
+        correctTimer();
+    break;
+    default:
+        break;
+    }
+}
+
+void TraceWindow::manageTraceStart(qint64 traceStart)
+{
+    // new trace started
+    clearDisplay();
+
+    emit traceStarted(traceStart);
+}
+
+void TraceWindow::firstDataReceived()
+{
+    if (m_plugin && m_plugin.data()->isRecording()) {
+        // serverside recording disabled
+        m_plugin.data()->setRecordingFromServer(false);
+    }
+}
+
 } // namespace Internal
 } // namespace QmlProfiler
diff --git a/src/plugins/qmlprofiler/tracewindow.h b/src/plugins/qmlprofiler/tracewindow.h
index b357d1e6038..80134c7c66d 100644
--- a/src/plugins/qmlprofiler/tracewindow.h
+++ b/src/plugins/qmlprofiler/tracewindow.h
@@ -110,9 +110,12 @@ public:
     double profiledTime() const;
 
 public slots:
+    void clearDisplay();
+    void selectNextEvent(int eventId);
+
+private slots:
     void updateCursorPosition();
     void updateTimer();
-    void clearDisplay();
     void updateToolbar();
     void toggleRangeMode(bool);
     void toggleLockMode(bool);
@@ -124,10 +127,13 @@ public slots:
 
     void qmlComplete();
     void v8Complete();
-    void selectNextEvent(int eventId);
     void updateProfilerState();
     void updateToolTip(const QString &text);
     void updateVerticalScroll(int newPosition);
+    void eventListStateChanged();
+    void manageTraceStart(qint64 traceStart);
+    void firstDataReceived();
+    void correctTimer();
 
 signals:
     void viewUpdated();
@@ -139,6 +145,7 @@ signals:
     void traceFinished(qint64);
     void traceStarted(qint64);
     void frameEvent(qint64, int, int);
+    void recordingChanged(bool);
 
     void internalClearDisplay();
     void jumpToPrev();
-- 
GitLab