+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * 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.synchronization;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Predicate;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.tracecompass.analysis.os.linux.core.kernel.KernelAnalysisModule;
+import org.eclipse.tracecompass.analysis.os.linux.core.kernel.KernelThreadInformationProvider;
+import org.eclipse.tracecompass.lttng2.kernel.core.trace.LttngKernelTrace;
+import org.eclipse.tracecompass.testtraces.ctf.CtfTestTrace;
+import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
+import org.eclipse.tracecompass.tmf.core.exceptions.TmfTraceException;
+import org.eclipse.tracecompass.tmf.core.signal.TmfSignalManager;
+import org.eclipse.tracecompass.tmf.core.signal.TmfTraceOpenedSignal;
+import org.eclipse.tracecompass.tmf.core.signal.TmfTraceSelectedSignal;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfContext;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils;
+import org.eclipse.tracecompass.tmf.core.trace.experiment.TmfExperiment;
+import org.eclipse.tracecompass.tmf.ctf.core.event.CtfTmfEvent;
+import org.eclipse.tracecompass.tmf.ctf.core.tests.shared.CtfTmfTestTraceUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.rules.Timeout;
+
+/**
+ * Test that synchronization between LTTng UST and kernel traces is done
+ * correctly.
+ *
+ * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=484620
+ *
+ * @author Alexandre Montplaisir
+ */
+public class UstKernelSyncTest {
+
+ /** Time-out tests after 60 seconds */
+ @Rule public TestRule globalTimeout= new Timeout(60, TimeUnit.SECONDS);
+
+ private static final @NonNull CtfTestTrace KERNEL_TRACE = CtfTestTrace.CONTEXT_SWITCHES_KERNEL;
+ private static final @NonNull CtfTestTrace UST_TRACE = CtfTestTrace.CONTEXT_SWITCHES_UST;
+
+ private TmfExperiment fExperiment;
+ private ITmfTrace fKernelTrace;
+ private ITmfTrace fUstTrace;
+ private KernelAnalysisModule fKernelModule;
+
+ /**
+ * Test setup
+ */
+ @Before
+ public void setup() {
+ ITmfTrace ustTrace = CtfTmfTestTraceUtils.getTrace(UST_TRACE);
+
+ /*
+ * We need to initialize the kernel trace to the "LttngKernelTrace"
+ * type ourselves, so the kernel analysis can run on it.
+ */
+ String kernelTracePath = CtfTmfTestTraceUtils.getTrace(KERNEL_TRACE).getPath();
+ ITmfTrace kernelTrace = new LttngKernelTrace();
+
+ try {
+ kernelTrace.initTrace(null, kernelTracePath, CtfTmfEvent.class);
+ } catch (TmfTraceException e) {
+ fail(e.getMessage());
+ }
+
+ TmfExperiment experiment = new TmfExperiment(CtfTmfEvent.class,
+ "test-exp",
+ new ITmfTrace[] { ustTrace, kernelTrace },
+ TmfExperiment.DEFAULT_INDEX_PAGE_SIZE,
+ null);
+
+ /* Simulate experiment being opened */
+ // We have to "open" the sub-traces too, or their analyses are
+ // never initialized. Is that on purpose?
+ TmfSignalManager.dispatchSignal(new TmfTraceOpenedSignal(this, ustTrace, null));
+ TmfSignalManager.dispatchSignal(new TmfTraceOpenedSignal(this, kernelTrace, null));
+ TmfSignalManager.dispatchSignal(new TmfTraceOpenedSignal(this, experiment, null));
+ TmfSignalManager.dispatchSignal(new TmfTraceSelectedSignal(this, experiment));
+
+ KernelAnalysisModule module = TmfTraceUtils.getAnalysisModuleOfClass(experiment,
+ KernelAnalysisModule.class, KernelAnalysisModule.ID);
+ assertNotNull(module);
+ module.waitForCompletion();
+
+ fExperiment = experiment;
+ fKernelTrace = kernelTrace;
+ fUstTrace = ustTrace;
+ fKernelModule = module;
+ }
+
+ /**
+ * Test teardown
+ */
+ @After
+ public void tearDown() {
+ if (fExperiment != null) {
+ fExperiment.dispose();
+ }
+ if (fKernelTrace != null) {
+ fKernelTrace.dispose();
+ }
+
+ CtfTmfTestTraceUtils.dispose(KERNEL_TRACE);
+ CtfTmfTestTraceUtils.dispose(UST_TRACE);
+ }
+
+ /**
+ * Test that the TID given by the kernel analysis matches the one from the
+ * UST event's context for a given UST event that was known to fail.
+ *
+ * Reproduces the specific example that was pointed out in bug 484620.
+ */
+ @Test
+ public void testOneEvent() {
+ TmfExperiment experiment = fExperiment;
+ ITmfTrace ustTrace = fUstTrace;
+ KernelAnalysisModule module = fKernelModule;
+ assertNotNull(experiment);
+ assertNotNull(ustTrace);
+ assertNotNull(module);
+
+ Predicate<@NonNull ITmfEvent> eventFinder = event -> {
+ Long addr = event.getContent().getFieldValue(Long.class, "addr");
+ Long cs = event.getContent().getFieldValue(Long.class, "call_site");
+ Long ctxVtid = event.getContent().getFieldValue(Long.class, "context._vtid");
+
+ if (addr == null || cs == null || ctxVtid == null) {
+ return false;
+ }
+
+ return Objects.equals(event.getType().getName(), "lttng_ust_cyg_profile:func_entry") &&
+ Objects.equals(Long.toHexString(addr), "804af97") &&
+ Objects.equals(Long.toHexString(cs), "804ab03") &&
+ Objects.equals(ctxVtid.longValue(), 594L);
+ };
+
+ /* The event we're looking for is the second event matching the predicate */
+ CtfTmfEvent ustEvent = (CtfTmfEvent) TmfTraceUtils.getNextEventMatching(experiment, 0, eventFinder, null);
+ assertNotNull(ustEvent);
+ long rank = experiment.seekEvent(ustEvent.getTimestamp()).getRank() + 1;
+ ustEvent = (CtfTmfEvent) TmfTraceUtils.getNextEventMatching(experiment, rank, eventFinder, null);
+ assertNotNull(ustEvent);
+
+ assertEquals(ustTrace, ustEvent.getTrace());
+
+ Integer tidFromKernel = KernelThreadInformationProvider.getThreadOnCpu(module,
+ ustEvent.getCPU(), ustEvent.getTimestamp().toNanos());
+
+ assertNotNull(tidFromKernel);
+ assertEquals(594, tidFromKernel.intValue());
+ }
+
+ /**
+ * Test going through the whole UST trace, making sure the VTID context of
+ * each event corresponds to the TID given by the kernel analysis at the
+ * same timestamp.
+ */
+ @Test
+ public void testWholeUstTrace() {
+ TmfExperiment experiment = fExperiment;
+ ITmfTrace ustTrace = fUstTrace;
+ KernelAnalysisModule module = fKernelModule;
+ assertNotNull(experiment);
+ assertNotNull(ustTrace);
+ assertNotNull(module);
+
+ ITmfContext context = ustTrace.seekEvent(0L);
+ CtfTmfEvent ustEvent = (CtfTmfEvent) ustTrace.getNext(context);
+ int count = 0;
+ while (ustEvent != null) {
+ Long ustVtid = ustEvent.getContent().getFieldValue(Long.class, "context._vtid");
+ /* All events in the trace should have that context */
+ assertNotNull(ustVtid);
+
+ long ts = ustEvent.getTimestamp().toNanos();
+ long cpu = ustEvent.getCPU();
+ Integer kernelTid = KernelThreadInformationProvider.getThreadOnCpu(module, cpu, ts);
+ assertNotNull(kernelTid);
+
+ assertEquals("Wrong TID for trace event " + ustEvent.toString(), ustVtid.longValue(), kernelTid.longValue());
+
+ ustEvent = (CtfTmfEvent) ustTrace.getNext(context);
+ count++;
+ }
+
+ /* Make sure we've read all expected events */
+ assertEquals(UST_TRACE.getNbEvents(), count);
+ }
+}