From fbb45e5a578832b47bdad86906a1edcf71875efe Mon Sep 17 00:00:00 2001
From: Ulf Hermann <ulf.hermann@qt.io>
Date: Fri, 9 Sep 2016 10:25:46 +0200
Subject: [PATCH] QmlProfiler: Keep Compiling events separate in flame graph

The QML compiler can run asynchronously and produce ranges that don't
match up with other QML/JS ranges. The flame graph model assumes that
all ranges are perfectly nested, and produces incorrect data if they
aren't. The compile ranges are perfectly nested among themselves, and
the other QML/JS ranges are also perfectly nested among themselves, so
we can fix this by keeping separate stacks for them.

Change-Id: If4ea251c6a2e74bd04e142cf184937600ea31a87
Reviewed-by: Kai Koehne <kai.koehne@qt.io>
---
 src/plugins/qmlprofiler/flamegraphmodel.cpp | 27 ++++++++++++++-------
 src/plugins/qmlprofiler/flamegraphmodel.h   |  4 ++-
 2 files changed, 21 insertions(+), 10 deletions(-)

diff --git a/src/plugins/qmlprofiler/flamegraphmodel.cpp b/src/plugins/qmlprofiler/flamegraphmodel.cpp
index 74eb70793a1..fe8d758d056 100644
--- a/src/plugins/qmlprofiler/flamegraphmodel.cpp
+++ b/src/plugins/qmlprofiler/flamegraphmodel.cpp
@@ -44,7 +44,9 @@ FlameGraphModel::FlameGraphModel(QmlProfilerModelManager *modelManager,
 {
     m_modelManager = modelManager;
     m_callStack.append(QmlEvent());
-    m_stackTop = &m_stackBottom;
+    m_compileStack.append(QmlEvent());
+    m_callStackTop = &m_stackBottom;
+    m_compileStackTop = &m_stackBottom;
     connect(modelManager, &QmlProfilerModelManager::stateChanged,
             this, &FlameGraphModel::onModelManagerStateChanged);
     connect(modelManager->notesModel(), &Timeline::TimelineNotesModel::changed,
@@ -64,8 +66,11 @@ void FlameGraphModel::clear()
     beginResetModel();
     m_stackBottom = FlameGraphData(0, -1, 1);
     m_callStack.clear();
+    m_compileStack.clear();
     m_callStack.append(QmlEvent());
-    m_stackTop = &m_stackBottom;
+    m_compileStack.append(QmlEvent());
+    m_callStackTop = &m_stackBottom;
+    m_compileStackTop = &m_stackBottom;
     m_typeIdsWithNotes.clear();
     endResetModel();
 }
@@ -99,16 +104,20 @@ void FlameGraphModel::loadEvent(const QmlEvent &event, const QmlEventType &type)
     if (m_stackBottom.children.isEmpty())
         beginResetModel();
 
-    const QmlEvent *potentialParent = &(m_callStack.top());
+    const bool isCompiling = (type.rangeType() == Compiling);
+    QStack<QmlEvent> &stack =  isCompiling ? m_compileStack : m_callStack;
+    FlameGraphData *&stackTop = isCompiling ? m_compileStackTop : m_callStackTop;
+
+    const QmlEvent *potentialParent = &(stack.top());
     if (event.rangeStage() == RangeEnd) {
-        m_stackTop->duration += event.timestamp() - potentialParent->timestamp();
-        m_callStack.pop();
-        m_stackTop = m_stackTop->parent;
-        potentialParent = &(m_callStack.top());
+        stackTop->duration += event.timestamp() - potentialParent->timestamp();
+        stack.pop();
+        stackTop = stackTop->parent;
+        potentialParent = &(stack.top());
     } else {
         QTC_ASSERT(event.rangeStage() == RangeStart, return);
-        m_callStack.push(event);
-        m_stackTop = pushChild(m_stackTop, event);
+        stack.push(event);
+        stackTop = pushChild(stackTop, event);
     }
 }
 
diff --git a/src/plugins/qmlprofiler/flamegraphmodel.h b/src/plugins/qmlprofiler/flamegraphmodel.h
index b01cd5fa233..f4092bf437e 100644
--- a/src/plugins/qmlprofiler/flamegraphmodel.h
+++ b/src/plugins/qmlprofiler/flamegraphmodel.h
@@ -102,8 +102,10 @@ private:
 
     // used by binding loop detection
     QStack<QmlEvent> m_callStack;
+    QStack<QmlEvent> m_compileStack;
     FlameGraphData m_stackBottom;
-    FlameGraphData *m_stackTop;
+    FlameGraphData *m_callStackTop;
+    FlameGraphData *m_compileStackTop;
 
     int m_modelId;
     QmlProfilerModelManager *m_modelManager;
-- 
GitLab