Skip to content

Commit

Permalink
callstack.core: Add kernel thread statuses to tests
Browse files Browse the repository at this point in the history
[Added] KernelAnalysisStub to add kernel thread statuses to tests
[Added] getStatusIntervalsForThreads to KernelThreadInformationProvider

To add these tests, a stub of the kernel analysis has been added and the
FlameChartDP has been modified to depend on the HostModel and not query
directly from the ThreadStatusDP.

To make the changes to the FlameChartDP, the CompositeHostModel has been
modified to query the thread statuses from multiple threads in one
query.

Signed-off-by: Arnaud Fiorini <fiorini.arnaud@gmail.com>
Change-Id: I1ae1b729b0c29c267915e4cb2110d838547cae9f
Reviewed-on: https://git.eclipse.org/r/c/tracecompass/org.eclipse.tracecompass/+/202595
Reviewed-by: Marco Miller <marco.miller@ericsson.com>
Reviewed-by: Matthew Khouzam <matthew.khouzam@ericsson.com>
Tested-by: Trace Compass Bot <tracecompass-bot@eclipse.org>
Tested-by: Matthew Khouzam <matthew.khouzam@ericsson.com>
  • Loading branch information
arfio authored and MatthewKhouzam committed Sep 15, 2023
1 parent 47f44bd commit 5ea491c
Show file tree
Hide file tree
Showing 31 changed files with 580 additions and 273 deletions.
Expand Up @@ -11,5 +11,13 @@
class="org.eclipse.tracecompass.tmf.tests.stubs.trace.xml.TmfXmlTraceStub">
</tracetype>
</module>
<module
analysis_module="org.eclipse.tracecompass.analysis.callstack.core.tests.stubs.KernelAnalysisModuleStub"
id="org.eclipse.tracecompass.analysis.callstack.core.tests.kernelanalysis.stub"
name="Test Kernel Analysis">
<tracetype
class="org.eclipse.tracecompass.tmf.tests.stubs.trace.xml.TmfXmlTraceStub">
</tracetype>
</module>
</extension>
</plugin>
Expand Up @@ -19,6 +19,7 @@
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.tracecompass.analysis.callstack.core.tests.stubs.CallStackAnalysisStub;
import org.eclipse.tracecompass.analysis.callstack.core.tests.stubs.KernelAnalysisModuleStub;
import org.eclipse.tracecompass.internal.analysis.callstack.core.base.ICallStackSymbol;
import org.eclipse.tracecompass.internal.analysis.callstack.core.callgraph.AggregatedCallSite;
import org.eclipse.tracecompass.internal.analysis.callstack.core.instrumented.InstrumentedCallStackAnalysis;
Expand All @@ -27,6 +28,7 @@
import org.eclipse.tracecompass.tmf.core.exceptions.TmfTraceException;
import org.eclipse.tracecompass.tmf.core.signal.TmfTraceOpenedSignal;
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 org.eclipse.tracecompass.tmf.tests.stubs.trace.xml.TmfXmlTraceStub;
import org.eclipse.tracecompass.tmf.tests.stubs.trace.xml.TmfXmlTraceStubNs;
Expand All @@ -43,6 +45,7 @@ public class CallStackTestBase {

private ITmfTrace fTrace;
private CallStackAnalysisStub fModule;
private KernelAnalysisModuleStub fKernelModule;

/**
* Setup the trace for the tests
Expand All @@ -61,11 +64,18 @@ public void setUp() {
fail(e.getMessage());
}
fTrace = trace;
TmfTraceManager traceManager = TmfTraceManager.getInstance();
traceManager.traceOpened(new TmfTraceOpenedSignal(this, trace, null));
trace.traceOpened(new TmfTraceOpenedSignal(this, trace, null));

KernelAnalysisModuleStub kernelModule = TmfTraceUtils.getAnalysisModuleOfClass(trace, KernelAnalysisModuleStub.class, KernelAnalysisModuleStub.ID1);
assertNotNull(kernelModule);
kernelModule.schedule();
assertTrue(kernelModule.waitForCompletion());
fKernelModule = kernelModule;

CallStackAnalysisStub module = TmfTraceUtils.getAnalysisModuleOfClass(trace, CallStackAnalysisStub.class, CallStackAnalysisStub.ID);
assertNotNull(module);

module.schedule();
assertTrue(module.waitForCompletion());
fModule = module;
Expand Down Expand Up @@ -116,6 +126,18 @@ public CallStackAnalysisStub getModule() {
return fModule;
}

/**
* Get the kernel analysis module.
*
* It mimicks the kernel analysis by reproducing the thread states attribute
* tree.
*
* @return The analysis module
*/
public KernelAnalysisModuleStub getKernelModule() {
return fKernelModule;
}

/**
* Get the trace
*
Expand Down
Expand Up @@ -33,8 +33,10 @@
import org.eclipse.tracecompass.internal.analysis.callstack.core.instrumented.FlameChartDataProviderFactory;
import org.eclipse.tracecompass.internal.analysis.callstack.core.instrumented.FlameChartEntryModel;
import org.eclipse.tracecompass.internal.analysis.callstack.core.instrumented.FlameChartEntryModel.EntryType;
import org.eclipse.tracecompass.internal.analysis.os.linux.core.registry.LinuxStyle;
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.OutputElementStyle;
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;
Expand Down Expand Up @@ -105,18 +107,14 @@ public void testFetchTree() {
TmfTreeModel<@NonNull FlameChartEntryModel> model = responseTree.getModel();
assertNotNull(model);
List<@NonNull FlameChartEntryModel> modelEntries = model.getEntries();
assertEquals(18, modelEntries.size());
assertEquals(26, 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
Expand Down Expand Up @@ -144,8 +142,10 @@ public void testFetchTree() {
}
}
break;
case FUNCTION:
case KERNEL:
fail("There should be no kernel entry in this callstack");
assertNotNull(parent);
assertEquals(EntryType.LEVEL, parent.getEntryType());
break;
case TRACE:
assertEquals(-1, entry.getParentId());
Expand All @@ -159,7 +159,7 @@ public void testFetchTree() {
/**
* Test getting the model from the flame chart data provider
*/
@SuppressWarnings("null")
@SuppressWarnings({ "null" })
@Test
public void testFetchModel() {
FlameChartDataProvider dataProvider = getDataProvider();
Expand All @@ -176,7 +176,7 @@ public void testFetchModel() {
assertNotNull(tid3);
selectedIds.add(tid3.getId());
List<FlameChartEntryModel> tid3Children = FlameDataProviderTestUtils.findEntriesByParent(modelEntries, tid3.getId());
assertEquals(2, tid3Children.size());
assertEquals(3, tid3Children.size());
tid3Children.forEach(child -> selectedIds.add(child.getId()));
// Pid 5
FlameChartEntryModel pid5 = FlameDataProviderTestUtils.findEntryByNameAndType(modelEntries, "5", EntryType.LEVEL);
Expand All @@ -187,7 +187,7 @@ public void testFetchModel() {
assertNotNull(tid6);
selectedIds.add(tid6.getId());
List<FlameChartEntryModel> tid6Children = FlameDataProviderTestUtils.findEntriesByParent(modelEntries, tid6.getId());
assertEquals(3, tid6Children.size());
assertEquals(4, tid6Children.size());
tid6Children.forEach(child -> selectedIds.add(child.getId()));

// Get the row model for those entries with high resolution
Expand All @@ -197,7 +197,7 @@ public void testFetchModel() {
TimeGraphModel rowModel = rowResponse.getModel();
assertNotNull(rowModel);
List<@NonNull ITimeGraphRowModel> rows = rowModel.getRows();
assertEquals(8, rows.size());
assertEquals(10, rows.size());

// Verify the level entries
verifyStates(rows, tid3, Collections.emptyList());
Expand All @@ -212,6 +212,13 @@ public void testFetchModel() {
new TimeGraphState(6, 1, Integer.MIN_VALUE),
new TimeGraphState(7, 6, Integer.MIN_VALUE, "op2"),
new TimeGraphState(13, 8, Integer.MIN_VALUE)));
// Verify kernel statuses of tid 3
verifyStates(rows, FlameDataProviderTestUtils.findEntryByDepthAndType(tid3Children, -1, EntryType.KERNEL), ImmutableList.of(
new TimeGraphState(3, 3, null, new OutputElementStyle(LinuxStyle.USERMODE.getLabel())),
new TimeGraphState(6, 1, null, new OutputElementStyle(LinuxStyle.WAIT_FOR_CPU.getLabel())),
new TimeGraphState(7, 6, null, new OutputElementStyle(LinuxStyle.USERMODE.getLabel())),
new TimeGraphState(13, 8, null, new OutputElementStyle(LinuxStyle.WAIT_FOR_CPU.getLabel()))), true);

// 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
Expand All @@ -228,6 +235,13 @@ public void testFetchModel() {
new TimeGraphState(6, 3, Integer.MIN_VALUE),
new TimeGraphState(9, 1, Integer.MIN_VALUE, "op3"),
new TimeGraphState(10, 11, Integer.MIN_VALUE)));
// Verify kernel statuses of tid 6
verifyStates(rows, FlameDataProviderTestUtils.findEntryByDepthAndType(tid6Children, -1, EntryType.KERNEL), ImmutableList.of(
new TimeGraphState(1, 5, null, new OutputElementStyle(LinuxStyle.USERMODE.getLabel())),
new TimeGraphState(6, 2, null, new OutputElementStyle(LinuxStyle.WAIT_FOR_CPU.getLabel())),
new TimeGraphState(8, 2, null, new OutputElementStyle(LinuxStyle.USERMODE.getLabel())),
new TimeGraphState(10, 2, null, new OutputElementStyle(LinuxStyle.WAIT_FOR_CPU.getLabel())),
new TimeGraphState(12, 8, null, new OutputElementStyle(LinuxStyle.USERMODE.getLabel()))), true);

// Get the row model for those entries with low resolution
rowResponse = dataProvider.fetchRowModel(FetchParametersUtils.selectionTimeQueryToMap(new SelectionTimeQueryFilter(3, 15, 2, selectedIds)), new NullProgressMonitor());
Expand All @@ -236,7 +250,7 @@ public void testFetchModel() {
rowModel = rowResponse.getModel();
assertNotNull(rowModel);
rows = rowModel.getRows();
assertEquals(8, rows.size());
assertEquals(10, rows.size());

// Verify the level entries
verifyStates(rows, tid3, Collections.emptyList());
Expand All @@ -248,6 +262,10 @@ public void testFetchModel() {
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 kernel statuses of tid 3
verifyStates(rows, FlameDataProviderTestUtils.findEntryByDepthAndType(tid3Children, -1, EntryType.KERNEL), ImmutableList.of(
new TimeGraphState(3, 3, null, new OutputElementStyle(LinuxStyle.USERMODE.getLabel())),
new TimeGraphState(13, 8, null, new OutputElementStyle(LinuxStyle.WAIT_FOR_CPU.getLabel()))), true);
// 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
Expand All @@ -258,6 +276,10 @@ public void testFetchModel() {
verifyStates(rows, FlameDataProviderTestUtils.findEntryByDepthAndType(tid6Children, 3, EntryType.FUNCTION), ImmutableList.of(
new TimeGraphState(1, 3, Integer.MIN_VALUE),
new TimeGraphState(10, 11, Integer.MIN_VALUE)));
// Verify kernel statuses of tid 6
verifyStates(rows, FlameDataProviderTestUtils.findEntryByDepthAndType(tid6Children, -1, EntryType.KERNEL), ImmutableList.of(
new TimeGraphState(1, 5, null, new OutputElementStyle(LinuxStyle.USERMODE.getLabel())),
new TimeGraphState(12, 8, null, new OutputElementStyle(LinuxStyle.USERMODE.getLabel()))), true);
}

/**
Expand All @@ -278,16 +300,18 @@ public void testFollowEvents() {
FlameChartEntryModel tid2 = FlameDataProviderTestUtils.findEntryByNameAndType(modelEntries, "2", EntryType.LEVEL);
assertNotNull(tid2);
List<FlameChartEntryModel> tid2Children = FlameDataProviderTestUtils.findEntriesByParent(modelEntries, tid2.getId());
assertEquals(3, tid2Children.size());
assertEquals(4, 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);
if (!tid2Child.getEntryType().equals(EntryType.KERNEL)) {
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()));
Set<@NonNull Long> selectedEntry = Objects.requireNonNull(Collections.singleton(tid2Children.get(2).getId()));
TmfModelResponse<@NonNull TimeGraphModel> rowModel = dataProvider.fetchRowModel(FetchParametersUtils.selectionTimeQueryToMap(new SelectionTimeQueryFilter(7, Long.MAX_VALUE, 2, selectedEntry)), MONITOR);
verifyFollowResponse(rowModel, 0, 10);

Expand Down Expand Up @@ -342,6 +366,10 @@ private static void verifyFollowResponse(TmfModelResponse<@NonNull TimeGraphMode
}

private static void verifyStates(List<ITimeGraphRowModel> rowModels, FlameChartEntryModel entry, List<TimeGraphState> expectedStates) {
verifyStates(rowModels, entry, expectedStates, false);
}

private static void verifyStates(List<ITimeGraphRowModel> rowModels, FlameChartEntryModel entry, List<TimeGraphState> expectedStates, boolean checkStyles) {
assertNotNull(entry);
ITimeGraphRowModel rowModel = rowModels.stream()
.filter(model -> model.getEntryID() == entry.getId())
Expand All @@ -359,6 +387,9 @@ private static void verifyStates(List<ITimeGraphRowModel> rowModels, FlameChartE
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());
if (checkStyles) {
assertEquals("Style at " + i + FOR_ENTRY + entryName, expected.getStyle(), actual.getStyle());
}
}
}
}
Expand Up @@ -33,6 +33,7 @@
import org.eclipse.tracecompass.analysis.callstack.core.tests.CallStackTestBase;
import org.eclipse.tracecompass.analysis.callstack.core.tests.FlameDataProviderTestUtils;
import org.eclipse.tracecompass.analysis.callstack.core.tests.stubs.CallStackAnalysisStub;
import org.eclipse.tracecompass.analysis.os.linux.core.model.ProcessStatus;
import org.eclipse.tracecompass.internal.analysis.callstack.core.flamegraph.FlameGraphDataProvider;
import org.eclipse.tracecompass.internal.analysis.callstack.core.instrumented.FlameChartEntryModel;
import org.eclipse.tracecompass.internal.analysis.callstack.core.tree.AllGroupDescriptor;
Expand Down Expand Up @@ -285,9 +286,9 @@ private static void assertRows(FlameGraphDataProvider<?, ?, ?> provider, Map<Lon

@NonNull String[] rowStates = split[1].split(",");
assertTooltip(provider, fgEntry.getId(), Long.parseLong(rowStates[0]), false, rowStates[3], Long.parseLong(rowStates[1]) + " ns");
assertTooltip(provider, fgEntry.getId(), Long.parseLong(rowStates[0]), true, rowStates[3], Long.parseLong(rowStates[1]) + " ns");
assertTooltip(provider, fgEntry.getId(), Long.parseLong(rowStates[0]), true, rowStates[3], Long.parseLong(rowStates[1]) + " ns");

assertEqualsStates(split[1], row.getStates(), descriptor + ": " + split[0]);
assertEqualsStates(split[1], row.getStates(), descriptor + ": " + split[0], getEntryType(split[0].split(",")[0]));
}
}

Expand All @@ -299,6 +300,13 @@ private static void assertTooltip(FlameGraphDataProvider<?, ?, ?> provider, Long
assertNotNull(tooltipModel);
if (expectedObject.equals("null")) {
assertTrue(tooltipModel.isEmpty());
} else if (List.of(ProcessStatus.RUN.toString(), ProcessStatus.WAIT_CPU.toString()).contains(expectedObject)) {
if (isAction) {
assertNotNull(tooltipModel);
} else {
assertEquals(expectedObject, tooltipModel.get("Object"));
assertEquals(expectedDuration, tooltipModel.get("Duration"));
}
} else {
if (isAction) {
assertNotNull(tooltipModel);
Expand Down Expand Up @@ -335,7 +343,7 @@ private static FlameChartEntryModel findRowEntry(String entryDetails, Collection
DataProviderParameterUtils.REQUESTED_ITEMS_KEY, Collections.singletonList(entryId));
}

private static void assertEqualsStates(String string, @NonNull List<@NonNull ITimeGraphState> states, String descriptor) {
private static void assertEqualsStates(String string, @NonNull List<@NonNull ITimeGraphState> states, String descriptor, FlameChartEntryModel.EntryType entryType) {
String[] stringStates = string.split(",");
for (int i = 0; i < stringStates.length / 4; i++) {
assertTrue(descriptor + " has state " + i, states.size() > i);
Expand All @@ -349,15 +357,19 @@ private static void assertEqualsStates(String string, @NonNull List<@NonNull ITi
} else {
assertNotNull(descriptor + ": existing style at position " + i, style);
String parentKey = style.getParentKey();
// The style should be a string that represents a number, so
// make sure it can be parsed as integer
try {
Integer.parseInt(parentKey);
String expectedStyle = VALUE_TO_STYLE.computeIfAbsent(strValue, str -> parentKey);
assertEquals(descriptor + ": style at position " + i, expectedStyle, parentKey);
} catch (NumberFormatException e) {
fail("Unexpected style: " + parentKey);

if (!entryType.equals(FlameChartEntryModel.EntryType.KERNEL)) {
// The style should be a string that represents a number if
// it is not a kernel entry, so make sure it can be parsed
// as integer
try {
Integer.parseInt(parentKey);
} catch (NumberFormatException e) {
fail("Unexpected style: " + parentKey);
}
}
String expectedStyle = VALUE_TO_STYLE.computeIfAbsent(strValue, str -> parentKey);
assertEquals(descriptor + ": style at position " + i, expectedStyle, parentKey);
}
assertEquals(descriptor + ": no value at position " + i, Integer.MIN_VALUE, state.getValue());
assertEquals(descriptor + ": label at position " + i, stringStates[i * 4 + 3], String.valueOf(state.getLabel()));
Expand Down

0 comments on commit 5ea491c

Please sign in to comment.