Calculate synchronization accuracy within the chull module
[lttv.git] / lttv / lttv / sync / event_analysis_chull.c
CommitLineData
08365995 1/* This file is part of the Linux Trace Toolkit viewer
277e5b53 2 * Copyright (C) 2009, 2010 Benjamin Poirier <benjamin.poirier@polymtl.ca>
08365995 3 *
277e5b53
BP
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.
08365995 8 *
277e5b53
BP
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.
08365995 13 *
277e5b53
BP
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/>.
08365995
BP
16 */
17#define _ISOC99_SOURCE
18
19#ifdef HAVE_CONFIG_H
20#include <config.h>
21#endif
22
23#include <errno.h>
053b4b77 24#include <inttypes.h>
08365995
BP
25#include <math.h>
26#include <float.h>
27#include <stdlib.h>
28#include <stdio.h>
2f076594 29#include <string.h>
08365995
BP
30#include <unistd.h>
31
2bd4b3e4 32#include "sync_chain.h"
08365995
BP
33
34#include "event_analysis_chull.h"
35
36
08365995
BP
37typedef enum
38{
39 LOWER,
40 UPPER
41} HullType;
42
08365995
BP
43typedef enum
44{
45 MINIMUM,
46 MAXIMUM
47} LineType;
48
ab6edc6a
BP
49#ifdef HAVE_LIBGLPK
50struct LPAddRowInfo
51{
52 glp_prob* lp;
53 int boundType;
54 GArray* iArray, * jArray, * aArray;
55};
56#endif
57
08365995
BP
58
59// Functions common to all analysis modules
60static void initAnalysisCHull(SyncState* const syncState);
61static void destroyAnalysisCHull(SyncState* const syncState);
62
10341d26
BP
63static void analyzeMessageCHull(SyncState* const syncState, Message* const
64 message);
0a87ec9a 65static AllFactors* finalizeAnalysisCHull(SyncState* const syncState);
08365995 66static void printAnalysisStatsCHull(SyncState* const syncState);
ab6edc6a
BP
67static void writeAnalysisTraceTraceForePlotsCHull(SyncState* const syncState,
68 const unsigned int i, const unsigned int j);
08365995
BP
69
70// Functions specific to this module
08365995
BP
71static void openGraphFiles(SyncState* const syncState);
72static void closeGraphFiles(SyncState* const syncState);
73static void writeGraphFiles(SyncState* const syncState);
74static void gfDumpHullToFile(gpointer data, gpointer userData);
75
ab6edc6a
BP
76AllFactors* calculateAllFactors(struct _SyncState* const syncState);
77void calculateFactorsMiddle(PairFactors* const factors);
78static Factors* calculateFactorsExact(GQueue* const cu, GQueue* const cl, const
79 LineType lineType) __attribute__((pure));
80static void calculateFactorsFallback(GQueue* const cr, GQueue* const cs,
81 PairFactors* const result);
08365995
BP
82static void grahamScan(GQueue* const hull, Point* const newPoint, const
83 HullType type);
84static int jointCmp(const Point* const p1, const Point* const p2, const Point*
85 const p3) __attribute__((pure));
86static double crossProductK(const Point const* p1, const Point const* p2,
87 const Point const* p3, const Point const* p4) __attribute__((pure));
08365995
BP
88static double slope(const Point* const p1, const Point* const p2)
89 __attribute__((pure));
90static double intercept(const Point* const p1, const Point* const p2)
91 __attribute__((pure));
08365995
BP
92static double verticalDistance(Point* p1, Point* p2, Point* const point)
93 __attribute__((pure));
08365995
BP
94
95static void gfPointDestroy(gpointer data, gpointer userData);
96
ab6edc6a
BP
97// The next group of functions is only needed when computing synchronization
98// accuracy.
99#ifdef HAVE_LIBGLPK
100static AllFactors* finalizeAnalysisCHullLP(SyncState* const syncState);
101static void writeAnalysisTraceTimeBackPlotsCHull(SyncState* const syncState,
102 const unsigned int i, const unsigned int j);
103static void writeAnalysisTraceTimeForePlotsCHull(SyncState* const syncState,
104 const unsigned int i, const unsigned int j);
105static void writeAnalysisTraceTraceBackPlotsCHull(SyncState* const syncState,
106 const unsigned int i, const unsigned int j);
107
108static glp_prob* lpCreateProblem(GQueue* const lowerHull, GQueue* const
109 upperHull);
110static void gfLPAddRow(gpointer data, gpointer user_data);
111static Factors* calculateFactorsLP(glp_prob* const lp, const int direction);
112static void calculateCompleteFactorsLP(glp_prob* const lp, PairFactors*
113 factors);
114void timeCorrectionLP(glp_prob* const lp, const PairFactors* const lpFactors,
115 const uint64_t time, CorrectedTime* const correctedTime);
116
117static void gfAddAbsiscaToArray(gpointer data, gpointer user_data);
118static gint gcfCompareUint64(gconstpointer a, gconstpointer b);
119#else
120static inline AllFactors* finalizeAnalysisCHullLP(SyncState* const syncState)
121{
122 return NULL;
123}
124#endif
125
126
08365995
BP
127
128static AnalysisModule analysisModuleCHull= {
129 .name= "chull",
130 .initAnalysis= &initAnalysisCHull,
131 .destroyAnalysis= &destroyAnalysisCHull,
10341d26 132 .analyzeMessage= &analyzeMessageCHull,
08365995
BP
133 .finalizeAnalysis= &finalizeAnalysisCHull,
134 .printAnalysisStats= &printAnalysisStatsCHull,
467066ee 135 .graphFunctions= {
ab6edc6a
BP
136#ifdef HAVE_LIBGLPK
137 .writeTraceTimeBackPlots= &writeAnalysisTraceTimeBackPlotsCHull,
138 .writeTraceTimeForePlots= &writeAnalysisTraceTimeForePlotsCHull,
139 .writeTraceTraceBackPlots= &writeAnalysisTraceTraceBackPlotsCHull,
140#endif
141 .writeTraceTraceForePlots= &writeAnalysisTraceTraceForePlotsCHull,
467066ee 142 }
08365995
BP
143};
144
c6356aa7 145
08365995
BP
146/*
147 * Analysis module registering function
148 */
2f961b65 149void registerAnalysisCHull()
08365995
BP
150{
151 g_queue_push_tail(&analysisModules, &analysisModuleCHull);
152}
153
154
155/*
156 * Analysis init function
157 *
158 * This function is called at the beginning of a synchronization run for a set
159 * of traces.
160 *
161 * Allocate some of the analysis specific data structures
162 *
163 * Args:
164 * syncState container for synchronization data.
165 * This function allocates or initializes these analysisData
166 * members:
167 * hullArray
168 * dropped
169 */
170static void initAnalysisCHull(SyncState* const syncState)
171{
172 unsigned int i, j;
173 AnalysisDataCHull* analysisData;
174
175 analysisData= malloc(sizeof(AnalysisDataCHull));
176 syncState->analysisData= analysisData;
177
178 analysisData->hullArray= malloc(syncState->traceNb * sizeof(GQueue**));
179 for (i= 0; i < syncState->traceNb; i++)
180 {
181 analysisData->hullArray[i]= malloc(syncState->traceNb * sizeof(GQueue*));
182
183 for (j= 0; j < syncState->traceNb; j++)
184 {
185 analysisData->hullArray[i][j]= g_queue_new();
186 }
187 }
ab6edc6a
BP
188#ifdef HAVE_LIBGLPK
189 analysisData->lps= NULL;
190#endif
08365995
BP
191
192 if (syncState->stats)
193 {
ab6edc6a 194 analysisData->stats= calloc(1, sizeof(AnalysisStatsCHull));
08365995
BP
195 }
196
8d7d16dd 197 if (syncState->graphsStream)
08365995 198 {
ab6edc6a 199 analysisData->graphsData= calloc(1, sizeof(AnalysisGraphsDataCHull));
08365995 200 openGraphFiles(syncState);
08365995
BP
201 }
202}
203
204
205/*
206 * Create and open files used to store convex hull points to genereate
207 * graphs. Allocate and populate array to store file pointers.
208 *
209 * Args:
210 * syncState: container for synchronization data
211 */
212static void openGraphFiles(SyncState* const syncState)
213{
214 unsigned int i, j;
215 int retval;
216 char* cwd;
217 char name[31];
218 AnalysisDataCHull* analysisData;
219
220 analysisData= (AnalysisDataCHull*) syncState->analysisData;
221
1d597550 222 cwd= changeToGraphsDir(syncState->graphsDir);
08365995
BP
223
224 analysisData->graphsData->hullPoints= malloc(syncState->traceNb *
225 sizeof(FILE**));
226 for (i= 0; i < syncState->traceNb; i++)
227 {
228 analysisData->graphsData->hullPoints[i]= malloc(syncState->traceNb *
229 sizeof(FILE*));
230 for (j= 0; j < syncState->traceNb; j++)
231 {
232 if (i != j)
233 {
234 retval= snprintf(name, sizeof(name),
235 "analysis_chull-%03u_to_%03u.data", j, i);
236 if (retval > sizeof(name) - 1)
237 {
238 name[sizeof(name) - 1]= '\0';
239 }
240 if ((analysisData->graphsData->hullPoints[i][j]= fopen(name, "w")) ==
241 NULL)
242 {
243 g_error(strerror(errno));
244 }
245 }
246 }
247 }
248
249 retval= chdir(cwd);
250 if (retval == -1)
251 {
252 g_error(strerror(errno));
253 }
254 free(cwd);
255}
256
257
258/*
259 * Write hull points to files to generate graphs.
260 *
261 * Args:
262 * syncState: container for synchronization data
263 */
264static void writeGraphFiles(SyncState* const syncState)
265{
266 unsigned int i, j;
267 AnalysisDataCHull* analysisData;
268
269 analysisData= (AnalysisDataCHull*) syncState->analysisData;
270
271 for (i= 0; i < syncState->traceNb; i++)
272 {
273 for (j= 0; j < syncState->traceNb; j++)
274 {
275 if (i != j)
276 {
277 g_queue_foreach(analysisData->hullArray[i][j],
278 &gfDumpHullToFile,
279 analysisData->graphsData->hullPoints[i][j]);
280 }
281 }
282 }
283}
284
285
286/*
287 * A GFunc for g_queue_foreach. Write a hull point to a file used to generate
288 * graphs
289 *
290 * Args:
291 * data: Point*, point to write to the file
292 * userData: FILE*, file pointer where to write the point
293 */
294static void gfDumpHullToFile(gpointer data, gpointer userData)
295{
296 Point* point;
297
298 point= (Point*) data;
053b4b77 299 fprintf((FILE*) userData, "%20" PRIu64 " %20" PRIu64 "\n", point->x, point->y);
08365995
BP
300}
301
302
303/*
304 * Close files used to store convex hull points to generate graphs.
305 * Deallocate array to store file pointers.
306 *
307 * Args:
308 * syncState: container for synchronization data
309 */
310static void closeGraphFiles(SyncState* const syncState)
311{
312 unsigned int i, j;
313 AnalysisDataCHull* analysisData;
314 int retval;
315
316 analysisData= (AnalysisDataCHull*) syncState->analysisData;
317
318 if (analysisData->graphsData->hullPoints == NULL)
319 {
320 return;
321 }
322
323 for (i= 0; i < syncState->traceNb; i++)
324 {
325 for (j= 0; j < syncState->traceNb; j++)
326 {
327 if (i != j)
328 {
329 retval= fclose(analysisData->graphsData->hullPoints[i][j]);
330 if (retval != 0)
331 {
332 g_error(strerror(errno));
333 }
334 }
335 }
336 free(analysisData->graphsData->hullPoints[i]);
337 }
338 free(analysisData->graphsData->hullPoints);
339 analysisData->graphsData->hullPoints= NULL;
340}
341
342
343/*
344 * Analysis destroy function
345 *
346 * Free the analysis specific data structures
347 *
348 * Args:
349 * syncState container for synchronization data.
350 * This function deallocates these analysisData members:
351 * hullArray
352 * stDev
353 */
354static void destroyAnalysisCHull(SyncState* const syncState)
355{
356 unsigned int i, j;
357 AnalysisDataCHull* analysisData;
358
359 analysisData= (AnalysisDataCHull*) syncState->analysisData;
360
361 if (analysisData == NULL)
362 {
363 return;
364 }
365
366 for (i= 0; i < syncState->traceNb; i++)
367 {
368 for (j= 0; j < syncState->traceNb; j++)
369 {
b2da0724
BP
370 g_queue_foreach(analysisData->hullArray[i][j], gfPointDestroy,
371 NULL);
6ce8ceac 372 g_queue_free(analysisData->hullArray[i][j]);
08365995
BP
373 }
374 free(analysisData->hullArray[i]);
375 }
376 free(analysisData->hullArray);
377
ab6edc6a
BP
378#ifdef HAVE_LIBGLPK
379 if (analysisData->lps != NULL)
380 {
381 for (i= 0; i < syncState->traceNb; i++)
382 {
383 unsigned int j;
384
385 for (j= 0; j < i; j++)
386 {
387 // There seems to be a memory leak in glpk, valgrind reports a
388 // loss (reachable) even if the problem is deleted
389 glp_delete_prob(analysisData->lps[i][j]);
390 }
391 free(analysisData->lps[i]);
392 }
393 free(analysisData->lps);
394 }
395#endif
396
08365995
BP
397 if (syncState->stats)
398 {
b2da0724 399 freeAllFactors(analysisData->stats->allFactors, syncState->traceNb);
ab6edc6a
BP
400 freeAllFactors(analysisData->stats->geoFactors, syncState->traceNb);
401
402#ifdef HAVE_LIBGLPK
403 freeAllFactors(analysisData->stats->lpFactors, syncState->traceNb);
404#endif
08365995
BP
405
406 free(analysisData->stats);
407 }
408
8d7d16dd 409 if (syncState->graphsStream)
08365995 410 {
ab6edc6a
BP
411 AnalysisGraphsDataCHull* graphs= analysisData->graphsData;
412
413 if (graphs->hullPoints != NULL)
08365995
BP
414 {
415 closeGraphFiles(syncState);
416 }
417
ab6edc6a
BP
418 freeAllFactors(graphs->allFactors, syncState->traceNb);
419
420#ifdef HAVE_LIBGLPK
421 freeAllFactors(graphs->lpFactors, syncState->traceNb);
422#endif
08365995
BP
423
424 free(analysisData->graphsData);
425 }
426
427 free(syncState->analysisData);
428 syncState->analysisData= NULL;
429}
430
431
432/*
433 * Perform analysis on an event pair.
434 *
435 * Args:
436 * syncState container for synchronization data
10341d26 437 * message structure containing the events
08365995 438 */
10341d26 439static void analyzeMessageCHull(SyncState* const syncState, Message* const message)
08365995
BP
440{
441 AnalysisDataCHull* analysisData;
442 Point* newPoint;
443 HullType hullType;
444 GQueue* hull;
445
446 analysisData= (AnalysisDataCHull*) syncState->analysisData;
447
448 newPoint= malloc(sizeof(Point));
10341d26 449 if (message->inE->traceNum < message->outE->traceNum)
08365995
BP
450 {
451 // CA is inE->traceNum
76be6fc2
BP
452 newPoint->x= message->inE->cpuTime;
453 newPoint->y= message->outE->cpuTime;
08365995 454 hullType= UPPER;
053b4b77
BP
455 g_debug("Reception point hullArray[%lu][%lu] "
456 "x= inE->time= %" PRIu64 " y= outE->time= %" PRIu64,
10341d26 457 message->inE->traceNum, message->outE->traceNum, newPoint->x,
08365995
BP
458 newPoint->y);
459 }
460 else
461 {
462 // CA is outE->traceNum
76be6fc2
BP
463 newPoint->x= message->outE->cpuTime;
464 newPoint->y= message->inE->cpuTime;
08365995 465 hullType= LOWER;
053b4b77
BP
466 g_debug("Send point hullArray[%lu][%lu] "
467 "x= inE->time= %" PRIu64 " y= outE->time= %" PRIu64,
10341d26 468 message->inE->traceNum, message->outE->traceNum, newPoint->x,
08365995
BP
469 newPoint->y);
470 }
471
472 hull=
10341d26 473 analysisData->hullArray[message->inE->traceNum][message->outE->traceNum];
08365995
BP
474
475 if (hull->length >= 1 && newPoint->x < ((Point*)
476 g_queue_peek_tail(hull))->x)
477 {
478 if (syncState->stats)
479 {
480 analysisData->stats->dropped++;
481 }
482
483 free(newPoint);
484 }
485 else
486 {
487 grahamScan(hull, newPoint, hullType);
488 }
489}
490
491
492/*
493 * Construct one half of a convex hull from abscissa-sorted points
494 *
495 * Args:
496 * hull: the points already in the hull
497 * newPoint: a new point to consider
498 * type: which half of the hull to construct
499 */
500static void grahamScan(GQueue* const hull, Point* const newPoint, const
501 HullType type)
502{
503 int inversionFactor;
504
505 g_debug("grahamScan(hull (length: %u), newPoint, %s)", hull->length, type
506 == LOWER ? "LOWER" : "UPPER");
507
508 if (type == LOWER)
509 {
510 inversionFactor= 1;
511 }
512 else
513 {
514 inversionFactor= -1;
515 }
516
517 if (hull->length >= 2)
518 {
519 g_debug("jointCmp(hull[%u], hull[%u], newPoint) * inversionFactor = %d * %d = %d",
520 hull->length - 2,
521 hull->length - 1,
522 jointCmp(g_queue_peek_nth(hull, hull->length - 2),
523 g_queue_peek_tail(hull), newPoint),
524 inversionFactor,
525 jointCmp(g_queue_peek_nth(hull, hull->length - 2),
526 g_queue_peek_tail(hull), newPoint) * inversionFactor);
527 }
528 while (hull->length >= 2 && jointCmp(g_queue_peek_nth(hull, hull->length -
529 2), g_queue_peek_tail(hull), newPoint) * inversionFactor <= 0)
530 {
531 g_debug("Removing hull[%u]", hull->length);
532 free((Point*) g_queue_pop_tail(hull));
533
534 if (hull->length >= 2)
535 {
536 g_debug("jointCmp(hull[%u], hull[%u], newPoint) * inversionFactor = %d * %d = %d",
537 hull->length - 2,
538 hull->length - 1,
539 jointCmp(g_queue_peek_nth(hull, hull->length - 2),
540 g_queue_peek_tail(hull), newPoint),
541 inversionFactor,
542 jointCmp(g_queue_peek_nth(hull, hull->length - 2),
543 g_queue_peek_tail(hull), newPoint) * inversionFactor);
544 }
545 }
546 g_queue_push_tail(hull, newPoint);
547}
548
549
550/*
551 * Finalize the factor calculations
552 *
553 * Args:
554 * syncState container for synchronization data.
555 *
556 * Returns:
0a87ec9a
BP
557 * AllFactors* synchronization factors for each trace pair, the caller is
558 * responsible for freeing the structure
08365995 559 */
0a87ec9a 560static AllFactors* finalizeAnalysisCHull(SyncState* const syncState)
08365995
BP
561{
562 AnalysisDataCHull* analysisData;
ab6edc6a 563 AllFactors* geoFactors, * lpFactors;
08365995
BP
564
565 analysisData= (AnalysisDataCHull*) syncState->analysisData;
566
8d7d16dd 567 if (syncState->graphsStream && analysisData->graphsData->hullPoints != NULL)
08365995
BP
568 {
569 writeGraphFiles(syncState);
570 closeGraphFiles(syncState);
571 }
572
ab6edc6a
BP
573 geoFactors= calculateAllFactors(syncState);
574 lpFactors= finalizeAnalysisCHullLP(syncState);
08365995 575
0a87ec9a 576 if (syncState->stats)
08365995 577 {
ab6edc6a
BP
578 geoFactors->refCount++;
579 analysisData->stats->geoFactors= geoFactors;
580
581 if (lpFactors != NULL)
582 {
583 lpFactors->refCount++;
584 analysisData->stats->allFactors= lpFactors;
585 }
586 else
587 {
588 geoFactors->refCount++;
589 analysisData->stats->allFactors= geoFactors;
590 }
08365995 591 }
0a87ec9a
BP
592
593 if (syncState->graphsStream)
08365995 594 {
ab6edc6a
BP
595 if (lpFactors != NULL)
596 {
597 lpFactors->refCount++;
598 analysisData->graphsData->allFactors= lpFactors;
599 }
600 else
601 {
602 geoFactors->refCount++;
603 analysisData->graphsData->allFactors= geoFactors;
604 }
08365995
BP
605 }
606
ab6edc6a
BP
607 if (lpFactors != NULL)
608 {
609 freeAllFactors(geoFactors, syncState->traceNb);
610 return lpFactors;
611 }
612 else
613 {
614 freeAllFactors(lpFactors, syncState->traceNb);
615 return geoFactors;
616 }
08365995
BP
617}
618
619
620/*
621 * Print statistics related to analysis. Must be called after
622 * finalizeAnalysis.
623 *
624 * Args:
625 * syncState container for synchronization data.
626 */
627static void printAnalysisStatsCHull(SyncState* const syncState)
628{
629 AnalysisDataCHull* analysisData;
630 unsigned int i, j;
631
632 if (!syncState->stats)
633 {
634 return;
635 }
636
637 analysisData= (AnalysisDataCHull*) syncState->analysisData;
638
639 printf("Convex hull analysis stats:\n");
640 printf("\tout of order packets dropped from analysis: %u\n",
641 analysisData->stats->dropped);
642
643 printf("\tNumber of points in convex hulls:\n");
644
645 for (i= 0; i < syncState->traceNb; i++)
646 {
647 for (j= i + 1; j < syncState->traceNb; j++)
648 {
649 printf("\t\t%3d - %-3d: lower half-hull %-5u upper half-hull %-5u\n",
650 i, j, analysisData->hullArray[j][i]->length,
651 analysisData->hullArray[i][j]->length);
652 }
653 }
654
655 printf("\tIndividual synchronization factors:\n");
656
657 for (i= 0; i < syncState->traceNb; i++)
658 {
659 for (j= i + 1; j < syncState->traceNb; j++)
660 {
0a87ec9a 661 PairFactors* factorsCHull;
08365995 662
0a87ec9a 663 factorsCHull= &analysisData->stats->allFactors->pairFactors[j][i];
ce3dcf0e
BP
664 printf("\t\t%3d - %-3d: %s", i, j,
665 approxNames[factorsCHull->type]);
08365995
BP
666
667 if (factorsCHull->type == EXACT)
668 {
ce3dcf0e 669 printf(" a0= % 7g a1= 1 %c %7g\n",
08365995
BP
670 factorsCHull->approx->offset,
671 factorsCHull->approx->drift < 0. ? '-' : '+',
672 fabs(factorsCHull->approx->drift));
673 }
0a87ec9a 674 else if (factorsCHull->type == ACCURATE)
08365995 675 {
0a87ec9a 676 printf("\n\t\t a0: % 7g to % 7g (delta= %7g)\n",
08365995
BP
677 factorsCHull->max->offset, factorsCHull->min->offset,
678 factorsCHull->min->offset - factorsCHull->max->offset);
679 printf("\t\t a1: 1 %+7g to %+7g (delta= %7g)\n",
680 factorsCHull->min->drift - 1., factorsCHull->max->drift -
681 1., factorsCHull->max->drift - factorsCHull->min->drift);
682 }
0a87ec9a 683 else if (factorsCHull->type == APPROXIMATE)
08365995 684 {
ce3dcf0e 685 printf(" a0= % 7g a1= 1 %c %7g error= %7g\n",
08365995
BP
686 factorsCHull->approx->offset, factorsCHull->approx->drift
687 - 1. < 0. ? '-' : '+', fabs(factorsCHull->approx->drift -
688 1.), factorsCHull->accuracy);
689 }
690 else if (factorsCHull->type == INCOMPLETE)
691 {
ce3dcf0e 692 printf("\n");
08365995
BP
693
694 if (factorsCHull->min->drift != -INFINITY)
695 {
696 printf("\t\t min: a0: % 7g a1: 1 %c %7g\n",
697 factorsCHull->min->offset, factorsCHull->min->drift -
698 1. < 0 ? '-' : '+', fabs(factorsCHull->min->drift -
699 1.));
700 }
701 if (factorsCHull->max->drift != INFINITY)
702 {
703 printf("\t\t max: a0: % 7g a1: 1 %c %7g\n",
704 factorsCHull->max->offset, factorsCHull->max->drift -
705 1. < 0 ? '-' : '+', fabs(factorsCHull->max->drift -
706 1.));
707 }
708 }
ab6edc6a 709 else if (factorsCHull->type == FAIL)
08365995 710 {
ce3dcf0e 711 printf("\n");
08365995
BP
712
713 if (factorsCHull->min != NULL && factorsCHull->min->drift != -INFINITY)
714 {
715 printf("\t\t min: a0: % 7g a1: 1 %c %7g\n",
716 factorsCHull->min->offset, factorsCHull->min->drift -
717 1. < 0 ? '-' : '+', fabs(factorsCHull->min->drift -
718 1.));
719 }
720 if (factorsCHull->max != NULL && factorsCHull->max->drift != INFINITY)
721 {
722 printf("\t\t max: a0: % 7g a1: 1 %c %7g\n",
723 factorsCHull->max->offset, factorsCHull->max->drift -
724 1. < 0 ? '-' : '+', fabs(factorsCHull->max->drift -
725 1.));
726 }
727 }
728 else if (factorsCHull->type == ABSENT)
729 {
ce3dcf0e 730 printf("\n");
08365995
BP
731 }
732 else
733 {
734 g_assert_not_reached();
735 }
736 }
737 }
ab6edc6a
BP
738
739#ifdef HAVE_LIBGLPK
740 printf("\tFactor comparison:\n"
741 "\t\tTrace pair Factors type Differences (lp - chull)\n"
742 "\t\t a0 a1\n"
743 "\t\t Min Max Min Max\n");
744
745 for (i= 0; i < syncState->traceNb; i++)
746 {
747 for (j= 0; j < i; j++)
748 {
749 PairFactors* geoFactors=
750 &analysisData->stats->geoFactors->pairFactors[i][j];
751 PairFactors* lpFactors=
752 &analysisData->stats->lpFactors->pairFactors[i][j];
753
754 printf("\t\t%3d - %-3d ", i, j);
755 if (lpFactors->type == geoFactors->type)
756 {
757 if (lpFactors->type == ACCURATE)
758 {
759 printf("%-13s %-10.4g %-10.4g %-10.4g %.4g\n",
760 approxNames[lpFactors->type],
761 lpFactors->min->offset - geoFactors->min->offset,
762 lpFactors->max->offset - geoFactors->max->offset,
763 lpFactors->min->drift - geoFactors->min->drift,
764 lpFactors->max->drift - geoFactors->max->drift);
765 }
766 else if (lpFactors->type == ABSENT)
767 {
768 printf("%s\n", approxNames[lpFactors->type]);
769 }
770 }
771 else
772 {
773 printf("Different! %s and %s\n", approxNames[lpFactors->type],
774 approxNames[geoFactors->type]);
775 }
776 }
777 }
778#endif
08365995
BP
779}
780
781
782/*
783 * A GFunc for g_queue_foreach()
784 *
785 * Args:
786 * data Point*, point to destroy
787 * user_data NULL
788 */
789static void gfPointDestroy(gpointer data, gpointer userData)
790{
791 Point* point;
792
793 point= (Point*) data;
794 free(point);
795}
796
797
798/*
799 * Find out if a sequence of three points constitutes a "left turn" or a
800 * "right turn".
801 *
802 * Args:
803 * p1, p2, p3: The three points.
804 *
805 * Returns:
806 * < 0 right turn
807 * 0 colinear (unlikely result since this uses floating point
808 * arithmetic)
809 * > 0 left turn
810 */
811static int jointCmp(const Point const* p1, const Point const* p2, const
812 Point const* p3)
813{
814 double result;
815 const double fuzzFactor= 0.;
816
817 result= crossProductK(p1, p2, p1, p3);
053b4b77
BP
818 g_debug("crossProductK(p1= (%" PRIu64 ", %" PRIu64 "), "
819 "p2= (%" PRIu64 ", %" PRIu64 "), p1= (%" PRIu64 ", %" PRIu64 "), "
820 "p3= (%" PRIu64 ", %" PRIu64 "))= %g",
08365995
BP
821 p1->x, p1->y, p2->x, p2->y, p1->x, p1->y, p3->x, p3->y, result);
822 if (result < fuzzFactor)
823 {
824 return -1;
825 }
826 else if (result > fuzzFactor)
827 {
828 return 1;
829 }
830 else
831 {
832 return 0;
833 }
834}
835
836
837/*
838 * Calculate the k component of the cross product of two vectors.
839 *
840 * Args:
841 * p1, p2: start and end points of the first vector
842 * p3, p4: start and end points of the second vector
843 *
844 * Returns:
845 * the k component of the cross product when considering the two vectors to
846 * be in the i-j plane. The direction (sign) of the result can be useful to
847 * determine the relative orientation of the two vectors.
848 */
849static double crossProductK(const Point const* p1, const Point const* p2,
850 const Point const* p3, const Point const* p4)
851{
852 return ((double) p2->x - p1->x) * ((double) p4->y - p3->y) - ((double)
853 p2->y - p1->y) * ((double) p4->x - p3->x);
854}
855
856
08365995
BP
857/*
858 * Analyze the convex hulls to determine the synchronization factors between
859 * each pair of trace.
860 *
861 * Args:
862 * syncState container for synchronization data.
863 *
864 * Returns:
ab6edc6a 865 * AllFactors*, see the documentation for the member geoFactors of
0a87ec9a 866 * AnalysisStatsCHull.
08365995 867 */
0a87ec9a 868AllFactors* calculateAllFactors(SyncState* const syncState)
08365995
BP
869{
870 unsigned int traceNumA, traceNumB;
ab6edc6a 871 AllFactors* geoFactors;
08365995
BP
872 AnalysisDataCHull* analysisData;
873
874 analysisData= (AnalysisDataCHull*) syncState->analysisData;
875
ab6edc6a
BP
876 // Allocate geoFactors and calculate min and max
877 geoFactors= createAllFactors(syncState->traceNb);
08365995
BP
878 for (traceNumA= 0; traceNumA < syncState->traceNb; traceNumA++)
879 {
08365995
BP
880 for (traceNumB= 0; traceNumB < traceNumA; traceNumB++)
881 {
882 unsigned int i;
883 GQueue* cs, * cr;
884 const struct
885 {
886 LineType lineType;
887 size_t factorsOffset;
888 } loopValues[]= {
0a87ec9a
BP
889 {MINIMUM, offsetof(PairFactors, min)},
890 {MAXIMUM, offsetof(PairFactors, max)}
08365995
BP
891 };
892
893 cr= analysisData->hullArray[traceNumB][traceNumA];
894 cs= analysisData->hullArray[traceNumA][traceNumB];
895
896 for (i= 0; i < sizeof(loopValues) / sizeof(*loopValues); i++)
897 {
ab6edc6a 898 g_debug("geoFactors[%u][%u].%s = calculateFactorsExact(cr= "
0a87ec9a 899 "hullArray[%u][%u], cs= hullArray[%u][%u], %s)",
08365995 900 traceNumA, traceNumB, loopValues[i].factorsOffset ==
0a87ec9a 901 offsetof(PairFactors, min) ? "min" : "max", traceNumB,
08365995
BP
902 traceNumA, traceNumA, traceNumB, loopValues[i].lineType ==
903 MINIMUM ? "MINIMUM" : "MAXIMUM");
0a87ec9a 904 *((Factors**) ((void*)
ab6edc6a 905 &geoFactors->pairFactors[traceNumA][traceNumB] +
08365995
BP
906 loopValues[i].factorsOffset))=
907 calculateFactorsExact(cr, cs, loopValues[i].lineType);
908 }
909 }
910 }
911
912 // Calculate approx when possible
913 for (traceNumA= 0; traceNumA < syncState->traceNb; traceNumA++)
914 {
915 for (traceNumB= 0; traceNumB < traceNumA; traceNumB++)
916 {
0a87ec9a 917 PairFactors* factorsCHull;
08365995 918
ab6edc6a 919 factorsCHull= &geoFactors->pairFactors[traceNumA][traceNumB];
08365995
BP
920 if (factorsCHull->min == NULL && factorsCHull->max == NULL)
921 {
0a87ec9a 922 factorsCHull->type= APPROXIMATE;
08365995
BP
923 calculateFactorsFallback(analysisData->hullArray[traceNumB][traceNumA],
924 analysisData->hullArray[traceNumA][traceNumB],
ab6edc6a 925 &geoFactors->pairFactors[traceNumA][traceNumB]);
08365995
BP
926 }
927 else if (factorsCHull->min != NULL && factorsCHull->max != NULL)
928 {
929 if (factorsCHull->min->drift != -INFINITY &&
930 factorsCHull->max->drift != INFINITY)
931 {
0a87ec9a 932 factorsCHull->type= ACCURATE;
08365995
BP
933 calculateFactorsMiddle(factorsCHull);
934 }
935 else if (factorsCHull->min->drift != -INFINITY ||
936 factorsCHull->max->drift != INFINITY)
937 {
938 factorsCHull->type= INCOMPLETE;
939 }
940 else
941 {
942 factorsCHull->type= ABSENT;
943 }
944 }
945 else
946 {
947 //g_assert_not_reached();
ab6edc6a 948 factorsCHull->type= FAIL;
08365995
BP
949 }
950 }
951 }
952
ab6edc6a 953 return geoFactors;
08365995
BP
954}
955
956
957/* Calculate approximative factors based on minimum and maximum limits. The
958 * best approximation to make is the interior bissector of the angle formed by
959 * the minimum and maximum lines.
960 *
961 * The formulae used come from [Haddad, Yoram: Performance dans les systèmes
962 * répartis: des outils pour les mesures, Université de Paris-Sud, Centre
963 * d'Orsay, September 1988] Section 6.1 p.44
964 *
965 * The reasoning for choosing this estimator comes from [Duda, A., Harrus, G.,
966 * Haddad, Y., and Bernard, G.: Estimating global time in distributed systems,
967 * Proc. 7th Int. Conf. on Distributed Computing Systems, Berlin, volume 18,
968 * 1987] p.303
969 *
970 * Args:
971 * factors: contains the min and max limits, used to store the result
972 */
0a87ec9a 973void calculateFactorsMiddle(PairFactors* const factors)
08365995
BP
974{
975 double amin, amax, bmin, bmax, bhat;
976
977 amin= factors->max->offset;
978 amax= factors->min->offset;
979 bmin= factors->min->drift;
980 bmax= factors->max->drift;
981
48b641c1 982 g_assert_cmpfloat(bmax, >=, bmin);
08365995
BP
983
984 factors->approx= malloc(sizeof(Factors));
985 bhat= (bmax * bmin - 1. + sqrt(1. + pow(bmax, 2.) * pow(bmin, 2.) +
986 pow(bmax, 2.) + pow(bmin, 2.))) / (bmax + bmin);
987 factors->approx->offset= amax - (amax - amin) / 2. * (pow(bhat, 2.) + 1.)
988 / (1. + bhat * bmax);
989 factors->approx->drift= bhat;
990 factors->accuracy= bmax - bmin;
991}
992
993
994/*
995 * Analyze the convex hulls to determine the minimum or maximum
996 * synchronization factors between one pair of trace.
997 *
998 * This implements and improves upon the algorithm in [Haddad, Yoram:
999 * Performance dans les systèmes répartis: des outils pour les mesures,
1000 * Université de Paris-Sud, Centre d'Orsay, September 1988] Section 6.2 p.47
1001 *
1002 * Some degenerate cases are possible:
1003 * 1) the result is unbounded. In that case, when searching for the maximum
1004 * factors, result->drift= INFINITY; result->offset= -INFINITY. When
1005 * searching for the minimum factors, it is the opposite. It is not
1006 * possible to improve the situation with this data.
1007 * 2) no line can be above the upper hull and below the lower hull. This is
1008 * because the hulls intersect each other or are reversed. This means that
1009 * an assertion was false. Most probably, the clocks are not linear. It is
1010 * possible to repeat the search with another algorithm that will find a
1011 * "best effort" approximation. See calculateFactorsApprox().
1012 *
1013 * Args:
1014 * cu: the upper half-convex hull, the line must pass above this
1015 * and touch it in one point
1016 * cl: the lower half-convex hull, the line must pass below this
1017 * and touch it in one point
1018 * lineType: search for minimum or maximum factors
1019 *
1020 * Returns:
1021 * If a result is found, a struct Factors is allocated, filed with the
1022 * result and returned
1023 * NULL otherwise, degenerate case 2 is in effect
1024 */
1025static Factors* calculateFactorsExact(GQueue* const cu, GQueue* const cl, const
1026 LineType lineType)
1027{
1028 GQueue* c1, * c2;
1029 unsigned int i1, i2;
1030 Point* p1, * p2;
1031 double inversionFactor;
1032 Factors* result;
1033
1034 g_debug("calculateFactorsExact(cu= %p, cl= %p, %s)", cu, cl, lineType ==
1035 MINIMUM ? "MINIMUM" : "MAXIMUM");
1036
1037 if (lineType == MINIMUM)
1038 {
1039 c1= cl;
1040 c2= cu;
1041 inversionFactor= -1.;
1042 }
1043 else
1044 {
1045 c1= cu;
1046 c2= cl;
1047 inversionFactor= 1.;
1048 }
1049
1050 i1= 0;
1051 i2= c2->length - 1;
1052
1053 // Check for degenerate case 1
1054 if (c1->length == 0 || c2->length == 0 || ((Point*) g_queue_peek_nth(c1,
1055 i1))->x >= ((Point*) g_queue_peek_nth(c2, i2))->x)
1056 {
1057 result= malloc(sizeof(Factors));
1058 if (lineType == MINIMUM)
1059 {
1060 result->drift= -INFINITY;
1061 result->offset= INFINITY;
1062 }
1063 else
1064 {
1065 result->drift= INFINITY;
1066 result->offset= -INFINITY;
1067 }
1068
1069 return result;
1070 }
1071
1072 do
1073 {
1074 while
1075 (
1076 (int) i2 - 1 > 0
1077 && crossProductK(
1078 g_queue_peek_nth(c1, i1),
1079 g_queue_peek_nth(c2, i2),
1080 g_queue_peek_nth(c1, i1),
1081 g_queue_peek_nth(c2, i2 - 1)) * inversionFactor < 0.
1082 )
1083 {
1084 if (((Point*) g_queue_peek_nth(c1, i1))->x
1085 < ((Point*) g_queue_peek_nth(c2, i2 - 1))->x)
1086 {
1087 i2--;
1088 }
1089 else
1090 {
1091 // Degenerate case 2
1092 return NULL;
1093 }
1094 }
1095 while
1096 (
1097 i1 + 1 < c1->length - 1
1098 && crossProductK(
1099 g_queue_peek_nth(c1, i1),
1100 g_queue_peek_nth(c2, i2),
1101 g_queue_peek_nth(c1, i1 + 1),
1102 g_queue_peek_nth(c2, i2)) * inversionFactor < 0.
1103 )
1104 {
1105 if (((Point*) g_queue_peek_nth(c1, i1 + 1))->x
1106 < ((Point*) g_queue_peek_nth(c2, i2))->x)
1107 {
1108 i1++;
1109 }
1110 else
1111 {
1112 // Degenerate case 2
1113 return NULL;
1114 }
1115 }
1116 } while
1117 (
1118 (int) i2 - 1 > 0
1119 && crossProductK(
1120 g_queue_peek_nth(c1, i1),
1121 g_queue_peek_nth(c2, i2),
1122 g_queue_peek_nth(c1, i1),
1123 g_queue_peek_nth(c2, i2 - 1)) * inversionFactor < 0.
1124 );
1125
1126 p1= g_queue_peek_nth(c1, i1);
1127 p2= g_queue_peek_nth(c2, i2);
1128
053b4b77
BP
1129 g_debug("Resulting points are: c1[i1]: x= %" PRIu64 " y= %" PRIu64
1130 " c2[i2]: x= %" PRIu64 " y= %" PRIu64 "", p1->x, p1->y, p2->x, p2->y);
08365995
BP
1131
1132 result= malloc(sizeof(Factors));
1133 result->drift= slope(p1, p2);
1134 result->offset= intercept(p1, p2);
1135
053b4b77
BP
1136 g_debug("Resulting factors are: drift= %g offset= %g", result->drift,
1137 result->offset);
08365995
BP
1138
1139 return result;
1140}
1141
1142
1143/*
1144 * Analyze the convex hulls to determine approximate synchronization factors
1145 * between one pair of trace when there is no line that can fit in the
1146 * corridor separating them.
1147 *
1148 * This implements the algorithm in [Ashton, P.: Algorithms for Off-line Clock
1149 * Synchronisation, University of Canterbury, December 1995, 26] Section 4.2.2
1150 * p.7
1151 *
1152 * For each point p1 in cr
1153 * For each point p2 in cs
1154 * errorMin= 0
1155 * Calculate the line paramaters
1156 * For each point p3 in each convex hull
1157 * If p3 is on the wrong side of the line
1158 * error+= distance
1159 * If error < errorMin
1160 * Update results
1161 *
1162 * Args:
1163 * cr: the upper half-convex hull
1164 * cs: the lower half-convex hull
1165 * result: a pointer to the pre-allocated struct where the results
1166 * will be stored
1167 */
1168static void calculateFactorsFallback(GQueue* const cr, GQueue* const cs,
0a87ec9a 1169 PairFactors* const result)
08365995
BP
1170{
1171 unsigned int i, j, k;
1172 double errorMin;
1173 Factors* approx;
1174
1175 errorMin= INFINITY;
1176 approx= malloc(sizeof(Factors));
1177
1178 for (i= 0; i < cs->length; i++)
1179 {
1180 for (j= 0; j < cr->length; j++)
1181 {
1182 double error;
1183 Point p1, p2;
1184
1185 error= 0.;
1186
1187 if (((Point*) g_queue_peek_nth(cs, i))->x < ((Point*)g_queue_peek_nth(cr, j))->x)
1188 {
1189 p1= *(Point*)g_queue_peek_nth(cs, i);
1190 p2= *(Point*)g_queue_peek_nth(cr, j);
1191 }
1192 else
1193 {
1194 p1= *(Point*)g_queue_peek_nth(cr, j);
1195 p2= *(Point*)g_queue_peek_nth(cs, i);
1196 }
1197
1198 // The lower hull should be above the point
1199 for (k= 0; k < cs->length; k++)
1200 {
1201 if (jointCmp(&p1, &p2, g_queue_peek_nth(cs, k)) < 0.)
1202 {
1203 error+= verticalDistance(&p1, &p2, g_queue_peek_nth(cs, k));
1204 }
1205 }
1206
1207 // The upper hull should be below the point
1208 for (k= 0; k < cr->length; k++)
1209 {
1210 if (jointCmp(&p1, &p2, g_queue_peek_nth(cr, k)) > 0.)
1211 {
1212 error+= verticalDistance(&p1, &p2, g_queue_peek_nth(cr, k));
1213 }
1214 }
1215
1216 if (error < errorMin)
1217 {
1218 g_debug("Fallback: i= %u j= %u is a better match (error= %g)", i, j, error);
1219 approx->drift= slope(&p1, &p2);
1220 approx->offset= intercept(&p1, &p2);
1221 errorMin= error;
1222 }
1223 }
1224 }
1225
1226 result->approx= approx;
1227 result->accuracy= errorMin;
1228}
1229
1230
1231/*
1232 * Calculate the vertical distance between a line and a point
1233 *
1234 * Args:
1235 * p1, p2: Two points defining the line
1236 * point: a point
1237 *
1238 * Return:
1239 * the vertical distance
1240 */
1241static double verticalDistance(Point* p1, Point* p2, Point* const point)
1242{
1243 return fabs(slope(p1, p2) * point->x + intercept(p1, p2) - point->y);
1244}
1245
1246
1247/*
1248 * Calculate the slope between two points
1249 *
1250 * Args:
1251 * p1, p2 the two points
1252 *
1253 * Returns:
1254 * the slope
1255 */
1256static double slope(const Point* const p1, const Point* const p2)
1257{
1258 return ((double) p2->y - p1->y) / (p2->x - p1->x);
1259}
1260
1261
1262/* Calculate the y-intercept of a line that passes by two points
1263 *
1264 * Args:
1265 * p1, p2 the two points
1266 *
1267 * Returns:
1268 * the y-intercept
1269 */
1270static double intercept(const Point* const p1, const Point* const p2)
1271{
1272 return ((double) p2->y * p1->x - (double) p1->y * p2->x) / ((double) p1->x - p2->x);
1273}
1274
1275
08365995
BP
1276/*
1277 * Write the analysis-specific graph lines in the gnuplot script.
1278 *
1279 * Args:
08365995
BP
1280 * syncState: container for synchronization data
1281 * i: first trace number
1282 * j: second trace number, garanteed to be larger than i
1283 */
ab6edc6a 1284void writeAnalysisTraceTraceForePlotsCHull(SyncState* const syncState, const unsigned
8d7d16dd 1285 int i, const unsigned int j)
08365995
BP
1286{
1287 AnalysisDataCHull* analysisData;
0a87ec9a 1288 PairFactors* factorsCHull;
08365995
BP
1289
1290 analysisData= (AnalysisDataCHull*) syncState->analysisData;
1291
8d7d16dd 1292 fprintf(syncState->graphsStream,
08365995
BP
1293 "\t\"analysis_chull-%1$03d_to_%2$03d.data\" "
1294 "title \"Lower half-hull\" with linespoints "
1295 "linecolor rgb \"#015a01\" linetype 4 pointtype 8 pointsize 0.8, \\\n"
1296 "\t\"analysis_chull-%2$03d_to_%1$03d.data\" "
1297 "title \"Upper half-hull\" with linespoints "
1298 "linecolor rgb \"#003366\" linetype 4 pointtype 10 pointsize 0.8, \\\n",
1299 i, j);
1300
0a87ec9a 1301 factorsCHull= &analysisData->graphsData->allFactors->pairFactors[j][i];
08365995
BP
1302 if (factorsCHull->type == EXACT)
1303 {
8d7d16dd 1304 fprintf(syncState->graphsStream,
08365995
BP
1305 "\t%7g + %7g * x "
1306 "title \"Exact conversion\" with lines "
1307 "linecolor rgb \"black\" linetype 1, \\\n",
1308 factorsCHull->approx->offset, factorsCHull->approx->drift);
1309 }
0a87ec9a 1310 else if (factorsCHull->type == ACCURATE)
08365995 1311 {
8d7d16dd 1312 fprintf(syncState->graphsStream,
08365995
BP
1313 "\t%.2f + %.10f * x "
1314 "title \"Min conversion\" with lines "
1315 "linecolor rgb \"black\" linetype 5, \\\n",
1316 factorsCHull->min->offset, factorsCHull->min->drift);
8d7d16dd 1317 fprintf(syncState->graphsStream,
08365995
BP
1318 "\t%.2f + %.10f * x "
1319 "title \"Max conversion\" with lines "
1320 "linecolor rgb \"black\" linetype 8, \\\n",
1321 factorsCHull->max->offset, factorsCHull->max->drift);
8d7d16dd 1322 fprintf(syncState->graphsStream,
08365995
BP
1323 "\t%.2f + %.10f * x "
1324 "title \"Middle conversion\" with lines "
66eaf2eb 1325 "linecolor rgb \"black\" linetype 1, \\\n",
08365995
BP
1326 factorsCHull->approx->offset, factorsCHull->approx->drift);
1327 }
0a87ec9a 1328 else if (factorsCHull->type == APPROXIMATE)
08365995 1329 {
8d7d16dd 1330 fprintf(syncState->graphsStream,
08365995
BP
1331 "\t%.2f + %.10f * x "
1332 "title \"Fallback conversion\" with lines "
1333 "linecolor rgb \"gray60\" linetype 1, \\\n",
1334 factorsCHull->approx->offset, factorsCHull->approx->drift);
1335 }
1336 else if (factorsCHull->type == INCOMPLETE)
1337 {
1338 if (factorsCHull->min->drift != -INFINITY)
1339 {
8d7d16dd 1340 fprintf(syncState->graphsStream,
08365995
BP
1341 "\t%.2f + %.10f * x "
1342 "title \"Min conversion\" with lines "
1343 "linecolor rgb \"black\" linetype 5, \\\n",
1344 factorsCHull->min->offset, factorsCHull->min->drift);
1345 }
1346
1347 if (factorsCHull->max->drift != INFINITY)
1348 {
8d7d16dd 1349 fprintf(syncState->graphsStream,
08365995
BP
1350 "\t%.2f + %.10f * x "
1351 "title \"Max conversion\" with lines "
1352 "linecolor rgb \"black\" linetype 8, \\\n",
1353 factorsCHull->max->offset, factorsCHull->max->drift);
1354 }
1355 }
ab6edc6a 1356 else if (factorsCHull->type == FAIL)
08365995
BP
1357 {
1358 if (factorsCHull->min != NULL && factorsCHull->min->drift != -INFINITY)
1359 {
8d7d16dd 1360 fprintf(syncState->graphsStream,
08365995
BP
1361 "\t%.2f + %.10f * x "
1362 "title \"Min conversion\" with lines "
1363 "linecolor rgb \"black\" linetype 5, \\\n",
1364 factorsCHull->min->offset, factorsCHull->min->drift);
1365 }
1366
1367 if (factorsCHull->max != NULL && factorsCHull->max->drift != INFINITY)
1368 {
8d7d16dd 1369 fprintf(syncState->graphsStream,
08365995
BP
1370 "\t%.2f + %.10f * x "
1371 "title \"Max conversion\" with lines "
1372 "linecolor rgb \"black\" linetype 8, \\\n",
1373 factorsCHull->max->offset, factorsCHull->max->drift);
1374 }
1375 }
1376 else if (factorsCHull->type == ABSENT)
1377 {
1378 }
1379 else
1380 {
1381 g_assert_not_reached();
1382 }
1383}
ab6edc6a
BP
1384
1385
1386#ifdef HAVE_LIBGLPK
1387/*
1388 * Create the linear programming problem containing the constraints defined by
1389 * two half-hulls. The objective function and optimization directions are not
1390 * written.
1391 *
1392 * Args:
1393 * syncState: container for synchronization data
1394 * i: first trace number
1395 * j: second trace number, garanteed to be larger than i
1396 * Returns:
1397 * A new glp_prob*, this problem must be freed by the caller with
1398 * glp_delete_prob()
1399 */
1400static glp_prob* lpCreateProblem(GQueue* const lowerHull, GQueue* const
1401 upperHull)
1402{
1403 unsigned int it;
1404 const int zero= 0;
1405 const double zeroD= 0.;
1406 glp_prob* lp= glp_create_prob();
1407 unsigned int hullPointNb= g_queue_get_length(lowerHull) +
1408 g_queue_get_length(upperHull);
1409 GArray* iArray= g_array_sized_new(FALSE, FALSE, sizeof(int), hullPointNb +
1410 1);
1411 GArray* jArray= g_array_sized_new(FALSE, FALSE, sizeof(int), hullPointNb +
1412 1);
1413 GArray* aArray= g_array_sized_new(FALSE, FALSE, sizeof(double),
1414 hullPointNb + 1);
1415 struct {
1416 GQueue* hull;
1417 struct LPAddRowInfo rowInfo;
1418 } loopValues[2]= {
1419 {lowerHull, {lp, GLP_UP, iArray, jArray, aArray}},
1420 {upperHull, {lp, GLP_LO, iArray, jArray, aArray}},
1421 };
1422
1423 // Create the LP problem
1424 glp_term_out(GLP_OFF);
1425 if (hullPointNb > 0)
1426 {
1427 glp_add_rows(lp, hullPointNb);
1428 }
1429 glp_add_cols(lp, 2);
1430
1431 glp_set_col_name(lp, 1, "a0");
1432 glp_set_col_bnds(lp, 1, GLP_FR, 0., 0.);
1433 glp_set_col_name(lp, 2, "a1");
1434 glp_set_col_bnds(lp, 2, GLP_LO, 0., 0.);
1435
1436 // Add row constraints
1437 g_array_append_val(iArray, zero);
1438 g_array_append_val(jArray, zero);
1439 g_array_append_val(aArray, zeroD);
1440
1441 for (it= 0; it < sizeof(loopValues) / sizeof(*loopValues); it++)
1442 {
1443 g_queue_foreach(loopValues[it].hull, &gfLPAddRow,
1444 &loopValues[it].rowInfo);
1445 }
1446
1447 g_assert_cmpuint(iArray->len, ==, jArray->len);
1448 g_assert_cmpuint(jArray->len, ==, aArray->len);
1449 g_assert_cmpuint(aArray->len - 1, ==, hullPointNb * 2);
1450
1451 glp_load_matrix(lp, aArray->len - 1, &g_array_index(iArray, int, 0),
1452 &g_array_index(jArray, int, 0), &g_array_index(aArray, double, 0));
1453
1454 glp_scale_prob(lp, GLP_SF_AUTO);
1455
1456 g_array_free(iArray, TRUE);
1457 g_array_free(jArray, TRUE);
1458 g_array_free(aArray, TRUE);
1459
1460 return lp;
1461}
1462
1463
1464/*
1465 * A GFunc for g_queue_foreach(). Add constraints and bounds for one row.
1466 *
1467 * Args:
1468 * data Point*, synchronization point for which to add an LP row
1469 * (a constraint)
1470 * user_data LPAddRowInfo*
1471 */
1472static void gfLPAddRow(gpointer data, gpointer user_data)
1473{
1474 Point* p= data;
1475 struct LPAddRowInfo* rowInfo= user_data;
1476 int indexes[2];
1477 double constraints[2];
1478
1479 indexes[0]= g_array_index(rowInfo->iArray, int, rowInfo->iArray->len - 1) + 1;
1480 indexes[1]= indexes[0];
1481
1482 if (rowInfo->boundType == GLP_UP)
1483 {
1484 glp_set_row_bnds(rowInfo->lp, indexes[0], GLP_UP, 0., p->y);
1485 }
1486 else if (rowInfo->boundType == GLP_LO)
1487 {
1488 glp_set_row_bnds(rowInfo->lp, indexes[0], GLP_LO, p->y, 0.);
1489 }
1490 else
1491 {
1492 g_assert_not_reached();
1493 }
1494
1495 g_array_append_vals(rowInfo->iArray, indexes, 2);
1496 indexes[0]= 1;
1497 indexes[1]= 2;
1498 g_array_append_vals(rowInfo->jArray, indexes, 2);
1499 constraints[0]= 1.;
1500 constraints[1]= p->x;
1501 g_array_append_vals(rowInfo->aArray, constraints, 2);
1502}
1503
1504
1505/*
1506 * Calculate min or max correction factors (as possible) using an LP problem.
1507 *
1508 * Args:
1509 * lp: A linear programming problem with constraints and bounds
1510 * initialized.
1511 * direction: The type of factors desired. Use GLP_MAX for max
1512 * approximation factors (a1, the drift or slope is the
1513 * largest) and GLP_MIN in the other case.
1514 *
1515 * Returns:
1516 * If the calculation was successful, a new Factors struct. Otherwise, NULL.
1517 * The calculation will fail if the hull assumptions are not respected.
1518 */
1519static Factors* calculateFactorsLP(glp_prob* const lp, const int direction)
1520{
1521 int retval, status;
1522 Factors* factors;
1523
1524 glp_set_obj_coef(lp, 1, 0.);
1525 glp_set_obj_coef(lp, 2, 1.);
1526
1527 glp_set_obj_dir(lp, direction);
1528 retval= glp_simplex(lp, NULL);
1529 status= glp_get_status(lp);
1530
1531 if (retval == 0 && status == GLP_OPT)
1532 {
1533 factors= malloc(sizeof(Factors));
1534 factors->offset= glp_get_col_prim(lp, 1);
1535 factors->drift= glp_get_col_prim(lp, 2);
1536 }
1537 else
1538 {
1539 factors= NULL;
1540 }
1541
1542 return factors;
1543}
1544
1545
1546/*
1547 * Calculate min, max and approx correction factors (as possible) using an LP
1548 * problem.
1549 *
1550 * Args:
1551 * lp A linear programming problem with constraints and bounds
1552 * initialized.
1553 * factors Resulting factors, must be preallocated
1554 */
1555static void calculateCompleteFactorsLP(glp_prob* const lp, PairFactors* factors)
1556{
1557 factors->min= calculateFactorsLP(lp, GLP_MIN);
1558 factors->max= calculateFactorsLP(lp, GLP_MAX);
1559
1560 if (factors->min && factors->max)
1561 {
1562 factors->type= ACCURATE;
1563 calculateFactorsMiddle(factors);
1564 }
1565 else if (factors->min || factors->max)
1566 {
1567 factors->type= INCOMPLETE;
1568 }
1569 else
1570 {
1571 factors->type= ABSENT;
1572 }
1573}
1574
1575
1576/*
1577 * A GFunc for g_queue_foreach()
1578 *
1579 * Args:
1580 * data Point*, a convex hull point
1581 * user_data GArray*, an array of convex hull point absisca values, as
1582 * uint64_t
1583 */
1584static void gfAddAbsiscaToArray(gpointer data, gpointer user_data)
1585{
1586 Point* p= data;
1587 GArray* a= user_data;
1588 uint64_t v= p->x;
1589
1590 g_array_append_val(a, v);
1591}
1592
1593
1594/*
1595 * A GCompareFunc for g_array_sort()
1596 *
1597 * Args:
1598 * a, b uint64_t*, absisca values
1599 *
1600 * Returns:
1601 * "returns less than zero for first arg is less than second arg, zero for
1602 * equal, greater zero if first arg is greater than second arg"
1603 * - the great glib documentation
1604 */
1605static gint gcfCompareUint64(gconstpointer a, gconstpointer b)
1606{
1607 if (*(uint64_t*) a < *(uint64_t*) b)
1608 {
1609 return -1;
1610 }
1611 else if (*(uint64_t*) a > *(uint64_t*) b)
1612 {
1613 return 1;
1614 }
1615 else
1616 {
1617 return 0;
1618 }
1619}
1620
1621
1622/*
1623 * Compute synchronization factors using a linear programming approach.
1624 *
1625 * Args:
1626 * syncState: container for synchronization data
1627 */
1628static AllFactors* finalizeAnalysisCHullLP(SyncState* const syncState)
1629{
1630 AnalysisDataCHull* analysisData= syncState->analysisData;
1631 unsigned int i, j;
1632 AllFactors* lpFactorsArray;
1633
1634 lpFactorsArray= createAllFactors(syncState->traceNb);
1635
1636 analysisData->lps= malloc(syncState->traceNb * sizeof(glp_prob**));
1637 for (i= 0; i < syncState->traceNb; i++)
1638 {
1639 analysisData->lps[i]= malloc(i * sizeof(glp_prob*));
1640 }
1641
1642 for (i= 0; i < syncState->traceNb; i++)
1643 {
1644 for (j= 0; j < i; j++)
1645 {
1646 glp_prob* lp;
1647 unsigned int it;
1648 GQueue*** hullArray= analysisData->hullArray;
1649 PairFactors* lpFactors= &lpFactorsArray->pairFactors[i][j];
1650
1651 // Create the LP problem
1652 lp= lpCreateProblem(hullArray[i][j], hullArray[j][i]);
1653 analysisData->lps[i][j]= lp;
1654
1655 // Use the LP problem to find the correction factors for this pair of
1656 // traces
1657 calculateCompleteFactorsLP(lp, lpFactors);
1658
1659 // If possible, compute synchronization accuracy information for
1660 // graphs
1661 if (syncState->graphsStream && lpFactors->type == ACCURATE)
1662 {
1663 int retval;
1664 char* cwd;
1665 char fileName[43];
1666 FILE* fp;
1667 GArray* xValues;
1668
1669 // Open the data file
1670 snprintf(fileName, sizeof(fileName),
1671 "analysis_chull_accuracy-%03u_and_%03u.data", j, i);
1672 fileName[sizeof(fileName) - 1]= '\0';
1673
1674 cwd= changeToGraphsDir(syncState->graphsDir);
1675
1676 if ((fp= fopen(fileName, "w")) == NULL)
1677 {
1678 g_error(strerror(errno));
1679 }
1680 fprintf(fp, "#%-24s %-25s %-25s %-25s\n", "x", "middle", "min", "max");
1681
1682 retval= chdir(cwd);
1683 if (retval == -1)
1684 {
1685 g_error(strerror(errno));
1686 }
1687 free(cwd);
1688
1689 // Build the list of absisca values for the points in the accuracy graph
1690 xValues= g_array_sized_new(FALSE, FALSE, sizeof(uint64_t),
1691 g_queue_get_length(hullArray[i][j]) +
1692 g_queue_get_length(hullArray[j][i]));
1693
1694 g_queue_foreach(hullArray[i][j], &gfAddAbsiscaToArray, xValues);
1695 g_queue_foreach(hullArray[j][i], &gfAddAbsiscaToArray, xValues);
1696
1697 g_array_sort(xValues, &gcfCompareUint64);
1698
1699 /* For each absisca value and each optimisation direction, solve the LP
1700 * and write a line in the data file */
1701 for (it= 0; it < xValues->len; it++)
1702 {
1703 uint64_t time;
1704 CorrectedTime correctedTime;
1705
1706 time= g_array_index(xValues, uint64_t, it);
1707 timeCorrectionLP(lp, lpFactors, time, &correctedTime);
1708 fprintf(fp, "%24" PRIu64 " %24" PRIu64 " %24" PRIu64
1709 "%24" PRIu64 "\n", time, correctedTime.time,
1710 correctedTime.min, correctedTime.max);
1711 }
1712
1713 g_array_free(xValues, TRUE);
1714 fclose(fp);
1715 }
1716 }
1717 }
1718
1719 if (syncState->stats)
1720 {
1721 lpFactorsArray->refCount++;
1722 analysisData->stats->lpFactors= lpFactorsArray;
1723 }
1724
1725 if (syncState->graphsStream)
1726 {
1727 lpFactorsArray->refCount++;
1728 analysisData->graphsData->lpFactors= lpFactorsArray;
1729 }
1730
1731 return lpFactorsArray;
1732}
1733
1734
1735/*
1736 * Perform correction on one time value and calculate accuracy bounds.
1737 *
1738 * Args:
1739 * lp: Linear Programming problem containing the coefficients for
1740 * the trace pair between which to perform time correction.
1741 * lpFactors: Correction factors for this trace pair, the factors must be
1742 * of type ACCURATE.
1743 * time: Time value to correct.
1744 * correctedTime: Result of the time correction, preallocated.
1745 */
1746void timeCorrectionLP(glp_prob* const lp, const PairFactors* const lpFactors,
1747 const uint64_t time, CorrectedTime* const correctedTime)
1748{
1749 unsigned int it;
1750 const struct
1751 {
1752 int direction;
1753 size_t offset;
1754 } loopValues[]= {
1755 {GLP_MIN, offsetof(CorrectedTime, min)},
1756 {GLP_MAX, offsetof(CorrectedTime, max)}
1757 };
1758
1759 glp_set_obj_coef(lp, 1, 1.);
1760 glp_set_obj_coef(lp, 2, time);
1761
1762 g_assert(lpFactors->type == ACCURATE);
1763
1764 correctedTime->time= lpFactors->approx->offset + lpFactors->approx->drift
1765 * time;
1766
1767 for (it= 0; it < ARRAY_SIZE(loopValues); it++)
1768 {
1769 int status;
1770 int retval;
1771
1772 glp_set_obj_dir(lp, loopValues[it].direction);
1773 retval= glp_simplex(lp, NULL);
1774 status= glp_get_status(lp);
1775
1776 g_assert(retval == 0 && status == GLP_OPT);
1777 *(uint64_t*) ((void*) correctedTime + loopValues[it].offset)=
1778 round(glp_get_obj_val(lp));
1779 }
1780}
1781
1782
1783/*
1784 * Write the analysis-specific graph lines in the gnuplot script.
1785 *
1786 * Args:
1787 * syncState: container for synchronization data
1788 * i: first trace number
1789 * j: second trace number, garanteed to be larger than i
1790 */
1791static void writeAnalysisTraceTimeBackPlotsCHull(SyncState* const syncState,
1792 const unsigned int i, const unsigned int j)
1793{
1794 if (((AnalysisDataCHull*)
1795 syncState->analysisData)->graphsData->lpFactors->pairFactors[j][i].type
1796 == ACCURATE)
1797 {
1798 fprintf(syncState->graphsStream,
1799 "\t\"analysis_chull_accuracy-%1$03u_and_%2$03u.data\" "
1800 "using 1:(($3 - $2) / clock_freq_%2$u):(($4 - $2) / clock_freq_%2$u) "
1801 "title \"Synchronization accuracy\" "
1802 "with filledcurves linewidth 2 linetype 1 "
1803 "linecolor rgb \"black\" fill solid 0.25 noborder, \\\n", i,
1804 j);
1805 }
1806}
1807
1808
1809/*
1810 * Write the analysis-specific graph lines in the gnuplot script.
1811 *
1812 * Args:
1813 * syncState: container for synchronization data
1814 * i: first trace number
1815 * j: second trace number, garanteed to be larger than i
1816 */
1817static void writeAnalysisTraceTimeForePlotsCHull(SyncState* const syncState,
1818 const unsigned int i, const unsigned int j)
1819{
1820 if (((AnalysisDataCHull*)
1821 syncState->analysisData)->graphsData->lpFactors->pairFactors[j][i].type
1822 == ACCURATE)
1823 {
1824 fprintf(syncState->graphsStream,
1825 "\t\"analysis_chull_accuracy-%1$03u_and_%2$03u.data\" "
1826 "using 1:(($3 - $2) / clock_freq_%2$u) notitle "
1827 "with lines linewidth 2 linetype 1 "
1828 "linecolor rgb \"gray60\", \\\n"
1829 "\t\"analysis_chull_accuracy-%1$03u_and_%2$03u.data\" "
1830 "using 1:(($4 - $2) / clock_freq_%2$u) notitle "
1831 "with lines linewidth 2 linetype 1 "
1832 "linecolor rgb \"gray60\", \\\n", i, j);
1833 }
1834}
1835
1836
1837/*
1838 * Write the analysis-specific graph lines in the gnuplot script.
1839 *
1840 * Args:
1841 * syncState: container for synchronization data
1842 * i: first trace number
1843 * j: second trace number, garanteed to be larger than i
1844 */
1845static void writeAnalysisTraceTraceBackPlotsCHull(SyncState* const syncState,
1846 const unsigned int i, const unsigned int j)
1847{
1848 if (((AnalysisDataCHull*)
1849 syncState->analysisData)->graphsData->lpFactors->pairFactors[j][i].type
1850 == ACCURATE)
1851 {
1852 fprintf(syncState->graphsStream,
1853 "\t\"analysis_chull_accuracy-%1$03u_and_%2$03u.data\" "
1854 "using 1:3:4 "
1855 "title \"Synchronization accuracy\" "
1856 "with filledcurves linewidth 2 linetype 1 "
1857 "linecolor rgb \"black\" fill solid 0.25 noborder, \\\n", i, j);
1858 }
1859}
1860#endif
This page took 0.099539 seconds and 4 git commands to generate.