+/*******************************************************************************
+ * 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 v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.lttng2.kernel.core.tests.perf.analysis.kernel;
+
+import static org.junit.Assert.fail;
+
+import java.io.File;
+import java.util.List;
+import java.util.Random;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.test.performance.Dimension;
+import org.eclipse.test.performance.Performance;
+import org.eclipse.test.performance.PerformanceMeter;
+import org.eclipse.tracecompass.analysis.os.linux.core.kernel.KernelAnalysisModule;
+import org.eclipse.tracecompass.analysis.os.linux.core.kernel.KernelThreadInformationProvider;
+import org.eclipse.tracecompass.internal.analysis.os.linux.core.kernel.Attributes;
+import org.eclipse.tracecompass.lttng2.kernel.core.trace.LttngKernelTrace;
+import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem;
+import org.eclipse.tracecompass.statesystem.core.exceptions.AttributeNotFoundException;
+import org.eclipse.tracecompass.statesystem.core.exceptions.StateSystemDisposedException;
+import org.eclipse.tracecompass.testtraces.ctf.CtfTestTrace;
+import org.eclipse.tracecompass.tmf.core.exceptions.TmfAnalysisException;
+import org.eclipse.tracecompass.tmf.core.exceptions.TmfTraceException;
+import org.eclipse.tracecompass.tmf.core.tests.shared.TmfTestHelper;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
+import org.eclipse.tracecompass.tmf.ctf.core.event.CtfTmfEvent;
+import org.eclipse.tracecompass.tmf.ctf.core.tests.shared.CtfTmfTestTraceUtils;
+import org.junit.Test;
+
+/**
+ * Benchmarks some typical usages of the kernel analysis
+ *
+ * @author Geneviève Bastien
+ */
+public class KernelAnalysisUsageBenchmark {
+
+ private static final String TEST_GET_RUNNING_THREAD = "Kernel: Threads On CPU";
+ private static final String TEST_FULL_QUERIES = "Kernel: Full Queries";
+ private static final int LOOP_COUNT = 25;
+ private static final long SEED = 65423897234L;
+ private static final int NUM_CPU_QUERIES = 20000;
+ private static final int NUM_FULL_QUERIES = 20000;
+
+ /**
+ * Run the benchmark with "trace2"
+ */
+ @Test
+ public void testTrace2() {
+ runTest(CtfTestTrace.TRACE2, "Trace2");
+ }
+
+ /**
+ * Run the benchmark with "many threads"
+ */
+ @Test
+ public void testManyThreads() {
+ runTest(CtfTestTrace.MANY_THREADS, "ManyThreads");
+ }
+
+ /**
+ * Run the benchmark with "django httpd"
+ */
+ @Test
+ public void testDjangoHttpd() {
+ runTest(CtfTestTrace.DJANGO_HTTPD, "Django httpd");
+ }
+
+ private static KernelAnalysisModule getModule(@NonNull CtfTestTrace testTrace, @NonNull LttngKernelTrace trace) {
+ KernelAnalysisModule module = null;
+ String path = CtfTmfTestTraceUtils.getTrace(testTrace).getPath();
+
+ try {
+ /* Initialize the analysis module */
+ module = new KernelAnalysisModule();
+ module.setId("test");
+ trace.initTrace(null, path, CtfTmfEvent.class);
+ module.setTrace(trace);
+ TmfTestHelper.executeAnalysis(module);
+ } catch (TmfAnalysisException | TmfTraceException e) {
+ fail(e.getMessage());
+ }
+ return module;
+ }
+
+ private static void deleteSupplementaryFiles(ITmfTrace trace) {
+ /*
+ * Delete the supplementary files at the end of the benchmarks
+ */
+ File suppDir = new File(TmfTraceManager.getSupplementaryFileDir(trace));
+ for (File file : suppDir.listFiles()) {
+ file.delete();
+ }
+ }
+
+ private static void runTest(@NonNull CtfTestTrace testTrace, String testName) {
+
+ /* First, complete the analysis */
+ LttngKernelTrace trace = new LttngKernelTrace();
+ deleteSupplementaryFiles(trace);
+ KernelAnalysisModule module = getModule(testTrace, trace);
+
+ /* Benchmark some query use cases */
+ benchmarkGetThreadOnCpu(testName, module);
+ benchmarkFullQueries(testName, module);
+
+ /*
+ * Delete the supplementary files at the end of the benchmarks
+ */
+ deleteSupplementaryFiles(trace);
+
+ module.dispose();
+ trace.dispose();
+
+ CtfTmfTestTraceUtils.dispose(testTrace);
+ }
+
+ /**
+ * Benchmarks getting a thread running on a random CPU from the kernel
+ * analysis at fixed intervals. This use case mimics an analysis that reads
+ * events and needs to get the currently running thread for those events.
+ */
+ private static void benchmarkGetThreadOnCpu(String testName, KernelAnalysisModule module) {
+
+ Performance perf = Performance.getDefault();
+ PerformanceMeter pmRunningThread = perf.createPerformanceMeter(KernelAnalysisBenchmark.TEST_ID + testName + ": " + TEST_GET_RUNNING_THREAD);
+ perf.tagAsSummary(pmRunningThread, TEST_GET_RUNNING_THREAD + '(' + testName + ')', Dimension.CPU_TIME);
+
+ @Nullable
+ ITmfStateSystem ss = module.getStateSystem();
+ if (ss == null) {
+ fail("The state system is null");
+ return;
+ }
+
+ /* Get the number of CPUs */
+ int cpuCount = -1;
+ try {
+ int cpusQuark = ss.getQuarkAbsolute(Attributes.CPUS);
+ @NonNull
+ List<@NonNull Integer> cpus = ss.getSubAttributes(cpusQuark, false);
+ cpuCount = cpus.size();
+ } catch (AttributeNotFoundException e) {
+ fail(e.getMessage());
+ }
+ if (cpuCount < 1) {
+ fail("Impossible to get the number of CPUs");
+ }
+
+ /* Get the step and start time of the queries */
+ long startTime = ss.getStartTime();
+ long endTime = ss.getCurrentEndTime();
+ long step = Math.floorDiv(endTime - startTime, NUM_CPU_QUERIES);
+
+ if (step < 1) {
+ fail("Trace is too short to run the get thread on CPU benchmark");
+ }
+
+ /* Verify the query work by fetching a value at the end of the trace */
+ Integer threadOnCpu = KernelThreadInformationProvider.getThreadOnCpu(module, 0, endTime);
+ if (threadOnCpu == null) {
+ fail("null thread on CPU at the end of the trace. Something is not right with the state system");
+ }
+
+ for (int i = 0; i < LOOP_COUNT; i++) {
+ /* Get the thread running on a random CPU at fixed intervals */
+ Random randomGenerator = new Random(SEED);
+ pmRunningThread.start();
+ for (long nextTime = startTime; nextTime < endTime; nextTime += step) {
+ int cpu = Math.abs(randomGenerator.nextInt()) % cpuCount;
+ KernelThreadInformationProvider.getThreadOnCpu(module, cpu, nextTime);
+ }
+ pmRunningThread.stop();
+ }
+ pmRunningThread.commit();
+ }
+
+ /**
+ * Benchmarks getting full queries at different times. This use cases is
+ * often used to populate the views.
+ */
+ private static void benchmarkFullQueries(String testName, KernelAnalysisModule module) {
+
+ Performance perf = Performance.getDefault();
+ PerformanceMeter pmRunningThread = perf.createPerformanceMeter(KernelAnalysisBenchmark.TEST_ID + testName + ": " + TEST_FULL_QUERIES);
+ perf.tagAsSummary(pmRunningThread, TEST_FULL_QUERIES + '(' + testName + ')', Dimension.CPU_TIME);
+
+ @Nullable
+ ITmfStateSystem ss = module.getStateSystem();
+ if (ss == null) {
+ fail("The state system is null");
+ return;
+ }
+
+ /* Get the step and start time of the queries */
+ long startTime = ss.getStartTime();
+ long endTime = ss.getCurrentEndTime();
+ long step = Math.floorDiv(endTime - startTime, NUM_FULL_QUERIES);
+
+ if (step < 1) {
+ fail("Trace is too short to run the get full queries benchmark");
+ }
+
+ for (int i = 0; i < LOOP_COUNT; i++) {
+ /* Make a full query at fixed intervals */
+ pmRunningThread.start();
+ for (long nextTime = startTime; nextTime < endTime; nextTime += step) {
+ try {
+ ss.queryFullState(nextTime);
+ } catch (StateSystemDisposedException e) {
+ fail(e.getMessage());
+ }
+ }
+ pmRunningThread.stop();
+ }
+ pmRunningThread.commit();
+ }
+}