Skip to content

Commit

Permalink
callstack: Add incubated LTTngUstCallStack tests
Browse files Browse the repository at this point in the history
To fully pass these tests locally, [1] has to be extracted under [2].

[1] https://archive.eclipse.org/tracecompass/test-traces/qmlscene.tgz
[2] ctf/org.eclipse.tracecompass.ctf.core.tests/traces/

Also, these tests require more than the default -Xmx512m of heap in the
Eclipse launcher. Required is at least -Xmx1024m of maximal heap.

Change-Id: I647cc228257646413957474130ea83c1835d36e1
Signed-off-by: Marco Miller <marco.miller@ericsson.com>
Reviewed-on: https://git.eclipse.org/r/c/tracecompass/org.eclipse.tracecompass/+/199766
Tested-by: Trace Compass Bot <tracecompass-bot@eclipse.org>
Tested-by: Bernd Hufmann <bernd.hufmann@ericsson.com>
Reviewed-by: Bernd Hufmann <bernd.hufmann@ericsson.com>
  • Loading branch information
marco-miller committed Mar 31, 2023
1 parent eda2ffe commit 21139ab
Show file tree
Hide file tree
Showing 5 changed files with 455 additions and 0 deletions.
Expand Up @@ -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
@@ -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<CtfTmfEventType> 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()));
}
}
}
@@ -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<IWeightedTreeGroupDescriptor> 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<ISegment> segmentStore = analysisModule.getSegmentStore();
assertNotNull(segmentStore);
callStackSegStorePm.start();
// Iterate through the whole segment store
Iterator<ISegment> 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;
}

0 comments on commit 21139ab

Please sign in to comment.