kernel: Aggregate the critical path as a weighted tree

This takes a critical path and flattens it to show the time spent on the
various critical path states and processes

Change-Id: Ie066d026b214d2b3ca8577571afdd621a3cdf6bd
Signed-off-by: Geneviève Bastien <gbastien+lttng@versatic.net>
Reviewed-on: https://git.eclipse.org/r/133090
Reviewed-by: Matthew Khouzam <matthew.khouzam@ericsson.com>
Tested-by: Matthew Khouzam <matthew.khouzam@ericsson.com>
Tested-by: Trace Compass Bot <tracecompass-bot@eclipse.org>
diff --git a/analyses/org.eclipse.tracecompass.incubator.kernel.core/META-INF/MANIFEST.MF b/analyses/org.eclipse.tracecompass.incubator.kernel.core/META-INF/MANIFEST.MF
index 4c90290..534dac2 100644
--- a/analyses/org.eclipse.tracecompass.incubator.kernel.core/META-INF/MANIFEST.MF
+++ b/analyses/org.eclipse.tracecompass.incubator.kernel.core/META-INF/MANIFEST.MF
@@ -15,11 +15,14 @@
  org.eclipse.tracecompass.analysis.os.linux.core,
  org.eclipse.tracecompass.incubator.analysis.core,
  org.eclipse.tracecompass.incubator.callstack.core,
- org.eclipse.jdt.annotation;bundle-version="[2.0.0,3.0.0)";resolution:=optional,
- org.eclipse.tracecompass.analysis.timing.core
+ org.eclipse.tracecompass.analysis.timing.core,
+ org.eclipse.tracecompass.analysis.graph.core,
+ org.eclipse.jdt.annotation;bundle-version="[2.0.0,3.0.0)";resolution:=optional
 Export-Package: org.eclipse.tracecompass.incubator.internal.kernel.core;x-friends:="org.eclipse.tracecompass.incubator.kernel.core.tests",
  org.eclipse.tracecompass.incubator.internal.kernel.core.callstack.context;x-friends:="org.eclipse.tracecompass.incubator.lttng2.ust.extras.core",
+ org.eclipse.tracecompass.incubator.internal.kernel.core.criticalpath;x-internal:=true,
  org.eclipse.tracecompass.incubator.internal.kernel.core.fileaccess;x-internal:=true,
  org.eclipse.tracecompass.incubator.internal.kernel.core.filedescriptor;x-internal:=true
 Automatic-Module-Name: org.eclipse.tracecompass.incubator.kernel.core
-Import-Package: com.google.common.collect
+Import-Package: com.google.common.collect,
+ org.apache.commons.lang3
diff --git a/analyses/org.eclipse.tracecompass.incubator.kernel.core/plugin.properties b/analyses/org.eclipse.tracecompass.incubator.kernel.core/plugin.properties
index 5113cb8..c4293a4 100644
--- a/analyses/org.eclipse.tracecompass.incubator.kernel.core/plugin.properties
+++ b/analyses/org.eclipse.tracecompass.incubator.kernel.core/plugin.properties
@@ -13,3 +13,4 @@
 
 kernel.fileaccess.name = File Access
 analysis.callstack.context = Context CallStacks
+analysis.criticalpath.aggregated = Critical Path Aggregated
diff --git a/analyses/org.eclipse.tracecompass.incubator.kernel.core/plugin.xml b/analyses/org.eclipse.tracecompass.incubator.kernel.core/plugin.xml
index 695da6b..b75e465 100644
--- a/analyses/org.eclipse.tracecompass.incubator.kernel.core/plugin.xml
+++ b/analyses/org.eclipse.tracecompass.incubator.kernel.core/plugin.xml
@@ -28,6 +28,14 @@
                class="org.eclipse.tracecompass.analysis.os.linux.core.trace.IKernelTrace">
          </tracetype>
       </module>
+      <module
+            analysis_module="org.eclipse.tracecompass.incubator.internal.kernel.core.criticalpath.CriticalPathAggregatedModule"
+            id="org.eclipse.tracecompass.incubator.kernel.core.criticalpath.aggregated"
+            name="%analysis.criticalpath.aggregated">
+         <tracetype
+               class="org.eclipse.tracecompass.tmf.core.trace.TmfTrace">
+         </tracetype>
+      </module>
    </extension>
    <extension
          point="org.eclipse.tracecompass.tmf.core.dataprovider">
diff --git a/analyses/org.eclipse.tracecompass.incubator.kernel.core/src/org/eclipse/tracecompass/incubator/internal/kernel/core/criticalpath/CriticalPathAggregatedModule.java b/analyses/org.eclipse.tracecompass.incubator.kernel.core/src/org/eclipse/tracecompass/incubator/internal/kernel/core/criticalpath/CriticalPathAggregatedModule.java
new file mode 100644
index 0000000..8d90ef5
--- /dev/null
+++ b/analyses/org.eclipse.tracecompass.incubator.kernel.core/src/org/eclipse/tracecompass/incubator/internal/kernel/core/criticalpath/CriticalPathAggregatedModule.java
@@ -0,0 +1,109 @@
+/*******************************************************************************
+ * Copyright (c) 2020 É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.internal.kernel.core.criticalpath;
+
+import java.util.Collection;
+import java.util.Objects;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.analysis.graph.core.criticalpath.CriticalPathModule;
+import org.eclipse.tracecompass.incubator.analysis.core.weighted.tree.IWeightedTreeProvider;
+import org.eclipse.tracecompass.incubator.analysis.core.weighted.tree.IWeightedTreeSet;
+import org.eclipse.tracecompass.incubator.analysis.core.weighted.tree.WeightedTree;
+import org.eclipse.tracecompass.tmf.core.TmfStrings;
+import org.eclipse.tracecompass.tmf.core.analysis.IAnalysisModule;
+import org.eclipse.tracecompass.tmf.core.analysis.TmfAbstractAnalysisModule;
+import org.eclipse.tracecompass.tmf.core.exceptions.TmfAnalysisException;
+import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
+import org.eclipse.tracecompass.tmf.core.signal.TmfStartAnalysisSignal;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
+
+/**
+ * Analysis modules that aggregates the states of the critical path into a
+ * weighted tree structure, to display in flame graphs or compare with other
+ * graphs.
+ *
+ * @author Geneviève Bastien
+ */
+public class CriticalPathAggregatedModule extends TmfAbstractAnalysisModule implements IWeightedTreeProvider<Object, String, WeightedTree<Object>> {
+
+    private static final MetricType DURATION_METRIC = new MetricType(Objects.requireNonNull(TmfStrings.duration()), DataType.NANOSECONDS, null);
+
+    private @Nullable CriticalPathModule fModule = null;
+    private @Nullable CriticalPathWeighted fCritPathCg = null;
+
+    @Override
+    protected boolean executeAnalysis(IProgressMonitor monitor) throws TmfAnalysisException {
+        CriticalPathModule module = fModule;
+        if (module == null) {
+            return false;
+        }
+        if (!module.waitForCompletion(Objects.requireNonNull(monitor))) {
+            return false;
+        }
+        fCritPathCg = CriticalPathWeighted.create(module.getCriticalPath());
+        return true;
+    }
+
+    /**
+     * Signal handler for analysis started, we need to rebuilt the entry list
+     * with updated statistics values for the current graph worker of the
+     * critical path module.
+     *
+     * @param signal
+     *            The signal
+     */
+    @TmfSignalHandler
+    public void analysisStarted(TmfStartAnalysisSignal signal) {
+        IAnalysisModule analysis = signal.getAnalysisModule();
+        if (analysis instanceof CriticalPathModule) {
+            CriticalPathModule criticalPath = (CriticalPathModule) analysis;
+            Collection<ITmfTrace> traces = TmfTraceManager.getTraceSetWithExperiment(getTrace());
+            if (traces.contains(criticalPath.getTrace())) {
+                cancel();
+                fModule = criticalPath;
+                fCritPathCg = null;
+                resetAnalysis();
+                schedule();
+            }
+        }
+    }
+
+    @Override
+    protected void canceling() {
+        // Nothing to do
+    }
+
+    @Override
+    public MetricType getWeightType() {
+        return DURATION_METRIC;
+    }
+
+    @Override
+    public String getTitle() {
+        return "What the process is waiting for"; //$NON-NLS-1$
+    }
+
+    @Override
+    public IWeightedTreeSet<Object, String, WeightedTree<Object>> getTreeSet() {
+        CriticalPathWeighted critPathCg = fCritPathCg;
+        if (critPathCg != null) {
+            return critPathCg;
+        }
+        CriticalPathModule module = fModule;
+        if (module == null) {
+            return CriticalPathWeighted.create(null);
+        }
+        return CriticalPathWeighted.create(module.getCriticalPath());
+    }
+
+}
diff --git a/analyses/org.eclipse.tracecompass.incubator.kernel.core/src/org/eclipse/tracecompass/incubator/internal/kernel/core/criticalpath/CriticalPathWeighted.java b/analyses/org.eclipse.tracecompass.incubator.kernel.core/src/org/eclipse/tracecompass/incubator/internal/kernel/core/criticalpath/CriticalPathWeighted.java
new file mode 100644
index 0000000..9ed38f2
--- /dev/null
+++ b/analyses/org.eclipse.tracecompass.incubator.kernel.core/src/org/eclipse/tracecompass/incubator/internal/kernel/core/criticalpath/CriticalPathWeighted.java
@@ -0,0 +1,246 @@
+/*******************************************************************************
+ * Copyright (c) 2020 É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.internal.kernel.core.criticalpath;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.commons.lang3.StringUtils;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.analysis.graph.core.base.IGraphWorker;
+import org.eclipse.tracecompass.analysis.graph.core.base.ITmfGraphVisitor;
+import org.eclipse.tracecompass.analysis.graph.core.base.TmfEdge;
+import org.eclipse.tracecompass.analysis.graph.core.base.TmfEdge.EdgeType;
+import org.eclipse.tracecompass.analysis.graph.core.base.TmfGraph;
+import org.eclipse.tracecompass.analysis.graph.core.base.TmfVertex;
+import org.eclipse.tracecompass.analysis.os.linux.core.execution.graph.OsWorker;
+import org.eclipse.tracecompass.incubator.analysis.core.weighted.tree.IWeightedTreeSet;
+import org.eclipse.tracecompass.incubator.analysis.core.weighted.tree.WeightedTree;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * A weighted tree set that contains the aggregation of the critical path graph
+ * states.
+ *
+ * The tree set will have 3 elements that all aggregate the full critical path,
+ * but differently.
+ * <ul>
+ * <li>1- Top level are all the processes involved and the second level is the
+ * time those processes spend in the various states</li>
+ * <li>2- with the _all suffix is the tree showing only the final status of the
+ * critical path: running, preempted, network, etc</li>
+ * <li>3- With the _proc suffix is the tree where the top level are the running
+ * processes and the states. Any of the dependent processes could have been in
+ * one of those states</li>
+ * </ul>
+ *
+ * @author Geneviève Bastien
+ */
+public class CriticalPathWeighted implements IWeightedTreeSet<Object, String, WeightedTree<Object>> {
+
+    /**
+     * An empty critical path
+     */
+    private static final CriticalPathWeighted EMPTY_CRIT_PATH_CG = new CriticalPathWeighted();
+    private static final String ALL_SUFFIX = "_all"; //$NON-NLS-1$
+    private static final String PROCESS_SUFFIX = "_proc"; //$NON-NLS-1$
+
+    private final List<String> fElements;
+    private WeightedTree<Object> fAggregatedTree;
+    private WeightedTree<Object> fTree;
+    private WeightedTree<Object> fProcessTree;
+
+    private class GraphToCallGraphConverter implements ITmfGraphVisitor {
+
+        private final TmfGraph fGraph;
+        private final IGraphWorker fMainWorker;
+
+        public GraphToCallGraphConverter(IGraphWorker mainWorker, TmfGraph graph) {
+            fGraph = graph;
+            fMainWorker = mainWorker;
+        }
+
+        @Override
+        public void visitHead(TmfVertex vertex) {
+            // Nothing to do
+
+        }
+
+        @Override
+        public void visit(TmfVertex vertex) {
+            // Nothing to do
+        }
+
+        @Override
+        public void visit(TmfEdge edge, boolean horizontal) {
+            if (edge.getDuration() == 0) {
+                return;
+            }
+            addEdgeToElement(edge);
+            addEdgeToAggregatedElement(edge);
+            addEdgeToProcessElement(edge);
+        }
+
+        private void addEdgeToAggregatedElement(TmfEdge edge) {
+            // Get the worker to which to attribute this edge, whether vertical
+            // or horizontal
+            IGraphWorker worker = fGraph.getParentOf(edge.getVertexTo());
+            if (worker == null) {
+                return;
+            }
+
+            // If it's another worker that is running, add a other process
+            // running state
+            if (worker != fMainWorker && edge.getType().equals(EdgeType.RUNNING)) {
+                WeightedTree<Object> callSite = new WeightedTree<>("Other process"); //$NON-NLS-1$
+                callSite.addToWeight(edge.getDuration());
+                fAggregatedTree.addChild(callSite);
+                return;
+            }
+
+            // Otherwise, add a first level call that corresponds to the worker
+            WeightedTree<Object> callSite = new WeightedTree<>(edge.getType());
+            callSite.addToWeight(edge.getDuration());
+            fAggregatedTree.addChild(callSite);
+
+        }
+
+        private void addEdgeToElement(TmfEdge edge) {
+            // Get the worker to which to attribute this edge, whether vertical
+            // or horizontal
+            IGraphWorker worker = fGraph.getParentOf(edge.getVertexTo());
+            if (worker == null) {
+                return;
+            }
+
+            // If it is the main worker, just add a 1st level call of the edge
+            // type
+            if (worker == fMainWorker) {
+                WeightedTree<Object> callSite = new WeightedTree<>(edge.getType());
+                callSite.addToWeight(edge.getDuration());
+                fTree.addChild(callSite);
+                return;
+            }
+
+            // Otherwise, add a first level call that corresponds to the worker
+            WeightedTree<Object> callSite = new WeightedTree<>(String.valueOf(worker));
+            callSite.addToWeight(edge.getDuration());
+
+            // Then, add a second level for the edge type if it is not running
+            if (!edge.getType().equals(EdgeType.RUNNING)) {
+                WeightedTree<Object> childType = new WeightedTree<>(edge.getType());
+                childType.addToWeight(edge.getDuration());
+                callSite.addChild(childType);
+            }
+            fTree.addChild(callSite);
+        }
+
+        private void addEdgeToProcessElement(TmfEdge edge) {
+            // Get the worker to which to attribute this edge, whether vertical
+            // or horizontal
+            IGraphWorker worker = fGraph.getParentOf(edge.getVertexTo());
+            if (worker == null) {
+                return;
+            }
+
+            // If it's another worker that is running, add a other process
+            // running state
+            if (worker != fMainWorker && edge.getType().equals(EdgeType.RUNNING)) {
+                WeightedTree<Object> callSite = new WeightedTree<>(((OsWorker) worker).getName());
+                callSite.addToWeight(edge.getDuration());
+                fProcessTree.addChild(callSite);
+                return;
+            }
+
+            // Otherwise, add a first level call that corresponds to the worker
+            WeightedTree<Object> callSite = new WeightedTree<>(edge.getType());
+            callSite.addToWeight(edge.getDuration());
+            fProcessTree.addChild(callSite);
+        }
+
+    }
+
+    private CriticalPathWeighted() {
+        // Private constructor to build the empty graph
+        fElements = Collections.emptyList();
+        fTree = new WeightedTree<>(StringUtils.EMPTY);
+        fAggregatedTree = new WeightedTree<>(StringUtils.EMPTY);
+        fProcessTree = new WeightedTree<>(StringUtils.EMPTY);
+    }
+
+    /**
+     * Create a new critical path weighted tree set from the graph received in
+     * parameter. The graph can be <code>null</code> or empty and this method
+     * will return an empty treeset. It transforms the graph received in
+     * parameter into a weighted tree, merging similar state's durations.
+     *
+     * @param graph
+     *            The graph to transform into an aggregated weighted tree set.
+     * @return The critical path weighted treeset
+     */
+    public static CriticalPathWeighted create(@Nullable TmfGraph graph) {
+        if (graph == null) {
+            return EMPTY_CRIT_PATH_CG;
+        }
+        TmfVertex head = graph.getHead();
+        if (head == null) {
+            return EMPTY_CRIT_PATH_CG;
+        }
+        return new CriticalPathWeighted(graph);
+    }
+
+    /**
+     * Constructor. It transforms the graph received in parameter into a
+     * weighted tree, merging similar state's durations.
+     *
+     * @param graph
+     *            The graph to flatten as a weighted tree
+     */
+    private CriticalPathWeighted(TmfGraph graph) {
+        TmfVertex head = graph.getHead();
+        if (head == null) {
+            throw new NullPointerException("Empty graph"); //$NON-NLS-1$
+        }
+
+        IGraphWorker worker = graph.getParentOf(head);
+        if (worker == null) {
+            throw new NullPointerException("head vertex has no parent"); //$NON-NLS-1$
+        }
+        fElements = ImmutableList.of(String.valueOf(worker), String.valueOf(worker) + ALL_SUFFIX, String.valueOf(worker) + PROCESS_SUFFIX);
+        fTree = new WeightedTree<>(String.valueOf(worker));
+        fAggregatedTree = new WeightedTree<>(String.valueOf(worker) + ALL_SUFFIX);
+        fProcessTree = new WeightedTree<>(String.valueOf(worker) + PROCESS_SUFFIX);
+        GraphToCallGraphConverter converter = new GraphToCallGraphConverter(worker, graph);
+        graph.scanLineTraverse(worker, converter);
+    }
+
+    @Override
+    public Collection<WeightedTree<Object>> getTreesFor(Object element) {
+        if (!(element instanceof String)) {
+            return Collections.emptyList();
+        }
+        String elStr = (String) element;
+        if (elStr.endsWith(ALL_SUFFIX)) {
+            return fAggregatedTree.getChildren();
+        }
+        if (elStr.endsWith(PROCESS_SUFFIX)) {
+            return fProcessTree.getChildren();
+        }
+        return fTree.getChildren();
+    }
+
+    @Override
+    public Collection<String> getElements() {
+        return fElements;
+    }
+
+}
diff --git a/analyses/org.eclipse.tracecompass.incubator.kernel.core/src/org/eclipse/tracecompass/incubator/internal/kernel/core/criticalpath/package-info.java b/analyses/org.eclipse.tracecompass.incubator.kernel.core/src/org/eclipse/tracecompass/incubator/internal/kernel/core/criticalpath/package-info.java
new file mode 100644
index 0000000..ef49324
--- /dev/null
+++ b/analyses/org.eclipse.tracecompass.incubator.kernel.core/src/org/eclipse/tracecompass/incubator/internal/kernel/core/criticalpath/package-info.java
@@ -0,0 +1,11 @@
+/*******************************************************************************
+ * Copyright (c) 2020 É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
+ *******************************************************************************/
+
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.eclipse.tracecompass.incubator.internal.kernel.core.criticalpath;
diff --git a/analyses/org.eclipse.tracecompass.incubator.kernel.ui/META-INF/MANIFEST.MF b/analyses/org.eclipse.tracecompass.incubator.kernel.ui/META-INF/MANIFEST.MF
index 41ddc0f..8579605 100644
--- a/analyses/org.eclipse.tracecompass.incubator.kernel.ui/META-INF/MANIFEST.MF
+++ b/analyses/org.eclipse.tracecompass.incubator.kernel.ui/META-INF/MANIFEST.MF
@@ -17,7 +17,10 @@
  org.eclipse.core.resources,
  org.eclipse.tracecompass.analysis.os.linux.core,
  org.eclipse.tracecompass.analysis.os.linux.ui,
- org.eclipse.jdt.annotation;bundle-version="[2.0.0,3.0.0)";resolution:=optional
+ org.eclipse.jdt.annotation;bundle-version="[2.0.0,3.0.0)";resolution:=optional,
+ org.eclipse.tracecompass.incubator.analysis.core,
+ org.eclipse.tracecompass.incubator.callstack.core,
+ org.eclipse.tracecompass.incubator.callstack.ui
 Export-Package: org.eclipse.tracecompass.incubator.internal.kernel.ui;x-internal:=true,
  org.eclipse.tracecompass.incubator.internal.kernel.ui.views.contextswitch;x-internal:=true,
  org.eclipse.tracecompass.incubator.internal.kernel.ui.views.fileaccess;x-internal:=true
diff --git a/analyses/org.eclipse.tracecompass.incubator.kernel.ui/plugin.xml b/analyses/org.eclipse.tracecompass.incubator.kernel.ui/plugin.xml
index f352d05..60b42e9 100644
--- a/analyses/org.eclipse.tracecompass.incubator.kernel.ui/plugin.xml
+++ b/analyses/org.eclipse.tracecompass.incubator.kernel.ui/plugin.xml
@@ -37,5 +37,4 @@
          </analysisModuleClass>
       </output>
    </extension>
-
 </plugin>
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/flamegraph/FlameGraphDataProvider.java b/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/flamegraph/FlameGraphDataProvider.java
index 805e93a..314b168 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/flamegraph/FlameGraphDataProvider.java
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/flamegraph/FlameGraphDataProvider.java
@@ -143,11 +143,37 @@
     private final long fTraceId = ENTRY_ID.getAndIncrement();
 
     private final ReentrantReadWriteLock fLock = new ReentrantReadWriteLock(false);
-    private @Nullable Pair<Map<String, Object>, TmfModelResponse<TmfTreeModel<FlameChartEntryModel>>> fCached;
+    private @Nullable Pair<CacheKey, TmfModelResponse<TmfTreeModel<FlameChartEntryModel>>> fCached;
     private final Map<Long, FlameChartEntryModel> fEntries = new HashMap<>();
     private final Map<Long, WeightedTreeEntry> fCgEntries = new HashMap<>();
     private final Map<Long, Long> fEndTimes = new HashMap<>();
 
+    private class CacheKey {
+        private final Map<String, Object> fParameters;
+        private final IWeightedTreeSet<N, Object, WeightedTree<N>> fTreeSet;
+
+        public CacheKey(Map<String, Object> parameters, IWeightedTreeSet<N, Object, WeightedTree<N>> treeset) {
+            fParameters = parameters;
+            fTreeSet = treeset;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(fParameters, fTreeSet);
+        }
+
+        @Override
+        public boolean equals(@Nullable Object obj) {
+            if (!(obj instanceof FlameGraphDataProvider.CacheKey)) {
+                return false;
+            }
+            return Objects.equals(fParameters, ((FlameGraphDataProvider.CacheKey) obj).fParameters)
+                    && Objects.equals(fTreeSet, ((FlameGraphDataProvider.CacheKey) obj).fTreeSet);
+        }
+
+
+    }
+
     /** An internal class to describe the data for an entry */
     private class WeightedTreeEntry {
         private ITmfStateSystem fSs;
@@ -218,23 +244,28 @@
         fLock.writeLock().lock();
         try (FlowScopeLog scope = new FlowScopeLogBuilder(LOGGER, Level.FINE, "FlameGraphDataProvider#fetchTree") //$NON-NLS-1$
                 .setCategory(getClass().getSimpleName()).build()) {
-            // Did we cache this tree with those parameters
-            Pair<Map<String, Object>, TmfModelResponse<TmfTreeModel<FlameChartEntryModel>>> cached = fCached;
-            if (cached != null && cached.getFirst().equals(fetchParameters)) {
+            // Did we cache this tree with those parameters and the callgraph?
+            // For some analyses, the returned callgraph for the same parameters
+            // may vary if the analysis was done again, we need to cache for
+            // callgraph as well
+            SubMonitor subMonitor = Objects.requireNonNull(SubMonitor.convert(monitor, "FlameGraphDataProvider#fetchRowModel", 2)); //$NON-NLS-1$
+            IWeightedTreeSet<N, Object, WeightedTree<N>> callGraph = getCallGraph(fetchParameters, subMonitor);
+            if (callGraph == null) {
+                return new TmfModelResponse<>(null, ITmfResponse.Status.FAILED, CommonStatusMessage.TASK_CANCELLED);
+            }
+
+            CacheKey cacheKey = new CacheKey(fetchParameters, callGraph);
+            Pair<CacheKey, TmfModelResponse<TmfTreeModel<FlameChartEntryModel>>> cached = fCached;
+            if (cached != null && cached.getFirst().equals(cacheKey)) {
                 return cached.getSecond();
             }
 
             fEntries.clear();
             fCgEntries.clear();
-            SubMonitor subMonitor = Objects.requireNonNull(SubMonitor.convert(monitor, "FlameGraphDataProvider#fetchRowModel", 2)); //$NON-NLS-1$
 
-            IWeightedTreeSet<N, Object, WeightedTree<N>> callGraph = getCallGraph(fetchParameters, subMonitor);
             if (subMonitor.isCanceled()) {
                 return new TmfModelResponse<>(null, ITmfResponse.Status.CANCELLED, CommonStatusMessage.TASK_CANCELLED);
             }
-            if (callGraph == null) {
-                return new TmfModelResponse<>(null, ITmfResponse.Status.FAILED, CommonStatusMessage.TASK_CANCELLED);
-            }
 
             long start = 0;
 
@@ -261,7 +292,7 @@
 
             TmfModelResponse<TmfTreeModel<FlameChartEntryModel>> response = new TmfModelResponse<>(new TmfTreeModel<>(Collections.emptyList(), tree),
                     ITmfResponse.Status.COMPLETED, CommonStatusMessage.COMPLETED);
-            fCached = new Pair<>(fetchParameters, response);
+            fCached = new Pair<>(cacheKey, response);
             return response;
 
         } finally {
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 b1fac22..a53f6dc 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
@@ -71,6 +71,7 @@
 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.analysis.core.weighted.tree.IWeightedTreeProvider;
 import org.eclipse.tracecompass.incubator.callstack.core.callgraph.ICallGraphProvider;
 import org.eclipse.tracecompass.incubator.internal.callstack.core.flamegraph.DataProviderUtils;
 import org.eclipse.tracecompass.incubator.internal.callstack.core.flamegraph.FlameGraphDataProvider;
@@ -97,6 +98,7 @@
 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.TmfStartAnalysisSignal;
 import org.eclipse.tracecompass.tmf.core.signal.TmfTraceClosedSignal;
 import org.eclipse.tracecompass.tmf.core.signal.TmfTraceSelectedSignal;
 import org.eclipse.tracecompass.tmf.core.symbols.ISymbolProvider;
@@ -1581,4 +1583,25 @@
         Display.getDefault().asyncExec(() -> restartZoomThread());
     }
 
+    /**
+     * Listen to see if one of the view's analysis is restarted
+     *
+     * @param signal
+     *            The analysis started signal
+     */
+    @TmfSignalHandler
+    public void analysisStart(TmfStartAnalysisSignal signal) {
+        ITmfTrace trace = getTrace();
+        if (trace == null) {
+            return;
+        }
+        IAnalysisModule module = signal.getAnalysisModule();
+        // It is not possible to link the module ID to the data provider, so
+        // just rebuild if the started module is a weighted tree provider
+        // FIXME This may be a performance issue
+        if (module instanceof IWeightedTreeProvider) {
+            buildFlameGraph(trace, null, null);
+        }
+    }
+
 }
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/views/flamechart/CallStackAnalysisListener.java b/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/views/flamechart/CallStackAnalysisListener.java
index c34b0fb..3e49a91 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/views/flamechart/CallStackAnalysisListener.java
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/views/flamechart/CallStackAnalysisListener.java
@@ -9,7 +9,7 @@
 
 package org.eclipse.tracecompass.incubator.internal.callstack.ui.views.flamechart;
 
-import org.eclipse.tracecompass.incubator.callstack.core.callgraph.ICallGraphProvider;
+import org.eclipse.tracecompass.incubator.analysis.core.weighted.tree.IWeightedTreeProvider;
 import org.eclipse.tracecompass.incubator.callstack.core.flamechart.IEventCallStackProvider;
 import org.eclipse.tracecompass.incubator.callstack.core.instrumented.IFlameChartProvider;
 import org.eclipse.tracecompass.incubator.internal.callstack.ui.flamegraph.FlameGraphSelView;
@@ -36,7 +36,7 @@
             module.registerOutput(new TmfAnalysisViewOutput(FlameChartView.ID, module.getId()));
             module.registerOutput(new TmfAnalysisViewOutput(FunctionDensityView.ID, module.getId()));
         }
-        if (module instanceof ICallGraphProvider) {
+        if (module instanceof IWeightedTreeProvider) {
             module.registerOutput(new TmfAnalysisViewOutput(FlameGraphView.ID, module.getId()));
             module.registerOutput(new TmfAnalysisViewOutput(FlameGraphSelView.SEL_ID, module.getId()));
             module.registerOutput(new TmfAnalysisViewOutput(WeightedTreeView.ID, module.getId()));
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/views/weightedtree/WeightedTreePieChartViewer.java b/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/views/weightedtree/WeightedTreePieChartViewer.java
index c22ae03..f00c89d 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/views/weightedtree/WeightedTreePieChartViewer.java
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/views/weightedtree/WeightedTreePieChartViewer.java
@@ -268,6 +268,10 @@
         for (WeightedTree<?> tree : trees) {
             totalWeight += tree.getWeight();
         }
+        if (totalWeight == 0) {
+            // Children have no weight, return
+            return;
+        }
 
         long otherWeight = 0;
         // Add to the list only the trees that would be visible (> threshold),
@@ -280,7 +284,7 @@
                 otherWeight += tree.getWeight();
             }
         }
-        Collections.sort(list);
+        Collections.sort(list, Collections.reverseOrder());
 
         int listSize = otherWeight == 0 ? list.size() : list.size() + 1;
         double[][] sliceValues = new double[listSize][1];
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/views/weightedtree/WeightedTreeViewer.java b/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/views/weightedtree/WeightedTreeViewer.java
index 73ef1c5..37e8e9d 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/views/weightedtree/WeightedTreeViewer.java
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/views/weightedtree/WeightedTreeViewer.java
@@ -350,6 +350,12 @@
         }
     }
 
+    /**
+     * Listen to see if one of the view's analysis is restarted
+     *
+     * @param signal
+     *            The analysis started signal
+     */
     @TmfSignalHandler
     public void analysisStart(TmfStartAnalysisSignal signal) {
         ITmfTrace trace = getTrace();
@@ -358,7 +364,7 @@
         }
         Set<IWeightedTreeProvider<?, ?, WeightedTree<?>>> modules = fView.getWeightedTrees(trace);
         if (modules.contains(signal.getAnalysisModule())) {
-            this.updateContent(trace.getStartTime().toNanos(), trace.getEndTime().toNanos(), false);
+            updateContent(trace.getStartTime().toNanos(), trace.getEndTime().toNanos(), false);
         }
 
     }
@@ -552,7 +558,7 @@
 
         Set<IWeightedTreeProvider<?, ?, WeightedTree<?>>> modules = fView.getWeightedTrees(trace);
 
-        if (modules.isEmpty()) {
+        if (isSelection || modules.isEmpty()) {
             return null;
         }
         modules.forEach(m -> {
@@ -593,7 +599,7 @@
      * @param monitor
      */
     private <@NonNull E> void setSelection(long start, long end, List<ITmfTreeViewerEntry> entryList, IWeightedTreeProvider<?, E, WeightedTree<?>> module, boolean isSelection, IProgressMonitor monitor) {
-
+        // Selection is not supported
     }
 
     @Override