From ffc11d5e9e1cc7b38ac7f87bfdf40f8a84aec939 Mon Sep 17 00:00:00 2001
From: Ulf Hermann <ulf.hermann@theqtcompany.com>
Date: Wed, 18 Nov 2015 12:34:52 +0100
Subject: [PATCH] QmlProfiler: allow for multiple sequential traces to be
 aggregated

Task-number: QTCREATORBUG-13317
Change-Id: Ic7d1d5c94d8d522741b6c4207a21a43f521da5fb
Reviewed-by: Joerg Bornemann <joerg.bornemann@theqtcompany.com>
---
 .../qmlprofiler/qmlprofilerclientmanager.cpp  | 14 ++++++++++-
 .../qmlprofiler/qmlprofilerclientmanager.h    |  3 +++
 .../qmlprofiler/qmlprofilerconfigwidget.cpp   |  4 ++++
 .../qmlprofiler/qmlprofilerconfigwidget.ui    | 17 +++++++++++++
 .../qmlprofiler/qmlprofilerconstants.h        |  1 +
 .../qmlprofiler/qmlprofilersettings.cpp       | 16 +++++++++++++
 src/plugins/qmlprofiler/qmlprofilersettings.h |  4 ++++
 .../qmlprofiler/qmlprofilerstatewidget.cpp    | 24 +++++++++++++++----
 src/plugins/qmlprofiler/qmlprofilertool.cpp   | 22 ++++++++++++-----
 9 files changed, 93 insertions(+), 12 deletions(-)

diff --git a/src/plugins/qmlprofiler/qmlprofilerclientmanager.cpp b/src/plugins/qmlprofiler/qmlprofilerclientmanager.cpp
index 39e87bb8082..c031a8d3176 100644
--- a/src/plugins/qmlprofiler/qmlprofilerclientmanager.cpp
+++ b/src/plugins/qmlprofiler/qmlprofilerclientmanager.cpp
@@ -63,6 +63,7 @@ public:
     quint64 tcpPort;
     QString sysroot;
     quint32 flushInterval;
+    bool aggregateTraces;
 
     QmlProfilerModelManager *modelManager;
 };
@@ -77,6 +78,7 @@ QmlProfilerClientManager::QmlProfilerClientManager(QObject *parent) :
     d->connection = 0;
     d->connectionAttempts = 0;
     d->flushInterval = 0;
+    d->aggregateTraces = true;
 
     d->modelManager = 0;
 
@@ -101,6 +103,16 @@ void QmlProfilerClientManager::setFlushInterval(quint32 flushInterval)
     d->flushInterval = flushInterval;
 }
 
+bool QmlProfilerClientManager::aggregateTraces() const
+{
+    return d->aggregateTraces;
+}
+
+void QmlProfilerClientManager::setAggregateTraces(bool aggregateTraces)
+{
+    d->aggregateTraces = aggregateTraces;
+}
+
 void QmlProfilerClientManager::setTcpConnection(QString host, quint64 port)
 {
     d->tcpHost = host;
@@ -349,7 +361,7 @@ void QmlProfilerClientManager::retryMessageBoxFinished(int result)
 void QmlProfilerClientManager::qmlComplete(qint64 maximumTime)
 {
     d->modelManager->traceTime()->increaseEndTime(maximumTime);
-    if (d->modelManager)
+    if (d->modelManager && !d->aggregateTraces)
         d->modelManager->acquiringDone();
 }
 
diff --git a/src/plugins/qmlprofiler/qmlprofilerclientmanager.h b/src/plugins/qmlprofiler/qmlprofilerclientmanager.h
index 342f254bb6a..2d11ffdb9d3 100644
--- a/src/plugins/qmlprofiler/qmlprofilerclientmanager.h
+++ b/src/plugins/qmlprofiler/qmlprofilerclientmanager.h
@@ -61,6 +61,9 @@ public:
     void setModelManager(QmlProfilerModelManager *m);
     void setFlushInterval(quint32 flushInterval);
 
+    bool aggregateTraces() const;
+    void setAggregateTraces(bool aggregateTraces);
+
 signals:
     void connectionFailed();
     void connectionClosed();
diff --git a/src/plugins/qmlprofiler/qmlprofilerconfigwidget.cpp b/src/plugins/qmlprofiler/qmlprofilerconfigwidget.cpp
index c7fd77db00f..9c130a82d67 100644
--- a/src/plugins/qmlprofiler/qmlprofilerconfigwidget.cpp
+++ b/src/plugins/qmlprofiler/qmlprofilerconfigwidget.cpp
@@ -46,6 +46,9 @@ QmlProfilerConfigWidget::QmlProfilerConfigWidget(QmlProfilerSettings *settings,
     connect(m_ui->flushInterval, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
             m_settings, &QmlProfilerSettings::setFlushInterval);
 
+    connect(m_ui->aggregateTraces, &QCheckBox::toggled,
+            m_settings, &QmlProfilerSettings::setAggregateTraces);
+
     connect(m_settings, &QmlProfilerSettings::changed, this, &QmlProfilerConfigWidget::updateUi);
 }
 
@@ -59,6 +62,7 @@ void QmlProfilerConfigWidget::updateUi()
     m_ui->flushEnabled->setChecked(m_settings->flushEnabled());
     m_ui->flushInterval->setEnabled(m_settings->flushEnabled());
     m_ui->flushInterval->setValue(m_settings->flushInterval());
+    m_ui->aggregateTraces->setChecked(m_settings->aggregateTraces());
 }
 
 } // Internal
diff --git a/src/plugins/qmlprofiler/qmlprofilerconfigwidget.ui b/src/plugins/qmlprofiler/qmlprofilerconfigwidget.ui
index faa76b3b7b9..28f8139d00a 100644
--- a/src/plugins/qmlprofiler/qmlprofilerconfigwidget.ui
+++ b/src/plugins/qmlprofiler/qmlprofilerconfigwidget.ui
@@ -47,6 +47,23 @@ itself takes time. </string>
      </property>
     </widget>
    </item>
+   <item row="2" column="0">
+    <widget class="QLabel" name="aggregateTracesLabel">
+     <property name="text">
+      <string>Process data only when process ends:</string>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="1">
+    <widget class="QCheckBox" name="aggregateTraces">
+     <property name="toolTip">
+      <string>Only process data when the process being profiled ends, not when the current recording
+session ends. This way multiple recording sessions can be aggregated in a single trace,
+for example if multiple QML engines start and stop sequentially during a single run of
+the program.</string>
+     </property>
+    </widget>
+   </item>
   </layout>
  </widget>
  <resources/>
diff --git a/src/plugins/qmlprofiler/qmlprofilerconstants.h b/src/plugins/qmlprofiler/qmlprofilerconstants.h
index 528bddbd101..5b3e0611887 100644
--- a/src/plugins/qmlprofiler/qmlprofilerconstants.h
+++ b/src/plugins/qmlprofiler/qmlprofilerconstants.h
@@ -41,6 +41,7 @@ const char TASK_SAVE[] = "QmlProfiler.TaskSave";
 const char FLUSH_ENABLED[] = "Analyzer.QmlProfiler.FlushEnabled";
 const char FLUSH_INTERVAL[] = "Analyzer.QmlProfiler.FlushInterval";
 const char LAST_TRACE_FILE[] = "Analyzer.QmlProfiler.LastTraceFile";
+const char AGGREGATE_TRACES[] = "Analyzer.QmlProfiler.AggregateTraces";
 const char SETTINGS[] = "Analyzer.QmlProfiler.Settings";
 const char ANALYZER[] = "Analyzer";
 
diff --git a/src/plugins/qmlprofiler/qmlprofilersettings.cpp b/src/plugins/qmlprofiler/qmlprofilersettings.cpp
index d1cb5ac1224..be5493a85fe 100644
--- a/src/plugins/qmlprofiler/qmlprofilersettings.cpp
+++ b/src/plugins/qmlprofiler/qmlprofilersettings.cpp
@@ -45,6 +45,7 @@ QmlProfilerSettings::QmlProfilerSettings()
     defaults.insert(QLatin1String(Constants::FLUSH_INTERVAL), 1000);
     defaults.insert(QLatin1String(Constants::FLUSH_ENABLED), false);
     defaults.insert(QLatin1String(Constants::LAST_TRACE_FILE), QString());
+    defaults.insert(QLatin1String(Constants::AGGREGATE_TRACES), false);
 
     // Read stored values
     QSettings *settings = Core::ICore::settings();
@@ -106,6 +107,19 @@ void QmlProfilerSettings::setLastTraceFile(const QString &lastTracePath)
     }
 }
 
+bool QmlProfilerSettings::aggregateTraces() const
+{
+    return m_aggregateTraces;
+}
+
+void QmlProfilerSettings::setAggregateTraces(bool aggregateTraces)
+{
+    if (m_aggregateTraces != aggregateTraces) {
+        m_aggregateTraces = aggregateTraces;
+        emit changed();
+    }
+}
+
 void QmlProfilerSettings::writeGlobalSettings() const
 {
     QSettings *settings = Core::ICore::settings();
@@ -122,6 +136,7 @@ void QmlProfilerSettings::toMap(QVariantMap &map) const
     map[QLatin1String(Constants::FLUSH_INTERVAL)] = m_flushInterval;
     map[QLatin1String(Constants::FLUSH_ENABLED)] = m_flushEnabled;
     map[QLatin1String(Constants::LAST_TRACE_FILE)] = m_lastTraceFile;
+    map[QLatin1String(Constants::AGGREGATE_TRACES)] = m_aggregateTraces;
 }
 
 void QmlProfilerSettings::fromMap(const QVariantMap &map)
@@ -129,6 +144,7 @@ void QmlProfilerSettings::fromMap(const QVariantMap &map)
     m_flushEnabled = map.value(QLatin1String(Constants::FLUSH_ENABLED)).toBool();
     m_flushInterval = map.value(QLatin1String(Constants::FLUSH_INTERVAL)).toUInt();
     m_lastTraceFile = map.value(QLatin1String(Constants::LAST_TRACE_FILE)).toString();
+    m_aggregateTraces = map.value(QLatin1String(Constants::AGGREGATE_TRACES)).toBool();
     emit changed();
 }
 
diff --git a/src/plugins/qmlprofiler/qmlprofilersettings.h b/src/plugins/qmlprofiler/qmlprofilersettings.h
index 91d94e89c87..31370e9cd2f 100644
--- a/src/plugins/qmlprofiler/qmlprofilersettings.h
+++ b/src/plugins/qmlprofiler/qmlprofilersettings.h
@@ -53,6 +53,9 @@ public:
     QString lastTraceFile() const;
     void setLastTraceFile(const QString &lastTraceFile);
 
+    bool aggregateTraces() const;
+    void setAggregateTraces(bool aggregateTraces);
+
     void writeGlobalSettings() const;
 
 signals:
@@ -66,6 +69,7 @@ private:
     bool m_flushEnabled;
     quint32 m_flushInterval;
     QString m_lastTraceFile;
+    bool m_aggregateTraces;
 };
 
 } // Internal
diff --git a/src/plugins/qmlprofiler/qmlprofilerstatewidget.cpp b/src/plugins/qmlprofiler/qmlprofilerstatewidget.cpp
index 5b1bd1c7232..2d1e929a320 100644
--- a/src/plugins/qmlprofiler/qmlprofilerstatewidget.cpp
+++ b/src/plugins/qmlprofiler/qmlprofilerstatewidget.cpp
@@ -30,6 +30,8 @@
 
 #include "qmlprofilerstatewidget.h"
 
+#include <utils/qtcassert.h>
+
 #include <QPainter>
 #include <QVBoxLayout>
 #include <QLabel>
@@ -202,11 +204,23 @@ void QmlProfilerStateWidget::updateDisplay()
             return;
         }
     } else if (d->m_modelManager->progress() != 0 && !d->m_modelManager->isEmpty()) {
-        // When datamodel is acquiring data
-        if (d->m_profilerState->currentState() != QmlProfilerStateManager::Idle)
-            showText(tr("Loading data"), true);
-        else // Application died before all data could be read
-            showText(tr("Application stopped before loading all data"), true);
+        // When datamodel is acquiring or processing data
+        if (state == QmlProfilerModelManager::ProcessingData) {
+            showText(tr("Processing data"), true);
+        } else  if (d->m_profilerState->currentState() != QmlProfilerStateManager::Idle) {
+            if (state == QmlProfilerModelManager::AcquiringData) {
+                // we don't know how much more, so progress numbers are strange here
+                showText(tr("Waiting for more data"));
+            } else if (state == QmlProfilerModelManager::ClearingData) {
+                // when starting a second recording from the same process without aggregation
+                showText(tr("Clearing old trace"));
+            }
+        } else if (state == QmlProfilerModelManager::AcquiringData) {
+            // Application died before all data could be read
+            showText(tr("Application stopped before loading all data"));
+        } else if (state == QmlProfilerModelManager::ClearingData) {
+            showText(tr("Clearing old trace"));
+        }
         return;
     } else if (state == QmlProfilerModelManager::AcquiringData) {
         showText(tr("Waiting for data"));
diff --git a/src/plugins/qmlprofiler/qmlprofilertool.cpp b/src/plugins/qmlprofiler/qmlprofilertool.cpp
index c80795ebf63..c94337526cb 100644
--- a/src/plugins/qmlprofiler/qmlprofilertool.cpp
+++ b/src/plugins/qmlprofiler/qmlprofilertool.cpp
@@ -209,6 +209,7 @@ AnalyzerRunControl *QmlProfilerTool::createRunControl(const AnalyzerStartParamet
 
     d->m_profilerConnections->setFlushInterval(settings->flushEnabled() ?
                                                    settings->flushInterval() : 0);
+    d->m_profilerConnections->setAggregateTraces(settings->aggregateTraces());
 
     QmlProfilerRunControl *engine = new QmlProfilerRunControl(sp, runConfiguration);
 
@@ -373,7 +374,9 @@ void QmlProfilerTool::recordingButtonChanged(bool recording)
 
     if (recording && d->m_profilerState->currentState() == QmlProfilerStateManager::AppRunning) {
         if (checkForUnsavedNotes()) {
-            clearData(); // clear right away, before the application starts
+            if (!d->m_profilerConnections->aggregateTraces() ||
+                    d->m_profilerModelManager->state() == QmlProfilerModelManager::Done)
+                clearData(); // clear before the recording starts, unless we aggregate recordings
             if (d->m_profilerState->clientRecording())
                 d->m_profilerState->setClientRecording(false);
             d->m_profilerState->setClientRecording(true);
@@ -652,9 +655,13 @@ void QmlProfilerTool::clientsDisconnected()
     if (d->m_profilerState->currentState() == QmlProfilerStateManager::AppDying ||
             d->m_profilerState->currentState() == QmlProfilerStateManager::Idle) {
         if (d->m_profilerModelManager->state() == QmlProfilerModelManager::AcquiringData) {
-            showNonmodalWarning(tr("Application finished before loading profiled data.\n"
-                                   "Please use the stop button instead."));
-            d->m_profilerModelManager->clear();
+            if (d->m_profilerConnections->aggregateTraces()) {
+                d->m_profilerModelManager->acquiringDone();
+            } else {
+                showNonmodalWarning(tr("Application finished before loading profiled data.\n"
+                                       "Please use the stop button instead."));
+                d->m_profilerModelManager->clear();
+            }
         }
 
         // ... and return to the "base" state
@@ -822,13 +829,16 @@ void QmlProfilerTool::serverRecordingChanged()
                 showSaveDialog();
 
             setRecording(true);
-            clearData();
+            if (!d->m_profilerConnections->aggregateTraces() ||
+                    d->m_profilerModelManager->state() == QmlProfilerModelManager::Done)
+                clearData();
             d->m_profilerModelManager->prepareForWriting();
         } else {
             setRecording(false);
 
             // changes back once loading is finished, see profilerDataModelStateChanged()
-            d->m_recordButton->setEnabled(false);
+            if (!d->m_profilerConnections->aggregateTraces())
+                d->m_recordButton->setEnabled(false);
         }
     }
 }
-- 
GitLab