Tmf: Trace synchronization using network events
[deliverable/tracecompass.git] / org.eclipse.linuxtools.tmf.core / src / org / eclipse / linuxtools / tmf / core / synchronization / SyncAlgorithmFullyIncremental.java
1 /*******************************************************************************
2 * Copyright (c) 2013 École Polytechnique de Montréal
3 *
4 * All rights reserved. This program and the accompanying materials are made
5 * available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
8 *
9 * Contributors:
10 * Geneviève Bastien - Initial implementation and API
11 *******************************************************************************/
12
13 package org.eclipse.linuxtools.tmf.core.synchronization;
14
15 import java.io.IOException;
16 import java.io.ObjectOutputStream;
17 import java.io.Serializable;
18 import java.math.BigDecimal;
19 import java.math.MathContext;
20 import java.util.LinkedHashMap;
21 import java.util.LinkedList;
22 import java.util.List;
23 import java.util.Map;
24
25 import org.eclipse.linuxtools.tmf.core.event.ITmfEvent;
26 import org.eclipse.linuxtools.tmf.core.event.matching.TmfEventDependency;
27 import org.eclipse.linuxtools.tmf.core.timestamp.ITmfTimestamp;
28 import org.eclipse.linuxtools.tmf.core.trace.ITmfTrace;
29
30 /**
31 * Class implementing fully incremental trace synchronization approach as
32 * described in
33 *
34 * Masoume Jabbarifar, Michel Dagenais and Alireza Shameli-Sendi,
35 * "Streaming Mode Incremental Clock Synchronization"
36 *
37 * Since the algorithm itself applies to two traces, it is implemented in a
38 * private class, while this public class manages the synchronization between
39 * all traces.
40 *
41 * @author Geneviève Bastien
42 * @since 3.0
43 */
44 public class SyncAlgorithmFullyIncremental extends SynchronizationAlgorithm {
45
46 /**
47 * Auto-generated serial UID
48 */
49 private static final long serialVersionUID = -1782788842774838830L;
50
51 private static final MathContext fMc = MathContext.DECIMAL128;
52
53 private final List<ConvexHull> fSyncs;
54
55 /**
56 * Initialization of the attributes
57 */
58 public SyncAlgorithmFullyIncremental() {
59 fSyncs = new LinkedList<ConvexHull>();
60 }
61
62 /**
63 * Function called after all matching has been done, to do any post-match
64 * treatment. For this class, it calculates stats, while the data is
65 * available
66 */
67 @Override
68 public void matchingEnded() {
69 getStats();
70 }
71
72 @Override
73 public void init(ITmfTrace[] fTraces) {
74 fSyncs.clear();
75 /* Create a convex hull for all trace pairs */
76 for (int i = 0; i < fTraces.length; i++) {
77 for (int j = i + 1; j < fTraces.length; j++) {
78 ConvexHull algo = new ConvexHull(fTraces[i].getName(), fTraces[j].getName());
79 fSyncs.add(algo);
80 }
81 }
82 }
83
84 @Override
85 protected void processMatch(TmfEventDependency match) {
86 String trace1 = match.getSourceEvent().getTrace().getName();
87 String trace2 = match.getDestinationEvent().getTrace().getName();
88
89 /* Process only if source and destination are different */
90 if (trace1.equals(trace2)) {
91 return;
92 }
93
94 /* Check if a convex hull algorithm already exists for these 2 traces */
95 ConvexHull algo = null;
96 for (ConvexHull traceSync : fSyncs) {
97 if (traceSync.isForTraces(trace1, trace2)) {
98 algo = traceSync;
99 }
100 }
101 if (algo == null) {
102 algo = new ConvexHull(trace1, trace2);
103 fSyncs.add(algo);
104 }
105 algo.processMatch(match);
106
107 }
108
109 @Override
110 public ITmfTimestampTransform getTimestampTransform(ITmfTrace trace) {
111 return getTimestampTransform(trace.getName());
112 }
113
114 @Override
115 public ITmfTimestampTransform getTimestampTransform(String name) {
116 for (ConvexHull traceSync : fSyncs) {
117 if (traceSync.isTraceSynced(name)) {
118 /*
119 * Since there are many traces, maybe the reference trace is
120 * also synchronized, so we need to chain sync formulas
121 */
122 ITmfTimestampTransform refTt = getTimestampTransform(traceSync.getReferenceTrace());
123 return refTt.composeWith(traceSync.getTimestampTransform(name));
124 }
125 }
126 return TmfTimestampTransform.IDENTITY;
127 }
128
129 @Override
130 public SyncQuality getSynchronizationQuality(ITmfTrace trace1, ITmfTrace trace2) {
131 for (ConvexHull traceSync : fSyncs) {
132 if (traceSync.isForTraces(trace1.getName(), trace2.getName())) {
133 return traceSync.getQuality();
134 }
135 }
136 return SyncQuality.ABSENT;
137 }
138
139 @Override
140 public boolean isTraceSynced(String name) {
141 boolean traceSynced = false;
142 for (ConvexHull traceSync : fSyncs) {
143 traceSynced = traceSynced || traceSync.isTraceSynced(name);
144 }
145 return traceSynced;
146 }
147
148 /**
149 * Rename one of the traces in the synchronization
150 *
151 * @param oldname
152 * The name of the original trace
153 * @param newname
154 * The new name of the trace
155 */
156 @Override
157 public void renameTrace(String oldname, String newname) {
158 for (ConvexHull traceSync : fSyncs) {
159 traceSync.renameTrace(oldname, newname);
160 }
161 }
162
163 @Override
164 public Map<String, Map<String, Object>> getStats() {
165 Map<String, Map<String, Object>> statmap = new LinkedHashMap<String, Map<String, Object>>();
166 for (ConvexHull traceSync : fSyncs) {
167 statmap.put(traceSync.getReferenceTrace() + " <==> " + traceSync.getOtherTrace(), traceSync.getStats()); //$NON-NLS-1$
168 }
169 return statmap;
170 }
171
172 @Override
173 public String toString() {
174 StringBuilder b = new StringBuilder();
175 b.append(getClass().getSimpleName() + " "); //$NON-NLS-1$
176 b.append(fSyncs);
177 return b.toString();
178 }
179
180 /**
181 * This is the actual synchronization algorithm between two traces using
182 * convex hull
183 */
184 private class ConvexHull implements Serializable {
185
186 private static final long serialVersionUID = 8309351175030935291L;
187
188 /**
189 * The list of meaningful points on the upper hull (received by the
190 * reference trace, below in a graph)
191 */
192 private final LinkedList<SyncPoint> fUpperBoundList = new LinkedList<SyncPoint>();
193
194 /**
195 * The list of meaninful points on the lower hull (sent by the reference
196 * trace, above in a graph)
197 */
198 private final LinkedList<SyncPoint> fLowerBoundList = new LinkedList<SyncPoint>();
199
200 /** Points forming the line with maximum slope */
201 private final SyncPoint[] fLmax;
202
203 /** Points forming the line with minimum slope */
204 private final SyncPoint[] fLmin;
205
206 /**
207 * Slopes and ordinate at origin of respectively fLmin, fLmax and the
208 * bisector
209 */
210 private BigDecimal fAlphamin, fBetamax, fAlphamax, fBetamin, fAlpha, fBeta;
211
212 private int fNbMatches, fNbAccurateMatches;
213 private String fReferenceTrace = "", fOtherTrace = ""; //$NON-NLS-1$//$NON-NLS-2$
214 private SyncQuality fQuality;
215
216 private Map<String, Object> fStats = new LinkedHashMap<String, Object>();
217
218 /**
219 * Initialization of the attributes
220 *
221 * @param trace1
222 * Name of the first trace
223 * @param trace2
224 * Name of the second trace
225 */
226 public ConvexHull(String trace1, String trace2) {
227 if (trace1.compareTo(trace2) > 0) {
228 fReferenceTrace = trace2;
229 fOtherTrace = trace1;
230 } else {
231 fReferenceTrace = trace1;
232 fOtherTrace = trace2;
233 }
234 fLmax = new SyncPoint[2];
235 fLmin = new SyncPoint[2];
236 fAlpha = BigDecimal.ONE;
237 fAlphamax = BigDecimal.ONE;
238 fAlphamin = BigDecimal.ONE;
239 fBeta = BigDecimal.ZERO;
240 fBetamax = BigDecimal.ZERO;
241 fBetamin = BigDecimal.ZERO;
242 fNbMatches = 0;
243 fNbAccurateMatches = 0;
244 fQuality = SyncQuality.ABSENT;
245 }
246
247 protected void processMatch(TmfEventDependency match) {
248
249 LinkedList<SyncPoint> boundList, otherBoundList;
250
251 SyncPoint[] line, otherLine;
252 SyncPoint p;
253 int inversionFactor = 1;
254 boolean qualify = false;
255 fNbMatches++;
256
257 /* Initialize data depending on the which hull the match is part of */
258 if (match.getSourceEvent().getTrace().getName().compareTo(match.getDestinationEvent().getTrace().getName()) > 0) {
259 boundList = fUpperBoundList;
260 otherBoundList = fLowerBoundList;
261 line = fLmin;
262 otherLine = fLmax;
263 p = new SyncPoint(match.getDestinationEvent(), match.getSourceEvent());
264 inversionFactor = 1;
265 } else {
266 boundList = fLowerBoundList;
267 otherBoundList = fUpperBoundList;
268 line = fLmax;
269 otherLine = fLmin;
270 p = new SyncPoint(match.getSourceEvent(), match.getDestinationEvent());
271 inversionFactor = -1;
272 }
273
274 /*
275 * Does the message qualify for the hull, or is in on the wrong side
276 * of the reference line
277 */
278 if ((line[0] == null) || (line[1] == null) || (p.crossProduct(line[0], line[1]) * inversionFactor > 0)) {
279 /*
280 * If message qualifies, verify if points need to be removed
281 * from the hull and add the new point as the maximum reference
282 * point for the line. Also clear the stats that are not good
283 * anymore
284 */
285 fNbAccurateMatches++;
286 qualify = true;
287 removeUselessPoints(p, boundList, inversionFactor);
288 line[1] = p;
289 fStats.clear();
290 }
291
292 /*
293 * Adjust the boundary of the reference line and if one of the
294 * reference point of the other line was removed from the hull, also
295 * adjust the other line
296 */
297 adjustBound(line, otherBoundList, inversionFactor);
298 if ((otherLine[1] != null) && !boundList.contains(otherLine[0])) {
299 adjustBound(otherLine, boundList, inversionFactor * -1);
300 }
301
302 if (qualify) {
303 approximateSync();
304 }
305
306 }
307
308 /**
309 * Calculates slopes and ordinate at origin of fLmax and fLmin to obtain
310 * and approximation of the synchronization at this time
311 */
312 private void approximateSync() {
313 /**
314 * Line slopes functions
315 *
316 * Lmax = alpha_max T + beta_min
317 *
318 * Lmin = alpha_min T + beta_max
319 */
320 if ((fLmax[0] != null) || (fLmin[0] != null)) {
321 fAlphamax = fLmax[1].getAlpha(fLmax[0]);
322 fBetamin = fLmax[1].getBeta(fAlphamax);
323 fAlphamin = fLmin[1].getAlpha(fLmin[0]);
324 fBetamax = fLmin[1].getBeta(fAlphamin);
325 fAlpha = fAlphamax.add(fAlphamin).divide(BigDecimal.valueOf(2), fMc);
326 fBeta = fBetamin.add(fBetamax).divide(BigDecimal.valueOf(2), fMc);
327 if ((fLmax[0] == null) || (fLmin[0] == null)) {
328 fQuality = SyncQuality.APPROXIMATE;
329 }
330 else if (fAlphamax.compareTo(fAlphamin) > 0) {
331 fQuality = SyncQuality.ACCURATE;
332 } else {
333 /* Lines intersect, not good */
334 fQuality = SyncQuality.FAIL;
335 }
336 } else if (((fLmax[0] == null) && (fLmin[1] == null))
337 || ((fLmax[1] == null) && (fLmin[0] == null))) {
338 /* Either there is no upper hull point or no lower hull */
339 fQuality = SyncQuality.INCOMPLETE;
340 }
341 }
342
343 /*
344 * Verify if the line should be adjusted to be more accurate give the
345 * hull
346 */
347 private void adjustBound(SyncPoint[] line, LinkedList<SyncPoint> otherBoundList, int inversionFactor) {
348 SyncPoint minPoint = null, nextPoint;
349 boolean finishedSearch = false;
350
351 /*
352 * Find in the other bound, the origin point of the line, start from
353 * the beginning if the point was lost
354 */
355 int i = Math.max(0, otherBoundList.indexOf(line[0]));
356
357 while ((i < otherBoundList.size() - 1) && !finishedSearch) {
358 minPoint = otherBoundList.get(i);
359 nextPoint = otherBoundList.get(i + 1);
360
361 /*
362 * If the rotation (cross-product) is not optimal, move to next
363 * point as reference for the line (if available)
364 *
365 * Otherwise, the current minPoint is the minPoint of the line
366 */
367 if (minPoint.crossProduct(nextPoint, line[1]) * inversionFactor > 0) {
368 if (nextPoint.getTimeX() < line[1].getTimeX()) {
369 i++;
370 } else {
371 line[0] = null;
372 finishedSearch = true;
373 }
374 } else {
375 line[0] = minPoint;
376 finishedSearch = true;
377 }
378 }
379
380 if (line[0] == null) {
381 line[0] = minPoint;
382 }
383
384 /* Make sure point 0 is before point 1 */
385 if ((line[0] != null) && (line[0].getTimeX() > line[1].getTimeX())) {
386 line[0] = null;
387 }
388 }
389
390 /*
391 * When a point qualifies to be in a hull, we verify if any of the
392 * existing points need to be removed from the hull
393 */
394 private void removeUselessPoints(final SyncPoint p, final LinkedList<SyncPoint> boundList, final int inversionFactor) {
395
396 boolean checkRemove = true;
397
398 while (checkRemove && boundList.size() >= 2) {
399 if (p.crossProduct(boundList.get(boundList.size() - 2), boundList.getLast()) * inversionFactor > 0) {
400 boundList.removeLast();
401 } else {
402 checkRemove = false;
403 }
404 }
405 boundList.addLast(p);
406 }
407
408 public ITmfTimestampTransform getTimestampTransform(String name) {
409 if (name.equals(fOtherTrace) && (fQuality == SyncQuality.ACCURATE || fQuality == SyncQuality.APPROXIMATE || fQuality == SyncQuality.FAIL)) {
410 /* alpha: beta => 1 / fAlpha, -1 * fBeta / fAlpha); */
411 return new TmfTimestampTransformLinear(BigDecimal.ONE.divide(fAlpha, fMc), BigDecimal.valueOf(-1).multiply(fBeta).divide(fAlpha, fMc));
412 }
413 return TmfTimestampTransform.IDENTITY;
414 }
415
416 public SyncQuality getQuality() {
417 return fQuality;
418 }
419
420 public Map<String, Object> getStats() {
421 if (fStats.size() == 0) {
422 String syncQuality;
423 switch (fQuality) {
424 case ABSENT:
425 syncQuality = Messages.SyncAlgorithmFullyIncremental_absent;
426 break;
427 case ACCURATE:
428 syncQuality = Messages.SyncAlgorithmFullyIncremental_accurate;
429 break;
430 case APPROXIMATE:
431 syncQuality = Messages.SyncAlgorithmFullyIncremental_approx;
432 break;
433 case INCOMPLETE:
434 syncQuality = Messages.SyncAlgorithmFullyIncremental_incomplete;
435 break;
436 case FAIL:
437 default:
438 syncQuality = Messages.SyncAlgorithmFullyIncremental_fail;
439 break;
440 }
441
442 fStats.put(Messages.SyncAlgorithmFullyIncremental_reftrace, fReferenceTrace);
443 fStats.put(Messages.SyncAlgorithmFullyIncremental_othertrace, fOtherTrace);
444 fStats.put(Messages.SyncAlgorithmFullyIncremental_quality, syncQuality);
445 fStats.put(Messages.SyncAlgorithmFullyIncremental_alpha, fAlpha);
446 fStats.put(Messages.SyncAlgorithmFullyIncremental_beta, fBeta);
447 fStats.put(Messages.SyncAlgorithmFullyIncremental_ub, (fUpperBoundList.size() == 0) ? Messages.SyncAlgorithmFullyIncremental_NA : fUpperBoundList.size());
448 fStats.put(Messages.SyncAlgorithmFullyIncremental_lb, (fLowerBoundList.size() == 0) ? Messages.SyncAlgorithmFullyIncremental_NA : fLowerBoundList.size());
449 fStats.put(Messages.SyncAlgorithmFullyIncremental_accuracy, fAlphamax.subtract(fAlphamin).doubleValue()); // -
450 // fAlphamin);
451 fStats.put(Messages.SyncAlgorithmFullyIncremental_nbmatch, (fNbMatches == 0) ? Messages.SyncAlgorithmFullyIncremental_NA : fNbMatches);
452 fStats.put(Messages.SyncAlgorithmFullyIncremental_nbacc, (fNbAccurateMatches == 0) ? Messages.SyncAlgorithmFullyIncremental_NA : fNbAccurateMatches);
453 fStats.put(Messages.SyncAlgorithmFullyIncremental_refformula, Messages.SyncAlgorithmFullyIncremental_T_ + fReferenceTrace);
454 fStats.put(Messages.SyncAlgorithmFullyIncremental_otherformula, fAlpha + Messages.SyncAlgorithmFullyIncremental_mult + Messages.SyncAlgorithmFullyIncremental_T_ + fReferenceTrace + Messages.SyncAlgorithmFullyIncremental_add + fBeta);
455 }
456 return fStats;
457
458 }
459
460 public String getReferenceTrace() {
461 return fReferenceTrace;
462 }
463
464 public String getOtherTrace() {
465 return fOtherTrace;
466 }
467
468 public boolean isTraceSynced(String name) {
469 /* Returns true if the timestamp transform is not identity */
470 return (name.equals(fOtherTrace) && (fQuality == SyncQuality.ACCURATE || fQuality == SyncQuality.APPROXIMATE || fQuality == SyncQuality.FAIL));
471 }
472
473 public boolean isForTraces(String trace1, String trace2) {
474 return ((fReferenceTrace.equals(trace1) && fOtherTrace.equals(trace2)) || (fReferenceTrace.equals(trace2) && fOtherTrace.equals(trace1)));
475 }
476
477 public void renameTrace(String oldname, String newname) {
478 if (oldname.equals(fOtherTrace)) {
479 fOtherTrace = newname;
480 } else if (oldname.equals(fReferenceTrace)) {
481 fReferenceTrace = newname;
482 }
483 }
484
485 private void writeObject(ObjectOutputStream s)
486 throws IOException {
487 /*
488 * Remove calculation data because most of it is not serializable.
489 * We have the statistics anyway
490 */
491 fUpperBoundList.clear();
492 fLowerBoundList.clear();
493 fLmin[0] = null;
494 fLmin[1] = null;
495 fLmax[0] = null;
496 fLmax[1] = null;
497 s.defaultWriteObject();
498
499 }
500
501 @SuppressWarnings("nls")
502 @Override
503 public String toString() {
504 StringBuilder b = new StringBuilder();
505 b.append("Between " + fReferenceTrace + " and " + fOtherTrace + " [");
506 b.append(" alpha " + fAlpha + " beta " + fBeta + " ]");
507 return b.toString();
508 }
509 }
510
511 /**
512 * Private class representing a point to synchronize on a graph. The x axis
513 * is the timestamp of the event from the reference trace while the y axis
514 * is the timestamp of the event on the other trace
515 */
516 private class SyncPoint {
517 private final ITmfTimestamp x, y;
518
519 public SyncPoint(ITmfEvent ex, ITmfEvent ey) {
520 x = ex.getTimestamp();
521 y = ey.getTimestamp();
522 }
523
524 public long getTimeX() {
525 return x.getValue();
526 }
527
528 /**
529 * Calculate a cross product of 3 points:
530 *
531 * If the cross-product < 0, then p, pa, pb are clockwise
532 *
533 * If the cross-product > 0, then p, pa, pb are counter-clockwise
534 *
535 * If cross-product == 0, then they are in a line
536 *
537 * @param pa
538 * First point
539 * @param pb
540 * Second point
541 * @return The cross product
542 */
543 public long crossProduct(SyncPoint pa, SyncPoint pb) {
544 long cp = ((pa.x.getValue() - x.getValue()) * (pb.y.getValue() - y.getValue()) - (pa.y.getValue() - y.getValue()) * (pb.x.getValue() - x.getValue()));
545 return cp;
546 }
547
548 /*
549 * Gets the alpha (slope) between two points
550 */
551 public BigDecimal getAlpha(SyncPoint p1) {
552 if (p1 == null) {
553 return BigDecimal.ONE;
554 }
555 BigDecimal deltay = BigDecimal.valueOf(y.getValue() - p1.y.getValue());
556 BigDecimal deltax = BigDecimal.valueOf(x.getValue() - p1.x.getValue());
557 if (deltax.equals(BigDecimal.ZERO)) {
558 return BigDecimal.ONE;
559 }
560 return deltay.divide(deltax, fMc);
561 }
562
563 /*
564 * Get the beta value (when x = 0) of the line given alpha
565 */
566 public BigDecimal getBeta(BigDecimal alpha) {
567 return BigDecimal.valueOf(y.getValue()).subtract(alpha.multiply(BigDecimal.valueOf(x.getValue()), fMc));
568 }
569
570 }
571
572 }
This page took 0.046397 seconds and 5 git commands to generate.