/*******************************************************************************
* Copyright (c) 2016 Ericsson
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License v1.0 which
* accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*******************************************************************************/
package org.eclipse.tracecompass.analysis.timing.core.tests.callgraph;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.util.Collections;
import java.util.List;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.tracecompass.common.core.NonNullUtils;
import org.eclipse.tracecompass.internal.analysis.timing.core.callgraph.CallGraphAnalysis;
import org.eclipse.tracecompass.internal.analysis.timing.core.callgraph.ICalledFunction;
import org.eclipse.tracecompass.segmentstore.core.ISegment;
import org.eclipse.tracecompass.segmentstore.core.ISegmentStore;
import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem;
import org.eclipse.tracecompass.statesystem.core.ITmfStateSystemBuilder;
import org.eclipse.tracecompass.statesystem.core.StateSystemFactory;
import org.eclipse.tracecompass.statesystem.core.backend.IStateHistoryBackend;
import org.eclipse.tracecompass.statesystem.core.backend.StateHistoryBackendFactory;
import org.eclipse.tracecompass.statesystem.core.statevalue.TmfStateValue;
import org.eclipse.tracecompass.tmf.core.segment.ISegmentAspect;
import org.junit.Test;
/**
* Test the CallGraphAnalysis.This creates a virtual state system in each test
* and tests the segment store returned by the CallGraphAnalysis.
*
* @author Sonia Farrah
*
*/
public class CallGraphAnalysisTest {
private static final @NonNull String PROCESS_PATH = "Processes";
private static final @NonNull String THREAD_PATH = "Thread";
private static final @NonNull String CALLSTACK_PATH = "CallStack";
private static final String QUARK_0 = "0";
private static final String QUARK_1 = "1";
private static final String QUARK_2 = "2";
private static final Integer SMALL_AMOUNT_OF_SEGMENT = 3;
private static final int LARGE_AMOUNT_OF_SEGMENTS = 1000;
private static final String @NonNull [] CSP = { CALLSTACK_PATH };
private static final String @NonNull [] PP = { PROCESS_PATH };
private static final String @NonNull [] TP = { THREAD_PATH };
/**
* This class is used to make the CallGraphAnalysis's method
* iterateOverStateSystem() visible to test
*/
private class CGAnalysis extends CallGraphAnalysis {
@Override
protected boolean iterateOverStateSystem(@Nullable ITmfStateSystem ss, String[] threadsPattern, String[] processesPattern, String[] callStackPath, IProgressMonitor monitor) {
return super.iterateOverStateSystem(ss, threadsPattern, processesPattern, callStackPath, monitor);
}
@Override
public @NonNull Iterable<@NonNull ISegmentAspect> getSegmentAspects() {
return Collections.EMPTY_LIST;
}
}
private static ITmfStateSystemBuilder createFixture() {
IStateHistoryBackend backend;
backend = StateHistoryBackendFactory.createInMemoryBackend("Test", 0L);
ITmfStateSystemBuilder fixture = StateSystemFactory.newStateSystem(backend);
return fixture;
}
/**
* Test cascade state system. The call stack's structure used in this test
* is shown below:
*
* <pre>
* ________
* ______
* ____
*
* </pre>
*/
@Test
public void CascadeTest() {
ITmfStateSystemBuilder fixture = createFixture();
// Build the state system
long start = 1;
long end = 1001;
int parentQuark = fixture.getQuarkAbsoluteAndAdd(PROCESS_PATH, THREAD_PATH, CALLSTACK_PATH);
for (int i = 1; i <= SMALL_AMOUNT_OF_SEGMENT; i++) {
int quark = fixture.getQuarkRelativeAndAdd(parentQuark, Integer.toString(i));
TmfStateValue statev = TmfStateValue.newValueLong(i);
fixture.modifyAttribute(start, TmfStateValue.nullValue(), quark);
fixture.modifyAttribute(start + i, statev, quark);
fixture.modifyAttribute(end - i, TmfStateValue.nullValue(), quark);
}
fixture.closeHistory(1002);
// Execute the CallGraphAnalysis
CGAnalysis cga = new CGAnalysis();
assertTrue(cga.iterateOverStateSystem(fixture, TP, PP, CSP, new NullProgressMonitor()));
ISegmentStore<@NonNull ISegment> segmentStore = cga.getSegmentStore();
// Test the segment store generated by the analysis
assertNotNull(segmentStore);
Object[] segments = segmentStore.toArray();
assertEquals("Number of segments Found", 3, segments.length);
ICalledFunction f1 = (ICalledFunction) segments[0];
ICalledFunction f2 = (ICalledFunction) segments[1];
ICalledFunction f3 = (ICalledFunction) segments[2];
assertEquals("Test the parenthood", NonNullUtils.checkNotNull(f2.getParent()).getSymbol(), f1.getSymbol());
assertEquals("Children number:First parent", 1, f1.getChildren().size());
assertEquals("Children number:Second parent", 1, f2.getChildren().size());
assertTrue("Children number:Second parent", f3.getChildren().isEmpty());
assertTrue("Children number:Child(leaf)", ((ICalledFunction) segments[2]).getChildren().isEmpty());
assertEquals("Parent's self time", 2, f1.getSelfTime());
assertEquals("Child's self time", 2, f2.getSelfTime());
assertEquals("The leaf's self time", 994, f3.getSelfTime());
assertEquals("Test first function's duration", 998, f1.getLength());
assertEquals("Test second function's duration", 996, f2.getLength());
assertEquals("Test third function's duration", 994, f3.getLength());
assertEquals("Depth:First parent", 0, f1.getDepth());
assertEquals("Depth:Second parent", 1, f2.getDepth());
assertEquals("Depth:Last child", 2, f3.getDepth());
cga.dispose();
}
/**
* Build a pyramid shaped call stack.This call stack contains three
* functions ,Its structure is shown below :
*
* <pre>
* __
* ____
* ______
* </pre>
*/
private static void buildPyramidCallStack(ITmfStateSystemBuilder fixture) {
int parentQuark = fixture.getQuarkAbsoluteAndAdd(PROCESS_PATH, THREAD_PATH, CALLSTACK_PATH);
// Create the first function
int quark = fixture.getQuarkRelativeAndAdd(parentQuark, QUARK_0);
TmfStateValue statev = TmfStateValue.newValueLong(0);
fixture.modifyAttribute(0, TmfStateValue.nullValue(), quark);
fixture.modifyAttribute(10, statev, quark);
fixture.modifyAttribute(20, TmfStateValue.nullValue(), quark);
// Create the second function
quark = fixture.getQuarkRelativeAndAdd(parentQuark, QUARK_1);
statev = TmfStateValue.newValueLong(1);
fixture.modifyAttribute(0, TmfStateValue.nullValue(), quark);
fixture.modifyAttribute(5, statev, quark);
fixture.modifyAttribute(25, TmfStateValue.nullValue(), quark);
// Create the third function
quark = fixture.getQuarkRelativeAndAdd(parentQuark, QUARK_2);
statev = TmfStateValue.newValueLong(2);
fixture.modifyAttribute(0, statev, quark);
fixture.modifyAttribute(30, TmfStateValue.nullValue(), quark);
fixture.closeHistory(31);
}
/**
* Test pyramid state system. The call stack's structure used in this test
* is shown below:
*
* <pre>
* __
* ____
* ______
* </pre>
*/
@Test
public void PyramidTest() {
ITmfStateSystemBuilder fixture = createFixture();
buildPyramidCallStack(fixture);
// Execute the callGraphAnalysis
CGAnalysis cga = new CGAnalysis();
assertTrue(cga.iterateOverStateSystem(fixture, TP, PP, CSP, new NullProgressMonitor()));
ISegmentStore<@NonNull ISegment> segmentStore = cga.getSegmentStore();
assertNotNull(segmentStore);
Object[] segments = segmentStore.toArray();
ICalledFunction f1 = (ICalledFunction) segments[0];
assertEquals("Number of segments Found", 1, segments.length);
assertEquals("Callees number", 0, f1.getChildren().size());
assertEquals("Function's self time", 10, f1.getSelfTime());
assertEquals("Compare the function's self time and total time", f1.getLength(), f1.getSelfTime());
assertEquals("Function's depth", 0, f1.getDepth());
cga.dispose();
}
/**
* Test mutliRoots state system. The call stack's structure used in this
* test is shown below:
*
* <pre>
* ___ ___
* _ _
* </pre>
*/
@Test
public void multiFunctionRootsTest() {
ITmfStateSystemBuilder fixture = createFixture();
int parentQuark = fixture.getQuarkAbsoluteAndAdd(PROCESS_PATH, THREAD_PATH, CALLSTACK_PATH);
// Create the first root function
int quark = fixture.getQuarkRelativeAndAdd(parentQuark, QUARK_0);
TmfStateValue statev = TmfStateValue.newValueLong(0);
fixture.modifyAttribute(0, statev, quark);
fixture.modifyAttribute(20, TmfStateValue.nullValue(), quark);
// Create the second root function
statev = TmfStateValue.newValueLong(1);
fixture.modifyAttribute(30, statev, quark);
fixture.modifyAttribute(50, TmfStateValue.nullValue(), quark);
// Create the first root function's callee
quark = fixture.getQuarkRelativeAndAdd(parentQuark, QUARK_1);
statev = TmfStateValue.newValueLong(2);
fixture.modifyAttribute(0, statev, quark);
fixture.modifyAttribute(10, TmfStateValue.nullValue(), quark);
// Create the second root function's callee
statev = TmfStateValue.newValueLong(3);
fixture.modifyAttribute(30, statev, quark);
fixture.modifyAttribute(40, TmfStateValue.nullValue(), quark);
fixture.closeHistory(51);
// Execute the callGraphAnalysis
CGAnalysis cga = new CGAnalysis();
assertTrue(cga.iterateOverStateSystem(fixture, TP, PP, CSP, new NullProgressMonitor()));
ISegmentStore<@NonNull ISegment> segmentStore = cga.getSegmentStore();
// Test the segment store
assertNotNull(segmentStore);
Object[] segments = segmentStore.toArray();
List<@NonNull ICalledFunction> threads = cga.getRootFunctions();
assertEquals("Number of root functions", 2, threads.size());
assertEquals("Number of children: first root function", 1, threads.get(0).getChildren().size());
assertEquals("Number of children: first root function", 1, threads.get(1).getChildren().size());
ICalledFunction firstChild = threads.get(0).getChildren().get(0);
ICalledFunction secondChild = threads.get(1).getChildren().get(0);
assertEquals("Number of segments found", 4, segments.length);
assertNotNull(firstChild.getParent());
assertNotNull(secondChild.getParent());
assertEquals("Test of parenthood", NonNullUtils.checkNotNull(firstChild.getParent()).getSymbol(), threads.get(0).getSymbol());
assertEquals("Test of parenthood", NonNullUtils.checkNotNull(secondChild.getParent()).getSymbol(), threads.get(1).getSymbol());
cga.dispose();
}
/**
* Test state system with a Large amount of segments. All segments have the
* same length. The call stack's structure used in this test is shown below:
*
* <pre>
* _____
* _____
* _____
* .....
* </pre>
*/
@Test
public void LargeTest() {
// Build the state system
ITmfStateSystemBuilder fixture = createFixture();
int parentQuark = fixture.getQuarkAbsoluteAndAdd(PROCESS_PATH, THREAD_PATH, CALLSTACK_PATH);
for (int i = 0; i < LARGE_AMOUNT_OF_SEGMENTS; i++) {
int quark = fixture.getQuarkRelativeAndAdd(parentQuark, Integer.toString(i));
TmfStateValue statev = TmfStateValue.newValueLong(i);
fixture.pushAttribute(0, statev, quark);
}
for (int i = 0; i < LARGE_AMOUNT_OF_SEGMENTS; i++) {
int quark = fixture.getQuarkRelativeAndAdd(parentQuark, Integer.toString(i));
fixture.popAttribute(10, quark);
}
fixture.closeHistory(11);
// Execute the callGraphAnalysis
CGAnalysis cga = new CGAnalysis();
assertTrue(cga.iterateOverStateSystem(fixture, TP, PP, CSP, new NullProgressMonitor()));
ISegmentStore<@NonNull ISegment> segmentStore = cga.getSegmentStore();
// Test segment store
assertNotNull(segmentStore);
Object[] segments = segmentStore.toArray();
assertEquals("Number of segments found", LARGE_AMOUNT_OF_SEGMENTS, segments.length);
for (int i = 1; i < LARGE_AMOUNT_OF_SEGMENTS; i++) {
assertEquals("Test parenthood", ((ICalledFunction) segments[i - 1]).getSymbol(), NonNullUtils.checkNotNull(((ICalledFunction) segments[i]).getParent()).getSymbol());
}
cga.dispose();
}
/**
* Test an empty state system
*/
@Test
public void EmptyStateSystemTest() {
ITmfStateSystemBuilder fixture = createFixture();
fixture.closeHistory(1002);
CGAnalysis cga = new CGAnalysis();
assertTrue(cga.iterateOverStateSystem(fixture, TP, PP, CSP, new NullProgressMonitor()));
ISegmentStore<@NonNull ISegment> segmentStore = cga.getSegmentStore();
assertNotNull(segmentStore);
Object[] segments = segmentStore.toArray();
assertEquals("Number of root functions", 0, segments.length);
cga.dispose();
}
/**
* Test a tree shaped call stack. The root function calls the same function
* twice. The call stack's structure used in this test is shown below:
*
* <pre>
*---------1----------
* --2-- -3- -2-
* -3-
* </pre>
*/
@Test
public void treeTest() {
// Build the state system
ITmfStateSystemBuilder fixture = createFixture();
int parentQuark = fixture.getQuarkAbsoluteAndAdd(PROCESS_PATH, THREAD_PATH, CALLSTACK_PATH);
// Create the root function
int quark = fixture.getQuarkRelativeAndAdd(parentQuark, QUARK_0);
TmfStateValue statev = TmfStateValue.newValueLong(0);
fixture.modifyAttribute(0, statev, quark);
fixture.modifyAttribute(100, TmfStateValue.nullValue(), quark);
// Create the first child
quark = fixture.getQuarkRelativeAndAdd(parentQuark, QUARK_1);
statev = TmfStateValue.newValueLong(1);
fixture.modifyAttribute(0, TmfStateValue.nullValue(), quark);
fixture.modifyAttribute(10, statev, quark);
fixture.modifyAttribute(40, TmfStateValue.nullValue(), quark);
// Create the second child
statev = TmfStateValue.newValueLong(2);
fixture.modifyAttribute(50, statev, quark);
fixture.modifyAttribute(70, TmfStateValue.nullValue(), quark);
// Create the third child
statev = TmfStateValue.newValueLong(1);
fixture.modifyAttribute(80, statev, quark);
fixture.modifyAttribute(100, TmfStateValue.nullValue(), quark);
// Create the leaf
quark = fixture.getQuarkRelativeAndAdd(parentQuark, QUARK_2);
statev = TmfStateValue.newValueLong(3);
fixture.modifyAttribute(0, TmfStateValue.nullValue(), quark);
fixture.modifyAttribute(15, statev, quark);
fixture.modifyAttribute(20, TmfStateValue.nullValue(), quark);
fixture.closeHistory(102);
// Execute the callGraphAnalysis
CGAnalysis cga = new CGAnalysis();
assertTrue(cga.iterateOverStateSystem(fixture, TP, PP, CSP, new NullProgressMonitor()));
ISegmentStore<@NonNull ISegment> segmentStore = cga.getSegmentStore();
// Test segment store
assertNotNull(segmentStore);
Object[] segments = segmentStore.toArray();
assertEquals("Number of segments found", 5, segments.length);
List<@NonNull ICalledFunction> rootFunctions = cga.getRootFunctions();
assertEquals("Test the number of root functions found", 1, rootFunctions.size());
ICalledFunction rootFunction = rootFunctions.get(0);
// Test the segments links
assertEquals("Children number:First parent", 3, rootFunction.getChildren().size());
List<@NonNull ICalledFunction> firstDepthFunctions = rootFunctions.get(0).getChildren();
ICalledFunction firstChild = firstDepthFunctions.get(0);
ICalledFunction secondChild = firstDepthFunctions.get(1);
ICalledFunction thirdChild = firstDepthFunctions.get(2);
assertEquals("Test parenthood: First child", rootFunction.getSymbol(), NonNullUtils.checkNotNull(firstChild.getParent()).getSymbol());
assertEquals("Test parenthood: Second parent", rootFunction.getSymbol(), NonNullUtils.checkNotNull(secondChild.getParent()).getSymbol());
assertEquals("Test parenthood: Third parent", rootFunction.getSymbol(), NonNullUtils.checkNotNull(thirdChild.getParent()).getSymbol());
assertEquals("Children number: First child", 1, firstChild.getChildren().size());
ICalledFunction leaf = firstChild.getChildren().get(0);
assertEquals("Test parenthood: Third parent", firstChild.getSymbol(), NonNullUtils.checkNotNull(leaf.getParent()).getSymbol());
assertTrue("Children number:leaf", leaf.getChildren().isEmpty());
// Test the segments self time
assertEquals("Parent's self time", 30, rootFunction.getSelfTime());
assertEquals("First child's self time", 25, firstChild.getSelfTime());
assertEquals("Second child's self time", 20, secondChild.getSelfTime());
assertEquals("Third child's self time", 20, thirdChild.getSelfTime());
assertEquals("Leaf's self time", 5, leaf.getSelfTime());
// Test the segments duration
assertEquals("Test first function's duration", 100, rootFunction.getLength());
assertEquals("Test first child's duration", 30, firstChild.getLength());
assertEquals("Test second child's duration", 20, secondChild.getLength());
assertEquals("Test third child's duration", 20, thirdChild.getLength());
assertEquals("Test leaf's duration", 5, leaf.getLength());
// Test the segments Depth
assertEquals("Depth: Parent", 0, rootFunction.getDepth());
assertEquals("Depth: First child", 1, firstChild.getDepth());
assertEquals("Depth: Second child", 1, secondChild.getDepth());
assertEquals("Depth: Third child", 1, thirdChild.getDepth());
assertEquals("Depth: Leaf", 2, leaf.getDepth());
// Test if the first child and the third one have the same address
assertEquals("Test the address of two functions", firstChild.getSymbol(), thirdChild.getSymbol());
cga.dispose();
}
}