tmf: Automatically sync experiments set up with the same hosts
authorAlexandre Montplaisir <alexmonthy@efficios.com>
Tue, 4 Oct 2016 23:56:44 +0000 (19:56 -0400)
committerAlexandre Montplaisir <alexmonthy@efficios.com>
Mon, 24 Oct 2016 18:56:05 +0000 (14:56 -0400)
Automatically correct the clock offset of traces inside an experiment
who define a custom property "clock_offset", by calculating
the average of those offsets for all concerned traces.

This is useful for LTTng traces coming from different domains.

This behaviour is similar to how Babeltrace handles traces with
clocks with different offsets.

Bug: 484620

Change-Id: Ic30837fd6e9fc8b9a7592a1722f0240145c77d62
Signed-off-by: Matthew Khouzam <matthew.khouzam@ericsson.com>
Signed-off-by: Alexandre Montplaisir <alexmonthy@efficios.com>
Signed-off-by: Genevieve Bastien <gbastien+lttng@versatic.net>
Reviewed-on: https://git.eclipse.org/r/65330
Reviewed-by: Hudson CI
lttng/org.eclipse.tracecompass.lttng2.kernel.core.tests/META-INF/MANIFEST.MF
lttng/org.eclipse.tracecompass.lttng2.kernel.core.tests/src/org/eclipse/tracecompass/lttng2/kernel/core/tests/synchronization/UstKernelSyncTest.java [new file with mode: 0644]
tmf/org.eclipse.tracecompass.tmf.core.tests/src/org/eclipse/tracecompass/tmf/core/tests/trace/TmfExperimentTest.java
tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/trace/experiment/TmfExperiment.java

index 0875c72768d932fe74b623681a51fa3c5f115fc6..4426e5e387e976e0a9e2cfcfcc688be87678211f 100644 (file)
@@ -38,4 +38,4 @@ Export-Package: org.eclipse.tracecompass.lttng2.kernel.core.tests,
  org.eclipse.tracecompass.lttng2.lttng.kernel.core.tests.shared.vm
 Import-Package: com.google.common.collect,
  org.eclipse.test.performance,
- org.eclipse.tracecompass.testtraces.ctf
+ org.eclipse.tracecompass.testtraces.ctf;version="1.6.0"
diff --git a/lttng/org.eclipse.tracecompass.lttng2.kernel.core.tests/src/org/eclipse/tracecompass/lttng2/kernel/core/tests/synchronization/UstKernelSyncTest.java b/lttng/org.eclipse.tracecompass.lttng2.kernel.core.tests/src/org/eclipse/tracecompass/lttng2/kernel/core/tests/synchronization/UstKernelSyncTest.java
new file mode 100644 (file)
index 0000000..bf5c589
--- /dev/null
@@ -0,0 +1,207 @@
+/*******************************************************************************
+ * 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);
+    }
+}
index da34b4fa1c2d553d83e6c5881a6cbbc9c546e475..e0ccd766ae3e5e2e1bd3a6d694d84229e0788267 100644 (file)
@@ -25,20 +25,27 @@ import java.io.File;
 import java.io.IOException;
 import java.net.URISyntaxException;
 import java.net.URL;
+import java.util.Map;
 import java.util.Vector;
 
 import org.eclipse.core.runtime.FileLocator;
 import org.eclipse.core.runtime.Path;
+import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.tracecompass.internal.tmf.core.component.TmfProviderManager;
+import org.eclipse.tracecompass.internal.tmf.core.synchronization.SyncAlgorithmFullyIncremental;
 import org.eclipse.tracecompass.internal.tmf.core.trace.experiment.TmfExperimentContext;
 import org.eclipse.tracecompass.internal.tmf.core.trace.experiment.TmfExperimentLocation;
 import org.eclipse.tracecompass.tmf.core.analysis.IAnalysisModule;
 import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
 import org.eclipse.tracecompass.tmf.core.exceptions.TmfTraceException;
+import org.eclipse.tracecompass.tmf.core.project.model.ITmfPropertiesProvider;
 import org.eclipse.tracecompass.tmf.core.request.ITmfEventRequest;
 import org.eclipse.tracecompass.tmf.core.request.ITmfEventRequest.ExecutionType;
 import org.eclipse.tracecompass.tmf.core.request.TmfEventRequest;
 import org.eclipse.tracecompass.tmf.core.signal.TmfTraceOpenedSignal;
+import org.eclipse.tracecompass.tmf.core.synchronization.ITmfTimestampTransform;
+import org.eclipse.tracecompass.tmf.core.synchronization.SynchronizationAlgorithm;
+import org.eclipse.tracecompass.tmf.core.synchronization.TimestampTransformFactory;
 import org.eclipse.tracecompass.tmf.core.tests.TmfCoreTestPlugin;
 import org.eclipse.tracecompass.tmf.core.tests.analysis.AnalysisManagerTest;
 import org.eclipse.tracecompass.tmf.core.tests.shared.TmfTestTrace;
@@ -53,10 +60,13 @@ import org.eclipse.tracecompass.tmf.core.trace.location.TmfLongLocation;
 import org.eclipse.tracecompass.tmf.tests.stubs.analysis.TestExperimentAnalysis;
 import org.eclipse.tracecompass.tmf.tests.stubs.trace.TmfExperimentStub;
 import org.eclipse.tracecompass.tmf.tests.stubs.trace.TmfTraceStub;
+import org.eclipse.tracecompass.tmf.tests.stubs.trace.xml.TmfXmlTraceStub;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
+import com.google.common.collect.ImmutableMap;
+
 /**
  * Test suite for the TmfExperiment class (single trace).
  */
@@ -996,4 +1006,134 @@ public class TmfExperimentTest {
         assertTrue("isCancelled", request.isCancelled());
     }
 
+    private static abstract class TestTrace extends TmfXmlTraceStub implements ITmfPropertiesProvider {
+
+    }
+
+    /**
+     * Tests that experiment with traces from the same host and a clock offset
+     * are well synchronized
+     */
+    @Test
+    public void testWithSingleHostClockOffset() {
+        // Data for this specific test
+        String hostId = "Test Host 1";
+        long minOffset = 2000;
+        long offset = 1000;
+        String clockOffset = "clock_offset";
+
+        ITmfTrace t1 = new TestTrace() {
+            @Override
+            public @NonNull String getHostId() {
+                return hostId;
+            }
+            @Override
+            public @NonNull Map<@NonNull String, @NonNull String> getProperties() {
+                return ImmutableMap.of(clockOffset, String.valueOf(minOffset));
+            }
+        };
+
+        ITmfTrace t2 = new TestTrace() {
+            @Override
+            public @NonNull String getHostId() {
+                return hostId;
+            }
+            @Override
+            public @NonNull Map<@NonNull String, @NonNull String> getProperties() {
+                return ImmutableMap.of(clockOffset, String.valueOf(minOffset + offset));
+            }
+        };
+
+        TmfExperiment exp = new TmfExperimentStub(EXPERIMENT, new ITmfTrace[] { t1, t2 }, BLOCK_SIZE);
+
+        try {
+            assertEquals(TimestampTransformFactory.createWithOffset(offset / 2), t1.getTimestampTransform());
+            assertEquals(TimestampTransformFactory.createWithOffset(-offset / 2), t2.getTimestampTransform());
+
+        } finally {
+            exp.dispose();
+        }
+    }
+
+    /**
+     * Tests that opening an experiment whose traces already have a
+     * synchronization formula will not eliminate that formula. This test makes
+     * the supposition that the experiment was synchronized and the
+     * synchronization added the clock offset correction to the total formula.
+     */
+    @Test
+    public void testWithMultiHostClockOffset() {
+        // Data for this specific test
+        String hostId = "Test Host 1";
+        String hostId2 = "Test Host 2";
+        long minOffset = 2000;
+        long offset = 1000;
+        String clockOffset = "clock_offset";
+
+        ITmfTimestampTransform tt1 = TimestampTransformFactory.createLinear(2.0, offset / 2);
+        ITmfTimestampTransform tt2 = TimestampTransformFactory.createLinear(2.0, -offset / 2);
+        ITmfTimestampTransform tt3 = TimestampTransformFactory.createWithOffset(offset);
+
+        ITmfTrace t1 = new TestTrace() {
+            @Override
+            public @NonNull String getHostId() {
+                return hostId;
+            }
+            @Override
+            public @NonNull Map<@NonNull String, @NonNull String> getProperties() {
+                return ImmutableMap.of(clockOffset, String.valueOf(minOffset));
+            }
+
+        };
+        t1.setTimestampTransform(tt1);
+
+        ITmfTrace t2 = new TestTrace() {
+            @Override
+            public @NonNull String getHostId() {
+                return hostId;
+            }
+            @Override
+            public @NonNull Map<@NonNull String, @NonNull String> getProperties() {
+                return ImmutableMap.of(clockOffset, String.valueOf(minOffset + offset));
+            }
+        };
+        t2.setTimestampTransform(tt2);
+
+        ITmfTrace t3 = new TmfXmlTraceStub() {
+            @Override
+            public @NonNull String getHostId() {
+                return hostId2;
+            }
+        };
+        t3.setTimestampTransform(tt3);
+
+        TmfExperiment exp = new TmfExperimentStub(EXPERIMENT, new ITmfTrace[] { t1, t2, t3 }, BLOCK_SIZE) {
+
+            @Override
+            public SynchronizationAlgorithm synchronizeTraces() {
+                return new SyncAlgorithmFullyIncremental() {
+
+                    private static final long serialVersionUID = 4206172498287480153L;
+
+                    @Override
+                    public ITmfTimestampTransform getTimestampTransform(String h) {
+                        if (hostId.equals(h)) {
+                            return TimestampTransformFactory.createLinear(2.0, 0);
+                        }
+                        return tt3;
+                    }
+                };
+            }
+        };
+
+        try {
+            assertEquals(tt1, t1.getTimestampTransform());
+            assertEquals(tt2, t2.getTimestampTransform());
+            assertEquals(tt3, t3.getTimestampTransform());
+
+        } finally {
+            exp.dispose();
+        }
+    }
+
 }
index a426fd3732d6d4c90c244ffe0a5a588fcda40c07..5ac6a2ed58f43737ebdab2069e299777cc7aedb0 100644 (file)
 
 package org.eclipse.tracecompass.tmf.core.trace.experiment;
 
+import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
+
 import java.io.File;
+import java.math.BigInteger;
 import java.nio.ByteBuffer;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
+import java.util.function.Function;
+import java.util.stream.Collectors;
 
 import org.eclipse.core.resources.IProject;
 import org.eclipse.core.resources.IResource;
@@ -34,19 +40,23 @@ import org.eclipse.core.runtime.Status;
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.Nullable;
 import org.eclipse.tracecompass.internal.tmf.core.Activator;
+import org.eclipse.tracecompass.internal.tmf.core.synchronization.TmfTimestampTransform;
 import org.eclipse.tracecompass.internal.tmf.core.trace.experiment.TmfExperimentContext;
 import org.eclipse.tracecompass.internal.tmf.core.trace.experiment.TmfExperimentLocation;
 import org.eclipse.tracecompass.internal.tmf.core.trace.experiment.TmfLocationArray;
 import org.eclipse.tracecompass.tmf.core.TmfCommonConstants;
 import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
 import org.eclipse.tracecompass.tmf.core.exceptions.TmfTraceException;
+import org.eclipse.tracecompass.tmf.core.project.model.ITmfPropertiesProvider;
 import org.eclipse.tracecompass.tmf.core.request.ITmfEventRequest;
 import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
 import org.eclipse.tracecompass.tmf.core.signal.TmfTraceOpenedSignal;
 import org.eclipse.tracecompass.tmf.core.signal.TmfTraceRangeUpdatedSignal;
 import org.eclipse.tracecompass.tmf.core.signal.TmfTraceSynchronizedSignal;
+import org.eclipse.tracecompass.tmf.core.synchronization.ITmfTimestampTransform;
 import org.eclipse.tracecompass.tmf.core.synchronization.SynchronizationAlgorithm;
 import org.eclipse.tracecompass.tmf.core.synchronization.SynchronizationManager;
+import org.eclipse.tracecompass.tmf.core.synchronization.TimestampTransformFactory;
 import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp;
 import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange;
 import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp;
@@ -59,6 +69,9 @@ import org.eclipse.tracecompass.tmf.core.trace.indexer.ITmfTraceIndexer;
 import org.eclipse.tracecompass.tmf.core.trace.indexer.TmfBTreeTraceIndexer;
 import org.eclipse.tracecompass.tmf.core.trace.location.ITmfLocation;
 
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
+
 /**
  * TmfExperiment presents a time-ordered, unified view of a set of ITmfTrace:s
  * that are part of a tracing experiment.
@@ -96,6 +109,16 @@ public class TmfExperiment extends TmfTrace implements ITmfPersistentlyIndexable
      */
     public static final int DEFAULT_INDEX_PAGE_SIZE = 5000;
 
+    /**
+     * Property name for traces defining a clock offset.
+     */
+    private static final String CLOCK_OFFSET_PROPERTY = "clock_offset"; //$NON-NLS-1$
+
+    /**
+     * If the automatic clock offset is higher than this value, emit a warning.
+     */
+    private static final long CLOCK_OFFSET_THRESHOLD_NS = 500000;
+
     // ------------------------------------------------------------------------
     // Attributes
     // ------------------------------------------------------------------------
@@ -208,11 +231,14 @@ public class TmfExperiment extends TmfTrace implements ITmfPersistentlyIndexable
         setCacheSize(indexPageSize);
         setStreamingInterval(0);
 
+        Multimap<String, ITmfTrace> tracesPerHost = HashMultimap.create();
+
         // traces have to be set before super.initialize()
         if (traces != null) {
             // initialize
             for (ITmfTrace trace : traces) {
                 if (trace != null) {
+                    tracesPerHost.put(trace.getHostId(), trace);
                     addChild(trace);
                 }
             }
@@ -225,7 +251,81 @@ public class TmfExperiment extends TmfTrace implements ITmfPersistentlyIndexable
         }
 
         if (resource != null) {
-            this.synchronizeTraces();
+            synchronizeTraces();
+        }
+
+        /*
+         * For all traces on the same host, if two or more specify different
+         * clock offsets, adjust their clock offset by the average of all of them.
+         *
+         * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=484620
+         */
+        Function<ITmfPropertiesProvider, @Nullable Long> offsetGetter = trace -> {
+            String offset = trace.getProperties().get(CLOCK_OFFSET_PROPERTY);
+            if (offset == null) {
+                return null;
+            }
+            try {
+                return Long.parseLong(offset);
+            } catch (NumberFormatException e) {
+                return null;
+            }
+        };
+
+        for (String host : tracesPerHost.keySet()) {
+            /*
+             * Only attempt to synchronize traces that provide a clock_offset
+             * property.
+             */
+            Collection<ITmfPropertiesProvider> tracesToSynchronize = tracesPerHost.get(host).stream()
+                    .filter(trace -> trace instanceof ITmfPropertiesProvider)
+                    .map(trace -> (ITmfPropertiesProvider) trace)
+                    .filter(trace -> offsetGetter.apply(trace) != null)
+                    .collect(Collectors.toList());
+
+            if (tracesToSynchronize.size() < 2) {
+                continue;
+            }
+
+            /* Only synchronize traces if they haven't previously been synchronized */
+            if (tracesToSynchronize.stream()
+                    .map(trace -> ((ITmfTrace) trace).getTimestampTransform())
+                    .anyMatch(transform -> !transform.equals(TmfTimestampTransform.IDENTITY))) {
+                continue;
+            }
+
+            /* Calculate the average of all clock offsets */
+            BigInteger sum = BigInteger.ZERO;
+            for (ITmfPropertiesProvider trace : tracesToSynchronize) {
+                long offset = checkNotNull(offsetGetter.apply(trace));
+                sum = sum.add(BigInteger.valueOf(offset));
+            }
+            long average = sum.divide(BigInteger.valueOf(tracesToSynchronize.size())).longValue();
+
+            if (average > CLOCK_OFFSET_THRESHOLD_NS) {
+                Activator.logWarning("Average clock correction (" + average + ") is higher than threshold of " + //$NON-NLS-1$ //$NON-NLS-2$
+                        CLOCK_OFFSET_THRESHOLD_NS + " ns for experiment " + this.toString()); //$NON-NLS-1$
+            }
+
+            /*
+             * Apply the offset correction to all identified traces, but only if
+             * they do not already have an equivalent one (for example, closing
+             * and re-opening the same experiment should not retrigger building
+             * all supplementary files).
+             */
+            tracesToSynchronize.forEach(t -> {
+                long offset = checkNotNull(offsetGetter.apply(t));
+                long delta = average - offset;
+
+                ITmfTrace trace = (ITmfTrace) t;
+                ITmfTimestampTransform currentTransform = trace.getTimestampTransform();
+                ITmfTimestampTransform newTransform = TimestampTransformFactory.createWithOffset(delta);
+
+                if (!newTransform.equals(currentTransform)) {
+                    TmfTraceManager.deleteSupplementaryFiles(trace);
+                    trace.setTimestampTransform(newTransform);
+                }
+            });
         }
     }
 
This page took 0.030589 seconds and 5 git commands to generate.