Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/tmf/core/synchronization/SyncAlgorithmFullyIncremental.java')
-rw-r--r--tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/tmf/core/synchronization/SyncAlgorithmFullyIncremental.java609
1 files changed, 609 insertions, 0 deletions
diff --git a/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/tmf/core/synchronization/SyncAlgorithmFullyIncremental.java b/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/tmf/core/synchronization/SyncAlgorithmFullyIncremental.java
new file mode 100644
index 0000000000..4b1b1ae026
--- /dev/null
+++ b/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/tmf/core/synchronization/SyncAlgorithmFullyIncremental.java
@@ -0,0 +1,609 @@
+/*******************************************************************************
+ * Copyright (c) 2013, 2014 É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
+ *
+ * Contributors:
+ * Geneviève Bastien - Initial implementation and API
+ * Francis Giraldeau - Transform computation using synchronization graph
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.tmf.core.synchronization;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.math.MathContext;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.tracecompass.common.core.NonNullUtils;
+import org.eclipse.tracecompass.internal.tmf.core.synchronization.graph.SyncSpanningTree;
+import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
+import org.eclipse.tracecompass.tmf.core.event.matching.TmfEventDependency;
+import org.eclipse.tracecompass.tmf.core.synchronization.ITmfTimestampTransform;
+import org.eclipse.tracecompass.tmf.core.synchronization.Messages;
+import org.eclipse.tracecompass.tmf.core.synchronization.SynchronizationAlgorithm;
+import org.eclipse.tracecompass.tmf.core.synchronization.TimestampTransformFactory;
+import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+
+/**
+ * Class implementing fully incremental trace synchronization approach as
+ * described in
+ *
+ * Masoume Jabbarifar, Michel Dagenais and Alireza Shameli-Sendi,
+ * "Streaming Mode Incremental Clock Synchronization"
+ *
+ * Since the algorithm itself applies to two traces, it is implemented in a
+ * private class, while this public class manages the synchronization between
+ * all traces.
+ *
+ * @author Geneviève Bastien
+ */
+public class SyncAlgorithmFullyIncremental extends SynchronizationAlgorithm {
+
+ /**
+ * Auto-generated serial UID
+ */
+ private static final long serialVersionUID = -1782788842774838830L;
+
+ private static final MathContext fMc = MathContext.DECIMAL128;
+
+ /** @Serial */
+ private final List<ConvexHull> fSyncs;
+
+ private transient SyncSpanningTree fTree = null;
+
+ /**
+ * Initialization of the attributes
+ */
+ public SyncAlgorithmFullyIncremental() {
+ fSyncs = new LinkedList<>();
+ }
+
+ /**
+ * Function called after all matching has been done, to do any post-match
+ * treatment. For this class, it calculates stats, while the data is
+ * available
+ */
+ @Override
+ public void matchingEnded() {
+ getStats();
+ }
+
+ @Override
+ public void init(Collection<ITmfTrace> traces) {
+ ITmfTrace[] traceArr = traces.toArray(new ITmfTrace[traces.size()]);
+ fSyncs.clear();
+ /* Create a convex hull for all trace pairs */
+ // FIXME: is it necessary to make ConvexHull for every pairs up-front?
+ // The ConvexHull seems to be created on the fly in processMatch().
+ for (int i = 0; i < traceArr.length; i++) {
+ for (int j = i + 1; j < traceArr.length; j++) {
+ if (!traceArr[i].getHostId().equals(traceArr[j].getHostId())) {
+ ConvexHull algo = new ConvexHull(traceArr[i].getHostId(), traceArr[j].getHostId());
+ fSyncs.add(algo);
+ }
+ }
+ }
+ }
+
+ @Override
+ protected void processMatch(TmfEventDependency match) {
+ String host1 = match.getSourceEvent().getTrace().getHostId();
+ String host2 = match.getDestinationEvent().getTrace().getHostId();
+
+ /* Process only if source and destination are different */
+ if (host1.equals(host2)) {
+ return;
+ }
+
+ /* Check if a convex hull algorithm already exists for these 2 hosts */
+ ConvexHull algo = null;
+ for (ConvexHull traceSync : fSyncs) {
+ if (traceSync.isForHosts(host1, host2)) {
+ algo = traceSync;
+ }
+ }
+ if (algo == null) {
+ algo = new ConvexHull(host1, host2);
+ fSyncs.add(algo);
+ }
+ algo.processMatch(match);
+ invalidateSyncGraph();
+ }
+
+ private void invalidateSyncGraph() {
+ fTree = null;
+ }
+
+ @Override
+ public ITmfTimestampTransform getTimestampTransform(ITmfTrace trace) {
+ return getTimestampTransform(trace.getHostId());
+ }
+
+ @Override
+ public ITmfTimestampTransform getTimestampTransform(String hostId) {
+ SyncSpanningTree tree = getSyncTree();
+ return tree.getTimestampTransform(hostId);
+ }
+
+ /**
+ * Each convex hull computes the synchronization between 2 given hosts. A
+ * synchronization can be done on multiple hosts that may not all
+ * communicate with each other. We must use another algorithm to determine
+ * which host will be the reference node and what synchronization formula
+ * will be used between each host and this reference node.
+ *
+ * For example, take traces a, b and c where a and c talk to b but do not
+ * know each other ({@literal a <-> b <-> c}). The convex hulls will contain
+ * the formulae between their 2 traces, but if a is the reference node, then
+ * the resulting formula of c would be the composition of {@literal a <-> b}
+ * and {@literal b <-> c}
+ *
+ * @return The synchronization spanning tree for this synchronization
+ */
+ private SyncSpanningTree getSyncTree() {
+ if (fTree == null) {
+ fTree = new SyncSpanningTree();
+ for (ConvexHull traceSync : fSyncs) {
+ SyncQuality q = traceSync.getQuality();
+ if (q == SyncQuality.ACCURATE || q == SyncQuality.APPROXIMATE) {
+ String from = traceSync.getReferenceHost();
+ String to = traceSync.getOtherHost();
+ fTree.addSynchronization(from, to, traceSync.getTimestampTransform(to), traceSync.getAccuracy());
+ }
+ }
+ }
+ return fTree;
+ }
+
+ @Override
+ public SyncQuality getSynchronizationQuality(ITmfTrace trace1, ITmfTrace trace2) {
+ for (ConvexHull traceSync : fSyncs) {
+ if (traceSync.isForHosts(trace1.getHostId(), trace2.getHostId())) {
+ return traceSync.getQuality();
+ }
+ }
+ return SyncQuality.ABSENT;
+ }
+
+ @Override
+ public boolean isTraceSynced(String hostId) {
+ ITmfTimestampTransform t = getTimestampTransform(hostId);
+ return !t.equals(TimestampTransformFactory.getDefaultTransform());
+ }
+
+ @Override
+ public Map<String, Map<String, Object>> getStats() {
+ /*
+ * TODO: Stats, while still accurate, may be misleading now that the
+ * sync tree changes synchronization formula. The stats should use the
+ * tree instead
+ */
+ Map<String, Map<String, Object>> statmap = new LinkedHashMap<>();
+ for (ConvexHull traceSync : fSyncs) {
+ statmap.put(traceSync.getReferenceHost() + " <==> " + traceSync.getOtherHost(), traceSync.getStats()); //$NON-NLS-1$
+ }
+ return statmap;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder b = new StringBuilder();
+ b.append(getClass().getSimpleName() + " "); //$NON-NLS-1$
+ b.append(fSyncs);
+ return b.toString();
+ }
+
+ /**
+ * This is the actual synchronization algorithm between two traces using
+ * convex hull
+ */
+ private class ConvexHull implements Serializable {
+
+ private static final long serialVersionUID = 8309351175030935291L;
+
+ private final String fReferenceHost;
+ private final String fOtherHost;
+
+ /**
+ * Slopes and ordinate at origin of respectively fLmin, fLmax and the
+ * bisector
+ */
+ private BigDecimal fAlphamin, fBetamax, fAlphamax, fBetamin, fAlpha, fBeta;
+ private int fNbMatches, fNbAccurateMatches;
+ private SyncQuality fQuality;
+
+ /**
+ * The list of meaningful points on the upper hull (received by the
+ * reference trace, below in a graph)
+ */
+ private transient LinkedList<SyncPoint> fUpperBoundList = new LinkedList<>();
+ /**
+ * The list of meaninful points on the lower hull (sent by the reference
+ * trace, above in a graph)
+ */
+ private transient LinkedList<SyncPoint> fLowerBoundList = new LinkedList<>();
+
+ /** Points forming the line with maximum slope */
+ private transient SyncPoint[] fLmax = new SyncPoint[2];
+ /** Points forming the line with minimum slope */
+ private transient SyncPoint[] fLmin = new SyncPoint[2];
+
+ private transient Map<String, Object> fStats = new LinkedHashMap<>();
+
+ /**
+ * Initialization of the attributes
+ *
+ * @param host1
+ * ID of the first host
+ * @param host2
+ * ID of the second host
+ */
+ public ConvexHull(String host1, String host2) {
+ if (host1.compareTo(host2) > 0) {
+ fReferenceHost = host2;
+ fOtherHost = host1;
+ } else {
+ fReferenceHost = host1;
+ fOtherHost = host2;
+ }
+ fAlpha = BigDecimal.ONE;
+ fAlphamax = BigDecimal.ONE;
+ fAlphamin = BigDecimal.ONE;
+ fBeta = BigDecimal.ZERO;
+ fBetamax = BigDecimal.ZERO;
+ fBetamin = BigDecimal.ZERO;
+ fNbMatches = 0;
+ fNbAccurateMatches = 0;
+ fQuality = SyncQuality.ABSENT; // default quality
+ }
+
+ protected void processMatch(TmfEventDependency match) {
+
+ LinkedList<SyncPoint> boundList, otherBoundList;
+
+ SyncPoint[] line, otherLine;
+ SyncPoint p;
+ int inversionFactor = 1;
+ boolean qualify = false;
+ fNbMatches++;
+
+ /* Initialize data depending on the which hull the match is part of */
+ if (match.getSourceEvent().getTrace().getHostId().compareTo(match.getDestinationEvent().getTrace().getHostId()) > 0) {
+ boundList = fUpperBoundList;
+ otherBoundList = fLowerBoundList;
+ line = fLmin;
+ otherLine = fLmax;
+ p = new SyncPoint(match.getDestinationEvent(), match.getSourceEvent());
+ inversionFactor = 1;
+ } else {
+ boundList = fLowerBoundList;
+ otherBoundList = fUpperBoundList;
+ line = fLmax;
+ otherLine = fLmin;
+ p = new SyncPoint(match.getSourceEvent(), match.getDestinationEvent());
+ inversionFactor = -1;
+ }
+
+ /*
+ * Does the message qualify for the hull, or is in on the wrong side
+ * of the reference line
+ */
+ if ((line[0] == null) || (line[1] == null) || (p.crossProduct(line[0], line[1]) * inversionFactor > 0)) {
+ /*
+ * If message qualifies, verify if points need to be removed
+ * from the hull and add the new point as the maximum reference
+ * point for the line. Also clear the stats that are not good
+ * anymore
+ */
+ fNbAccurateMatches++;
+ qualify = true;
+ removeUselessPoints(p, boundList, inversionFactor);
+ line[1] = p;
+ fStats.clear();
+ }
+
+ /*
+ * Adjust the boundary of the reference line and if one of the
+ * reference point of the other line was removed from the hull, also
+ * adjust the other line
+ */
+ adjustBound(line, otherBoundList, inversionFactor);
+ if ((otherLine[1] != null) && !boundList.contains(otherLine[0])) {
+ adjustBound(otherLine, boundList, inversionFactor * -1);
+ }
+
+ if (qualify) {
+ approximateSync();
+ }
+
+ }
+
+ /**
+ * Calculates slopes and ordinate at origin of fLmax and fLmin to obtain
+ * and approximation of the synchronization at this time
+ */
+ private void approximateSync() {
+
+ /**
+ * Line slopes functions
+ *
+ * Lmax = alpha_max T + beta_min
+ *
+ * Lmin = alpha_min T + beta_max
+ */
+ if ((fLmax[0] != null) || (fLmin[0] != null)) {
+ /**
+ * Do not recalculate synchronization after it is failed. We
+ * keep the last not failed result.
+ */
+ if (getQuality() != SyncQuality.FAIL) {
+ BigDecimal alphamax = fLmax[1].getAlpha(fLmax[0]);
+ BigDecimal alphamin = fLmin[1].getAlpha(fLmin[0]);
+ SyncQuality quality = null;
+
+ if ((fLmax[0] == null) || (fLmin[0] == null)) {
+ quality = SyncQuality.APPROXIMATE;
+ }
+ else if (alphamax.compareTo(alphamin) > 0) {
+ quality = SyncQuality.ACCURATE;
+ } else {
+ /* Lines intersect, not good */
+ quality = SyncQuality.FAIL;
+ }
+ /*
+ * Only calculate sync if this match does not cause failure
+ * of synchronization
+ */
+ if (quality != SyncQuality.FAIL) {
+ fAlphamax = alphamax;
+ fBetamin = fLmax[1].getBeta(fAlphamax);
+ fAlphamin = alphamin;
+ fBetamax = fLmin[1].getBeta(fAlphamin);
+ fAlpha = fAlphamax.add(fAlphamin).divide(BigDecimal.valueOf(2), fMc);
+ fBeta = fBetamin.add(fBetamax).divide(BigDecimal.valueOf(2), fMc);
+ }
+ setQuality(quality);
+ }
+ } else if (((fLmax[0] == null) && (fLmin[1] == null))
+ || ((fLmax[1] == null) && (fLmin[0] == null))) {
+ /* Either there is no upper hull point or no lower hull */
+ setQuality(SyncQuality.INCOMPLETE);
+ }
+ }
+
+ /*
+ * Verify if the line should be adjusted to be more accurate give the
+ * hull
+ */
+ private void adjustBound(SyncPoint[] line, LinkedList<SyncPoint> otherBoundList, int inversionFactor) {
+ SyncPoint minPoint = null, nextPoint;
+ boolean finishedSearch = false;
+
+ /*
+ * Find in the other bound, the origin point of the line, start from
+ * the beginning if the point was lost
+ */
+ int i = Math.max(0, otherBoundList.indexOf(line[0]));
+
+ while ((i < otherBoundList.size() - 1) && !finishedSearch) {
+ minPoint = otherBoundList.get(i);
+ nextPoint = otherBoundList.get(i + 1);
+
+ /*
+ * If the rotation (cross-product) is not optimal, move to next
+ * point as reference for the line (if available)
+ *
+ * Otherwise, the current minPoint is the minPoint of the line
+ */
+ if (minPoint.crossProduct(nextPoint, line[1]) * inversionFactor > 0) {
+ if (nextPoint.getTimeX() < line[1].getTimeX()) {
+ i++;
+ } else {
+ line[0] = null;
+ finishedSearch = true;
+ }
+ } else {
+ line[0] = minPoint;
+ finishedSearch = true;
+ }
+ }
+
+ if (line[0] == null) {
+ line[0] = minPoint;
+ }
+
+ /* Make sure point 0 is before point 1 */
+ if ((line[0] != null) && (line[0].getTimeX() > line[1].getTimeX())) {
+ line[0] = null;
+ }
+ }
+
+ /*
+ * When a point qualifies to be in a hull, we verify if any of the
+ * existing points need to be removed from the hull
+ */
+ private void removeUselessPoints(final SyncPoint p, final LinkedList<SyncPoint> boundList, final int inversionFactor) {
+
+ boolean checkRemove = true;
+
+ while (checkRemove && boundList.size() >= 2) {
+ if (p.crossProduct(boundList.get(boundList.size() - 2), boundList.getLast()) * inversionFactor > 0) {
+ boundList.removeLast();
+ } else {
+ checkRemove = false;
+ }
+ }
+ boundList.addLast(p);
+ }
+
+ public ITmfTimestampTransform getTimestampTransform(String hostId) {
+ if (hostId.equals(fOtherHost) && (getQuality() == SyncQuality.ACCURATE || getQuality() == SyncQuality.APPROXIMATE || getQuality() == SyncQuality.FAIL)) {
+ /* alpha: beta => 1 / fAlpha, -1 * fBeta / fAlpha); */
+ return TimestampTransformFactory.createLinear(NonNullUtils.checkNotNull(BigDecimal.ONE.divide(fAlpha, fMc)), NonNullUtils.checkNotNull(BigDecimal.valueOf(-1).multiply(fBeta).divide(fAlpha, fMc)));
+ }
+ return TimestampTransformFactory.getDefaultTransform();
+ }
+
+ public SyncQuality getQuality() {
+ return fQuality;
+ }
+
+ public BigDecimal getAccuracy() {
+ return fAlphamax.subtract(fAlphamin);
+ }
+
+ public Map<String, Object> getStats() {
+ if (fStats.size() == 0) {
+ String syncQuality;
+ switch (getQuality()) {
+ case ABSENT:
+ syncQuality = Messages.SyncAlgorithmFullyIncremental_absent;
+ break;
+ case ACCURATE:
+ syncQuality = Messages.SyncAlgorithmFullyIncremental_accurate;
+ break;
+ case APPROXIMATE:
+ syncQuality = Messages.SyncAlgorithmFullyIncremental_approx;
+ break;
+ case INCOMPLETE:
+ syncQuality = Messages.SyncAlgorithmFullyIncremental_incomplete;
+ break;
+ case FAIL:
+ default:
+ syncQuality = Messages.SyncAlgorithmFullyIncremental_fail;
+ break;
+ }
+
+ fStats.put(Messages.SyncAlgorithmFullyIncremental_refhost, fReferenceHost);
+ fStats.put(Messages.SyncAlgorithmFullyIncremental_otherhost, fOtherHost);
+ fStats.put(Messages.SyncAlgorithmFullyIncremental_quality, syncQuality);
+ fStats.put(Messages.SyncAlgorithmFullyIncremental_alpha, fAlpha);
+ fStats.put(Messages.SyncAlgorithmFullyIncremental_beta, fBeta);
+ fStats.put(Messages.SyncAlgorithmFullyIncremental_ub, (fUpperBoundList.size() == 0) ? Messages.SyncAlgorithmFullyIncremental_NA : fUpperBoundList.size());
+ fStats.put(Messages.SyncAlgorithmFullyIncremental_lb, (fLowerBoundList.size() == 0) ? Messages.SyncAlgorithmFullyIncremental_NA : fLowerBoundList.size());
+ fStats.put(Messages.SyncAlgorithmFullyIncremental_accuracy, getAccuracy().doubleValue());
+ fStats.put(Messages.SyncAlgorithmFullyIncremental_nbmatch, (fNbMatches == 0) ? Messages.SyncAlgorithmFullyIncremental_NA : fNbMatches);
+ fStats.put(Messages.SyncAlgorithmFullyIncremental_nbacc, (fNbAccurateMatches == 0) ? Messages.SyncAlgorithmFullyIncremental_NA : fNbAccurateMatches);
+ fStats.put(Messages.SyncAlgorithmFullyIncremental_refformula, Messages.SyncAlgorithmFullyIncremental_T_ + fReferenceHost);
+ fStats.put(Messages.SyncAlgorithmFullyIncremental_otherformula, fAlpha + Messages.SyncAlgorithmFullyIncremental_mult + Messages.SyncAlgorithmFullyIncremental_T_ + fReferenceHost + Messages.SyncAlgorithmFullyIncremental_add + fBeta);
+ }
+ return fStats;
+
+ }
+
+ public String getReferenceHost() {
+ return fReferenceHost;
+ }
+
+ public String getOtherHost() {
+ return fOtherHost;
+ }
+
+ public boolean isForHosts(String hostId1, String hostId2) {
+ return ((fReferenceHost.equals(hostId1) && fOtherHost.equals(hostId2)) || (fReferenceHost.equals(hostId2) && fOtherHost.equals(hostId1)));
+ }
+
+ private void readObject(ObjectInputStream stream)
+ throws IOException, ClassNotFoundException {
+ stream.defaultReadObject();
+
+ /* Initialize transient fields */
+ fUpperBoundList = new LinkedList<>();
+ fLowerBoundList = new LinkedList<>();
+ fLmax = new SyncPoint[2];
+ fLmin = new SyncPoint[2];
+ fStats = new LinkedHashMap<>();
+ }
+
+ @SuppressWarnings("nls")
+ @Override
+ public String toString() {
+ StringBuilder b = new StringBuilder();
+ b.append("Between " + fReferenceHost + " and " + fOtherHost + " [");
+ b.append(" alpha " + fAlpha + " beta " + fBeta + " ]");
+ return b.toString();
+ }
+
+ private void setQuality(SyncQuality fQuality) {
+ this.fQuality = fQuality;
+ }
+
+ }
+
+ /**
+ * Private class representing a point to synchronize on a graph. The x axis
+ * is the timestamp of the event from the reference trace while the y axis
+ * is the timestamp of the event on the other trace
+ */
+ private class SyncPoint {
+ private final ITmfTimestamp x, y;
+
+ public SyncPoint(ITmfEvent ex, ITmfEvent ey) {
+ x = ex.getTimestamp();
+ y = ey.getTimestamp();
+ }
+
+ public long getTimeX() {
+ return x.getValue();
+ }
+
+ /**
+ * Calculate a cross product of 3 points:
+ *
+ * If the cross-product < 0, then p, pa, pb are clockwise
+ *
+ * If the cross-product > 0, then p, pa, pb are counter-clockwise
+ *
+ * If cross-product == 0, then they are in a line
+ *
+ * @param pa
+ * First point
+ * @param pb
+ * Second point
+ * @return The cross product
+ */
+ public long crossProduct(SyncPoint pa, SyncPoint pb) {
+ long cp = ((pa.x.getValue() - x.getValue()) * (pb.y.getValue() - y.getValue()) - (pa.y.getValue() - y.getValue()) * (pb.x.getValue() - x.getValue()));
+ return cp;
+ }
+
+ /*
+ * Gets the alpha (slope) between two points
+ */
+ public BigDecimal getAlpha(SyncPoint p1) {
+ if (p1 == null) {
+ return BigDecimal.ONE;
+ }
+ BigDecimal deltay = BigDecimal.valueOf(y.getValue() - p1.y.getValue());
+ BigDecimal deltax = BigDecimal.valueOf(x.getValue() - p1.x.getValue());
+ if (deltax.equals(BigDecimal.ZERO)) {
+ return BigDecimal.ONE;
+ }
+ return deltay.divide(deltax, fMc);
+ }
+
+ /*
+ * Get the beta value (when x = 0) of the line given alpha
+ */
+ public BigDecimal getBeta(BigDecimal alpha) {
+ return BigDecimal.valueOf(y.getValue()).subtract(alpha.multiply(BigDecimal.valueOf(x.getValue()), fMc));
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%s (%s, %s)", this.getClass().getCanonicalName(), x, y); //$NON-NLS-1$
+ }
+ }
+
+}

Back to the top