Perform trace factor reduction as a separate step
[lttv.git] / lttv / lttv / sync / event_analysis_chull.c
1 /* This file is part of the Linux Trace Toolkit viewer
2 * Copyright (C) 2009, 2010 Benjamin Poirier <benjamin.poirier@polymtl.ca>
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation, either version 2.1 of the License, or (at
7 * your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
12 * License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17 #define _ISOC99_SOURCE
18
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22
23 #include <errno.h>
24 #include <inttypes.h>
25 #include <math.h>
26 #include <float.h>
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <unistd.h>
31
32 #include "sync_chain.h"
33
34 #include "event_analysis_chull.h"
35
36
37 typedef enum
38 {
39 LOWER,
40 UPPER
41 } HullType;
42
43
44 typedef enum
45 {
46 MINIMUM,
47 MAXIMUM
48 } LineType;
49
50
51 // Functions common to all analysis modules
52 static void initAnalysisCHull(SyncState* const syncState);
53 static void destroyAnalysisCHull(SyncState* const syncState);
54
55 static void analyzeMessageCHull(SyncState* const syncState, Message* const
56 message);
57 static AllFactors* finalizeAnalysisCHull(SyncState* const syncState);
58 static void printAnalysisStatsCHull(SyncState* const syncState);
59 static void writeAnalysisGraphsPlotsCHull(SyncState* const syncState, const
60 unsigned int i, const unsigned int j);
61
62 // Functions specific to this module
63 static void openGraphFiles(SyncState* const syncState);
64 static void closeGraphFiles(SyncState* const syncState);
65 static void writeGraphFiles(SyncState* const syncState);
66 static void gfDumpHullToFile(gpointer data, gpointer userData);
67
68 static void grahamScan(GQueue* const hull, Point* const newPoint, const
69 HullType type);
70 static int jointCmp(const Point* const p1, const Point* const p2, const Point*
71 const p3) __attribute__((pure));
72 static double crossProductK(const Point const* p1, const Point const* p2,
73 const Point const* p3, const Point const* p4) __attribute__((pure));
74 static Factors* calculateFactorsExact(GQueue* const cu, GQueue* const cl, const
75 LineType lineType) __attribute__((pure));
76 static void calculateFactorsFallback(GQueue* const cr, GQueue* const cs,
77 PairFactors* const result);
78 static double slope(const Point* const p1, const Point* const p2)
79 __attribute__((pure));
80 static double intercept(const Point* const p1, const Point* const p2)
81 __attribute__((pure));
82 static double verticalDistance(Point* p1, Point* p2, Point* const point)
83 __attribute__((pure));
84
85 static void gfPointDestroy(gpointer data, gpointer userData);
86
87
88 static AnalysisModule analysisModuleCHull= {
89 .name= "chull",
90 .initAnalysis= &initAnalysisCHull,
91 .destroyAnalysis= &destroyAnalysisCHull,
92 .analyzeMessage= &analyzeMessageCHull,
93 .finalizeAnalysis= &finalizeAnalysisCHull,
94 .printAnalysisStats= &printAnalysisStatsCHull,
95 .graphFunctions= {
96 .writeTraceTraceForePlots= &writeAnalysisGraphsPlotsCHull,
97 }
98 };
99
100
101 /*
102 * Analysis module registering function
103 */
104 void registerAnalysisCHull()
105 {
106 g_queue_push_tail(&analysisModules, &analysisModuleCHull);
107 }
108
109
110 /*
111 * Analysis init function
112 *
113 * This function is called at the beginning of a synchronization run for a set
114 * of traces.
115 *
116 * Allocate some of the analysis specific data structures
117 *
118 * Args:
119 * syncState container for synchronization data.
120 * This function allocates or initializes these analysisData
121 * members:
122 * hullArray
123 * dropped
124 */
125 static void initAnalysisCHull(SyncState* const syncState)
126 {
127 unsigned int i, j;
128 AnalysisDataCHull* analysisData;
129
130 analysisData= malloc(sizeof(AnalysisDataCHull));
131 syncState->analysisData= analysisData;
132
133 analysisData->hullArray= malloc(syncState->traceNb * sizeof(GQueue**));
134 for (i= 0; i < syncState->traceNb; i++)
135 {
136 analysisData->hullArray[i]= malloc(syncState->traceNb * sizeof(GQueue*));
137
138 for (j= 0; j < syncState->traceNb; j++)
139 {
140 analysisData->hullArray[i][j]= g_queue_new();
141 }
142 }
143
144 if (syncState->stats)
145 {
146 analysisData->stats= malloc(sizeof(AnalysisStatsCHull));
147 analysisData->stats->dropped= 0;
148 analysisData->stats->allFactors= NULL;
149 }
150
151 if (syncState->graphsStream)
152 {
153 analysisData->graphsData= malloc(sizeof(AnalysisGraphsDataCHull));
154 openGraphFiles(syncState);
155 analysisData->graphsData->allFactors= NULL;
156 }
157 }
158
159
160 /*
161 * Create and open files used to store convex hull points to genereate
162 * graphs. Allocate and populate array to store file pointers.
163 *
164 * Args:
165 * syncState: container for synchronization data
166 */
167 static void openGraphFiles(SyncState* const syncState)
168 {
169 unsigned int i, j;
170 int retval;
171 char* cwd;
172 char name[31];
173 AnalysisDataCHull* analysisData;
174
175 analysisData= (AnalysisDataCHull*) syncState->analysisData;
176
177 cwd= changeToGraphsDir(syncState->graphsDir);
178
179 analysisData->graphsData->hullPoints= malloc(syncState->traceNb *
180 sizeof(FILE**));
181 for (i= 0; i < syncState->traceNb; i++)
182 {
183 analysisData->graphsData->hullPoints[i]= malloc(syncState->traceNb *
184 sizeof(FILE*));
185 for (j= 0; j < syncState->traceNb; j++)
186 {
187 if (i != j)
188 {
189 retval= snprintf(name, sizeof(name),
190 "analysis_chull-%03u_to_%03u.data", j, i);
191 if (retval > sizeof(name) - 1)
192 {
193 name[sizeof(name) - 1]= '\0';
194 }
195 if ((analysisData->graphsData->hullPoints[i][j]= fopen(name, "w")) ==
196 NULL)
197 {
198 g_error(strerror(errno));
199 }
200 }
201 }
202 }
203
204 retval= chdir(cwd);
205 if (retval == -1)
206 {
207 g_error(strerror(errno));
208 }
209 free(cwd);
210 }
211
212
213 /*
214 * Write hull points to files to generate graphs.
215 *
216 * Args:
217 * syncState: container for synchronization data
218 */
219 static void writeGraphFiles(SyncState* const syncState)
220 {
221 unsigned int i, j;
222 AnalysisDataCHull* analysisData;
223
224 analysisData= (AnalysisDataCHull*) syncState->analysisData;
225
226 for (i= 0; i < syncState->traceNb; i++)
227 {
228 for (j= 0; j < syncState->traceNb; j++)
229 {
230 if (i != j)
231 {
232 g_queue_foreach(analysisData->hullArray[i][j],
233 &gfDumpHullToFile,
234 analysisData->graphsData->hullPoints[i][j]);
235 }
236 }
237 }
238 }
239
240
241 /*
242 * A GFunc for g_queue_foreach. Write a hull point to a file used to generate
243 * graphs
244 *
245 * Args:
246 * data: Point*, point to write to the file
247 * userData: FILE*, file pointer where to write the point
248 */
249 static void gfDumpHullToFile(gpointer data, gpointer userData)
250 {
251 Point* point;
252
253 point= (Point*) data;
254 fprintf((FILE*) userData, "%20" PRIu64 " %20" PRIu64 "\n", point->x, point->y);
255 }
256
257
258 /*
259 * Close files used to store convex hull points to generate graphs.
260 * Deallocate array to store file pointers.
261 *
262 * Args:
263 * syncState: container for synchronization data
264 */
265 static void closeGraphFiles(SyncState* const syncState)
266 {
267 unsigned int i, j;
268 AnalysisDataCHull* analysisData;
269 int retval;
270
271 analysisData= (AnalysisDataCHull*) syncState->analysisData;
272
273 if (analysisData->graphsData->hullPoints == NULL)
274 {
275 return;
276 }
277
278 for (i= 0; i < syncState->traceNb; i++)
279 {
280 for (j= 0; j < syncState->traceNb; j++)
281 {
282 if (i != j)
283 {
284 retval= fclose(analysisData->graphsData->hullPoints[i][j]);
285 if (retval != 0)
286 {
287 g_error(strerror(errno));
288 }
289 }
290 }
291 free(analysisData->graphsData->hullPoints[i]);
292 }
293 free(analysisData->graphsData->hullPoints);
294 analysisData->graphsData->hullPoints= NULL;
295 }
296
297
298 /*
299 * Analysis destroy function
300 *
301 * Free the analysis specific data structures
302 *
303 * Args:
304 * syncState container for synchronization data.
305 * This function deallocates these analysisData members:
306 * hullArray
307 * stDev
308 */
309 static void destroyAnalysisCHull(SyncState* const syncState)
310 {
311 unsigned int i, j;
312 AnalysisDataCHull* analysisData;
313
314 analysisData= (AnalysisDataCHull*) syncState->analysisData;
315
316 if (analysisData == NULL)
317 {
318 return;
319 }
320
321 for (i= 0; i < syncState->traceNb; i++)
322 {
323 for (j= 0; j < syncState->traceNb; j++)
324 {
325 g_queue_foreach(analysisData->hullArray[i][j], gfPointDestroy, NULL);
326 g_queue_free(analysisData->hullArray[i][j]);
327 }
328 free(analysisData->hullArray[i]);
329 }
330 free(analysisData->hullArray);
331
332 if (syncState->stats)
333 {
334 freeAllFactors(analysisData->stats->allFactors);
335
336 free(analysisData->stats);
337 }
338
339 if (syncState->graphsStream)
340 {
341 if (analysisData->graphsData->hullPoints != NULL)
342 {
343 closeGraphFiles(syncState);
344 }
345
346 freeAllFactors(analysisData->graphsData->allFactors);
347
348 free(analysisData->graphsData);
349 }
350
351 free(syncState->analysisData);
352 syncState->analysisData= NULL;
353 }
354
355
356 /*
357 * Perform analysis on an event pair.
358 *
359 * Args:
360 * syncState container for synchronization data
361 * message structure containing the events
362 */
363 static void analyzeMessageCHull(SyncState* const syncState, Message* const message)
364 {
365 AnalysisDataCHull* analysisData;
366 Point* newPoint;
367 HullType hullType;
368 GQueue* hull;
369
370 analysisData= (AnalysisDataCHull*) syncState->analysisData;
371
372 newPoint= malloc(sizeof(Point));
373 if (message->inE->traceNum < message->outE->traceNum)
374 {
375 // CA is inE->traceNum
376 newPoint->x= message->inE->cpuTime;
377 newPoint->y= message->outE->cpuTime;
378 hullType= UPPER;
379 g_debug("Reception point hullArray[%lu][%lu] "
380 "x= inE->time= %" PRIu64 " y= outE->time= %" PRIu64,
381 message->inE->traceNum, message->outE->traceNum, newPoint->x,
382 newPoint->y);
383 }
384 else
385 {
386 // CA is outE->traceNum
387 newPoint->x= message->outE->cpuTime;
388 newPoint->y= message->inE->cpuTime;
389 hullType= LOWER;
390 g_debug("Send point hullArray[%lu][%lu] "
391 "x= inE->time= %" PRIu64 " y= outE->time= %" PRIu64,
392 message->inE->traceNum, message->outE->traceNum, newPoint->x,
393 newPoint->y);
394 }
395
396 hull=
397 analysisData->hullArray[message->inE->traceNum][message->outE->traceNum];
398
399 if (hull->length >= 1 && newPoint->x < ((Point*)
400 g_queue_peek_tail(hull))->x)
401 {
402 if (syncState->stats)
403 {
404 analysisData->stats->dropped++;
405 }
406
407 free(newPoint);
408 }
409 else
410 {
411 grahamScan(hull, newPoint, hullType);
412 }
413 }
414
415
416 /*
417 * Construct one half of a convex hull from abscissa-sorted points
418 *
419 * Args:
420 * hull: the points already in the hull
421 * newPoint: a new point to consider
422 * type: which half of the hull to construct
423 */
424 static void grahamScan(GQueue* const hull, Point* const newPoint, const
425 HullType type)
426 {
427 int inversionFactor;
428
429 g_debug("grahamScan(hull (length: %u), newPoint, %s)", hull->length, type
430 == LOWER ? "LOWER" : "UPPER");
431
432 if (type == LOWER)
433 {
434 inversionFactor= 1;
435 }
436 else
437 {
438 inversionFactor= -1;
439 }
440
441 if (hull->length >= 2)
442 {
443 g_debug("jointCmp(hull[%u], hull[%u], newPoint) * inversionFactor = %d * %d = %d",
444 hull->length - 2,
445 hull->length - 1,
446 jointCmp(g_queue_peek_nth(hull, hull->length - 2),
447 g_queue_peek_tail(hull), newPoint),
448 inversionFactor,
449 jointCmp(g_queue_peek_nth(hull, hull->length - 2),
450 g_queue_peek_tail(hull), newPoint) * inversionFactor);
451 }
452 while (hull->length >= 2 && jointCmp(g_queue_peek_nth(hull, hull->length -
453 2), g_queue_peek_tail(hull), newPoint) * inversionFactor <= 0)
454 {
455 g_debug("Removing hull[%u]", hull->length);
456 free((Point*) g_queue_pop_tail(hull));
457
458 if (hull->length >= 2)
459 {
460 g_debug("jointCmp(hull[%u], hull[%u], newPoint) * inversionFactor = %d * %d = %d",
461 hull->length - 2,
462 hull->length - 1,
463 jointCmp(g_queue_peek_nth(hull, hull->length - 2),
464 g_queue_peek_tail(hull), newPoint),
465 inversionFactor,
466 jointCmp(g_queue_peek_nth(hull, hull->length - 2),
467 g_queue_peek_tail(hull), newPoint) * inversionFactor);
468 }
469 }
470 g_queue_push_tail(hull, newPoint);
471 }
472
473
474 /*
475 * Finalize the factor calculations
476 *
477 * Args:
478 * syncState container for synchronization data.
479 *
480 * Returns:
481 * AllFactors* synchronization factors for each trace pair, the caller is
482 * responsible for freeing the structure
483 */
484 static AllFactors* finalizeAnalysisCHull(SyncState* const syncState)
485 {
486 AnalysisDataCHull* analysisData;
487 AllFactors* allFactors;
488
489 analysisData= (AnalysisDataCHull*) syncState->analysisData;
490
491 if (syncState->graphsStream && analysisData->graphsData->hullPoints != NULL)
492 {
493 writeGraphFiles(syncState);
494 closeGraphFiles(syncState);
495 }
496
497 allFactors= calculateAllFactors(syncState);
498
499 if (syncState->stats)
500 {
501 allFactors->refCount++;
502 analysisData->stats->allFactors= allFactors;
503 }
504
505 if (syncState->graphsStream)
506 {
507 allFactors->refCount++;
508 analysisData->graphsData->allFactors= allFactors;
509 }
510
511 return allFactors;
512 }
513
514
515 /*
516 * Print statistics related to analysis. Must be called after
517 * finalizeAnalysis.
518 *
519 * Args:
520 * syncState container for synchronization data.
521 */
522 static void printAnalysisStatsCHull(SyncState* const syncState)
523 {
524 AnalysisDataCHull* analysisData;
525 unsigned int i, j;
526
527 if (!syncState->stats)
528 {
529 return;
530 }
531
532 analysisData= (AnalysisDataCHull*) syncState->analysisData;
533
534 printf("Convex hull analysis stats:\n");
535 printf("\tout of order packets dropped from analysis: %u\n",
536 analysisData->stats->dropped);
537
538 printf("\tNumber of points in convex hulls:\n");
539
540 for (i= 0; i < syncState->traceNb; i++)
541 {
542 for (j= i + 1; j < syncState->traceNb; j++)
543 {
544 printf("\t\t%3d - %-3d: lower half-hull %-5u upper half-hull %-5u\n",
545 i, j, analysisData->hullArray[j][i]->length,
546 analysisData->hullArray[i][j]->length);
547 }
548 }
549
550 printf("\tIndividual synchronization factors:\n");
551
552 for (i= 0; i < syncState->traceNb; i++)
553 {
554 for (j= i + 1; j < syncState->traceNb; j++)
555 {
556 PairFactors* factorsCHull;
557
558 factorsCHull= &analysisData->stats->allFactors->pairFactors[j][i];
559 printf("\t\t%3d - %-3d: %s", i, j,
560 approxNames[factorsCHull->type]);
561
562 if (factorsCHull->type == EXACT)
563 {
564 printf(" a0= % 7g a1= 1 %c %7g\n",
565 factorsCHull->approx->offset,
566 factorsCHull->approx->drift < 0. ? '-' : '+',
567 fabs(factorsCHull->approx->drift));
568 }
569 else if (factorsCHull->type == ACCURATE)
570 {
571 printf("\n\t\t a0: % 7g to % 7g (delta= %7g)\n",
572 factorsCHull->max->offset, factorsCHull->min->offset,
573 factorsCHull->min->offset - factorsCHull->max->offset);
574 printf("\t\t a1: 1 %+7g to %+7g (delta= %7g)\n",
575 factorsCHull->min->drift - 1., factorsCHull->max->drift -
576 1., factorsCHull->max->drift - factorsCHull->min->drift);
577 }
578 else if (factorsCHull->type == APPROXIMATE)
579 {
580 printf(" a0= % 7g a1= 1 %c %7g error= %7g\n",
581 factorsCHull->approx->offset, factorsCHull->approx->drift
582 - 1. < 0. ? '-' : '+', fabs(factorsCHull->approx->drift -
583 1.), factorsCHull->accuracy);
584 }
585 else if (factorsCHull->type == INCOMPLETE)
586 {
587 printf("\n");
588
589 if (factorsCHull->min->drift != -INFINITY)
590 {
591 printf("\t\t min: a0: % 7g a1: 1 %c %7g\n",
592 factorsCHull->min->offset, factorsCHull->min->drift -
593 1. < 0 ? '-' : '+', fabs(factorsCHull->min->drift -
594 1.));
595 }
596 if (factorsCHull->max->drift != INFINITY)
597 {
598 printf("\t\t max: a0: % 7g a1: 1 %c %7g\n",
599 factorsCHull->max->offset, factorsCHull->max->drift -
600 1. < 0 ? '-' : '+', fabs(factorsCHull->max->drift -
601 1.));
602 }
603 }
604 else if (factorsCHull->type == SCREWED)
605 {
606 printf("\n");
607
608 if (factorsCHull->min != NULL && factorsCHull->min->drift != -INFINITY)
609 {
610 printf("\t\t min: a0: % 7g a1: 1 %c %7g\n",
611 factorsCHull->min->offset, factorsCHull->min->drift -
612 1. < 0 ? '-' : '+', fabs(factorsCHull->min->drift -
613 1.));
614 }
615 if (factorsCHull->max != NULL && factorsCHull->max->drift != INFINITY)
616 {
617 printf("\t\t max: a0: % 7g a1: 1 %c %7g\n",
618 factorsCHull->max->offset, factorsCHull->max->drift -
619 1. < 0 ? '-' : '+', fabs(factorsCHull->max->drift -
620 1.));
621 }
622 }
623 else if (factorsCHull->type == ABSENT)
624 {
625 printf("\n");
626 }
627 else
628 {
629 g_assert_not_reached();
630 }
631 }
632 }
633 }
634
635
636 /*
637 * A GFunc for g_queue_foreach()
638 *
639 * Args:
640 * data Point*, point to destroy
641 * user_data NULL
642 */
643 static void gfPointDestroy(gpointer data, gpointer userData)
644 {
645 Point* point;
646
647 point= (Point*) data;
648 free(point);
649 }
650
651
652 /*
653 * Find out if a sequence of three points constitutes a "left turn" or a
654 * "right turn".
655 *
656 * Args:
657 * p1, p2, p3: The three points.
658 *
659 * Returns:
660 * < 0 right turn
661 * 0 colinear (unlikely result since this uses floating point
662 * arithmetic)
663 * > 0 left turn
664 */
665 static int jointCmp(const Point const* p1, const Point const* p2, const
666 Point const* p3)
667 {
668 double result;
669 const double fuzzFactor= 0.;
670
671 result= crossProductK(p1, p2, p1, p3);
672 g_debug("crossProductK(p1= (%" PRIu64 ", %" PRIu64 "), "
673 "p2= (%" PRIu64 ", %" PRIu64 "), p1= (%" PRIu64 ", %" PRIu64 "), "
674 "p3= (%" PRIu64 ", %" PRIu64 "))= %g",
675 p1->x, p1->y, p2->x, p2->y, p1->x, p1->y, p3->x, p3->y, result);
676 if (result < fuzzFactor)
677 {
678 return -1;
679 }
680 else if (result > fuzzFactor)
681 {
682 return 1;
683 }
684 else
685 {
686 return 0;
687 }
688 }
689
690
691 /*
692 * Calculate the k component of the cross product of two vectors.
693 *
694 * Args:
695 * p1, p2: start and end points of the first vector
696 * p3, p4: start and end points of the second vector
697 *
698 * Returns:
699 * the k component of the cross product when considering the two vectors to
700 * be in the i-j plane. The direction (sign) of the result can be useful to
701 * determine the relative orientation of the two vectors.
702 */
703 static double crossProductK(const Point const* p1, const Point const* p2,
704 const Point const* p3, const Point const* p4)
705 {
706 return ((double) p2->x - p1->x) * ((double) p4->y - p3->y) - ((double)
707 p2->y - p1->y) * ((double) p4->x - p3->x);
708 }
709
710
711 /*
712 * Analyze the convex hulls to determine the synchronization factors between
713 * each pair of trace.
714 *
715 * Args:
716 * syncState container for synchronization data.
717 *
718 * Returns:
719 * AllFactors*, see the documentation for the member allFactors of
720 * AnalysisStatsCHull.
721 */
722 AllFactors* calculateAllFactors(SyncState* const syncState)
723 {
724 unsigned int traceNumA, traceNumB;
725 AllFactors* allFactors;
726 AnalysisDataCHull* analysisData;
727
728 analysisData= (AnalysisDataCHull*) syncState->analysisData;
729
730 // Allocate allFactors and calculate min and max
731 allFactors= createAllFactors(syncState->traceNb);
732 for (traceNumA= 0; traceNumA < syncState->traceNb; traceNumA++)
733 {
734 for (traceNumB= 0; traceNumB < traceNumA; traceNumB++)
735 {
736 unsigned int i;
737 GQueue* cs, * cr;
738 const struct
739 {
740 LineType lineType;
741 size_t factorsOffset;
742 } loopValues[]= {
743 {MINIMUM, offsetof(PairFactors, min)},
744 {MAXIMUM, offsetof(PairFactors, max)}
745 };
746
747 cr= analysisData->hullArray[traceNumB][traceNumA];
748 cs= analysisData->hullArray[traceNumA][traceNumB];
749
750 for (i= 0; i < sizeof(loopValues) / sizeof(*loopValues); i++)
751 {
752 g_debug("allFactors[%u][%u].%s = calculateFactorsExact(cr= "
753 "hullArray[%u][%u], cs= hullArray[%u][%u], %s)",
754 traceNumA, traceNumB, loopValues[i].factorsOffset ==
755 offsetof(PairFactors, min) ? "min" : "max", traceNumB,
756 traceNumA, traceNumA, traceNumB, loopValues[i].lineType ==
757 MINIMUM ? "MINIMUM" : "MAXIMUM");
758 *((Factors**) ((void*)
759 &allFactors->pairFactors[traceNumA][traceNumB] +
760 loopValues[i].factorsOffset))=
761 calculateFactorsExact(cr, cs, loopValues[i].lineType);
762 }
763 }
764 }
765
766 // Calculate approx when possible
767 for (traceNumA= 0; traceNumA < syncState->traceNb; traceNumA++)
768 {
769 for (traceNumB= 0; traceNumB < traceNumA; traceNumB++)
770 {
771 PairFactors* factorsCHull;
772
773 factorsCHull= &allFactors->pairFactors[traceNumA][traceNumB];
774 if (factorsCHull->min == NULL && factorsCHull->max == NULL)
775 {
776 factorsCHull->type= APPROXIMATE;
777 calculateFactorsFallback(analysisData->hullArray[traceNumB][traceNumA],
778 analysisData->hullArray[traceNumA][traceNumB],
779 &allFactors->pairFactors[traceNumA][traceNumB]);
780 }
781 else if (factorsCHull->min != NULL && factorsCHull->max != NULL)
782 {
783 if (factorsCHull->min->drift != -INFINITY &&
784 factorsCHull->max->drift != INFINITY)
785 {
786 factorsCHull->type= ACCURATE;
787 calculateFactorsMiddle(factorsCHull);
788 }
789 else if (factorsCHull->min->drift != -INFINITY ||
790 factorsCHull->max->drift != INFINITY)
791 {
792 factorsCHull->type= INCOMPLETE;
793 }
794 else
795 {
796 factorsCHull->type= ABSENT;
797 }
798 }
799 else
800 {
801 //g_assert_not_reached();
802 factorsCHull->type= SCREWED;
803 }
804 }
805 }
806
807 return allFactors;
808 }
809
810
811 /* Calculate approximative factors based on minimum and maximum limits. The
812 * best approximation to make is the interior bissector of the angle formed by
813 * the minimum and maximum lines.
814 *
815 * The formulae used come from [Haddad, Yoram: Performance dans les systèmes
816 * répartis: des outils pour les mesures, Université de Paris-Sud, Centre
817 * d'Orsay, September 1988] Section 6.1 p.44
818 *
819 * The reasoning for choosing this estimator comes from [Duda, A., Harrus, G.,
820 * Haddad, Y., and Bernard, G.: Estimating global time in distributed systems,
821 * Proc. 7th Int. Conf. on Distributed Computing Systems, Berlin, volume 18,
822 * 1987] p.303
823 *
824 * Args:
825 * factors: contains the min and max limits, used to store the result
826 */
827 void calculateFactorsMiddle(PairFactors* const factors)
828 {
829 double amin, amax, bmin, bmax, bhat;
830
831 amin= factors->max->offset;
832 amax= factors->min->offset;
833 bmin= factors->min->drift;
834 bmax= factors->max->drift;
835
836 g_assert_cmpfloat(bmax, >=, bmin);
837
838 factors->approx= malloc(sizeof(Factors));
839 bhat= (bmax * bmin - 1. + sqrt(1. + pow(bmax, 2.) * pow(bmin, 2.) +
840 pow(bmax, 2.) + pow(bmin, 2.))) / (bmax + bmin);
841 factors->approx->offset= amax - (amax - amin) / 2. * (pow(bhat, 2.) + 1.)
842 / (1. + bhat * bmax);
843 factors->approx->drift= bhat;
844 factors->accuracy= bmax - bmin;
845 }
846
847
848 /*
849 * Analyze the convex hulls to determine the minimum or maximum
850 * synchronization factors between one pair of trace.
851 *
852 * This implements and improves upon the algorithm in [Haddad, Yoram:
853 * Performance dans les systèmes répartis: des outils pour les mesures,
854 * Université de Paris-Sud, Centre d'Orsay, September 1988] Section 6.2 p.47
855 *
856 * Some degenerate cases are possible:
857 * 1) the result is unbounded. In that case, when searching for the maximum
858 * factors, result->drift= INFINITY; result->offset= -INFINITY. When
859 * searching for the minimum factors, it is the opposite. It is not
860 * possible to improve the situation with this data.
861 * 2) no line can be above the upper hull and below the lower hull. This is
862 * because the hulls intersect each other or are reversed. This means that
863 * an assertion was false. Most probably, the clocks are not linear. It is
864 * possible to repeat the search with another algorithm that will find a
865 * "best effort" approximation. See calculateFactorsApprox().
866 *
867 * Args:
868 * cu: the upper half-convex hull, the line must pass above this
869 * and touch it in one point
870 * cl: the lower half-convex hull, the line must pass below this
871 * and touch it in one point
872 * lineType: search for minimum or maximum factors
873 *
874 * Returns:
875 * If a result is found, a struct Factors is allocated, filed with the
876 * result and returned
877 * NULL otherwise, degenerate case 2 is in effect
878 */
879 static Factors* calculateFactorsExact(GQueue* const cu, GQueue* const cl, const
880 LineType lineType)
881 {
882 GQueue* c1, * c2;
883 unsigned int i1, i2;
884 Point* p1, * p2;
885 double inversionFactor;
886 Factors* result;
887
888 g_debug("calculateFactorsExact(cu= %p, cl= %p, %s)", cu, cl, lineType ==
889 MINIMUM ? "MINIMUM" : "MAXIMUM");
890
891 if (lineType == MINIMUM)
892 {
893 c1= cl;
894 c2= cu;
895 inversionFactor= -1.;
896 }
897 else
898 {
899 c1= cu;
900 c2= cl;
901 inversionFactor= 1.;
902 }
903
904 i1= 0;
905 i2= c2->length - 1;
906
907 // Check for degenerate case 1
908 if (c1->length == 0 || c2->length == 0 || ((Point*) g_queue_peek_nth(c1,
909 i1))->x >= ((Point*) g_queue_peek_nth(c2, i2))->x)
910 {
911 result= malloc(sizeof(Factors));
912 if (lineType == MINIMUM)
913 {
914 result->drift= -INFINITY;
915 result->offset= INFINITY;
916 }
917 else
918 {
919 result->drift= INFINITY;
920 result->offset= -INFINITY;
921 }
922
923 return result;
924 }
925
926 do
927 {
928 while
929 (
930 (int) i2 - 1 > 0
931 && crossProductK(
932 g_queue_peek_nth(c1, i1),
933 g_queue_peek_nth(c2, i2),
934 g_queue_peek_nth(c1, i1),
935 g_queue_peek_nth(c2, i2 - 1)) * inversionFactor < 0.
936 )
937 {
938 if (((Point*) g_queue_peek_nth(c1, i1))->x
939 < ((Point*) g_queue_peek_nth(c2, i2 - 1))->x)
940 {
941 i2--;
942 }
943 else
944 {
945 // Degenerate case 2
946 return NULL;
947 }
948 }
949 while
950 (
951 i1 + 1 < c1->length - 1
952 && crossProductK(
953 g_queue_peek_nth(c1, i1),
954 g_queue_peek_nth(c2, i2),
955 g_queue_peek_nth(c1, i1 + 1),
956 g_queue_peek_nth(c2, i2)) * inversionFactor < 0.
957 )
958 {
959 if (((Point*) g_queue_peek_nth(c1, i1 + 1))->x
960 < ((Point*) g_queue_peek_nth(c2, i2))->x)
961 {
962 i1++;
963 }
964 else
965 {
966 // Degenerate case 2
967 return NULL;
968 }
969 }
970 } while
971 (
972 (int) i2 - 1 > 0
973 && crossProductK(
974 g_queue_peek_nth(c1, i1),
975 g_queue_peek_nth(c2, i2),
976 g_queue_peek_nth(c1, i1),
977 g_queue_peek_nth(c2, i2 - 1)) * inversionFactor < 0.
978 );
979
980 p1= g_queue_peek_nth(c1, i1);
981 p2= g_queue_peek_nth(c2, i2);
982
983 g_debug("Resulting points are: c1[i1]: x= %" PRIu64 " y= %" PRIu64
984 " c2[i2]: x= %" PRIu64 " y= %" PRIu64 "", p1->x, p1->y, p2->x, p2->y);
985
986 result= malloc(sizeof(Factors));
987 result->drift= slope(p1, p2);
988 result->offset= intercept(p1, p2);
989
990 g_debug("Resulting factors are: drift= %g offset= %g", result->drift,
991 result->offset);
992
993 return result;
994 }
995
996
997 /*
998 * Analyze the convex hulls to determine approximate synchronization factors
999 * between one pair of trace when there is no line that can fit in the
1000 * corridor separating them.
1001 *
1002 * This implements the algorithm in [Ashton, P.: Algorithms for Off-line Clock
1003 * Synchronisation, University of Canterbury, December 1995, 26] Section 4.2.2
1004 * p.7
1005 *
1006 * For each point p1 in cr
1007 * For each point p2 in cs
1008 * errorMin= 0
1009 * Calculate the line paramaters
1010 * For each point p3 in each convex hull
1011 * If p3 is on the wrong side of the line
1012 * error+= distance
1013 * If error < errorMin
1014 * Update results
1015 *
1016 * Args:
1017 * cr: the upper half-convex hull
1018 * cs: the lower half-convex hull
1019 * result: a pointer to the pre-allocated struct where the results
1020 * will be stored
1021 */
1022 static void calculateFactorsFallback(GQueue* const cr, GQueue* const cs,
1023 PairFactors* const result)
1024 {
1025 unsigned int i, j, k;
1026 double errorMin;
1027 Factors* approx;
1028
1029 errorMin= INFINITY;
1030 approx= malloc(sizeof(Factors));
1031
1032 for (i= 0; i < cs->length; i++)
1033 {
1034 for (j= 0; j < cr->length; j++)
1035 {
1036 double error;
1037 Point p1, p2;
1038
1039 error= 0.;
1040
1041 if (((Point*) g_queue_peek_nth(cs, i))->x < ((Point*)g_queue_peek_nth(cr, j))->x)
1042 {
1043 p1= *(Point*)g_queue_peek_nth(cs, i);
1044 p2= *(Point*)g_queue_peek_nth(cr, j);
1045 }
1046 else
1047 {
1048 p1= *(Point*)g_queue_peek_nth(cr, j);
1049 p2= *(Point*)g_queue_peek_nth(cs, i);
1050 }
1051
1052 // The lower hull should be above the point
1053 for (k= 0; k < cs->length; k++)
1054 {
1055 if (jointCmp(&p1, &p2, g_queue_peek_nth(cs, k)) < 0.)
1056 {
1057 error+= verticalDistance(&p1, &p2, g_queue_peek_nth(cs, k));
1058 }
1059 }
1060
1061 // The upper hull should be below the point
1062 for (k= 0; k < cr->length; k++)
1063 {
1064 if (jointCmp(&p1, &p2, g_queue_peek_nth(cr, k)) > 0.)
1065 {
1066 error+= verticalDistance(&p1, &p2, g_queue_peek_nth(cr, k));
1067 }
1068 }
1069
1070 if (error < errorMin)
1071 {
1072 g_debug("Fallback: i= %u j= %u is a better match (error= %g)", i, j, error);
1073 approx->drift= slope(&p1, &p2);
1074 approx->offset= intercept(&p1, &p2);
1075 errorMin= error;
1076 }
1077 }
1078 }
1079
1080 result->approx= approx;
1081 result->accuracy= errorMin;
1082 }
1083
1084
1085 /*
1086 * Calculate the vertical distance between a line and a point
1087 *
1088 * Args:
1089 * p1, p2: Two points defining the line
1090 * point: a point
1091 *
1092 * Return:
1093 * the vertical distance
1094 */
1095 static double verticalDistance(Point* p1, Point* p2, Point* const point)
1096 {
1097 return fabs(slope(p1, p2) * point->x + intercept(p1, p2) - point->y);
1098 }
1099
1100
1101 /*
1102 * Calculate the slope between two points
1103 *
1104 * Args:
1105 * p1, p2 the two points
1106 *
1107 * Returns:
1108 * the slope
1109 */
1110 static double slope(const Point* const p1, const Point* const p2)
1111 {
1112 return ((double) p2->y - p1->y) / (p2->x - p1->x);
1113 }
1114
1115
1116 /* Calculate the y-intercept of a line that passes by two points
1117 *
1118 * Args:
1119 * p1, p2 the two points
1120 *
1121 * Returns:
1122 * the y-intercept
1123 */
1124 static double intercept(const Point* const p1, const Point* const p2)
1125 {
1126 return ((double) p2->y * p1->x - (double) p1->y * p2->x) / ((double) p1->x - p2->x);
1127 }
1128
1129
1130 /*
1131 * Write the analysis-specific graph lines in the gnuplot script.
1132 *
1133 * Args:
1134 * syncState: container for synchronization data
1135 * i: first trace number
1136 * j: second trace number, garanteed to be larger than i
1137 */
1138 void writeAnalysisGraphsPlotsCHull(SyncState* const syncState, const unsigned
1139 int i, const unsigned int j)
1140 {
1141 AnalysisDataCHull* analysisData;
1142 PairFactors* factorsCHull;
1143
1144 analysisData= (AnalysisDataCHull*) syncState->analysisData;
1145
1146 fprintf(syncState->graphsStream,
1147 "\t\"analysis_chull-%1$03d_to_%2$03d.data\" "
1148 "title \"Lower half-hull\" with linespoints "
1149 "linecolor rgb \"#015a01\" linetype 4 pointtype 8 pointsize 0.8, \\\n"
1150 "\t\"analysis_chull-%2$03d_to_%1$03d.data\" "
1151 "title \"Upper half-hull\" with linespoints "
1152 "linecolor rgb \"#003366\" linetype 4 pointtype 10 pointsize 0.8, \\\n",
1153 i, j);
1154
1155 factorsCHull= &analysisData->graphsData->allFactors->pairFactors[j][i];
1156 if (factorsCHull->type == EXACT)
1157 {
1158 fprintf(syncState->graphsStream,
1159 "\t%7g + %7g * x "
1160 "title \"Exact conversion\" with lines "
1161 "linecolor rgb \"black\" linetype 1, \\\n",
1162 factorsCHull->approx->offset, factorsCHull->approx->drift);
1163 }
1164 else if (factorsCHull->type == ACCURATE)
1165 {
1166 fprintf(syncState->graphsStream,
1167 "\t%.2f + %.10f * x "
1168 "title \"Min conversion\" with lines "
1169 "linecolor rgb \"black\" linetype 5, \\\n",
1170 factorsCHull->min->offset, factorsCHull->min->drift);
1171 fprintf(syncState->graphsStream,
1172 "\t%.2f + %.10f * x "
1173 "title \"Max conversion\" with lines "
1174 "linecolor rgb \"black\" linetype 8, \\\n",
1175 factorsCHull->max->offset, factorsCHull->max->drift);
1176 fprintf(syncState->graphsStream,
1177 "\t%.2f + %.10f * x "
1178 "title \"Middle conversion\" with lines "
1179 "linecolor rgb \"black\" linetype 1, \\\n",
1180 factorsCHull->approx->offset, factorsCHull->approx->drift);
1181 }
1182 else if (factorsCHull->type == APPROXIMATE)
1183 {
1184 fprintf(syncState->graphsStream,
1185 "\t%.2f + %.10f * x "
1186 "title \"Fallback conversion\" with lines "
1187 "linecolor rgb \"gray60\" linetype 1, \\\n",
1188 factorsCHull->approx->offset, factorsCHull->approx->drift);
1189 }
1190 else if (factorsCHull->type == INCOMPLETE)
1191 {
1192 if (factorsCHull->min->drift != -INFINITY)
1193 {
1194 fprintf(syncState->graphsStream,
1195 "\t%.2f + %.10f * x "
1196 "title \"Min conversion\" with lines "
1197 "linecolor rgb \"black\" linetype 5, \\\n",
1198 factorsCHull->min->offset, factorsCHull->min->drift);
1199 }
1200
1201 if (factorsCHull->max->drift != INFINITY)
1202 {
1203 fprintf(syncState->graphsStream,
1204 "\t%.2f + %.10f * x "
1205 "title \"Max conversion\" with lines "
1206 "linecolor rgb \"black\" linetype 8, \\\n",
1207 factorsCHull->max->offset, factorsCHull->max->drift);
1208 }
1209 }
1210 else if (factorsCHull->type == SCREWED)
1211 {
1212 if (factorsCHull->min != NULL && factorsCHull->min->drift != -INFINITY)
1213 {
1214 fprintf(syncState->graphsStream,
1215 "\t%.2f + %.10f * x "
1216 "title \"Min conversion\" with lines "
1217 "linecolor rgb \"black\" linetype 5, \\\n",
1218 factorsCHull->min->offset, factorsCHull->min->drift);
1219 }
1220
1221 if (factorsCHull->max != NULL && factorsCHull->max->drift != INFINITY)
1222 {
1223 fprintf(syncState->graphsStream,
1224 "\t%.2f + %.10f * x "
1225 "title \"Max conversion\" with lines "
1226 "linecolor rgb \"black\" linetype 8, \\\n",
1227 factorsCHull->max->offset, factorsCHull->max->drift);
1228 }
1229 }
1230 else if (factorsCHull->type == ABSENT)
1231 {
1232 }
1233 else
1234 {
1235 g_assert_not_reached();
1236 }
1237 }
This page took 0.089325 seconds and 4 git commands to generate.