blob: af1efde445b56917178c8cd80f85e01b4e17a542 [file] [log] [blame]
/*******************************************************************************
* 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 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.callstack.core.tests.stubs.FlameDataProviderTestUtils;
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.tmf.core.model.filters.FetchParametersUtils;
import org.eclipse.tracecompass.tmf.core.dataprovider.IDataProviderDescriptor;
import org.eclipse.tracecompass.tmf.core.model.filters.SelectionTimeQueryFilter;
import org.eclipse.tracecompass.tmf.core.model.filters.TimeQueryFilter;
import org.eclipse.tracecompass.tmf.core.model.timegraph.ITimeGraphRowModel;
import org.eclipse.tracecompass.tmf.core.model.timegraph.ITimeGraphState;
import org.eclipse.tracecompass.tmf.core.model.timegraph.TimeGraphModel;
import org.eclipse.tracecompass.tmf.core.model.timegraph.TimeGraphState;
import org.eclipse.tracecompass.tmf.core.model.tree.TmfTreeModel;
import org.eclipse.tracecompass.tmf.core.response.ITmfResponse;
import org.eclipse.tracecompass.tmf.core.response.TmfModelResponse;
import org.junit.Test;
import com.google.common.collect.ImmutableList;
/**
* 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 descriptors built in the flame chart data provider factory
*/
@Test
public void testGetDescriptors() {
FlameChartDataProviderFactory dataProviderFactory = new FlameChartDataProviderFactory();
Collection<IDataProviderDescriptor> descriptors = dataProviderFactory.getDescriptors(getTrace());
assertEquals(descriptors.size(), 2);
for (IDataProviderDescriptor descriptor : descriptors) {
switch(descriptor.getId()) {
case "org.eclipse.tracecompass.incubator.internal.callstack.core.instrumented.provider.flamechart:org.eclipse.tracecompass.incubator.callstack.analysis.test":
assertEquals("FlameChart Test Callstack", descriptor.getName());
assertEquals(IDataProviderDescriptor.ProviderType.TIME_GRAPH, descriptor.getType());
assertEquals("Show FlameChart provided by Analysis module: Test Callstack", descriptor.getDescription());
break;
case "org.eclipse.tracecompass.incubator.internal.callstack.core.instrumented.provider.flamechart:callstack.analysis":
assertEquals("FlameChart Test XML callstack", descriptor.getName());
assertEquals(IDataProviderDescriptor.ProviderType.TIME_GRAPH, descriptor.getType());
assertEquals("Show FlameChart provided by Analysis module: Test XML callstack", descriptor.getDescription());
break;
default:
fail("Unknown Entry" + descriptor.getId());
break;
}
}
}
/**
* Test getting the tree from the flame chart data provider
*/
@Test
public void testFetchTree() {
FlameChartDataProvider dataProvider = getDataProvider();
TmfModelResponse<@NonNull TmfTreeModel<@NonNull FlameChartEntryModel>> responseTree = dataProvider.fetchTree(FetchParametersUtils.timeQueryToMap(new TimeQueryFilter(0, Long.MAX_VALUE, 2)), new NullProgressMonitor());
assertTrue(responseTree.getStatus().equals(ITmfResponse.Status.COMPLETED));
// Test the size of the tree
TmfTreeModel<@NonNull FlameChartEntryModel> model = responseTree.getModel();
assertNotNull(model);
List<@NonNull FlameChartEntryModel> modelEntries = model.getEntries();
assertEquals(18, modelEntries.size());
String traceName = getTrace().getName();
// Test the hierarchy of the tree
for (FlameChartEntryModel entry : modelEntries) {
FlameChartEntryModel parent = FlameDataProviderTestUtils.findEntryById(modelEntries, 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;
}
}
}
/**
* Test getting the model from the flame chart data provider
*/
@Test
public void testFetchModel() {
FlameChartDataProvider dataProvider = getDataProvider();
TmfModelResponse<@NonNull TmfTreeModel<@NonNull FlameChartEntryModel>> responseTree = dataProvider.fetchTree(FetchParametersUtils.timeQueryToMap(new TimeQueryFilter(0, Long.MAX_VALUE, 2)), new NullProgressMonitor());
assertTrue(responseTree.getStatus().equals(ITmfResponse.Status.COMPLETED));
TmfTreeModel<@NonNull FlameChartEntryModel> model = responseTree.getModel();
assertNotNull(model);
List<@NonNull FlameChartEntryModel> modelEntries = model.getEntries();
// Find the entries corresponding to threads 3 and 6 (along with pid 5)
Set<@NonNull Long> selectedIds = new HashSet<>();
// Thread 3
FlameChartEntryModel tid3 = FlameDataProviderTestUtils.findEntryByNameAndType(modelEntries, "3", EntryType.LEVEL);
assertNotNull(tid3);
selectedIds.add(tid3.getId());
List<FlameChartEntryModel> tid3Children = FlameDataProviderTestUtils.findEntriesByParent(modelEntries, tid3.getId());
assertEquals(2, tid3Children.size());
tid3Children.forEach(child -> selectedIds.add(child.getId()));
// Pid 5
FlameChartEntryModel pid5 = FlameDataProviderTestUtils.findEntryByNameAndType(modelEntries, "5", EntryType.LEVEL);
assertNotNull(pid5);
selectedIds.add(pid5.getId());
// Thread 6
FlameChartEntryModel tid6 = FlameDataProviderTestUtils.findEntryByNameAndType(modelEntries, "6", EntryType.LEVEL);
assertNotNull(tid6);
selectedIds.add(tid6.getId());
List<FlameChartEntryModel> tid6Children = FlameDataProviderTestUtils.findEntriesByParent(modelEntries, tid6.getId());
assertEquals(3, tid6Children.size());
tid6Children.forEach(child -> selectedIds.add(child.getId()));
// Get the row model for those entries with high resolution
TmfModelResponse<@NonNull TimeGraphModel> rowResponse = dataProvider.fetchRowModel(FetchParametersUtils.selectionTimeQueryToMap(new SelectionTimeQueryFilter(3, 15, 50, selectedIds)), new NullProgressMonitor());
assertEquals(ITmfResponse.Status.COMPLETED, rowResponse.getStatus());
TimeGraphModel rowModel = rowResponse.getModel();
assertNotNull(rowModel);
List<@NonNull ITimeGraphRowModel> rows = rowModel.getRows();
assertEquals(8, rows.size());
// Verify the level entries
verifyStates(rows, tid3, Collections.emptyList());
verifyStates(rows, pid5, Collections.emptyList());
verifyStates(rows, tid6, Collections.emptyList());
// Verify function level 1 of tid 3
verifyStates(rows, FlameDataProviderTestUtils.findEntryByDepthAndType(tid3Children, 1, EntryType.FUNCTION), ImmutableList.of(new TimeGraphState(3, 17, Integer.MIN_VALUE, "op2")));
// Verify function level 2 of tid 3
verifyStates(rows, FlameDataProviderTestUtils.findEntryByDepthAndType(tid3Children, 2, EntryType.FUNCTION), ImmutableList.of(
new TimeGraphState(1, 4, Integer.MIN_VALUE),
new TimeGraphState(5, 1, Integer.MIN_VALUE, "op3"),
new TimeGraphState(6, 1, Integer.MIN_VALUE),
new TimeGraphState(7, 6, Integer.MIN_VALUE, "op2"),
new TimeGraphState(13, 8, Integer.MIN_VALUE)));
// Verify function level 1 of tid 6
verifyStates(rows, FlameDataProviderTestUtils.findEntryByDepthAndType(tid6Children, 1, EntryType.FUNCTION), ImmutableList.of(new TimeGraphState(1, 19, Integer.MIN_VALUE, "op1")));
// Verify function level 2 of tid 6
verifyStates(rows, FlameDataProviderTestUtils.findEntryByDepthAndType(tid6Children, 2, EntryType.FUNCTION), ImmutableList.of(
new TimeGraphState(2, 5, Integer.MIN_VALUE, "op3"),
new TimeGraphState(7, 1, Integer.MIN_VALUE),
new TimeGraphState(8, 3, Integer.MIN_VALUE, "op2"),
new TimeGraphState(11, 1, Integer.MIN_VALUE),
new TimeGraphState(12, 8, Integer.MIN_VALUE, "op4")));
// Verify function level 3 of tid 6
verifyStates(rows, FlameDataProviderTestUtils.findEntryByDepthAndType(tid6Children, 3, EntryType.FUNCTION), ImmutableList.of(
new TimeGraphState(1, 3, Integer.MIN_VALUE),
new TimeGraphState(4, 2, Integer.MIN_VALUE, "op1"),
new TimeGraphState(6, 3, Integer.MIN_VALUE),
new TimeGraphState(9, 1, Integer.MIN_VALUE, "op3"),
new TimeGraphState(10, 11, Integer.MIN_VALUE)));
// Get the row model for those entries with low resolution
rowResponse = dataProvider.fetchRowModel(FetchParametersUtils.selectionTimeQueryToMap(new SelectionTimeQueryFilter(3, 15, 2, selectedIds)), new NullProgressMonitor());
assertEquals(ITmfResponse.Status.COMPLETED, rowResponse.getStatus());
rowModel = rowResponse.getModel();
assertNotNull(rowModel);
rows = rowModel.getRows();
assertEquals(8, rows.size());
// Verify the level entries
verifyStates(rows, tid3, Collections.emptyList());
verifyStates(rows, pid5, Collections.emptyList());
verifyStates(rows, tid6, Collections.emptyList());
// Verify function level 1 of tid 3
verifyStates(rows, FlameDataProviderTestUtils.findEntryByDepthAndType(tid3Children, 1, EntryType.FUNCTION), ImmutableList.of(new TimeGraphState(3, 17, Integer.MIN_VALUE, "op2")));
// Verify function level 2 of tid 3
verifyStates(rows, FlameDataProviderTestUtils.findEntryByDepthAndType(tid3Children, 2, EntryType.FUNCTION), ImmutableList.of(
new TimeGraphState(1, 4, Integer.MIN_VALUE),
new TimeGraphState(13, 8, Integer.MIN_VALUE)));
// Verify function level 1 of tid 6
verifyStates(rows, FlameDataProviderTestUtils.findEntryByDepthAndType(tid6Children, 1, EntryType.FUNCTION), ImmutableList.of(new TimeGraphState(1, 19, Integer.MIN_VALUE, "op1")));
// Verify function level 2 of tid 6
verifyStates(rows, FlameDataProviderTestUtils.findEntryByDepthAndType(tid6Children, 2, EntryType.FUNCTION), ImmutableList.of(
new TimeGraphState(2, 5, Integer.MIN_VALUE, "op3"),
new TimeGraphState(12, 8, Integer.MIN_VALUE, "op4")));
// Verify function level 3 of tid 6
verifyStates(rows, FlameDataProviderTestUtils.findEntryByDepthAndType(tid6Children, 3, EntryType.FUNCTION), ImmutableList.of(
new TimeGraphState(1, 3, Integer.MIN_VALUE),
new TimeGraphState(10, 11, Integer.MIN_VALUE)));
}
/**
* Test following a callstack backward and forward
*/
@Test
public void testFollowEvents() {
FlameChartDataProvider dataProvider = getDataProvider();
TmfModelResponse<@NonNull TmfTreeModel<@NonNull FlameChartEntryModel>> responseTree = dataProvider.fetchTree(FetchParametersUtils.timeQueryToMap(new TimeQueryFilter(0, Long.MAX_VALUE, 2)), new NullProgressMonitor());
assertTrue(responseTree.getStatus().equals(ITmfResponse.Status.COMPLETED));
TmfTreeModel<@NonNull FlameChartEntryModel> model = responseTree.getModel();
assertNotNull(model);
List<@NonNull FlameChartEntryModel> modelEntries = model.getEntries();
// Thread 2
FlameChartEntryModel tid2 = FlameDataProviderTestUtils.findEntryByNameAndType(modelEntries, "2", EntryType.LEVEL);
assertNotNull(tid2);
List<FlameChartEntryModel> tid2Children = FlameDataProviderTestUtils.findEntriesByParent(modelEntries, tid2.getId());
assertEquals(3, tid2Children.size());
// For each child, make sure the response is always the same
for (FlameChartEntryModel tid2Child : tid2Children) {
TmfModelResponse<@NonNull TimeGraphModel> rowModel = dataProvider.fetchRowModel(FetchParametersUtils.selectionTimeQueryToMap(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<@NonNull TimeGraphModel> rowModel = dataProvider.fetchRowModel(FetchParametersUtils.selectionTimeQueryToMap(new SelectionTimeQueryFilter(7, Long.MAX_VALUE, 2, selectedEntry)), MONITOR);
verifyFollowResponse(rowModel, 0, 10);
rowModel = dataProvider.fetchRowModel(FetchParametersUtils.selectionTimeQueryToMap(new SelectionTimeQueryFilter(10, Long.MAX_VALUE, 2, selectedEntry)), new NullProgressMonitor());
verifyFollowResponse(rowModel, 1, 12);
rowModel = dataProvider.fetchRowModel(FetchParametersUtils.selectionTimeQueryToMap(new SelectionTimeQueryFilter(12, Long.MAX_VALUE, 2, selectedEntry)), new NullProgressMonitor());
verifyFollowResponse(rowModel, 0, 20);
rowModel = dataProvider.fetchRowModel(FetchParametersUtils.selectionTimeQueryToMap(new SelectionTimeQueryFilter(20, Long.MAX_VALUE, 2, selectedEntry)), new NullProgressMonitor());
verifyFollowResponse(rowModel, -1, -1);
// Go backward from the back
rowModel = dataProvider.fetchRowModel(FetchParametersUtils.selectionTimeQueryToMap(new SelectionTimeQueryFilter(ImmutableList.of(Long.MIN_VALUE, 20L), selectedEntry)), new NullProgressMonitor());
verifyFollowResponse(rowModel, 1, 12);
// Go backward from time 7 till the beginning
rowModel = dataProvider.fetchRowModel(FetchParametersUtils.selectionTimeQueryToMap(new SelectionTimeQueryFilter(ImmutableList.of(Long.MIN_VALUE, 7L), selectedEntry)), new NullProgressMonitor());
verifyFollowResponse(rowModel, 2, 5);
rowModel = dataProvider.fetchRowModel(FetchParametersUtils.selectionTimeQueryToMap(new SelectionTimeQueryFilter(ImmutableList.of(Long.MIN_VALUE, 5L), selectedEntry)), new NullProgressMonitor());
verifyFollowResponse(rowModel, 3, 4);
rowModel = dataProvider.fetchRowModel(FetchParametersUtils.selectionTimeQueryToMap(new SelectionTimeQueryFilter(ImmutableList.of(Long.MIN_VALUE, 4L), selectedEntry)), new NullProgressMonitor());
verifyFollowResponse(rowModel, 2, 3);
rowModel = dataProvider.fetchRowModel(FetchParametersUtils.selectionTimeQueryToMap(new SelectionTimeQueryFilter(ImmutableList.of(Long.MIN_VALUE, 3L), selectedEntry)), new NullProgressMonitor());
verifyFollowResponse(rowModel, 1, 1);
rowModel = dataProvider.fetchRowModel(FetchParametersUtils.selectionTimeQueryToMap(new SelectionTimeQueryFilter(ImmutableList.of(Long.MIN_VALUE, 1L), selectedEntry)), new NullProgressMonitor());
verifyFollowResponse(rowModel, -1, -1);
}
private static void verifyFollowResponse(TmfModelResponse<@NonNull TimeGraphModel> rowModel, int expectedDepth, int expectedTime) {
assertEquals(ITmfResponse.Status.COMPLETED, rowModel.getStatus());
TimeGraphModel model = rowModel.getModel();
if (expectedDepth < 0) {
assertNull(model);
return;
}
assertNotNull(model);
List<@NonNull ITimeGraphRowModel> rows = model.getRows();
assertEquals(1, rows.size());
List<ITimeGraphState> row = rows.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++) {
String entryName = entry.getName();
if (i > expectedStates.size() - 1) {
fail("Unexpected state at position " + i + " for entry " + entryName + ": " + states.get(i));
}
ITimeGraphState actual = states.get(i);
ITimeGraphState expected = expectedStates.get(i);
assertEquals("State start time at " + i + " for entry " + entryName, expected.getStartTime(), actual.getStartTime());
assertEquals("Duration at " + i + " for entry " + entryName, expected.getDuration(), actual.getDuration());
assertEquals("Label at " + i + " for entry " + entryName, expected.getLabel(), actual.getLabel());
}
}
}