From 5ea491c72293cd5b253f59214f6537c6b787fcfc Mon Sep 17 00:00:00 2001 From: Arnaud Fiorini Date: Mon, 19 Jun 2023 18:00:46 -0400 Subject: [PATCH] callstack.core: Add kernel thread statuses to tests [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 Change-Id: I1ae1b729b0c29c267915e4cb2110d838547cae9f Reviewed-on: https://git.eclipse.org/r/c/tracecompass/org.eclipse.tracecompass/+/202595 Reviewed-by: Marco Miller Reviewed-by: Matthew Khouzam Tested-by: Trace Compass Bot Tested-by: Matthew Khouzam --- .../plugin.xml | 8 + .../core/tests/CallStackTestBase.java | 24 +- .../tests/FlameChartDataProviderTest.java | 59 ++++- .../callgraph/FlameGraphDataProviderTest.java | 34 ++- .../tests/stubs/KernelAnalysisModuleStub.java | 40 +++ .../tests/stubs/KernelStateProviderStub.java | 81 ++++++ .../testfiles/dp/expectedFgRowFull2Times | 4 + .../testfiles/dp/expectedFgRowFullAll | 4 + .../testfiles/dp/expectedFgRowFullZoom | 4 + .../testfiles/dp/expectedFgRowOne2Times | 3 +- .../testfiles/dp/expectedFgRowOneAll | 3 +- .../testfiles/dp/expectedFgRowOneZoom | 3 +- .../testfiles/dp/expectedFgRowProcess2Times | 4 +- .../testfiles/dp/expectedFgRowProcessAll | 4 +- .../testfiles/dp/expectedFgRowProcessZoom | 4 +- .../testfiles/dp/expectedFgRowSelection2Times | 4 + .../testfiles/dp/expectedFgRowSelectionAll | 4 + .../testfiles/dp/expectedFgRowSelectionZoom | 4 + .../testfiles/dp/expectedFgTreeFull | 6 +- .../testfiles/dp/expectedFgTreeOne | 3 +- .../testfiles/dp/expectedFgTreeProcess | 4 +- .../testfiles/dp/expectedFgTreeSelection | 6 +- .../core/base/FlameWithKernelPalette.java | 10 +- .../core/callgraph/CallGraphAnalysis.java | 5 +- .../flamegraph/FlameGraphDataProvider.java | 2 +- .../instrumented/FlameChartDataProvider.java | 239 +++--------------- .../core/model/CompositeHostModel.java | 80 +++++- .../callstack/core/model/IHostModel.java | 30 +++ .../core/model/ProcessStatusInterval.java | 29 ++- .../META-INF/MANIFEST.MF | 6 +- .../KernelThreadInformationProvider.java | 142 +++++++++-- 31 files changed, 580 insertions(+), 273 deletions(-) create mode 100644 analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/stubs/org/eclipse/tracecompass/analysis/callstack/core/tests/stubs/KernelAnalysisModuleStub.java create mode 100644 analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/stubs/org/eclipse/tracecompass/analysis/callstack/core/tests/stubs/KernelStateProviderStub.java diff --git a/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/plugin.xml b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/plugin.xml index 529ba92a19..7ba64ace0c 100644 --- a/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/plugin.xml +++ b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/plugin.xml @@ -11,5 +11,13 @@ class="org.eclipse.tracecompass.tmf.tests.stubs.trace.xml.TmfXmlTraceStub"> + + + + diff --git a/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/src/org/eclipse/tracecompass/analysis/callstack/core/tests/CallStackTestBase.java b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/src/org/eclipse/tracecompass/analysis/callstack/core/tests/CallStackTestBase.java index 99a207bda7..b5e57a49ec 100644 --- a/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/src/org/eclipse/tracecompass/analysis/callstack/core/tests/CallStackTestBase.java +++ b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/src/org/eclipse/tracecompass/analysis/callstack/core/tests/CallStackTestBase.java @@ -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; @@ -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; @@ -43,6 +45,7 @@ public class CallStackTestBase { private ITmfTrace fTrace; private CallStackAnalysisStub fModule; + private KernelAnalysisModuleStub fKernelModule; /** * Setup the trace for the tests @@ -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; @@ -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 * diff --git a/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/src/org/eclipse/tracecompass/analysis/callstack/core/tests/FlameChartDataProviderTest.java b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/src/org/eclipse/tracecompass/analysis/callstack/core/tests/FlameChartDataProviderTest.java index 381db475f2..066872ae4a 100644 --- a/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/src/org/eclipse/tracecompass/analysis/callstack/core/tests/FlameChartDataProviderTest.java +++ b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/src/org/eclipse/tracecompass/analysis/callstack/core/tests/FlameChartDataProviderTest.java @@ -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; @@ -105,7 +107,7 @@ 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(); @@ -113,10 +115,6 @@ public void testFetchTree() { 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 @@ -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()); @@ -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(); @@ -176,7 +176,7 @@ public void testFetchModel() { assertNotNull(tid3); selectedIds.add(tid3.getId()); List 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); @@ -187,7 +187,7 @@ public void testFetchModel() { assertNotNull(tid6); selectedIds.add(tid6.getId()); List 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 @@ -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()); @@ -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 @@ -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()); @@ -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()); @@ -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 @@ -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); } /** @@ -278,16 +300,18 @@ public void testFollowEvents() { FlameChartEntryModel tid2 = FlameDataProviderTestUtils.findEntryByNameAndType(modelEntries, "2", EntryType.LEVEL); assertNotNull(tid2); List 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); @@ -342,6 +366,10 @@ private static void verifyFollowResponse(TmfModelResponse<@NonNull TimeGraphMode } private static void verifyStates(List rowModels, FlameChartEntryModel entry, List expectedStates) { + verifyStates(rowModels, entry, expectedStates, false); + } + + private static void verifyStates(List rowModels, FlameChartEntryModel entry, List expectedStates, boolean checkStyles) { assertNotNull(entry); ITimeGraphRowModel rowModel = rowModels.stream() .filter(model -> model.getEntryID() == entry.getId()) @@ -359,6 +387,9 @@ private static void verifyStates(List 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()); + } } } } diff --git a/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/src/org/eclipse/tracecompass/analysis/callstack/core/tests/callgraph/FlameGraphDataProviderTest.java b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/src/org/eclipse/tracecompass/analysis/callstack/core/tests/callgraph/FlameGraphDataProviderTest.java index e1b8ab5f9f..4692eac816 100644 --- a/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/src/org/eclipse/tracecompass/analysis/callstack/core/tests/callgraph/FlameGraphDataProviderTest.java +++ b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/src/org/eclipse/tracecompass/analysis/callstack/core/tests/callgraph/FlameGraphDataProviderTest.java @@ -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; @@ -285,9 +286,9 @@ private static void assertRows(FlameGraphDataProvider provider, Map 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); @@ -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); @@ -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())); diff --git a/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/stubs/org/eclipse/tracecompass/analysis/callstack/core/tests/stubs/KernelAnalysisModuleStub.java b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/stubs/org/eclipse/tracecompass/analysis/callstack/core/tests/stubs/KernelAnalysisModuleStub.java new file mode 100644 index 0000000000..ad12ada63f --- /dev/null +++ b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/stubs/org/eclipse/tracecompass/analysis/callstack/core/tests/stubs/KernelAnalysisModuleStub.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2023 É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 2.0 which + * accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package org.eclipse.tracecompass.analysis.callstack.core.tests.stubs; + +import java.util.Objects; + +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.tracecompass.analysis.os.linux.core.kernel.KernelAnalysisModule; +import org.eclipse.tracecompass.tmf.core.statesystem.ITmfStateProvider; +import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; + +/** + * A kernel analysis stub, using a kernel state provider stub + * + * This reproduces only the threads statuses part of the kernel state analysis. + * + * @author Arnaud Fiorini + */ +public class KernelAnalysisModuleStub extends KernelAnalysisModule { + + /** + * The ID of this analysis + */ + public static final String ID1 = "org.eclipse.tracecompass.analysis.callstack.core.tests.kernelanalysis.stub"; + + @Override + protected @NonNull ITmfStateProvider createStateProvider() { + ITmfTrace trace = Objects.requireNonNull(getTrace()); + return new KernelStateProviderStub(trace); + } +} diff --git a/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/stubs/org/eclipse/tracecompass/analysis/callstack/core/tests/stubs/KernelStateProviderStub.java b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/stubs/org/eclipse/tracecompass/analysis/callstack/core/tests/stubs/KernelStateProviderStub.java new file mode 100644 index 0000000000..8d830d67a0 --- /dev/null +++ b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/stubs/org/eclipse/tracecompass/analysis/callstack/core/tests/stubs/KernelStateProviderStub.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (c) 2023 É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 2.0 which + * accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package org.eclipse.tracecompass.analysis.callstack.core.tests.stubs; + +import org.eclipse.tracecompass.analysis.os.linux.core.model.ProcessStatus; +import org.eclipse.tracecompass.internal.analysis.os.linux.core.kernel.Attributes; +import org.eclipse.tracecompass.statesystem.core.ITmfStateSystemBuilder; +import org.eclipse.tracecompass.tmf.core.event.ITmfEvent; +import org.eclipse.tracecompass.tmf.core.statesystem.AbstractTmfStateProvider; +import org.eclipse.tracecompass.tmf.core.statesystem.ITmfStateProvider; +import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; + +/** + * A kernel state provider stub + * + * This reproduces the thread statuses as follows: + * + *
+ * |- THREADS
+ * |  |-  -> Thread Status
+ * |  |  |- SYSTEM_CALL
+ * 
+ * + * @author Arnaud Fiorini + */ +public class KernelStateProviderStub extends AbstractTmfStateProvider { + + private static final String ENTRY = "entry"; + + /** + * Constructor + * + * @param trace + * The trace to run this provider on + */ + public KernelStateProviderStub(ITmfTrace trace) { + super(trace, KernelAnalysisModuleStub.ID1); + } + + @Override + public int getVersion() { + return 0; + } + + @Override + public ITmfStateProvider getNewInstance() { + return new KernelStateProviderStub(getTrace()); + } + + @Override + protected void eventHandle(ITmfEvent event) { + ITmfStateSystemBuilder ssb = getStateSystemBuilder(); + if (ssb == null) { + return; + } + int threadQuark = ssb.getQuarkAbsoluteAndAdd(Attributes.THREADS, event.getContent().getFieldValue(String.class, "tid")); + boolean isEntry = event.getName().equals(ENTRY); + long timestamp = event.getTimestamp().getValue(); + if (isEntry) { + if (event.getName().equals("op4")) { + int systemCallQuark = ssb.getQuarkRelativeAndAdd(threadQuark, Attributes.SYSTEM_CALL); + ssb.modifyAttribute(timestamp, "openat", systemCallQuark); + /* Put the process in system call mode */ + ssb.modifyAttribute(timestamp, ProcessStatus.RUN_SYTEMCALL.getStateValue().unboxValue(), threadQuark); + } else { + ssb.modifyAttribute(timestamp, ProcessStatus.RUN.getStateValue().unboxValue(), threadQuark); + } + } else { + ssb.modifyAttribute(timestamp, ProcessStatus.WAIT_CPU.getStateValue().unboxValue(), threadQuark); + } + } +} diff --git a/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgRowFull2Times b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgRowFull2Times index 4caf7aa6e5..d80d755d3d 100644 --- a/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgRowFull2Times +++ b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgRowFull2Times @@ -1,11 +1,15 @@ function,0,level,2:0,8,op4,op4 function,1,level,2:0,8,-,null function,2,level,2:0,8,-,null +kernel,Kernel statuses,level,2:0,8,RUN,RUN function,0,level,3:0,17,op2,op2 function,1,level,3:0,1,op3,op3 +kernel,Kernel statuses,level,3:0,8,WAIT_CPU,WAIT_CPU function,0,level,6:0,19,op1,op1 function,1,level,6:0,3,op2,op2,16,3,-,null function,2,level,6:0,1,op3,op3,5,14,-,null +kernel,Kernel statuses,level,6:0,4,WAIT_CPU,WAIT_CPU,4,15,RUN,RUN function,0,level,7:0,19,op5,op5 function,1,level,7:0,12,op2,op2,12,7,-,null function,2,level,7:0,1,op3,op3,1,18,-,null +kernel,Kernel statuses,level,7:0,8,WAIT_CPU,WAIT_CPU,8,11,RUN,RUN diff --git a/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgRowFullAll b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgRowFullAll index 83783f3f96..7fee3929f3 100644 --- a/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgRowFullAll +++ b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgRowFullAll @@ -1,11 +1,15 @@ function,0,level,2:0,8,op4,op4,8,9,op1,op1 function,1,level,2:0,8,-,null,8,4,op2,op2,12,5,-,null function,2,level,2:0,8,-,null,8,1,op3,op3,9,8,-,null +kernel,Kernel statuses,level,2:0,8,RUN,RUN,8,4,RUN,RUN,12,5,WAIT_CPU,WAIT_CPU function,0,level,3:0,17,op2,op2 function,1,level,3:0,1,op3,op3,1,6,op2,op2,7,10,-,null +kernel,Kernel statuses,level,3:0,8,WAIT_CPU,WAIT_CPU,8,9,RUN,RUN function,0,level,6:0,19,op1,op1 function,1,level,6:0,3,op2,op2,3,5,op3,op3,8,8,op4,op4,16,3,-,null function,2,level,6:0,1,op3,op3,1,2,-,null,3,2,op1,op1,5,14,-,null +kernel,Kernel statuses,level,6:0,4,WAIT_CPU,WAIT_CPU,4,15,RUN,RUN function,0,level,7:0,19,op5,op5 function,1,level,7:0,12,op2,op2,12,7,-,null function,2,level,7:0,1,op3,op3,1,18,-,null +kernel,Kernel statuses,level,7:0,8,WAIT_CPU,WAIT_CPU,8,11,RUN,RUN diff --git a/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgRowFullZoom b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgRowFullZoom index 6db4f2848b..edd66b7503 100644 --- a/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgRowFullZoom +++ b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgRowFullZoom @@ -1,11 +1,15 @@ function,0,level,2:8,9,op1,op1 function,1,level,2:8,4,op2,op2,12,5,-,null function,2,level,2:9,8,-,null +kernel,Kernel statuses,level,2:8,4,RUN,RUN,12,5,WAIT_CPU,WAIT_CPU function,0,level,3:0,17,op2,op2 function,1,level,3:7,10,-,null +kernel,Kernel statuses,level,3:8,9,RUN,RUN function,0,level,6:0,19,op1,op1 function,1,level,6:8,8,op4,op4,16,3,-,null function,2,level,6:5,14,-,null +kernel,Kernel statuses,level,6:4,15,RUN,RUN function,0,level,7:0,19,op5,op5 function,1,level,7:0,12,op2,op2,12,7,-,null function,2,level,7:1,18,-,null +kernel,Kernel statuses,level,7:8,11,RUN,RUN diff --git a/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgRowOne2Times b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgRowOne2Times index 07c8b23049..fcc3af0b5c 100644 --- a/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgRowOne2Times +++ b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgRowOne2Times @@ -1,3 +1,4 @@ function,0,level,All:0,8,op4,op4,44,28,op1,op1 function,1,level,All:0,8,-,null,64,8,-,null -function,2,level,All:0,25,-,null,51,21,-,null \ No newline at end of file +function,2,level,All:0,25,-,null,51,21,-,null +kernel,Kernel statuses,level,All:0,8,RUN,RUN,53,19,RUN,RUN diff --git a/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgRowOneAll b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgRowOneAll index 9c68cb33a2..fcc9f83a45 100644 --- a/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgRowOneAll +++ b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgRowOneAll @@ -1,3 +1,4 @@ function,0,level,All:0,8,op4,op4,8,17,op2,op2,25,19,op5,op5,44,28,op1,op1 function,1,level,All:0,8,-,null,8,1,op3,op3,9,6,op2,op2,15,10,-,null,25,12,op2,op2,37,7,-,null,44,5,op3,op3,49,7,op2,op2,56,8,op4,op4,64,8,-,null -function,2,level,All:0,25,-,null,25,1,op3,op3,26,18,-,null,44,2,op1,op1,46,3,-,null,49,2,op3,op3,51,21,-,null \ No newline at end of file +function,2,level,All:0,25,-,null,25,1,op3,op3,26,18,-,null,44,2,op1,op1,46,3,-,null,49,2,op3,op3,51,21,-,null +kernel,Kernel statuses,level,All:0,8,RUN,RUN,8,8,WAIT_CPU,WAIT_CPU,16,9,RUN,RUN,25,8,WAIT_CPU,WAIT_CPU,33,11,RUN,RUN,44,9,WAIT_CPU,WAIT_CPU,53,19,RUN,RUN diff --git a/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgRowOneZoom b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgRowOneZoom index e0f8c8edb3..598ea9b2ec 100644 --- a/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgRowOneZoom +++ b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgRowOneZoom @@ -1,3 +1,4 @@ function,0,level,All:25,19,op5,op5,44,28,op1,op1 function,1,level,All:25,12,op2,op2,37,7,-,null,44,5,op3,op3,49,7,op2,op2,56,8,op4,op4,64,8,-,null -function,2,level,All:26,18,-,null,44,2,op1,op1,46,3,-,null,49,2,op3,op3,51,21,-,null \ No newline at end of file +function,2,level,All:26,18,-,null,44,2,op1,op1,46,3,-,null,49,2,op3,op3,51,21,-,null +kernel,Kernel statuses,level,All:33,11,RUN,RUN,44,9,WAIT_CPU,WAIT_CPU,53,19,RUN,RUN diff --git a/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgRowProcess2Times b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgRowProcess2Times index 18a97cc8c6..7af334668d 100644 --- a/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgRowProcess2Times +++ b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgRowProcess2Times @@ -1,6 +1,8 @@ function,0,level,1:0,8,op4,op4 function,1,level,1:0,8,-,null function,2,level,1:0,8,-,null +kernel,Kernel statuses,level,1:0,8,RUN,RUN function,0,level,5:0,19,op1,op1,19,19,op5,op5 function,1,level,5:0,3,op2,op2,31,7,-,null -function,2,level,5:0,1,op3,op3,20,18,-,null \ No newline at end of file +function,2,level,5:0,1,op3,op3,20,18,-,null +kernel,Kernel statuses,level,5:0,4,WAIT_CPU,WAIT_CPU,27,11,RUN,RUN diff --git a/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgRowProcessAll b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgRowProcessAll index c482f6b7a3..ec3921b351 100644 --- a/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgRowProcessAll +++ b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgRowProcessAll @@ -1,6 +1,8 @@ function,0,level,1:0,8,op4,op4,8,9,op1,op1,17,17,op2,op2 function,1,level,1:0,8,-,null,8,4,op2,op2,12,5,-,null,17,1,op3,op3,18,6,op2,op2,24,10,-,null function,2,level,1:0,8,-,null,8,1,op3,op3,9,25,-,null +kernel,Kernel statuses,level,1:0,8,RUN,RUN,8,4,RUN,RUN,12,5,WAIT_CPU,WAIT_CPU,17,8,WAIT_CPU,WAIT_CPU,25,9,RUN,RUN function,0,level,5:0,19,op1,op1,19,19,op5,op5 function,1,level,5:0,3,op2,op2,3,5,op3,op3,8,8,op4,op4,16,3,-,null,19,12,op2,op2,31,7,-,null -function,2,level,5:0,1,op3,op3,1,2,-,null,3,2,op1,op1,5,14,-,null,19,1,op3,op3,20,18,-,null \ No newline at end of file +function,2,level,5:0,1,op3,op3,1,2,-,null,3,2,op1,op1,5,14,-,null,19,1,op3,op3,20,18,-,null +kernel,Kernel statuses,level,5:0,4,WAIT_CPU,WAIT_CPU,4,15,RUN,RUN,19,8,WAIT_CPU,WAIT_CPU,27,11,RUN,RUN diff --git a/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgRowProcessZoom b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgRowProcessZoom index 190327cc76..f62dc61de8 100644 --- a/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgRowProcessZoom +++ b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgRowProcessZoom @@ -1,6 +1,8 @@ function,0,level,1:17,17,op2,op2 function,1,level,1:18,6,op2,op2,24,10,-,null function,2,level,1:9,25,-,null +kernel,Kernel statuses,level,1:17,8,WAIT_CPU,WAIT_CPU,25,9,RUN,RUN function,0,level,5:19,19,op5,op5 function,1,level,5:19,12,op2,op2,31,7,-,null -function,2,level,5:19,1,op3,op3,20,18,-,null \ No newline at end of file +function,2,level,5:19,1,op3,op3,20,18,-,null +kernel,Kernel statuses,level,5:19,8,WAIT_CPU,WAIT_CPU,27,11,RUN,RUN diff --git a/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgRowSelection2Times b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgRowSelection2Times index cc29a44fd8..cd4dfa3268 100644 --- a/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgRowSelection2Times +++ b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgRowSelection2Times @@ -1,10 +1,14 @@ function,0,level,2:0,3,op4,op4 function,1,level,2:0,3,-,null +kernel,Kernel statuses,level,2:0,3,RUN,RUN function,0,level,3:0,10,op2,op2 function,1,level,3:0,1,op3,op3,7,3,-,null +kernel,Kernel statuses,level,3:0,3,WAIT_CPU,WAIT_CPU,3,7,RUN,RUN function,0,level,6:0,10,op1,op1 function,1,level,6:0,2,op3,op3,8,2,-,null function,2,level,6:0,1,op1,op1,3,7,-,null +kernel,Kernel statuses,level,6:0,4,WAIT_CPU,WAIT_CPU,4,6,RUN,RUN function,0,level,7:0,10,op5,op5 function,1,level,7:0,5,op2,op2,5,5,-,null function,2,level,7:0,1,op3,op3,1,9,-,null +kernel,Kernel statuses,level,7:0,3,RUN,RUN,3,7,WAIT_CPU,WAIT_CPU diff --git a/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgRowSelectionAll b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgRowSelectionAll index 4fe776247e..b7671d1336 100644 --- a/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgRowSelectionAll +++ b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgRowSelectionAll @@ -1,10 +1,14 @@ function,0,level,2:0,3,op4,op4,3,5,op1,op1 function,1,level,2:0,3,-,null,3,2,op2,op2,5,3,-,null +kernel,Kernel statuses,level,2:0,3,RUN,RUN,3,5,WAIT_CPU,WAIT_CPU function,0,level,3:0,10,op2,op2 function,1,level,3:0,1,op3,op3,1,6,op2,op2,7,3,-,null +kernel,Kernel statuses,level,3:0,3,WAIT_CPU,WAIT_CPU,3,7,RUN,RUN function,0,level,6:0,10,op1,op1 function,1,level,6:0,2,op3,op3,2,3,op2,op2,5,3,op4,op4,8,2,-,null function,2,level,6:0,1,op1,op1,1,1,-,null,2,1,op3,op3,3,7,-,null +kernel,Kernel statuses,level,6:0,4,WAIT_CPU,WAIT_CPU,4,6,RUN,RUN function,0,level,7:0,10,op5,op5 function,1,level,7:0,5,op2,op2,5,5,-,null function,2,level,7:0,1,op3,op3,1,9,-,null +kernel,Kernel statuses,level,7:0,3,RUN,RUN,3,7,WAIT_CPU,WAIT_CPU diff --git a/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgRowSelectionZoom b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgRowSelectionZoom index 4ad99e5795..12521ab70b 100644 --- a/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgRowSelectionZoom +++ b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgRowSelectionZoom @@ -1,10 +1,14 @@ function,0,level,2:3,5,op1,op1 function,1,level,2:5,3,-,null +kernel,Kernel statuses,level,2:3,5,WAIT_CPU,WAIT_CPU function,0,level,3:0,10,op2,op2 function,1,level,3:1,6,op2,op2,7,3,-,null +kernel,Kernel statuses,level,3:3,7,RUN,RUN function,0,level,6:0,10,op1,op1 function,1,level,6:5,3,op4,op4,8,2,-,null function,2,level,6:3,7,-,null +kernel,Kernel statuses,level,6:4,6,RUN,RUN function,0,level,7:0,10,op5,op5 function,1,level,7:5,5,-,null function,2,level,7:1,9,-,null +kernel,Kernel statuses,level,7:3,7,WAIT_CPU,WAIT_CPU diff --git a/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgTreeFull b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgTreeFull index c791615791..38ed503b33 100644 --- a/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgTreeFull +++ b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgTreeFull @@ -4,15 +4,19 @@ level,2,0,17,level,1 function,0,0,17,level,2 function,1,0,17,level,2 function,2,0,17,level,2 +kernel,Kernel statuses,0,17,level,2 level,3,0,17,level,1 function,0,0,17,level,3 function,1,0,17,level,3 +kernel,Kernel statuses,0,17,level,3 level,5,0,19,trace,callstack.xml level,6,0,19,level,5 function,0,0,19,level,6 function,1,0,19,level,6 function,2,0,19,level,6 +kernel,Kernel statuses,0,19,level,6 level,7,0,19,level,5 function,0,0,19,level,7 function,1,0,19,level,7 -function,2,0,19,level,7 \ No newline at end of file +function,2,0,19,level,7 +kernel,Kernel statuses,0,19,level,7 diff --git a/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgTreeOne b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgTreeOne index 2c5efdfaa2..13668a827f 100644 --- a/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgTreeOne +++ b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgTreeOne @@ -2,4 +2,5 @@ trace,callstack.xml,0,72,-,- level,All,0,72,trace,callstack.xml function,0,0,72,level,All function,1,0,72,level,All -function,2,0,72,level,All \ No newline at end of file +function,2,0,72,level,All +kernel,Kernel statuses,0,72,level,All diff --git a/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgTreeProcess b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgTreeProcess index 5d60eb7331..95137d530e 100644 --- a/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgTreeProcess +++ b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgTreeProcess @@ -3,7 +3,9 @@ level,1,0,34,trace,callstack.xml function,0,0,34,level,1 function,1,0,34,level,1 function,2,0,34,level,1 +kernel,Kernel statuses,0,34,level,1 level,5,0,38,trace,callstack.xml function,0,0,38,level,5 function,1,0,38,level,5 -function,2,0,38,level,5 \ No newline at end of file +function,2,0,38,level,5 +kernel,Kernel statuses,0,38,level,5 diff --git a/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgTreeSelection b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgTreeSelection index 30500c48d5..d291a84c13 100644 --- a/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgTreeSelection +++ b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/testfiles/dp/expectedFgTreeSelection @@ -3,15 +3,19 @@ level,1,0,10,trace,callstack.xml level,2,0,8,level,1 function,0,0,8,level,2 function,1,0,8,level,2 +kernel,Kernel statuses,0,8,level,2 level,3,0,10,level,1 function,0,0,10,level,3 function,1,0,10,level,3 +kernel,Kernel statuses,0,10,level,3 level,5,0,10,trace,callstack.xml level,6,0,10,level,5 function,0,0,10,level,6 function,1,0,10,level,6 function,2,0,10,level,6 +kernel,Kernel statuses,0,10,level,6 level,7,0,10,level,5 function,0,0,10,level,7 function,1,0,10,level,7 -function,2,0,10,level,7 \ No newline at end of file +function,2,0,10,level,7 +kernel,Kernel statuses,0,10,level,7 diff --git a/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/base/FlameWithKernelPalette.java b/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/base/FlameWithKernelPalette.java index 0de7954c62..d61a603d63 100644 --- a/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/base/FlameWithKernelPalette.java +++ b/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/base/FlameWithKernelPalette.java @@ -79,7 +79,15 @@ private static OutputElementStyle getElementStyle(int stateValue) { return STYLE_MAP.computeIfAbsent(styleFor, style -> new OutputElementStyle(style)); } - private static String getStyleFor(int stateValue) { + /** + * Gets the thread state label corresponding to the state value integer for + * threads + * + * @param stateValue + * Integer corresponding to a thread state in the state system + * @return the thread state label + */ + public static String getStyleFor(int stateValue) { switch (stateValue) { case StateValues.PROCESS_STATUS_UNKNOWN: return LinuxStyle.UNKNOWN.getLabel(); diff --git a/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/callgraph/CallGraphAnalysis.java b/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/callgraph/CallGraphAnalysis.java index ab5025e486..06d95fe618 100644 --- a/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/callgraph/CallGraphAnalysis.java +++ b/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/callgraph/CallGraphAnalysis.java @@ -17,7 +17,6 @@ import java.util.List; import java.util.Objects; -import org.apache.commons.lang3.StringUtils; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.jdt.annotation.NonNull; @@ -353,7 +352,7 @@ public Object getAdditionalMetric(AggregatedCallSite object, int metricIndex) { return ((AggregatedCalledFunction) object).getSelfTime(); case 1: long cpuTime = ((AggregatedCalledFunction) object).getCpuTime(); - return cpuTime >= 0 ? cpuTime : StringUtils.EMPTY; + return cpuTime >= 0 ? cpuTime : 0L; case 2: return ((AggregatedCalledFunction) object).getNbCalls(); default: @@ -362,7 +361,7 @@ public Object getAdditionalMetric(AggregatedCallSite object, int metricIndex) { break; } } - return StringUtils.EMPTY; + return 0L; } @Override diff --git a/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/flamegraph/FlameGraphDataProvider.java b/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/flamegraph/FlameGraphDataProvider.java index ae9ba3f483..9f861c53ea 100644 --- a/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/flamegraph/FlameGraphDataProvider.java +++ b/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/flamegraph/FlameGraphDataProvider.java @@ -406,7 +406,7 @@ private void recursivelyAddChildren(IWeightedTreeProvider wtProvider, I // Add the extra sites List extraDataSets = wtProvider.getExtraDataSets(); for (int i = 0; i < extraDataSets.size(); i++) { - Collection> extraDataTrees = callSite.getExtraDataTrees(i); + List> extraDataTrees = callSite.getExtraDataTrees(i).stream().sorted(fCctComparator2).toList(); if (extraDataTrees.isEmpty()) { continue; } diff --git a/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/instrumented/FlameChartDataProvider.java b/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/instrumented/FlameChartDataProvider.java index a11f0f43a7..2c37b50af3 100644 --- a/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/instrumented/FlameChartDataProvider.java +++ b/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/instrumented/FlameChartDataProvider.java @@ -45,28 +45,24 @@ import org.eclipse.tracecompass.internal.analysis.callstack.core.callstack.CallStackDepth; import org.eclipse.tracecompass.internal.analysis.callstack.core.callstack.CallStackSeries; import org.eclipse.tracecompass.internal.analysis.callstack.core.instrumented.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.tmf.core.model.filters.FetchParametersUtils; +import org.eclipse.tracecompass.internal.analysis.callstack.core.model.IHostModel; +import org.eclipse.tracecompass.internal.analysis.callstack.core.model.ModelManager; +import org.eclipse.tracecompass.internal.analysis.callstack.core.model.ProcessStatusInterval; import org.eclipse.tracecompass.segmentstore.core.ISegment; 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.dataprovider.DataProviderParameterUtils; import org.eclipse.tracecompass.tmf.core.model.AbstractTmfTraceDataProvider; import org.eclipse.tracecompass.tmf.core.model.CommonStatusMessage; import org.eclipse.tracecompass.tmf.core.model.IOutputStyleProvider; import org.eclipse.tracecompass.tmf.core.model.OutputElementStyle; import org.eclipse.tracecompass.tmf.core.model.OutputStyleModel; -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.ITimeGraphArrow; import org.eclipse.tracecompass.tmf.core.model.timegraph.ITimeGraphDataProvider; 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.ITimeGraphStateFilter; import org.eclipse.tracecompass.tmf.core.model.timegraph.TimeGraphArrow; -import org.eclipse.tracecompass.tmf.core.model.timegraph.TimeGraphEntryModel; import org.eclipse.tracecompass.tmf.core.model.timegraph.TimeGraphModel; import org.eclipse.tracecompass.tmf.core.model.timegraph.TimeGraphRowModel; import org.eclipse.tracecompass.tmf.core.model.timegraph.TimeGraphState; @@ -78,7 +74,6 @@ 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; @@ -88,9 +83,7 @@ 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.ImmutableMultimap; -import com.google.common.collect.Maps; import com.google.common.collect.Multimap; /** @@ -127,64 +120,16 @@ public class FlameChartDataProvider extends AbstractTmfTraceDataProvider impleme private static class TidInformation { private final HostThread fTid; - private final long fStart; - private final long fEnd; /* * The ID of the entry in this data provider where to put the thread * information */ private final Long fLinked; - public TidInformation(HostThread hostThread, long start, long end, Long linked) { + public TidInformation(HostThread hostThread, 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); - return new TimeGraphState(start, end - start, state.getLabel(), state.getStyle()); - } - return state; - } - } - - private static class ThreadData { - private final ThreadStatusDataProvider fThreadDataProvider; - private final List fThreadTree = new ArrayList<>(); - private final Status fStatus; - - public ThreadData(ThreadStatusDataProvider dataProvider, List threadTree, Status status) { - fThreadDataProvider = dataProvider; - for (TimeGraphEntryModel model : threadTree) { - if (model instanceof ThreadEntryModel) { - fThreadTree.add((ThreadEntryModel) model); - } - } - fStatus = status; - } - - public @Nullable Map fetchTooltip(int threadId, long time, @Nullable IProgressMonitor monitor) { - for (ThreadEntryModel entry : fThreadTree) { - if (entry.getThreadId() == threadId && entry.getStartTime() <= time && entry.getEndTime() >= time) { - TmfModelResponse> tooltip = fThreadDataProvider.fetchTooltip(FetchParametersUtils.selectionTimeQueryToMap(new SelectionTimeQueryFilter(Collections.singletonList(time), Collections.singleton(entry.getId()))), - monitor); - return tooltip.getModel(); - } - } - return null; - } } private final LoadingCache, @Nullable String> fTimeEventNames = Objects.requireNonNull(CacheBuilder.newBuilder() @@ -224,7 +169,6 @@ public ThreadData(ThreadStatusDataProvider dataProvider, List> fCached; - private @Nullable ThreadData fThreadData = null; /** * Constructor @@ -324,13 +268,13 @@ public TmfModelResponse> fetchTooltip(Map fe return new TmfModelResponse<>(null, Status.COMPLETED, CommonStatusMessage.COMPLETED); } Entry entry = entries.entrySet().iterator().next(); - Map tooltip = getTooltip(entry.getKey(), entry.getValue(), times.get(0), monitor); + Map tooltip = getTooltip(entry.getKey(), entry.getValue(), times.get(0)); return new TmfModelResponse<>(tooltip, Status.COMPLETED, CommonStatusMessage.COMPLETED); } } - private @Nullable Map getTooltip(Long entryId, FlameChartEntryModel entryModel, Long time, @Nullable IProgressMonitor monitor) { + private @Nullable Map getTooltip(Long entryId, FlameChartEntryModel entryModel, Long time) { switch (entryModel.getEntryType()) { case FUNCTION: { CallStackDepth selectedDepth = fIdToCallstack.get(entryId); @@ -359,23 +303,6 @@ public TmfModelResponse> fetchTooltip(Map fe 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(entryId); - if (csId == null) { - return null; - } - CallStackDepth selectedDepth = fIdToCallstack.get(csId); - - if (selectedDepth == null) { - return null; - } - int threadId = selectedDepth.getCallStack().getThreadId(time); - ThreadData threadData = fThreadData; - if (threadData == null) { - return null; - } - return threadData.fetchTooltip(threadId, time, monitor); case LEVEL: case TRACE: default: @@ -430,16 +357,11 @@ public TmfModelResponse> fetchTree(Map(null, ITmfResponse.Status.CANCELLED, CommonStatusMessage.TASK_CANCELLED); } - needsKernel |= processCallStackElement(element, builder, callStackRoot); - } - // Initialize the thread status data provider - if (needsKernel) { - prepareKernelData(monitor, start); + processCallStackElement(element, builder, callStackRoot); } List tree = builder.build(); tree.forEach(entry -> fEntries.put(entry.getId(), entry)); @@ -458,31 +380,8 @@ public TmfModelResponse> fetchTree(Map tracesForHost = TmfTraceManager.getInstance().getTracesForHost(getTrace().getHostId()); - for (ITmfTrace trace : tracesForHost) { - ThreadStatusDataProvider dataProvider = DataProviderManager.getInstance().getOrCreateDataProvider(trace, ThreadStatusDataProvider.ID, ThreadStatusDataProvider.class); - if (dataProvider != null) { - // Get the tree for the trace's current range - TmfModelResponse> threadTreeResp = dataProvider.fetchTree(FetchParametersUtils.timeQueryToMap(new TimeQueryFilter(start, Long.MAX_VALUE, 2)), monitor); - TmfTreeModel threadTree = threadTreeResp.getModel(); - if (threadTree != null) { - fThreadData = new ThreadData(dataProvider, threadTree.getEntries(), threadTreeResp.getStatus()); - break; - } - } - } - } - - private boolean processCallStackElement(ICallStackElement element, Builder builder, FlameChartEntryModel parentEntry) { + private void processCallStackElement(ICallStackElement element, Builder builder, FlameChartEntryModel parentEntry) { long elementId = getEntryId(element); - boolean needsKernel = false; // Is this an intermediate or leaf element if ((element instanceof InstrumentedCallStackElement) && element.isLeaf()) { @@ -500,22 +399,25 @@ private boolean processCallStackElement(ICallStackElement element, Builder entries) { } // 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()); - } + rows.computeIfAbsent(key, k -> Collections.emptyList()); } if (!tids.isEmpty()) { rows.putAll(getKernelStates(tids, times, predicates, subMonitor)); @@ -658,90 +558,29 @@ private void addRequiredCallstacks(Map entries) { } private Map> getKernelStates(List tids, List times, Map>> predicates, SubMonitor monitor) { - // Get the thread statuses from the thread status provider - ThreadData threadData = fThreadData; - if (threadData == null) { - return Collections.emptyMap(); - } - List 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 threadModelIds = filterThreads(tree, tids); - SelectionTimeQueryFilter tidFilter = new SelectionTimeQueryFilter(times, threadModelIds.keySet()); - TmfModelResponse rowModel = threadData.fThreadDataProvider.fetchRowModel(FetchParametersUtils.selectionTimeQueryToMap(tidFilter), monitor); - TimeGraphModel rowModels = rowModel.getModel(); - if (rowModel.getStatus() == Status.CANCELLED || rowModel.getStatus() == Status.FAILED || rowModels == null) { - return Collections.emptyMap(); - } - return mapThreadStates(rowModels.getRows(), threadModelIds, tids, predicates, monitor); - } - - private Map> mapThreadStates(List rowModels, BiMap threadModelIds, List tids, Map>> predicates, SubMonitor monitor) { - ImmutableMap statusRows = Maps.uniqueIndex(rowModels, m -> m.getEntryID()); - // Match the states of thread status to the requested tid lines - Long prevId = -1L; - List states = null; - Map> kernelStatuses = new HashMap<>(); - // The tid information data are ordered by id and times - for (TidInformation tidInfo : tids) { - // Get the ID of the linked callstack entry ID to get the filter - // data - Long linkedCsEntryId = fLinkedEntries.get(tidInfo.fLinked); - if (linkedCsEntryId == null) { - // fallback to the entry's own ID - linkedCsEntryId = tidInfo.fLinked; - } - Long tidEntryId = threadModelIds.inverse().get(tidInfo.fTid.getTid()); - if (tidEntryId == null) { + Map> kernelStates = new HashMap<>(); + + IHostModel model = ModelManager.getModelFor(getTrace().getHostId()); + Map threadIdsToEntryIds = tids.stream().collect(Collectors.toMap(tid -> tid.fTid.getTid(), tid -> tid.fLinked)); + Map> kernelStatuses = model.getThreadStatusIntervals(threadIdsToEntryIds.keySet(), times, monitor); + for (Entry> processStatuses : kernelStatuses.entrySet()) { + Long linkedEntryId = threadIdsToEntryIds.get(processStatuses.getKey()); + if (linkedEntryId == null) { continue; } - ITimeGraphRowModel rowModel = statusRows.get(tidEntryId); - if (!Objects.equals(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)) { - ITimeGraphState timeGraphState = tidInfo.sanitize(state); - // Use the callstack entry's filter data - applyFilterAndAddState(states, timeGraphState, linkedCsEntryId, predicates, monitor); - } - if (!tidInfo.precedes(state)) { - break; - } + Long entryId = fLinkedEntries.get(linkedEntryId); + if (entryId == null) { + entryId = linkedEntryId; } - prevId = tidInfo.fLinked; - } - if (states != null) { - kernelStatuses.put(prevId, states); - } - return kernelStatuses; - } - - private static BiMap filterThreads(List model, List tids) { - // Get the entry model IDs that match requested tids - BiMap tidEntries = HashBiMap.create(); - Set 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. - } + List states = kernelStates.computeIfAbsent(linkedEntryId, k -> new ArrayList<>()); + for (ProcessStatusInterval processStatus : Objects.requireNonNull(processStatuses.getValue())) { + OutputElementStyle style = new OutputElementStyle( + FlameWithKernelPalette.getStyleFor(processStatus.getProcessStatus().getStateValue().unboxInt())); + ITimeGraphState state = new TimeGraphState(processStatus.getStart(), processStatus.getLength(), processStatus.getSyscallName(), style); + applyFilterAndAddState(states, state, entryId, predicates, monitor); } } - return tidEntries; + return kernelStates; } private static Collection getKernelTids(CallStackDepth callStackDepth, Collection states, Long linked) { @@ -752,7 +591,7 @@ private static Collection getKernelTids(CallStackDepth callStack // query HostThread hostThread = callStack.getHostThread(); if (hostThread != null) { - tids.add(new TidInformation(hostThread, Long.MIN_VALUE, Long.MAX_VALUE, linked)); + tids.add(new TidInformation(hostThread, linked)); } return tids; } @@ -764,7 +603,7 @@ private static Collection getKernelTids(CallStackDepth callStack ICalledFunction function = (ICalledFunction) state; HostThread hostThread = callStack.getHostThread(function.getStart()); if (hostThread != null) { - tids.add(new TidInformation(hostThread, function.getStart(), function.getEnd(), linked)); + tids.add(new TidInformation(hostThread, linked)); } } return tids; @@ -850,7 +689,7 @@ public void resetFunctionNames(IProgressMonitor monitor) { if (entryModel == null) { return data; } - Map tooltip = getTooltip(entryId, entryModel, time, monitor); + Map tooltip = getTooltip(entryId, entryModel, time); if (tooltip == null) { return data; } diff --git a/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/model/CompositeHostModel.java b/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/model/CompositeHostModel.java index 84686de059..d9f1269eae 100644 --- a/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/model/CompositeHostModel.java +++ b/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/model/CompositeHostModel.java @@ -15,20 +15,27 @@ import java.util.Collection; import java.util.Collections; import java.util.EnumSet; +import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import java.util.NoSuchElementException; import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.WeakHashMap; +import java.util.stream.Collectors; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.tracecompass.analysis.os.linux.core.kernel.KernelAnalysisModule; import org.eclipse.tracecompass.analysis.os.linux.core.kernel.KernelThreadInformationProvider; import org.eclipse.tracecompass.analysis.os.linux.core.model.ProcessStatus; import org.eclipse.tracecompass.internal.analysis.callstack.core.callgraph.AggregatedCallSite; import org.eclipse.tracecompass.internal.analysis.callstack.core.model.ModelListener.IModuleWrapper; +import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem; import org.eclipse.tracecompass.statesystem.core.interval.ITmfStateInterval; import org.eclipse.tracecompass.tmf.core.analysis.IAnalysisModule; import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler; @@ -50,14 +57,10 @@ public class CompositeHostModel implements IHostModel { private final Multimap fTraceObjectMap = HashMultimap.create(); - @SuppressWarnings("null") - private final Set fCpuTimeProviders = Objects.requireNonNull(Collections.newSetFromMap(new WeakHashMap())); - @SuppressWarnings("null") - private final Set fThreadOnCpuProviders = Objects.requireNonNull(Collections.newSetFromMap(new WeakHashMap())); - @SuppressWarnings("null") - private final Set fSamplingDataProviders = Objects.requireNonNull(Collections.newSetFromMap(new WeakHashMap())); - @SuppressWarnings("null") - private final Set fKernelModules = Objects.requireNonNull(Collections.newSetFromMap(new WeakHashMap())); + private final Set fCpuTimeProviders = Objects.requireNonNull(Collections.newSetFromMap(new WeakHashMap<>())); + private final Set fThreadOnCpuProviders = Objects.requireNonNull(Collections.newSetFromMap(new WeakHashMap<>())); + private final Set fSamplingDataProviders = Objects.requireNonNull(Collections.newSetFromMap(new WeakHashMap<>())); + private final Set fKernelModules = Objects.requireNonNull(Collections.newSetFromMap(new WeakHashMap<>())); private final String fHostId; @@ -218,11 +221,13 @@ private static class ThreadStatusIterator implements Iterator fIntervalIterator; private final long fStart; private final long fEnd; + private final int fTid; - public ThreadStatusIterator(long start, long end, Iterator iter) { + public ThreadStatusIterator(long start, long end, int tid, Iterator iter) { fIntervalIterator = iter; fStart = start; fEnd = end; + fTid = tid; } @Override @@ -237,8 +242,8 @@ public ProcessStatusInterval next() { } ITmfStateInterval interval = fIntervalIterator.next(); long start = Math.max(interval.getStartTime(), fStart); - long end = Math.min(interval.getEndTime(), fEnd); - return new ProcessStatusInterval(start, end, ProcessStatus.getStatusFromStateValue(interval.getStateValue())); + long end = Math.min(interval.getEndTime() + 1, fEnd); + return new ProcessStatusInterval(start, end, fTid, ProcessStatus.getStatusFromStateValue(interval.getStateValue()), null); } } @@ -264,7 +269,7 @@ public ThreadStatusIterable(long start, long end, KernelAnalysisModule module, i @Override public Iterator iterator() { - return new ThreadStatusIterator(fStart, fEnd, KernelThreadInformationProvider.getStatusIntervalsForThread(fModule, fTid, fStart, fEnd, fResolution)); + return new ThreadStatusIterator(fStart, fEnd, fTid, KernelThreadInformationProvider.getStatusIntervalsForThread(fModule, fTid, fStart, fEnd, fResolution)); } } @@ -281,6 +286,57 @@ public Iterable getThreadStatusIntervals(int tid, long st return Objects.requireNonNull(Collections.emptyList()); } + @Override + public Map> getThreadStatusIntervals(Collection tids, Collection times, IProgressMonitor monitor) { + Iterable modules = TmfTraceUtils.getAnalysisModulesOfClass(fHostId, KernelAnalysisModule.class); + if (modules.iterator().hasNext()) { + KernelAnalysisModule module = modules.iterator().next(); + @NonNull Map> kernelStatusIntervals = KernelThreadInformationProvider.getStatusIntervalsForThreads(module, tids, times, monitor); + // Mapping ITmfStateInterval to ProcessStatusInterval + Map> threadStatusIntervals = new HashMap<>(); + for (Entry> intervalsEntry : kernelStatusIntervals.entrySet()) { + List intervals = Objects.requireNonNull(intervalsEntry.getValue()); + threadStatusIntervals.put(intervalsEntry.getKey(), intervals.stream() + .map(i -> { + Object state = i.getValue(); + if (state instanceof String) { + return new ProcessStatusInterval(i.getStartTime(), i.getEndTime() + 1, intervalsEntry.getKey(), ProcessStatus.RUN_SYTEMCALL, i.getValueString()); + } + return new ProcessStatusInterval(i.getStartTime(), i.getEndTime() + 1, intervalsEntry.getKey(), ProcessStatus.getStatusFromStateValue(i.getStateValue()), null); + }) + .collect(Collectors.toList())); + } + return threadStatusIntervals; + } + return Objects.requireNonNull(Collections.emptyMap()); + } + + @Override + public long getStartTime() { + Optional start = fKernelModules.stream().map(v -> { + ITmfStateSystem ss = v.getStateSystem(); + @NonNull Long startTime = ss != null ? ss.getStartTime() : Long.MIN_VALUE; + return startTime; + }).max(Long::compare); + if (start.isPresent()) { + return start.get(); + } + return Long.MIN_VALUE; + } + + @Override + public long getEndTime() { + Optional end = fKernelModules.stream().map(v -> { + ITmfStateSystem ss = v.getStateSystem(); + @NonNull Long endTime = ss != null ? ss.getCurrentEndTime() : Long.MAX_VALUE; + return endTime; + }).min(Long::compare); + if (end.isPresent()) { + return end.get(); + } + return Long.MAX_VALUE; + } + @Override public boolean isSamplingDataAvailable() { return !fSamplingDataProviders.isEmpty(); diff --git a/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/model/IHostModel.java b/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/model/IHostModel.java index dbce3f24c0..ad5c9f702f 100644 --- a/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/model/IHostModel.java +++ b/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/model/IHostModel.java @@ -13,7 +13,9 @@ import java.util.Collection; import java.util.EnumSet; +import java.util.Map; +import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.tracecompass.internal.analysis.callstack.core.callgraph.AggregatedCallSite; import org.eclipse.tracecompass.tmf.core.analysis.IAnalysisModule; @@ -164,6 +166,34 @@ default int getThreadOnCpu(int cpu, long t) { */ Iterable getThreadStatusIntervals(int tid, long start, long end, long resolution); + /** + * Get the start time of the underlying model analyses. + * + * @return The start time + */ + long getStartTime(); + + /** + * Get the end time of the underlying model analyses. + * + * @return The end time + */ + long getEndTime(); + + /** + * Get a map with an entry for each tid and an iterable with all the process + * status intervals. + * + * @param tids + * The tids for which to get the intervals + * @param times + * The times requested from the query parameters + * @param monitor + * A monitor to cancel the query + * @return A map with an iterable of status intervals for each tid entry + */ + Map> getThreadStatusIntervals(Collection tids, Collection times, IProgressMonitor monitor); + /** * Get whether sampling data is available for this host * diff --git a/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/model/ProcessStatusInterval.java b/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/model/ProcessStatusInterval.java index efc7d03188..91f6d6cdad 100644 --- a/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/model/ProcessStatusInterval.java +++ b/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/model/ProcessStatusInterval.java @@ -11,6 +11,7 @@ package org.eclipse.tracecompass.internal.analysis.callstack.core.model; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.tracecompass.analysis.os.linux.core.model.ProcessStatus; import org.eclipse.tracecompass.segmentstore.core.ISegment; @@ -28,7 +29,9 @@ public class ProcessStatusInterval implements ISegment { private final long fStartTime; private final long fEndTime; + private final int fThreadId; private final ProcessStatus fStatus; + private final @Nullable String fSyscallName; /** * Constructor @@ -37,13 +40,19 @@ public class ProcessStatusInterval implements ISegment { * The start time of this interval * @param end * The end time of this interval + * @param threadId + * The thread id of this interval * @param status * The status of this interval + * @param syscallName + * The syscall name if necessary */ - public ProcessStatusInterval(long start, long end, ProcessStatus status) { + public ProcessStatusInterval(long start, long end, int threadId, ProcessStatus status, @Nullable String syscallName) { fStartTime = start; fEndTime = end; + fThreadId = threadId; fStatus = status; + fSyscallName = syscallName; } @Override @@ -56,6 +65,15 @@ public long getEnd() { return fEndTime; } + /** + * Gets the thread id represented by this interval + * + * @return The thread id + */ + public int getThreadId() { + return fThreadId; + } + /** * Get the process status represented by this interval * @@ -64,4 +82,13 @@ public long getEnd() { public ProcessStatus getProcessStatus() { return fStatus; } + + /** + * Get the syscall name represented by this interval + * + * @return The syscall name for this interval + */ + public @Nullable String getSyscallName() { + return fSyscallName; + } } diff --git a/analysis/org.eclipse.tracecompass.analysis.os.linux.core/META-INF/MANIFEST.MF b/analysis/org.eclipse.tracecompass.analysis.os.linux.core/META-INF/MANIFEST.MF index 73b5ddc8f4..cba5f38d17 100644 --- a/analysis/org.eclipse.tracecompass.analysis.os.linux.core/META-INF/MANIFEST.MF +++ b/analysis/org.eclipse.tracecompass.analysis.os.linux.core/META-INF/MANIFEST.MF @@ -48,7 +48,8 @@ Export-Package: org.eclipse.tracecompass.analysis.os.linux.core.contextswitch, x-friends:="org.eclipse.tracecompass.analysis.callstack.core, org.eclipse.tracecompass.analysis.os.linux.core.tests, org.eclipse.tracecompass.analysis.os.linux.ui, - org.eclipse.tracecompass.lttng2.kernel.core.tests", + org.eclipse.tracecompass.lttng2.kernel.core.tests, + org.eclipse.tracecompass.analysis.callstack.core.tests", org.eclipse.tracecompass.internal.analysis.os.linux.core.kernel.handlers;x-friends:="org.eclipse.tracecompass.analysis.os.linux.core.tests", org.eclipse.tracecompass.internal.analysis.os.linux.core.kernelmemoryusage;x-friends:="org.eclipse.tracecompass.analysis.os.linux.ui", org.eclipse.tracecompass.internal.analysis.os.linux.core.latency; @@ -60,7 +61,8 @@ Export-Package: org.eclipse.tracecompass.analysis.os.linux.core.contextswitch, x-friends:="org.eclipse.tracecompass.analysis.callstack.core, org.eclipse.tracecompass.analysis.os.linux.core.tests, org.eclipse.tracecompass.analysis.os.linux.ui, - org.eclipse.tracecompass.lttng2.kernel.core.tests", + org.eclipse.tracecompass.lttng2.kernel.core.tests, + org.eclipse.tracecompass.analysis.callstack.core.tests", org.eclipse.tracecompass.internal.analysis.os.linux.core.resourcesstatus;x-friends:="org.eclipse.tracecompass.analysis.os.linux.ui", org.eclipse.tracecompass.internal.analysis.os.linux.core.threadstatus; x-friends:="org.eclipse.tracecompass.analysis.callstack.core, diff --git a/analysis/org.eclipse.tracecompass.analysis.os.linux.core/src/org/eclipse/tracecompass/analysis/os/linux/core/kernel/KernelThreadInformationProvider.java b/analysis/org.eclipse.tracecompass.analysis.os.linux.core/src/org/eclipse/tracecompass/analysis/os/linux/core/kernel/KernelThreadInformationProvider.java index 7743809542..055277d756 100644 --- a/analysis/org.eclipse.tracecompass.analysis.os.linux.core/src/org/eclipse/tracecompass/analysis/os/linux/core/kernel/KernelThreadInformationProvider.java +++ b/analysis/org.eclipse.tracecompass.analysis.os.linux.core/src/org/eclipse/tracecompass/analysis/os/linux/core/kernel/KernelThreadInformationProvider.java @@ -16,8 +16,13 @@ import java.util.Collection; import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.Map; +import java.util.NavigableSet; +import java.util.Objects; import java.util.Set; import java.util.TreeSet; import java.util.function.Predicate; @@ -29,6 +34,7 @@ import org.eclipse.tracecompass.analysis.os.linux.core.model.ProcessStatus; import org.eclipse.tracecompass.common.core.NonNullUtils; import org.eclipse.tracecompass.internal.analysis.os.linux.core.kernel.Attributes; +import org.eclipse.tracecompass.internal.analysis.os.linux.core.kernel.StateValues; import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem; import org.eclipse.tracecompass.statesystem.core.StateSystemUtils; import org.eclipse.tracecompass.statesystem.core.StateSystemUtils.QuarkIterator; @@ -36,10 +42,12 @@ 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.statesystem.core.interval.TmfStateInterval; import org.eclipse.tracecompass.statesystem.core.statevalue.ITmfStateValue; import org.eclipse.tracecompass.statesystem.core.statevalue.ITmfStateValue.Type; import com.google.common.collect.ImmutableSet; +import com.google.common.collect.TreeMultimap; /** * Information provider utility class that retrieves thread-related information @@ -119,8 +127,8 @@ private KernelThreadInformationProvider() { List threadQuarks = ss.getSubAttributes(threadsQuark, false); return threadQuarks.stream() /* - * Keep only the quarks of threads that are on at least one of the - * wanted CPUs' run queue. + * Keep only the quarks of threads that are on at least one of + * the wanted CPUs' run queue. */ .filter(threadQuark -> { int threadCurrentCpuQuark = ss.optQuarkRelative(threadQuark, Attributes.CURRENT_CPU_RQ); @@ -128,7 +136,10 @@ private KernelThreadInformationProvider() { return false; } - /* Check if the thread was seen on any of the requested CPUs. */ + /* + * Check if the thread was seen on any of the requested + * CPUs. + */ QuarkIterator it = new QuarkIterator(ss, threadCurrentCpuQuark, rangeStart, rangeEnd); while (it.hasNext()) { Object o = it.next().getValue(); @@ -158,8 +169,8 @@ private KernelThreadInformationProvider() { }; /** - * Return all the threads that are considered active in the given time range. - * Threads with TID 0 (swapper threads) will never be included. + * Return all the threads that are considered active in the given time + * range. Threads with TID 0 (swapper threads) will never be included. * * @param module * The kernel analysis module to query @@ -167,9 +178,9 @@ private KernelThreadInformationProvider() { * The start of the time range * @param rangeEnd * The end of the time range - * @return A set of all the thread IDs that are considered active in the time - * range. Empty set if there are none or if unavailable for the time - * range. + * @return A set of all the thread IDs that are considered active in the + * time range. Empty set if there are none or if unavailable for the + * time range. * @since 2.5 */ public static Set getActiveThreadsForRange(KernelAnalysisModule module, long rangeStart, long rangeEnd) { @@ -197,7 +208,6 @@ public static Set getActiveThreadsForRange(KernelAnalysisModule module, return Collections.emptySet(); } - List threadQuarks = ss.getSubAttributes(threadsQuark, false); return threadQuarks.stream() /* @@ -234,7 +244,10 @@ public static Set getActiveThreadsForRange(KernelAnalysisModule module, return true; } } - /* We haven't found an active state value in the whole range. */ + /* + * We haven't found an active state value in the whole + * range. + */ return false; }) @@ -261,7 +274,7 @@ public static Collection getThreadIds(KernelAnalysisModule module) { int threadQuark; try { threadQuark = ss.getQuarkAbsolute(Attributes.THREADS); - Set<@NonNull Integer> tids = new TreeSet<>(); + Set tids = new TreeSet<>(); for (Integer quark : ss.getSubAttributes(threadQuark, false)) { final @NonNull String attributeName = ss.getAttributeName(quark); tids.add(attributeName.startsWith(Attributes.THREAD_0_PREFIX) ? 0 : Integer.parseInt(attributeName)); @@ -360,7 +373,7 @@ public static Collection getThreadIds(KernelAnalysisModule module) { */ public static @Nullable String getExecutableName(KernelAnalysisModule module, Integer threadId) { ITmfStateSystem stateSystem = module.getStateSystem(); - if(stateSystem == null) { + if (stateSystem == null) { return null; } return getExecutableName(module, threadId, stateSystem.getCurrentEndTime()); @@ -378,8 +391,8 @@ public static Collection getThreadIds(KernelAnalysisModule module) { * never seen to be used in this trace, and * {@link #getExecutableName(KernelAnalysisModule, Integer, long)} returns * null - *
  • ts > trace end: the name of the last thread to run is returned. - * (could be null if the tid was never used)
  • + *
  • ts > trace end: the name of the last thread to run is + * returned. (could be null if the tid was never used)
  • *
  • thread start > ts > thread end: the thread name will be the * thread name of the process at that time. This may be a parent as the * thread inherrits the parent's name until it is changed.
  • @@ -482,7 +495,8 @@ public static List getStatusIntervalsForThread(KernelAnalysis } /** - * Get an iterator for the status intervals of a given thread in a time range + * Get an iterator for the status intervals of a given thread in a time + * range * * @param module * The kernel analysis instance to run this method on @@ -493,8 +507,9 @@ public static List getStatusIntervalsForThread(KernelAnalysis * @param end * The end time of the requested range * @param resolution - * The resolution, ie the number of nanoseconds between kernel status - * queries. A value lower or equal to 1 will return all intervals + * The resolution, ie the number of nanoseconds between kernel + * status queries. A value lower or equal to 1 will return all + * intervals * @return The list of status intervals for this thread, an empty list is * returned if either the state system is {@code null} or the quark * is not found @@ -513,4 +528,97 @@ public static Iterator getStatusIntervalsForThread(KernelAnal return new StateSystemUtils.QuarkIterator(ss, threadQuark, start, end - 1, resolution); } + /** + * Get a map of each tid with their associated thread status intervals + * containing syscalls if present + * + * @param module + * The kernel analysis instance to run this method on + * @param threadIds + * The ID of the threads to get the intervals for + * @param times + * The times to execute the query on + * @param monitor + * The monitor to cancel the query + * @return A Map associating the intervals to each tid + * @since 8.1 + */ + public static Map> getStatusIntervalsForThreads(KernelAnalysisModule module, Collection threadIds, Collection times, IProgressMonitor monitor) { + ITmfStateSystem ss = module.getStateSystem(); + if (ss == null) { + return Objects.requireNonNull(Collections.emptyMap()); + } + Map quarkToThreadIds = getQuarkToThreadIds(module, threadIds); + TreeMultimap intervals = TreeMultimap.create(Comparator.naturalOrder(), + Comparator.comparing(ITmfStateInterval::getStartTime)); + Map> kernelStatuses = new HashMap<>(); + try { + for (ITmfStateInterval interval : ss.query2D(quarkToThreadIds.keySet(), times)) { + if (monitor.isCanceled()) { + return Objects.requireNonNull(Collections.emptyMap()); + } + String threadId = quarkToThreadIds.get(interval.getAttribute()); + if (threadId != null) { + intervals.put(threadId, interval); + } + } + for (Integer threadId : threadIds) { + NavigableSet states = intervals.get(threadId.toString()); + if (monitor.isCanceled()) { + return Collections.emptyMap(); + } + NavigableSet syscalls = intervals.get(threadId.toString() + Attributes.SYSTEM_CALL); + if (syscalls != null) { + kernelStatuses.put(threadId, states.stream().map(i -> addSyscallInformation(i, syscalls)).collect(Collectors.toList())); + continue; + } + kernelStatuses.put(threadId, Objects.requireNonNull(states.stream().toList())); + } + return kernelStatuses; + } catch (IndexOutOfBoundsException | TimeRangeException | StateSystemDisposedException e) { + // Do nothing + } + return Objects.requireNonNull(Collections.emptyMap()); + } + + private static ITmfStateInterval addSyscallInformation(ITmfStateInterval interval, NavigableSet syscalls) { + Object status = interval.getValue(); + if (status instanceof Integer && ((int) status) == StateValues.PROCESS_STATUS_RUN_SYSCALL) { + // intervals are sorted by start time + ITmfStateInterval syscall = syscalls.floor(interval); + + if (syscall != null) { + Object value = syscall.getValue(); + if (value instanceof String) { + return new TmfStateInterval(interval.getStartTime(), interval.getEndTime(), interval.getAttribute(), String.valueOf(value)); + } + } + } + return interval; + } + + /** + * @param module + * The analysis module to query the state system + * @param threadIds + * the threadIds to select the quarks + * @return A map associating the quarks to the thread ids or their syscall + * quarks + */ + private static Map getQuarkToThreadIds(KernelAnalysisModule module, Collection threadIds) { + ITmfStateSystem ss = module.getStateSystem(); + if (ss == null) { + return Objects.requireNonNull(Collections.emptyMap()); + } + Map quarkToThreadIds = new HashMap<>(); + for (Integer threadId : threadIds) { + int threadQuark = ss.optQuarkAbsolute(Attributes.THREADS, threadId.toString()); + quarkToThreadIds.put(threadQuark, threadId.toString()); + int syscallQuark = ss.optQuarkRelative(threadQuark, Attributes.SYSTEM_CALL); + if (syscallQuark != ITmfStateSystem.INVALID_ATTRIBUTE) { + quarkToThreadIds.put(syscallQuark, threadId.toString() + Attributes.SYSTEM_CALL); + } + } + return quarkToThreadIds; + } }