Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGeneviève Bastien2018-03-09 18:53:36 +0000
committerGenevieve Bastien2018-04-30 21:00:12 +0000
commitfd68941c8c672f55f2a9b2ca09f059f7670560d8 (patch)
treeaa8840ad1e11e9aff366c6d9bd7fdc4b34a2fb1c
parent4bd98ad1a400adac8c79c5e9e709a005aa28fae5 (diff)
downloadorg.eclipse.tracecompass.incubator-fd68941c8c672f55f2a9b2ca09f059f7670560d8.tar.gz
org.eclipse.tracecompass.incubator-fd68941c8c672f55f2a9b2ca09f059f7670560d8.tar.xz
org.eclipse.tracecompass.incubator-fd68941c8c672f55f2a9b2ca09f059f7670560d8.zip
callstack: Add the flame chart data provider
And the unit tests. Change-Id: I9d049f2e08d21b1c31064a987c67c9aed3b6e282 Signed-off-by: Geneviève Bastien <gbastien+lttng@versatic.net> Reviewed-on: https://git.eclipse.org/r/119103 Tested-by: CI Bot Reviewed-by: Matthew Khouzam <matthew.khouzam@ericsson.com> Tested-by: Matthew Khouzam <matthew.khouzam@ericsson.com>
-rw-r--r--callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/src/org/eclipse/tracecompass/incubator/callstack/core/tests/flamechart/CallStackTestBase.java6
-rw-r--r--callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/src/org/eclipse/tracecompass/incubator/callstack/core/tests/flamechart/FlameChartDataProviderTest.java354
-rw-r--r--callstack/org.eclipse.tracecompass.incubator.callstack.core/META-INF/MANIFEST.MF4
-rw-r--r--callstack/org.eclipse.tracecompass.incubator.callstack.core/plugin.xml7
-rw-r--r--callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/callstack/core/flamechart/CallStack.java173
-rw-r--r--callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/callstack/core/instrumented/CallStackDepth.java79
-rw-r--r--callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/callstack/core/instrumented/IFlameChartProvider.java36
-rw-r--r--callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/callstack/core/instrumented/statesystem/CallStackSeries.java65
-rw-r--r--callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/callstack/core/instrumented/statesystem/InstrumentedCallStackAnalysis.java13
-rw-r--r--callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/instrumented/InstrumentedCallStackElement.java54
-rw-r--r--callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/instrumented/callgraph/CallGraphAnalysis.java2
-rw-r--r--callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/instrumented/provider/FlameChartDataProvider.java740
-rw-r--r--callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/instrumented/provider/FlameChartDataProviderFactory.java80
-rw-r--r--callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/instrumented/provider/FlameChartEntryModel.java138
-rw-r--r--callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/instrumented/provider/Messages.java36
-rw-r--r--callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/instrumented/provider/messages.properties11
-rw-r--r--callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/instrumented/provider/package-info.java11
-rw-r--r--callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/xml/callstack/CallstackXmlAnalysis.java13
-rw-r--r--callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/views/flamechart/ProcessStatusEntry.java3
19 files changed, 1758 insertions, 67 deletions
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/src/org/eclipse/tracecompass/incubator/callstack/core/tests/flamechart/CallStackTestBase.java b/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/src/org/eclipse/tracecompass/incubator/callstack/core/tests/flamechart/CallStackTestBase.java
index 13faa93e9..4420ac88c 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/src/org/eclipse/tracecompass/incubator/callstack/core/tests/flamechart/CallStackTestBase.java
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/src/org/eclipse/tracecompass/incubator/callstack/core/tests/flamechart/CallStackTestBase.java
@@ -98,9 +98,9 @@ public class CallStackTestBase {
* |-- tid3 3e2 --------------------------------20x
* 5e3--6x 7e2--------13x
*
- * pid5 --- tid6 1e1 -----------------------------------20x
- * | 2e3 ---------7x 12e4------------20x
- * | 4e1--6x
+ * pid5 --- tid6 1e1 ----------------------------------------20x
+ * | 2e3 ---------7x 8e2---11x 12e4------------20x
+ * | 4e1--6x 9e3-10x
* |-- tid7 1e5 -----------------------------------20x
* 2e2 +++ 6x 9e2 ++++ 13x 15e2 ++ 19x
* 10e3 + 11x
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/src/org/eclipse/tracecompass/incubator/callstack/core/tests/flamechart/FlameChartDataProviderTest.java b/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/src/org/eclipse/tracecompass/incubator/callstack/core/tests/flamechart/FlameChartDataProviderTest.java
new file mode 100644
index 000000000..b67b4c2e7
--- /dev/null
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/src/org/eclipse/tracecompass/incubator/callstack/core/tests/flamechart/FlameChartDataProviderTest.java
@@ -0,0 +1,354 @@
+/*******************************************************************************
+ * Copyright (c) 2018 École Polytechnique de Montréal
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.incubator.callstack.core.tests.flamechart;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.incubator.callstack.core.tests.stubs.CallStackAnalysisStub;
+import org.eclipse.tracecompass.incubator.internal.callstack.core.instrumented.provider.FlameChartDataProvider;
+import org.eclipse.tracecompass.incubator.internal.callstack.core.instrumented.provider.FlameChartDataProviderFactory;
+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.tmf.core.model.filters.SelectionTimeQueryFilter;
+import org.eclipse.tracecompass.internal.provisional.tmf.core.model.filters.TimeQueryFilter;
+import org.eclipse.tracecompass.internal.provisional.tmf.core.model.timegraph.ITimeGraphRowModel;
+import org.eclipse.tracecompass.internal.provisional.tmf.core.model.timegraph.ITimeGraphState;
+import org.eclipse.tracecompass.internal.provisional.tmf.core.model.timegraph.TimeGraphState;
+import org.eclipse.tracecompass.internal.provisional.tmf.core.response.ITmfResponse;
+import org.eclipse.tracecompass.internal.provisional.tmf.core.response.TmfModelResponse;
+import org.junit.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+
+/**
+ * Test the {@link FlameChartDataProvider} class
+ *
+ * @author Geneviève Bastien
+ */
+@SuppressWarnings("restriction")
+public class FlameChartDataProviderTest extends CallStackTestBase {
+
+ private static final @Nullable IProgressMonitor MONITOR = new NullProgressMonitor();
+
+ private FlameChartDataProvider getDataProvider() {
+ CallStackAnalysisStub module = getModule();
+ assertNotNull(module);
+
+ FlameChartDataProviderFactory factory = new FlameChartDataProviderFactory();
+
+ FlameChartDataProvider dataProvider = (FlameChartDataProvider) factory.createProvider(getTrace(), module.getId());
+ assertNotNull(dataProvider);
+ return dataProvider;
+ }
+
+ /**
+ * Test getting the tree from the flame chart data provider
+ */
+ @Test
+ public void testFetchTree() {
+ FlameChartDataProvider dataProvider = getDataProvider();
+
+ TmfModelResponse<List<FlameChartEntryModel>> responseTree = dataProvider.fetchTree(new TimeQueryFilter(0, Long.MAX_VALUE, 2), new NullProgressMonitor());
+ assertTrue(responseTree.getStatus().equals(ITmfResponse.Status.COMPLETED));
+
+ // Test the size of the tree
+ List<FlameChartEntryModel> model = responseTree.getModel();
+ assertNotNull(model);
+ assertEquals(18, model.size());
+
+ String traceName = getTrace().getName();
+
+ // Test the hierarchy of the tree
+ for (FlameChartEntryModel entry : model) {
+ FlameChartEntryModel parent = findEntryById(model, entry.getParentId());
+ switch (entry.getEntryType()) {
+ case FUNCTION:
+ assertNotNull(parent);
+ assertEquals(EntryType.LEVEL, parent.getEntryType());
+ break;
+ case LEVEL: {
+ assertNotNull(parent);
+ // Verify the hierarchy of the elements
+ switch (entry.getName()) {
+ case "1":
+ assertEquals(traceName, parent.getName());
+ break;
+ case "2":
+ assertEquals("1", parent.getName());
+ break;
+ case "3":
+ assertEquals("1", parent.getName());
+ break;
+ case "5":
+ assertEquals(traceName, parent.getName());
+ break;
+ case "6":
+ assertEquals("5", parent.getName());
+ break;
+ case "7":
+ assertEquals("5", parent.getName());
+ break;
+ default:
+ fail("Unknown entry " + entry.getName());
+ break;
+ }
+ }
+ break;
+ case KERNEL:
+ fail("There should be no kernel entry in this callstack");
+ break;
+ case TRACE:
+ assertEquals(-1, entry.getParentId());
+ break;
+ default:
+ fail("Unknown entry " + entry);
+ break;
+ }
+ }
+ }
+
+ private static @Nullable FlameChartEntryModel findEntryById(Collection<FlameChartEntryModel> list, long id) {
+ return list.stream()
+ .filter(entry -> entry.getId() == id)
+ .findFirst().orElse(null);
+ }
+
+ private static @Nullable FlameChartEntryModel findEntryByNameAndType(Collection<FlameChartEntryModel> list, String name, EntryType type) {
+ return list.stream()
+ .filter(entry -> entry.getEntryType().equals(type) && entry.getName().equals(name))
+ .findFirst().orElse(null);
+ }
+
+ private static @Nullable FlameChartEntryModel findEntryByDepthAndType(Collection<FlameChartEntryModel> list, int depth, EntryType type) {
+ return list.stream()
+ .filter(entry -> entry.getEntryType().equals(type) && entry.getDepth() == depth)
+ .findFirst().orElse(null);
+ }
+
+ private static List<FlameChartEntryModel> findEntriesByParent(Collection<FlameChartEntryModel> list, long parentId) {
+ return list.stream()
+ .filter(entry -> entry.getParentId() == parentId)
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * Test getting the model from the flame chart data provider
+ */
+ @Test
+ public void testFetchModel() {
+ FlameChartDataProvider dataProvider = getDataProvider();
+
+ TmfModelResponse<List<FlameChartEntryModel>> responseTree = dataProvider.fetchTree(new TimeQueryFilter(0, Long.MAX_VALUE, 2), new NullProgressMonitor());
+ assertTrue(responseTree.getStatus().equals(ITmfResponse.Status.COMPLETED));
+ List<FlameChartEntryModel> model = responseTree.getModel();
+
+ // Find the entries corresponding to threads 3 and 6 (along with pid 5)
+ Set<@NonNull Long> selectedIds = new HashSet<>();
+ // Thread 3
+ FlameChartEntryModel tid3 = findEntryByNameAndType(model, "3", EntryType.LEVEL);
+ assertNotNull(tid3);
+ selectedIds.add(tid3.getId());
+ List<FlameChartEntryModel> tid3Children = findEntriesByParent(model, tid3.getId());
+ assertEquals(2, tid3Children.size());
+ tid3Children.forEach(child -> selectedIds.add(child.getId()));
+ // Pid 5
+ FlameChartEntryModel pid5 = findEntryByNameAndType(model, "5", EntryType.LEVEL);
+ assertNotNull(pid5);
+ selectedIds.add(pid5.getId());
+ // Thread 6
+ FlameChartEntryModel tid6 = findEntryByNameAndType(model, "6", EntryType.LEVEL);
+ assertNotNull(tid6);
+ selectedIds.add(tid6.getId());
+ List<FlameChartEntryModel> tid6Children = findEntriesByParent(model, tid6.getId());
+ assertEquals(3, tid6Children.size());
+ tid6Children.forEach(child -> selectedIds.add(child.getId()));
+
+ // Get the row model for those entries with high resolution
+ TmfModelResponse<List<ITimeGraphRowModel>> rowModel = dataProvider.fetchRowModel(new SelectionTimeQueryFilter(3, 15, 50, selectedIds), new NullProgressMonitor());
+ assertEquals(ITmfResponse.Status.COMPLETED, rowModel.getStatus());
+
+ List<ITimeGraphRowModel> rowModels = rowModel.getModel();
+ assertNotNull(rowModels);
+ assertEquals(8, rowModels.size());
+
+ // Verify the level entries
+ verifyStates(rowModels, tid3, Collections.emptyList());
+ verifyStates(rowModels, pid5, Collections.emptyList());
+ verifyStates(rowModels, tid6, Collections.emptyList());
+ // Verify function level 1 of tid 3
+ verifyStates(rowModels, findEntryByDepthAndType(tid3Children, 1, EntryType.FUNCTION), ImmutableList.of(new TimeGraphState(3, 17, Long.MIN_VALUE, "op2")));
+ // Verify function level 2 of tid 3
+ verifyStates(rowModels, findEntryByDepthAndType(tid3Children, 2, EntryType.FUNCTION), ImmutableList.of(
+ new TimeGraphState(1, 4, Long.MIN_VALUE),
+ new TimeGraphState(5, 1, Long.MIN_VALUE, "op3"),
+ new TimeGraphState(6, 1, Long.MIN_VALUE),
+ new TimeGraphState(7, 6, Long.MIN_VALUE, "op2"),
+ new TimeGraphState(13, 8, Long.MIN_VALUE)));
+ // Verify function level 1 of tid 6
+ verifyStates(rowModels, findEntryByDepthAndType(tid6Children, 1, EntryType.FUNCTION), ImmutableList.of(new TimeGraphState(1, 19, Long.MIN_VALUE, "op1")));
+ // Verify function level 2 of tid 6
+ verifyStates(rowModels, findEntryByDepthAndType(tid6Children, 2, EntryType.FUNCTION), ImmutableList.of(
+ new TimeGraphState(2, 5, Long.MIN_VALUE, "op3"),
+ new TimeGraphState(7, 1, Long.MIN_VALUE),
+ new TimeGraphState(8, 3, Long.MIN_VALUE, "op2"),
+ new TimeGraphState(11, 1, Long.MIN_VALUE),
+ new TimeGraphState(12, 8, Long.MIN_VALUE, "op4")));
+ // Verify function level 3 of tid 6
+ verifyStates(rowModels, findEntryByDepthAndType(tid6Children, 3, EntryType.FUNCTION), ImmutableList.of(
+ new TimeGraphState(1, 3, Long.MIN_VALUE),
+ new TimeGraphState(4, 2, Long.MIN_VALUE, "op1"),
+ new TimeGraphState(6, 3, Long.MIN_VALUE),
+ new TimeGraphState(9, 1, Long.MIN_VALUE, "op3"),
+ new TimeGraphState(10, 11, Long.MIN_VALUE)));
+
+ // Get the row model for those entries with low resolution
+ rowModel = dataProvider.fetchRowModel(new SelectionTimeQueryFilter(3, 15, 2, selectedIds), new NullProgressMonitor());
+ assertEquals(ITmfResponse.Status.COMPLETED, rowModel.getStatus());
+
+ rowModels = rowModel.getModel();
+ assertNotNull(rowModels);
+ assertEquals(8, rowModels.size());
+
+ // Verify the level entries
+ verifyStates(rowModels, tid3, Collections.emptyList());
+ verifyStates(rowModels, pid5, Collections.emptyList());
+ verifyStates(rowModels, tid6, Collections.emptyList());
+ // Verify function level 1 of tid 3
+ verifyStates(rowModels, findEntryByDepthAndType(tid3Children, 1, EntryType.FUNCTION), ImmutableList.of(new TimeGraphState(3, 17, Long.MIN_VALUE, "op2")));
+ // Verify function level 2 of tid 3
+ verifyStates(rowModels, findEntryByDepthAndType(tid3Children, 2, EntryType.FUNCTION), ImmutableList.of(
+ new TimeGraphState(1, 4, Long.MIN_VALUE),
+ new TimeGraphState(13, 8, Long.MIN_VALUE)));
+ // Verify function level 1 of tid 6
+ verifyStates(rowModels, findEntryByDepthAndType(tid6Children, 1, EntryType.FUNCTION), ImmutableList.of(new TimeGraphState(1, 19, Long.MIN_VALUE, "op1")));
+ // Verify function level 2 of tid 6
+ verifyStates(rowModels, findEntryByDepthAndType(tid6Children, 2, EntryType.FUNCTION), ImmutableList.of(
+ new TimeGraphState(2, 5, Long.MIN_VALUE, "op3"),
+ new TimeGraphState(12, 8, Long.MIN_VALUE, "op4")));
+ // Verify function level 3 of tid 6
+ verifyStates(rowModels, findEntryByDepthAndType(tid6Children, 3, EntryType.FUNCTION), ImmutableList.of(
+ new TimeGraphState(1, 3, Long.MIN_VALUE),
+ new TimeGraphState(10, 11, Long.MIN_VALUE)));
+ }
+
+ /**
+ * Test following a callstack backward and forward
+ */
+ @Test
+ public void testFollowEvents() {
+ FlameChartDataProvider dataProvider = getDataProvider();
+
+ TmfModelResponse<List<FlameChartEntryModel>> responseTree = dataProvider.fetchTree(new TimeQueryFilter(0, Long.MAX_VALUE, 2), new NullProgressMonitor());
+ assertTrue(responseTree.getStatus().equals(ITmfResponse.Status.COMPLETED));
+ List<FlameChartEntryModel> model = responseTree.getModel();
+
+ // Thread 2
+ FlameChartEntryModel tid2 = findEntryByNameAndType(model, "2", EntryType.LEVEL);
+ assertNotNull(tid2);
+ List<FlameChartEntryModel> tid2Children = findEntriesByParent(model, tid2.getId());
+ assertEquals(3, tid2Children.size());
+
+ // For each child, make sure the response is always the same
+ for (FlameChartEntryModel tid2Child : tid2Children) {
+ TmfModelResponse<List<ITimeGraphRowModel>> rowModel = dataProvider.fetchRowModel(new SelectionTimeQueryFilter(6, Long.MAX_VALUE, 2, Collections.singleton(tid2Child.getId())), MONITOR);
+ verifyFollowResponse(rowModel, 1, 7);
+ }
+
+ // Go forward from time 7 till the end for one of the child element
+ Set<@NonNull Long> selectedEntry = Objects.requireNonNull(Collections.singleton(tid2Children.get(1).getId()));
+ TmfModelResponse<List<ITimeGraphRowModel>> rowModel = dataProvider.fetchRowModel(new SelectionTimeQueryFilter(7, Long.MAX_VALUE, 2, selectedEntry), MONITOR);
+ verifyFollowResponse(rowModel, 0, 10);
+
+ rowModel = dataProvider.fetchRowModel(new SelectionTimeQueryFilter(10, Long.MAX_VALUE, 2, selectedEntry), new NullProgressMonitor());
+ verifyFollowResponse(rowModel, 1, 12);
+
+ rowModel = dataProvider.fetchRowModel(new SelectionTimeQueryFilter(12, Long.MAX_VALUE, 2, selectedEntry), new NullProgressMonitor());
+ verifyFollowResponse(rowModel, 0, 20);
+
+ rowModel = dataProvider.fetchRowModel(new SelectionTimeQueryFilter(20, Long.MAX_VALUE, 2, selectedEntry), new NullProgressMonitor());
+ verifyFollowResponse(rowModel, -1, -1);
+
+ // Go backward from the back
+ rowModel = dataProvider.fetchRowModel(new SelectionTimeQueryFilter(Lists.newArrayList(Long.MIN_VALUE, 20L), selectedEntry), new NullProgressMonitor());
+ verifyFollowResponse(rowModel, 1, 12);
+
+ // Go backward from time 7 till the beginning
+ rowModel = dataProvider.fetchRowModel(new SelectionTimeQueryFilter(Lists.newArrayList(Long.MIN_VALUE, 7L), selectedEntry), new NullProgressMonitor());
+ verifyFollowResponse(rowModel, 2, 5);
+
+ rowModel = dataProvider.fetchRowModel(new SelectionTimeQueryFilter(Lists.newArrayList(Long.MIN_VALUE, 5L), selectedEntry), new NullProgressMonitor());
+ verifyFollowResponse(rowModel, 3, 4);
+
+ rowModel = dataProvider.fetchRowModel(new SelectionTimeQueryFilter(Lists.newArrayList(Long.MIN_VALUE, 4L), selectedEntry), new NullProgressMonitor());
+ verifyFollowResponse(rowModel, 2, 3);
+
+ rowModel = dataProvider.fetchRowModel(new SelectionTimeQueryFilter(Lists.newArrayList(Long.MIN_VALUE, 3L), selectedEntry), new NullProgressMonitor());
+ verifyFollowResponse(rowModel, 1, 1);
+
+ rowModel = dataProvider.fetchRowModel(new SelectionTimeQueryFilter(Lists.newArrayList(Long.MIN_VALUE, 1L), selectedEntry), new NullProgressMonitor());
+ verifyFollowResponse(rowModel, -1, -1);
+ }
+
+ private static void verifyFollowResponse(TmfModelResponse<List<ITimeGraphRowModel>> rowModel, int expectedDepth, int expectedTime) {
+ assertEquals(ITmfResponse.Status.COMPLETED, rowModel.getStatus());
+
+ List<ITimeGraphRowModel> rowModels = rowModel.getModel();
+ if (expectedDepth < 0) {
+ assertNull(rowModels);
+ return;
+ }
+ assertNotNull(rowModels);
+ assertEquals(1, rowModels.size());
+ List<ITimeGraphState> row = rowModels.get(0).getStates();
+ assertEquals(1, row.size());
+ ITimeGraphState stackInterval = row.get(0);
+ long depth = stackInterval.getValue();
+ assertEquals(expectedDepth, depth);
+ assertEquals(expectedTime, stackInterval.getStartTime());
+ }
+
+ private static void verifyStates(List<ITimeGraphRowModel> rowModels, FlameChartEntryModel entry, List<TimeGraphState> expectedStates) {
+ assertNotNull(entry);
+ ITimeGraphRowModel rowModel = rowModels.stream()
+ .filter(model -> model.getEntryID() == entry.getId())
+ .findFirst().orElse(null);
+ assertNotNull(rowModel);
+ List<ITimeGraphState> states = rowModel.getStates();
+ for (int i = 0; i < states.size(); i++) {
+ if (i > expectedStates.size() - 1) {
+ fail("Unexpected state at position " + i + " for entry " + entry.getName() + ": " + states.get(i));
+ }
+ ITimeGraphState actual = states.get(i);
+ ITimeGraphState expected = expectedStates.get(i);
+ assertEquals("State start time at " + i + " for entry " + entry.getName(), expected.getStartTime(), actual.getStartTime());
+ assertEquals("Duration at " + i + " for entry " + entry.getName(), expected.getDuration(), actual.getDuration());
+ assertEquals("Label at " + i + " for entry " + entry.getName(), expected.getLabel(), actual.getLabel());
+
+ }
+ }
+
+}
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core/META-INF/MANIFEST.MF b/callstack/org.eclipse.tracecompass.incubator.callstack.core/META-INF/MANIFEST.MF
index 19d018401..24f1c8ada 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.core/META-INF/MANIFEST.MF
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.core/META-INF/MANIFEST.MF
@@ -34,9 +34,11 @@ Export-Package: org.eclipse.tracecompass.incubator.callstack.core.base,
org.eclipse.tracecompass.incubator.internal.callstack.core.base;x-friends:="org.eclipse.tracecompass.incubator.callstack.core.tests,org.eclipse.tracecompass.incubator.callstack.ui",
org.eclipse.tracecompass.incubator.internal.callstack.core.instrumented;x-friends:="org.eclipse.tracecompass.incubator.callstack.core.tests,org.eclipse.tracecompass.incubator.callstack.ui",
org.eclipse.tracecompass.incubator.internal.callstack.core.instrumented.callgraph;x-friends:="org.eclipse.tracecompass.incubator.callstack.core.tests,org.eclipse.tracecompass.incubator.callstack.ui",
+ org.eclipse.tracecompass.incubator.internal.callstack.core.instrumented.provider,
org.eclipse.tracecompass.incubator.internal.callstack.core.symbol;x-internal:=true,
org.eclipse.tracecompass.incubator.internal.callstack.core.xml.callstack;x-friends:="org.eclipse.tracecompass.incubator.callstack.core.tests,org.eclipse.tracecompass.incubator.callstack.ui"
Import-Package: com.google.common.annotations,
- com.google.common.collect,
com.google.common.base,
+ com.google.common.cache,
+ com.google.common.collect,
org.apache.commons.lang3
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core/plugin.xml b/callstack/org.eclipse.tracecompass.incubator.callstack.core/plugin.xml
index 1cd694bc3..253fe1192 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.core/plugin.xml
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.core/plugin.xml
@@ -21,4 +21,11 @@
</tracetype>
</module>
</extension>
+ <extension
+ point="org.eclipse.tracecompass.tmf.core.dataprovider">
+ <dataProviderFactory
+ class="org.eclipse.tracecompass.incubator.internal.callstack.core.instrumented.provider.FlameChartDataProviderFactory"
+ id="org.eclipse.tracecompass.incubator.internal.callstack.core.instrumented.provider.flamechart">
+ </dataProviderFactory>
+ </extension>
</plugin>
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/callstack/core/flamechart/CallStack.java b/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/callstack/core/flamechart/CallStack.java
index 733e4cae6..4a171c832 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/callstack/core/flamechart/CallStack.java
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/callstack/core/flamechart/CallStack.java
@@ -10,8 +10,10 @@
package org.eclipse.tracecompass.incubator.callstack.core.flamechart;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
import java.util.function.Consumer;
import org.eclipse.core.runtime.IProgressMonitor;
@@ -129,43 +131,24 @@ public class CallStack {
}
}
- private String getHostId(long time) {
- return fHostProvider.apply(time);
- }
-
/**
- * Get the function call with closest beginning or end from time, either
- * forward or backward.
+ * Get the quark for a given depth
*
- * @param time
- * The time of query
- * @param forward
- * Set to <code>true</code> if the beginning or end is forward in
- * time, <code>false</code> to go backwards
- * @return The next function
+ * @param depth
+ * The requested depth
+ * @return Get the quark for the function at a given depth
*/
- public @Nullable ICalledFunction getNextFunction(long time, boolean forward) {
- // From the bottom of the stack, query at time t to find the last level
- // with an active call
- try {
- for (int i = fQuarks.size() - 1; i >= 0; i--) {
- ITmfStateInterval interval;
-
- interval = fStateSystem.querySingleState(time, fQuarks.get(i));
- if (!interval.getStateValue().isNull()) {
-
- }
- }
- } catch (StateSystemDisposedException e) {
-
- }
- return null;
+ public Integer getQuarkAtDepth(int depth) {
+ return fQuarks.get(depth - 1);
+ }
+ private String getHostId(long time) {
+ return fHostProvider.apply(time);
}
/**
- * Get the function call at a given depth that either begins or ends after
- * the requested time.
+ * Get the function call at a given depth that either begins or ends after the
+ * requested time.
*
* @param time
* The time to query
@@ -232,7 +215,43 @@ public class CallStack {
interval = fStateSystem.querySingleState(interval.getEndTime() + 1, fQuarks.get(depth - 1));
}
if (!interval.getStateValue().isNull() && interval.getStartTime() < end) {
- return CalledFunctionFactory.create(Math.max(start, interval.getStartTime()), Math.min(end, interval.getEndTime() + 1), interval.getValue(), getSymbolKeyAt(interval.getStartTime()), getThreadId(interval.getStartTime()), parent, model);
+ return CalledFunctionFactory.create(Math.max(start, interval.getStartTime()), Math.min(end, interval.getEndTime() + 1), interval.getValue(), getSymbolKeyAt(interval.getStartTime()), getThreadId(interval.getStartTime()), parent,
+ model);
+ }
+ } catch (StateSystemDisposedException e) {
+
+ }
+ return null;
+ }
+
+ /**
+ * Get the next depth of this callstack, from the selected time. This function
+ * is used to navigate the callstack forward or backward
+ *
+ * @param time
+ * The reference time from which to calculate the next depth
+ * @param forward
+ * If <code>true</code>, the returned depth is the next depth after
+ * the requested time, otherwise, it will return the next depth
+ * before.
+ * @return The interval whose value is a number referring to the next depth (or
+ * null if the stack is empty at this time), or <code>null</code> if
+ * there is no next depth to this callstack
+ */
+ public @Nullable ITmfStateInterval getNextDepth(long time, boolean forward) {
+ Integer oneQuark = fQuarks.get(0);
+ int parent = fStateSystem.getParentAttributeQuark(oneQuark);
+ long queryTime = Long.max(fStateSystem.getStartTime(), Long.min(time, fStateSystem.getCurrentEndTime()));
+ try {
+ ITmfStateInterval currentDepth = fStateSystem.querySingleState(queryTime, parent);
+ ITmfStateInterval interval = null;
+ if (forward && currentDepth.getEndTime() + 1 <= fStateSystem.getCurrentEndTime()) {
+ interval = fStateSystem.querySingleState(currentDepth.getEndTime() + 1, parent);
+ } else if (!forward && currentDepth.getStartTime() - 1 >= fStateSystem.getStartTime()) {
+ interval = fStateSystem.querySingleState(currentDepth.getStartTime() - 1, parent);
+ }
+ if (interval != null) {
+ return interval;
}
} catch (StateSystemDisposedException e) {
@@ -270,6 +289,18 @@ public class CallStack {
}
/**
+ * Update the quarks list. Only the quarks at positions higher than the size of
+ * the quarks will be copied in the list. The ones currently present should not
+ * change.
+ *
+ * @param subAttributes
+ * The new complete list of attributes
+ */
+ public void updateAttributes(List<Integer> subAttributes) {
+ fQuarks.addAll(fQuarks.size(), subAttributes.subList(fQuarks.size(), subAttributes.size()));
+ }
+
+ /**
* Get the ID of the thread running this callstack at time t. This method is
* used in conjunction with other trace data to get the time spent on the
* CPU for this call.
@@ -303,6 +334,18 @@ public class CallStack {
}
/**
+ * Get the unique {@link HostThread} for this callstack. This returns a value only if the TID is not variable in time _and_ it is defined
+ *
+ * @return The {@link HostThread} that spans this callstack or <code>null</code> if TID is variable or it is not defined.
+ */
+ public @Nullable HostThread getHostThread() {
+ if (!isTidVariable()) {
+ return getHostThread(fStateSystem.getStartTime());
+ }
+ return null;
+ }
+
+ /**
* Return whether the TID is variable through time for this callstack or if it
* fixed
*
@@ -407,16 +450,76 @@ public class CallStack {
*
* @param function
* The function for which to get the kernel statuses
- * @param resolution
- * The resolution, ie the number of nanoseconds between kernel status
- * queries. A value lower or equal to 1 will return all intervals.
+ * @param times
+ * The times at which to query kernel statuses. An empty collection
+ * will return all intervals.
* @return An iterator over the kernel status. The iterator can be empty is
* statuses are not available or if the function is outside the range of
* the available data.
*/
- public Iterable<ProcessStatusInterval> getKernelStatuses(ICalledFunction function, long resolution) {
+ public Iterable<ProcessStatusInterval> getKernelStatuses(ICalledFunction function, Collection<Long> times) {
IHostModel model = ModelManager.getModelFor(getHostId(function.getStart()));
+ int resolution = 1;
+ // Filter the times
+ if (!times.isEmpty()) {
+ // Filter the times overlapping this function and calculate a resolution
+ List<Long> filtered = new ArrayList<>();
+ for (Long time : times) {
+ if (function.intersects(time)) {
+ filtered.add(time);
+ }
+ }
+ Collections.sort(filtered);
+ resolution = !filtered.isEmpty() ? (int) (filtered.get(filtered.size() - 1) - filtered.get(0) / filtered.size()) : resolution;
+ return model.getThreadStatusIntervals(function.getThreadId(), function.getStart(), function.getEnd(), resolution);
+ }
return model.getThreadStatusIntervals(function.getThreadId(), function.getStart(), function.getEnd(), resolution);
}
+ /**
+ * Transforms a state interval from the state system into a
+ * {@link ICalledFunction}. The function allows to retrieve data from this
+ * function, for instance, the thread ID, the symbol provider, etc.
+ *
+ * Note: It is the responsibility of the caller to make sure that the interval
+ * does not have a null-value, otherwise, a NullPointerException will be thrown.
+ *
+ * @param callInterval
+ * The state interval
+ * @return An {@link ICalledFunction} object, with its fields resolved
+ */
+ public ICalledFunction getFunctionFromInterval(ITmfStateInterval callInterval) {
+ int threadId = getThreadId(callInterval.getStartTime());
+ return CalledFunctionFactory.create(callInterval.getStartTime(),
+ callInterval.getEndTime() + 1,
+ callInterval.getValue(),
+ getSymbolKeyAt(callInterval.getStartTime()),
+ threadId,
+ null,
+ ModelManager.getModelFor(getHostId(callInterval.getStartTime())));
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(fSymbolKeyElement, fThreadIdProvider, fStateSystem, fQuarks, fHostProvider);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (!(obj instanceof CallStack)) {
+ return false;
+ }
+ CallStack other = (CallStack) obj;
+ return (Objects.equals(fSymbolKeyElement, other.fSymbolKeyElement) &&
+ Objects.equals(fThreadIdProvider, other.fThreadIdProvider) &&
+ Objects.equals(fStateSystem, other.fStateSystem) &&
+ Objects.equals(fQuarks, other.fQuarks) &&
+ Objects.equals(fHostProvider, other.fHostProvider));
+ }
+
+ @Override
+ public String toString() {
+ return "Callstack for quarks " + fQuarks; //$NON-NLS-1$
+ }
+
}
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/callstack/core/instrumented/CallStackDepth.java b/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/callstack/core/instrumented/CallStackDepth.java
new file mode 100644
index 000000000..fbcf8da16
--- /dev/null
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/callstack/core/instrumented/CallStackDepth.java
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * Copyright (c) 2018 École Polytechnique de Montréal
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.incubator.callstack.core.instrumented;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.incubator.callstack.core.flamechart.CallStack;
+
+import com.google.common.base.Objects;
+
+/**
+ * A class that associates a callstack with a depth, to abstract the state
+ * system accesses.
+ *
+ * @author Geneviève Bastien
+ */
+public class CallStackDepth {
+
+ private final CallStack fCallstack;
+ private final int fDepth;
+
+ /**
+ * Constructor. The caller must make sure that the callstack has the requested
+ * depth.
+ *
+ * @param callstack
+ * The callstack
+ * @param depth
+ * The depth of the callstack
+ */
+ public CallStackDepth(CallStack callstack, int depth) {
+ fCallstack = callstack;
+ fDepth = depth;
+ }
+
+ /**
+ * Get the quark corresponding to this callstack depth
+ *
+ * @return The quark at this depth
+ */
+ public int getQuark() {
+ return fCallstack.getQuarkAtDepth(fDepth);
+ }
+
+ /**
+ * Get the callstack corresponding to this callstack depth
+ *
+ * @return The callstack
+ */
+ public CallStack getCallStack() {
+ return fCallstack;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(fCallstack, fDepth);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (!(obj instanceof CallStackDepth)) {
+ return false;
+ }
+ CallStackDepth csd = (CallStackDepth) obj;
+ return Objects.equal(fCallstack, csd.fCallstack) && (fDepth == csd.fDepth);
+ }
+
+ @Override
+ public String toString() {
+ return fCallstack + " at depth " + fDepth; //$NON-NLS-1$
+ }
+
+}
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/callstack/core/instrumented/IFlameChartProvider.java b/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/callstack/core/instrumented/IFlameChartProvider.java
index 9dbf48565..179bca38f 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/callstack/core/instrumented/IFlameChartProvider.java
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/callstack/core/instrumented/IFlameChartProvider.java
@@ -9,12 +9,19 @@
package org.eclipse.tracecompass.incubator.callstack.core.instrumented;
+import java.util.Collection;
+import java.util.Objects;
+
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.tracecompass.analysis.timing.core.segmentstore.ISegmentStoreProvider;
import org.eclipse.tracecompass.incubator.callstack.core.instrumented.statesystem.CallStackHostUtils;
import org.eclipse.tracecompass.incubator.callstack.core.instrumented.statesystem.CallStackSeries;
+import org.eclipse.tracecompass.segmentstore.core.ISegment;
import org.eclipse.tracecompass.tmf.core.analysis.IAnalysisModule;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Multimap;
+
/**
* Interface that can be implemented by components who provide call stacks as
* part of their data.
@@ -38,4 +45,33 @@ public interface IFlameChartProvider extends IAnalysisModule, ISegmentStoreProvi
* @return The ID of the host
*/
String getHostId();
+
+ /**
+ * Return whether this analysis is complete
+ *
+ * @return <code>true</code> if the analysis is completed, whether failed or
+ * not, <code>false</code> if it is currently running
+ */
+ boolean isComplete();
+
+ /**
+ * Query the requested callstacks and return the segments for the sampled times.
+ * The returned segments will be simply {@link ISegment} when there is no
+ * function at a given depth, or {@link ICalledFunction} when there is an actual
+ * function.
+ *
+ * @param collection
+ * The callstack entries to query
+ * @param times
+ * The complete list of times to query, they may not all be within
+ * this series's range
+ * @return A map of callstack depths to a list of segments.
+ */
+ default Multimap<CallStackDepth, ISegment> queryCallStacks(Collection<CallStackDepth> collection, Collection<Long> times) {
+ CallStackSeries callStackSeries = getCallStackSeries();
+ if (callStackSeries == null) {
+ return Objects.requireNonNull(ArrayListMultimap.create());
+ }
+ return callStackSeries.queryCallStacks(collection, times);
+ }
}
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/callstack/core/instrumented/statesystem/CallStackSeries.java b/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/callstack/core/instrumented/statesystem/CallStackSeries.java
index 1196cdcd9..5d669c434 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/callstack/core/instrumented/statesystem/CallStackSeries.java
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/callstack/core/instrumented/statesystem/CallStackSeries.java
@@ -13,6 +13,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -27,6 +28,7 @@ import org.eclipse.tracecompass.incubator.analysis.core.model.ModelManager;
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.CallStackDepth;
import org.eclipse.tracecompass.incubator.callstack.core.instrumented.ICalledFunction;
import org.eclipse.tracecompass.incubator.callstack.core.instrumented.statesystem.CallStackHostUtils.IHostIdProvider;
import org.eclipse.tracecompass.incubator.callstack.core.instrumented.statesystem.CallStackHostUtils.IHostIdResolver;
@@ -34,15 +36,20 @@ import org.eclipse.tracecompass.incubator.internal.callstack.core.Activator;
import org.eclipse.tracecompass.incubator.internal.callstack.core.instrumented.InstrumentedCallStackElement;
import org.eclipse.tracecompass.incubator.internal.callstack.core.instrumented.InstrumentedGroupDescriptor;
import org.eclipse.tracecompass.incubator.internal.callstack.core.instrumented.callgraph.CalledFunctionFactory;
+import org.eclipse.tracecompass.segmentstore.core.BasicSegment;
import org.eclipse.tracecompass.segmentstore.core.ISegment;
import org.eclipse.tracecompass.segmentstore.core.ISegmentStore;
import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem;
import org.eclipse.tracecompass.statesystem.core.exceptions.StateSystemDisposedException;
+import org.eclipse.tracecompass.statesystem.core.exceptions.TimeRangeException;
import org.eclipse.tracecompass.statesystem.core.interval.ITmfStateInterval;
import com.google.common.base.Function;
+import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Multimap;
/**
* A callstack series contain the information necessary to build all the
@@ -141,8 +148,10 @@ public class CallStackSeries implements ISegmentStore<ISegment> {
public int getThreadId(long time) {
ITmfStateInterval interval = fInterval;
int tid = fLastThreadId;
- if (interval != null && interval.intersects(time)) {
- return fLastThreadId;
+ // If interval is not null and either the tid does not vary in time or the
+ // interval intersects the requested time
+ if (interval != null && (!fVariesInTime || interval.intersects(time))) {
+ return tid;
}
try {
interval = fSs.querySingleState(time, fQuark);
@@ -402,6 +411,8 @@ public class CallStackSeries implements ISegmentStore<ISegment> {
private final String fName;
private final @Nullable IThreadIdResolver fResolver;
private final IHostIdResolver fHostResolver;
+ private final ITmfStateSystem fStateSystem;
+ private final Map<Integer, ICallStackElement> fRootElements = new HashMap<>();
/**
* Constructor
@@ -434,6 +445,7 @@ public class CallStackSeries implements ISegmentStore<ISegment> {
InstrumentedGroupDescriptor level = new InstrumentedGroupDescriptor(ss, patternPaths.get(i), prevLevel, symbolKeyLevelIndex == i ? true : false);
prevLevel = level;
}
+ fStateSystem = ss;
fRootGroup = prevLevel;
fName = name;
fResolver = threadResolver;
@@ -446,7 +458,7 @@ public class CallStackSeries implements ISegmentStore<ISegment> {
* @return The root elements of the callstack series
*/
public Collection<ICallStackElement> getRootElements() {
- return InstrumentedCallStackElement.getRootElements(fRootGroup, fHostResolver, fResolver);
+ return InstrumentedCallStackElement.getRootElements(fRootGroup, fHostResolver, fResolver, fRootElements);
}
/**
@@ -467,6 +479,53 @@ public class CallStackSeries implements ISegmentStore<ISegment> {
return fName;
}
+ /**
+ * Query the requested callstacks and return the segments for the sampled times.
+ * The returned segments will be simply {@link ISegment} when there is no
+ * function at a given depth, or {@link ICalledFunction} when there is an actual
+ * function.
+ *
+ * @param callstacks
+ * The callstack entries to query
+ * @param times
+ * The complete list of times to query, they may not all be within
+ * this series's range
+ * @return A map of callstack depths to a list of segments.
+ */
+ public Multimap<CallStackDepth, ISegment> queryCallStacks(Collection<CallStackDepth> callstacks, Collection<Long> times) {
+ Map<Integer, CallStackDepth> quarks = Maps.uniqueIndex(callstacks, cs -> cs.getQuark());
+ Multimap<CallStackDepth, ISegment> map = Objects.requireNonNull(ArrayListMultimap.create());
+ Collection<Long> queryTimes = getTimes(fStateSystem, times);
+ try {
+ Iterable<ITmfStateInterval> query2d = fStateSystem.query2D(quarks.keySet(), queryTimes);
+ for (ITmfStateInterval callInterval : query2d) {
+ CallStackDepth callStackDepth = Objects.requireNonNull(quarks.get(callInterval.getAttribute()));
+ if (callInterval.getValue() != null) {
+ map.put(callStackDepth, callStackDepth.getCallStack().getFunctionFromInterval(callInterval));
+ } else {
+ map.put(callStackDepth, new BasicSegment(callInterval.getStartTime(), callInterval.getEndTime() + 1));
+ }
+ }
+ } catch (IndexOutOfBoundsException | TimeRangeException | StateSystemDisposedException e) {
+ e.printStackTrace();
+ }
+ return map;
+ }
+
+ private static Collection<Long> getTimes(ITmfStateSystem ss, Collection<Long> times) {
+ // Filter and deduplicate the time stamps for the statesystem
+ long start = ss.getStartTime();
+ long end = ss.getCurrentEndTime();
+ // use a HashSet to deduplicate time stamps
+ Collection<Long> queryTimes = new HashSet<>();
+ for (long t : times) {
+ if (t >= start && t <= end) {
+ queryTimes.add(t);
+ }
+ }
+ return queryTimes;
+ }
+
// ---------------------------------------------------
// Segment store methods
// ---------------------------------------------------
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 0bf82880c..3613ea3d4 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
@@ -324,4 +324,17 @@ public abstract class InstrumentedCallStackAnalysis extends TmfStateSystemAnalys
return Collections.emptyList();
}
+ @Override
+ public boolean isComplete() {
+ // Initialization error, but the analysis is completed
+ if (!waitForInitialization()) {
+ return true;
+ }
+ ITmfStateSystem stateSystem = getStateSystem();
+ if (stateSystem == null) {
+ throw new IllegalStateException("The initialiation is complete, so the state system must not be null"); //$NON-NLS-1$
+ }
+ return stateSystem.waitUntilBuilt(0);
+ }
+
} \ No newline at end of file
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 07cdf5766..85d9ee65f 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
@@ -12,8 +12,11 @@ package org.eclipse.tracecompass.incubator.internal.callstack.core.instrumented;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.NoSuchElementException;
+import java.util.Objects;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.jdt.annotation.NonNull;
@@ -46,8 +49,8 @@ public class InstrumentedCallStackElement extends CallStackElement {
private final int fQuark;
private final IHostIdResolver fHostResolver;
private final @Nullable IThreadIdResolver fThreadIdResolver;
+ private final Map<Integer, ICallStackElement> fNextElements = new HashMap<>();
- private @Nullable Collection<ICallStackElement> fChildren;
private @Nullable CallStack fCallstack = null;
/**
@@ -83,19 +86,12 @@ public class InstrumentedCallStackElement extends CallStackElement {
@Override
public Collection<ICallStackElement> getChildren() {
- Collection<ICallStackElement> children = fChildren;
- if (children == null) {
- // Get the elements from the next group in the hierarchy
- @Nullable
- ICallStackGroupDescriptor nextGroup = getNextGroup();
- if (!(nextGroup instanceof InstrumentedGroupDescriptor)) {
- children = Collections.EMPTY_LIST;
- } else {
- children = getNextGroupElements((InstrumentedGroupDescriptor) nextGroup);
- }
- fChildren = children;
+ // Get the elements from the next group in the hierarchy
+ @Nullable ICallStackGroupDescriptor nextGroup = getNextGroup();
+ if (!(nextGroup instanceof InstrumentedGroupDescriptor)) {
+ return Collections.emptyList();
}
- return children;
+ return getNextGroupElements((InstrumentedGroupDescriptor) nextGroup);
}
@Override
@@ -112,18 +108,21 @@ public class InstrumentedCallStackElement extends CallStackElement {
* The host ID resolver
* @param resolver
* the thread ID resolver
+ * @param cache
+ * A cache of elements already built. It maps a quark to an element
+ * and the element will be returned if it has already been computed
* @return A collection of elements that are roots of the given callstack
* grouping
*/
- public static Collection<ICallStackElement> getRootElements(InstrumentedGroupDescriptor rootGroup, IHostIdResolver hostResolver, @Nullable IThreadIdResolver resolver) {
- return getNextElements(rootGroup, rootGroup.getStateSystem(), ITmfStateSystem.ROOT_ATTRIBUTE, hostResolver, resolver, null);
+ public static Collection<ICallStackElement> getRootElements(InstrumentedGroupDescriptor rootGroup, IHostIdResolver hostResolver, @Nullable IThreadIdResolver resolver, Map<Integer, ICallStackElement> cache) {
+ return getNextElements(rootGroup, rootGroup.getStateSystem(), ITmfStateSystem.ROOT_ATTRIBUTE, hostResolver, resolver, null, cache);
}
private Collection<ICallStackElement> getNextGroupElements(InstrumentedGroupDescriptor nextGroup) {
- return getNextElements(nextGroup, fStateSystem, fQuark, fHostResolver, fThreadIdResolver, this);
+ return getNextElements(nextGroup, fStateSystem, fQuark, fHostResolver, fThreadIdResolver, this, fNextElements);
}
- private static Collection<ICallStackElement> getNextElements(InstrumentedGroupDescriptor nextGroup, ITmfStateSystem stateSystem, int baseQuark, IHostIdResolver hostResolver, @Nullable IThreadIdResolver threadIdProvider, @Nullable InstrumentedCallStackElement parent) {
+ private static Collection<ICallStackElement> getNextElements(InstrumentedGroupDescriptor nextGroup, ITmfStateSystem stateSystem, int baseQuark, IHostIdResolver hostResolver, @Nullable IThreadIdResolver threadIdProvider, @Nullable InstrumentedCallStackElement parent, Map<Integer, ICallStackElement> cache) {
// Get the elements from the base quark at the given pattern
List<Integer> quarks = stateSystem.getQuarks(baseQuark, nextGroup.getSubPattern());
if (quarks.isEmpty()) {
@@ -134,10 +133,14 @@ public class InstrumentedCallStackElement extends CallStackElement {
// If the next level is null, then this is a callstack final element
List<ICallStackElement> elements = new ArrayList<>(quarks.size());
for (Integer quark : quarks) {
- InstrumentedCallStackElement element = new InstrumentedCallStackElement(hostResolver, stateSystem, quark,
- nextGroup, nextLevel, threadIdProvider, parent);
- if (nextGroup.isSymbolKeyGroup()) {
- element.setSymbolKeyElement(element);
+ ICallStackElement element = cache.get(quark);
+ if (element == null) {
+ element = new InstrumentedCallStackElement(hostResolver, stateSystem, quark,
+ nextGroup, nextLevel, threadIdProvider, parent);
+ if (nextGroup.isSymbolKeyGroup()) {
+ element.setSymbolKeyElement(element);
+ }
+ cache.put(quark, element);
}
elements.add(element);
}
@@ -225,15 +228,20 @@ public class InstrumentedCallStackElement extends CallStackElement {
*/
public CallStack getCallStack() {
CallStack callstack = fCallstack;
+ List<Integer> subAttributes = getStackQuarks();
if (callstack == null) {
IHostIdProvider hostProvider = fHostResolver.apply(this);
IThreadIdResolver threadIdResolver = fThreadIdResolver;
IThreadIdProvider threadIdProvider = threadIdResolver == null ? null : threadIdResolver.resolve(hostProvider, this);
- List<Integer> subAttributes = getStackQuarks();
callstack = new CallStack(getStateSystem(), subAttributes, this, hostProvider, threadIdProvider);
fCallstack = callstack;
+ } else {
+ // Update the callstack if attributes were added
+ if (callstack.getMaxDepth() < subAttributes.size() ) {
+ callstack.updateAttributes(subAttributes);
+ }
}
- return callstack;
+ return Objects.requireNonNull(callstack);
}
/**
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 8ea681768..da4851f3f 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
@@ -209,7 +209,7 @@ public class CallGraphAnalysis extends TmfAbstractAnalysisModule implements ICal
iterateOverCallstack(element, callStack, nextFunction, 2, aggregatedChild, model, start, end, monitor);
aggregatedChild.addFunctionCall(nextFunction);
// Add the kernel statuses if available
- Iterable<ProcessStatusInterval> kernelStatuses = callStack.getKernelStatuses(nextFunction, -1);
+ Iterable<ProcessStatusInterval> kernelStatuses = callStack.getKernelStatuses(nextFunction, Collections.emptyList());
for (ProcessStatusInterval status : kernelStatuses) {
aggregatedChild.addKernelStatus(status);
}
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/instrumented/provider/FlameChartDataProvider.java b/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/instrumented/provider/FlameChartDataProvider.java
new file mode 100644
index 000000000..885efab8e
--- /dev/null
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/instrumented/provider/FlameChartDataProvider.java
@@ -0,0 +1,740 @@
+/*******************************************************************************
+ * Copyright (c) 2018 École Polytechnique de Montréal
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.incubator.internal.callstack.core.instrumented.provider;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+
+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.os.linux.core.model.HostThread;
+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.incubator.callstack.core.base.ICallStackElement;
+import org.eclipse.tracecompass.incubator.callstack.core.flamechart.CallStack;
+import org.eclipse.tracecompass.incubator.callstack.core.instrumented.CallStackDepth;
+import org.eclipse.tracecompass.incubator.callstack.core.instrumented.ICalledFunction;
+import org.eclipse.tracecompass.incubator.callstack.core.instrumented.IFlameChartProvider;
+import org.eclipse.tracecompass.incubator.callstack.core.instrumented.statesystem.CallStackSeries;
+import org.eclipse.tracecompass.incubator.internal.callstack.core.instrumented.InstrumentedCallStackElement;
+import org.eclipse.tracecompass.incubator.internal.callstack.core.instrumented.provider.FlameChartEntryModel.EntryType;
+import org.eclipse.tracecompass.internal.analysis.os.linux.core.threadstatus.ThreadEntryModel;
+import org.eclipse.tracecompass.internal.analysis.os.linux.core.threadstatus.ThreadStatusDataProvider;
+import org.eclipse.tracecompass.internal.provisional.tmf.core.model.AbstractTmfTraceDataProvider;
+import org.eclipse.tracecompass.internal.provisional.tmf.core.model.CommonStatusMessage;
+import org.eclipse.tracecompass.internal.provisional.tmf.core.model.filters.SelectionTimeQueryFilter;
+import org.eclipse.tracecompass.internal.provisional.tmf.core.model.filters.TimeQueryFilter;
+import org.eclipse.tracecompass.internal.provisional.tmf.core.model.timegraph.ITimeGraphArrow;
+import org.eclipse.tracecompass.internal.provisional.tmf.core.model.timegraph.ITimeGraphDataProvider;
+import org.eclipse.tracecompass.internal.provisional.tmf.core.model.timegraph.ITimeGraphRowModel;
+import org.eclipse.tracecompass.internal.provisional.tmf.core.model.timegraph.ITimeGraphState;
+import org.eclipse.tracecompass.internal.provisional.tmf.core.model.timegraph.TimeGraphRowModel;
+import org.eclipse.tracecompass.internal.provisional.tmf.core.model.timegraph.TimeGraphState;
+import org.eclipse.tracecompass.internal.provisional.tmf.core.response.ITmfResponse;
+import org.eclipse.tracecompass.internal.provisional.tmf.core.response.ITmfResponse.Status;
+import org.eclipse.tracecompass.internal.provisional.tmf.core.response.TmfModelResponse;
+import org.eclipse.tracecompass.segmentstore.core.ISegment;
+import org.eclipse.tracecompass.statesystem.core.exceptions.StateSystemDisposedException;
+import org.eclipse.tracecompass.statesystem.core.exceptions.TimeRangeException;
+import org.eclipse.tracecompass.statesystem.core.interval.ITmfStateInterval;
+import org.eclipse.tracecompass.tmf.core.dataprovider.DataProviderManager;
+import org.eclipse.tracecompass.tmf.core.symbols.ISymbolProvider;
+import org.eclipse.tracecompass.tmf.core.symbols.SymbolProviderManager;
+import org.eclipse.tracecompass.tmf.core.symbols.SymbolProviderUtils;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
+import org.eclipse.tracecompass.tmf.core.util.Pair;
+
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Multimap;
+
+/**
+ * This class provides the data from an instrumented callstack analysis, in the
+ * form of a flamechart, ie the groups are returned hierarchically and leaf
+ * groups return their callstacks.
+ *
+ * @author Geneviève Bastien
+ */
+@SuppressWarnings("restriction")
+public class FlameChartDataProvider extends AbstractTmfTraceDataProvider implements ITimeGraphDataProvider<FlameChartEntryModel> {
+
+ /**
+ * Provider ID.
+ */
+ public static final String ID = "org.eclipse.tracecompass.incubator.internal.callstack.core.instrumented.provider.flamechart"; //$NON-NLS-1$
+ private static final AtomicLong ENTRY_ID = new AtomicLong();
+ /**
+ * Logger for Abstract Tree Data Providers.
+ */
+ private static final Logger LOGGER = TraceCompassLog.getLogger(FlameChartDataProvider.class);
+
+ private final Map<Long, FlameChartEntryModel> fEntries = new HashMap<>();
+ // Key is the row ID that requires linked data (for instance a kernel row) and
+ // value is the row being linked to (the one from the callstack)
+ private final BiMap<Long, Long> fLinkedEntries = HashBiMap.create();
+ private final Collection<ISymbolProvider> fProviders = new ArrayList<>();
+ private final BiMap<Long, CallStackDepth> fIdToCallstack = HashBiMap.create();
+ private final BiMap<Long, ICallStackElement> fIdToElement = HashBiMap.create();
+ private final long fTraceId = ENTRY_ID.getAndIncrement();
+
+ private static class TidInformation {
+ private final HostThread fTid;
+ private final long fStart;
+ private final long fEnd;
+ private final Long fLinked;
+
+ public TidInformation(HostThread hostThread, long start, long end, Long linked) {
+ fTid = hostThread;
+ fStart = start;
+ fEnd = end;
+ fLinked = linked;
+ }
+
+ public boolean intersects(ITimeGraphState state) {
+ return !(state.getStartTime() > fEnd || (state.getStartTime() + state.getDuration()) < fStart);
+ }
+
+ public boolean precedes(ITimeGraphState state) {
+ return (state.getStartTime() + state.getDuration() < fEnd);
+ }
+
+ public ITimeGraphState sanitize(ITimeGraphState state) {
+ if (state.getStartTime() < fStart || state.getStartTime() + state.getDuration() > fEnd) {
+ long start = Math.max(state.getStartTime(), fStart);
+ long end = Math.min(state.getStartTime() + state.getDuration(), fEnd);
+ String label = state.getLabel();
+ if (label != null) {
+ return new TimeGraphState(start, end - start, state.getValue(), label);
+ }
+ return new TimeGraphState(start, end - start, state.getValue());
+ }
+ return state;
+ }
+ }
+
+ private static class ThreadData {
+
+ private final ThreadStatusDataProvider fThreadDataProvider;
+ private final List<ThreadEntryModel> fThreadTree;
+ private final Status fStatus;
+
+ public ThreadData(ThreadStatusDataProvider dataProvider, List<ThreadEntryModel> threadTree, Status status) {
+ fThreadDataProvider = dataProvider;
+ fThreadTree = threadTree;
+ fStatus = status;
+ }
+
+ public @Nullable Map<String, String> fetchTooltip(int threadId, long time, @Nullable IProgressMonitor monitor) {
+ for (ThreadEntryModel entry : fThreadTree) {
+ if (entry.getThreadId() == threadId && entry.getStartTime() <= time && entry.getEndTime() >= time) {
+ TmfModelResponse<Map<String, String>> tooltip = fThreadDataProvider.fetchTooltip(new SelectionTimeQueryFilter(Collections.singletonList(time), Collections.singleton(entry.getId())), monitor);
+ return tooltip.getModel();
+ }
+ }
+ return null;
+ }
+
+ }
+
+ private final LoadingCache<Pair<Integer, ICalledFunction>, @Nullable String> fTimeEventNames = Objects.requireNonNull(CacheBuilder.newBuilder()
+ .maximumSize(1000)
+ .build(new CacheLoader<Pair<Integer, ICalledFunction>, @Nullable String>() {
+ @Override
+ public @Nullable String load(Pair<Integer, ICalledFunction> pidInterval) {
+ Integer pid = pidInterval.getFirst();
+ ICalledFunction interval = pidInterval.getSecond();
+
+ Object nameValue = interval.getSymbol();
+ Long address = null;
+ String name = null;
+ if (nameValue instanceof String) {
+ name = (String) nameValue;
+ try {
+ address = Long.parseLong(name, 16);
+ } catch (NumberFormatException e) {
+ // leave name as null
+ }
+ } else if (nameValue instanceof Integer) {
+ Integer intValue = (Integer) nameValue;
+ name = "0x" + Integer.toUnsignedString(intValue, 16); //$NON-NLS-1$
+ address = intValue.longValue();
+ } else if (nameValue instanceof Long) {
+ address = (long) nameValue;
+ name = "0x" + Long.toUnsignedString(address, 16); //$NON-NLS-1$
+ }
+ if (address != null) {
+ name = SymbolProviderUtils.getSymbolText(fProviders, pid, interval.getStart(), address);
+ }
+ return name;
+ }
+ }));
+
+ private final IFlameChartProvider fFcProvider;
+
+ private final String fAnalysisId;
+ private final ReentrantReadWriteLock fLock = new ReentrantReadWriteLock(false);
+ private @Nullable TmfModelResponse<List<FlameChartEntryModel>> fCached;
+ private @Nullable ThreadData fThreadData = null;
+
+ /**
+ * Constructor
+ *
+ * @param trace
+ * The trace for which this data provider applies
+ * @param module
+ * The flame chart provider encapsulated by this provider
+ * @param secondaryId
+ * The ID of the flame chart provider
+ */
+ public FlameChartDataProvider(ITmfTrace trace, IFlameChartProvider module, String secondaryId) {
+ super(trace);
+ fFcProvider = module;
+ fAnalysisId = secondaryId;
+ resetFunctionNames(new NullProgressMonitor());
+ }
+
+ @Override
+ public TmfModelResponse<List<ITimeGraphArrow>> fetchArrows(TimeQueryFilter filter, @Nullable IProgressMonitor monitor) {
+ // TODO Implement
+ return new TmfModelResponse<>(null, Status.COMPLETED, CommonStatusMessage.COMPLETED);
+ }
+
+ @Override
+ public TmfModelResponse<Map<String, String>> fetchTooltip(SelectionTimeQueryFilter filter, @Nullable IProgressMonitor monitor) {
+ try (FlowScopeLog scope = new FlowScopeLogBuilder(LOGGER, Level.FINE, "FlameChartDataProvider#fetchTooltip") //$NON-NLS-1$
+ .setCategory(getClass().getSimpleName()).build()) {
+ Map<Long, FlameChartEntryModel> entries = getSelectedEntries(filter);
+ if (entries.size() != 1) {
+ // Not the expected size of tooltip, just return empty
+ return new TmfModelResponse<>(null, Status.COMPLETED, CommonStatusMessage.COMPLETED);
+ }
+ Entry<@NonNull Long, @NonNull FlameChartEntryModel> entry = entries.entrySet().iterator().next();
+ Map<String, String> tooltip = getTooltip(entry, filter, monitor);
+
+ return new TmfModelResponse<>(tooltip, Status.COMPLETED, CommonStatusMessage.COMPLETED);
+ }
+ }
+
+ private @Nullable Map<String, String> getTooltip(Entry<Long, FlameChartEntryModel> entry, SelectionTimeQueryFilter filter, @Nullable IProgressMonitor monitor) {
+ FlameChartEntryModel value = Objects.requireNonNull(entry.getValue());
+ switch (value.getEntryType()) {
+ case FUNCTION:
+ {
+ CallStackDepth selectedDepth = fIdToCallstack.get(entry.getKey());
+ if (selectedDepth == null) {
+ return null;
+ }
+ Multimap<CallStackDepth, ISegment> csFunctions = fFcProvider.queryCallStacks(Collections.singleton(selectedDepth), Collections.singleton(filter.getStart()));
+ Collection<ISegment> functions = csFunctions.get(selectedDepth);
+ if (functions.isEmpty()) {
+ return null;
+ }
+ ISegment next = functions.iterator().next();
+ if (!(next instanceof ICalledFunction)) {
+ return null;
+ }
+ ICalledFunction currentFct = (ICalledFunction) next;
+ Map<String, String> tooltips = new HashMap<>();
+ int threadId = currentFct.getThreadId();
+ if (threadId > 0) {
+ tooltips.put(String.valueOf(Messages.FlameChartDataProvider_ThreadId), String.valueOf(threadId));
+ }
+ // TODO: Add symbol origin (library, language, etc) when better supported
+ return tooltips;
+ }
+ case KERNEL:
+ // Get the tooltip from the the ThreadStatusDataProvider
+ // First get the linked function to know which TID to retrieve
+ Long csId = fLinkedEntries.get(entry.getKey());
+ if (csId == null) {
+ return null;
+ }
+ CallStackDepth selectedDepth = fIdToCallstack.get(csId);
+ if (selectedDepth == null) {
+ return null;
+ }
+ int threadId = selectedDepth.getCallStack().getThreadId(filter.getStart());
+ ThreadData threadData = fThreadData;
+ if (threadData == null) {
+ return null;
+ }
+ return threadData.fetchTooltip(threadId, filter.getStart(), monitor);
+ case LEVEL:
+ // Fall-through
+ case TRACE:
+ // Fall-through
+ default:
+ return null;
+ }
+
+ }
+
+ @Override
+ public String getId() {
+ return ID + ':' + fAnalysisId;
+ }
+
+ // Get an entry for a quark
+ private long getEntryId(CallStackDepth stack) {
+ return fIdToCallstack.inverse().computeIfAbsent(stack, q -> ENTRY_ID.getAndIncrement());
+ }
+
+ private long getEntryId(ICallStackElement instrumentedCallStackElement) {
+ return fIdToElement.inverse().computeIfAbsent(instrumentedCallStackElement, q -> ENTRY_ID.getAndIncrement());
+ }
+
+ // Get a new entry for a kernel entry ID
+ private long getKernelEntryId(long baseId) {
+ return fLinkedEntries.inverse().computeIfAbsent(baseId, id -> ENTRY_ID.getAndIncrement());
+ }
+
+ @Override
+ public TmfModelResponse<List<FlameChartEntryModel>> fetchTree(TimeQueryFilter filter, @Nullable IProgressMonitor monitor) {
+ if (fCached != null) {
+ return fCached;
+ }
+
+ fLock.writeLock().lock();
+ try (FlowScopeLog scope = new FlowScopeLogBuilder(LOGGER, Level.FINE, "FlameChartDataProvider#fetchTree") //$NON-NLS-1$
+ .setCategory(getClass().getSimpleName()).build()) {
+ IFlameChartProvider fcProvider = fFcProvider;
+ boolean complete = fcProvider.isComplete();
+ CallStackSeries callstack = fcProvider.getCallStackSeries();
+ if (callstack == null) {
+ return new TmfModelResponse<>(null, ITmfResponse.Status.FAILED, CommonStatusMessage.ANALYSIS_INITIALIZATION_FAILED);
+ }
+ long start = getTrace().getStartTime().getValue();
+ long end = getTrace().readEnd().getValue();
+
+ // Initialize the first element of the tree
+ ImmutableList.Builder<FlameChartEntryModel> builder = ImmutableList.builder();
+ FlameChartEntryModel traceEntry = new FlameChartEntryModel(fTraceId, -1, getTrace().getName(), start, end, FlameChartEntryModel.EntryType.TRACE);
+ builder.add(traceEntry);
+
+ FlameChartEntryModel callStackRoot = traceEntry;
+ // If there is more than one callstack objects in the analysis, create a root
+ // per series
+ boolean needsKernel = false;
+ for (ICallStackElement element : callstack.getRootElements()) {
+ if (monitor != null && monitor.isCanceled()) {
+ return new TmfModelResponse<>(null, ITmfResponse.Status.CANCELLED, CommonStatusMessage.TASK_CANCELLED);
+ }
+ needsKernel |= processCallStackElement(element, builder, callStackRoot);
+ }
+ // Initialize the thread status data provider
+ if (needsKernel) {
+ prepareKernelData(monitor, start);
+ }
+ List<FlameChartEntryModel> tree = builder.build();
+ tree.forEach(entry -> fEntries.put(entry.getId(), entry));
+ if (complete) {
+ TmfModelResponse<List<FlameChartEntryModel>> response = new TmfModelResponse<>(tree,
+ ITmfResponse.Status.COMPLETED, CommonStatusMessage.COMPLETED);
+ fCached = response;
+ return response;
+ }
+ return new TmfModelResponse<>(tree, ITmfResponse.Status.RUNNING, CommonStatusMessage.RUNNING);
+ } finally {
+ fLock.writeLock().unlock();
+ }
+ }
+
+ private void prepareKernelData(@Nullable IProgressMonitor monitor, long start) {
+ ThreadData data = fThreadData;
+ if (data != null && data.fStatus.equals(Status.COMPLETED)) {
+ return;
+ }
+ // FIXME: Wouldn't work correctly if trace is an experiment as it would cover many hosts
+ Set<ITmfTrace> tracesForHost = TmfTraceManager.getInstance().getTracesForHost(getTrace().getHostId());
+ for (ITmfTrace trace : tracesForHost) {
+ ThreadStatusDataProvider dataProvider = DataProviderManager.getInstance().getDataProvider(trace, ThreadStatusDataProvider.ID, ThreadStatusDataProvider.class);
+ if (dataProvider != null) {
+ // Get the tree for the trace's current range
+ TmfModelResponse<List<ThreadEntryModel>> threadTreeResp = dataProvider.fetchTree(new TimeQueryFilter(start, Long.MAX_VALUE, 2), monitor);
+ List<ThreadEntryModel> threadTree = threadTreeResp.getModel();
+ if (threadTree != null) {
+ fThreadData = new ThreadData(dataProvider, threadTree, threadTreeResp.getStatus());
+ break;
+ }
+ }
+ }
+ }
+
+ private boolean processCallStackElement(ICallStackElement element, Builder<FlameChartEntryModel> builder, FlameChartEntryModel parentEntry) {
+
+ long elementId = getEntryId(element);
+ FlameChartEntryModel entry = new FlameChartEntryModel(elementId, parentEntry.getId(), element.getName(), parentEntry.getStartTime(), parentEntry.getEndTime(), FlameChartEntryModel.EntryType.LEVEL);
+ builder.add(entry);
+
+ boolean needsKernel = false;
+
+ // Is this an intermediate or leaf element
+ if ((element instanceof InstrumentedCallStackElement) && element.isLeaf()) {
+ // For the leaf element, add the callstack entries
+ InstrumentedCallStackElement finalElement = (InstrumentedCallStackElement) element;
+ CallStack callStack = finalElement.getCallStack();
+ for (int depth = 0; depth < callStack.getMaxDepth(); depth++) {
+ FlameChartEntryModel flameChartEntry = new FlameChartEntryModel(getEntryId(new CallStackDepth(callStack, depth + 1)), entry.getId(), element.getName(), parentEntry.getStartTime(), parentEntry.getEndTime(),
+ FlameChartEntryModel.EntryType.FUNCTION, depth + 1);
+ builder.add(flameChartEntry);
+ if (depth == 0 && callStack.hasKernelStatuses()) {
+ needsKernel = true;
+ builder.add(new FlameChartEntryModel(getKernelEntryId(flameChartEntry.getId()), entry.getId(), String.valueOf(Messages.FlameChartDataProvider_KernelStatusTitle), parentEntry.getStartTime(), parentEntry.getEndTime(), FlameChartEntryModel.EntryType.KERNEL));
+ }
+ }
+ return needsKernel;
+ }
+ // Intermediate element, process children
+ for (ICallStackElement child : element.getChildren()) {
+ needsKernel |= processCallStackElement(child, builder, entry);
+ }
+ return needsKernel;
+ }
+
+ // Get the selected entries with the quark
+ private BiMap<Long, FlameChartEntryModel> getSelectedEntries(SelectionTimeQueryFilter filter) {
+ fLock.readLock().lock();
+ try {
+ BiMap<Long, FlameChartEntryModel> selectedEntries = HashBiMap.create();
+
+ for (Long selectedItem : filter.getSelectedItems()) {
+ FlameChartEntryModel entryModel = fEntries.get(selectedItem);
+ if (entryModel != null) {
+ selectedEntries.put(selectedItem, entryModel);
+ }
+ }
+ return selectedEntries;
+ } finally {
+ fLock.readLock().unlock();
+ }
+ }
+
+ @Override
+ public TmfModelResponse<List<ITimeGraphRowModel>> fetchRowModel(SelectionTimeQueryFilter filter, @Nullable IProgressMonitor monitor) {
+ try (FlowScopeLog scope = new FlowScopeLogBuilder(LOGGER, Level.FINE, "FlameChartDataProvider#fetchRowModel") //$NON-NLS-1$
+ .setCategory(getClass().getSimpleName()).build()) {
+
+ Map<Long, FlameChartEntryModel> entries = getSelectedEntries(filter);
+ if (entries.size() == 1 && filter.getTimesRequested().length == 2) {
+ // this is a request for a follow event.
+ Entry<@NonNull Long, @NonNull FlameChartEntryModel> entry = entries.entrySet().iterator().next();
+ if (filter.getStart() == Long.MIN_VALUE) {
+ return new TmfModelResponse<>(getFollowEvent(entry, filter.getEnd(), false), ITmfResponse.Status.COMPLETED, CommonStatusMessage.COMPLETED);
+ } else if (filter.getEnd() == Long.MAX_VALUE) {
+ return new TmfModelResponse<>(getFollowEvent(entry, filter.getStart(), true), ITmfResponse.Status.COMPLETED, CommonStatusMessage.COMPLETED);
+ }
+ }
+ // For each kernel status entry, add the first row of the callstack
+ addRequiredCallstacks(entries);
+
+ SubMonitor subMonitor = SubMonitor.convert(monitor, "FlameChartDataProvider#fetchRowModel", 2); //$NON-NLS-1$
+ IFlameChartProvider fcProvider = fFcProvider;
+ boolean complete = fcProvider.isComplete();
+
+ Map<Long, List<ITimeGraphState>> csRows = getCallStackRows(filter, entries, subMonitor);
+ if (csRows == null) {
+ // getRowModel returns null if the query was cancelled.
+ return new TmfModelResponse<>(null, ITmfResponse.Status.CANCELLED, CommonStatusMessage.TASK_CANCELLED);
+ }
+ List<ITimeGraphRowModel> collect = csRows.entrySet().stream().map(entry -> new TimeGraphRowModel(entry.getKey(), entry.getValue())).collect(Collectors.toList());
+ return new TmfModelResponse<>(collect, complete ? Status.COMPLETED : Status.RUNNING,
+ complete ? CommonStatusMessage.COMPLETED : CommonStatusMessage.RUNNING);
+ } catch (IndexOutOfBoundsException | TimeRangeException | StateSystemDisposedException e) {
+ return new TmfModelResponse<>(null, Status.FAILED, String.valueOf(e.getMessage()));
+ }
+ }
+
+ private void addRequiredCallstacks(Map<Long, FlameChartEntryModel> entries) {
+ fLock.readLock().lock();
+ try {
+ Map<Long, FlameChartEntryModel> toAdd = new HashMap<>();
+ for (Long id : entries.keySet()) {
+ Long csId = fLinkedEntries.get(id);
+ if (csId != null) {
+ FlameChartEntryModel entry = fEntries.get(csId);
+ if (entry != null) {
+ toAdd.put(csId, entry);
+ }
+ }
+ }
+ entries.putAll(toAdd);
+ } finally {
+ fLock.readLock().unlock();
+ }
+ }
+
+ private @Nullable Map<Long, List<ITimeGraphState>> getCallStackRows(SelectionTimeQueryFilter filter, Map<Long, FlameChartEntryModel> entries, SubMonitor subMonitor) throws IndexOutOfBoundsException, TimeRangeException, StateSystemDisposedException {
+
+ // Get the data for the model entries that are of type function
+ Map<Long, List<ITimeGraphState>> rows = new HashMap<>();
+ List<TidInformation> tids = new ArrayList<>();
+ Map<Long, CallStackDepth> csEntries = new HashMap<>();
+ for (Entry<Long, @NonNull FlameChartEntryModel> entry : entries.entrySet()) {
+ CallStackDepth selectedDepth = fIdToCallstack.get(entry.getKey());
+ if (selectedDepth != null && entry.getValue().getEntryType().equals(EntryType.FUNCTION)) {
+ csEntries.put(entry.getKey(), selectedDepth);
+ }
+ }
+
+ long[] timesRequested = filter.getTimesRequested();
+ // Prepare the list of times
+ List<Long> times = new ArrayList<>();
+ for (long time : timesRequested) {
+ times.add(time);
+ }
+ Collections.sort(times);
+ Multimap<CallStackDepth, ISegment> csFunctions = fFcProvider.queryCallStacks(csEntries.values(), times);
+
+ for (Map.Entry<Long, CallStackDepth> entry : csEntries.entrySet()) {
+ if (subMonitor.isCanceled()) {
+ return null;
+ }
+ Collection<ISegment> states = csFunctions.get(entry.getValue());
+
+ // Create the time graph states for this row
+ List<ITimeGraphState> eventList = new ArrayList<>(states.size());
+ states.forEach(state -> eventList.add(createTimeGraphState(state)));
+ eventList.sort(Comparator.comparingLong(ITimeGraphState::getStartTime));
+ rows.put(entry.getKey(), eventList);
+
+ // See if any more row needs to be filled with these function's data
+ // TODO: Kernel might not be the only type of linked entries (for instance,
+ // locations of sampling data)
+ Long linked = fLinkedEntries.inverse().get(entry.getKey());
+ if (linked == null || !entries.containsKey(linked)) {
+ continue;
+ }
+ tids.addAll(getKernelTids(entry.getValue(), states, linked));
+
+ }
+ // Add an empty state to rows that do not have data
+ for (Long key : entries.keySet()) {
+ if (!rows.containsKey(key)) {
+ rows.put(key, Collections.emptyList());
+ }
+ }
+ if (!tids.isEmpty()) {
+ rows.putAll(getKernelStates(tids, times, subMonitor));
+ }
+ subMonitor.worked(1);
+ return rows;
+ }
+
+ private Map<Long, List<ITimeGraphState>> getKernelStates(List<TidInformation> tids, List<Long> times, SubMonitor monitor) {
+ // Get the thread statuses from the thread status provider
+ ThreadData threadData = fThreadData;
+ if (threadData == null) {
+ return Collections.emptyMap();
+
+ }
+ List<ThreadEntryModel> tree = threadData.fThreadTree;
+
+ // FIXME: A callstack analysis may be for an experiment that span many hosts,
+ // the thread data provider will be a composite and the models may be for
+ // different host IDs. But for now, suppose the callstack is a composite also
+ // and the trace filtered the right host.
+ BiMap<Long, Integer> threadModelIds = filterThreads(tree, tids);
+ SelectionTimeQueryFilter tidFilter = new SelectionTimeQueryFilter(times, threadModelIds.keySet());
+ TmfModelResponse<List<ITimeGraphRowModel>> rowModel = threadData.fThreadDataProvider.fetchRowModel(tidFilter, monitor);
+ List<ITimeGraphRowModel> rowModels = rowModel.getModel();
+ if (rowModel.getStatus().equals(Status.CANCELLED) || rowModel.getStatus().equals(Status.FAILED) || rowModels == null) {
+ return Collections.emptyMap();
+ }
+ return mapThreadStates(rowModels, threadModelIds, tids);
+ }
+
+ private static Map<Long, List<ITimeGraphState>> mapThreadStates(List<ITimeGraphRowModel> rowModels, BiMap<Long, Integer> threadModelIds, List<TidInformation> tids) {
+ ImmutableMap<Long, ITimeGraphRowModel> statusRows = Maps.uniqueIndex(rowModels, m -> m.getEntryID());
+ // Match the states of thread status to the requested tid lines
+ Long prevId = -1L;
+ List<ITimeGraphState> states = null;
+ Map<Long, List<ITimeGraphState>> kernelStatuses = new HashMap<>();
+ // The tid information data are ordered by id and times
+ for (TidInformation tidInfo : tids) {
+ Long tidEntryId = threadModelIds.inverse().get(tidInfo.fTid.getTid());
+ if (tidEntryId == null) {
+ continue;
+ }
+ ITimeGraphRowModel rowModel = statusRows.get(tidEntryId);
+ if (tidInfo.fLinked != prevId || states == null) {
+ if (states != null) {
+ kernelStatuses.put(prevId, states);
+ }
+ states = new ArrayList<>();
+ }
+ rowModel.getStates();
+ for (ITimeGraphState state : rowModel.getStates()) {
+ if (tidInfo.intersects(state)) {
+ states.add(tidInfo.sanitize(state));
+ }
+ if (!tidInfo.precedes(state)) {
+ break;
+ }
+ }
+ prevId = tidInfo.fLinked;
+ }
+ if (states != null) {
+ kernelStatuses.put(prevId, states);
+ }
+ return kernelStatuses;
+ }
+
+ private static BiMap<Long, Integer> filterThreads(List<ThreadEntryModel> model, List<TidInformation> tids) {
+ // Get the entry model IDs that match requested tids
+ BiMap<Long, Integer> tidEntries = HashBiMap.create();
+ Set<Integer> selectedTids = new HashSet<>();
+ for (TidInformation tidInfo : tids) {
+ selectedTids.add(tidInfo.fTid.getTid());
+ }
+ for (ThreadEntryModel entryModel : model) {
+ if (selectedTids.contains(entryModel.getThreadId())) {
+ try {
+ tidEntries.put(entryModel.getId(), entryModel.getThreadId());
+ } catch (IllegalArgumentException e) {
+ // FIXME: There may be many entries for one tid, don't rely on exception for
+ // real workflow. Works for now.
+ }
+ }
+ }
+ return tidEntries;
+ }
+
+ private static Collection<TidInformation> getKernelTids(CallStackDepth callStackDepth, Collection<ISegment> states, Long linked) {
+
+ List<TidInformation> tids = new ArrayList<>();
+ CallStack callStack = callStackDepth.getCallStack();
+ if (!callStack.isTidVariable()) {
+ // Find the time of the first function to know which timestamp to query
+ HostThread hostThread = callStack.getHostThread();
+ if (hostThread != null) {
+ tids.add(new TidInformation(hostThread, Long.MIN_VALUE, Long.MAX_VALUE, linked));
+ }
+ return tids;
+ }
+ // Get the thread IDs for all functions
+ for (ISegment state : states) {
+ if (!(state instanceof ICalledFunction)) {
+ continue;
+ }
+ ICalledFunction function = (ICalledFunction) state;
+ HostThread hostThread = callStack.getHostThread(function.getStart());
+ if (hostThread != null) {
+ tids.add(new TidInformation(hostThread, function.getStart(), function.getEnd(), linked));
+ }
+ }
+
+ return tids;
+ }
+
+ private ITimeGraphState createTimeGraphState(ISegment state) {
+ if (!(state instanceof ICalledFunction)) {
+ return new TimeGraphState(state.getStart(), state.getLength(), Integer.MIN_VALUE);
+ }
+ ICalledFunction function = (ICalledFunction) state;
+ Object value = function.getSymbol();
+ Integer pid = function.getProcessId();
+ String name = String.valueOf(fTimeEventNames.getUnchecked(new Pair<>(pid, function)));
+ return new TimeGraphState(function.getStart(), function.getLength(), value.hashCode(), name);
+ }
+
+ /**
+ * Invalidate the function names cache and load the symbol providers. This
+ * function should be used at the beginning of the provider, or whenever new
+ * symbol providers are added
+ *
+ * @param monitor
+ * A progress monitor to follow this operation
+ */
+ public void resetFunctionNames(IProgressMonitor monitor) {
+ fTimeEventNames.invalidateAll();
+ synchronized (fProviders) {
+ Collection<@NonNull ISymbolProvider> symbolProviders = SymbolProviderManager.getInstance().getSymbolProviders(getTrace());
+ SubMonitor sub = SubMonitor.convert(monitor, "CallStackDataProvider#resetFunctionNames", symbolProviders.size()); //$NON-NLS-1$
+ fProviders.clear();
+ for (ISymbolProvider symbolProvider : symbolProviders) {
+ fProviders.add(symbolProvider);
+ symbolProvider.loadConfiguration(sub);
+ sub.worked(1);
+ }
+ }
+ }
+
+ /**
+ * Get the next or previous interval for a call stack entry ID, time and
+ * direction
+ *
+ * @param entry
+ * whose key is the ID and value is the quark for the entry whose
+ * next / previous state we are searching for
+ * @param time
+ * selection start time
+ * @param forward
+ * if going to next or previous
+ * @return the next / previous state encapsulated in a row if it exists, else
+ * null
+ */
+ private @Nullable List<ITimeGraphRowModel> getFollowEvent(Entry<Long, FlameChartEntryModel> entry, long time, boolean forward) {
+ FlameChartEntryModel value = Objects.requireNonNull(entry.getValue());
+ switch (value.getEntryType()) {
+ case FUNCTION:
+ CallStackDepth selectedDepth = fIdToCallstack.get(entry.getKey());
+ if (selectedDepth == null) {
+ return null;
+ }
+ // Ask the callstack the depth at the current time
+ ITmfStateInterval nextDepth = selectedDepth.getCallStack().getNextDepth(time, forward);
+ if (nextDepth == null) {
+ return null;
+ }
+ Object depthVal = nextDepth.getValue();
+ int depth = (depthVal instanceof Number) ? ((Number) depthVal).intValue() : 0;
+ TimeGraphState state = new TimeGraphState(nextDepth.getStartTime(), nextDepth.getEndTime() - nextDepth.getStartTime(), depth);
+ TimeGraphRowModel row = new TimeGraphRowModel(entry.getKey(), Collections.singletonList(state));
+ return Collections.singletonList(row);
+
+ case KERNEL:
+ break;
+ case LEVEL:
+ // Fall-through
+ case TRACE:
+ // Fall-through
+ default:
+ return null;
+ }
+ return null;
+ }
+
+}
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/instrumented/provider/FlameChartDataProviderFactory.java b/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/instrumented/provider/FlameChartDataProviderFactory.java
new file mode 100644
index 000000000..c2cab0169
--- /dev/null
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/instrumented/provider/FlameChartDataProviderFactory.java
@@ -0,0 +1,80 @@
+/*******************************************************************************
+ * Copyright (c) 2018 École Polytechnique de Montréal
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.incubator.internal.callstack.core.instrumented.provider;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Objects;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.incubator.callstack.core.instrumented.IFlameChartProvider;
+import org.eclipse.tracecompass.internal.provisional.tmf.core.model.tree.ITmfTreeDataModel;
+import org.eclipse.tracecompass.internal.provisional.tmf.core.model.tree.ITmfTreeDataProvider;
+import org.eclipse.tracecompass.internal.provisional.tmf.core.model.xy.TmfTreeXYCompositeDataProvider;
+import org.eclipse.tracecompass.tmf.core.analysis.IAnalysisModule;
+import org.eclipse.tracecompass.tmf.core.dataprovider.IDataProviderFactory;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
+import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils;
+
+import com.google.common.collect.Iterables;
+
+/**
+ * Factory for the flame chart data provider
+ *
+ * @author Geneviève Bastien
+ */
+public class FlameChartDataProviderFactory implements IDataProviderFactory {
+
+ @Override
+ public @Nullable ITmfTreeDataProvider<? extends ITmfTreeDataModel> createProvider(ITmfTrace trace) {
+ // Need the analysis
+ return null;
+ }
+
+ @Override
+ public @Nullable ITmfTreeDataProvider<? extends ITmfTreeDataModel> createProvider(ITmfTrace trace, String secondaryId) {
+ // Create with the trace or experiment first
+ ITmfTreeDataProvider<? extends ITmfTreeDataModel> provider = create(trace, secondaryId);
+ if (provider != null) {
+ return provider;
+ }
+ // Otherwise, see if it's an experiment and create a composite if that's the
+ // case
+ Collection<ITmfTrace> traces = TmfTraceManager.getTraceSet(trace);
+ if (traces.size() > 1) {
+ // Try creating a composite only if there are many traces, otherwise, the
+ // previous call to create should have returned the data provider
+ return TmfTreeXYCompositeDataProvider.create(traces, Objects.requireNonNull("FlameChart"), FlameChartDataProvider.ID, secondaryId);
+ }
+ return null;
+
+ }
+
+ 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<IFlameChartProvider> modules = TmfTraceUtils.getAnalysisModulesOfClass(trace, IFlameChartProvider.class);
+ Iterable<IFlameChartProvider> filteredModules = Iterables.filter(modules, m -> ((IAnalysisModule) m).getId().equals(secondaryId));
+ Iterator<IFlameChartProvider> iterator = filteredModules.iterator();
+ if (iterator.hasNext()) {
+ IFlameChartProvider module = iterator.next();
+ if (iterator.hasNext()) {
+ // 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 FlameChartDataProvider(trace, module, secondaryId);
+ }
+ return null;
+ }
+
+}
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/instrumented/provider/FlameChartEntryModel.java b/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/instrumented/provider/FlameChartEntryModel.java
new file mode 100644
index 000000000..c82af1740
--- /dev/null
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/instrumented/provider/FlameChartEntryModel.java
@@ -0,0 +1,138 @@
+/**********************************************************************
+ * Copyright (c) 2018 École Polytechnique de Montréal
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ **********************************************************************/
+
+package org.eclipse.tracecompass.incubator.internal.callstack.core.instrumented.provider;
+
+import java.util.Objects;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.internal.provisional.tmf.core.model.timegraph.TimeGraphEntryModel;
+
+/**
+ * {@link TimeGraphEntryModel} for the Flame chart data
+ *
+ * @author Geneviève Bastien
+ */
+@SuppressWarnings("restriction")
+public class FlameChartEntryModel extends TimeGraphEntryModel {
+
+ /**
+ * An enumeration for the type of flame chart entries
+ */
+ public enum EntryType {
+ /**
+ * A descriptive entry, for example the one for the trace
+ */
+ TRACE,
+ /**
+ * Represent a group of the callstack analysis
+ */
+ LEVEL,
+ /**
+ * Represent an entry with function data, the actual callstack data
+ */
+ FUNCTION,
+ /**
+ * This entry will show the kernel statuses for the TID running the callstack.
+ * Will not always be present
+ */
+ KERNEL
+ }
+
+ private final EntryType fEntryType;
+ private final int fDepth;
+
+ /**
+ * Constructor
+ *
+ * @param id
+ * unique ID for this {@link FlameChartEntryModel}
+ * @param parentId
+ * parent's ID to build the tree
+ * @param name
+ * entry's name
+ * @param startTime
+ * entry's start time
+ * @param endTime
+ * entry's end time
+ * @param entryType
+ * The type of this entry
+ */
+ public FlameChartEntryModel(long id, long parentId, String name, long startTime, long endTime, EntryType entryType) {
+ super(id, parentId, name, startTime, endTime);
+ fEntryType = entryType;
+ fDepth = -1;
+ }
+
+ /**
+ * Constructor
+ *
+ * @param elementId
+ * unique ID for this {@link FlameChartEntryModel}
+ * @param parentId
+ * parent's ID to build the tree
+ * @param name
+ * entry's name
+ * @param startTime
+ * entry's start time
+ * @param endTime
+ * entry's end time
+ * @param entryType
+ * The type of this entry
+ * @param depth
+ * entry's PID or TID if is a thread
+ */
+ public FlameChartEntryModel(long elementId, long parentId, String name, long startTime, long endTime, EntryType entryType, int depth) {
+ super(elementId, parentId, name, startTime, endTime);
+ fEntryType = entryType;
+ fDepth = depth;
+ }
+
+ /**
+ * Getter for the entry type
+ *
+ * @return The type of entry.
+ */
+ public EntryType getEntryType() {
+ return fEntryType;
+ }
+
+ /**
+ * Get the depth of this entry
+ *
+ * @return The depth of this entry
+ */
+ public int getDepth() {
+ return fDepth;
+ }
+
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (!super.equals(obj)) {
+ // nullness, class, name, ids
+ return false;
+ }
+ if (!(obj instanceof FlameChartEntryModel)) {
+ return false;
+ }
+ FlameChartEntryModel other = (FlameChartEntryModel) obj;
+ return fEntryType == other.fEntryType;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), fEntryType);
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + ' ' + fEntryType.toString();
+ }
+
+}
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/instrumented/provider/Messages.java b/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/instrumented/provider/Messages.java
new file mode 100644
index 000000000..8d77f2c3c
--- /dev/null
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/instrumented/provider/Messages.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * Copyright (c) 2018 École Polytechnique de Montréal
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.incubator.internal.callstack.core.instrumented.provider;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * Message bundle for the call stack state provider.
+ */
+public class Messages extends NLS {
+
+ private static final String BUNDLE_NAME = Messages.class.getPackage().getName() + ".messages"; //$NON-NLS-1$
+
+ /** Title of the thread ID tooltip key */
+ public static @Nullable String FlameChartDataProvider_ThreadId = null;
+
+ /** Title of kernel status rows */
+ public static @Nullable String FlameChartDataProvider_KernelStatusTitle;
+
+ static {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+ }
+
+ private Messages() {
+ }
+
+}
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/instrumented/provider/messages.properties b/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/instrumented/provider/messages.properties
new file mode 100644
index 000000000..cfc5acd59
--- /dev/null
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/instrumented/provider/messages.properties
@@ -0,0 +1,11 @@
+###############################################################################
+# Copyright (c) 2018 École Polytechnique de Montréal
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+###############################################################################
+
+FlameChartDataProvider_KernelStatusTitle=Kernel statuses
+FlameChartDataProvider_ThreadId=TID
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/instrumented/provider/package-info.java b/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/instrumented/provider/package-info.java
new file mode 100644
index 000000000..c3da9b075
--- /dev/null
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/instrumented/provider/package-info.java
@@ -0,0 +1,11 @@
+/*******************************************************************************
+ * Copyright (c) 2018 École Polytechnique de Montréal
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.eclipse.tracecompass.incubator.internal.callstack.core.instrumented.provider;
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 fc28f1d0b..b640e21a4 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
@@ -359,4 +359,17 @@ public class CallstackXmlAnalysis extends TmfAbstractAnalysisModule implements I
}
}
+ @Override
+ public boolean isComplete() {
+ // Initialization error, but the analysis is completed
+ if (!waitForInitialization()) {
+ return true;
+ }
+ Iterator<ITmfStateSystem> iterator = getStateSystems().iterator();
+ if (!iterator.hasNext()) {
+ throw new IllegalStateException("The initialization is complete, so the state system must not be null"); //$NON-NLS-1$
+ }
+ return iterator.next().waitUntilBuilt(0);
+ }
+
}
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/views/flamechart/ProcessStatusEntry.java b/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/views/flamechart/ProcessStatusEntry.java
index 9b29b6acb..e69c5dc6b 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/views/flamechart/ProcessStatusEntry.java
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/views/flamechart/ProcessStatusEntry.java
@@ -18,6 +18,7 @@ import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.tracecompass.incubator.analysis.core.concepts.ProcessStatusInterval;
import org.eclipse.tracecompass.incubator.callstack.core.flamechart.CallStack;
import org.eclipse.tracecompass.incubator.callstack.core.instrumented.ICalledFunction;
+import org.eclipse.tracecompass.statesystem.core.StateSystemUtils;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeEvent;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeEvent;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeGraphEntry;
@@ -76,7 +77,7 @@ public class ProcessStatusEntry extends TimeGraphEntry {
ICalledFunction function = fcEvent.getFunction();
// FIXME: This gets all the statuses, that can be big for large time ranges. Use
// a method with resolution when it is available
- Iterable<@NonNull ProcessStatusInterval> statuses = fCallStack.getKernelStatuses(function, resolution);
+ Iterable<@NonNull ProcessStatusInterval> statuses = fCallStack.getKernelStatuses(function, StateSystemUtils.getTimes(startTime, endTime, resolution));
for (ProcessStatusInterval status : statuses) {
events.add(new TimeEvent(this, status.getStart(), status.getLength(), status.getProcessStatus().getStateValue().unboxInt()));
}

Back to the top