diff --git a/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/META-INF/MANIFEST.MF b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/META-INF/MANIFEST.MF index 7d65271099..bb05ceca54 100644 --- a/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/META-INF/MANIFEST.MF +++ b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/META-INF/MANIFEST.MF @@ -12,17 +12,25 @@ Automatic-Module-Name: org.eclipse.tracecompass.analysis.callstack.core.tests Require-Bundle: org.eclipse.core.resources, org.eclipse.core.runtime, org.eclipse.jdt.annotation;bundle-version="[2.0.0,3.0.0)";resolution:=optional, + org.eclipse.test.performance, org.eclipse.tracecompass.analysis.callstack.core, org.eclipse.tracecompass.analysis.os.linux.core, org.eclipse.tracecompass.analysis.profiling.core, org.eclipse.tracecompass.analysis.timing.core, org.eclipse.tracecompass.common.core, + org.eclipse.tracecompass.ctf.core.tests, org.eclipse.tracecompass.datastore.core, + org.eclipse.tracecompass.lttng2.common.core, + org.eclipse.tracecompass.lttng2.ust.core, org.eclipse.tracecompass.segmentstore.core, org.eclipse.tracecompass.statesystem.core.tests, + org.eclipse.tracecompass.testtraces.tracecompass-test-traces-ctf, org.eclipse.tracecompass.tmf.core, org.eclipse.tracecompass.tmf.core.tests, + org.eclipse.tracecompass.tmf.ctf.core, + org.eclipse.tracecompass.tmf.ctf.core.tests, org.junit Export-Package: org.eclipse.tracecompass.analysis.callstack.core.tests, + org.eclipse.tracecompass.analysis.callstack.core.tests.perf, org.eclipse.tracecompass.analysis.callstack.core.tests.stubs Import-Package: com.google.common.collect diff --git a/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/src/org/eclipse/tracecompass/analysis/callstack/core/tests/LTTngUstCallStackAnalysisRequirementTest.java b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/src/org/eclipse/tracecompass/analysis/callstack/core/tests/LTTngUstCallStackAnalysisRequirementTest.java new file mode 100644 index 0000000000..34d666ab78 --- /dev/null +++ b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/src/org/eclipse/tracecompass/analysis/callstack/core/tests/LTTngUstCallStackAnalysisRequirementTest.java @@ -0,0 +1,114 @@ +/******************************************************************************* + * Copyright (c) 2016 Ericsson + * + * 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; + +import static org.junit.Assert.assertEquals; + +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.tracecompass.internal.analysis.callstack.core.LttngUstCallStackAnalysisRequirement; +import org.eclipse.tracecompass.lttng2.ust.core.trace.LttngUstTrace; +import org.eclipse.tracecompass.lttng2.ust.core.trace.layout.ILttngUstEventLayout; +import org.eclipse.tracecompass.tmf.ctf.core.event.CtfTmfEventType; +import org.junit.AfterClass; +import org.junit.Test; + +import com.google.common.collect.ImmutableSet; + +/** + * Test the {@link LttngUstCallStackAnalysisRequirement} class + * + * @author Bernd Hufmann + */ +public class LTTngUstCallStackAnalysisRequirementTest { + + private static final @NonNull String FUNC_EXIT_FAST = "lttng_ust_cyg_profile_fast:func_exit"; + private static final @NonNull String FUNC_EXIT = "lttng_ust_cyg_profile:func_exit"; + private static final @NonNull String FUNC_ENTRY_FAST = "lttng_ust_cyg_profile_fast:func_entry"; + private static final @NonNull String FUNC_ENTRY = "lttng_ust_cyg_profile:func_entry"; + private static final @NonNull String OTHER_EVENT = "OTHER"; + + enum EventType { + + EVT_EXIT_FAST(FUNC_EXIT_FAST), + EVT_EXIT(FUNC_EXIT), + EVT_ENTRY_FAST(FUNC_ENTRY_FAST), + EVT_ENTRY(FUNC_ENTRY), + EVT_OTHER(OTHER_EVENT); + + private final @NonNull CtfTmfEventType fType; + + EventType(@NonNull String name) { + fType = new CtfTmfEventType(name, null) { + @Override + public String getName() { + return name; + } + }; + } + + @NonNull CtfTmfEventType getEventType() { + return fType; + } + } + + enum TestData { + + TRACE_WITH_VALID_EVENTS(EventType.EVT_ENTRY, EventType.EVT_EXIT, true), + TRACE_WITH_VALID_EVENTS_FAST(EventType.EVT_ENTRY_FAST, EventType.EVT_EXIT_FAST, true), + TRACE_WITH_MISSING_EVENTS(EventType.EVT_OTHER, EventType.EVT_EXIT_FAST, false), + TRACE_MISMATCH_EVENTS(EventType.EVT_ENTRY_FAST, EventType.EVT_EXIT, false); + + private final @NonNull LttngUstTrace fTrace; + private final boolean fIsValid; + + TestData(EventType first, EventType second, boolean isValid) { + fTrace = new LttngUstTrace() { + @Override + public Set getContainedEventTypes() { + return ImmutableSet.of(first.getEventType(), second.getEventType()); + } + }; + fIsValid = isValid; + } + + @NonNull LttngUstTrace getTrace() { + return fTrace; + } + + boolean isValid() { + return fIsValid; + } + } + + /** + * Clean up + */ + @AfterClass + public static void cleanup() { + for (TestData testData : TestData.values()) { + testData.getTrace().dispose(); + } + } + + /** + * Test Call Stack Analysis requirements + */ + @Test + public void testCallStackRequirements() { + LttngUstCallStackAnalysisRequirement req = new LttngUstCallStackAnalysisRequirement(ILttngUstEventLayout.DEFAULT_LAYOUT); + for (TestData item : TestData.values()) { + assertEquals(item.name(), item.isValid(), req.test(item.getTrace())); + } + } +} diff --git a/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/src/org/eclipse/tracecompass/analysis/callstack/core/tests/perf/CallStackAndGraphBenchmark.java b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/src/org/eclipse/tracecompass/analysis/callstack/core/tests/perf/CallStackAndGraphBenchmark.java new file mode 100644 index 0000000000..296d39a484 --- /dev/null +++ b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/src/org/eclipse/tracecompass/analysis/callstack/core/tests/perf/CallStackAndGraphBenchmark.java @@ -0,0 +1,238 @@ +/******************************************************************************* + * Copyright (c) 2016 É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.perf; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; + +import org.eclipse.test.performance.Dimension; +import org.eclipse.test.performance.Performance; +import org.eclipse.test.performance.PerformanceMeter; +import org.eclipse.tracecompass.internal.analysis.callstack.core.callgraph.CallGraph; +import org.eclipse.tracecompass.internal.analysis.callstack.core.callgraph.ICallGraphProvider; +import org.eclipse.tracecompass.internal.analysis.callstack.core.instrumented.IFlameChartProvider; +import org.eclipse.tracecompass.internal.analysis.callstack.core.instrumented.InstrumentedCallStackAnalysis; +import org.eclipse.tracecompass.internal.analysis.callstack.core.tree.AllGroupDescriptor; +import org.eclipse.tracecompass.internal.analysis.callstack.core.tree.IWeightedTreeGroupDescriptor; +import org.eclipse.tracecompass.internal.analysis.callstack.core.tree.WeightedTreeGroupBy; +import org.eclipse.tracecompass.segmentstore.core.ISegment; +import org.eclipse.tracecompass.segmentstore.core.ISegmentStore; +import org.eclipse.tracecompass.tmf.core.analysis.IAnalysisModule; +import org.eclipse.tracecompass.tmf.core.exceptions.TmfTraceException; +import org.eclipse.tracecompass.tmf.core.signal.TmfTraceOpenedSignal; +import org.eclipse.tracecompass.tmf.core.tests.shared.TmfTestHelper; +import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp; +import org.eclipse.tracecompass.tmf.core.trace.TmfTrace; +import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager; +import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils; +import org.junit.Test; + +/** + * Benchmarks the flame chart analysis execution and call graph execution, + * partial callgraph for time ranges and group by of call graph. + * + * This base class can be extended by any performance test for analysis that + * implement {@link ICallGraphProvider}, whether or not it also implements + * {@link IFlameChartProvider}. + * + * @author Geneviève Bastien + */ +public abstract class CallStackAndGraphBenchmark { + + /** + * Test ID for kernel analysis benchmarks + */ + public static final String TEST_ID = "org.eclipse.tracecompass.analysis#CallStack#"; + + private static final String TEST_CALLSTACK_BUILD = "Building Callstack (%s)"; + private static final String TEST_CALLSTACK_PARSESEGSTORE = "Callstack segment store (%s)"; + private static final String TEST_CALLGRAPH_BUILD = "Building CallGraph (%s)"; + private static final String TEST_CALLGRAPH_QUERY = "CallGraph Query (%s)"; + private static final String TEST_CALLGRAPH_GROUPBY = "CallGraph Group By (%s)"; + + private static final byte[] SEED = { 0x45, 0x73, 0x74, 0x65, 0x6c, 0x6c, 0x65 }; + + private static final int LOOP_COUNT = 5; + + private final String fName; + private final String fAnalysisId; + + /** + * Constructor + * + * @param name + * The name of this test + * @param analysisId + * the ID of the analysis to run this benchmark on + */ + public CallStackAndGraphBenchmark(String name, String analysisId) { + fName = name; + fAnalysisId = analysisId; + } + + /** + * Run benchmark for the trace + * + * @throws TmfTraceException + * Exceptions thrown getting the trace + */ + @Test + public void runCpuBenchmark() throws TmfTraceException { + Performance perf = Performance.getDefault(); + PerformanceMeter callStackBuildPm = Objects.requireNonNull(perf.createPerformanceMeter(TEST_ID + String.format(TEST_CALLSTACK_BUILD, fName))); + perf.tagAsSummary(callStackBuildPm, String.format(TEST_CALLSTACK_BUILD, fName), Dimension.CPU_TIME); + PerformanceMeter callStackSegStorePm = Objects.requireNonNull(perf.createPerformanceMeter(TEST_ID + String.format(TEST_CALLSTACK_PARSESEGSTORE, fName))); + perf.tagAsSummary(callStackSegStorePm, String.format(TEST_CALLSTACK_PARSESEGSTORE, fName), Dimension.CPU_TIME); + PerformanceMeter callgraphBuildPm = Objects.requireNonNull(perf.createPerformanceMeter(TEST_ID + String.format(TEST_CALLGRAPH_BUILD, fName))); + perf.tagAsSummary(callgraphBuildPm, String.format(TEST_CALLGRAPH_BUILD, fName), Dimension.CPU_TIME); + PerformanceMeter callgraphQueryPm = perf.createPerformanceMeter(TEST_ID + String.format(TEST_CALLGRAPH_QUERY, fName)); + perf.tagAsSummary(callgraphQueryPm, String.format(TEST_CALLGRAPH_QUERY, fName), Dimension.CPU_TIME); + PerformanceMeter callgraphGroupByPm = perf.createPerformanceMeter(TEST_ID + String.format(TEST_CALLGRAPH_GROUPBY, fName)); + perf.tagAsSummary(callgraphGroupByPm, String.format(TEST_CALLGRAPH_GROUPBY, fName), Dimension.CPU_TIME); + + boolean isFlameChartProvider = false; + for (int i = 0; i < LOOP_COUNT; i++) { + TmfTrace trace = null; + try { + trace = getTrace(); + trace.traceOpened(new TmfTraceOpenedSignal(this, trace, null)); + IAnalysisModule analysisModule = TmfTraceUtils.getAnalysisModuleOfClass(trace, IAnalysisModule.class, fAnalysisId); + assertTrue(analysisModule instanceof ICallGraphProvider); + ICallGraphProvider callGraphModule = (ICallGraphProvider) analysisModule; + + if (analysisModule instanceof IFlameChartProvider) { + // Do the performance test for the instrumented call stack, + // then the call graph building + isFlameChartProvider = true; + benchmarkInstrumented((IFlameChartProvider) analysisModule, callStackBuildPm, callStackSegStorePm, callgraphBuildPm); + } else { + benchmarkCallGraphProvider(callGraphModule, callgraphBuildPm); + } + + /* + * Common benchmarks for both instrumented and profiled + * callgraphs + */ + // We just read the trace for the first time, so it should be + // safe to use the end time + long startTime = trace.getStartTime().toNanos(); + long endTime = trace.getEndTime().toNanos(); + long delta = endTime - startTime; + + // Get partial callgraphs + SecureRandom randomGenerator = new SecureRandom(SEED); + callgraphQueryPm.start(); + for (int j = 0; j < 50; j++) { + long time0 = Math.abs(randomGenerator.nextLong()) % delta; + long time1 = Math.abs(randomGenerator.nextLong()) % delta; + callGraphModule.getCallGraph(TmfTimestamp.fromNanos(startTime + Math.min(time0, time1)), TmfTimestamp.fromNanos(startTime + Math.max(time0, time1))); + } + callgraphQueryPm.stop(); + + // Benchmark the group by. Do a few iterations in different + // orders + List descriptors = new ArrayList<>(); + descriptors.add(AllGroupDescriptor.getInstance()); + descriptors.addAll(callGraphModule.getGroupDescriptors()); + CallGraph callGraphToGroup = callGraphModule.getCallGraph(); + callgraphGroupByPm.start(); + for (int j = 0; j < 10; j++) { + descriptors.forEach(group -> WeightedTreeGroupBy.groupWeightedTreeBy(group, callGraphToGroup, callGraphModule)); + Collections.reverse(descriptors); + descriptors.forEach(group -> WeightedTreeGroupBy.groupWeightedTreeBy(group, callGraphToGroup, callGraphModule)); + } + callgraphGroupByPm.stop(); + + /* + * Delete the supplementary files, so that the next iteration + * rebuilds the state system. + */ + File suppDir = new File(TmfTraceManager.getSupplementaryFileDir(trace)); + for (File file : suppDir.listFiles()) { + file.delete(); + } + } finally { + if (trace != null) { + trace.dispose(); + } + } + } + if (isFlameChartProvider) { + callStackBuildPm.commit(); + callStackSegStorePm.commit(); + } + callgraphBuildPm.commit(); + callgraphQueryPm.commit(); + callgraphGroupByPm.commit(); + } + + private static void benchmarkCallGraphProvider(ICallGraphProvider callGraphModule, PerformanceMeter callgraphBuildPm) { + // Do the performance test for building the callgraph only + callgraphBuildPm.start(); + TmfTestHelper.executeAnalysis((IAnalysisModule) callGraphModule); + callgraphBuildPm.stop(); + CallGraph callGraph = callGraphModule.getCallGraph(); + + assertTrue(!callGraph.getElements().isEmpty()); + } + + private static void benchmarkInstrumented(IFlameChartProvider analysisModule, PerformanceMeter callStackBuildPm, PerformanceMeter callStackSegStorePm, PerformanceMeter callgraphBuildPm) { + // Set the instrumented analysis to not trigger the call graph + // automatically, we will do it when ready + if (analysisModule instanceof InstrumentedCallStackAnalysis) { + ((InstrumentedCallStackAnalysis) analysisModule).triggerAutomatically(false); + } + + // Benchmark the call stack analysis + callStackBuildPm.start(); + TmfTestHelper.executeAnalysis(analysisModule); + callStackBuildPm.stop(); + + // Benchmark the segment store iteration + ISegmentStore segmentStore = analysisModule.getSegmentStore(); + assertNotNull(segmentStore); + callStackSegStorePm.start(); + // Iterate through the whole segment store + Iterator iterator = segmentStore.iterator(); + while (iterator.hasNext()) { + iterator.next(); + } + callStackSegStorePm.stop(); + + // Getting the callgraph will schedule the analysis and wait for its + // completion + callgraphBuildPm.start(); + CallGraph callGraph = ((ICallGraphProvider) analysisModule).getCallGraph(); + callgraphBuildPm.stop(); + + assertTrue(!callGraph.getElements().isEmpty()); + } + + /** + * Get the trace for this analysis. Every call to getTrace() should return a + * fresh trace fully initialized. The caller is responsible to dispose the + * trace when not required anymore + * + * @return A freshly initialized trace + * @throws TmfTraceException + * Exceptions thrown getting the trace + */ + protected abstract TmfTrace getTrace() throws TmfTraceException; +} diff --git a/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/src/org/eclipse/tracecompass/analysis/callstack/core/tests/perf/LttngUstCallstackBenchmark.java b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/src/org/eclipse/tracecompass/analysis/callstack/core/tests/perf/LttngUstCallstackBenchmark.java new file mode 100644 index 0000000000..b367aa8186 --- /dev/null +++ b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/src/org/eclipse/tracecompass/analysis/callstack/core/tests/perf/LttngUstCallstackBenchmark.java @@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright (c) 2016 É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.perf; + +import java.util.Arrays; + +import org.eclipse.tracecompass.ctf.core.tests.shared.CtfBenchmarkTrace; +import org.eclipse.tracecompass.internal.analysis.callstack.core.LttngUstCallStackAnalysis; +import org.eclipse.tracecompass.internal.analysis.callstack.core.callgraph.CallGraphAnalysis; +import org.eclipse.tracecompass.lttng2.ust.core.trace.LttngUstTrace; +import org.eclipse.tracecompass.testtraces.ctf.CtfTestTrace; +import org.eclipse.tracecompass.tmf.core.event.ITmfEvent; +import org.eclipse.tracecompass.tmf.core.exceptions.TmfTraceException; +import org.eclipse.tracecompass.tmf.core.trace.TmfTrace; +import org.eclipse.tracecompass.tmf.ctf.core.tests.shared.CtfTmfTestTraceUtils; +import org.eclipse.tracecompass.tmf.ctf.core.trace.CtfTmfTrace; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +/** + * Benchmarks the {@link LttngUstCallStackAnalysis} class and its associated + * {@link CallGraphAnalysis} + * + * @author Geneviève Bastien + */ +@RunWith(Parameterized.class) +public class LttngUstCallstackBenchmark extends CallStackAndGraphBenchmark { + + private static String getPathFromCtfTestTrace(CtfTestTrace testTrace) { + CtfTmfTrace ctftrace = CtfTmfTestTraceUtils.getTrace(testTrace); + String path = ctftrace.getPath(); + if (path == null) { + throw new IllegalArgumentException("Path shouldn't be null"); + } + ctftrace.dispose(); + return path; + } + + /** + * Get the traces to benchmark + * + * @return The arrays of parameters + */ + @Parameters(name = "{index}: {0}") + public static Iterable getParameters() { + return Arrays.asList(new Object[][] { + { CtfTestTrace.CYG_PROFILE.name(), getPathFromCtfTestTrace(CtfTestTrace.CYG_PROFILE) }, + { CtfBenchmarkTrace.UST_QMLSCENE.name(), CtfBenchmarkTrace.UST_QMLSCENE.getTracePath().toString() }, + }); + } + + private final String fTestTrace; + + /** + * Constructor + * + * @param name + * A name for this test + * @param tracePath + * The absolute path to the trace to test + */ + public LttngUstCallstackBenchmark(String name, String tracePath) { + super(name, LttngUstCallStackAnalysis.ID); + fTestTrace = tracePath; + } + + @Override + protected TmfTrace getTrace() throws TmfTraceException { + LttngUstTrace trace = new LttngUstTrace(); + trace.initTrace(null, fTestTrace, ITmfEvent.class); + return trace; + } +} diff --git a/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/src/org/eclipse/tracecompass/analysis/callstack/core/tests/perf/package-info.java b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/src/org/eclipse/tracecompass/analysis/callstack/core/tests/perf/package-info.java new file mode 100644 index 0000000000..b84c0bd1c2 --- /dev/null +++ b/analysis/org.eclipse.tracecompass.analysis.callstack.core.tests/src/org/eclipse/tracecompass/analysis/callstack/core/tests/perf/package-info.java @@ -0,0 +1,12 @@ +/******************************************************************************* + * Copyright (c) 2023 Ericsson + * + * 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 + *******************************************************************************/ + +@org.eclipse.jdt.annotation.NonNullByDefault +package org.eclipse.tracecompass.analysis.callstack.core.tests.perf;