callstack: Save flame graph data to temporary state system
This saves the callgraph data into an in-memory state system for each
element that will be easier to query for the purpose of the view
[Changed] Cache flame graph data to temporary state system
Change-Id: I02971b59dd4aad75e93bb4c3aa5106d6a8546ef3
Signed-off-by: Geneviève Bastien <gbastien+lttng@versatic.net>
Reviewed-on: https://git.eclipse.org/r/152353
Tested-by: CI Bot
Reviewed-by: Bernd Hufmann <bernd.hufmann@ericsson.com>
Tested-by: Bernd Hufmann <bernd.hufmann@ericsson.com>
Reviewed-by: Matthew Khouzam <matthew.khouzam@ericsson.com>
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 9ad0b01..f9b55d8 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
@@ -145,7 +145,7 @@
assertRows(provider, idsToNames, builder.build(), filePrefix, "All");
// Test getting only the first and last states
- assertRows(provider, idsToNames, ImmutableList.of(0L, maxDuration), filePrefix, "2Times");
+ assertRows(provider, idsToNames, ImmutableList.of(0L, maxDuration - 1), filePrefix, "2Times");
// Test getting the states for the last half of the flamegraph
builder = ImmutableList.builder();
@@ -250,7 +250,7 @@
assertEquals(descriptor + ": value at position " + i, strValue.equals("-") ? Integer.MIN_VALUE : Objects.hash(strValue), state.getValue());
assertEquals(descriptor + ": label at position " + i, stringStates[i * 4 + 3], String.valueOf(state.getLabel()));
}
- assertTrue(descriptor + " no extra state", states.size() == stringStates.length / 4);
+ assertEquals(descriptor + " no extra state", stringStates.length / 4, states.size());
}
}
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/testfiles/dp/expectedFgRowFull2Times b/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/testfiles/dp/expectedFgRowFull2Times
index 1667cd4..4caf7aa 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/testfiles/dp/expectedFgRowFull2Times
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/testfiles/dp/expectedFgRowFull2Times
@@ -5,7 +5,7 @@
function,1,level,3:0,1,op3,op3
function,0,level,6:0,19,op1,op1
function,1,level,6:0,3,op2,op2,16,3,-,null
-function,2,level,6:0,1,op3,op3,16,3,-,null
+function,2,level,6:0,1,op3,op3,5,14,-,null
function,0,level,7:0,19,op5,op5
function,1,level,7:0,12,op2,op2,12,7,-,null
-function,2,level,7:0,1,op3,op3,12,7,-,null
+function,2,level,7:0,1,op3,op3,1,18,-,null
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/testfiles/dp/expectedFgRowFullAll b/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/testfiles/dp/expectedFgRowFullAll
index bbfa892..83783f3 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/testfiles/dp/expectedFgRowFullAll
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/testfiles/dp/expectedFgRowFullAll
@@ -1,11 +1,11 @@
function,0,level,2:0,8,op4,op4,8,9,op1,op1
function,1,level,2:0,8,-,null,8,4,op2,op2,12,5,-,null
-function,2,level,2:0,8,-,null,8,1,op3,op3,9,3,-,null,12,5,-,null
+function,2,level,2:0,8,-,null,8,1,op3,op3,9,8,-,null
function,0,level,3:0,17,op2,op2
function,1,level,3:0,1,op3,op3,1,6,op2,op2,7,10,-,null
function,0,level,6:0,19,op1,op1
function,1,level,6:0,3,op2,op2,3,5,op3,op3,8,8,op4,op4,16,3,-,null
-function,2,level,6:0,1,op3,op3,1,2,-,null,3,2,op1,op1,5,3,-,null,8,8,-,null,16,3,-,null
+function,2,level,6:0,1,op3,op3,1,2,-,null,3,2,op1,op1,5,14,-,null
function,0,level,7:0,19,op5,op5
function,1,level,7:0,12,op2,op2,12,7,-,null
-function,2,level,7:0,1,op3,op3,1,11,-,null,12,7,-,null
+function,2,level,7:0,1,op3,op3,1,18,-,null
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/testfiles/dp/expectedFgRowFullZoom b/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/testfiles/dp/expectedFgRowFullZoom
index 17bc171..6db4f28 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/testfiles/dp/expectedFgRowFullZoom
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/testfiles/dp/expectedFgRowFullZoom
@@ -1,11 +1,11 @@
function,0,level,2:8,9,op1,op1
function,1,level,2:8,4,op2,op2,12,5,-,null
-function,2,level,2:8,1,op3,op3,9,3,-,null,12,5,-,null
+function,2,level,2:9,8,-,null
function,0,level,3:0,17,op2,op2
function,1,level,3:7,10,-,null
function,0,level,6:0,19,op1,op1
function,1,level,6:8,8,op4,op4,16,3,-,null
-function,2,level,6:8,8,-,null,16,3,-,null
+function,2,level,6:5,14,-,null
function,0,level,7:0,19,op5,op5
function,1,level,7:0,12,op2,op2,12,7,-,null
-function,2,level,7:1,11,-,null,12,7,-,null
+function,2,level,7:1,18,-,null
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/testfiles/dp/expectedFgRowOne2Times b/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/testfiles/dp/expectedFgRowOne2Times
index f9b175c..07c8b23 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/testfiles/dp/expectedFgRowOne2Times
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/testfiles/dp/expectedFgRowOne2Times
@@ -1,3 +1,3 @@
function,0,level,All:0,8,op4,op4,44,28,op1,op1
function,1,level,All:0,8,-,null,64,8,-,null
-function,2,level,All:0,8,-,null,64,8,-,null
\ No newline at end of file
+function,2,level,All:0,25,-,null,51,21,-,null
\ No newline at end of file
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/testfiles/dp/expectedFgRowOneAll b/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/testfiles/dp/expectedFgRowOneAll
index 6e97b45..9c68cb3 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/testfiles/dp/expectedFgRowOneAll
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/testfiles/dp/expectedFgRowOneAll
@@ -1,3 +1,3 @@
function,0,level,All:0,8,op4,op4,8,17,op2,op2,25,19,op5,op5,44,28,op1,op1
function,1,level,All:0,8,-,null,8,1,op3,op3,9,6,op2,op2,15,10,-,null,25,12,op2,op2,37,7,-,null,44,5,op3,op3,49,7,op2,op2,56,8,op4,op4,64,8,-,null
-function,2,level,All:0,8,-,null,8,17,-,null,25,1,op3,op3,26,11,-,null,37,7,-,null,44,2,op1,op1,46,3,-,null,49,2,op3,op3,51,5,-,null,56,8,-,null,64,8,-,null
\ No newline at end of file
+function,2,level,All:0,25,-,null,25,1,op3,op3,26,18,-,null,44,2,op1,op1,46,3,-,null,49,2,op3,op3,51,21,-,null
\ No newline at end of file
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/testfiles/dp/expectedFgRowOneZoom b/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/testfiles/dp/expectedFgRowOneZoom
index 4ac2502..e0f8c8e 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/testfiles/dp/expectedFgRowOneZoom
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/testfiles/dp/expectedFgRowOneZoom
@@ -1,3 +1,3 @@
function,0,level,All:25,19,op5,op5,44,28,op1,op1
function,1,level,All:25,12,op2,op2,37,7,-,null,44,5,op3,op3,49,7,op2,op2,56,8,op4,op4,64,8,-,null
-function,2,level,All:26,11,-,null,37,7,-,null,44,2,op1,op1,46,3,-,null,49,2,op3,op3,51,5,-,null,56,8,-,null,64,8,-,null
\ No newline at end of file
+function,2,level,All:26,18,-,null,44,2,op1,op1,46,3,-,null,49,2,op3,op3,51,21,-,null
\ No newline at end of file
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/testfiles/dp/expectedFgRowProcess2Times b/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/testfiles/dp/expectedFgRowProcess2Times
index 6267e99..18a97cc 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/testfiles/dp/expectedFgRowProcess2Times
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/testfiles/dp/expectedFgRowProcess2Times
@@ -3,4 +3,4 @@
function,2,level,1:0,8,-,null
function,0,level,5:0,19,op1,op1,19,19,op5,op5
function,1,level,5:0,3,op2,op2,31,7,-,null
-function,2,level,5:0,1,op3,op3,31,7,-,null
\ No newline at end of file
+function,2,level,5:0,1,op3,op3,20,18,-,null
\ No newline at end of file
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/testfiles/dp/expectedFgRowProcessAll b/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/testfiles/dp/expectedFgRowProcessAll
index 51a260c..c482f6b 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/testfiles/dp/expectedFgRowProcessAll
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/testfiles/dp/expectedFgRowProcessAll
@@ -1,6 +1,6 @@
function,0,level,1:0,8,op4,op4,8,9,op1,op1,17,17,op2,op2
function,1,level,1:0,8,-,null,8,4,op2,op2,12,5,-,null,17,1,op3,op3,18,6,op2,op2,24,10,-,null
-function,2,level,1:0,8,-,null,8,1,op3,op3,9,3,-,null,12,5,-,null,17,17,-,null
+function,2,level,1:0,8,-,null,8,1,op3,op3,9,25,-,null
function,0,level,5:0,19,op1,op1,19,19,op5,op5
function,1,level,5:0,3,op2,op2,3,5,op3,op3,8,8,op4,op4,16,3,-,null,19,12,op2,op2,31,7,-,null
-function,2,level,5:0,1,op3,op3,1,2,-,null,3,2,op1,op1,5,3,-,null,8,8,-,null,16,3,-,null,19,1,op3,op3,20,11,-,null,31,7,-,null
\ No newline at end of file
+function,2,level,5:0,1,op3,op3,1,2,-,null,3,2,op1,op1,5,14,-,null,19,1,op3,op3,20,18,-,null
\ No newline at end of file
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/testfiles/dp/expectedFgRowProcessZoom b/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/testfiles/dp/expectedFgRowProcessZoom
index bb2aa59..190327c 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/testfiles/dp/expectedFgRowProcessZoom
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/testfiles/dp/expectedFgRowProcessZoom
@@ -1,6 +1,6 @@
function,0,level,1:17,17,op2,op2
function,1,level,1:18,6,op2,op2,24,10,-,null
-function,2,level,1:17,17,-,null
-function,0,level,5:0,19,op1,op1,19,19,op5,op5
-function,1,level,5:16,3,-,null,19,12,op2,op2,31,7,-,null
-function,2,level,5:16,3,-,null,19,1,op3,op3,20,11,-,null,31,7,-,null
\ No newline at end of file
+function,2,level,1:9,25,-,null
+function,0,level,5:19,19,op5,op5
+function,1,level,5:19,12,op2,op2,31,7,-,null
+function,2,level,5:19,1,op3,op3,20,18,-,null
\ No newline at end of file
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/testfiles/dp/expectedFgRowSelection2Times b/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/testfiles/dp/expectedFgRowSelection2Times
index 6a7fe5f..cc29a44 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/testfiles/dp/expectedFgRowSelection2Times
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/testfiles/dp/expectedFgRowSelection2Times
@@ -4,7 +4,7 @@
function,1,level,3:0,1,op3,op3,7,3,-,null
function,0,level,6:0,10,op1,op1
function,1,level,6:0,2,op3,op3,8,2,-,null
-function,2,level,6:0,1,op1,op1,8,2,-,null
+function,2,level,6:0,1,op1,op1,3,7,-,null
function,0,level,7:0,10,op5,op5
function,1,level,7:0,5,op2,op2,5,5,-,null
-function,2,level,7:0,1,op3,op3,5,5,-,null
+function,2,level,7:0,1,op3,op3,1,9,-,null
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/testfiles/dp/expectedFgRowSelectionAll b/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/testfiles/dp/expectedFgRowSelectionAll
index 6b3e73f..4fe7762 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/testfiles/dp/expectedFgRowSelectionAll
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/testfiles/dp/expectedFgRowSelectionAll
@@ -4,7 +4,7 @@
function,1,level,3:0,1,op3,op3,1,6,op2,op2,7,3,-,null
function,0,level,6:0,10,op1,op1
function,1,level,6:0,2,op3,op3,2,3,op2,op2,5,3,op4,op4,8,2,-,null
-function,2,level,6:0,1,op1,op1,1,1,-,null,2,1,op3,op3,3,2,-,null,5,3,-,null,8,2,-,null
+function,2,level,6:0,1,op1,op1,1,1,-,null,2,1,op3,op3,3,7,-,null
function,0,level,7:0,10,op5,op5
function,1,level,7:0,5,op2,op2,5,5,-,null
-function,2,level,7:0,1,op3,op3,1,4,-,null,5,5,-,null
+function,2,level,7:0,1,op3,op3,1,9,-,null
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/testfiles/dp/expectedFgRowSelectionZoom b/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/testfiles/dp/expectedFgRowSelectionZoom
index 63b426a..4ad99e5 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/testfiles/dp/expectedFgRowSelectionZoom
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/testfiles/dp/expectedFgRowSelectionZoom
@@ -1,10 +1,10 @@
function,0,level,2:3,5,op1,op1
-function,1,level,2:3,2,op2,op2,5,3,-,null
+function,1,level,2:5,3,-,null
function,0,level,3:0,10,op2,op2
function,1,level,3:1,6,op2,op2,7,3,-,null
function,0,level,6:0,10,op1,op1
-function,1,level,6:2,3,op2,op2,5,3,op4,op4,8,2,-,null
-function,2,level,6:3,2,-,null,5,3,-,null,8,2,-,null
+function,1,level,6:5,3,op4,op4,8,2,-,null
+function,2,level,6:3,7,-,null
function,0,level,7:0,10,op5,op5
-function,1,level,7:0,5,op2,op2,5,5,-,null
-function,2,level,7:1,4,-,null,5,5,-,null
+function,1,level,7:5,5,-,null
+function,2,level,7:1,9,-,null
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/testfiles/dp/expectedFgTreeProcess b/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/testfiles/dp/expectedFgTreeProcess
index d71b3be..5d60eb7 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/testfiles/dp/expectedFgTreeProcess
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/testfiles/dp/expectedFgTreeProcess
@@ -2,7 +2,7 @@
level,1,0,34,trace,callstack.xml
function,0,0,34,level,1
function,1,0,34,level,1
-function,2,0,17,level,1
+function,2,0,34,level,1
level,5,0,38,trace,callstack.xml
function,0,0,38,level,5
function,1,0,38,level,5
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 f6f4315..82e0098 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
@@ -21,6 +21,7 @@
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;
import java.util.concurrent.locks.ReentrantReadWriteLock;
@@ -38,10 +39,12 @@
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.IWeightedTreeProvider;
+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;
@@ -51,8 +54,17 @@
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;
+import org.eclipse.tracecompass.internal.provisional.statesystem.core.statevalue.CustomStateValue;
import org.eclipse.tracecompass.internal.tmf.core.model.AbstractTmfTraceDataProvider;
import org.eclipse.tracecompass.internal.tmf.core.model.filters.FetchParametersUtils;
+import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem;
+import org.eclipse.tracecompass.statesystem.core.ITmfStateSystemBuilder;
+import org.eclipse.tracecompass.statesystem.core.StateSystemFactory;
+import org.eclipse.tracecompass.statesystem.core.backend.IStateHistoryBackend;
+import org.eclipse.tracecompass.statesystem.core.backend.StateHistoryBackendFactory;
+import org.eclipse.tracecompass.statesystem.core.exceptions.StateSystemDisposedException;
+import org.eclipse.tracecompass.statesystem.core.interval.ITmfStateInterval;
+import org.eclipse.tracecompass.statesystem.core.statevalue.ITmfStateValue;
import org.eclipse.tracecompass.tmf.core.analysis.IAnalysisModule;
import org.eclipse.tracecompass.tmf.core.dataprovider.DataProviderParameterUtils;
import org.eclipse.tracecompass.tmf.core.model.CommonStatusMessage;
@@ -78,6 +90,7 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Multimap;
+import com.google.common.collect.TreeMultimap;
/**
* A data provider for flame graphs, using a {@link IWeightedTreeProvider} as
@@ -109,12 +122,17 @@
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()));
/**
* 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 final ICallGraphProvider fCgProvider;
private final String fAnalysisId;
private final long fTraceId = ENTRY_ID.getAndIncrement();
@@ -129,16 +147,48 @@
/** An internal class to describe the data for an entry */
private static class CallGraphEntry {
private final ICallStackElement fElement;
- private final CallGraph fCallgraph;
- private final int fDepth;
+ private ITmfStateSystem fSs;
+ private Integer fQuark;
- private CallGraphEntry(ICallStackElement element, CallGraph callgraph, int depth) {
+ public CallGraphEntry(ICallStackElement element, ITmfStateSystem ss, Integer quark) {
fElement = element;
- fCallgraph = callgraph;
- fDepth = depth;
+ fSs = ss;
+ fQuark = quark;
}
}
+ private static class CallSiteCustomValue extends CustomStateValue {
+ private WeightedTree<ICallStackSymbol> fCallSite;
+
+ public CallSiteCustomValue(WeightedTree<ICallStackSymbol> rootFunction) {
+ fCallSite = rootFunction;
+ }
+
+ @Override
+ public int compareTo(ITmfStateValue o) {
+ if (!(o instanceof CallSiteCustomValue)) {
+ return -1;
+ }
+ return fCallSite.compareTo(((CallSiteCustomValue) o).fCallSite);
+ }
+
+ @Override
+ protected Byte getCustomTypeId() {
+ return 103;
+ }
+
+ @Override
+ protected void serializeValue(ISafeByteBufferWriter buffer) {
+ throw new UnsupportedOperationException("This state value is not meant to be written to disk"); //$NON-NLS-1$
+ }
+
+ @Override
+ protected int getSerializedValueSize() {
+ throw new UnsupportedOperationException("This state value is not meant to be written to disk"); //$NON-NLS-1$
+ }
+
+ }
+
/**
* Constructor
*
@@ -276,6 +326,62 @@
}
+ private ITmfStateSystem elementToStateSystem(CallGraph callgraph, ICallStackElement 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);
+ int quarkFct = ssb.getQuarkAbsoluteAndAdd(FUNCTION_LEVEL);
+ Deque<Long> timestampStack = new ArrayDeque<>();
+ timestampStack.push(0L);
+ for (AggregatedCallSite rootFunction : rootFunctions) {
+ recursivelyAddChildren(ssb, quarkFct, rootFunction, timestampStack);
+ }
+ Long endTime = timestampStack.pop();
+ ssb.closeHistory(endTime);
+
+ return ssb;
+ }
+
+ private void recursivelyAddChildren(ITmfStateSystemBuilder ssb, int quarkFct, WeightedTree<ICallStackSymbol> callSite, Deque<Long> timestampStack) {
+ Long lastEnd = timestampStack.peek();
+ if (lastEnd == null) {
+ return;
+ }
+ ssb.pushAttribute(lastEnd, (Object) new CallSiteCustomValue(callSite), quarkFct);
+
+ // Push the children to the state system
+ timestampStack.push(lastEnd);
+ List<WeightedTree<ICallStackSymbol>> children = new ArrayList<>(callSite.getChildren());
+ children.sort(CCT_COMPARATOR2);
+ for (WeightedTree<ICallStackSymbol> callsite : children) {
+ recursivelyAddChildren(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();
+ }
+ }
+ }
+
+ long currentEnd = timestampStack.pop() + callSite.getWeight();
+ timestampStack.push(currentEnd);
+ ssb.popAttribute(currentEnd, quarkFct);
+ }
+
/**
* Build the entry list for one thread
*
@@ -306,84 +412,40 @@
return;
}
- List<FlameChartEntryModel.Builder> childrenEntries = new ArrayList<>();
- List<FlameChartEntryModel.Builder> extraEntries = new ArrayList<>();
Deque<Long> timestampStack = new ArrayDeque<>();
timestampStack.push(0L);
- // Sort children by duration
- List<AggregatedCallSite> rootFunctions = new ArrayList<>(callgraph.getCallingContextTree(element));
- rootFunctions.sort(CCT_COMPARATOR);
- for (AggregatedCallSite rootFunction : rootFunctions) {
- createLevelChildren(element, rootFunction, childrenEntries, timestampStack, entry.getId());
- createExtraChildren(rootFunction, extraEntries, timestampStack, entry.getId());
- long currentThreadDuration = timestampStack.pop() + rootFunction.getWeight();
- timestampStack.push(currentThreadDuration);
+ // Get the state system to represent this callgraph
+ ITmfStateSystem ss = elementToStateSystem(callgraph, element);
+ entry.setEndTime(ss.getCurrentEndTime());
+
+ // Add items for the function entries
+ int quark = ss.optQuarkAbsolute(FUNCTION_LEVEL);
+ if (quark == ITmfStateSystem.INVALID_ATTRIBUTE) {
+ return;
}
- for (FlameChartEntryModel.Builder child : childrenEntries) {
+ int i = 0;
+ for (Integer subQuark : ss.getSubAttributes(quark, false)) {
+ FlameChartEntryModel.Builder child = new FlameChartEntryModel.Builder(ENTRY_ID.getAndIncrement(), entry.getId(), String.valueOf(i), 0, EntryType.FUNCTION, i);
+ child.setEndTime(ss.getCurrentEndTime());
builder.add(child);
- fCgEntries.put(child.getId(), new CallGraphEntry(element, callgraph, child.getDepth()));
+ i++;
+ fCgEntries.put(child.getId(), new CallGraphEntry(element, ss, subQuark));
}
- for (FlameChartEntryModel.Builder child : extraEntries) {
- builder.add(child);
- fCgEntries.put(child.getId(), new CallGraphEntry(element, callgraph, child.getDepth()));
+
+ // Add items for the extra entries
+ quark = ss.optQuarkAbsolute(EXTRA_LEVEL);
+ if (quark == ITmfStateSystem.INVALID_ATTRIBUTE) {
+ return;
}
- entry.setEndTime(timestampStack.pop());
+ 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;
}
- /**
- * Parse the aggregated tree created by the callGraphAnalysis and creates
- * the event list (functions) for each entry (depth)
- *
- * @param element
- *
- * @param firstNode
- * The first node of the aggregation tree
- * @param childrenEntries
- * The list of entries for one thread
- * @param timestampStack
- * A stack used to save the functions timeStamps
- */
- private static void createLevelChildren(ICallStackElement element, AggregatedCallSite firstNode, List<FlameChartEntryModel.Builder> childrenEntries, Deque<Long> timestampStack, long parentId) {
- Long lastEnd = timestampStack.peek();
- if (lastEnd == null) {
- return;
- }
- // Prepare all the level entries for this callsite
- for (int i = 0; i <= firstNode.getMaxDepth() - 1; i++) {
- if (i >= childrenEntries.size()) {
- FlameChartEntryModel.Builder entry = new FlameChartEntryModel.Builder(ENTRY_ID.getAndIncrement(), parentId, String.valueOf(i), 0, EntryType.FUNCTION, i);
- childrenEntries.add(entry);
- }
- childrenEntries.get(i).setEndTime(lastEnd + firstNode.getWeight());
- }
- }
-
- private static void createExtraChildren(AggregatedCallSite firstNode, List<FlameChartEntryModel.Builder> extraEntries, Deque<Long> timestampStack, long parentId) {
- Long lastEnd = timestampStack.peek();
- if (lastEnd == null) {
- return;
- }
- Iterator<AggregatedCallSite> extraChildrenSites = firstNode.getExtraChildrenSites().iterator();
-
- if (!extraChildrenSites.hasNext()) {
- return;
- }
- // Get or add the entry
- if (extraEntries.isEmpty()) {
- FlameChartEntryModel.Builder entry = new FlameChartEntryModel.Builder(ENTRY_ID.getAndIncrement(), parentId, Objects.requireNonNull(Messages.FlameGraph_KernelStatusTitle), 0, EntryType.KERNEL, -1);
- extraEntries.add(entry);
- }
- FlameChartEntryModel.Builder entry = extraEntries.get(0);
-
- while (extraChildrenSites.hasNext()) {
- AggregatedCallSite next = extraChildrenSites.next();
- lastEnd += next.getWeight();
- entry.setEndTime(lastEnd);
- }
- }
-
@Override
public @NonNull TmfModelResponse<@NonNull TimeGraphModel> fetchRowModel(@NonNull Map<@NonNull String, @NonNull Object> fetchParameters, @Nullable IProgressMonitor monitor) {
SubMonitor subMonitor = Objects.requireNonNull(SubMonitor.convert(monitor, "FlameGraphDataProvider#fetchRowModel", 2)); //$NON-NLS-1$
@@ -401,12 +463,12 @@
selected = fEntries.keySet();
}
List<CallGraphEntry> selectedEntries = new ArrayList<>();
- Multimap<Pair<CallGraph, ICallStackElement>, Pair<Integer, Long>> requested = HashMultimap.create();
+ Multimap<Pair<ITmfStateSystem, ICallStackElement>, Pair<Integer, Long>> requested = HashMultimap.create();
for (Long id : selected) {
CallGraphEntry entry = fCgEntries.get(id);
if (entry != null) {
selectedEntries.add(entry);
- requested.put(new Pair<>(entry.fCallgraph, entry.fElement), new Pair<>(entry.fDepth, id));
+ requested.put(new Pair<>(entry.fSs, entry.fElement), new Pair<>(entry.fQuark, id));
}
}
@@ -422,146 +484,80 @@
}
// For each element and callgraph, get the states
- for (Pair<CallGraph, ICallStackElement> element : requested.keySet()) {
- if (subMonitor.isCanceled()) {
- return new TmfModelResponse<>(null, ITmfResponse.Status.CANCELLED, CommonStatusMessage.TASK_CANCELLED);
+ try {
+ for (Pair<ITmfStateSystem, ICallStackElement> 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 = requested.get(element);
- rowModels.addAll(getStatesForElement(times, predicates, subMonitor, element.getFirst(), element.getSecond(), depths));
+ } catch (StateSystemDisposedException e) {
+ // Nothing to do
}
return new TmfModelResponse<>(new TimeGraphModel(rowModels), ITmfResponse.Status.COMPLETED, CommonStatusMessage.COMPLETED);
}
private List<ITimeGraphRowModel> getStatesForElement(List<Long> times, Map<Integer, Predicate<Multimap<String, Object>>> predicates, IProgressMonitor monitor,
- CallGraph callgraph, ICallStackElement csElement,
- Collection<Pair<Integer, Long>> depths) {
- // Get the cct for this element (first level callsites) and sort them
- Collection<AggregatedCallSite> cct = callgraph.getCallingContextTree(csElement);
- List<AggregatedCallSite> sortedCct = new ArrayList<>(cct);
- sortedCct.sort(CCT_COMPARATOR);
-
- // Maps a depth with a pair of entry ID and list of states
- Map<Integer, Pair<Long, List<ITimeGraphState>>> depthIds = new HashMap<>();
- int maxDepth = 0;
- long maxEndTime = 0;
- for (Pair<Integer, Long> depth : depths) {
- maxDepth = Math.max(depth.getFirst(), maxDepth);
- Long endTime = fEndTimes.get(depth.getSecond());
- maxEndTime = endTime != null ? Math.max(endTime, maxEndTime) : maxEndTime;
- depthIds.put(depth.getFirst(), new Pair<>(depth.getSecond(), new ArrayList<>()));
+ ITmfStateSystem ss, Collection<Pair<Integer, Long>> depths) throws StateSystemDisposedException {
+ List<Integer> quarks = new ArrayList<>();
+ for (Pair<Integer, Long> pair : depths) {
+ quarks.add(pair.getFirst());
}
-
- long currentWeightTime = 0;
- Pair<Long, List<ITimeGraphState>> kernelData = depthIds.get(-1);
- // Start parsing the callgraph
- for (AggregatedCallSite callsite : sortedCct) {
- if (timeOverlap(currentWeightTime, callsite.getWeight(), times)) {
- recurseAddCallsite(callsite, currentWeightTime, predicates, times, depthIds, 0, maxDepth, monitor);
-
- // Get the kernel data if necessary
- if (kernelData != null) {
- List<AggregatedCallSite> extraChildrenSites = new ArrayList<>(callsite.getExtraChildrenSites());
- extraChildrenSites.sort(CCT_COMPARATOR);
- // Add the required children
- long weightTime = currentWeightTime;
- for (AggregatedCallSite child : extraChildrenSites) {
- if (timeOverlap(weightTime, child.getWeight(), times)) {
- ITimeGraphState timeGraphState = new TimeGraphState(weightTime, child.getWeight(), ((AggregatedThreadStatus) child).getProcessStatus().getStateValue().unboxInt());
- applyFilterAndAddState(kernelData.getSecond(), timeGraphState, kernelData.getFirst(), predicates, monitor);
-
- }
- weightTime += child.getWeight();
- }
- }
+ TreeMultimap<Integer, ITmfStateInterval> intervals = TreeMultimap.create(Comparator.naturalOrder(),
+ Comparator.comparing(ITmfStateInterval::getStartTime));
+ long ssEndTime = ss.getCurrentEndTime();
+ for (ITmfStateInterval interval : ss.query2D(quarks, times)) {
+ if (monitor.isCanceled()) {
+ return Collections.emptyList();
}
- currentWeightTime += callsite.getWeight();
- }
-
- // We may need to fill with null after the last callsite
- if (maxEndTime > currentWeightTime && timeOverlap(currentWeightTime, maxEndTime - currentWeightTime, times)) {
- fillDeeperWithNull(0, maxDepth, depthIds, currentWeightTime, maxEndTime - currentWeightTime);
- }
-
- List<ITimeGraphRowModel> rowModels = new ArrayList<>();
- for (Pair<Long, List<ITimeGraphState>> states : depthIds.values()) {
- rowModels.add(new TimeGraphRowModel(states.getFirst(), states.getSecond()));
- }
- return rowModels;
- }
-
- // Recursively adds this callsite states and its children
- private void recurseAddCallsite(AggregatedCallSite callsite, long stateStartTime,
- Map<Integer, Predicate<Multimap<String, Object>>> predicates,
- List<Long> times, Map<Integer, Pair<Long, List<ITimeGraphState>>> depthIds,
- int depth, int maxDepth, IProgressMonitor monitor) {
- if (monitor.isCanceled()) {
- return;
- }
- // Add the state if current depth is requested
- Pair<Long, List<ITimeGraphState>> stateList = depthIds.get(depth);
- if (stateList != null) {
- ITimeGraphState timeGraphState = createTimeGraphState(callsite, stateStartTime);
- applyFilterAndAddState(stateList.getSecond(), timeGraphState, stateList.getFirst(), predicates, monitor);
- }
- // Stop recursing if there's no more depth requested or if depth is -1
- if (depth >= maxDepth) {
- return;
- }
-
- /* We can fill with null states all depth deeper than the current site's max depth. Max depth includes the current element, so we -1 */
- int thisMaxDepth = depth + callsite.getMaxDepth() - 1;
- fillDeeperWithNull(thisMaxDepth, maxDepth, depthIds, stateStartTime, callsite.getWeight());
-
- // Get and sort the children
- List<AggregatedCallSite> children = new ArrayList<>(callsite.getCallees());
- if (children.isEmpty()) {
- return;
- }
- children.sort(CCT_COMPARATOR);
-
- // Add the required children
- long weightTime = stateStartTime;
- for (AggregatedCallSite child : children) {
- if (timeOverlap(weightTime, child.getWeight(), times)) {
- recurseAddCallsite(child, weightTime, predicates, times, depthIds, depth + 1, thisMaxDepth, monitor);
+ // Ignore the null intervals of value 1 at the end of the state
+ // system
+ if (interval.getStartTime() == ssEndTime &&
+ interval.getStartTime() == interval.getEndTime() &&
+ interval.getValue() == null) {
+ continue;
}
- weightTime += child.getWeight();
+ intervals.put(interval.getAttribute(), interval);
}
- // We may need to fill the remaining data with null states
- if (callsite.getWeight() > weightTime - stateStartTime && timeOverlap(weightTime, callsite.getWeight() - (weightTime - stateStartTime), times)) {
- fillDeeperWithNull(depth, thisMaxDepth, depthIds, weightTime, callsite.getWeight() - (weightTime - stateStartTime));
+
+ List<ITimeGraphRowModel> rows = new ArrayList<>();
+ for (Pair<Integer, Long> pair : depths) {
+ int quark = pair.getFirst();
+ NavigableSet<ITmfStateInterval> states = intervals.get(quark);
+
+ if (monitor.isCanceled()) {
+ return Collections.emptyList();
+ }
+ List<ITimeGraphState> eventList = new ArrayList<>();
+ Long key = Objects.requireNonNull(pair.getSecond());
+ states.forEach(i -> {
+ ITimeGraphState timegraphState = createTimeGraphState(i, ssEndTime);
+ applyFilterAndAddState(eventList, timegraphState, key, predicates, monitor);
+ });
+ rows.add(new TimeGraphRowModel(key, eventList));
}
+ return rows;
}
- private static void fillDeeperWithNull(int depth, int depthLimit, Map<Integer, Pair<Long, List<ITimeGraphState>>> depthIds, long time, long duration) {
- if (depthLimit <= depth) {
- return;
- }
- /* Fill with null time graph states all entries deeper than depth */
- for (Entry<Integer, Pair<Long, List<ITimeGraphState>>> depthEntry : depthIds.entrySet()) {
- if (depthEntry.getKey() > depth && depthEntry.getKey() <= depthLimit) {
- depthEntry.getValue().getSecond().add(new TimeGraphState(time, duration, Integer.MIN_VALUE));
- }
- }
- }
+ private ITimeGraphState createTimeGraphState(ITmfStateInterval interval, long ssEndTime) {
+ long startTime = interval.getStartTime();
+ long duration = interval.getEndTime() - startTime + (ssEndTime == interval.getEndTime() ? 0 : 1);
+ Object valueObject = interval.getValue();
+ if (valueObject instanceof CallSiteCustomValue) {
- private ITimeGraphState createTimeGraphState(AggregatedCallSite callsite, long currentWeightTime) {
- ICallStackSymbol value = callsite.getObject();
- String resolved = value.resolve(fSymbolProviders);
- return new TimeGraphState(currentWeightTime, callsite.getWeight(), value.hashCode(), resolved);
- }
-
- /** Verify if one of the requested time overlaps this callsite */
- private static boolean timeOverlap(long start, long duration, List<Long> times) {
- long end = start + duration;
- for (Long time : times) {
- if (time >= start && time <= end) {
- return true;
+ 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
+ if (callsite instanceof AggregatedThreadStatus) {
+ return new TimeGraphState(startTime, duration, ((AggregatedThreadStatus) callsite).getProcessStatus().getStateValue().unboxInt(), resolved);
}
+ return new TimeGraphState(startTime, duration, value.hashCode(), resolved);
}
- return false;
+ return new TimeGraphState(startTime, duration, Integer.MIN_VALUE);
}
@Override
@@ -585,7 +581,7 @@
if (callGraphEntry == null) {
return new TmfModelResponse<>(Collections.emptyMap(), ITmfResponse.Status.COMPLETED, CommonStatusMessage.COMPLETED);
}
- AggregatedCallSite callSite = findCallSite(callGraphEntry.fCallgraph.getCallingContextTree(callGraphEntry.fElement), time, callGraphEntry.fDepth, 0, 0);
+ WeightedTree<ICallStackSymbol> callSite = findCallSite(callGraphEntry, time);
if (callSite != null) {
return new TmfModelResponse<>(getTooltip(callSite), ITmfResponse.Status.COMPLETED, CommonStatusMessage.COMPLETED);
}
@@ -593,8 +589,13 @@
return new TmfModelResponse<>(Collections.emptyMap(), ITmfResponse.Status.COMPLETED, CommonStatusMessage.COMPLETED);
}
- private static Map<String, String> getTooltip(AggregatedCallSite callSite) {
+ private Map<String, String> getTooltip(WeightedTree<ICallStackSymbol> tree) {
+ if (!(tree instanceof AggregatedCallSite)) {
+ return Collections.emptyMap();
+ }
+ AggregatedCallSite callSite = (AggregatedCallSite) tree;
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();
@@ -612,21 +613,17 @@
return builder.build();
}
- /** Find the callsite at the time and depth requested */
- private static @Nullable AggregatedCallSite findCallSite(Collection<AggregatedCallSite> collection, Long time, int depth, long currentTime, int currentDepth) {
- List<AggregatedCallSite> cct = new ArrayList<>(collection);
- cct.sort(CCT_COMPARATOR);
- long weight = currentTime;
- for (AggregatedCallSite callsite : cct) {
- if (weight + callsite.getWeight() < time) {
- weight += callsite.getWeight();
- continue;
+ /** Find the callsite at the time requested */
+ private static @Nullable WeightedTree<ICallStackSymbol> findCallSite(CallGraphEntry cgEntry, Long time) {
+ try {
+ ITmfStateInterval interval = cgEntry.fSs.querySingleState(time, cgEntry.fQuark);
+
+ Object valueObject = interval.getValue();
+ if (valueObject instanceof CallSiteCustomValue) {
+ return ((CallSiteCustomValue) valueObject).fCallSite;
}
- // This is the right callsite, let's check the depth
- if (currentDepth == depth) {
- return callsite;
- }
- return findCallSite(callsite.getCallees(), time, depth, weight, currentDepth + 1);
+ } catch (StateSystemDisposedException e) {
+ // Nothing to do
}
return null;
}