callstack: Flame graph data provider use weithed tree

Change-Id: If517bae4eaa49bf29151399ffa673af0a753873c
Signed-off-by: Geneviève Bastien <gbastien+lttng@versatic.net>
Reviewed-on: https://git.eclipse.org/r/149805
Tested-by: CI Bot
Reviewed-by: Matthew Khouzam <matthew.khouzam@ericsson.com>
Tested-by: Matthew Khouzam <matthew.khouzam@ericsson.com>
diff --git a/analyses/org.eclipse.tracecompass.incubator.kernel.core/src/org/eclipse/tracecompass/incubator/internal/kernel/core/callstack/context/ContextCallStackAnalysis.java b/analyses/org.eclipse.tracecompass.incubator.kernel.core/src/org/eclipse/tracecompass/incubator/internal/kernel/core/callstack/context/ContextCallStackAnalysis.java
index 16b28ce..74a4b49 100644
--- a/analyses/org.eclipse.tracecompass.incubator.kernel.core/src/org/eclipse/tracecompass/incubator/internal/kernel/core/callstack/context/ContextCallStackAnalysis.java
+++ b/analyses/org.eclipse.tracecompass.incubator.kernel.core/src/org/eclipse/tracecompass/incubator/internal/kernel/core/callstack/context/ContextCallStackAnalysis.java
@@ -23,10 +23,10 @@
 import org.eclipse.tracecompass.analysis.os.linux.core.event.aspect.LinuxPidAspect;
 import org.eclipse.tracecompass.analysis.os.linux.core.event.aspect.LinuxTidAspect;
 import org.eclipse.tracecompass.incubator.analysis.core.concepts.AggregatedCallSite;
+import org.eclipse.tracecompass.incubator.analysis.core.weighted.tree.IWeightedTreeGroupDescriptor;
 import org.eclipse.tracecompass.incubator.callstack.core.base.CallStackElement;
 import org.eclipse.tracecompass.incubator.callstack.core.base.CallStackGroupDescriptor;
 import org.eclipse.tracecompass.incubator.callstack.core.base.ICallStackElement;
-import org.eclipse.tracecompass.incubator.callstack.core.base.ICallStackGroupDescriptor;
 import org.eclipse.tracecompass.incubator.callstack.core.sampled.callgraph.ProfilingCallGraphAnalysisModule;
 import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
 import org.eclipse.tracecompass.tmf.core.event.ITmfEventField;
@@ -208,7 +208,7 @@
     }
 
     @Override
-    public Collection<ICallStackGroupDescriptor> getGroupDescriptors() {
+    public Collection<IWeightedTreeGroupDescriptor> getGroupDescriptors() {
         return ImmutableList.of(fEventDescriptor);
     }
 
diff --git a/callstack/org.eclipse.tracecompass.incubator.analysis.core/src/org/eclipse/tracecompass/incubator/analysis/core/weighted/tree/WeightedTreeGroupBy.java b/callstack/org.eclipse.tracecompass.incubator.analysis.core/src/org/eclipse/tracecompass/incubator/analysis/core/weighted/tree/WeightedTreeGroupBy.java
index 7168cb6..dc7b055 100644
--- a/callstack/org.eclipse.tracecompass.incubator.analysis.core/src/org/eclipse/tracecompass/incubator/analysis/core/weighted/tree/WeightedTreeGroupBy.java
+++ b/callstack/org.eclipse.tracecompass.incubator.analysis.core/src/org/eclipse/tracecompass/incubator/analysis/core/weighted/tree/WeightedTreeGroupBy.java
@@ -54,13 +54,10 @@
             return groupWeightedTreeByAll(weightedTreeSet);
         }
 
-        WeightedTreeSet<N, Object> wts = new WeightedTreeSet<>();
-        searchForGroups(groupBy, weightedTreeSet, provider, wts);
-        return wts;
+        return searchForGroups(groupBy, weightedTreeSet, provider);
     }
 
-    private static <@NonNull N, E, T extends WeightedTree<N>> void searchForGroups(IWeightedTreeGroupDescriptor groupBy, IWeightedTreeSet<N, E, T> callGraph, IWeightedTreeProvider<N, E, T> provider,
-            WeightedTreeSet<N, Object> newCg) {
+    private static <@NonNull N, E, T extends WeightedTree<N>> WeightedTreeSet<N, Object> searchForGroups(IWeightedTreeGroupDescriptor groupBy, IWeightedTreeSet<N, E, T> callGraph, IWeightedTreeProvider<N, E, T> provider) {
         IWeightedTreeGroupDescriptor groupDescriptor = provider.getGroupDescriptor();
         int level = 0;
         while (groupDescriptor != null && !groupDescriptor.equals(groupBy)) {
@@ -68,11 +65,14 @@
             level++;
         }
 
+        WeightedTreeSet<N, Object> newCg = new WeightedTreeSet<>();
+
         Collection<E> elements = callGraph.getElements();
         for (E element : elements) {
             Object groupElement = (element instanceof ITree) ? ((ITree) element).copyElement() : Objects.requireNonNull(element);
             recurseAddElementData(element, groupElement, callGraph, newCg, 0, level);
         }
+        return newCg;
     }
 
     /**
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/perf/org/eclipse/tracecompass/incubator/callstack/core/tests/perf/analysis/CallStackAndGraphBenchmark.java b/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/perf/org/eclipse/tracecompass/incubator/callstack/core/tests/perf/analysis/CallStackAndGraphBenchmark.java
index c0ce489..732bb08 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/perf/org/eclipse/tracecompass/incubator/callstack/core/tests/perf/analysis/CallStackAndGraphBenchmark.java
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/perf/org/eclipse/tracecompass/incubator/callstack/core/tests/perf/analysis/CallStackAndGraphBenchmark.java
@@ -23,10 +23,10 @@
 import org.eclipse.test.performance.Dimension;
 import org.eclipse.test.performance.Performance;
 import org.eclipse.test.performance.PerformanceMeter;
-import org.eclipse.tracecompass.incubator.callstack.core.base.ICallStackGroupDescriptor;
-import org.eclipse.tracecompass.incubator.callstack.core.callgraph.AllGroupDescriptor;
+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.WeightedTreeGroupBy;
 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.IFlameChartProvider;
 import org.eclipse.tracecompass.incubator.callstack.core.instrumented.statesystem.InstrumentedCallStackAnalysis;
@@ -141,15 +141,15 @@
                 callgraphQueryPm.stop();
 
                 // Benchmark the group by. Do a few iterations in different orders
-                List<ICallStackGroupDescriptor> descriptors = new ArrayList<>();
+                List<IWeightedTreeGroupDescriptor> descriptors = new ArrayList<>();
                 descriptors.add(AllGroupDescriptor.getInstance());
                 descriptors.addAll(callGraphModule.getGroupDescriptors());
                 CallGraph callGraphToGroup = callGraphModule.getCallGraph();
                 callgraphGroupByPm.start();
                 for (int j = 0; j < 10; j++) {
-                    descriptors.forEach(group -> CallGraphGroupBy.groupCallGraphBy(group, callGraphToGroup));
+                    descriptors.forEach(group -> WeightedTreeGroupBy.groupWeightedTreeBy(group, callGraphToGroup, callGraphModule));
                     Collections.reverse(descriptors);
-                    descriptors.forEach(group -> CallGraphGroupBy.groupCallGraphBy(group, callGraphToGroup));
+                    descriptors.forEach(group -> WeightedTreeGroupBy.groupWeightedTreeBy(group, callGraphToGroup, callGraphModule));
                 }
                 callgraphGroupByPm.stop();
 
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/src/org/eclipse/tracecompass/incubator/callstack/core/tests/callgraph/CallGraphGroupByInstrumentedTest.java b/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/src/org/eclipse/tracecompass/incubator/callstack/core/tests/callgraph/CallGraphGroupByInstrumentedTest.java
index 835c995..316c448 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/src/org/eclipse/tracecompass/incubator/callstack/core/tests/callgraph/CallGraphGroupByInstrumentedTest.java
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/src/org/eclipse/tracecompass/incubator/callstack/core/tests/callgraph/CallGraphGroupByInstrumentedTest.java
@@ -18,12 +18,16 @@
 import java.util.Collections;
 import java.util.Map;
 
-import org.eclipse.tracecompass.incubator.analysis.core.concepts.AggregatedCallSite;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.tracecompass.incubator.analysis.core.concepts.ICallStackSymbol;
+import org.eclipse.tracecompass.incubator.analysis.core.weighted.tree.AllGroupDescriptor;
+import org.eclipse.tracecompass.incubator.analysis.core.weighted.tree.ITree;
+import org.eclipse.tracecompass.incubator.analysis.core.weighted.tree.IWeightedTreeGroupDescriptor;
+import org.eclipse.tracecompass.incubator.analysis.core.weighted.tree.WeightedTree;
+import org.eclipse.tracecompass.incubator.analysis.core.weighted.tree.WeightedTreeGroupBy;
+import org.eclipse.tracecompass.incubator.analysis.core.weighted.tree.WeightedTreeSet;
 import org.eclipse.tracecompass.incubator.callstack.core.base.ICallStackElement;
-import org.eclipse.tracecompass.incubator.callstack.core.base.ICallStackGroupDescriptor;
-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.tests.flamechart.CallStackTestBase;
 import org.eclipse.tracecompass.incubator.callstack.core.tests.stubs.CallStackAnalysisStub;
 import org.eclipse.tracecompass.incubator.internal.callstack.core.instrumented.callgraph.AggregatedCalledFunction;
@@ -33,7 +37,7 @@
 import com.google.common.collect.Iterables;
 
 /**
- * Test the {@link CallGraphGroupBy} class
+ * Test the {@link WeightedTreeGroupBy} class with a callgraph
  *
  * @author Geneviève Bastien
  */
@@ -132,14 +136,14 @@
         CallStackAnalysisStub cga = getModule();
         CallGraph baseCallGraph = cga.getCallGraph();
 
-        CallGraph callGraph = CallGraphGroupBy.groupCallGraphBy(AllGroupDescriptor.getInstance(), baseCallGraph);
-        Collection<ICallStackElement> elements = callGraph.getElements();
+        WeightedTreeSet<@NonNull ICallStackSymbol, @NonNull Object> callGraph = WeightedTreeGroupBy.groupWeightedTreeBy(AllGroupDescriptor.getInstance(), baseCallGraph, cga);
+        Collection<Object> elements = callGraph.getElements();
         assertEquals(1, elements.size());
 
-        ICallStackElement element = Iterables.getFirst(elements, null);
+        Object element = Iterables.getFirst(elements, null);
         assertNotNull(element);
 
-        Collection<AggregatedCallSite> callingContextTree = callGraph.getCallingContextTree(element);
+        Collection<@NonNull WeightedTree<@NonNull ICallStackSymbol>> callingContextTree = callGraph.getTreesFor(element);
         compareCcts("", getExpectedAll(), callingContextTree);
 
     }
@@ -153,27 +157,35 @@
         CallGraph baseCallGraph = cga.getCallGraph();
 
         // The first group descriptor is the process
-        Collection<ICallStackGroupDescriptor> groupDescriptors = cga.getGroupDescriptors();
-        ICallStackGroupDescriptor processGroup = Iterables.getFirst(groupDescriptors, null);
+        Collection<IWeightedTreeGroupDescriptor> groupDescriptors = cga.getGroupDescriptors();
+        IWeightedTreeGroupDescriptor processGroup = Iterables.getFirst(groupDescriptors, null);
         assertNotNull(processGroup);
 
-        CallGraph callGraph = CallGraphGroupBy.groupCallGraphBy(processGroup, baseCallGraph);
-        Collection<ICallStackElement> elements = callGraph.getElements();
+        WeightedTreeSet<@NonNull ICallStackSymbol, @NonNull Object> callGraph = WeightedTreeGroupBy.groupWeightedTreeBy(processGroup, baseCallGraph, cga);
+        Collection<Object> elements = callGraph.getElements();
         assertEquals(2, elements.size());
 
-        for (ICallStackElement element : elements) {
-            switch (element.getName()) {
+        for (Object element : elements) {
+            assertTrue(element instanceof ICallStackElement);
+            switch (String.valueOf(element)) {
             case "1": {
-                Collection<ICallStackElement> children = element.getChildrenElements();
+                Collection<@NonNull ITree> children = ((ICallStackElement) element).getChildren();
                 assertEquals(0, children.size());
-                Collection<AggregatedCallSite> callingContextTree = callGraph.getCallingContextTree(element);
+                // Make sure the children have no tree with them
+                for (ITree child : children) {
+                    assertTrue(callGraph.getTreesFor(child).isEmpty());
+                }
+                Collection<@NonNull WeightedTree<@NonNull ICallStackSymbol>> callingContextTree = callGraph.getTreesFor(element);
                 compareCcts("", getExpectedProcess1(), callingContextTree);
             }
                 break;
             case "5": {
-                Collection<ICallStackElement> children = element.getChildrenElements();
+                Collection<@NonNull ITree> children = ((ICallStackElement) element).getChildren();
                 assertEquals(0, children.size());
-                Collection<AggregatedCallSite> callingContextTree = callGraph.getCallingContextTree(element);
+                for (ITree child : children) {
+                    assertTrue(callGraph.getTreesFor(child).isEmpty());
+                }
+                Collection<@NonNull WeightedTree<@NonNull ICallStackSymbol>> callingContextTree = callGraph.getTreesFor(element);
                 compareCcts("", getExpectedProcess5(), callingContextTree);
             }
                 break;
@@ -192,8 +204,8 @@
         CallGraph baseCallGraph = cga.getCallGraph();
 
         // The first group descriptor is the process
-        Collection<ICallStackGroupDescriptor> groupDescriptors = cga.getGroupDescriptors();
-        ICallStackGroupDescriptor group = Iterables.getFirst(groupDescriptors, null);
+        Collection<IWeightedTreeGroupDescriptor> groupDescriptors = cga.getGroupDescriptors();
+        IWeightedTreeGroupDescriptor group = Iterables.getFirst(groupDescriptors, null);
         assertNotNull(group);
         while (group.getNextGroup() != null) {
             group = group.getNextGroup();
@@ -201,24 +213,25 @@
         }
 
         // Group by thread
-        CallGraph callGraph = CallGraphGroupBy.groupCallGraphBy(group, baseCallGraph);
-        Collection<ICallStackElement> elements = callGraph.getElements();
+        WeightedTreeSet<@NonNull ICallStackSymbol, @NonNull Object> callGraph = WeightedTreeGroupBy.groupWeightedTreeBy(group, baseCallGraph, cga);
+        Collection<Object> elements = callGraph.getElements();
         assertEquals(2, elements.size());
 
-        for (ICallStackElement element : elements) {
-            switch (element.getName()) {
+        for (Object element : elements) {
+            assertTrue(element instanceof ICallStackElement);
+            switch (String.valueOf(element)) {
             case "1": {
-                Collection<ICallStackElement> children = element.getChildrenElements();
+                Collection<@NonNull ITree> children = ((ICallStackElement) element).getChildren();
                 assertEquals(2, children.size());
-                for (ICallStackElement thread : children) {
-                    switch (thread.getName()) {
+                for (ITree thread : children) {
+                    switch (String.valueOf(thread)) {
                     case "2": {
-                        Collection<AggregatedCallSite> callingContextTree = callGraph.getCallingContextTree(thread);
+                        Collection<@NonNull WeightedTree<@NonNull ICallStackSymbol>> callingContextTree = callGraph.getTreesFor(thread);
                         compareCcts("", getExpectedThread2(), callingContextTree);
                     }
                         break;
                     case "3": {
-                        Collection<AggregatedCallSite> callingContextTree = callGraph.getCallingContextTree(thread);
+                        Collection<@NonNull WeightedTree<@NonNull ICallStackSymbol>> callingContextTree = callGraph.getTreesFor(thread);
                         compareCcts("", getExpectedThread3(), callingContextTree);
                     }
                         break;
@@ -229,17 +242,17 @@
             }
                 break;
             case "5": {
-                Collection<ICallStackElement> children = element.getChildrenElements();
+                Collection<@NonNull ITree> children = ((ICallStackElement) element).getChildren();
                 assertEquals(2, children.size());
-                for (ICallStackElement thread : children) {
-                    switch (thread.getName()) {
+                for (ITree thread : children) {
+                    switch (String.valueOf(thread)) {
                     case "6": {
-                        Collection<AggregatedCallSite> callingContextTree = callGraph.getCallingContextTree(thread);
+                        Collection<@NonNull WeightedTree<@NonNull ICallStackSymbol>> callingContextTree = callGraph.getTreesFor(thread);
                         compareCcts("", getExpectedThread6(), callingContextTree);
                     }
                         break;
                     case "7": {
-                        Collection<AggregatedCallSite> callingContextTree = callGraph.getCallingContextTree(thread);
+                        Collection<@NonNull WeightedTree<@NonNull ICallStackSymbol>> callingContextTree = callGraph.getTreesFor(thread);
                         compareCcts("", getExpectedThread7(), callingContextTree);
                     }
                         break;
@@ -281,16 +294,17 @@
 
     }
 
-    private void compareCcts(String prefix, Map<String, CallGraphExpected> expected, Collection<AggregatedCallSite> callingContextTree) {
+    private void compareCcts(String prefix, Map<String, CallGraphExpected> expected, Collection<@NonNull WeightedTree<@NonNull ICallStackSymbol>> callingContextTree) {
         assertEquals(expected.size(), callingContextTree.size());
-        for (AggregatedCallSite callsite : callingContextTree) {
+        for (WeightedTree<@NonNull ICallStackSymbol> callsite : callingContextTree) {
             assertTrue(callsite instanceof AggregatedCalledFunction);
             AggregatedCalledFunction function = (AggregatedCalledFunction) callsite;
-            CallGraphExpected cgExpected = expected.get(getCallSiteSymbol(callsite).resolve(Collections.emptySet()));
+            ICallStackSymbol callSiteSymbol = getCallSiteSymbol(function);
+            CallGraphExpected cgExpected = expected.get(callSiteSymbol.resolve(Collections.emptySet()));
             assertNotNull(cgExpected);
-            assertEquals("Callsite " + getCallSiteSymbol(callsite), cgExpected.duration, function.getDuration());
-            assertEquals("Callsite " + getCallSiteSymbol(callsite), cgExpected.selfTime, function.getSelfTime());
-            compareCcts(prefix + getCallSiteSymbol(callsite) + ", ", cgExpected.children, function.getCallees());
+            assertEquals("Callsite " + callSiteSymbol, cgExpected.duration, function.getDuration());
+            assertEquals("Callsite " + callSiteSymbol, cgExpected.selfTime, function.getSelfTime());
+            compareCcts(prefix + callSiteSymbol + ", ", cgExpected.children, callsite.getChildren());
         }
     }
 
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/src/org/eclipse/tracecompass/incubator/callstack/core/tests/callgraph/FlameGraphDataProviderTest.java b/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/src/org/eclipse/tracecompass/incubator/callstack/core/tests/callgraph/FlameGraphDataProviderTest.java
index f9b55d8..1cd8f14 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/src/org/eclipse/tracecompass/incubator/callstack/core/tests/callgraph/FlameGraphDataProviderTest.java
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/src/org/eclipse/tracecompass/incubator/callstack/core/tests/callgraph/FlameGraphDataProviderTest.java
@@ -25,7 +25,7 @@
 import java.util.Set;
 
 import org.eclipse.jdt.annotation.NonNull;
-import org.eclipse.tracecompass.incubator.callstack.core.callgraph.AllGroupDescriptor;
+import org.eclipse.tracecompass.incubator.analysis.core.weighted.tree.AllGroupDescriptor;
 import org.eclipse.tracecompass.incubator.callstack.core.tests.flamechart.CallStackTestBase;
 import org.eclipse.tracecompass.incubator.callstack.core.tests.stubs.CallStackAnalysisStub;
 import org.eclipse.tracecompass.incubator.callstack.core.tests.stubs.FlameDataProviderTestUtils;
@@ -68,7 +68,7 @@
     public void testFlameGraphDataProviderAllItems() throws IOException {
         CallStackAnalysisStub cga = getModule();
 
-        FlameGraphDataProvider provider = new FlameGraphDataProvider(getTrace(), cga, cga.getId());
+        FlameGraphDataProvider<?, ?, ?> provider = new FlameGraphDataProvider<>(getTrace(), cga, cga.getId());
 
         Map<Long, FlameChartEntryModel> idsToNames = assertAndGetTree(provider, "expectedFgTreeFull", Collections.emptyMap());
 
@@ -87,7 +87,7 @@
     public void testFlameGraphDataProviderGroupByProcess() throws IOException {
         CallStackAnalysisStub cga = getModule();
 
-        FlameGraphDataProvider provider = new FlameGraphDataProvider(getTrace(), cga, cga.getId());
+        FlameGraphDataProvider<?, ?, ?> provider = new FlameGraphDataProvider<>(getTrace(), cga, cga.getId());
 
         Map<Long, FlameChartEntryModel> idsToNames = assertAndGetTree(provider, "expectedFgTreeProcess", ImmutableMap.of(FlameGraphDataProvider.GROUP_BY_KEY, "Processes/*"));
 
@@ -107,7 +107,7 @@
     public void testFlameGraphDataProviderGrouped() throws IOException {
         CallStackAnalysisStub cga = getModule();
 
-        FlameGraphDataProvider provider = new FlameGraphDataProvider(getTrace(), cga, cga.getId());
+        FlameGraphDataProvider<?, ?, ?> provider = new FlameGraphDataProvider<>(getTrace(), cga, cga.getId());
 
         Map<Long, FlameChartEntryModel> idsToNames = assertAndGetTree(provider, "expectedFgTreeOne", ImmutableMap.of(FlameGraphDataProvider.GROUP_BY_KEY, AllGroupDescriptor.getInstance().getName()));
 
@@ -127,7 +127,7 @@
     public void testFlameGraphDataProviderSelection() throws IOException {
         CallStackAnalysisStub cga = getModule();
 
-        FlameGraphDataProvider provider = new FlameGraphDataProvider(getTrace(), cga, cga.getId());
+        FlameGraphDataProvider<?, ?, ?> provider = new FlameGraphDataProvider<>(getTrace(), cga, cga.getId());
 
         Map<Long, FlameChartEntryModel> idsToNames = assertAndGetTree(provider, "expectedFgTreeSelection", ImmutableMap.of(FlameGraphDataProvider.SELECTION_RANGE_KEY, ImmutableList.of(5, 15)));
 
@@ -135,7 +135,7 @@
 
     }
 
-    private static void assertRowsRequests(FlameGraphDataProvider provider, Map<Long, FlameChartEntryModel> idsToNames, String resultFileSuffix, long maxDuration) throws IOException {
+    private static void assertRowsRequests(FlameGraphDataProvider<?, ?, ?> provider, Map<Long, FlameChartEntryModel> idsToNames, String resultFileSuffix, long maxDuration) throws IOException {
         String filePrefix = "expectedFgRow" + resultFileSuffix;
         // Test getting all the states
         Builder<Long> builder = ImmutableList.builder();
@@ -155,7 +155,7 @@
         assertRows(provider, idsToNames, builder.build(), filePrefix, "Zoom");
     }
 
-    private static Map<Long, FlameChartEntryModel> assertAndGetTree(FlameGraphDataProvider provider, String filePath, @NonNull Map<@NonNull String, @NonNull Object> additionalParameters) throws IOException {
+    private static Map<Long, FlameChartEntryModel> assertAndGetTree(FlameGraphDataProvider<?, ?, ?> provider, String filePath, @NonNull Map<@NonNull String, @NonNull Object> additionalParameters) throws IOException {
         Map<@NonNull String, @NonNull Object> parameters = new HashMap<>(TREE_PARAMETERS);
         parameters.putAll(additionalParameters);
         TmfModelResponse<TmfTreeModel<@NonNull FlameChartEntryModel>> treeResponse = provider.fetchTree(parameters, null);
@@ -198,7 +198,7 @@
         return FlameChartEntryModel.EntryType.valueOf(string.toUpperCase());
     }
 
-    private static void assertRows(FlameGraphDataProvider provider, Map<Long, FlameChartEntryModel> idsToNames, @NonNull List<Long> requestedTimes, String filePath, String descriptor) throws IOException {
+    private static void assertRows(FlameGraphDataProvider<?, ?, ?> provider, Map<Long, FlameChartEntryModel> idsToNames, @NonNull List<Long> requestedTimes, String filePath, String descriptor) throws IOException {
         TmfModelResponse<TimeGraphModel> rowResponse = provider.fetchRowModel(prepareRowParameters(idsToNames.keySet(), requestedTimes), null);
         assertNotNull(rowResponse);
         assertEquals(ITmfResponse.Status.COMPLETED, rowResponse.getStatus());
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/src/org/eclipse/tracecompass/incubator/callstack/core/tests/sampled/SampledCallGraphTest.java b/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/src/org/eclipse/tracecompass/incubator/callstack/core/tests/sampled/SampledCallGraphTest.java
index 9f43309..cb1a380 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/src/org/eclipse/tracecompass/incubator/callstack/core/tests/sampled/SampledCallGraphTest.java
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/src/org/eclipse/tracecompass/incubator/callstack/core/tests/sampled/SampledCallGraphTest.java
@@ -19,10 +19,10 @@
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.Nullable;
 import org.eclipse.tracecompass.incubator.analysis.core.concepts.AggregatedCallSite;
+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.base.CallStackElement;
 import org.eclipse.tracecompass.incubator.callstack.core.base.ICallStackElement;
-import org.eclipse.tracecompass.incubator.callstack.core.base.ICallStackGroupDescriptor;
-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.sampled.callgraph.ProfilingCallGraphAnalysisModule;
 import org.eclipse.tracecompass.incubator.callstack.core.tests.flamechart.CallStackTestBase;
@@ -67,7 +67,7 @@
         }
 
         @Override
-        public Collection<ICallStackGroupDescriptor> getGroupDescriptors() {
+        public Collection<IWeightedTreeGroupDescriptor> getGroupDescriptors() {
             return Collections.singleton(AllGroupDescriptor.getInstance());
         }
 
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/callstack/core/base/CallStackElement.java b/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/callstack/core/base/CallStackElement.java
index bb32d3c..e935cf2 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/callstack/core/base/CallStackElement.java
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/callstack/core/base/CallStackElement.java
@@ -15,6 +15,7 @@
 
 import org.eclipse.jdt.annotation.Nullable;
 import org.eclipse.tracecompass.incubator.analysis.core.weighted.tree.ITree;
+import org.eclipse.tracecompass.incubator.analysis.core.weighted.tree.IWeightedTreeGroupDescriptor;
 
 /**
  * A basic callstack element implementing the methods of the interface.
@@ -29,8 +30,8 @@
     public static final int DEFAULT_SYMBOL_KEY = -1;
 
     private final String fName;
-    private final ICallStackGroupDescriptor fDescriptor;
-    private final @Nullable ICallStackGroupDescriptor fNextDescriptor;
+    private final IWeightedTreeGroupDescriptor fDescriptor;
+    private final @Nullable IWeightedTreeGroupDescriptor fNextDescriptor;
     private final Collection<ICallStackElement> fChildren = new ArrayList<>();
     private @Nullable ICallStackElement fParent;
     private @Nullable ICallStackElement fSymbolKeyElement = null;
@@ -43,7 +44,7 @@
      * @param descriptor
      *            The corresponding group descriptor
      */
-    public CallStackElement(String name, ICallStackGroupDescriptor descriptor) {
+    public CallStackElement(String name, IWeightedTreeGroupDescriptor descriptor) {
         this(name, descriptor, null, null);
     }
 
@@ -59,7 +60,7 @@
      * @param parent
      *            The parent element
      */
-    public CallStackElement(String name, ICallStackGroupDescriptor descriptor, @Nullable ICallStackGroupDescriptor nextGroup, @Nullable ICallStackElement parent) {
+    public CallStackElement(String name, IWeightedTreeGroupDescriptor descriptor, @Nullable IWeightedTreeGroupDescriptor nextGroup, @Nullable ICallStackElement parent) {
         fName = name;
         fDescriptor = descriptor;
         fParent = parent;
@@ -92,7 +93,7 @@
     }
 
     @Override
-    public ICallStackGroupDescriptor getGroup() {
+    public IWeightedTreeGroupDescriptor getGroup() {
         return fDescriptor;
     }
 
@@ -107,7 +108,7 @@
     }
 
     @Override
-    public @Nullable ICallStackGroupDescriptor getNextGroup() {
+    public @Nullable IWeightedTreeGroupDescriptor getNextGroup() {
         return fNextDescriptor;
     }
 
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/callstack/core/base/ICallStackElement.java b/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/callstack/core/base/ICallStackElement.java
index 26e4192..dfc7791 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/callstack/core/base/ICallStackElement.java
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/callstack/core/base/ICallStackElement.java
@@ -13,6 +13,7 @@
 
 import org.eclipse.jdt.annotation.Nullable;
 import org.eclipse.tracecompass.incubator.analysis.core.weighted.tree.ITree;
+import org.eclipse.tracecompass.incubator.analysis.core.weighted.tree.IWeightedTreeGroupDescriptor;
 
 /**
  * Interface that classes representing a single element in the callstack
@@ -31,6 +32,8 @@
      * Get the elements at the next level of the callstack hierarchy from this
      * element
      *
+     * FIXME: Can this method be completely replace by {@link ITree#getChildren()}?
+     *
      * @return The list of children elements in the hierarchy
      */
     Collection<ICallStackElement> getChildrenElements();
@@ -38,17 +41,21 @@
     /**
      * Get the corresponding group descriptor
      *
+     * FIXME: Remove this method?
+     *
      * @return The group descriptor of this element
      */
-    ICallStackGroupDescriptor getGroup();
+    IWeightedTreeGroupDescriptor getGroup();
 
     /**
      * Get the next group descriptor
      *
+     * FIXME: Remove this method?
+     *
      * @return The next group descriptor, or <code>null</code> if this is a leaf
      *         element
      */
-    @Nullable ICallStackGroupDescriptor getNextGroup();
+    @Nullable IWeightedTreeGroupDescriptor getNextGroup();
 
     /**
      * Get the key for symbol resolution at a given time
@@ -78,6 +85,8 @@
      * Get the parent element, or <code>null</code> if this element corresponds
      * to the first group of the hierarchy
      *
+     * FIXME: Can this method be completely replace by {@link ITree#getParent()}?
+     *
      * @return The parent element
      */
     @Nullable ICallStackElement getParentElement();
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/callstack/core/callgraph/CallGraphGroupBy.java b/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/callstack/core/callgraph/CallGraphGroupBy.java
index bdffee5..61064d4 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/callstack/core/callgraph/CallGraphGroupBy.java
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/callstack/core/callgraph/CallGraphGroupBy.java
@@ -11,9 +11,10 @@
 
 import org.eclipse.jdt.annotation.Nullable;
 import org.eclipse.tracecompass.incubator.analysis.core.concepts.AggregatedCallSite;
+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.base.CallStackElement;
 import org.eclipse.tracecompass.incubator.callstack.core.base.ICallStackElement;
-import org.eclipse.tracecompass.incubator.callstack.core.base.ICallStackGroupDescriptor;
 
 /**
  * A class containing helper methods to group aggregated callgraph data by the
@@ -37,7 +38,7 @@
      * @return A collection of data that is the result of the grouping by the
      *         descriptor
      */
-    public static CallGraph groupCallGraphBy(ICallStackGroupDescriptor groupBy, CallGraph callGraph) {
+    public static CallGraph groupCallGraphBy(IWeightedTreeGroupDescriptor groupBy, CallGraph callGraph) {
         // Fast return: just aggregated all groups together
         if (groupBy.equals(AllGroupDescriptor.getInstance())) {
             return groupCallGraphByAll(callGraph);
@@ -68,7 +69,7 @@
         return cg;
     }
 
-    private static void searchForGroups(ICallStackElement element, ICallStackGroupDescriptor groupBy, CallGraph callGraph, @Nullable ICallStackElement parentElement, CallGraph newCg) {
+    private static void searchForGroups(ICallStackElement element, IWeightedTreeGroupDescriptor groupBy, CallGraph callGraph, @Nullable ICallStackElement parentElement, CallGraph newCg) {
         if (element.getGroup().equals(groupBy)) {
             ICallStackElement groupedElement = new CallStackElement(element.getName(), groupBy, null, parentElement);
             if (parentElement != null) {
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/callstack/core/callgraph/ICallGraphProvider.java b/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/callstack/core/callgraph/ICallGraphProvider.java
index f7f3e88..92315bc 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/callstack/core/callgraph/ICallGraphProvider.java
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/callstack/core/callgraph/ICallGraphProvider.java
@@ -38,12 +38,12 @@
      *
      * @return The collection of group descriptors for this call graph
      */
-    Collection<ICallStackGroupDescriptor> getGroupDescriptors();
+    Collection<IWeightedTreeGroupDescriptor> getGroupDescriptors();
 
     @Override
     default @Nullable IWeightedTreeGroupDescriptor getGroupDescriptor() {
         // Return the first group descriptor
-        Collection<ICallStackGroupDescriptor> groupDescriptors = getGroupDescriptors();
+        Collection<IWeightedTreeGroupDescriptor> groupDescriptors = getGroupDescriptors();
         if (groupDescriptors.isEmpty()) {
             return null;
         }
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/callstack/core/instrumented/statesystem/InstrumentedCallStackAnalysis.java b/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/callstack/core/instrumented/statesystem/InstrumentedCallStackAnalysis.java
index ba996d5..b64a2f5 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/callstack/core/instrumented/statesystem/InstrumentedCallStackAnalysis.java
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/callstack/core/instrumented/statesystem/InstrumentedCallStackAnalysis.java
@@ -20,8 +20,8 @@
 import org.eclipse.tracecompass.analysis.profiling.core.callstack.CallStackStateProvider;
 import org.eclipse.tracecompass.analysis.timing.core.segmentstore.IAnalysisProgressListener;
 import org.eclipse.tracecompass.incubator.analysis.core.concepts.AggregatedCallSite;
+import org.eclipse.tracecompass.incubator.analysis.core.weighted.tree.IWeightedTreeGroupDescriptor;
 import org.eclipse.tracecompass.incubator.callstack.core.base.EdgeStateValue;
-import org.eclipse.tracecompass.incubator.callstack.core.base.ICallStackGroupDescriptor;
 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.callgraph.SymbolAspect;
@@ -229,7 +229,7 @@
     }
 
     @Override
-    public Collection<ICallStackGroupDescriptor> getGroupDescriptors() {
+    public Collection<IWeightedTreeGroupDescriptor> getGroupDescriptors() {
         fCallGraph.schedule();
         fCallGraph.waitForCompletion();
         return fCallGraph.getGroupDescriptors();
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 82e0098..dd37f35 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
@@ -9,7 +9,6 @@
 
 package org.eclipse.tracecompass.incubator.internal.callstack.core.flamegraph;
 
-import java.text.Format;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -17,10 +16,9 @@
 import java.util.Comparator;
 import java.util.Deque;
 import java.util.HashMap;
-import java.util.Iterator;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Map.Entry;
 import java.util.NavigableSet;
 import java.util.Objects;
 import java.util.concurrent.atomic.AtomicLong;
@@ -29,28 +27,25 @@
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
+import org.apache.commons.lang3.StringUtils;
 import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.core.runtime.NullProgressMonitor;
 import org.eclipse.core.runtime.SubMonitor;
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.Nullable;
 import org.eclipse.tracecompass.analysis.timing.core.statistics.IStatistics;
-import org.eclipse.tracecompass.common.core.format.SubSecondTimeWithUnitFormat;
 import org.eclipse.tracecompass.common.core.log.TraceCompassLog;
 import org.eclipse.tracecompass.common.core.log.TraceCompassLogUtils.FlowScopeLog;
 import org.eclipse.tracecompass.common.core.log.TraceCompassLogUtils.FlowScopeLogBuilder;
 import org.eclipse.tracecompass.datastore.core.serialization.ISafeByteBufferWriter;
-import org.eclipse.tracecompass.incubator.analysis.core.concepts.AggregatedCallSite;
-import org.eclipse.tracecompass.incubator.analysis.core.concepts.ICallStackSymbol;
 import org.eclipse.tracecompass.incubator.analysis.core.model.IHostModel;
+import org.eclipse.tracecompass.incubator.analysis.core.weighted.tree.AllGroupDescriptor;
+import org.eclipse.tracecompass.incubator.analysis.core.weighted.tree.ITree;
+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.analysis.core.weighted.tree.IWeightedTreeProvider.MetricType;
+import org.eclipse.tracecompass.incubator.analysis.core.weighted.tree.IWeightedTreeSet;
 import org.eclipse.tracecompass.incubator.analysis.core.weighted.tree.WeightedTree;
-import org.eclipse.tracecompass.incubator.callstack.core.base.ICallStackElement;
-import org.eclipse.tracecompass.incubator.callstack.core.base.ICallStackGroupDescriptor;
-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.analysis.core.weighted.tree.WeightedTreeGroupBy;
 import org.eclipse.tracecompass.incubator.internal.callstack.core.instrumented.callgraph.AggregatedThreadStatus;
 import org.eclipse.tracecompass.incubator.internal.callstack.core.instrumented.provider.FlameChartEntryModel;
 import org.eclipse.tracecompass.incubator.internal.callstack.core.instrumented.provider.FlameChartEntryModel.EntryType;
@@ -80,8 +75,6 @@
 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.symbols.ISymbolProvider;
-import org.eclipse.tracecompass.tmf.core.symbols.SymbolProviderManager;
 import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp;
 import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
 import org.eclipse.tracecompass.tmf.core.util.Pair;
@@ -103,9 +96,15 @@
  * TODO: Use weighted tree instead of callgraph provider
  *
  * @author Geneviève Bastien
+ * @param <N>
+ *            The type of objects represented by each node in the tree
+ * @param <E>
+ *            The type of elements used to group the trees
+ * @param <T>
+ *            The type of the tree provided
  */
 @SuppressWarnings("restriction")
-public class FlameGraphDataProvider extends AbstractTmfTraceDataProvider implements ITimeGraphDataProvider<FlameChartEntryModel> {
+public class FlameGraphDataProvider<@NonNull N, E, @NonNull T extends WeightedTree<@NonNull N>> extends AbstractTmfTraceDataProvider implements ITimeGraphDataProvider<FlameChartEntryModel> {
 
     /**
      * Provider ID.
@@ -121,55 +120,50 @@
      */
     public static final String SELECTION_RANGE_KEY = "selection_range"; //$NON-NLS-1$
     private static final AtomicLong ENTRY_ID = new AtomicLong();
-    private static final Comparator<AggregatedCallSite> CCT_COMPARATOR = Comparator.comparingLong(AggregatedCallSite::getWeight).thenComparing(s -> String.valueOf(s.getObject()));
-    private final Comparator<WeightedTree<ICallStackSymbol>> CCT_COMPARATOR2 = Comparator.comparing(WeightedTree<ICallStackSymbol>::getWeight).thenComparing(s -> String.valueOf(s.getObject()));
+    private final Comparator<WeightedTree<N>> CCT_COMPARATOR2 = Comparator.comparing(WeightedTree<N>::getWeight).thenComparing(s -> String.valueOf(s.getObject()));
     /**
      * Logger for Abstract Tree Data Providers.
      */
     private static final Logger LOGGER = TraceCompassLog.getLogger(FlameGraphDataProvider.class);
-    private static final Format FORMATTER = SubSecondTimeWithUnitFormat.getInstance();
 
     /* State System attributes for the root levels */
-    private static final String FUNCTION_LEVEL = "Function"; //$NON-NLS-1$
-    private static final String EXTRA_LEVEL = "Extra"; //$NON-NLS-1$
+    private static final String FUNCTION_LEVEL = "::Function"; //$NON-NLS-1$
 
-    private final ICallGraphProvider fCgProvider;
+    private final IWeightedTreeProvider<N, E, T> fWtProvider;
+
     private final String fAnalysisId;
     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 final Map<Long, FlameChartEntryModel> fEntries = new HashMap<>();
-    private final Map<Long, CallGraphEntry> fCgEntries = new HashMap<>();
-    private final Collection<ISymbolProvider> fSymbolProviders;
+    private final Map<Long, WeightedTreeEntry> fCgEntries = new HashMap<>();
     private final Map<Long, Long> fEndTimes = new HashMap<>();
 
     /** An internal class to describe the data for an entry */
-    private static class CallGraphEntry {
-        private final ICallStackElement fElement;
+    private class WeightedTreeEntry {
         private ITmfStateSystem fSs;
         private Integer fQuark;
 
-        public CallGraphEntry(ICallStackElement element, ITmfStateSystem ss, Integer quark) {
-            fElement = element;
+        public WeightedTreeEntry(ITmfStateSystem ss, Integer quark) {
             fSs = ss;
             fQuark = quark;
         }
     }
 
-    private static class CallSiteCustomValue extends CustomStateValue {
-        private WeightedTree<ICallStackSymbol> fCallSite;
+    private static class CalleeCustomValue<@NonNull N> extends CustomStateValue {
+        private WeightedTree<N> fCallSite;
 
-        public CallSiteCustomValue(WeightedTree<ICallStackSymbol> rootFunction) {
+        public CalleeCustomValue(WeightedTree<N> rootFunction) {
             fCallSite = rootFunction;
         }
 
         @Override
         public int compareTo(ITmfStateValue o) {
-            if (!(o instanceof CallSiteCustomValue)) {
+            if (!(o instanceof CalleeCustomValue)) {
                 return -1;
             }
-            return fCallSite.compareTo(((CallSiteCustomValue) o).fCallSite);
+            return fCallSite.compareTo(((CalleeCustomValue) o).fCallSite);
         }
 
         @Override
@@ -199,36 +193,34 @@
      * @param secondaryId
      *            The ID of the weighted tree provider
      */
-    public FlameGraphDataProvider(ITmfTrace trace, ICallGraphProvider module, String secondaryId) {
+    public FlameGraphDataProvider(ITmfTrace trace, IWeightedTreeProvider<N, E, T> module, String secondaryId) {
         super(trace);
-        fCgProvider = module;
+        fWtProvider = module;
         fAnalysisId = secondaryId;
-        Collection<ISymbolProvider> symbolProviders = SymbolProviderManager.getInstance().getSymbolProviders(trace);
-        symbolProviders.forEach(provider -> provider.loadConfiguration(new NullProgressMonitor()));
-        fSymbolProviders = symbolProviders;
     }
 
     @Override
     public String getId() {
-        return ID + ':' + fAnalysisId;
+        return fAnalysisId;
     }
 
     @Override
     public @NonNull TmfModelResponse<@NonNull TmfTreeModel<@NonNull FlameChartEntryModel>> fetchTree(@NonNull Map<@NonNull String, @NonNull Object> fetchParameters, @Nullable IProgressMonitor monitor) {
-        // Did we cache this tree with those parameters
-        Pair<Map<String, Object>, TmfModelResponse<TmfTreeModel<FlameChartEntryModel>>> cached = fCached;
-        if (cached != null && cached.getFirst().equals(fetchParameters)) {
-            return cached.getSecond();
-        }
+
         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)) {
+                return cached.getSecond();
+            }
 
             fEntries.clear();
             fCgEntries.clear();
             SubMonitor subMonitor = Objects.requireNonNull(SubMonitor.convert(monitor, "FlameGraphDataProvider#fetchRowModel", 2)); //$NON-NLS-1$
 
-            CallGraph callGraph = getCallGraph(fetchParameters, subMonitor);
+            IWeightedTreeSet<N, Object, WeightedTree<N>> callGraph = getCallGraph(fetchParameters, subMonitor);
             if (subMonitor.isCanceled()) {
                 return new TmfModelResponse<>(null, ITmfResponse.Status.CANCELLED, CommonStatusMessage.TASK_CANCELLED);
             }
@@ -242,7 +234,7 @@
             List<FlameChartEntryModel.Builder> builder = new ArrayList<>();
             FlameChartEntryModel.Builder traceEntry = new FlameChartEntryModel.Builder(fTraceId, -1, getTrace().getName(), start, FlameChartEntryModel.EntryType.TRACE, -1);
 
-            buildCallGraphEntries(callGraph, builder, traceEntry);
+            buildWeightedTreeEntries(callGraph, builder, traceEntry);
 
             ImmutableList.Builder<FlameChartEntryModel> treeBuilder = ImmutableList.builder();
             long end = traceEntry.getEndTime();
@@ -269,11 +261,14 @@
         }
     }
 
-    private @Nullable CallGraph getCallGraph(Map<String, Object> fetchParameters, SubMonitor subMonitor) {
+    /**
+     * @param fetchParameters
+     */
+    private @Nullable IWeightedTreeSet<N, Object, WeightedTree<N>> getCallGraph(Map<String, Object> fetchParameters, SubMonitor subMonitor) {
         // Get the provider and wait for the analysis completion
-        ICallGraphProvider fcProvider = fCgProvider;
-        if (fcProvider instanceof IAnalysisModule) {
-            ((IAnalysisModule) fcProvider).waitForCompletion(subMonitor);
+        IWeightedTreeProvider<N, E, T> wtProvider = fWtProvider;
+        if (wtProvider instanceof IAnalysisModule) {
+            ((IAnalysisModule) wtProvider).waitForCompletion(subMonitor);
         }
         if (subMonitor.isCanceled()) {
             return null;
@@ -281,25 +276,28 @@
 
         // Get the full or selection callgraph
         List<Long> selectionRange = DataProviderParameterUtils.extractLongList(fetchParameters, SELECTION_RANGE_KEY);
-        CallGraph callGraph;
+        IWeightedTreeSet<@NonNull N, E, @NonNull T> callGraph;
         if (selectionRange == null || selectionRange.size() != 2) {
-            callGraph = fcProvider.getCallGraph();
+            callGraph = wtProvider.getTreeSet();
         } else {
             long time0 = selectionRange.get(0);
             long time1 = selectionRange.get(1);
-            callGraph = fcProvider.getCallGraph(TmfTimestamp.fromNanos(Math.min(time0, time1)), TmfTimestamp.fromNanos(Math.max(time0, time1)));
+            callGraph = wtProvider.getSelection(TmfTimestamp.fromNanos(Math.min(time0, time1)), TmfTimestamp.fromNanos(Math.max(time0, time1)));
+        }
+        if (callGraph == null) {
+            return null;
         }
 
         // Look if we need to group the callgraph
-        ICallStackGroupDescriptor groupDescriptor = extractGroupDescriptor(fetchParameters, fcProvider);
+        IWeightedTreeGroupDescriptor groupDescriptor = extractGroupDescriptor(fetchParameters, wtProvider);
         if (groupDescriptor != null) {
-            callGraph = CallGraphGroupBy.groupCallGraphBy(groupDescriptor, callGraph);
+            return WeightedTreeGroupBy.groupWeightedTreeBy(groupDescriptor, callGraph, wtProvider);
         }
 
-        return callGraph;
+        return (IWeightedTreeSet<@NonNull N, Object, WeightedTree<@NonNull N>>) callGraph;
     }
 
-    private static @Nullable ICallStackGroupDescriptor extractGroupDescriptor(Map<String, Object> fetchParameters, ICallGraphProvider fcProvider) {
+    private static @Nullable IWeightedTreeGroupDescriptor extractGroupDescriptor(Map<String, Object> fetchParameters, IWeightedTreeProvider<?, ?, ?> fcProvider) {
         Object groupBy = fetchParameters.get(GROUP_BY_KEY);
         if (groupBy == null) {
             return null;
@@ -310,35 +308,38 @@
             return AllGroupDescriptor.getInstance();
         }
         // Try to find the right group descriptor
-        for (ICallStackGroupDescriptor groupDescriptor : fcProvider.getGroupDescriptors()) {
+        IWeightedTreeGroupDescriptor groupDescriptor = fcProvider.getGroupDescriptor();
+        while (groupDescriptor != null) {
             if (groupDescriptor.getName().equals(groupName)) {
                 return groupDescriptor;
             }
+            groupDescriptor = groupDescriptor.getNextGroup();
         }
         return null;
     }
 
-    private void buildCallGraphEntries(CallGraph callgraph, List<FlameChartEntryModel.Builder> builder, FlameChartEntryModel.Builder traceEntry) {
-        Collection<ICallStackElement> elements = callgraph.getElements();
-        for (ICallStackElement element : elements) {
-            buildChildrenEntries(element, callgraph, builder, traceEntry);
+    private void buildWeightedTreeEntries(IWeightedTreeSet<N, Object, WeightedTree<N>> callGraph, List<FlameChartEntryModel.Builder> builder, FlameChartEntryModel.Builder traceEntry) {
+        IWeightedTreeProvider<N, E, T> wtProvider = fWtProvider;
+        Collection<@NonNull ?> elements = callGraph.getElements();
+        for (Object element : elements) {
+            buildChildrenEntries(element, wtProvider, callGraph, builder, traceEntry);
         }
 
     }
 
-    private ITmfStateSystem elementToStateSystem(CallGraph callgraph, ICallStackElement element) {
+    private ITmfStateSystem elementToStateSystem(IWeightedTreeProvider<N, E, T> wtProvider, IWeightedTreeSet<N, Object, WeightedTree<N>> callGraph, Object element) {
         // Create an in-memory state system for this element
         IStateHistoryBackend backend = StateHistoryBackendFactory.createInMemoryBackend("org.eclipse.tracecompass.incubator.callgraph.ss", 0L); //$NON-NLS-1$
         ITmfStateSystemBuilder ssb = StateSystemFactory.newStateSystem(backend);
 
         // Add the functions
-        List<AggregatedCallSite> rootFunctions = new ArrayList<>(callgraph.getCallingContextTree(element));
-        rootFunctions.sort(CCT_COMPARATOR);
+        List<WeightedTree<N>> rootFunctions = new ArrayList<>(callGraph.getTreesFor(element));
+        rootFunctions.sort(CCT_COMPARATOR2);
         int quarkFct = ssb.getQuarkAbsoluteAndAdd(FUNCTION_LEVEL);
         Deque<Long> timestampStack = new ArrayDeque<>();
         timestampStack.push(0L);
-        for (AggregatedCallSite rootFunction : rootFunctions) {
-            recursivelyAddChildren(ssb, quarkFct, rootFunction, timestampStack);
+        for (WeightedTree<N> rootFunction : rootFunctions) {
+            recursivelyAddChildren(wtProvider, ssb, quarkFct, rootFunction, timestampStack);
         }
         Long endTime = timestampStack.pop();
         ssb.closeHistory(endTime);
@@ -346,35 +347,37 @@
         return ssb;
     }
 
-    private void recursivelyAddChildren(ITmfStateSystemBuilder ssb, int quarkFct, WeightedTree<ICallStackSymbol> callSite, Deque<Long> timestampStack) {
+    private void recursivelyAddChildren(IWeightedTreeProvider<N, E, T> wtProvider, ITmfStateSystemBuilder ssb, int quarkFct, WeightedTree<N> callSite, Deque<Long> timestampStack) {
         Long lastEnd = timestampStack.peek();
         if (lastEnd == null) {
             return;
         }
-        ssb.pushAttribute(lastEnd, (Object) new CallSiteCustomValue(callSite), quarkFct);
+        ssb.pushAttribute(lastEnd, (Object) new CalleeCustomValue(callSite), quarkFct);
 
         // Push the children to the state system
         timestampStack.push(lastEnd);
-        List<WeightedTree<ICallStackSymbol>> children = new ArrayList<>(callSite.getChildren());
+        List<WeightedTree<N>> children = new ArrayList<>(callSite.getChildren());
         children.sort(CCT_COMPARATOR2);
-        for (WeightedTree<ICallStackSymbol> callsite : children) {
-            recursivelyAddChildren(ssb, quarkFct, callsite, timestampStack);
+        for (WeightedTree<N> callsite : children) {
+            recursivelyAddChildren(wtProvider, ssb, quarkFct, callsite, timestampStack);
         }
         timestampStack.pop();
 
         // Add the extra sites
-        // TODO Support extra sites from weighted tree
-        if (callSite instanceof AggregatedCallSite) {
-            Iterator<AggregatedCallSite> extraChildrenSites = ((AggregatedCallSite) callSite).getExtraChildrenSites().iterator();
-            if (extraChildrenSites.hasNext()) {
-                int quarkExtra = ssb.getQuarkAbsoluteAndAdd(EXTRA_LEVEL);
-                long extraStartTime = lastEnd;
-                while (extraChildrenSites.hasNext()) {
-                    AggregatedCallSite next = extraChildrenSites.next();
-                    ssb.modifyAttribute(extraStartTime, (Object) new CallSiteCustomValue(next), quarkExtra);
-                    extraStartTime += next.getWeight();
-                }
+        List<String> extraDataSets = wtProvider.getExtraDataSets();
+        for (int i = 0; i < extraDataSets.size(); i++) {
+            Collection<WeightedTree<@NonNull N>> extraDataTrees = callSite.getExtraDataTrees(i);
+            if (extraDataTrees.isEmpty()) {
+                continue;
             }
+            String dataSetName = extraDataSets.get(i);
+            int quarkExtra = ssb.getQuarkAbsoluteAndAdd(dataSetName);
+            long extraStartTime = lastEnd;
+            for (WeightedTree<@NonNull N> extraTree : extraDataTrees) {
+                ssb.modifyAttribute(extraStartTime, (Object) new CalleeCustomValue(extraTree), quarkExtra);
+                extraStartTime += extraTree.getWeight();
+            }
+
         }
 
         long currentEnd = timestampStack.pop() + callSite.getWeight();
@@ -384,18 +387,18 @@
 
     /**
      * Build the entry list for one thread
-     *
-     * @param element
-     *            The node of the aggregation tree
      */
-    private void buildChildrenEntries(ICallStackElement element, CallGraph callgraph, List<FlameChartEntryModel.Builder> builder, FlameChartEntryModel.Builder parent) {
+    private void buildChildrenEntries(Object element, IWeightedTreeProvider<N, E, T> wtProvider, IWeightedTreeSet<N, Object, WeightedTree<N>> callGraph, List<FlameChartEntryModel.Builder> builder, FlameChartEntryModel.Builder parent) {
         // Add the entry
-        FlameChartEntryModel.Builder entry = new FlameChartEntryModel.Builder(ENTRY_ID.getAndIncrement(), parent.getId(), element.getName(), 0, FlameChartEntryModel.EntryType.LEVEL, -1);
+        FlameChartEntryModel.Builder entry = new FlameChartEntryModel.Builder(ENTRY_ID.getAndIncrement(),
+                parent.getId(), (element instanceof ITree) ? String.valueOf(((ITree) element).getName()) : String.valueOf(element), 0, FlameChartEntryModel.EntryType.LEVEL, -1);
         builder.add(entry);
 
-        // Create the children entries
-        for (ICallStackElement child : element.getChildrenElements()) {
-            buildChildrenEntries(child, callgraph, builder, entry);
+        // Create the hierarchy of children entries if available
+        if (element instanceof ITree) {
+            for (ITree child : ((ITree) element).getChildren()) {
+                buildChildrenEntries(child, wtProvider, callGraph, builder, entry);
+            }
         }
 
         // Update endtime with the children and add them to builder
@@ -407,8 +410,9 @@
         }
         entry.setEndTime(endTime);
 
+        List<WeightedTree<N>> rootTrees = new ArrayList<>(callGraph.getTreesFor(element));
         // Create the function callsite entries
-        if (!(element.isLeaf())) {
+        if (rootTrees.isEmpty()) {
             return;
         }
 
@@ -416,10 +420,10 @@
         timestampStack.push(0L);
 
         // Get the state system to represent this callgraph
-        ITmfStateSystem ss = elementToStateSystem(callgraph, element);
+        ITmfStateSystem ss = elementToStateSystem(wtProvider, callGraph, element);
         entry.setEndTime(ss.getCurrentEndTime());
 
-        // Add items for the function entries
+        // Add entry items for the main weighted tree levels
         int quark = ss.optQuarkAbsolute(FUNCTION_LEVEL);
         if (quark == ITmfStateSystem.INVALID_ATTRIBUTE) {
             return;
@@ -430,18 +434,22 @@
             child.setEndTime(ss.getCurrentEndTime());
             builder.add(child);
             i++;
-            fCgEntries.put(child.getId(), new CallGraphEntry(element, ss, subQuark));
+            fCgEntries.put(child.getId(), new WeightedTreeEntry(ss, subQuark));
         }
 
         // Add items for the extra entries
-        quark = ss.optQuarkAbsolute(EXTRA_LEVEL);
-        if (quark == ITmfStateSystem.INVALID_ATTRIBUTE) {
-            return;
+        List<String> extraDataSets = wtProvider.getExtraDataSets();
+        for (int set = 0; set < extraDataSets.size(); set++) {
+            String dataSetName = extraDataSets.get(set);
+            quark = ss.optQuarkAbsolute(dataSetName);
+            if (quark == ITmfStateSystem.INVALID_ATTRIBUTE) {
+                continue;
+            }
+            FlameChartEntryModel.Builder child = new FlameChartEntryModel.Builder(ENTRY_ID.getAndIncrement(), entry.getId(), dataSetName, 0, EntryType.KERNEL, -1);
+            child.setEndTime(ss.getCurrentEndTime());
+            builder.add(child);
+            fCgEntries.put(child.getId(), new WeightedTreeEntry(ss, quark));
         }
-        FlameChartEntryModel.Builder child = new FlameChartEntryModel.Builder(ENTRY_ID.getAndIncrement(), entry.getId(), Objects.requireNonNull(Messages.FlameGraph_KernelStatusTitle), 0, EntryType.KERNEL, -1);
-        child.setEndTime(ss.getCurrentEndTime());
-        builder.add(child);
-        fCgEntries.put(child.getId(), new CallGraphEntry(element, ss, quark));
 
         return;
     }
@@ -462,13 +470,14 @@
             // No entry selected, assume all
             selected = fEntries.keySet();
         }
-        List<CallGraphEntry> selectedEntries = new ArrayList<>();
-        Multimap<Pair<ITmfStateSystem, ICallStackElement>, Pair<Integer, Long>> requested = HashMultimap.create();
+        List<WeightedTreeEntry> selectedEntries = new ArrayList<>();
+        Multimap<WeightedTreeEntry, Pair<Integer, Long>> requested = HashMultimap.create();
+
         for (Long id : selected) {
-            CallGraphEntry entry = fCgEntries.get(id);
+            WeightedTreeEntry entry = fCgEntries.get(id);
             if (entry != null) {
                 selectedEntries.add(entry);
-                requested.put(new Pair<>(entry.fSs, entry.fElement), new Pair<>(entry.fQuark, id));
+                requested.put(entry, new Pair<>(entry.fQuark, id));
             }
         }
 
@@ -485,12 +494,12 @@
 
         // For each element and callgraph, get the states
         try {
-            for (Pair<ITmfStateSystem, ICallStackElement> element : requested.keySet()) {
+            for (WeightedTreeEntry element : requested.keySet()) {
                 if (subMonitor.isCanceled()) {
                     return new TmfModelResponse<>(null, ITmfResponse.Status.CANCELLED, CommonStatusMessage.TASK_CANCELLED);
                 }
-                Collection<Pair<Integer, Long>> depths = requested.get(element);
-                rowModels.addAll(getStatesForElement(times, predicates, subMonitor, element.getFirst(), depths));
+                Collection<Pair<Integer, Long>> depths = Objects.requireNonNull(requested.get(element));
+                rowModels.addAll(getStatesForElement(times, predicates, subMonitor, element.fSs, depths));
             }
         } catch (StateSystemDisposedException e) {
             // Nothing to do
@@ -543,19 +552,20 @@
     }
 
     private ITimeGraphState createTimeGraphState(ITmfStateInterval interval, long ssEndTime) {
+        IWeightedTreeProvider<N, E, T> wtProvider = fWtProvider;
         long startTime = interval.getStartTime();
         long duration = interval.getEndTime() - startTime + (ssEndTime == interval.getEndTime() ? 0 : 1);
         Object valueObject = interval.getValue();
-        if (valueObject instanceof CallSiteCustomValue) {
+        if (valueObject instanceof CalleeCustomValue) {
 
-            WeightedTree<ICallStackSymbol> callsite = ((CallSiteCustomValue) valueObject).fCallSite;
-            ICallStackSymbol value = callsite.getObject();
-            String resolved = value.resolve(fSymbolProviders);
-            // FIXME there shouldn't be any direct reference to AggregatedThreadStatus. Passing from CallGraph to WeightedTree should fix this
+            WeightedTree<N> callsite = ((CalleeCustomValue) valueObject).fCallSite;
+            String displayString = wtProvider.toDisplayString((T) callsite);
+            // FIXME there shouldn't be any direct reference to
+            // AggregatedThreadStatus. Moving styling to core should fix this
             if (callsite instanceof AggregatedThreadStatus) {
-                return new TimeGraphState(startTime, duration, ((AggregatedThreadStatus) callsite).getProcessStatus().getStateValue().unboxInt(), resolved);
+                return new TimeGraphState(startTime, duration, ((AggregatedThreadStatus) callsite).getProcessStatus().getStateValue().unboxInt(), displayString);
             }
-            return new TimeGraphState(startTime, duration, value.hashCode(), resolved);
+            return new TimeGraphState(startTime, duration, callsite.getObject().hashCode(), displayString);
         }
         return new TimeGraphState(startTime, duration, Integer.MIN_VALUE);
     }
@@ -577,11 +587,11 @@
         }
         Long time = times.get(0);
         Long item = items.get(0);
-        CallGraphEntry callGraphEntry = fCgEntries.get(item);
+        WeightedTreeEntry callGraphEntry = fCgEntries.get(item);
         if (callGraphEntry == null) {
             return new TmfModelResponse<>(Collections.emptyMap(), ITmfResponse.Status.COMPLETED, CommonStatusMessage.COMPLETED);
         }
-        WeightedTree<ICallStackSymbol> callSite = findCallSite(callGraphEntry, time);
+        WeightedTree<@NonNull N> callSite = findCallSite(callGraphEntry, time);
         if (callSite != null) {
             return new TmfModelResponse<>(getTooltip(callSite), ITmfResponse.Status.COMPLETED, CommonStatusMessage.COMPLETED);
         }
@@ -589,38 +599,67 @@
         return new TmfModelResponse<>(Collections.emptyMap(), ITmfResponse.Status.COMPLETED, CommonStatusMessage.COMPLETED);
     }
 
-    private Map<String, String> getTooltip(WeightedTree<ICallStackSymbol> tree) {
-        if (!(tree instanceof AggregatedCallSite)) {
-            return Collections.emptyMap();
-        }
-        AggregatedCallSite callSite = (AggregatedCallSite) tree;
+    private Map<String, String> getTooltip(WeightedTree<@NonNull N> callSite) {
         ImmutableMap.Builder<String, String> builder = new ImmutableMap.Builder<>();
-        builder.put(Messages.FlameGraph_Symbol, callSite.getObject().resolve(fSymbolProviders));
-        for (Entry<String, IStatistics<?>> entry : callSite.getStatistics().entrySet()) {
-            String statType = String.valueOf(entry.getKey());
-            IStatistics<?> stats = entry.getValue();
-            if (stats.getMax() != IHostModel.TIME_UNKNOWN) {
-                builder.put(statType, ""); //$NON-NLS-1$
-                String lowerType = statType.toLowerCase();
-                builder.put("\t" + Messages.FlameGraph_Total + ' ' + lowerType, FORMATTER.format(stats.getTotal())); //$NON-NLS-1$
-                builder.put("\t" + Messages.FlameGraph_Average + ' ' + lowerType, FORMATTER.format(stats.getMean())); //$NON-NLS-1$
-                builder.put("\t" + Messages.FlameGraph_Max + ' ' + lowerType, FORMATTER.format(stats.getMax())); //$NON-NLS-1$
-                builder.put("\t" + Messages.FlameGraph_Min + ' ' + lowerType, FORMATTER.format(stats.getMin())); //$NON-NLS-1$
-                builder.put("\t" + Messages.FlameGraph_Deviation + ' ' + lowerType, FORMATTER.format(stats.getStdDev())); //$NON-NLS-1$
+        // Display the object name first
+        String string = callSite.getObject().toString();
+        String displayString = fWtProvider.toDisplayString((T) callSite);
+        builder.put(Objects.requireNonNull(Messages.FlameGraph_Object), string.equals(displayString) ? displayString : displayString + ' ' + '(' + string + ')');
+        List<MetricType> additionalMetrics = fWtProvider.getAdditionalMetrics();
 
+        // First, display the metrics that do not have statistics to not loose
+        // them in the middle of stats
+        MetricType metric = fWtProvider.getWeightType();
+        if (!metric.hasStatistics()) {
+            builder.put(metric.getTitle(), metric.format(callSite.getWeight()));
+        }
+        for (int i = 0; i < additionalMetrics.size(); i++) {
+            MetricType otherMetric = additionalMetrics.get(i);
+            if (!otherMetric.hasStatistics()) {
+                builder.put(otherMetric.getTitle(), otherMetric.format(fWtProvider.getAdditionalMetric((T) callSite, i)));
             }
         }
+
+        // Then, display the metrics with statistics
+        if (metric.hasStatistics()) {
+            builder.putAll(getMetricWithStatTooltip(metric, callSite, -1));
+        }
+        for (int i = 0; i < additionalMetrics.size(); i++) {
+            MetricType otherMetric = additionalMetrics.get(i);
+            if (otherMetric.hasStatistics()) {
+                builder.putAll(getMetricWithStatTooltip(otherMetric, callSite, i));
+            }
+        }
+
         return builder.build();
     }
 
-    /** Find the callsite at the time requested */
-    private static @Nullable WeightedTree<ICallStackSymbol> findCallSite(CallGraphEntry cgEntry, Long time) {
+    private Map<String, String> getMetricWithStatTooltip(MetricType metric, WeightedTree<@NonNull N> callSite, int metricIndex) {
+        Object metricValue = metricIndex < 0 ? callSite.getWeight() : fWtProvider.getAdditionalMetric((T) callSite, metricIndex);
+        IStatistics<?> statistics = fWtProvider.getStatistics((T) callSite, metricIndex);
+        Map<String, String> map = new LinkedHashMap<>();
+        if (statistics == null || statistics.getMax() == IHostModel.TIME_UNKNOWN) {
+            map.put(metric.getTitle(), metric.format(metricValue));
+        } else {
+            map.put(metric.getTitle(), StringUtils.EMPTY);
+            String lowerTitle = metric.getTitle().toLowerCase();
+            map.put("\t" + Messages.FlameGraph_Total + ' ' + lowerTitle, metric.format(statistics.getTotal())); //$NON-NLS-1$
+            map.put("\t" + Messages.FlameGraph_Average + ' ' + lowerTitle, metric.format(statistics.getMean())); //$NON-NLS-1$
+            map.put("\t" + Messages.FlameGraph_Max + ' ' + lowerTitle, metric.format(statistics.getMax())); //$NON-NLS-1$
+            map.put("\t" + Messages.FlameGraph_Min + ' ' + lowerTitle, metric.format(statistics.getMin())); //$NON-NLS-1$
+            map.put("\t" + Messages.FlameGraph_Deviation + ' ' + lowerTitle, metric.format(statistics.getStdDev())); //$NON-NLS-1$
+        }
+        return map;
+    }
+
+    /** Find the callsite at the time and depth requested */
+    private @Nullable WeightedTree<@NonNull N> findCallSite(WeightedTreeEntry cgEntry, Long time) {
         try {
             ITmfStateInterval interval = cgEntry.fSs.querySingleState(time, cgEntry.fQuark);
 
             Object valueObject = interval.getValue();
-            if (valueObject instanceof CallSiteCustomValue) {
-                return ((CallSiteCustomValue) valueObject).fCallSite;
+            if (valueObject instanceof CalleeCustomValue) {
+                return ((CalleeCustomValue) valueObject).fCallSite;
             }
         } catch (StateSystemDisposedException e) {
             // Nothing to do
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 3bb17cf..4120167 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
@@ -14,7 +14,7 @@
 import java.util.Objects;
 
 import org.eclipse.jdt.annotation.Nullable;
-import org.eclipse.tracecompass.incubator.callstack.core.callgraph.ICallGraphProvider;
+import org.eclipse.tracecompass.incubator.analysis.core.weighted.tree.IWeightedTreeProvider;
 import org.eclipse.tracecompass.internal.tmf.core.model.xy.TmfTreeXYCompositeDataProvider;
 import org.eclipse.tracecompass.tmf.core.analysis.IAnalysisModule;
 import org.eclipse.tracecompass.tmf.core.dataprovider.IDataProviderFactory;
@@ -59,20 +59,21 @@
     }
 
     private static @Nullable ITmfTreeDataProvider<? extends ITmfTreeDataModel> create(ITmfTrace trace, String secondaryId) {
-        // The trace can be an experiment, so we need to know if there are multiple
-        // analysis modules with the same ID
-        Iterable<ICallGraphProvider> modules = TmfTraceUtils.getAnalysisModulesOfClass(trace, ICallGraphProvider.class);
-        Iterable<ICallGraphProvider> filteredModules = Iterables.filter(modules, m -> ((IAnalysisModule) m).getId().equals(secondaryId));
-        Iterator<ICallGraphProvider> iterator = filteredModules.iterator();
+        // 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);
+        Iterable<IWeightedTreeProvider> filteredModules = Iterables.filter(modules, m -> ((IAnalysisModule) m).getId().equals(secondaryId));
+        Iterator<IWeightedTreeProvider> iterator = filteredModules.iterator();
         if (iterator.hasNext()) {
-            ICallGraphProvider module = iterator.next();
+            IWeightedTreeProvider<?, ?, ?> module = iterator.next();
             if (iterator.hasNext()) {
-                // More than one module, must be an experiment, return null so the factory can
+                // More than one module, must be an experiment, return null so
+                // the factory can
                 // try with individual traces
                 return null;
             }
             ((IAnalysisModule) module).schedule();
-            return new FlameGraphDataProvider(trace, module, secondaryId);
+            return new FlameGraphDataProvider<>(trace, module, FlameGraphDataProvider.ID + ':' + secondaryId);
         }
         return null;
     }
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/flamegraph/Messages.java b/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/flamegraph/Messages.java
index f7a1299..fdaf465 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/flamegraph/Messages.java
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/flamegraph/Messages.java
@@ -23,7 +23,7 @@
     /** Title of the data provider */
     public static String FlameGraphDataProvider_Title;
     /** Label for the symbol */
-    public static String FlameGraph_Symbol;
+    public static String FlameGraph_Object;
     /** Label for the CPU times */
     public static String FlameGraph_Total;
     /** Label for total CPU time */
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/flamegraph/messages.properties b/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/flamegraph/messages.properties
index 46edba7..a482e4a 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/flamegraph/messages.properties
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/flamegraph/messages.properties
@@ -8,7 +8,7 @@
 ###############################################################################
 
 FlameGraphDataProvider_Title=FlameGraph
-FlameGraph_Symbol=Symbol
+FlameGraph_Object=Object
 FlameGraph_Total=Total
 FlameGraph_Average=Mean
 FlameGraph_Min=Minimum
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/instrumented/InstrumentedCallStackElement.java b/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/instrumented/InstrumentedCallStackElement.java
index f844772..33fcf66 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/instrumented/InstrumentedCallStackElement.java
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/instrumented/InstrumentedCallStackElement.java
@@ -21,9 +21,9 @@
 import org.apache.commons.lang3.StringUtils;
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.incubator.analysis.core.weighted.tree.IWeightedTreeGroupDescriptor;
 import org.eclipse.tracecompass.incubator.callstack.core.base.CallStackElement;
 import org.eclipse.tracecompass.incubator.callstack.core.base.ICallStackElement;
-import org.eclipse.tracecompass.incubator.callstack.core.base.ICallStackGroupDescriptor;
 import org.eclipse.tracecompass.incubator.callstack.core.flamechart.CallStack;
 import org.eclipse.tracecompass.incubator.callstack.core.instrumented.statesystem.CallStackHostUtils.IHostIdProvider;
 import org.eclipse.tracecompass.incubator.callstack.core.instrumented.statesystem.CallStackHostUtils.IHostIdResolver;
@@ -73,8 +73,8 @@
      *            element
      */
     public InstrumentedCallStackElement(IHostIdResolver hostResolver, ITmfStateSystem stateSystem, Integer quark,
-            InstrumentedGroupDescriptor group,
-            @Nullable InstrumentedGroupDescriptor nextGroup,
+            IWeightedTreeGroupDescriptor group,
+            @Nullable IWeightedTreeGroupDescriptor nextGroup,
             @Nullable IThreadIdResolver threadIdResolver,
             @Nullable InstrumentedCallStackElement parent) {
         super(INSTRUMENTED, group, nextGroup, parent);
@@ -87,7 +87,7 @@
     @Override
     public Collection<ICallStackElement> getChildrenElements() {
         // Get the elements from the next group in the hierarchy
-        @Nullable ICallStackGroupDescriptor nextGroup = getNextGroup();
+        @Nullable IWeightedTreeGroupDescriptor nextGroup = getNextGroup();
         if (!(nextGroup instanceof InstrumentedGroupDescriptor)) {
             return Collections.emptyList();
         }
@@ -266,4 +266,9 @@
         return subAttributes;
     }
 
+    @Override
+    public InstrumentedCallStackElement copyElement() {
+        return new InstrumentedCallStackElement(fHostResolver, fStateSystem, fQuark, super.getGroup(), null, fThreadIdResolver, null);
+    }
+
 }
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/instrumented/callgraph/AggregatedCalledFunction.java b/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/instrumented/callgraph/AggregatedCalledFunction.java
index 4e2571f..f3e1499 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/instrumented/callgraph/AggregatedCalledFunction.java
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/instrumented/callgraph/AggregatedCalledFunction.java
@@ -10,6 +10,7 @@
 package org.eclipse.tracecompass.incubator.internal.callstack.core.instrumented.callgraph;
 
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -304,8 +305,11 @@
     }
 
     @Override
-    public Collection<AggregatedCallSite> getExtraChildrenSites() {
-        return ImmutableList.copyOf(fProcessStatuses.values());
+    public @NonNull Collection<@NonNull WeightedTree<@NonNull ICallStackSymbol>> getExtraDataTrees(int index) {
+        if (index == 0) {
+            return ImmutableList.copyOf(fProcessStatuses.values());
+        }
+        return Collections.emptyList();
     }
 
     @Override
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/instrumented/callgraph/CallGraphAnalysis.java b/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/instrumented/callgraph/CallGraphAnalysis.java
index b46e10d..41cca36 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/instrumented/callgraph/CallGraphAnalysis.java
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/instrumented/callgraph/CallGraphAnalysis.java
@@ -26,8 +26,8 @@
 import org.eclipse.tracecompass.incubator.analysis.core.concepts.ProcessStatusInterval;
 import org.eclipse.tracecompass.incubator.analysis.core.model.IHostModel;
 import org.eclipse.tracecompass.incubator.analysis.core.model.ModelManager;
+import org.eclipse.tracecompass.incubator.analysis.core.weighted.tree.IWeightedTreeGroupDescriptor;
 import org.eclipse.tracecompass.incubator.callstack.core.base.ICallStackElement;
-import org.eclipse.tracecompass.incubator.callstack.core.base.ICallStackGroupDescriptor;
 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.flamechart.CallStack;
@@ -323,8 +323,8 @@
     }
 
     @Override
-    public Collection<ICallStackGroupDescriptor> getGroupDescriptors() {
-        List<ICallStackGroupDescriptor> descriptors = new ArrayList<>();
+    public Collection<IWeightedTreeGroupDescriptor> getGroupDescriptors() {
+        List<IWeightedTreeGroupDescriptor> descriptors = new ArrayList<>();
         for (IAnalysisModule module : getDependentAnalyses()) {
             if (module instanceof IFlameChartProvider) {
                 CallStackSeries serie = ((IFlameChartProvider) module).getCallStackSeries();
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/xml/callstack/CallstackXmlAnalysis.java b/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/xml/callstack/CallstackXmlAnalysis.java
index 81b3a3f..cd64275 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/xml/callstack/CallstackXmlAnalysis.java
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/xml/callstack/CallstackXmlAnalysis.java
@@ -23,7 +23,7 @@
 import org.eclipse.jdt.annotation.Nullable;
 import org.eclipse.tracecompass.analysis.timing.core.segmentstore.IAnalysisProgressListener;
 import org.eclipse.tracecompass.incubator.analysis.core.concepts.AggregatedCallSite;
-import org.eclipse.tracecompass.incubator.callstack.core.base.ICallStackGroupDescriptor;
+import org.eclipse.tracecompass.incubator.analysis.core.weighted.tree.IWeightedTreeGroupDescriptor;
 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.callgraph.SymbolAspect;
@@ -287,7 +287,7 @@
     }
 
     @Override
-    public Collection<ICallStackGroupDescriptor> getGroupDescriptors() {
+    public Collection<IWeightedTreeGroupDescriptor> getGroupDescriptors() {
         fCallGraph.schedule();
         fCallGraph.waitForCompletion();
         return fCallGraph.getGroupDescriptors();
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.ui.swtbot.tests/META-INF/MANIFEST.MF b/callstack/org.eclipse.tracecompass.incubator.callstack.ui.swtbot.tests/META-INF/MANIFEST.MF
index fa1cb54..36531e6 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.ui.swtbot.tests/META-INF/MANIFEST.MF
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.ui.swtbot.tests/META-INF/MANIFEST.MF
@@ -25,8 +25,10 @@
  org.eclipse.tracecompass.tmf.core,
  org.eclipse.tracecompass.segmentstore.core,
  org.eclipse.tracecompass.incubator.callstack.core,
- 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
 Import-Package: org.apache.log4j,
- org.swtchart
+ org.swtchart,
+ com.google.common.collect
 Export-Package: org.eclipse.tracecompass.incubator.callstack.core.ui.swtbot.tests.flamegraph
 Automatic-Module-Name: org.eclipse.tracecompass.incubator.callstack.ui.swtbot.tests
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 a1e912b..f235c2d 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
@@ -60,7 +60,7 @@
 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.callstack.core.base.ICallStackGroupDescriptor;
+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;
@@ -141,7 +141,7 @@
     private Action fConfigureSymbolsAction;
 
     private final Multimap<ITmfTrace, ISymbolProvider> fSymbolProviders = LinkedHashMultimap.create();
-    private @Nullable ICallStackGroupDescriptor fGroupBy = null;
+    private @Nullable IWeightedTreeGroupDescriptor fGroupBy = null;
     /**
      * A plain old semaphore is used since different threads will be competing
      * for the same resource.
@@ -333,7 +333,7 @@
                         fDirty.incrementAndGet();
                         fLock.release();
                         Set<CallGraph> callgraphs = new HashSet<>();
-                        ICallStackGroupDescriptor group = fGroupBy;
+                        IWeightedTreeGroupDescriptor group = fGroupBy;
                         for (ICallGraphProvider provider : callGraphProviders) {
                             if (provider instanceof IAnalysisModule) {
                                 ((IAnalysisModule) provider).waitForCompletion(monitor);
@@ -558,9 +558,9 @@
                  // Add the all group element
                     Action allGroupAction = createActionForGroup(provider, AllGroupDescriptor.getInstance());
                     new ActionContributionItem(allGroupAction).fill(menu, -1);
-                    Collection<ICallStackGroupDescriptor> series = provider.getGroupDescriptors();
+                    Collection<IWeightedTreeGroupDescriptor> series = provider.getGroupDescriptors();
                     series.forEach(group -> {
-                        ICallStackGroupDescriptor subGroup = group;
+                        IWeightedTreeGroupDescriptor subGroup = group;
                         do {
                             Action groupAction = createActionForGroup(provider, subGroup);
                             new ActionContributionItem(groupAction).fill(menu, -1);
@@ -579,7 +579,7 @@
         return fAggregateByAction;
     }
 
-    private Action createActionForGroup(ICallGraphProvider provider, ICallStackGroupDescriptor descriptor) {
+    private Action createActionForGroup(ICallGraphProvider provider, IWeightedTreeGroupDescriptor descriptor) {
         return new Action(descriptor.getName(), IAction.AS_RADIO_BUTTON) {
             @Override
             public void run() {
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.perf.profiling.core/src/org/eclipse/tracecompass/incubator/internal/perf/profiling/core/callgraph/PerfCallchainAnalysisModule.java b/tracetypes/org.eclipse.tracecompass.incubator.perf.profiling.core/src/org/eclipse/tracecompass/incubator/internal/perf/profiling/core/callgraph/PerfCallchainAnalysisModule.java
index ee6809c..6a77bbf 100644
--- a/tracetypes/org.eclipse.tracecompass.incubator.perf.profiling.core/src/org/eclipse/tracecompass/incubator/internal/perf/profiling/core/callgraph/PerfCallchainAnalysisModule.java
+++ b/tracetypes/org.eclipse.tracecompass.incubator.perf.profiling.core/src/org/eclipse/tracecompass/incubator/internal/perf/profiling/core/callgraph/PerfCallchainAnalysisModule.java
@@ -20,10 +20,10 @@
 import org.eclipse.jdt.annotation.Nullable;
 import org.eclipse.tracecompass.incubator.analysis.core.concepts.AggregatedCallSite;
 import org.eclipse.tracecompass.incubator.analysis.core.concepts.ISamplingDataProvider;
+import org.eclipse.tracecompass.incubator.analysis.core.weighted.tree.IWeightedTreeGroupDescriptor;
 import org.eclipse.tracecompass.incubator.callstack.core.base.CallStackElement;
 import org.eclipse.tracecompass.incubator.callstack.core.base.CallStackGroupDescriptor;
 import org.eclipse.tracecompass.incubator.callstack.core.base.ICallStackElement;
-import org.eclipse.tracecompass.incubator.callstack.core.base.ICallStackGroupDescriptor;
 import org.eclipse.tracecompass.incubator.callstack.core.sampled.callgraph.ProfilingCallGraphAnalysisModule;
 import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
 import org.eclipse.tracecompass.tmf.core.event.ITmfEventField;
@@ -140,7 +140,7 @@
     }
 
     @Override
-    public Collection<ICallStackGroupDescriptor> getGroupDescriptors() {
+    public Collection<IWeightedTreeGroupDescriptor> getGroupDescriptors() {
         return ImmutableList.of(fProcessDescriptor);
     }