/*******************************************************************************
* Copyright (c) 2014, 2015 É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 v1.0 which
* accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Geneviève Bastien - Initial API and implementation
*******************************************************************************/
package org.eclipse.tracecompass.analysis.os.linux.core.tests.cpuusage;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.tracecompass.analysis.os.linux.core.cpuusage.KernelCpuUsageAnalysis;
import org.eclipse.tracecompass.analysis.os.linux.core.kernel.KernelAnalysisModule;
import org.eclipse.tracecompass.analysis.os.linux.core.tests.Activator;
import org.eclipse.tracecompass.analysis.os.linux.core.tests.stubs.trace.TmfXmlKernelTraceStub;
import org.eclipse.tracecompass.analysis.os.linux.core.trace.IKernelAnalysisEventLayout;
import org.eclipse.tracecompass.analysis.os.linux.core.trace.IKernelTrace;
import org.eclipse.tracecompass.internal.analysis.os.linux.core.kernel.Attributes;
import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem;
import org.eclipse.tracecompass.statesystem.core.exceptions.AttributeNotFoundException;
import org.eclipse.tracecompass.statesystem.core.interval.ITmfStateInterval;
import org.eclipse.tracecompass.statesystem.core.statevalue.ITmfStateValue;
import org.eclipse.tracecompass.statesystem.core.statevalue.TmfStateValue;
import org.eclipse.tracecompass.statesystem.core.tests.shared.utils.StateIntervalStub;
import org.eclipse.tracecompass.statesystem.core.tests.shared.utils.StateSystemTestUtils;
import org.eclipse.tracecompass.tmf.core.analysis.IAnalysisModule;
import org.eclipse.tracecompass.tmf.core.event.TmfEvent;
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.trace.ITmfTrace;
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.After;
import org.junit.Before;
import org.junit.Test;
import com.google.common.collect.ImmutableSet;
/**
* Test suite for the {@link KernelCpuUsageAnalysis} class
*
* @author Geneviève Bastien
*/
public class CpuUsageStateProviderTest {
private static final String CPU_USAGE_FILE = "testfiles/cpu_analysis.xml";
private IKernelTrace fTrace;
private KernelCpuUsageAnalysis fModule;
private static void deleteSuppFiles(ITmfTrace trace) {
/* Remove supplementary files */
File suppDir = new File(TmfTraceManager.getSupplementaryFileDir(trace));
for (File file : suppDir.listFiles()) {
file.delete();
}
}
/**
* Setup the trace for the tests
*/
@Before
public void setUp() {
IKernelTrace trace = new TmfXmlKernelTraceStub();
IPath filePath = Activator.getAbsoluteFilePath(CPU_USAGE_FILE);
IStatus status = trace.validate(null, filePath.toOSString());
if (!status.isOK()) {
fail(status.getException().getMessage());
}
try {
trace.initTrace(null, filePath.toOSString(), TmfEvent.class);
} catch (TmfTraceException e) {
fail(e.getMessage());
}
deleteSuppFiles(trace);
((TmfTrace) trace).traceOpened(new TmfTraceOpenedSignal(this, trace, null));
/*
* FIXME: Make sure this analysis is finished before running the CPU
* analysis. This block can be removed once analysis dependency and
* request precedence is implemented
*/
IAnalysisModule module = null;
for (IAnalysisModule mod : TmfTraceUtils.getAnalysisModulesOfClass(trace, KernelAnalysisModule.class)) {
module = mod;
}
assertNotNull(module);
module.schedule();
module.waitForCompletion();
/* End of the FIXME block */
fModule = TmfTraceUtils.getAnalysisModuleOfClass(trace, KernelCpuUsageAnalysis.class, KernelCpuUsageAnalysis.ID);
assertNotNull(fModule);
fTrace = trace;
}
/**
* Clean up
*/
@After
public void tearDown() {
deleteSuppFiles(fTrace);
fTrace.dispose();
}
/**
* Test that the analysis executes without problems
*/
@Test
public void testAnalysisExecution() {
/* Make sure the analysis hasn't run yet */
assertNull(fModule.getStateSystem());
/* Execute the analysis */
assertTrue(TmfTestHelper.executeAnalysis(fModule));
assertNotNull(fModule.getStateSystem());
}
/**
* Test that the state system is returned with the expected results
*/
@Test
public void testReturnedStateSystem() {
fModule.schedule();
fModule.waitForCompletion();
ITmfStateSystem ss = fModule.getStateSystem();
assertNotNull(ss);
assertEquals(1L, ss.getStartTime());
assertEquals(25L, ss.getCurrentEndTime());
try {
int cpusQuark = ss.getQuarkAbsolute(Attributes.CPUS);
/*
* There should be 2 CPU entries: 0 and 1 and 3 process entries
* under each
*/
List<Integer> cpuQuarks = ss.getSubAttributes(cpusQuark, false);
assertEquals(2, cpuQuarks.size());
for (Integer cpuQuark : cpuQuarks) {
assertEquals(3, ss.getSubAttributes(cpuQuark, false).size());
}
/* Test the intervals of proc2 on CPU 0 */
List<@NonNull ITmfStateInterval> intervals = new ArrayList<>();
intervals.add(new StateIntervalStub(1, 19, TmfStateValue.nullValue()));
intervals.add(new StateIntervalStub(20, 25, TmfStateValue.newValueLong(19L)));
StateSystemTestUtils.testIntervalForAttributes(ss, intervals, Attributes.CPUS, "0", "2");
/* Test the intervals of proc 4 CPU 1 */
intervals.clear();
intervals.add(new StateIntervalStub(1, 4, TmfStateValue.nullValue()));
intervals.add(new StateIntervalStub(5, 14, TmfStateValue.newValueLong(3L)));
intervals.add(new StateIntervalStub(15, 25, TmfStateValue.newValueLong(8L)));
StateSystemTestUtils.testIntervalForAttributes(ss, intervals, Attributes.CPUS, "1", "4");
/* Test the intervals of proc 3 on both CPUs */
intervals.clear();
intervals.add(new StateIntervalStub(1, 24, TmfStateValue.nullValue()));
intervals.add(new StateIntervalStub(25, 25, TmfStateValue.newValueLong(5L)));
StateSystemTestUtils.testIntervalForAttributes(ss, intervals, Attributes.CPUS, "0", "3");
intervals.clear();
intervals.add(new StateIntervalStub(1, 1, TmfStateValue.nullValue()));
intervals.add(new StateIntervalStub(2, 9, TmfStateValue.newValueLong(1L)));
intervals.add(new StateIntervalStub(10, 25, TmfStateValue.newValueLong(6L)));
StateSystemTestUtils.testIntervalForAttributes(ss, intervals, Attributes.CPUS, "1", "3");
/*
* Query at the end and make sure all processes on all CPU have the
* expected values
*/
Map<@NonNull String @NonNull [], @NonNull ITmfStateValue> map = new HashMap<>();
map.put(StateSystemTestUtils.makeAttribute(Attributes.CPUS, "0", "1"), TmfStateValue.newValueLong(0L));
map.put(StateSystemTestUtils.makeAttribute(Attributes.CPUS, "0", "2"), TmfStateValue.newValueLong(19L));
map.put(StateSystemTestUtils.makeAttribute(Attributes.CPUS, "0", "3"), TmfStateValue.newValueLong(5L));
map.put(StateSystemTestUtils.makeAttribute(Attributes.CPUS, "1", "1"), TmfStateValue.newValueLong(5L));
map.put(StateSystemTestUtils.makeAttribute(Attributes.CPUS, "1", "3"), TmfStateValue.newValueLong(6L));
map.put(StateSystemTestUtils.makeAttribute(Attributes.CPUS, "1", "4"), TmfStateValue.newValueLong(8L));
StateSystemTestUtils.testValuesAtTime(ss, 25L, map);
} catch (AttributeNotFoundException e) {
fail(e.getMessage());
}
}
/**
* Test the
* {@link KernelCpuUsageAnalysis#getCpuUsageInRange(java.util.Set, long, long)}
* method.
* <p>
* TODO: extend!
*/
@Test
public void testUsageInRange() {
fModule.schedule();
fModule.waitForCompletion();
/* This range should query the total range */
Map<String, Long> expected = new HashMap<>();
expected.put("0/1", 0L);
expected.put("0/2", 19L);
expected.put("0/3", 5L);
expected.put("1/1", 5L);
expected.put("1/3", 6L);
expected.put("1/4", 13L);
expected.put("total", 48L);
expected.put("total/1", 5L);
expected.put("total/2", 19L);
expected.put("total/3", 11L);
expected.put("total/4", 13L);
expected.put("0", 24L);
expected.put("1", 24L);
Map<String, Long> resultMap = fModule.getCpuUsageInRange(Collections.EMPTY_SET, 0L, 30L);
assertEquals(expected, resultMap);
/* Verify a range when a process runs at the start */
expected.clear();
expected.put("0/1", 0L);
expected.put("0/2", 0L);
expected.put("0/3", 3L);
expected.put("1/1", 0L);
expected.put("1/3", 0L);
expected.put("1/4", 3L);
expected.put("total", 6L);
expected.put("total/1", 0L);
expected.put("total/2", 0L);
expected.put("total/3", 3L);
expected.put("total/4", 3L);
expected.put("0", 3L);
expected.put("1", 3L);
resultMap = fModule.getCpuUsageInRange(Collections.EMPTY_SET, 22L, 25L);
assertEquals(expected, resultMap);
/* Verify a range when a process runs at the end */
expected.clear();
expected.put("0/1", 0L);
expected.put("0/2", 3L);
expected.put("0/3", 0L);
expected.put("1/1", 0L);
expected.put("1/3", 1L);
expected.put("1/4", 2L);
expected.put("total", 6L);
expected.put("total/1", 0L);
expected.put("total/2", 3L);
expected.put("total/3", 1L);
expected.put("total/4", 2L);
expected.put("0", 3L);
expected.put("1", 3L);
resultMap = fModule.getCpuUsageInRange(Collections.EMPTY_SET, 1L, 4L);
assertEquals(expected, resultMap);
/* Verify a range when a process runs at start and at the end */
expected.clear();
expected.put("0/1", 0L);
expected.put("0/2", 9L);
expected.put("0/3", 0L);
expected.put("1/1", 0L);
expected.put("1/3", 5L);
expected.put("1/4", 4L);
expected.put("total", 18L);
expected.put("total/1", 0L);
expected.put("total/2", 9L);
expected.put("total/3", 5L);
expected.put("total/4", 4L);
expected.put("0", 9L);
expected.put("1", 9L);
resultMap = fModule.getCpuUsageInRange(Collections.EMPTY_SET, 4L, 13L);
assertEquals(expected, resultMap);
}
/**
* Tests the cpu usage for a cpu subset within a range
*/
@Test
public void testInRangeWithCpuSubset() {
fModule.schedule();
fModule.waitForCompletion();
/* Verify a range when a process runs at start and at the end */
Map<String, Long> expected = new HashMap<>();
expected.put("0/1", 0L);
expected.put("0/2", 9L);
expected.put("0/3", 0L);
expected.put("total/1", 0L);
expected.put("total/2", 9L);
expected.put("total/3", 0L);
expected.put("0", 9L);
expected.put("total", 9L);
Map<String, Long> resultMap = fModule.getCpuUsageInRange(Collections.<@NonNull Integer> singleton(0), 4L, 13L);
assertEquals(expected, resultMap);
/* Verify a range when a process runs at start and at the end */
expected.clear();
expected.put("1/1", 0L);
expected.put("1/3", 5L);
expected.put("1/4", 4L);
expected.put("total/1", 0L);
expected.put("total/3", 5L);
expected.put("total/4", 4L);
expected.put("1", 9L);
expected.put("total", 9L);
resultMap = fModule.getCpuUsageInRange(ImmutableSet.of(1, 2), 4L, 13L);
assertEquals(expected, resultMap);
}
/**
* Test the requirements of the analysis module
*/
@Test
public void testRequirements() {
IKernelTrace trace = fTrace;
assertNotNull(trace);
IKernelAnalysisEventLayout layout = trace.getKernelEventLayout();
Set<String> expected = ImmutableSet.of(layout.eventSchedSwitch());
Set<String> actual = StreamSupport.stream(fModule.getAnalysisRequirements().spliterator(), false)
.flatMap(req -> req.getValues().stream())
.collect(Collectors.toSet());
assertEquals(expected, actual);
}
}