callstack: The flame graph view uses a data provider

The flame graph view is updated to use a data provider instead of the
content provider, which was ui only. Symbol resolution is done by the
data provider, as well as the grouping.

GoTo Min/Max are taking a break

[changed] The flame graph view uses a data provider

Change-Id: Id5e37179bd65ae29f224bfcb03d2c0048cdb14b0
Signed-off-by: Geneviève Bastien <gbastien+lttng@versatic.net>
Reviewed-on: https://git.eclipse.org/r/149298
Tested-by: CI Bot
Reviewed-by: Matthew Khouzam <matthew.khouzam@ericsson.com>
Tested-by: Matthew Khouzam <matthew.khouzam@ericsson.com>
diff --git a/callstack/org.eclipse.tracecompass.incubator.analysis.core/src/org/eclipse/tracecompass/incubator/analysis/core/concepts/AggregatedCallSite.java b/callstack/org.eclipse.tracecompass.incubator.analysis.core/src/org/eclipse/tracecompass/incubator/analysis/core/concepts/AggregatedCallSite.java
index fbd99f1..bf1d932 100644
--- a/callstack/org.eclipse.tracecompass.incubator.analysis.core/src/org/eclipse/tracecompass/incubator/analysis/core/concepts/AggregatedCallSite.java
+++ b/callstack/org.eclipse.tracecompass.incubator.analysis.core/src/org/eclipse/tracecompass/incubator/analysis/core/concepts/AggregatedCallSite.java
@@ -11,7 +11,6 @@
 
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
@@ -93,14 +92,4 @@
         return "CallSite: " + getObject(); //$NON-NLS-1$
     }
 
-    /**
-     * Get extra children sites that come with this callsite. For instance, an
-     * instrumented callsite could return the kernel processes
-     *
-     * @return The extra children sites
-     */
-    public Collection<AggregatedCallSite> getExtraChildrenSites() {
-        return Collections.emptyList();
-    }
-
 }
diff --git a/callstack/org.eclipse.tracecompass.incubator.analysis.core/src/org/eclipse/tracecompass/incubator/internal/analysis/core/weighted/tree/Messages.java b/callstack/org.eclipse.tracecompass.incubator.analysis.core/src/org/eclipse/tracecompass/incubator/internal/analysis/core/weighted/tree/Messages.java
index d63d620..287df3d 100644
--- a/callstack/org.eclipse.tracecompass.incubator.analysis.core/src/org/eclipse/tracecompass/incubator/internal/analysis/core/weighted/tree/Messages.java
+++ b/callstack/org.eclipse.tracecompass.incubator.analysis.core/src/org/eclipse/tracecompass/incubator/internal/analysis/core/weighted/tree/Messages.java
@@ -30,4 +30,4 @@
 
     private Messages() {
     }
-}
\ No newline at end of file
+}
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/build.properties b/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/build.properties
index e8ad703..3eea029 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/build.properties
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/build.properties
@@ -15,4 +15,6 @@
                .,\
                about.html,\
                plugin.properties,\
-               plugin.xml
+               plugin.xml,\
+               test_xml_files/,\
+               testfiles/
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/src/org/eclipse/tracecompass/incubator/callstack/core/tests/callgraph/instrumented/AggregationTreeTest.java b/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/src/org/eclipse/tracecompass/incubator/callstack/core/tests/callgraph/instrumented/AggregationTreeTest.java
index c0f6e05..390e8de 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/src/org/eclipse/tracecompass/incubator/callstack/core/tests/callgraph/instrumented/AggregationTreeTest.java
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/src/org/eclipse/tracecompass/incubator/callstack/core/tests/callgraph/instrumented/AggregationTreeTest.java
@@ -12,16 +12,21 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
+import java.util.Objects;
 
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IStatus;
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.tracecompass.incubator.callstack.core.base.ICallStackElement;
 import org.eclipse.tracecompass.incubator.callstack.core.callgraph.CallGraph;
 import org.eclipse.tracecompass.incubator.callstack.core.callgraph.ICallGraphProvider;
+import org.eclipse.tracecompass.incubator.callstack.core.tests.Activator;
 import org.eclipse.tracecompass.incubator.callstack.core.tests.flamechart.CallStackTestBase;
 import org.eclipse.tracecompass.incubator.callstack.core.tests.stubs.CallGraphAnalysisStub;
 import org.eclipse.tracecompass.incubator.internal.callstack.core.instrumented.callgraph.AggregatedCalledFunction;
@@ -30,7 +35,15 @@
 import org.eclipse.tracecompass.statesystem.core.backend.IStateHistoryBackend;
 import org.eclipse.tracecompass.statesystem.core.backend.StateHistoryBackendFactory;
 import org.eclipse.tracecompass.statesystem.core.statevalue.TmfStateValue;
+import org.eclipse.tracecompass.tmf.core.event.TmfEvent;
+import org.eclipse.tracecompass.tmf.core.exceptions.TmfTraceException;
+import org.eclipse.tracecompass.tmf.core.signal.TmfTraceOpenedSignal;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
+import org.eclipse.tracecompass.tmf.tests.stubs.trace.xml.TmfXmlTraceStub;
+import org.eclipse.tracecompass.tmf.tests.stubs.trace.xml.TmfXmlTraceStubNs;
 import org.junit.After;
+import org.junit.Before;
 import org.junit.Test;
 
 import com.google.common.collect.ImmutableList;
@@ -44,6 +57,10 @@
  */
 public class AggregationTreeTest {
 
+    private static final String CALLSTACK_FILE = "testfiles/traces/callstack.xml";
+
+    private ITmfTrace fTrace;
+
     private static final String QUARK_0 = "0";
     private static final String QUARK_1 = "1";
     private static final String QUARK_2 = "2";
@@ -79,6 +96,28 @@
     }
 
     /**
+     * Open a flamegraph
+     */
+    @Before
+    public void before() {
+        TmfXmlTraceStub trace = new TmfXmlTraceStubNs();
+        IPath filePath = Activator.getAbsoluteFilePath(CALLSTACK_FILE);
+        IStatus status = trace.validate(null, filePath.toOSString());
+        if (!status.isOK()) {
+            fail(status.getException().getMessage());
+        }
+        try {
+            trace.initTrace(null, filePath.toOSString(), TmfEvent.class);
+        } catch (TmfTraceException e) {
+            fail(e.getMessage());
+        }
+        fTrace = trace;
+        TmfTraceOpenedSignal signal = new TmfTraceOpenedSignal(this, trace, null);
+        trace.traceOpened(signal);
+        TmfTraceManager.getInstance().traceOpened(signal);
+    }
+
+    /**
      * Dispose the callgraph analysis that has been set
      */
     @After
@@ -87,6 +126,10 @@
         if (cga != null) {
             cga.dispose();
         }
+        ITmfTrace trace = fTrace;
+        if (trace != null) {
+            trace.dispose();
+        }
     }
 
     /**
@@ -631,4 +674,13 @@
     private void setCga(CallGraphAnalysisStub cga) {
         fCga = cga;
     }
+
+    /**
+     * Get a trace for when a trace is needed
+     *
+     * @return A trace, that has no relation with the callgraphs of these tests
+     */
+    protected @NonNull ITmfTrace getTrace() {
+        return Objects.requireNonNull(fTrace);
+    }
 }
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core/META-INF/MANIFEST.MF b/callstack/org.eclipse.tracecompass.incubator.callstack.core/META-INF/MANIFEST.MF
index 2cc39a4..86e4448 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.core/META-INF/MANIFEST.MF
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.core/META-INF/MANIFEST.MF
@@ -34,7 +34,7 @@
  org.eclipse.tracecompass.incubator.callstack.core.symbol,
  org.eclipse.tracecompass.incubator.internal.callstack.core;x-friends:="org.eclipse.tracecompass.incubator.callstack.core.tests,org.eclipse.tracecompass.incubator.callstack.ui",
  org.eclipse.tracecompass.incubator.internal.callstack.core.base;x-friends:="org.eclipse.tracecompass.incubator.callstack.core.tests,org.eclipse.tracecompass.incubator.callstack.ui",
- org.eclipse.tracecompass.incubator.internal.callstack.core.flamegraph;x-friends:="org.eclipse.tracecompass.incubator.callstack.core.tests,org.eclipse.tracecompass.incubator.callstack.ui",
+ org.eclipse.tracecompass.incubator.internal.callstack.core.flamegraph;x-friends:="org.eclipse.tracecompass.incubator.callstack.core.tests,org.eclipse.tracecompass.incubator.callstack.ui,org.eclipse.tracecompass.incubator.callstack.ui.swtbot.tests",
  org.eclipse.tracecompass.incubator.internal.callstack.core.instrumented;x-friends:="org.eclipse.tracecompass.incubator.callstack.core.tests,org.eclipse.tracecompass.incubator.callstack.ui",
  org.eclipse.tracecompass.incubator.internal.callstack.core.instrumented.callgraph;x-friends:="org.eclipse.tracecompass.incubator.callstack.core.tests,org.eclipse.tracecompass.incubator.callstack.ui",
  org.eclipse.tracecompass.incubator.internal.callstack.core.instrumented.provider,
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/callstack/core/callgraph/AllGroupDescriptor.java b/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/callstack/core/callgraph/AllGroupDescriptor.java
deleted file mode 100644
index 5fe7950..0000000
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/callstack/core/callgraph/AllGroupDescriptor.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2017 École Polytechnique de Montréal
- *
- * All rights reserved. This program and the accompanying materials are
- * made available under the terms of the Eclipse Public License v1.0 which
- * accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *******************************************************************************/
-
-package org.eclipse.tracecompass.incubator.callstack.core.callgraph;
-
-import org.eclipse.jdt.annotation.Nullable;
-import org.eclipse.tracecompass.incubator.callstack.core.base.ICallStackGroupDescriptor;
-
-/**
- * A group descriptor represents all the groups together
- *
- * @author Geneviève Bastien
- */
-public final class AllGroupDescriptor implements ICallStackGroupDescriptor {
-
-    private static final String ALL_NAME = "all"; //$NON-NLS-1$
-
-    private static final ICallStackGroupDescriptor INSTANCE = new AllGroupDescriptor();
-
-    /**
-     * Get the instance of the all group descriptor
-     *
-     * @return The instance of this group.
-     */
-    public static ICallStackGroupDescriptor getInstance() {
-        return INSTANCE;
-    }
-
-    private AllGroupDescriptor() {
-
-    }
-
-    @Override
-    public @Nullable ICallStackGroupDescriptor getNextGroup() {
-        return null;
-    }
-
-    @Override
-    public boolean isSymbolKeyGroup() {
-        return false;
-    }
-
-    @Override
-    public String getName() {
-        return ALL_NAME;
-    }
-
-//    @Override
-//    public List<ICallStackElement> getElements(@Nullable ICallStackElement parent, @Nullable ICallStackElement symbolKeyElement) {
-//        return Collections.emptyList();
-//    }
-
-}
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/flamegraph/FlameGraphDataProviderFactory.java b/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/flamegraph/FlameGraphDataProviderFactory.java
index 4120167..401da11 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/flamegraph/FlameGraphDataProviderFactory.java
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/flamegraph/FlameGraphDataProviderFactory.java
@@ -10,7 +10,9 @@
 package org.eclipse.tracecompass.incubator.internal.callstack.core.flamegraph;
 
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.Iterator;
+import java.util.Map;
 import java.util.Objects;
 
 import org.eclipse.jdt.annotation.Nullable;
@@ -24,15 +26,18 @@
 import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
 import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils;
 
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.Iterables;
 
 /**
- * Factory for the flame chart data provider
+ * Factory for the flame graph data provider
  *
  * @author Geneviève Bastien
  */
 public class FlameGraphDataProviderFactory implements IDataProviderFactory {
 
+    private static Map<String, FlameGraphDataProvider<?, ?, ?>> INSTANCES = new HashMap<>();
+
     @Override
     public @Nullable ITmfTreeDataProvider<? extends ITmfTreeDataModel> createProvider(ITmfTrace trace) {
         // Need the analysis
@@ -59,6 +64,10 @@
     }
 
     private static @Nullable ITmfTreeDataProvider<? extends ITmfTreeDataModel> create(ITmfTrace trace, String secondaryId) {
+        FlameGraphDataProvider<?, ?, ?> dataProvider = INSTANCES.get(secondaryId);
+        if (dataProvider != null) {
+            return dataProvider;
+        }
         // The trace can be an experiment, so we need to know if there are
         // multiple analysis modules with the same ID
         Iterable<IWeightedTreeProvider> modules = TmfTraceUtils.getAnalysisModulesOfClass(trace, IWeightedTreeProvider.class);
@@ -78,4 +87,19 @@
         return null;
     }
 
+    /**
+     * Adds a reference to a data provider identified by the id, but not
+     * associated with a trace. Useful for data provider unit testing where
+     * fixtures of data are used without a trace
+     *
+     * @param id
+     *            ID of the data provider
+     * @param dataProvider
+     *            The data provider
+     */
+    @VisibleForTesting
+    public static void registerDataProviderWithId(String id, FlameGraphDataProvider<?, ?, ?> dataProvider) {
+        INSTANCES.put(id, dataProvider);
+    }
+
 }
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.ui.swtbot.tests/src/org/eclipse/tracecompass/incubator/callstack/core/ui/swtbot/tests/flamegraph/FlameGraphTest.java b/callstack/org.eclipse.tracecompass.incubator.callstack.ui.swtbot.tests/src/org/eclipse/tracecompass/incubator/callstack/core/ui/swtbot/tests/flamegraph/FlameGraphTest.java
index 50d5dee..ef91a54 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.ui.swtbot.tests/src/org/eclipse/tracecompass/incubator/callstack/core/ui/swtbot/tests/flamegraph/FlameGraphTest.java
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.ui.swtbot.tests/src/org/eclipse/tracecompass/incubator/callstack/core/ui/swtbot/tests/flamegraph/FlameGraphTest.java
@@ -14,8 +14,8 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
-import java.util.Collections;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.Spliterator;
 import java.util.Spliterators;
@@ -28,12 +28,20 @@
 import org.eclipse.swtbot.eclipse.finder.SWTWorkbenchBot;
 import org.eclipse.swtbot.eclipse.finder.widgets.SWTBotView;
 import org.eclipse.swtbot.swt.finder.finders.UIThreadRunnable;
+import org.eclipse.swtbot.swt.finder.junit.SWTBotJunit4ClassRunner;
 import org.eclipse.swtbot.swt.finder.results.Result;
 import org.eclipse.swtbot.swt.finder.utils.SWTBotPreferences;
 import org.eclipse.tracecompass.incubator.callstack.core.tests.callgraph.instrumented.AggregationTreeTest;
+import org.eclipse.tracecompass.incubator.callstack.core.tests.stubs.CallGraphAnalysisStub;
+import org.eclipse.tracecompass.incubator.internal.callstack.core.flamegraph.FlameGraphDataProvider;
+import org.eclipse.tracecompass.incubator.internal.callstack.core.flamegraph.FlameGraphDataProviderFactory;
 import org.eclipse.tracecompass.incubator.internal.callstack.ui.flamegraph.FlameGraphPresentationProvider;
 import org.eclipse.tracecompass.incubator.internal.callstack.ui.flamegraph.FlameGraphView;
+import org.eclipse.tracecompass.tmf.core.signal.TmfTraceSelectedSignal;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.eclipse.tracecompass.tmf.ui.swtbot.tests.shared.ConditionHelpers;
 import org.eclipse.tracecompass.tmf.ui.swtbot.tests.shared.ConditionHelpers.SWTBotTestCondition;
+import org.eclipse.tracecompass.tmf.ui.swtbot.tests.shared.SWTBotTimeGraph;
 import org.eclipse.tracecompass.tmf.ui.swtbot.tests.shared.SWTBotUtils;
 import org.eclipse.tracecompass.tmf.ui.tests.shared.WaitUtils;
 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.TimeGraphViewer;
@@ -42,24 +50,29 @@
 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.NullTimeEvent;
 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeEvent;
 import org.eclipse.ui.IViewPart;
+import org.junit.After;
 import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Ignore;
+import org.junit.runner.RunWith;
 
 /**
  * Unit tests for the flame graph view
  *
  * @author Matthew Khouzam
  */
+@RunWith(SWTBotJunit4ClassRunner.class)
 public class FlameGraphTest extends AggregationTreeTest {
 
+    private static final @NonNull String SECONDARY_ID = "org.eclipse.tracecompass.incubator.callstack.ui.swtbot.test";
     private static final String FLAMEGRAPH_ID = FlameGraphView.ID;
     private SWTWorkbenchBot fBot;
-    private SWTBotView fView;
+    private SWTBotView fViewBot;
     private FlameGraphView fFg;
     /** The Log4j logger instance. */
     private static final Logger fLogger = Logger.getRootLogger();
     private TimeGraphViewer fTimeGraphViewer;
+    private SWTBotTimeGraph fTimeGraph;
 
     /**
      * Initialization
@@ -86,14 +99,14 @@
      * Open a flamegraph
      */
     @Before
-    public void before() {
+    public void openView() {
         fBot = new SWTWorkbenchBot();
-        SWTBotUtils.openView(FLAMEGRAPH_ID);
+        SWTBotUtils.openView(FLAMEGRAPH_ID, SECONDARY_ID);
         SWTBotView view = fBot.viewById(FLAMEGRAPH_ID);
         assertNotNull(view);
-        fView = view;
+        fViewBot = view;
         FlameGraphView flamegraph = UIThreadRunnable.syncExec((Result<FlameGraphView>) () -> {
-            IViewPart viewRef = fView.getViewReference().getView(true);
+            IViewPart viewRef = fViewBot.getViewReference().getView(true);
             return (viewRef instanceof FlameGraphView) ? (FlameGraphView) viewRef : null;
         });
         assertNotNull(flamegraph);
@@ -101,14 +114,54 @@
         assertNotNull(fTimeGraphViewer);
         SWTBotUtils.maximize(flamegraph);
         fFg = flamegraph;
+        fTimeGraph = new SWTBotTimeGraph(view.bot());
     }
 
-    private void loadFlameGraph() {
-        UIThreadRunnable.syncExec(() -> fFg.buildFlameGraph(Collections.singleton(getCga()), null, null));
+    /**
+     * Clean up after a test, reset the views and reset the states of the
+     * timegraph by pressing reset on all the resets of the legend
+     */
+    @After
+    public void after() {
+        // Calling maximize again will minimize
+        FlameGraphView fg = fFg;
+        if (fg != null) {
+            SWTBotUtils.maximize(fg);
+        }
+        // Setting the input to null so the view can be emptied, to avoid race conditions with subsequent tests
+        TimeGraphViewer tg = fTimeGraphViewer;
+        if (tg != null) {
+            UIThreadRunnable.syncExec(() -> {
+                tg.setInput(null);
+                tg.refresh();
+            });
+        }
         fBot.waitUntil(new SWTBotTestCondition() {
             @Override
             public boolean test() throws Exception {
-                return !fFg.isDirty();
+                return fTimeGraph.getEntries().length == 0;
+            }
+
+            @Override
+            public String getFailureMessage() {
+                return "Flame graph not emptied";
+            }
+        });
+        fViewBot.close();
+        fBot.waitUntil(ConditionHelpers.viewIsClosed(fViewBot));
+    }
+
+    private void loadFlameGraph() {
+        CallGraphAnalysisStub cga = Objects.requireNonNull(getCga());
+        ITmfTrace trace = getTrace();
+        fFg.traceSelected(new TmfTraceSelectedSignal(this, trace));
+        FlameGraphDataProvider<?, ?, ?> dp = new FlameGraphDataProvider<>(trace, cga, FlameGraphDataProvider.ID + ':' + SECONDARY_ID);
+        FlameGraphDataProviderFactory.registerDataProviderWithId(SECONDARY_ID, dp);
+        UIThreadRunnable.syncExec(() -> fFg.buildFlameGraph(trace, null, null));
+        fBot.waitUntil(new SWTBotTestCondition() {
+            @Override
+            public boolean test() throws Exception {
+                return fTimeGraph.getEntries().length > 0;
             }
 
             @Override
@@ -120,10 +173,10 @@
     }
 
     private ITimeGraphEntry selectRoot() {
-        // FIXME: there should be only 2 selectNextItem, not 3
         UIThreadRunnable.syncExec(() -> {
             fTimeGraphViewer.selectNextItem();
             fTimeGraphViewer.selectNextItem();
+            fTimeGraphViewer.selectNextItem();
         });
         ITimeGraphEntry entry = fTimeGraphViewer.getSelection();
         assertNotNull(entry);
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/flamegraph/FlameGraphContentProvider.java b/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/flamegraph/FlameGraphContentProvider.java
deleted file mode 100644
index e53ea14..0000000
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/flamegraph/FlameGraphContentProvider.java
+++ /dev/null
@@ -1,284 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2016 Ericsson
- *
- * All rights reserved. This program and the accompanying materials are made
- * available under the terms of the Eclipse Public License v1.0 which
- * accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *******************************************************************************/
-
-package org.eclipse.tracecompass.incubator.internal.callstack.ui.flamegraph;
-
-import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
-
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Comparator;
-import java.util.Deque;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
-import org.eclipse.jdt.annotation.Nullable;
-import org.eclipse.jface.viewers.Viewer;
-import org.eclipse.tracecompass.incubator.analysis.core.concepts.AggregatedCallSite;
-import org.eclipse.tracecompass.incubator.callstack.core.base.ICallStackElement;
-import org.eclipse.tracecompass.incubator.callstack.core.callgraph.CallGraph;
-import org.eclipse.tracecompass.incubator.internal.callstack.core.instrumented.callgraph.AggregatedThreadStatus;
-import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.ITimeGraphContentProvider;
-import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeGraphEntry;
-import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeEvent;
-import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeGraphEntry;
-
-/**
- * Content provider for the flame graph view
- *
- * @author Sonia Farrah
- *
- */
-public class FlameGraphContentProvider implements ITimeGraphContentProvider {
-
-    private final List<TimeGraphEntry> fFlameGraphEntries = new ArrayList<>();
-
-    private SortOption fSortOption = SortOption.BY_NAME;
-    private Comparator<TimeGraphEntry> fThreadComparator = new ThreadNameComparator();
-
-    private final Map<ICallStackElement, TimeGraphEntry> fLevelEntries = new HashMap<>();
-
-    /**
-     * Parse the aggregated tree created by the callGraphAnalysis and creates
-     * the event list (functions) for each entry (depth)
-     *
-     * @param firstNode
-     *            The first node of the aggregation tree
-     * @param childrenEntries
-     *            The list of entries for one thread
-     * @param timestampStack
-     *            A stack used to save the functions timeStamps
-     */
-    private void setData(AggregatedCallSite firstNode, List<FlamegraphDepthEntry> childrenEntries, Deque<Long> timestampStack) {
-        Long lastEnd = timestampStack.peek();
-        if (lastEnd == null) {
-            return;
-        }
-        for (int i = 0; i <= firstNode.getMaxDepth() - 1; i++) {
-            if (i >= childrenEntries.size()) {
-                FlamegraphDepthEntry entry = new FlamegraphDepthEntry(String.valueOf(i), 0, firstNode.getWeight(), i, String.valueOf(i));
-                childrenEntries.add(entry);
-            }
-            childrenEntries.get(i).updateEndTime(lastEnd + firstNode.getWeight());
-        }
-        FlamegraphDepthEntry firstEntry = checkNotNull(childrenEntries.get(0));
-        firstEntry.addEvent(new FlamegraphEvent(firstEntry, lastEnd, firstNode));
-        // Build the event list for next entries (next depth)
-        addEvent(firstNode, childrenEntries, timestampStack, 1);
-        timestampStack.pop();
-    }
-
-    private static void setExtraData(AggregatedCallSite firstNode, List<TimeGraphEntry> extraEntries, Deque<Long> timestampStack) {
-        Long lastEnd = timestampStack.peek();
-        if (lastEnd == null) {
-            return;
-        }
-        Iterator<AggregatedCallSite> extraChildrenSites = firstNode.getExtraChildrenSites().iterator();
-
-        if (!extraChildrenSites.hasNext()) {
-            return;
-        }
-        // Get or add the entry
-        if (extraEntries.isEmpty()) {
-            TimeGraphEntry entry = new TimeGraphEntry(Messages.FlameGraphContentProvider_EntryName, 0, 0);
-            extraEntries.add(entry);
-        }
-        TimeGraphEntry entry = extraEntries.get(0);
-
-        // TODO: loop in the extra data and add the events
-        while (extraChildrenSites.hasNext()) {
-            AggregatedCallSite next = extraChildrenSites.next();
-            if (next instanceof AggregatedThreadStatus) {
-               entry.addEvent(new TimeEvent(entry, lastEnd, next.getWeight(), ((AggregatedThreadStatus) next).getProcessStatus().getStateValue().unboxInt()));
-            } else {
-                entry.addEvent(new FlamegraphEvent(entry, lastEnd, next));
-            }
-            lastEnd += next.getWeight();
-            entry.updateEndTime(lastEnd);
-        }
-    }
-
-    /**
-     * Build the events list for an entry (depth), then creates recursively the
-     * events for the next entries. This parses the aggregation tree starting
-     * from the bottom. This uses a stack to save the timestamp for each
-     * function. Once we save a function's timestamp we'll use it to create the
-     * callees events.
-     *
-     * @param node
-     *            The node of the aggregation tree
-     * @param childrenEntries
-     *            The list of entries for one thread
-     * @param timestampStack
-     *            A stack used to save the functions timeStamps
-     */
-    private void addEvent(AggregatedCallSite node, List<FlamegraphDepthEntry> childrenEntries, Deque<Long> timestampStack, int depth) {
-        node.getCallees().stream()
-                .sorted(Comparator.comparingLong(AggregatedCallSite::getWeight))
-                .forEach(child -> {
-                    addEvent(child, childrenEntries, timestampStack, depth + 1);
-                });
-        node.getCallees().stream().forEach(child -> {
-            timestampStack.pop();
-        });
-
-        Long lastEnd = timestampStack.peek();
-        if (lastEnd == null) {
-            return;
-        }
-        FlamegraphDepthEntry entry = checkNotNull(childrenEntries.get(depth - 1));
-        // Create the event corresponding to the function using the caller's
-        // timestamp
-        entry.addEvent(new FlamegraphEvent(entry, lastEnd, node));
-        timestampStack.push(lastEnd + node.getWeight());
-    }
-
-    @Override
-    public boolean hasChildren(@Nullable Object element) {
-        return !fFlameGraphEntries.isEmpty();
-    }
-
-    @Override
-    public ITimeGraphEntry[] getElements(@Nullable Object inputElement) {
-        fFlameGraphEntries.clear();
-        fLevelEntries.clear();
-        // Get the root of each thread
-        if (inputElement instanceof Collection<?>) {
-            Collection<?> callgraphProviders = (Collection<?>) inputElement;
-            for (Object object : callgraphProviders) {
-                if (object instanceof CallGraph) {
-                    buildCallGraph((CallGraph) object);
-                }
-            }
-        } else {
-            return new ITimeGraphEntry[0];
-        }
-
-        // Sort the threads
-        fFlameGraphEntries.sort(fThreadComparator);
-        return fFlameGraphEntries.toArray(new ITimeGraphEntry[fFlameGraphEntries.size()]);
-    }
-
-    private void buildCallGraph(CallGraph object) {
-        Collection<ICallStackElement> elements = object.getElements();
-        for (ICallStackElement element : elements) {
-            buildChildrenEntries(element, object, null);
-        }
-    }
-
-    /**
-     * Build the entry list for one thread
-     *
-     * @param element
-     *            The node of the aggregation tree
-     */
-    private void buildChildrenEntries(ICallStackElement element, CallGraph object, @Nullable TimeGraphEntry parent) {
-        // Add the entry
-        TimeGraphEntry currentEntry = new TimeGraphEntry(element.getName(), 0L, 0L);
-        if (parent != null) {
-            parent.addChild(currentEntry);
-        } else {
-            fFlameGraphEntries.add(currentEntry);
-        }
-
-        // Create the children entries
-        for (ICallStackElement child : element.getChildrenElements()) {
-            buildChildrenEntries(child, object, currentEntry);
-        }
-
-        // Create the callsites entries
-        if (!(element.isLeaf())) {
-            return;
-        }
-
-        List<FlamegraphDepthEntry> childrenEntries = new ArrayList<>();
-        List<TimeGraphEntry> extraEntries = new ArrayList<>();
-        Deque<Long> timestampStack = new ArrayDeque<>();
-        timestampStack.push(0L);
-
-        // Sort children by duration
-        object.getCallingContextTree(element).stream()
-                .sorted(Comparator.comparingLong(AggregatedCallSite::getWeight))
-                .forEach(rootFunction -> {
-                    setData(rootFunction, childrenEntries, timestampStack);
-                    setExtraData(rootFunction, extraEntries, timestampStack);
-                    long currentThreadDuration = timestampStack.pop() + rootFunction.getWeight();
-                    timestampStack.push(currentThreadDuration);
-                });
-        childrenEntries.forEach(child -> {
-            currentEntry.addChild(child);
-        });
-        extraEntries.forEach(child -> {
-            currentEntry.addChild(child);
-        });
-        currentEntry.updateEndTime(timestampStack.pop());
-        return;
-
-    }
-
-    @Override
-    public ITimeGraphEntry[] getChildren(@Nullable Object parentElement) {
-        return fFlameGraphEntries.toArray(new TimeGraphEntry[fFlameGraphEntries.size()]);
-    }
-
-    @Override
-    public @Nullable ITimeGraphEntry getParent(@Nullable Object element) {
-        // Do nothing
-        return null;
-    }
-
-    @Override
-    public void dispose() {
-        // Do nothing
-    }
-
-    @Override
-    public void inputChanged(@Nullable Viewer viewer, @Nullable Object oldInput, @Nullable Object newInput) {
-        // Do nothing
-    }
-
-    /**
-     * Get the sort option
-     *
-     * @return the sort option.
-     */
-    public SortOption getSortOption() {
-        return fSortOption;
-    }
-
-    /**
-     * Set the sort option for sorting the thread entries
-     *
-     * @param sortOption
-     *            the sort option to set
-     *
-     */
-    public void setSortOption(SortOption sortOption) {
-        fSortOption = sortOption;
-        switch (sortOption) {
-        case BY_NAME:
-            fThreadComparator = new ThreadNameComparator();
-            break;
-        case BY_NAME_REV:
-            fThreadComparator = checkNotNull(new ThreadNameComparator().reversed());
-            break;
-        case BY_ID:
-            fThreadComparator = new ThreadIdComparator();
-            break;
-        case BY_ID_REV:
-            fThreadComparator = checkNotNull(new ThreadIdComparator().reversed());
-            break;
-        default:
-            break;
-        }
-    }
-}
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/flamegraph/FlameGraphPresentationProvider.java b/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/flamegraph/FlameGraphPresentationProvider.java
index 97ac26e..b8fa89c 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/flamegraph/FlameGraphPresentationProvider.java
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/flamegraph/FlameGraphPresentationProvider.java
@@ -8,31 +8,29 @@
  *******************************************************************************/
 package org.eclipse.tracecompass.incubator.internal.callstack.ui.flamegraph;
 
-import java.text.Format;
-import java.text.NumberFormat;
 import java.util.Collections;
+import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.Objects;
 
+import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.graphics.GC;
-import org.eclipse.swt.graphics.Rectangle;
-import org.eclipse.tracecompass.analysis.timing.ui.views.segmentstore.SubSecondTimeWithUnitFormat;
-import org.eclipse.tracecompass.incubator.analysis.core.concepts.ICallStackSymbol;
+import org.eclipse.tracecompass.incubator.internal.callstack.core.instrumented.provider.FlameChartEntryModel;
 import org.eclipse.tracecompass.incubator.internal.callstack.ui.FlameViewPalette;
-import org.eclipse.tracecompass.tmf.core.symbols.SymbolProviderManager;
-import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
-import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
+import org.eclipse.tracecompass.internal.tmf.core.model.filters.FetchParametersUtils;
+import org.eclipse.tracecompass.tmf.core.model.filters.SelectionTimeQueryFilter;
+import org.eclipse.tracecompass.tmf.core.model.timegraph.ITimeGraphDataProvider;
+import org.eclipse.tracecompass.tmf.core.model.timegraph.TimeGraphEntryModel;
+import org.eclipse.tracecompass.tmf.core.model.tree.ITmfTreeDataModel;
+import org.eclipse.tracecompass.tmf.core.response.TmfModelResponse;
 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.StateItem;
 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.TimeGraphPresentationProvider;
 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeEvent;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeGraphEntry;
 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.NullTimeEvent;
 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeEvent;
-import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.Utils;
-
-import com.google.common.collect.ImmutableMap;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeGraphEntry;
 
 /**
  * Presentation provider for the flame graph view, based on the generic TMF
@@ -44,12 +42,8 @@
     /** Number of colors used for flameGraph events */
     public static final int NUM_COLORS = 360;
 
-    private static final Format FORMATTER = new SubSecondTimeWithUnitFormat();
-
     private @Nullable FlameGraphView fView;
 
-    private @Nullable Integer fAverageCharWidth;
-
     private FlameViewPalette fFlameViewPalette;
 
     /**
@@ -77,70 +71,56 @@
     @NonNullByDefault({})
     @Override
     public Map<String, String> getEventHoverToolTipInfo(ITimeEvent event, long hoverTime) {
-        if (!(event instanceof FlamegraphEvent)) {
-            return Collections.emptyMap();
+        Map<String, String> retMap = super.getEventHoverToolTipInfo(event, hoverTime);
+        if (retMap == null) {
+            retMap = new LinkedHashMap<>(1);
         }
-        ImmutableMap.Builder<String, String> builder = new ImmutableMap.Builder<>();
-        ITmfTrace activeTrace = TmfTraceManager.getInstance().getActiveTrace();
-        String funcSymbol = null;
-        FlamegraphEvent fgEvent = (FlamegraphEvent) event;
-        Object symbol = fgEvent.getSymbol();
-        if (activeTrace != null && (symbol instanceof ICallStackSymbol)) {
-            funcSymbol = ((ICallStackSymbol) symbol).resolve(SymbolProviderManager.getInstance().getSymbolProviders(activeTrace));
+        if (!(event instanceof TimeEvent) || !((TimeEvent) event).hasValue() ||
+                !(event.getEntry() instanceof TimeGraphEntry)) {
+            return retMap;
         }
-        builder.put(Messages.FlameGraph_Symbol, funcSymbol == null ? String.valueOf(fgEvent.getSymbol()) : funcSymbol + " (" + fgEvent.getSymbol() + ")"); //$NON-NLS-1$ //$NON-NLS-2$
-        long nb = (fgEvent.getNumberOfCalls());
-        builder.put(Messages.FlameGraph_NbCalls, NumberFormat.getIntegerInstance().format(nb)); // $NON-NLS-1$
-        Map<String, String> tooltip = ((FlamegraphEvent) event).getTooltip(FORMATTER);
-        builder.putAll(tooltip);
-        return builder.build();
+
+        TimeGraphEntry entry = (TimeGraphEntry) event.getEntry();
+        ITimeGraphDataProvider<? extends TimeGraphEntryModel> dataProvider = FlameGraphView.getProvider(entry);
+        TmfModelResponse<@NonNull Map<@NonNull String, @NonNull String>> response = dataProvider.fetchTooltip(
+        FetchParametersUtils.selectionTimeQueryToMap(new SelectionTimeQueryFilter(hoverTime, hoverTime, 1, Collections.singletonList(entry.getEntryModel().getId()))), null);
+        Map<@NonNull String, @NonNull String> tooltipModel = response.getModel();
+        if (tooltipModel != null) {
+            retMap.putAll(tooltipModel);
+        }
+
+        return retMap;
     }
 
     @Override
     public int getStateTableIndex(@Nullable ITimeEvent event) {
-        if (event instanceof FlamegraphEvent) {
-            FlamegraphEvent flameGraphEvent = (FlamegraphEvent) event;
-            return FlameViewPalette.getIndexForValue(flameGraphEvent.getValue());
-        } else if (event instanceof NullTimeEvent) {
+        if (event == null || event instanceof NullTimeEvent) {
             return INVISIBLE;
-        } else if (event instanceof TimeEvent) {
-            int cfIndex = fFlameViewPalette.getControlFlowIndex(event);
-            if (cfIndex >= 0) {
-                return cfIndex;
+        }
+        ITimeGraphEntry entry = event.getEntry();
+        if (event instanceof TimeEvent && entry instanceof TimeGraphEntry) {
+            TimeGraphEntry tgEntry = (TimeGraphEntry) entry;
+            ITmfTreeDataModel entryModel = tgEntry.getEntryModel();
+            if (entryModel instanceof FlameChartEntryModel) {
+                switch(((FlameChartEntryModel) entryModel).getEntryType()) {
+                case FUNCTION:
+                    return FlameViewPalette.getIndexForValue(((TimeEvent) event).getValue());
+                case KERNEL:
+                    int cfIndex = fFlameViewPalette.getControlFlowIndex(event);
+                    if (cfIndex >= 0) {
+                        return cfIndex;
+                    }
+                    break;
+                case LEVEL: // Fallthrough
+                case TRACE: // Fallthrough
+                default:
+                    break;
+                }
             }
         }
-        return FlameViewPalette.MULTIPLE_STATE_INDEX;
-    }
 
-    @Override
-    public void postDrawEvent(@Nullable ITimeEvent event, @Nullable Rectangle bounds, @Nullable GC gc) {
-        if (bounds == null || gc == null) {
-            return;
-        }
-        Integer averageCharWidth = fAverageCharWidth;
-        if (averageCharWidth == null) {
-            averageCharWidth = gc.getFontMetrics().getAverageCharWidth();
-            fAverageCharWidth = averageCharWidth;
-        }
-        if (bounds.width <= averageCharWidth) {
-            return;
-        }
-        if (!(event instanceof FlamegraphEvent)) {
-            return;
-        }
-        String funcSymbol = ""; //$NON-NLS-1$
-        ITmfTrace activeTrace = TmfTraceManager.getInstance().getActiveTrace();
-        if (activeTrace != null) {
-            FlamegraphEvent fgEvent = (FlamegraphEvent) event;
-            Object symbol = fgEvent.getSymbol();
-            if (symbol instanceof ICallStackSymbol) {
-                funcSymbol = ((ICallStackSymbol) symbol).resolve(SymbolProviderManager.getInstance().getSymbolProviders(activeTrace));
-            } else {
-                funcSymbol = String.valueOf(symbol);
-            }
-        }
-        gc.setForeground(gc.getDevice().getSystemColor(SWT.COLOR_WHITE));
-        Utils.drawText(gc, funcSymbol, bounds.x, bounds.y, bounds.width, bounds.height, true, true);
+
+        return FlameViewPalette.MULTIPLE_STATE_INDEX;
     }
 
     /**
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/flamegraph/FlameGraphSelView.java b/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/flamegraph/FlameGraphSelView.java
index 2d7dfc0..d1f51d9 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/flamegraph/FlameGraphSelView.java
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/flamegraph/FlameGraphSelView.java
@@ -16,6 +16,7 @@
 import org.eclipse.tracecompass.tmf.core.signal.TmfSelectionRangeUpdatedSignal;
 import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
 import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
 
 /**
  * View to display the flame graph. This uses the flameGraphNode tree generated
@@ -48,13 +49,17 @@
      */
     @TmfSignalHandler
     public void handleSelectionChange(TmfSelectionRangeUpdatedSignal signal) {
+        ITmfTrace trace = getTrace();
+        if (trace == null) {
+            return;
+        }
         ITmfTimestamp beginTime = signal.getBeginTime();
         ITmfTimestamp endTime = signal.getEndTime();
 
         if (beginTime != endTime) {
-            buildFlameGraph(getCallgraphModules(), beginTime, endTime);
+            buildFlameGraph(trace, beginTime, endTime);
         } else {
-            buildFlameGraph(getCallgraphModules(), null, null);
+            buildFlameGraph(trace, null, null);
         }
 
     }
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/flamegraph/FlameGraphView.java b/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/flamegraph/FlameGraphView.java
index f235c2d..a0e230a 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/flamegraph/FlameGraphView.java
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/flamegraph/FlameGraphView.java
@@ -14,10 +14,15 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Set;
+import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.logging.Level;
@@ -29,6 +34,7 @@
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.NullProgressMonitor;
 import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
 import org.eclipse.core.runtime.jobs.Job;
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.NonNullByDefault;
@@ -53,6 +59,8 @@
 import org.eclipse.swt.events.MenuDetectListener;
 import org.eclipse.swt.events.MouseAdapter;
 import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Control;
 import org.eclipse.swt.widgets.Display;
@@ -60,15 +68,29 @@
 import org.eclipse.tracecompass.common.core.NonNullUtils;
 import org.eclipse.tracecompass.common.core.log.TraceCompassLogUtils.FlowScopeLog;
 import org.eclipse.tracecompass.common.core.log.TraceCompassLogUtils.FlowScopeLogBuilder;
+import org.eclipse.tracecompass.incubator.analysis.core.weighted.tree.AllGroupDescriptor;
 import org.eclipse.tracecompass.incubator.analysis.core.weighted.tree.IWeightedTreeGroupDescriptor;
-import org.eclipse.tracecompass.incubator.callstack.core.callgraph.AllGroupDescriptor;
-import org.eclipse.tracecompass.incubator.callstack.core.callgraph.CallGraph;
-import org.eclipse.tracecompass.incubator.callstack.core.callgraph.CallGraphGroupBy;
 import org.eclipse.tracecompass.incubator.callstack.core.callgraph.ICallGraphProvider;
-import org.eclipse.tracecompass.incubator.callstack.core.instrumented.ICalledFunction;
+import org.eclipse.tracecompass.incubator.internal.callstack.core.flamegraph.FlameGraphDataProvider;
 import org.eclipse.tracecompass.incubator.internal.callstack.ui.Activator;
+import org.eclipse.tracecompass.internal.provisional.tmf.core.model.filters.TmfFilterAppliedSignal;
+import org.eclipse.tracecompass.internal.provisional.tmf.core.model.filters.TraceCompassFilter;
+import org.eclipse.tracecompass.internal.tmf.core.model.filters.FetchParametersUtils;
+import org.eclipse.tracecompass.statesystem.core.StateSystemUtils;
 import org.eclipse.tracecompass.tmf.core.analysis.IAnalysisModule;
-import org.eclipse.tracecompass.tmf.core.signal.TmfSelectionRangeUpdatedSignal;
+import org.eclipse.tracecompass.tmf.core.dataprovider.DataProviderManager;
+import org.eclipse.tracecompass.tmf.core.dataprovider.DataProviderParameterUtils;
+import org.eclipse.tracecompass.tmf.core.model.filters.SelectionTimeQueryFilter;
+import org.eclipse.tracecompass.tmf.core.model.timegraph.IFilterProperty;
+import org.eclipse.tracecompass.tmf.core.model.timegraph.ITimeGraphDataProvider;
+import org.eclipse.tracecompass.tmf.core.model.timegraph.ITimeGraphRowModel;
+import org.eclipse.tracecompass.tmf.core.model.timegraph.ITimeGraphState;
+import org.eclipse.tracecompass.tmf.core.model.timegraph.TimeGraphEntryModel;
+import org.eclipse.tracecompass.tmf.core.model.timegraph.TimeGraphModel;
+import org.eclipse.tracecompass.tmf.core.model.timegraph.TimeGraphState;
+import org.eclipse.tracecompass.tmf.core.model.tree.TmfTreeModel;
+import org.eclipse.tracecompass.tmf.core.response.ITmfResponse;
+import org.eclipse.tracecompass.tmf.core.response.TmfModelResponse;
 import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
 import org.eclipse.tracecompass.tmf.core.signal.TmfSignalManager;
 import org.eclipse.tracecompass.tmf.core.signal.TmfTraceClosedSignal;
@@ -76,10 +98,10 @@
 import org.eclipse.tracecompass.tmf.core.symbols.ISymbolProvider;
 import org.eclipse.tracecompass.tmf.core.symbols.SymbolProviderManager;
 import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp;
-import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp;
 import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
 import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
 import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils;
+import org.eclipse.tracecompass.tmf.ui.TmfUiRefreshHandler;
 import org.eclipse.tracecompass.tmf.ui.editors.ITmfTraceEditor;
 import org.eclipse.tracecompass.tmf.ui.symbols.ISymbolProviderPreferencePage;
 import org.eclipse.tracecompass.tmf.ui.symbols.SymbolProviderConfigDialog;
@@ -88,6 +110,13 @@
 import org.eclipse.tracecompass.tmf.ui.views.TmfView;
 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.TimeGraphPresentationProvider;
 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.TimeGraphViewer;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeEvent;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeGraphEntry;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.NamedTimeEvent;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.NullTimeEvent;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeEvent;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeGraphEntry;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeGraphEntry.Sampling;
 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.TimeGraphControl;
 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.Utils.TimeFormat;
 import org.eclipse.ui.IActionBars;
@@ -97,7 +126,10 @@
 import org.eclipse.ui.progress.IWorkbenchSiteProgressService;
 
 import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.LinkedHashMultimap;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
 import com.google.common.collect.Multimap;
 
 /**
@@ -114,7 +146,7 @@
      */
     public static final String ID = FlameGraphView.class.getPackage().getName() + ".flamegraphView"; //$NON-NLS-1$
 
-    private static final String SYMBOL_MAPPING_ICON_PATH = "icons/obj16/binaries_obj.gif"; //$NON-NLS-1$
+    private static final @NonNull String SYMBOL_MAPPING_ICON_PATH = "icons/obj16/binaries_obj.gif"; //$NON-NLS-1$
     private static final @NonNull String GROUP_BY_ICON_PATH = "icons/etool16/group_by.gif"; //$NON-NLS-1$
 
     private static final String SORT_OPTION_KEY = "sort.option"; //$NON-NLS-1$
@@ -124,9 +156,11 @@
     private static final ImageDescriptor SORT_BY_ID_REV_ICON = Activator.getDefault().getImageDescripterFromPath("icons/etool16/sort_num_rev.gif"); //$NON-NLS-1$
     private static final ImageDescriptor AGGREGATE_BY_ICON = Activator.getDefault().getImageDescripterFromPath(GROUP_BY_ICON_PATH);
 
+    private static final int DEFAULT_BUFFER_SIZE = 3;
+
     private TimeGraphViewer fTimeGraphViewer;
 
-    private FlameGraphContentProvider fTimeGraphContentProvider;
+    private SortOption fSortOption = SortOption.BY_NAME;
 
     private TimeGraphPresentationProvider fPresentationProvider;
 
@@ -140,7 +174,6 @@
     // The action to import a binary file mapping */
     private Action fConfigureSymbolsAction;
 
-    private final Multimap<ITmfTrace, ISymbolProvider> fSymbolProviders = LinkedHashMultimap.create();
     private @Nullable IWeightedTreeGroupDescriptor fGroupBy = null;
     /**
      * A plain old semaphore is used since different threads will be competing
@@ -151,7 +184,24 @@
     // Variable used to specify when the graph is dirty, ie waiting for data refresh
     private final AtomicInteger fDirty = new AtomicInteger();
 
-    private Job fJob;
+    /** The trace to build thread hash map */
+    private final Map<ITmfTrace, Job> fBuildJobMap = new HashMap<>();
+    private final Map<ITimeGraphDataProvider<? extends @NonNull TimeGraphEntryModel>, Map<Long, @NonNull TimeGraphEntry>> fEntries = new HashMap<>();
+
+    /**
+     * Set of visible entries to zoom on.
+     */
+    private @NonNull Set<@NonNull TimeGraphEntry> fVisibleEntries = Collections.emptySet();
+
+    private long fEndTime = Long.MIN_VALUE;
+
+    /** The trace to entry list hash map */
+    private final Map<ITmfTrace, List<@NonNull TimeGraphEntry>> fEntryListMap = new HashMap<>();
+
+    private int fDisplayWidth;
+    private @Nullable ZoomThread fZoomThread;
+    private final Object fZoomThreadResultLock = new Object();
+
 
     /**
      * Constructor
@@ -173,10 +223,9 @@
     @Override
     public void createPartControl(Composite parent) {
         super.createPartControl(parent);
+        fDisplayWidth = Display.getDefault().getBounds().width;
         fTimeGraphViewer = new TimeGraphViewer(parent, SWT.NONE);
-        fTimeGraphContentProvider = new FlameGraphContentProvider();
         fPresentationProvider = new FlameGraphPresentationProvider();
-        fTimeGraphViewer.setTimeGraphContentProvider(fTimeGraphContentProvider);
         fTimeGraphViewer.setTimeGraphProvider(fPresentationProvider);
         fTimeGraphViewer.setTimeFormat(TimeFormat.NUMBER);
         IEditorPart editor = getSite().getPage().getActiveEditor();
@@ -198,8 +247,8 @@
                 ISelection selection = timeGraphControl.getSelection();
                 if (selection instanceof IStructuredSelection) {
                     for (Object object : ((IStructuredSelection) selection).toList()) {
-                        if (object instanceof FlamegraphEvent) {
-                            FlamegraphEvent event = (FlamegraphEvent) object;
+                        if (object instanceof TimeEvent) {
+                            TimeEvent event = (TimeEvent) object;
                             long startTime = event.getTime();
                             long endTime = startTime + event.getDuration();
                             getTimeGraphViewer().setStartFinishTime(startTime, endTime);
@@ -209,6 +258,41 @@
                 }
             }
         });
+        fTimeGraphViewer.addRangeListener(event -> {
+            startZoomThread(event.getStartTime(), event.getEndTime(), false);
+        });
+        TimeGraphControl timeGraphControl = fTimeGraphViewer.getTimeGraphControl();
+        timeGraphControl.addPaintListener(new PaintListener() {
+
+            /**
+             * This paint control allows the virtual time graph refresh to occur on paint
+             * events instead of just scrolling the time axis or zooming. To avoid
+             * refreshing the model on every paint event, we use a TmfUiRefreshHandler to
+             * coalesce requests and only execute the last one, we also check if the entries
+             * have changed to avoid useless model refresh.
+             *
+             * @param e
+             *            paint event on the visible area
+             */
+            @Override
+            public void paintControl(PaintEvent e) {
+                TmfUiRefreshHandler.getInstance().queueUpdate(this, () -> {
+                    if (timeGraphControl.isDisposed()) {
+                        return;
+                    }
+                    Set<@NonNull TimeGraphEntry> newSet = getVisibleItems(DEFAULT_BUFFER_SIZE);
+                    if (!fVisibleEntries.equals(newSet)) {
+                        /*
+                         * Start a zoom thread if the set of visible entries has changed. We do not use
+                         * lists as the order is not important. We cannot use the start index / size of
+                         * the visible entries as we can collapse / reorder events.
+                         */
+                        fVisibleEntries = newSet;
+                        startZoomThread(getTimeGraphViewer().getTime0(), getTimeGraphViewer().getTime1(), false);
+                    }
+                });
+            }
+        });
     }
 
     /**
@@ -229,8 +313,29 @@
      */
     @TmfSignalHandler
     public void traceSelected(final TmfTraceSelectedSignal signal) {
-        fTrace = signal.getTrace();
-        Display.getDefault().asyncExec(() -> buildFlameGraph(getCallgraphModules(), null, null));
+        ITmfTrace trace = signal.getTrace();
+
+        fTrace = trace;
+        if (trace == null) {
+            return;
+        }
+        // If entries for this trace are already available, just zoom on them,
+        // otherwise, rebuild
+        List<@NonNull TimeGraphEntry> list = fEntryListMap.get(trace);
+        if (list == null) {
+            refresh();
+            Display.getDefault().asyncExec(() -> buildFlameGraph(trace, null, null));
+        } else {
+            // Reset end time
+            long endTime = Long.MIN_VALUE;
+            for (TimeGraphEntry entry : list) {
+                endTime = Math.max(endTime, entry.getEndTime());
+            }
+            setEndTime(endTime);
+            refresh();
+            startZoomThread(0, endTime, false);
+        }
+
     }
 
     /**
@@ -255,11 +360,698 @@
                 .collect(Collectors.toSet());
     }
 
+    private String getProviderId() {
+        String secondaryId = this.getViewSite().getSecondaryId();
+        return (secondaryId == null) ? FlameGraphDataProvider.ID : FlameGraphDataProvider.ID + ':' + secondaryId;
+    }
+
+    private class BuildRunnable {
+        private final @NonNull ITmfTrace fBuildTrace;
+        private final @NonNull ITmfTrace fParentTrace;
+        private final @NonNull FlowScopeLog fScope;
+        private final @NonNull Map<String, Object> fParameters;
+
+        public BuildRunnable(final @NonNull ITmfTrace trace, final @NonNull ITmfTrace parentTrace, @Nullable ITmfTimestamp selStart, @Nullable ITmfTimestamp selEnd, final @NonNull FlowScopeLog log) {
+            fBuildTrace = trace;
+            fParentTrace = parentTrace;
+            fScope = log;
+            if (selStart != null && selEnd != null) {
+                fParameters = ImmutableMap.of(FlameGraphDataProvider.SELECTION_RANGE_KEY, ImmutableList.of(selStart.toNanos(), selEnd.toNanos()));
+            } else {
+                fParameters = Collections.emptyMap();
+            }
+        }
+
+        public void run(IProgressMonitor monitor) {
+            try (FlowScopeLog log = new FlowScopeLogBuilder(LOGGER, Level.FINE, "FlameGraphView:BuildThread", "trace", fBuildTrace.getName()).setParentScope(fScope).build()) { //$NON-NLS-1$ //$NON-NLS-2$
+                buildEntryList(fBuildTrace, fParentTrace, fParameters, NonNullUtils.checkNotNull(monitor));
+                synchronized (fBuildJobMap) {
+                    fBuildJobMap.remove(fBuildTrace);
+                }
+            }
+        }
+    }
+
+    private void buildEntryList(@NonNull ITmfTrace trace, @NonNull ITmfTrace parentTrace, @NonNull Map<String, Object> additionalParams, @NonNull IProgressMonitor monitor) {
+        ITimeGraphDataProvider<@NonNull TimeGraphEntryModel> dataProvider = DataProviderManager
+                .getInstance().getDataProvider(trace, getProviderId(), ITimeGraphDataProvider.class);
+        if (dataProvider == null) {
+            return;
+        }
+        boolean complete = false;
+        while (!complete && !monitor.isCanceled()) {
+            Map<String, Object> parameters = new HashMap<>(additionalParams);
+            parameters.put(DataProviderParameterUtils.REQUESTED_TIME_KEY, ImmutableList.of(0, Long.MAX_VALUE));
+            IWeightedTreeGroupDescriptor groupBy = fGroupBy;
+            if (groupBy != null) {
+                parameters.put(FlameGraphDataProvider.GROUP_BY_KEY, groupBy.getName());
+            }
+            TmfModelResponse<TmfTreeModel<@NonNull TimeGraphEntryModel>> response = dataProvider.fetchTree(parameters, monitor);
+            if (response.getStatus() == ITmfResponse.Status.FAILED) {
+                Activator.getDefault().logError(getClass().getSimpleName() + " Data Provider failed: " + response.getStatusMessage()); //$NON-NLS-1$
+                return;
+            } else if (response.getStatus() == ITmfResponse.Status.CANCELLED) {
+                return;
+            }
+            complete = response.getStatus() == ITmfResponse.Status.COMPLETED;
+
+
+            TmfTreeModel<@NonNull TimeGraphEntryModel> model = response.getModel();
+            long endTime = Long.MIN_VALUE;
+            if (model != null) {
+                Map<Long, TimeGraphEntry> entries;
+                synchronized (fEntries) {
+                    entries = fEntries.computeIfAbsent(dataProvider, dp -> new HashMap<>());
+                    /*
+                     * The provider may send entries unordered and parents may
+                     * not exist when child is constructor, we'll re-unite
+                     * families at the end
+                     */
+                    List<TimeGraphEntry> orphaned = new ArrayList<>();
+                    for (TimeGraphEntryModel entry : model.getEntries()) {
+                        TimeGraphEntry uiEntry = entries.get(entry.getId());
+                        if (entry.getParentId() != -1) {
+                            if (uiEntry == null) {
+                                uiEntry = new TimeGraphEntry(entry);
+                                TimeGraphEntry parent = entries.get(entry.getParentId());
+                                if (parent != null) {
+                                    parent.addChild(uiEntry);
+                                } else {
+                                    orphaned.add(uiEntry);
+                                }
+                                entries.put(entry.getId(), uiEntry);
+                            } else {
+                                uiEntry.updateModel(entry);
+                            }
+                        } else {
+                            endTime = Long.max(endTime, entry.getEndTime() + 1);
+
+                            if (uiEntry != null) {
+                                uiEntry.updateModel(entry);
+                            } else {
+                                // Do not assume that parentless entries are
+                                // trace entries
+                                uiEntry = new ParentEntry(entry, dataProvider);
+                                entries.put(entry.getId(), uiEntry);
+                                addToEntryList(parentTrace, Collections.singletonList(uiEntry));
+                            }
+                        }
+                    }
+                    setEndTime(endTime);
+                    // Find missing parents
+                    for (TimeGraphEntry orphanedEntry : orphaned) {
+                        TimeGraphEntry parent = entries.get(orphanedEntry.getEntryModel().getParentId());
+                        if (parent != null) {
+                            parent.addChild(orphanedEntry);
+                        }
+                    }
+                }
+
+                long start = 0;
+                long end = getEndTime();
+                final long resolution = Long.max(1, (end - start) / getDisplayWidth());
+                zoomEntries(ImmutableList.copyOf(entries.values()), start, end, resolution, monitor);
+            }
+
+            if (monitor.isCanceled()) {
+                return;
+            }
+
+            if (parentTrace.equals(getTrace())) {
+                refresh();
+            }
+            monitor.worked(1);
+
+            if (!complete && !monitor.isCanceled()) {
+                try {
+                    Thread.sleep(100);
+                } catch (InterruptedException e) {
+                    Activator.getDefault().logError("Failed to wait for data provider", e); //$NON-NLS-1$
+                }
+            }
+        }
+    }
+
+    /**
+     * Zoom thread
+     *
+     * @since 1.1
+     */
+    protected class ZoomThread extends Thread {
+        private final long fZoomStartTime;
+        private final long fZoomEndTime;
+        private final long fResolution;
+        private int fScopeId = -1;
+        private final @NonNull IProgressMonitor fMonitor;
+        private @NonNull Collection<@NonNull TimeGraphEntry> fCurrentEntries;
+        private boolean fForce;
+
+
+        /**
+         * Constructor
+         *
+         * @param entries
+         *            The entries to zoom on
+         * @param startTime
+         *            the start time
+         * @param endTime
+         *            the end time
+         * @param resolution
+         *            the resolution
+         * @param force
+         *            Whether to force the zoom of all entries or only those
+         *            that have not the same sampling
+         */
+        public ZoomThread(@NonNull Collection<@NonNull TimeGraphEntry> entries, long startTime, long endTime, long resolution, boolean force) {
+            super(FlameGraphView.this.getName() + " zoom"); //$NON-NLS-1$
+            fZoomStartTime = startTime;
+            fZoomEndTime = endTime;
+            fResolution = resolution;
+            fCurrentEntries = entries;
+            fMonitor = new NullProgressMonitor();
+            fForce = force;
+        }
+
+        /**
+         * @return the zoom start time
+         */
+        public long getZoomStartTime() {
+            return fZoomStartTime;
+        }
+
+        /**
+         * @return the zoom end time
+         */
+        public long getZoomEndTime() {
+            return fZoomEndTime;
+        }
+
+        /**
+         * @return the resolution
+         */
+        public long getResolution() {
+            return fResolution;
+        }
+
+        /**
+         * @return the monitor
+         */
+        public @NonNull IProgressMonitor getMonitor() {
+            return fMonitor;
+        }
+
+        /**
+         * Cancel the zoom thread
+         */
+        public void cancel() {
+            fMonitor.setCanceled(true);
+        }
+
+        @Override
+        public final void run() {
+            try (FlowScopeLog log = new FlowScopeLogBuilder(LOGGER, Level.FINE, "FlameGraphView:ZoomThread", "start", fZoomStartTime, "end", fZoomEndTime).setCategoryAndId(getViewId(), fScopeId).build()) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+                if (fCurrentEntries.isEmpty()) {
+                    // No rows to zoom on
+                    return;
+                }
+                Sampling sampling = new Sampling(getZoomStartTime(), getZoomEndTime(), getResolution());
+                Iterable<@NonNull TimeGraphEntry> incorrectSample = fForce ? fCurrentEntries : Iterables.filter(fCurrentEntries, entry -> !sampling.equals(entry.getSampling()));
+                zoomEntries(incorrectSample, getZoomStartTime(), getZoomEndTime(), getResolution(), getMonitor());
+            } finally {
+
+                if (fDirty.decrementAndGet() < 0) {
+                    Activator.getDefault().logError("Dirty underflow", new Throwable()); //$NON-NLS-1$
+                }
+            }
+        }
+
+        /**
+         * Set the ID of the calling flow scope. This data will allow to determine the
+         * causality between the zoom thread and its caller if tracing is enabled.
+         *
+         * @param scopeId
+         *            The ID of the calling flow scope
+         * @since 3.0
+         */
+        public void setScopeId(int scopeId) {
+            fScopeId = scopeId;
+        }
+    }
+
+    /**
+     * Start or restart the zoom thread.
+     *
+     * @param startTime
+     *            the zoom start time
+     * @param endTime
+     *            the zoom end time
+     * @param force
+     *            Whether to force the fetch of all rows, or only those that
+     *            don't have the same range
+     */
+    protected final void startZoomThread(long startTime, long endTime, boolean force) {
+        ITmfTrace trace = getTrace();
+        if (trace == null) {
+            return;
+        }
+
+        fDirty.incrementAndGet();
+        try (FlowScopeLog log = new FlowScopeLogBuilder(LOGGER, Level.FINE, "FlameGraphView:ZoomThreadCreated").setCategory(getViewId()).build()) { //$NON-NLS-1$
+            long clampedStartTime = Math.max(0, Math.min(startTime, getEndTime()));
+            long clampedEndTime = Math.min(getEndTime(), Math.max(endTime, 0));
+            // Ignore if end time < start time, data has not been set correctly [yet]
+            if (clampedEndTime < clampedStartTime) {
+                return;
+            }
+            ZoomThread zoomThread = fZoomThread;
+            if (zoomThread != null) {
+                zoomThread.cancel();
+            }
+            int timeSpace = getTimeGraphViewer().getTimeSpace();
+            if (timeSpace > 0) {
+                long resolution = Long.max(1, (clampedEndTime - clampedStartTime) / timeSpace);
+                zoomThread = new ZoomThread(getVisibleItems(DEFAULT_BUFFER_SIZE), clampedStartTime, clampedEndTime, resolution, force);
+            } else {
+                zoomThread = null;
+            }
+            fZoomThread = zoomThread;
+            if (zoomThread != null) {
+                zoomThread.setScopeId(log.getId());
+                /*
+                 * Don't start a new thread right away if results are being applied from an old
+                 * ZoomThread. Otherwise, the old results might overwrite the new results if it
+                 * finishes after.
+                 */
+                synchronized (fZoomThreadResultLock) {
+                    zoomThread.start();
+                    // zoomThread decrements, so we increment here
+                    fDirty.incrementAndGet();
+                }
+            }
+        } finally {
+            if (fDirty.decrementAndGet() < 0) {
+                Activator.getDefault().logError("Dirty underflow", new Throwable()); //$NON-NLS-1$
+            }
+        }
+    }
+
+    private @NonNull Set<@NonNull TimeGraphEntry> getVisibleItems(int buffer) {
+
+        TimeGraphControl timeGraphControl = fTimeGraphViewer.getTimeGraphControl();
+        if (timeGraphControl.isDisposed()) {
+            return Collections.emptySet();
+        }
+
+        int start = Integer.max(0, fTimeGraphViewer.getTopIndex() - buffer);
+        int end = Integer.min(fTimeGraphViewer.getExpandedElementCount() - 1,
+                fTimeGraphViewer.getTopIndex() + timeGraphControl.countPerPage() + buffer);
+
+        Set<@NonNull TimeGraphEntry> visible = new HashSet<>(end - start + 1);
+        for (int i = start; i <= end; i++) {
+            /*
+             * Use the getExpandedElement by index to avoid creating a copy of
+             * all the the elements.
+             */
+            TimeGraphEntry element = (TimeGraphEntry) timeGraphControl.getExpandedElement(i);
+            if (element != null) {
+                visible.add(element);
+            }
+        }
+        return visible;
+
+    }
+
+    private void zoomEntries(@NonNull Iterable<@NonNull TimeGraphEntry> entries, long zoomStartTime, long zoomEndTime, long resolution, @NonNull IProgressMonitor monitor) {
+        if (resolution < 0) {
+            // StateSystemUtils.getTimes would throw an illegal argument exception.
+            return;
+        }
+
+        long start = Long.min(zoomStartTime, zoomEndTime);
+        long end = Long.max(zoomStartTime, zoomEndTime);
+        List<@NonNull Long> times = StateSystemUtils.getTimes(start, end, resolution);
+        Sampling sampling = new Sampling(start, end, resolution);
+        Multimap<ITimeGraphDataProvider<? extends TimeGraphEntryModel>, Long> providersToModelIds = filterGroupEntries(entries, zoomStartTime, zoomEndTime);
+        SubMonitor subMonitor = SubMonitor.convert(monitor, getClass().getSimpleName() + "#zoomEntries", providersToModelIds.size()); //$NON-NLS-1$
+
+        for (Entry<ITimeGraphDataProvider<? extends TimeGraphEntryModel>, Collection<Long>> entry : providersToModelIds.asMap().entrySet()) {
+            ITimeGraphDataProvider<? extends TimeGraphEntryModel> dataProvider = entry.getKey();
+            SelectionTimeQueryFilter filter = new SelectionTimeQueryFilter(times, entry.getValue());
+            Map<@NonNull String, @NonNull Object> parameters = FetchParametersUtils.selectionTimeQueryToMap(filter);
+            Multimap<@NonNull Integer, @NonNull String> regexesMap = getRegexes();
+            if (!regexesMap.isEmpty()) {
+                parameters.put(DataProviderParameterUtils.REGEX_MAP_FILTERS_KEY, regexesMap.asMap());
+            }
+            TmfModelResponse<TimeGraphModel> response = dataProvider.fetchRowModel(parameters, monitor);
+
+            TimeGraphModel model = response.getModel();
+            if (model != null) {
+                zoomEntries(fEntries.get(dataProvider), model.getRows(), response.getStatus() == ITmfResponse.Status.COMPLETED, sampling);
+            }
+            subMonitor.worked(1);
+        }
+        redraw();
+    }
+
+    /**
+     * This method build the multimap of regexes by property that will be used to
+     * filter the timegraph states
+     *
+     * Override this method to add other regexes with their properties. The data
+     * provider should handle everything after.
+     *
+     * @return The multimap of regexes by property
+     */
+    private @NonNull Multimap<@NonNull Integer, @NonNull String> getRegexes() {
+        Multimap<@NonNull Integer, @NonNull String> regexes = HashMultimap.create();
+
+        ITmfTrace trace = getTrace();
+        if (trace == null) {
+            return regexes;
+        }
+        TraceCompassFilter globalFilter = TraceCompassFilter.getFilterForTrace(trace);
+        if (globalFilter == null) {
+            return regexes;
+        }
+        regexes.putAll(IFilterProperty.DIMMED, globalFilter.getRegexes());
+
+        return regexes;
+    }
+
+    private void zoomEntries(Map<Long, TimeGraphEntry> map, List<ITimeGraphRowModel> model, boolean completed, Sampling sampling) {
+        boolean isZoomThread = false; // Thread.currentThread() instanceof ZoomThread;
+        for (ITimeGraphRowModel rowModel : model) {
+            TimeGraphEntry entry = map.get(rowModel.getEntryID());
+
+            if (entry != null) {
+                List<ITimeEvent> events = createTimeEvents(entry, rowModel.getStates());
+                if (isZoomThread) {
+                    synchronized (fZoomThreadResultLock) {
+                        Display.getDefault().asyncExec(() -> {
+                            entry.setZoomedEventList(events);
+                            if (completed) {
+                                entry.setSampling(sampling);
+                            }
+                        });
+                    }
+                } else {
+                    entry.setEventList(events);
+                }
+            }
+        }
+    }
+
+    /**
+     * Create {@link ITimeEvent}s for an entry from the list of
+     * {@link ITimeGraphState}s, filling in the gaps.
+     *
+     * @param entry
+     *            the {@link TimeGraphEntry} on which we are working
+     * @param values
+     *            the list of {@link ITimeGraphState}s from the
+     *            {@link ITimeGraphDataProvider}.
+     * @return a contiguous List of {@link ITimeEvent}s
+     */
+    private List<ITimeEvent> createTimeEvents(TimeGraphEntry entry, List<ITimeGraphState> values) {
+        List<ITimeEvent> events = new ArrayList<>(values.size());
+        ITimeEvent prev = null;
+        for (ITimeGraphState state : values) {
+            ITimeEvent event = createTimeEvent(entry, state);
+            if (prev != null) {
+                long prevEnd = prev.getTime() + prev.getDuration();
+                if (prevEnd < event.getTime()) {
+                    // fill in the gap.
+                    TimeEvent timeEvent = new TimeEvent(entry, prevEnd, event.getTime() - prevEnd);
+                    events.add(timeEvent);
+                }
+            }
+            prev = event;
+            events.add(event);
+        }
+        return events;
+    }
+
+    /**
+     * Create a {@link TimeEvent} for a {@link TimeGraphEntry} and a
+     * {@link TimeGraphState}
+     *
+     * @param entry
+     *            {@link TimeGraphEntry} for which we create a state
+     * @param state
+     *            {@link ITimeGraphState} from the data provider
+     * @return a new {@link TimeEvent} for these arguments
+     *
+     * @since 3.3
+     */
+    protected TimeEvent createTimeEvent(TimeGraphEntry entry, ITimeGraphState state) {
+        String label = state.getLabel();
+        if (state.getValue() == Integer.MIN_VALUE && label == null) {
+            return new NullTimeEvent(entry, state.getStartTime(), state.getDuration());
+        }
+        if (label != null) {
+            return new NamedTimeEvent(entry, state.getStartTime(), state.getDuration(), state.getValue(), label, state.getActiveProperties());
+        }
+        return new TimeEvent(entry, state.getStartTime(), state.getDuration(), state.getValue(), state.getActiveProperties());
+    }
+
+    /**
+     * Filter the entries to return only the Non Null {@link TimeGraphEntry} which
+     * intersect the time range.
+     *
+     * @param visible
+     *            the input list of visible entries
+     * @param zoomStartTime
+     *            the leftmost time bound of the view
+     * @param zoomEndTime
+     *            the rightmost time bound of the view
+     * @return A Multimap of data providers to their visible entries' model IDs.
+     */
+    private static Multimap<ITimeGraphDataProvider<? extends TimeGraphEntryModel>, Long> filterGroupEntries(Iterable<TimeGraphEntry> visible,
+            long zoomStartTime, long zoomEndTime) {
+        Multimap<ITimeGraphDataProvider<? extends TimeGraphEntryModel>, Long> providersToModelIds = HashMultimap.create();
+        for (TimeGraphEntry entry : visible) {
+            if (zoomStartTime <= entry.getEndTime() && zoomEndTime >= entry.getStartTime() && entry.hasTimeEvents()) {
+                ITimeGraphDataProvider<? extends TimeGraphEntryModel> provider = getProvider(entry);
+                if (provider != null) {
+                    providersToModelIds.put(provider, entry.getEntryModel().getId());
+                }
+            }
+        }
+        return providersToModelIds;
+    }
+
+    /**
+     * Get the {@link ITimeGraphDataProvider} from a {@link TimeGraphEntry}'s
+     * parent.
+     *
+     * @param entry
+     *            queried {@link TimeGraphEntry}.
+     * @return the {@link ITimeGraphDataProvider}
+     * @since 3.3
+     */
+    public static ITimeGraphDataProvider<? extends TimeGraphEntryModel> getProvider(ITimeGraphEntry entry) {
+        ITimeGraphEntry parent = entry;
+        while (parent != null) {
+            if (parent instanceof ParentEntry) {
+                return ((ParentEntry) parent).getProvider();
+            }
+            parent = parent.getParent();
+        }
+        throw new IllegalStateException(entry + " should have a TraceEntry parent"); //$NON-NLS-1$
+    }
+
+    /**
+     * Get the trace associated with this view
+     *
+     * @return The trace
+     */
+    protected ITmfTrace getTrace() {
+        return fTrace;
+    }
+
+    private void refresh() {
+        try (FlowScopeLog parentLogger = new FlowScopeLogBuilder(LOGGER, Level.FINE, "FlameGraphView:RefreshRequested").setCategory(getViewId()).build()) { //$NON-NLS-1$
+            final boolean isZoomThread = Thread.currentThread() instanceof ZoomThread;
+            TmfUiRefreshHandler.getInstance().queueUpdate(this, () -> {
+                try (FlowScopeLog log = new FlowScopeLogBuilder(LOGGER, Level.FINE, "FlameGraphView:Refresh").setParentScope(parentLogger).build()) { //$NON-NLS-1$
+                    fDirty.incrementAndGet();
+                    if (fTimeGraphViewer.getControl().isDisposed()) {
+                        return;
+                    }
+                    List<TimeGraphEntry> entries;
+                    synchronized (fEntryListMap) {
+                        entries = fEntryListMap.get(getTrace());
+                        Comparator<ITimeGraphEntry> entryComparator = getEntryComparator();
+                        if (entries == null) {
+                            entries = new CopyOnWriteArrayList<>();
+                        } else if (entryComparator != null) {
+                            List<TimeGraphEntry> list = new ArrayList<>(entries);
+                            Collections.sort(list, entryComparator);
+                            for (ITimeGraphEntry entry : list) {
+                                sortChildren(entry, entryComparator);
+                            }
+                            entries.clear();
+                            entries.addAll(list);
+                        }
+                    }
+
+                    boolean inputChanged = entries != fTimeGraphViewer.getInput();
+                    if (inputChanged) {
+                        fTimeGraphViewer.setInput(entries);
+                    } else {
+                        fTimeGraphViewer.refresh();
+                    }
+
+                    long startBound = 0;
+                    long endBound = getEndTime();
+                    endBound = (endBound == Long.MIN_VALUE ? SWT.DEFAULT : endBound);
+                    fTimeGraphViewer.setTimeBounds(startBound, endBound);
+
+                    if (inputChanged && !isZoomThread) {
+                        fTimeGraphViewer.resetStartFinishTime();
+                    }
+
+                } finally {
+                    if (fDirty.decrementAndGet() < 0) {
+                        Activator.getDefault().logError("Dirty underflow", new Throwable()); //$NON-NLS-1$
+                    }
+                }
+            });
+        }
+    }
+
+    private Comparator<ITimeGraphEntry> getEntryComparator() {
+        switch (fSortOption) {
+        case BY_ID:
+            return ThreadIdComparator.getInstance();
+        case BY_ID_REV:
+            return ThreadIdComparator.getInstance().reversed();
+        case BY_NAME:
+            return ThreadNameComparator.getInstance();
+        case BY_NAME_REV:
+            return ThreadNameComparator.getInstance().reversed();
+        default:
+            break;
+        }
+        return null;
+    }
+
+    private void redraw() {
+
+        try (FlowScopeLog flowParent = new FlowScopeLogBuilder(LOGGER, Level.FINE, "FlameGraphView:RedrawRequested").setCategory(getViewId()).build()) { //$NON-NLS-1$
+            Display.getDefault().asyncExec(() -> {
+                try (FlowScopeLog log = new FlowScopeLogBuilder(LOGGER, Level.FINE, "FlameGraphView:Redraw").setParentScope(flowParent).build()) { //$NON-NLS-1$
+                    if (fTimeGraphViewer.getControl().isDisposed()) {
+                        return;
+                    }
+                    fTimeGraphViewer.getControl().redraw();
+                    fTimeGraphViewer.getControl().update();
+                }
+            });
+        }
+    }
+
+    private void sortChildren(ITimeGraphEntry entry, Comparator<ITimeGraphEntry> comparator) {
+        if (entry instanceof TimeGraphEntry) {
+            ((TimeGraphEntry) entry).sortChildren(comparator);
+        }
+        for (ITimeGraphEntry child : entry.getChildren()) {
+            sortChildren(child, comparator);
+        }
+    }
+
+    private int getDisplayWidth() {
+        int displayWidth = fDisplayWidth;
+        return displayWidth <= 0 ? 1 : displayWidth;
+    }
+
+    /**
+     * A class for parent entries that contain a link to the data provider
+     *
+     * @author Geneviève Bastien
+     */
+    private static class ParentEntry extends TimeGraphEntry {
+        private final @NonNull ITimeGraphDataProvider<? extends TimeGraphEntryModel> fProvider;
+
+        /**
+         * Constructor
+         *
+         * @param model
+         *            trace level model
+         * @param provider
+         *            reference to the provider for this trace and view
+         */
+        public ParentEntry(@NonNull TimeGraphEntryModel model,
+                @NonNull ITimeGraphDataProvider<? extends TimeGraphEntryModel> provider) {
+            super(model);
+            fProvider = provider;
+        }
+
+        /**
+         * Getter for the data provider for this {@link ParentEntry}
+         *
+         * @return this entry's {@link ITimeGraphDataProvider}
+         */
+        public @NonNull ITimeGraphDataProvider<? extends TimeGraphEntryModel> getProvider() {
+            return fProvider;
+        }
+    }
+
+    private synchronized void setEndTime(long endTime) {
+        fEndTime = endTime;
+    }
+
+    private long getEndTime() {
+        return fEndTime;
+    }
+
+    /**
+     * Adds a list of entries to a trace's entry list
+     *
+     * @param trace
+     *            the trace
+     * @param list
+     *            the list of time graph entries to add
+     */
+    private void addToEntryList(ITmfTrace trace, List<@NonNull TimeGraphEntry> list) {
+        synchronized (fEntryListMap) {
+            List<TimeGraphEntry> entryList = fEntryListMap.get(trace);
+            if (entryList == null) {
+                fEntryListMap.put(trace, new CopyOnWriteArrayList<>(list));
+            } else {
+                for (TimeGraphEntry entry : list) {
+                    if (!entryList.contains(entry)) {
+                        entryList.add(entry);
+                    }
+                }
+            }
+        }
+    }
+
+    private void resetEntries(ITmfTrace trace) {
+        synchronized (fEntries) {
+            synchronized (fEntryListMap) {
+                // Remove the entries from the entry list map and from the
+                // fEntries cache
+                List<@NonNull TimeGraphEntry> entries = fEntryListMap.remove(trace);
+                if (entries == null) {
+                    return;
+                }
+                for (TimeGraphEntry entry : entries) {
+                    if (entry instanceof ParentEntry) {
+                        fEntries.remove(((ParentEntry) entry).getProvider());
+                    }
+                }
+                refresh();
+            }
+        }
+    }
+
     /**
      * Get the necessary data for the flame graph and display it
      *
-     * @param callGraphProviders
-     *            the callGraphAnalysis
+     * @param viewTrace
+     *            the trace
      * @param selStart
      *            The selection start timestamp or <code>null</code> to show all
      *            data
@@ -268,7 +1060,7 @@
      *            data
      */
     @VisibleForTesting
-    public void buildFlameGraph(Iterable<ICallGraphProvider> callGraphProviders, @Nullable ITmfTimestamp selStart, @Nullable ITmfTimestamp selEnd) {
+    public void buildFlameGraph(@NonNull ITmfTrace viewTrace, @Nullable ITmfTimestamp selStart, @Nullable ITmfTimestamp selEnd) {
         /*
          * Note for synchronization:
          *
@@ -280,10 +1072,6 @@
          *
          * 3- when the job starts running and can thus be canceled
          */
-        Job job = fJob;
-        if (job != null) {
-            job.cancel();
-        }
         try (FlowScopeLog log = new FlowScopeLogBuilder(LOGGER, Level.FINE, "FlameGraphView:Building").setCategory(getViewId()).build()) { //$NON-NLS-1$
             try {
                 fLock.acquire();
@@ -291,97 +1079,40 @@
                 Activator.getDefault().logError(e.getMessage(), e);
                 fLock.release();
             }
-            /*
-             * Load the symbol provider for the current trace, even if it does not provide a
-             * call stack analysis module. See
-             * https://bugs.eclipse.org/bugs/show_bug.cgi?id=494212
-             */
-            ITmfTrace trace = fTrace;
-            if (trace != null) {
-                /*
-                 * Load the symbol provider for the current trace, even if it does not provide a
-                 * call stack analysis module. See
-                 * https://bugs.eclipse.org/bugs/show_bug.cgi?id=494212
-                 */
-                Collection<ISymbolProvider> symbolProviders = fSymbolProviders.get(trace);
-                if (symbolProviders.isEmpty()) {
-                    symbolProviders = SymbolProviderManager.getInstance().getSymbolProviders(trace);
-                    symbolProviders.forEach(provider -> provider.loadConfiguration(new NullProgressMonitor()));
-                    fSymbolProviders.putAll(trace, symbolProviders);
-                }
-            }
 
-            if (!callGraphProviders.iterator().hasNext()) {
-                fTimeGraphViewer.setInput(null);
-                fLock.release();
-                return;
-            }
-            for (ICallGraphProvider provider : callGraphProviders) {
-                if (provider instanceof IAnalysisModule) {
-                    ((IAnalysisModule) provider).schedule();
-                }
-            }
-            job = new Job(Messages.FlameGraphView_RetrievingData) {
-
-                @Override
-                protected IStatus run(IProgressMonitor monitor) {
-                    try (FlowScopeLog runLog = new FlowScopeLogBuilder(LOGGER, Level.FINE, "FlameGraphView:GettingFlameGraphs").setParentScope(log).build()) { //$NON-NLS-1$
-                        if (monitor.isCanceled()) {
-                            return Status.CANCEL_STATUS;
-                        }
-                        // Set the view as dirty before releasing the lock
-                        fDirty.incrementAndGet();
-                        fLock.release();
-                        Set<CallGraph> callgraphs = new HashSet<>();
-                        IWeightedTreeGroupDescriptor group = fGroupBy;
-                        for (ICallGraphProvider provider : callGraphProviders) {
-                            if (provider instanceof IAnalysisModule) {
-                                ((IAnalysisModule) provider).waitForCompletion(monitor);
-                            }
-                            // FIXME: This waits for completion, there is no way of cancelling this call, so
-                            // make the views responsive to updates in the model, so that we can return a
-                            // partial callgraph
-                            CallGraph callGraph;
-                            if (selStart == null || selEnd == null) {
-                                callGraph = provider.getCallGraph();
-                            } else {
-                                callGraph = provider.getCallGraph(selStart, selEnd);
-                            }
-                            if (group == null) {
-                                callgraphs.add(callGraph);
-                            } else {
-                                callgraphs.add(CallGraphGroupBy.groupCallGraphBy(group, callGraph));
-                            }
-                        }
-                        if (monitor.isCanceled()) {
-                            // Decrease dirtiness, job canceled
-                            fDirty.decrementAndGet();
-                            return Status.CANCEL_STATUS;
-                        }
-                        Display.getDefault().asyncExec(() -> {
-                            try (FlowScopeLog asyncLog = new FlowScopeLogBuilder(LOGGER, Level.FINE, "FlameGraphView:SettingInput").setParentScope(runLog).build()) { //$NON-NLS-1$
-                                fTimeGraphViewer.setInput(callgraphs);
-                                fTimeGraphViewer.resetStartFinishTime();
-                            } finally {
-                                // Finished updating, decrease dirtiness
-                                fDirty.decrementAndGet();
-                            }
-                        });
-                        return Status.OK_STATUS;
-                    }
-                }
-            };
+            // Run the build jobs through the site progress service if available
             IWorkbenchSiteProgressService service = null;
             IWorkbenchPartSite site = getSite();
             if (site != null) {
                 service = site.getService(IWorkbenchSiteProgressService.class);
             }
-            fJob = job;
-            if (service != null) {
-                service.schedule(job);
-            } else {
-                job.schedule();
+
+            for (ITmfTrace trace : TmfTraceManager.getTraceSet(viewTrace)) {
+
+                // Cancel previous build job for this trace
+                Job buildJob = fBuildJobMap.remove(trace);
+                if (buildJob != null) {
+                    buildJob.cancel();
+                }
+                resetEntries(viewTrace);
+                // Build job will decrement
+
+                buildJob = new Job(getTitle() + Messages.FlameGraphView_RetrievingData) {
+                    @Override
+                    protected IStatus run(IProgressMonitor monitor) {
+                        new BuildRunnable(trace, viewTrace, selStart, selEnd, log).run(monitor);
+                        monitor.done();
+                        return Status.OK_STATUS;
+                    }
+                };
+                fBuildJobMap.put(trace, buildJob);
+                if (service != null) {
+                    service.schedule(buildJob);
+                } else {
+                    buildJob.schedule();
+                }
             }
+            fLock.release();
         }
     }
 
@@ -448,8 +1179,10 @@
             @Override
             public void menuDetected(MenuDetectEvent event) {
                 Menu menu = timeEventMenu;
-                if (event.data instanceof FlamegraphEvent) {
-                    timeGraphControl.setMenu(menu);
+                if (event.data instanceof TimeEvent) {
+                    if (((TimeEvent) event.data).hasValue()) {
+                        timeGraphControl.setMenu(menu);
+                    }
                     return;
                 }
                 timeGraphControl.setMenu(null);
@@ -474,35 +1207,36 @@
      *            a menuManager to fill
      */
     protected void fillTimeEventContextMenu(@NonNull IMenuManager menuManager) {
-        ISelection selection = getSite().getSelectionProvider().getSelection();
-        if (selection instanceof IStructuredSelection) {
-            for (Object object : ((IStructuredSelection) selection).toList()) {
-                if (object instanceof FlamegraphEvent) {
-                    final FlamegraphEvent flamegraphEvent = (FlamegraphEvent) object;
-                    final ICalledFunction maxSeg = flamegraphEvent.getMaxObject();
-                    if (maxSeg != null) {
-                        menuManager.add(new Action(Messages.FlameGraphView_GotoMaxDuration) {
-                            @Override
-                            public void run() {
-                                TmfSelectionRangeUpdatedSignal sig = new TmfSelectionRangeUpdatedSignal(this, TmfTimestamp.fromNanos(maxSeg.getStart()), TmfTimestamp.fromNanos(maxSeg.getEnd()), fTrace);
-                                broadcast(sig);
-                            }
-                        });
-                    }
-
-                    final ICalledFunction minSeg = flamegraphEvent.getMinObject();
-                    if (minSeg != null) {
-                        menuManager.add(new Action(Messages.FlameGraphView_GotoMinDuration) {
-                            @Override
-                            public void run() {
-                                TmfSelectionRangeUpdatedSignal sig = new TmfSelectionRangeUpdatedSignal(this, TmfTimestamp.fromNanos(minSeg.getStart()), TmfTimestamp.fromNanos(minSeg.getEnd()), fTrace);
-                                broadcast(sig);
-                            }
-                        });
-                    }
-                }
-            }
-        }
+        // TODO Bring this functionality back
+//        ISelection selection = getSite().getSelectionProvider().getSelection();
+//        if (selection instanceof IStructuredSelection) {
+//            for (Object object : ((IStructuredSelection) selection).toList()) {
+//                if (object instanceof FlamegraphEvent) {
+//                    final FlamegraphEvent flamegraphEvent = (FlamegraphEvent) object;
+//                    final ICalledFunction maxSeg = flamegraphEvent.getMaxObject();
+//                    if (maxSeg != null) {
+//                        menuManager.add(new Action(Messages.FlameGraphView_GotoMaxDuration) {
+//                            @Override
+//                            public void run() {
+//                                TmfSelectionRangeUpdatedSignal sig = new TmfSelectionRangeUpdatedSignal(this, TmfTimestamp.fromNanos(maxSeg.getStart()), TmfTimestamp.fromNanos(maxSeg.getEnd()), fTrace);
+//                                broadcast(sig);
+//                            }
+//                        });
+//                    }
+//
+//                    final ICalledFunction minSeg = flamegraphEvent.getMinObject();
+//                    if (minSeg != null) {
+//                        menuManager.add(new Action(Messages.FlameGraphView_GotoMinDuration) {
+//                            @Override
+//                            public void run() {
+//                                TmfSelectionRangeUpdatedSignal sig = new TmfSelectionRangeUpdatedSignal(this, TmfTimestamp.fromNanos(minSeg.getStart()), TmfTimestamp.fromNanos(minSeg.getEnd()), fTrace);
+//                                broadcast(sig);
+//                            }
+//                        });
+//                    }
+//                }
+//            }
+//        }
     }
 
     private void contributeToActionBars() {
@@ -523,7 +1257,7 @@
             fAggregateByAction = new Action(Messages.FlameGraphView_GroupByName, IAction.AS_DROP_DOWN_MENU) {
                 @Override
                 public void run() {
-                    SortOption sortOption = fTimeGraphContentProvider.getSortOption();
+                    SortOption sortOption = fSortOption;
                     if (sortOption == SortOption.BY_NAME) {
                         setSortOption(SortOption.BY_NAME_REV);
                     } else {
@@ -533,8 +1267,9 @@
             };
             fAggregateByAction.setToolTipText(Messages.FlameGraphView_GroupByTooltip);
             fAggregateByAction.setImageDescriptor(AGGREGATE_BY_ICON);
-            fAggregateByAction.setMenuCreator(new IMenuCreator () {
+            fAggregateByAction.setMenuCreator(new IMenuCreator() {
                 Menu menu = null;
+
                 @Override
                 public void dispose() {
                     if (menu != null) {
@@ -555,14 +1290,14 @@
                         return menu;
                     }
                     ICallGraphProvider provider = iterator.next();
-                 // Add the all group element
-                    Action allGroupAction = createActionForGroup(provider, AllGroupDescriptor.getInstance());
+                    // Add the all group element
+                    Action allGroupAction = createActionForGroup(AllGroupDescriptor.getInstance());
                     new ActionContributionItem(allGroupAction).fill(menu, -1);
-                    Collection<IWeightedTreeGroupDescriptor> series = provider.getGroupDescriptors();
+                    Collection<@NonNull IWeightedTreeGroupDescriptor> series = provider.getGroupDescriptors();
                     series.forEach(group -> {
                         IWeightedTreeGroupDescriptor subGroup = group;
                         do {
-                            Action groupAction = createActionForGroup(provider, subGroup);
+                            Action groupAction = createActionForGroup(subGroup);
                             new ActionContributionItem(groupAction).fill(menu, -1);
                             subGroup = subGroup.getNextGroup();
                         } while (subGroup != null);
@@ -579,22 +1314,30 @@
         return fAggregateByAction;
     }
 
-    private Action createActionForGroup(ICallGraphProvider provider, IWeightedTreeGroupDescriptor descriptor) {
+    private Action createActionForGroup(IWeightedTreeGroupDescriptor descriptor) {
         return new Action(descriptor.getName(), IAction.AS_RADIO_BUTTON) {
             @Override
             public void run() {
+                ITmfTrace trace = getTrace();
+                if (trace == null) {
+                    return;
+                }
                 fGroupBy = descriptor;
-                buildFlameGraph(Collections.singleton(provider), null, null);
+                buildFlameGraph(trace, null, null);
             }
         };
     }
 
+    // --------------------------------
+    // Sorting related methods
+    // --------------------------------
+
     private Action getSortByNameAction() {
         if (fSortByNameAction == null) {
             fSortByNameAction = new Action(Messages.FlameGraph_SortByThreadName, IAction.AS_CHECK_BOX) {
                 @Override
                 public void run() {
-                    SortOption sortOption = fTimeGraphContentProvider.getSortOption();
+                    SortOption sortOption = fSortOption;
                     if (sortOption == SortOption.BY_NAME) {
                         setSortOption(SortOption.BY_NAME_REV);
                     } else {
@@ -613,7 +1356,7 @@
             fSortByIdAction = new Action(Messages.FlameGraph_SortByThreadId, IAction.AS_CHECK_BOX) {
                 @Override
                 public void run() {
-                    SortOption sortOption = fTimeGraphContentProvider.getSortOption();
+                    SortOption sortOption = fSortOption;
                     if (sortOption == SortOption.BY_ID) {
                         setSortOption(SortOption.BY_ID_REV);
                     } else {
@@ -627,6 +1370,59 @@
         return fSortByIdAction;
     }
 
+    private void setSortOption(SortOption sortOption) {
+        // reset defaults
+        getSortByNameAction().setChecked(false);
+        getSortByNameAction().setImageDescriptor(SORT_BY_NAME_ICON);
+        getSortByIdAction().setChecked(false);
+        getSortByIdAction().setImageDescriptor(SORT_BY_ID_ICON);
+
+        if (sortOption.equals(SortOption.BY_NAME)) {
+            fSortOption = SortOption.BY_NAME;
+            getSortByNameAction().setChecked(true);
+        } else if (sortOption.equals(SortOption.BY_NAME_REV)) {
+            fSortOption = SortOption.BY_NAME_REV;
+            getSortByNameAction().setChecked(true);
+            getSortByNameAction().setImageDescriptor(SORT_BY_NAME_REV_ICON);
+        } else if (sortOption.equals(SortOption.BY_ID)) {
+            fSortOption = SortOption.BY_ID;
+            getSortByIdAction().setChecked(true);
+        } else if (sortOption.equals(SortOption.BY_ID_REV)) {
+            fSortOption = SortOption.BY_ID_REV;
+            getSortByIdAction().setChecked(true);
+            getSortByIdAction().setImageDescriptor(SORT_BY_ID_REV_ICON);
+        }
+        saveSortOption();
+        refresh();
+    }
+
+    private void saveSortOption() {
+        SortOption sortOption = fSortOption;
+        IDialogSettings settings = Activator.getDefault().getDialogSettings();
+        IDialogSettings section = settings.getSection(getClass().getName());
+        if (section == null) {
+            section = settings.addNewSection(getClass().getName());
+        }
+        section.put(SORT_OPTION_KEY, sortOption.name());
+    }
+
+    private void loadSortOption() {
+        IDialogSettings settings = Activator.getDefault().getDialogSettings();
+        IDialogSettings section = settings.getSection(getClass().getName());
+        if (section == null) {
+            return;
+        }
+        String sortOption = section.get(SORT_OPTION_KEY);
+        if (sortOption == null) {
+            return;
+        }
+        setSortOption(SortOption.fromName(sortOption));
+    }
+
+    //--------------------------------
+    // Symbol related methods
+    //--------------------------------
+
     private Action getConfigureSymbolsAction() {
         if (fConfigureSymbolsAction != null) {
             return fConfigureSymbolsAction;
@@ -637,8 +1433,7 @@
             public void run() {
                 SymbolProviderConfigDialog dialog = new SymbolProviderConfigDialog(getSite().getShell(), getProviderPages());
                 if (dialog.open() == IDialogConstants.OK_ID) {
-//                    fPresentationProvider.resetFunctionNames();
-//                    refresh();
+                    startZoomThread(getTimeGraphViewer().getTime0(), getTimeGraphViewer().getTime1(), true);
                 }
             }
         };
@@ -679,55 +1474,6 @@
         return pages.toArray(new ISymbolProviderPreferencePage[pages.size()]);
     }
 
-    private void setSortOption(SortOption sortOption) {
-        // reset defaults
-        getSortByNameAction().setChecked(false);
-        getSortByNameAction().setImageDescriptor(SORT_BY_NAME_ICON);
-        getSortByIdAction().setChecked(false);
-        getSortByIdAction().setImageDescriptor(SORT_BY_ID_ICON);
-
-        if (sortOption.equals(SortOption.BY_NAME)) {
-            fTimeGraphContentProvider.setSortOption(SortOption.BY_NAME);
-            getSortByNameAction().setChecked(true);
-        } else if (sortOption.equals(SortOption.BY_NAME_REV)) {
-            fTimeGraphContentProvider.setSortOption(SortOption.BY_NAME_REV);
-            getSortByNameAction().setChecked(true);
-            getSortByNameAction().setImageDescriptor(SORT_BY_NAME_REV_ICON);
-        } else if (sortOption.equals(SortOption.BY_ID)) {
-            fTimeGraphContentProvider.setSortOption(SortOption.BY_ID);
-            getSortByIdAction().setChecked(true);
-        } else if (sortOption.equals(SortOption.BY_ID_REV)) {
-            fTimeGraphContentProvider.setSortOption(SortOption.BY_ID_REV);
-            getSortByIdAction().setChecked(true);
-            getSortByIdAction().setImageDescriptor(SORT_BY_ID_REV_ICON);
-        }
-        saveSortOption();
-        fTimeGraphViewer.refresh();
-    }
-
-    private void saveSortOption() {
-        SortOption sortOption = fTimeGraphContentProvider.getSortOption();
-        IDialogSettings settings = Activator.getDefault().getDialogSettings();
-        IDialogSettings section = settings.getSection(getClass().getName());
-        if (section == null) {
-            section = settings.addNewSection(getClass().getName());
-        }
-        section.put(SORT_OPTION_KEY, sortOption.name());
-    }
-
-    private void loadSortOption() {
-        IDialogSettings settings = Activator.getDefault().getDialogSettings();
-        IDialogSettings section = settings.getSection(getClass().getName());
-        if (section == null) {
-            return;
-        }
-        String sortOption = section.get(SORT_OPTION_KEY);
-        if (sortOption == null) {
-            return;
-        }
-        setSortOption(SortOption.fromName(sortOption));
-    }
-
     /**
      * Symbol map provider updated
      *
@@ -736,9 +1482,9 @@
      */
     @TmfSignalHandler
     public void symbolMapUpdated(TmfSymbolProviderUpdatedSignal signal) {
-//        if (signal.getSource() != this) {
-            fTimeGraphViewer.refresh();
-//        }
+        if (signal.getSource() != this) {
+            startZoomThread(getTimeGraphViewer().getTime0(), getTimeGraphViewer().getTime1(), true);
+        }
     }
 
     @Override
@@ -746,4 +1492,29 @@
         return SaveImageUtil.createSaveAction(getName(), this::getTimeGraphViewer);
     }
 
+    /**
+     * Cancel and restart the zoom thread.
+     */
+    public void restartZoomThread() {
+        ZoomThread zoomThread = fZoomThread;
+        if (zoomThread != null) {
+            // Make sure that the zoom thread is not a restart (resume of the previous)
+            zoomThread.cancel();
+            fZoomThread = null;
+        }
+        startZoomThread(getTimeGraphViewer().getTime0(), getTimeGraphViewer().getTime1(), true);
+    }
+
+    /**
+     * Set or remove the global regex filter value
+     *
+     * @param signal
+     *                   the signal carrying the regex value
+     */
+    @TmfSignalHandler
+    public void regexFilterApplied(TmfFilterAppliedSignal signal) {
+        // Restart the zoom thread to apply the new filter
+        Display.getDefault().asyncExec(() -> restartZoomThread());
+    }
+
 }
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/flamegraph/FlamegraphDepthEntry.java b/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/flamegraph/FlamegraphDepthEntry.java
deleted file mode 100644
index a6f7619..0000000
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/flamegraph/FlamegraphDepthEntry.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2016 Ericsson
- *
- * All rights reserved. This program and the accompanying materials are made
- * available under the terms of the Eclipse Public License v1.0 which
- * accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *******************************************************************************/
-
-package org.eclipse.tracecompass.incubator.internal.callstack.ui.flamegraph;
-
-import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeGraphEntry;
-
-/**
- * An entry, or row, in the flame Graph view
- *
- * @author Sonia Farrah
- */
-public class FlamegraphDepthEntry extends TimeGraphEntry {
-
-    private final int fDepth;
-    private final String fId;
-
-    /**
-     * Constructor
-     *
-     * @param name
-     *            name of an entry
-     * @param startTime
-     *            Start time of an entry
-     * @param endTime
-     *            The end time of an entry
-     * @param depth
-     *            The Depth of an entry
-     * @param id
-     *            The id of an entry
-     */
-    public FlamegraphDepthEntry(String name, long startTime, long endTime, int depth, String id) {
-        super(name, startTime, endTime);
-        fDepth = depth;
-        fId = id;
-    }
-
-    /**
-     * The depth of a flame graph entry
-     *
-     * @return The depth of a flame graph entry
-     */
-    public int getDepth() {
-        return fDepth;
-    }
-
-    /**
-     * The id of an entry
-     *
-     * @return The id
-     */
-    public String getId() {
-        return fId;
-    }
-}
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/flamegraph/ThreadIdComparator.java b/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/flamegraph/ThreadIdComparator.java
index f71b270..8fc739e 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/flamegraph/ThreadIdComparator.java
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/flamegraph/ThreadIdComparator.java
@@ -10,6 +10,10 @@
 
 import java.util.Comparator;
 
+import org.eclipse.tracecompass.incubator.internal.callstack.core.instrumented.provider.FlameChartEntryModel;
+import org.eclipse.tracecompass.incubator.internal.callstack.core.instrumented.provider.FlameChartEntryModel.EntryType;
+import org.eclipse.tracecompass.tmf.core.model.tree.ITmfTreeDataModel;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeGraphEntry;
 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeGraphEntry;
 
 /**
@@ -18,12 +22,33 @@
  * @author Bernd Hufmann
  *
  */
-class ThreadIdComparator implements Comparator<TimeGraphEntry> {
+class ThreadIdComparator implements Comparator<ITimeGraphEntry> {
+
+    private static final Comparator<ITimeGraphEntry> INSTANCE = new ThreadIdComparator();
+
+    private ThreadIdComparator() {
+        // Nothing to do
+    }
+
+    public static Comparator<ITimeGraphEntry> getInstance() {
+        return INSTANCE;
+    }
+
     @Override
-    public int compare(TimeGraphEntry o1, TimeGraphEntry o2) {
-        if (o1 instanceof FlamegraphDepthEntry && o2 instanceof FlamegraphDepthEntry) {
-            return ((FlamegraphDepthEntry) o1).getId().compareTo(((FlamegraphDepthEntry) o2).getId());
+    public int compare(ITimeGraphEntry o1, ITimeGraphEntry o2) {
+        if (o1 instanceof TimeGraphEntry && o2 instanceof TimeGraphEntry) {
+            ITmfTreeDataModel entryModel1 = ((TimeGraphEntry) o1).getEntryModel();
+            ITmfTreeDataModel entryModel2 = ((TimeGraphEntry) o2).getEntryModel();
+            if (entryModel1 instanceof FlameChartEntryModel && entryModel2 instanceof FlameChartEntryModel) {
+                FlameChartEntryModel fcEntry1 = (FlameChartEntryModel) entryModel1;
+                FlameChartEntryModel fcEntry2 = (FlameChartEntryModel) entryModel2;
+                // If any of the entry is a function, compare their depth
+                if (fcEntry1.getEntryType().equals(EntryType.FUNCTION) && fcEntry2.getEntryType().equals(EntryType.FUNCTION)) {
+                    return Integer.compare(fcEntry1.getDepth(), fcEntry2.getDepth());
+                }
+            }
         }
-        return o1.getName().compareTo(o2.getName());
+        // Fallback where all entries are equal
+        return 0;
     }
 }
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/flamegraph/ThreadNameComparator.java b/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/flamegraph/ThreadNameComparator.java
index 33ab1ed..2dc62b4 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/flamegraph/ThreadNameComparator.java
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/flamegraph/ThreadNameComparator.java
@@ -10,17 +10,45 @@
 
 import java.util.Comparator;
 
+import org.eclipse.tracecompass.incubator.internal.callstack.core.instrumented.provider.FlameChartEntryModel;
+import org.eclipse.tracecompass.incubator.internal.callstack.core.instrumented.provider.FlameChartEntryModel.EntryType;
+import org.eclipse.tracecompass.tmf.core.model.tree.ITmfTreeDataModel;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeGraphEntry;
 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeGraphEntry;
 
 /**
  * Comparator to compare by thread name.
  *
  * @author Bernd Hufmann
- *
  */
-class ThreadNameComparator implements Comparator<TimeGraphEntry> {
+class ThreadNameComparator implements Comparator<ITimeGraphEntry> {
+
+    private static final Comparator<ITimeGraphEntry> INSTANCE = new ThreadNameComparator();
+
+    private ThreadNameComparator() {
+        // Nothing to do
+    }
+
+    public static Comparator<ITimeGraphEntry> getInstance() {
+        return INSTANCE;
+    }
+
     @Override
-    public int compare(TimeGraphEntry o1, TimeGraphEntry o2) {
+    public int compare(ITimeGraphEntry o1, ITimeGraphEntry o2) {
+        if (o1 instanceof TimeGraphEntry && o2 instanceof TimeGraphEntry) {
+            ITmfTreeDataModel entryModel1 = ((TimeGraphEntry) o1).getEntryModel();
+            ITmfTreeDataModel entryModel2 = ((TimeGraphEntry) o2).getEntryModel();
+            if (entryModel1 instanceof FlameChartEntryModel && entryModel2 instanceof FlameChartEntryModel) {
+                FlameChartEntryModel fcEntry1 = (FlameChartEntryModel) entryModel1;
+                FlameChartEntryModel fcEntry2 = (FlameChartEntryModel) entryModel2;
+                // If any of the entry is a function of kernel, don't compare
+                if (fcEntry1.getEntryType().equals(EntryType.FUNCTION) || fcEntry2.getEntryType().equals(EntryType.FUNCTION) ||
+                        fcEntry1.getEntryType().equals(EntryType.KERNEL) || fcEntry2.getEntryType().equals(EntryType.KERNEL)) {
+                    return 0;
+                }
+            }
+        }
+        // Fallback to entry name comparator
         return o1.getName().compareTo(o2.getName());
     }
 }