From 613e9594318e2b23c81dfcca71b51621ef407922 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Genevi=C3=A8ve=20Bastien?= Date: Wed, 24 Feb 2016 22:48:44 -0500 Subject: [PATCH] analysis.os: Benchmark some typical usages of Kernel Analysis MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit This adds benchmark of getting the thread running on a CPU from a kernel analysis and making full queries on that analysis. It paves the way to adding some more of those benchmarks. Change-Id: I853a350a7647973b87e49dabaf2ac14b09cd0136 Signed-off-by: Geneviève Bastien Reviewed-on: https://git.eclipse.org/r/67444 Reviewed-by: Hudson CI Reviewed-by: Matthew Khouzam Tested-by: Matthew Khouzam --- .../kernel/KernelAnalysisBenchmark.java | 21 +- .../kernel/KernelAnalysisUsageBenchmark.java | 227 ++++++++++++++++++ 2 files changed, 247 insertions(+), 1 deletion(-) create mode 100644 lttng/org.eclipse.tracecompass.lttng2.kernel.core.tests/perf/org/eclipse/tracecompass/lttng2/kernel/core/tests/perf/analysis/kernel/KernelAnalysisUsageBenchmark.java diff --git a/lttng/org.eclipse.tracecompass.lttng2.kernel.core.tests/perf/org/eclipse/tracecompass/lttng2/kernel/core/tests/perf/analysis/kernel/KernelAnalysisBenchmark.java b/lttng/org.eclipse.tracecompass.lttng2.kernel.core.tests/perf/org/eclipse/tracecompass/lttng2/kernel/core/tests/perf/analysis/kernel/KernelAnalysisBenchmark.java index 11c3d22be8..3b45e94cda 100644 --- a/lttng/org.eclipse.tracecompass.lttng2.kernel.core.tests/perf/org/eclipse/tracecompass/lttng2/kernel/core/tests/perf/analysis/kernel/KernelAnalysisBenchmark.java +++ b/lttng/org.eclipse.tracecompass.lttng2.kernel.core.tests/perf/org/eclipse/tracecompass/lttng2/kernel/core/tests/perf/analysis/kernel/KernelAnalysisBenchmark.java @@ -45,7 +45,10 @@ import org.junit.runners.Parameterized.Parameters; @RunWith(Parameterized.class) public class KernelAnalysisBenchmark { - private static final String TEST_ID = "org.eclipse.linuxtools#LTTng kernel analysis#"; + /** + * Test test ID for kernel analysis benchmarks + */ + public static final String TEST_ID = "org.eclipse.linuxtools#LTTng kernel analysis#"; private static final int LOOP_COUNT = 25; private final TestModule fTestModule; @@ -108,6 +111,22 @@ public class KernelAnalysisBenchmark { runTest(CtfTestTrace.TRACE2, "Trace2", fTestModule); } + /** + * Run the benchmark with "many thread" + */ + @Test + public void testManyThreads() { + runTest(CtfTestTrace.MANY_THREADS, "Many Threads", fTestModule); + } + + /** + * Run the benchmark with "django httpd" + */ + @Test + public void testDjangoHttpd() { + runTest(CtfTestTrace.DJANGO_HTTPD, "Django httpd", fTestModule); + } + private static void runTest(@NonNull CtfTestTrace testTrace, String testName, TestModule testModule) { Performance perf = Performance.getDefault(); PerformanceMeter pm = perf.createPerformanceMeter(TEST_ID + testName + testModule.getTestNameString()); diff --git a/lttng/org.eclipse.tracecompass.lttng2.kernel.core.tests/perf/org/eclipse/tracecompass/lttng2/kernel/core/tests/perf/analysis/kernel/KernelAnalysisUsageBenchmark.java b/lttng/org.eclipse.tracecompass.lttng2.kernel.core.tests/perf/org/eclipse/tracecompass/lttng2/kernel/core/tests/perf/analysis/kernel/KernelAnalysisUsageBenchmark.java new file mode 100644 index 0000000000..0d3aec6fe8 --- /dev/null +++ b/lttng/org.eclipse.tracecompass.lttng2.kernel.core.tests/perf/org/eclipse/tracecompass/lttng2/kernel/core/tests/perf/analysis/kernel/KernelAnalysisUsageBenchmark.java @@ -0,0 +1,227 @@ +/******************************************************************************* + * 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(); + } +} -- 2.34.1