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