From: Alexandre Montplaisir Date: Tue, 4 Oct 2016 23:56:44 +0000 (-0400) Subject: tmf: Automatically sync experiments set up with the same hosts X-Git-Url: http://git.efficios.com/?p=deliverable%2Ftracecompass.git;a=commitdiff_plain;h=472ea248a0bba4cee7ee055b4a4d676e1bf2dda1 tmf: Automatically sync experiments set up with the same hosts 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 Signed-off-by: Alexandre Montplaisir Signed-off-by: Genevieve Bastien Reviewed-on: https://git.eclipse.org/r/65330 Reviewed-by: Hudson CI --- diff --git a/lttng/org.eclipse.tracecompass.lttng2.kernel.core.tests/META-INF/MANIFEST.MF b/lttng/org.eclipse.tracecompass.lttng2.kernel.core.tests/META-INF/MANIFEST.MF index 0875c72768..4426e5e387 100644 --- a/lttng/org.eclipse.tracecompass.lttng2.kernel.core.tests/META-INF/MANIFEST.MF +++ b/lttng/org.eclipse.tracecompass.lttng2.kernel.core.tests/META-INF/MANIFEST.MF @@ -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 index 0000000000..bf5c589a09 --- /dev/null +++ b/lttng/org.eclipse.tracecompass.lttng2.kernel.core.tests/src/org/eclipse/tracecompass/lttng2/kernel/core/tests/synchronization/UstKernelSyncTest.java @@ -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); + } +} diff --git a/tmf/org.eclipse.tracecompass.tmf.core.tests/src/org/eclipse/tracecompass/tmf/core/tests/trace/TmfExperimentTest.java b/tmf/org.eclipse.tracecompass.tmf.core.tests/src/org/eclipse/tracecompass/tmf/core/tests/trace/TmfExperimentTest.java index da34b4fa1c..e0ccd766ae 100644 --- a/tmf/org.eclipse.tracecompass.tmf.core.tests/src/org/eclipse/tracecompass/tmf/core/tests/trace/TmfExperimentTest.java +++ b/tmf/org.eclipse.tracecompass.tmf.core.tests/src/org/eclipse/tracecompass/tmf/core/tests/trace/TmfExperimentTest.java @@ -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(); + } + } + } diff --git a/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/trace/experiment/TmfExperiment.java b/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/trace/experiment/TmfExperiment.java index a426fd3732..5ac6a2ed58 100644 --- a/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/trace/experiment/TmfExperiment.java +++ b/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/trace/experiment/TmfExperiment.java @@ -18,12 +18,18 @@ 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 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 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 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); + } + }); } }