Generate graphs of synchronization accuracy
[lttv.git] / lttv / lttv / sync / event_analysis_eval.c
CommitLineData
cdce23b3
BP
1/* This file is part of the Linux Trace Toolkit viewer
2 * Copyright (C) 2009 Benjamin Poirier <benjamin.poirier@polymtl.ca>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License Version 2 as
6 * published by the Free Software Foundation;
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
16 * MA 02111-1307, USA.
17 */
18
2bd4b3e4 19#define _GNU_SOURCE
d4721e1a 20#define _ISOC99_SOURCE
2bd4b3e4 21
cdce23b3
BP
22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
25
2bd4b3e4
BP
26#include <arpa/inet.h>
27#include <errno.h>
76be6fc2 28#include <math.h>
2bd4b3e4
BP
29#include <netinet/in.h>
30#include <stddef.h>
cdce23b3 31#include <stdlib.h>
2bd4b3e4
BP
32#include <stdio.h>
33#include <string.h>
34#include <sys/socket.h>
4ee223e5 35#include <unistd.h>
cdce23b3 36
2bd4b3e4
BP
37#include "lookup3.h"
38#include "sync_chain.h"
66eaf2eb 39#include "event_analysis_chull.h"
cdce23b3
BP
40
41#include "event_analysis_eval.h"
42
43
66eaf2eb 44struct WriteHistogramInfo
e072e1ab
BP
45{
46 GHashTable* rttInfo;
47 FILE* graphsStream;
48};
49
66eaf2eb
BP
50#ifdef HAVE_LIBGLPK
51struct LPAddRowInfo
52{
53 glp_prob* lp;
54 int boundType;
55 GArray* iArray, * jArray, * aArray;
56};
57#endif
e072e1ab 58
cdce23b3
BP
59// Functions common to all analysis modules
60static void initAnalysisEval(SyncState* const syncState);
61static void destroyAnalysisEval(SyncState* const syncState);
62
63static void analyzeMessageEval(SyncState* const syncState, Message* const
64 message);
65static void analyzeExchangeEval(SyncState* const syncState, Exchange* const
66 exchange);
67static void analyzeBroadcastEval(SyncState* const syncState, Broadcast* const
68 broadcast);
69static GArray* finalizeAnalysisEval(SyncState* const syncState);
70static void printAnalysisStatsEval(SyncState* const syncState);
66eaf2eb
BP
71static void writeAnalysisTraceTimePlotsEval(SyncState* const syncState, const
72 unsigned int i, const unsigned int j);
73static void writeAnalysisTraceTracePlotsEval(SyncState* const syncState, const
74 unsigned int i, const unsigned int j);
cdce23b3
BP
75
76// Functions specific to this module
77static void registerAnalysisEval() __attribute__((constructor (102)));
2bd4b3e4
BP
78static guint ghfRttKeyHash(gconstpointer key);
79static gboolean gefRttKeyEqual(gconstpointer a, gconstpointer b);
80static void gdnDestroyRttKey(gpointer data);
81static void gdnDestroyDouble(gpointer data);
82static void readRttInfo(GHashTable* rttInfo, FILE* rttFile);
83static void positionStream(FILE* stream);
cdce23b3 84
76be6fc2
BP
85static void gfSum(gpointer data, gpointer userData);
86static void gfSumSquares(gpointer data, gpointer userData);
66eaf2eb
BP
87static void ghfPrintExchangeRtt(gpointer key, gpointer value, gpointer
88 user_data);
76be6fc2 89
e072e1ab 90static void hitBin(struct Bins* const bins, const double value);
4ee223e5
BP
91static unsigned int binNum(const double value) __attribute__((pure));
92static double binStart(const unsigned int binNum) __attribute__((pure));
93static double binEnd(const unsigned int binNum) __attribute__((pure));
467066ee 94static uint32_t normalTotal(struct Bins* const bins) __attribute__((const));
4ee223e5 95
66eaf2eb 96static AnalysisHistogramEval* constructAnalysisHistogramEval(const char* const
e072e1ab 97 graphsDir, const struct RttKey* const rttKey);
66eaf2eb
BP
98static void destroyAnalysisHistogramEval(AnalysisHistogramEval* const
99 histogram);
100static void gdnDestroyAnalysisHistogramEval(gpointer data);
101static void ghfWriteHistogram(gpointer key, gpointer value, gpointer
102 user_data);
e072e1ab
BP
103static void dumpBinToFile(const struct Bins* const bins, FILE* const file);
104static void writeHistogram(FILE* graphsStream, const struct RttKey* rttKey,
66eaf2eb
BP
105 double* minRtt, AnalysisHistogramEval* const histogram);
106
107static void updateBounds(Bounds** const bounds, Event* const e1, Event* const e2);
108
109// The next group of functions is only needed when computing synchronization
110// accuracy.
111#ifdef HAVE_LIBGLPK
112static glp_prob* lpCreateProblem(GQueue* const lowerHull, GQueue* const upperHull);
113static void gfLPAddRow(gpointer data, gpointer user_data);
114static Factors* calculateFactors(glp_prob* const lp, const int direction);
115static void calculateCompleteFactors(glp_prob* const lp, FactorsCHull* factors);
116static FactorsCHull** createAllFactors(const unsigned int traceNb);
117static inline void finalizeAnalysisEvalLP(SyncState* const syncState);
118#else
119static void finalizeAnalysisEvalLP(SyncState* const syncState);
120#endif
e072e1ab 121
4ee223e5 122
66eaf2eb 123// initialized in registerAnalysisEval()
4ee223e5 124double binBase;
cdce23b3
BP
125
126static AnalysisModule analysisModuleEval= {
127 .name= "eval",
128 .initAnalysis= &initAnalysisEval,
129 .destroyAnalysis= &destroyAnalysisEval,
130 .analyzeMessage= &analyzeMessageEval,
131 .analyzeExchange= &analyzeExchangeEval,
132 .analyzeBroadcast= &analyzeBroadcastEval,
133 .finalizeAnalysis= &finalizeAnalysisEval,
134 .printAnalysisStats= &printAnalysisStatsEval,
66eaf2eb
BP
135 .graphFunctions= {
136 .writeTraceTimePlots= &writeAnalysisTraceTimePlotsEval,
137 .writeTraceTracePlots= &writeAnalysisTraceTracePlotsEval,
138 }
cdce23b3
BP
139};
140
2bd4b3e4
BP
141static ModuleOption optionEvalRttFile= {
142 .longName= "eval-rtt-file",
143 .hasArg= REQUIRED_ARG,
144 {.arg= NULL},
4ee223e5 145 .optionHelp= "specify the file containing RTT information",
2bd4b3e4
BP
146 .argHelp= "FILE",
147};
148
cdce23b3
BP
149
150/*
151 * Analysis module registering function
152 */
153static void registerAnalysisEval()
154{
66eaf2eb
BP
155 binBase= exp10(6. / (BIN_NB - 3));
156
cdce23b3 157 g_queue_push_tail(&analysisModules, &analysisModuleEval);
2bd4b3e4 158 g_queue_push_tail(&moduleOptions, &optionEvalRttFile);
cdce23b3
BP
159}
160
161
162/*
163 * Analysis init function
164 *
165 * This function is called at the beginning of a synchronization run for a set
166 * of traces.
167 *
168 * Args:
169 * syncState container for synchronization data.
170 */
171static void initAnalysisEval(SyncState* const syncState)
172{
173 AnalysisDataEval* analysisData;
66eaf2eb 174 unsigned int i, j;
cdce23b3
BP
175
176 analysisData= malloc(sizeof(AnalysisDataEval));
177 syncState->analysisData= analysisData;
178
2bd4b3e4
BP
179 analysisData->rttInfo= g_hash_table_new_full(&ghfRttKeyHash,
180 &gefRttKeyEqual, &gdnDestroyRttKey, &gdnDestroyDouble);
181 if (optionEvalRttFile.arg)
182 {
183 FILE* rttStream;
184 int retval;
185
186 rttStream= fopen(optionEvalRttFile.arg, "r");
187 if (rttStream == NULL)
188 {
189 g_error(strerror(errno));
190 }
191
192 readRttInfo(analysisData->rttInfo, rttStream);
193
194 retval= fclose(rttStream);
195 if (retval == EOF)
196 {
197 g_error(strerror(errno));
198 }
199 }
cdce23b3
BP
200
201 if (syncState->stats)
202 {
76be6fc2 203 analysisData->stats= calloc(1, sizeof(AnalysisStatsEval));
cdce23b3
BP
204 analysisData->stats->broadcastDiffSum= 0.;
205
76be6fc2
BP
206 analysisData->stats->messageStats= malloc(syncState->traceNb *
207 sizeof(MessageStats*));
cdce23b3
BP
208 for (i= 0; i < syncState->traceNb; i++)
209 {
76be6fc2
BP
210 analysisData->stats->messageStats[i]= calloc(syncState->traceNb,
211 sizeof(MessageStats));
cdce23b3 212 }
d4721e1a
BP
213
214 analysisData->stats->exchangeRtt=
215 g_hash_table_new_full(&ghfRttKeyHash, &gefRttKeyEqual,
216 &gdnDestroyRttKey, &gdnDestroyDouble);
66eaf2eb
BP
217
218#ifdef HAVE_LIBGLPK
219 analysisData->stats->chFactorsArray= NULL;
220 analysisData->stats->lpFactorsArray= NULL;
221#endif
cdce23b3 222 }
4ee223e5 223
8d7d16dd 224 if (syncState->graphsStream)
4ee223e5 225 {
66eaf2eb
BP
226 AnalysisGraphsEval* graphs= malloc(sizeof(AnalysisGraphsEval));
227
228 analysisData->graphs= graphs;
229
230 graphs->histograms= g_hash_table_new_full(&ghfRttKeyHash,
231 &gefRttKeyEqual, &gdnDestroyRttKey,
232 &gdnDestroyAnalysisHistogramEval);
233
234 graphs->bounds= malloc(syncState->traceNb * sizeof(Bounds*));
235 for (i= 0; i < syncState->traceNb; i++)
236 {
237 graphs->bounds[i]= malloc(i * sizeof(Bounds));
238 for (j= 0; j < i; j++)
239 {
240 graphs->bounds[i][j].min= UINT64_MAX;
241 graphs->bounds[i][j].max= 0;
242 }
243 }
244
245#ifdef HAVE_LIBGLPK
246 graphs->lps= NULL;
247 graphs->lpFactorsArray= NULL;
248#endif
249 }
250
251 if (syncState->stats || syncState->graphsStream)
252 {
253 GList* result;
254
255 analysisData->chullSS= malloc(sizeof(SyncState));
256 memcpy(analysisData->chullSS, syncState, sizeof(SyncState));
257 analysisData->chullSS->stats= false;
258 analysisData->chullSS->analysisData= NULL;
259 result= g_queue_find_custom(&analysisModules, "chull",
260 &gcfCompareAnalysis);
261 analysisData->chullSS->analysisModule= (AnalysisModule*) result->data;
262 analysisData->chullSS->analysisModule->initAnalysis(analysisData->chullSS);
4ee223e5
BP
263 }
264}
265
266
267/*
e072e1ab
BP
268 * Create and open files used to store histogram points to generate graphs.
269 * Create data structures to store histogram points during analysis.
4ee223e5
BP
270 *
271 * Args:
e072e1ab
BP
272 * graphsDir: folder where to write files
273 * rttKey: host pair, make sure saddr < daddr
4ee223e5 274 */
66eaf2eb 275static AnalysisHistogramEval* constructAnalysisHistogramEval(const char* const
e072e1ab 276 graphsDir, const struct RttKey* const rttKey)
4ee223e5 277{
4ee223e5 278 int retval;
e072e1ab 279 unsigned int i;
4ee223e5 280 char* cwd;
e072e1ab 281 char name[60], saddr[16], daddr[16];
66eaf2eb 282 AnalysisHistogramEval* histogram= calloc(1, sizeof(*histogram));
e072e1ab
BP
283 const struct {
284 size_t pointsOffset;
285 const char* fileName;
286 const char* host1, *host2;
287 } loopValues[]= {
66eaf2eb
BP
288 {offsetof(AnalysisHistogramEval, ttSendPoints),
289 "analysis_eval_tt-%s_to_%s.data", saddr, daddr},
290 {offsetof(AnalysisHistogramEval, ttRecvPoints),
291 "analysis_eval_tt-%s_to_%s.data", daddr, saddr},
292 {offsetof(AnalysisHistogramEval, hrttPoints),
293 "analysis_eval_hrtt-%s_and_%s.data", saddr, daddr},
e072e1ab
BP
294 };
295
66eaf2eb
BP
296 histogram->ttSendBins.min= BIN_NB - 1;
297 histogram->ttRecvBins.min= BIN_NB - 1;
298 histogram->hrttBins.min= BIN_NB - 1;
e072e1ab
BP
299
300 convertIP(saddr, rttKey->saddr);
301 convertIP(daddr, rttKey->daddr);
302
303 cwd= changeToGraphDir(graphsDir);
304
305 for (i= 0; i < sizeof(loopValues) / sizeof(*loopValues); i++)
4ee223e5 306 {
e072e1ab
BP
307 retval= snprintf(name, sizeof(name), loopValues[i].fileName,
308 loopValues[i].host1, loopValues[i].host2);
309 if (retval > sizeof(name) - 1)
4ee223e5 310 {
e072e1ab 311 name[sizeof(name) - 1]= '\0';
4ee223e5 312 }
66eaf2eb 313 if ((*(FILE**)((void*) histogram + loopValues[i].pointsOffset)=
e072e1ab 314 fopen(name, "w")) == NULL)
4ee223e5 315 {
e072e1ab 316 g_error(strerror(errno));
4ee223e5
BP
317 }
318 }
319
320 retval= chdir(cwd);
321 if (retval == -1)
322 {
323 g_error(strerror(errno));
324 }
325 free(cwd);
e072e1ab 326
66eaf2eb 327 return histogram;
4ee223e5
BP
328}
329
330
331/*
e072e1ab 332 * Close files used to store histogram points to generate graphs.
4ee223e5
BP
333 *
334 * Args:
e072e1ab
BP
335 * graphsDir: folder where to write files
336 * rttKey: host pair, make sure saddr < daddr
4ee223e5 337 */
66eaf2eb
BP
338static void destroyAnalysisHistogramEval(AnalysisHistogramEval* const
339 histogram)
4ee223e5 340{
e072e1ab
BP
341 unsigned int i;
342 int retval;
343 const struct {
344 size_t pointsOffset;
345 } loopValues[]= {
66eaf2eb
BP
346 {offsetof(AnalysisHistogramEval, ttSendPoints)},
347 {offsetof(AnalysisHistogramEval, ttRecvPoints)},
348 {offsetof(AnalysisHistogramEval, hrttPoints)},
e072e1ab
BP
349 };
350
351 for (i= 0; i < sizeof(loopValues) / sizeof(*loopValues); i++)
4ee223e5 352 {
66eaf2eb 353 retval= fclose(*(FILE**)((void*) histogram + loopValues[i].pointsOffset));
e072e1ab 354 if (retval != 0)
4ee223e5 355 {
e072e1ab 356 g_error(strerror(errno));
4ee223e5
BP
357 }
358 }
66eaf2eb
BP
359
360 free(histogram);
4ee223e5
BP
361}
362
363
e072e1ab
BP
364/*
365 * A GDestroyNotify function for g_hash_table_new_full()
366 *
367 * Args:
66eaf2eb 368 * data: AnalysisHistogramEval*
e072e1ab 369 */
66eaf2eb 370static void gdnDestroyAnalysisHistogramEval(gpointer data)
e072e1ab 371{
66eaf2eb 372 destroyAnalysisHistogramEval(data);
e072e1ab
BP
373}
374
375
376/*
377 * A GHFunc for g_hash_table_foreach()
378 *
379 * Args:
380 * key: RttKey* where saddr < daddr
66eaf2eb
BP
381 * value: AnalysisHistogramEval*
382 * user_data struct WriteHistogramInfo*
e072e1ab 383 */
66eaf2eb 384static void ghfWriteHistogram(gpointer key, gpointer value, gpointer user_data)
e072e1ab
BP
385{
386 double* rtt1, * rtt2;
387 struct RttKey* rttKey= key;
388 struct RttKey oppositeRttKey= {.saddr= rttKey->daddr, .daddr=
389 rttKey->saddr};
66eaf2eb
BP
390 AnalysisHistogramEval* histogram= value;
391 struct WriteHistogramInfo* info= user_data;
e072e1ab
BP
392
393 rtt1= g_hash_table_lookup(info->rttInfo, rttKey);
394 rtt2= g_hash_table_lookup(info->rttInfo, &oppositeRttKey);
395
396 if (rtt1 == NULL)
397 {
398 rtt1= rtt2;
399 }
400 else if (rtt2 != NULL)
401 {
402 rtt1= MIN(rtt1, rtt2);
403 }
404
66eaf2eb
BP
405 dumpBinToFile(&histogram->ttSendBins, histogram->ttSendPoints);
406 dumpBinToFile(&histogram->ttRecvBins, histogram->ttRecvPoints);
407 dumpBinToFile(&histogram->hrttBins, histogram->hrttPoints);
408 writeHistogram(info->graphsStream, rttKey, rtt1, histogram);
e072e1ab
BP
409}
410
411
4ee223e5
BP
412/*
413 * Write the content of one bin in a histogram point file
414 *
415 * Args:
416 * bin: array of values that make up a histogram
e072e1ab 417 * file: FILE*, write to this file
4ee223e5 418 */
e072e1ab 419static void dumpBinToFile(const struct Bins* const bins, FILE* const file)
4ee223e5
BP
420{
421 unsigned int i;
422
e072e1ab
BP
423 // The first and last bins are skipped, see struct Bins
424 for (i= 1; i < BIN_NB - 1; i++)
4ee223e5 425 {
e072e1ab 426 if (bins->bin[i] > 0)
4ee223e5 427 {
e072e1ab
BP
428 fprintf(file, "%20.9f %20.9f %20.9f\n", (binStart(i) + binEnd(i))
429 / 2., (double) bins->bin[i] / ((binEnd(i) - binStart(i)) *
430 bins->total), binEnd(i) - binStart(i));
4ee223e5
BP
431 }
432 }
433}
434
435
436/*
e072e1ab 437 * Write the analysis-specific plot in the gnuplot script.
4ee223e5
BP
438 *
439 * Args:
e072e1ab
BP
440 * graphsStream: write to this file
441 * rttKey: must be sorted such that saddr < daddr
442 * minRtt: if available, else NULL
66eaf2eb 443 * histogram: struct that contains the bins for the pair of traces
467066ee 444 * identified by rttKey
4ee223e5 445 */
e072e1ab 446static void writeHistogram(FILE* graphsStream, const struct RttKey* rttKey,
66eaf2eb 447 double* minRtt, AnalysisHistogramEval* const histogram)
4ee223e5 448{
e072e1ab 449 char saddr[16], daddr[16];
4ee223e5 450
e072e1ab
BP
451 convertIP(saddr, rttKey->saddr);
452 convertIP(daddr, rttKey->daddr);
4ee223e5 453
e072e1ab 454 fprintf(graphsStream,
66eaf2eb 455 "\nreset\n"
e072e1ab
BP
456 "set output \"histogram-%s-%s.eps\"\n"
457 "set title \"\"\n"
458 "set xlabel \"Message Latency (s)\"\n"
459 "set ylabel \"Proportion of messages per second\"\n", saddr, daddr);
4ee223e5 460
e072e1ab 461 if (minRtt != NULL)
4ee223e5 462 {
e072e1ab
BP
463 fprintf(graphsStream,
464 "set arrow from %.9f, 0 rto 0, graph 1 "
467066ee
BP
465 "nohead linetype 3 linewidth 3 linecolor rgb \"black\"\n", *minRtt
466 / 2);
4ee223e5 467 }
4ee223e5 468
66eaf2eb
BP
469 if (normalTotal(&histogram->ttSendBins) ||
470 normalTotal(&histogram->ttRecvBins) ||
471 normalTotal(&histogram->hrttBins))
467066ee
BP
472 {
473 fprintf(graphsStream, "plot \\\n");
474
66eaf2eb 475 if (normalTotal(&histogram->hrttBins))
467066ee
BP
476 {
477 fprintf(graphsStream,
478 "\t\"analysis_eval_hrtt-%s_and_%s.data\" "
479 "title \"RTT/2\" with linespoints linetype 1 linewidth 2 "
480 "linecolor rgb \"black\" pointtype 6 pointsize 1,\\\n",
481 saddr, daddr);
482 }
483
66eaf2eb 484 if (normalTotal(&histogram->ttSendBins))
467066ee
BP
485 {
486 fprintf(graphsStream,
487 "\t\"analysis_eval_tt-%1$s_to_%2$s.data\" "
488 "title \"%1$s to %2$s\" with linespoints linetype 4 linewidth 2 "
489 "linecolor rgb \"gray60\" pointtype 6 pointsize 1,\\\n",
490 saddr, daddr);
491 }
492
66eaf2eb 493 if (normalTotal(&histogram->ttRecvBins))
467066ee
BP
494 {
495 fprintf(graphsStream,
496 "\t\"analysis_eval_tt-%1$s_to_%2$s.data\" "
497 "title \"%1$s to %2$s\" with linespoints linetype 4 linewidth 2 "
498 "linecolor rgb \"gray30\" pointtype 6 pointsize 1,\\\n",
499 daddr, saddr);
500 }
501
502 // Remove the ",\\\n" from the last graph plot line
503 if (ftruncate(fileno(graphsStream), ftell(graphsStream) - 3) == -1)
504 {
505 g_error(strerror(errno));
506 }
507 if (fseek(graphsStream, 0, SEEK_END) == -1)
508 {
509 g_error(strerror(errno));
510 }
511 fprintf(graphsStream, "\n");
512 }
cdce23b3
BP
513}
514
515
516/*
517 * Analysis destroy function
518 *
519 * Free the analysis specific data structures
520 *
521 * Args:
522 * syncState container for synchronization data.
523 */
524static void destroyAnalysisEval(SyncState* const syncState)
525{
66eaf2eb 526 unsigned int i, j;
cdce23b3
BP
527 AnalysisDataEval* analysisData;
528
529 analysisData= (AnalysisDataEval*) syncState->analysisData;
530
66eaf2eb 531 if (analysisData == NULL)
cdce23b3
BP
532 {
533 return;
534 }
535
2bd4b3e4 536 g_hash_table_destroy(analysisData->rttInfo);
cdce23b3
BP
537
538 if (syncState->stats)
539 {
66eaf2eb
BP
540 AnalysisStatsEval* stats= analysisData->stats;
541
542 for (i= 0; i < syncState->traceNb; i++)
543 {
544 free(stats->messageStats[i]);
545 }
546 free(stats->messageStats);
547
548 g_hash_table_destroy(stats->exchangeRtt);
549
550#ifdef HAVE_LIBGLPK
551 freeAllFactors(syncState->traceNb, stats->chFactorsArray);
552 freeAllFactors(syncState->traceNb, stats->lpFactorsArray);
553#endif
554
555 free(stats);
556 }
557
558 if (syncState->graphsStream)
559 {
560 AnalysisGraphsEval* graphs= analysisData->graphs;
561
562 if (graphs->histograms)
563 {
564 g_hash_table_destroy(graphs->histograms);
565 }
566
cdce23b3
BP
567 for (i= 0; i < syncState->traceNb; i++)
568 {
66eaf2eb
BP
569 free(graphs->bounds[i]);
570 }
571 free(graphs->bounds);
572
573#ifdef HAVE_LIBGLPK
574 for (i= 0; i < syncState->traceNb; i++)
575 {
576 for (j= 0; j < i; j++)
577 {
578 // There seems to be a memory leak in glpk, valgrind reports a
579 // loss even if the problem is deleted
580 glp_delete_prob(graphs->lps[i][j]);
581 }
582 free(graphs->lps[i]);
cdce23b3 583 }
66eaf2eb 584 free(graphs->lps);
d4721e1a 585
66eaf2eb
BP
586 if (!syncState->stats)
587 {
588 freeAllFactors(syncState->traceNb, graphs->lpFactorsArray);
589 }
590#endif
d4721e1a 591
66eaf2eb 592 free(graphs);
cdce23b3
BP
593 }
594
66eaf2eb 595 if (syncState->stats || syncState->graphsStream)
4ee223e5 596 {
66eaf2eb
BP
597 analysisData->chullSS->analysisModule->destroyAnalysis(analysisData->chullSS);
598 free(analysisData->chullSS);
4ee223e5
BP
599 }
600
cdce23b3
BP
601 free(syncState->analysisData);
602 syncState->analysisData= NULL;
603}
604
605
606/*
607 * Perform analysis on an event pair.
608 *
76be6fc2
BP
609 * Check if there is message inversion or messages that are too fast.
610 *
cdce23b3
BP
611 * Args:
612 * syncState container for synchronization data
613 * message structure containing the events
614 */
66eaf2eb
BP
615static void analyzeMessageEval(SyncState* const syncState, Message* const
616 message)
cdce23b3 617{
e072e1ab
BP
618 AnalysisDataEval* analysisData= syncState->analysisData;
619 MessageStats* messageStats=
66eaf2eb 620 &analysisData->stats->messageStats[message->outE->traceNum][message->inE->traceNum];
d4721e1a 621 double* rtt;
76be6fc2
BP
622 double tt;
623 struct RttKey rttKey;
624
e072e1ab 625 g_assert(message->inE->type == TCP);
76be6fc2 626
66eaf2eb
BP
627 if (syncState->stats)
628 {
629 messageStats->total++;
630 }
76be6fc2
BP
631
632 tt= wallTimeSub(&message->inE->wallTime, &message->outE->wallTime);
633 if (tt <= 0)
634 {
66eaf2eb
BP
635 if (syncState->stats)
636 {
637 messageStats->inversionNb++;
638 }
76be6fc2 639 }
8d7d16dd 640 else if (syncState->graphsStream)
4ee223e5 641 {
e072e1ab
BP
642 struct RttKey rttKey= {
643 .saddr=MIN(message->inE->event.tcpEvent->segmentKey->connectionKey.saddr,
644 message->inE->event.tcpEvent->segmentKey->connectionKey.daddr),
645 .daddr=MAX(message->inE->event.tcpEvent->segmentKey->connectionKey.saddr,
646 message->inE->event.tcpEvent->segmentKey->connectionKey.daddr),
647 };
66eaf2eb
BP
648 AnalysisHistogramEval* histogram=
649 g_hash_table_lookup(analysisData->graphs->histograms, &rttKey);
e072e1ab 650
66eaf2eb 651 if (histogram == NULL)
e072e1ab
BP
652 {
653 struct RttKey* tableKey= malloc(sizeof(*tableKey));
654
66eaf2eb 655 histogram= constructAnalysisHistogramEval(syncState->graphsDir, &rttKey);
e072e1ab 656 memcpy(tableKey, &rttKey, sizeof(*tableKey));
66eaf2eb 657 g_hash_table_insert(analysisData->graphs->histograms, tableKey, histogram);
e072e1ab
BP
658 }
659
660 if (message->inE->event.udpEvent->datagramKey->saddr <
661 message->inE->event.udpEvent->datagramKey->daddr)
662 {
66eaf2eb 663 hitBin(&histogram->ttSendBins, tt);
e072e1ab
BP
664 }
665 else
666 {
66eaf2eb 667 hitBin(&histogram->ttRecvBins, tt);
e072e1ab 668 }
4ee223e5 669 }
76be6fc2 670
66eaf2eb 671 if (syncState->stats)
76be6fc2 672 {
66eaf2eb
BP
673 rttKey.saddr=
674 message->inE->event.tcpEvent->segmentKey->connectionKey.saddr;
675 rttKey.daddr=
676 message->inE->event.tcpEvent->segmentKey->connectionKey.daddr;
677 rtt= g_hash_table_lookup(analysisData->rttInfo, &rttKey);
678 g_debug("rttInfo, looking up (%u, %u)->(%f)", rttKey.saddr,
679 rttKey.daddr, rtt ? *rtt : NAN);
680
681 if (rtt)
76be6fc2 682 {
66eaf2eb
BP
683 g_debug("rttInfo, tt: %f rtt / 2: %f", tt, *rtt / 2.);
684 if (tt < *rtt / 2.)
685 {
686 messageStats->tooFastNb++;
687 }
688 }
689 else
690 {
691 messageStats->noRTTInfoNb++;
76be6fc2
BP
692 }
693 }
66eaf2eb
BP
694
695 if (syncState->graphsStream)
696 {
697 updateBounds(analysisData->graphs->bounds, message->inE,
698 message->outE);
699 }
700
701 if (syncState->stats || syncState->graphsStream)
76be6fc2 702 {
66eaf2eb
BP
703 analysisData->chullSS->analysisModule->analyzeMessage(analysisData->chullSS,
704 message);
76be6fc2 705 }
cdce23b3
BP
706}
707
708
709/*
710 * Perform analysis on multiple messages
711 *
76be6fc2
BP
712 * Measure the RTT
713 *
cdce23b3
BP
714 * Args:
715 * syncState container for synchronization data
716 * exchange structure containing the messages
717 */
66eaf2eb
BP
718static void analyzeExchangeEval(SyncState* const syncState, Exchange* const
719 exchange)
cdce23b3 720{
d4721e1a
BP
721 AnalysisDataEval* analysisData= syncState->analysisData;
722 Message* m1= g_queue_peek_tail(exchange->acks);
723 Message* m2= exchange->message;
724 struct RttKey* rttKey;
725 double* rtt, * exchangeRtt;
cdce23b3 726
e072e1ab
BP
727 g_assert(m1->inE->type == TCP);
728
d4721e1a
BP
729 // (T2 - T1) - (T3 - T4)
730 rtt= malloc(sizeof(double));
731 *rtt= wallTimeSub(&m1->inE->wallTime, &m1->outE->wallTime) -
732 wallTimeSub(&m2->outE->wallTime, &m2->inE->wallTime);
733
d4721e1a
BP
734 rttKey= malloc(sizeof(struct RttKey));
735 rttKey->saddr=
736 MIN(m1->inE->event.tcpEvent->segmentKey->connectionKey.saddr,
737 m1->inE->event.tcpEvent->segmentKey->connectionKey.daddr);
738 rttKey->daddr=
739 MAX(m1->inE->event.tcpEvent->segmentKey->connectionKey.saddr,
740 m1->inE->event.tcpEvent->segmentKey->connectionKey.daddr);
e072e1ab
BP
741
742 if (syncState->graphsStream)
743 {
66eaf2eb
BP
744 AnalysisHistogramEval* histogram=
745 g_hash_table_lookup(analysisData->graphs->histograms, rttKey);
e072e1ab 746
66eaf2eb 747 if (histogram == NULL)
e072e1ab
BP
748 {
749 struct RttKey* tableKey= malloc(sizeof(*tableKey));
750
66eaf2eb
BP
751 histogram= constructAnalysisHistogramEval(syncState->graphsDir,
752 rttKey);
e072e1ab 753 memcpy(tableKey, rttKey, sizeof(*tableKey));
66eaf2eb
BP
754 g_hash_table_insert(analysisData->graphs->histograms, tableKey,
755 histogram);
e072e1ab
BP
756 }
757
66eaf2eb 758 hitBin(&histogram->hrttBins, *rtt / 2);
e072e1ab
BP
759 }
760
66eaf2eb 761 if (syncState->stats)
d4721e1a 762 {
66eaf2eb
BP
763 exchangeRtt= g_hash_table_lookup(analysisData->stats->exchangeRtt,
764 rttKey);
765
766 if (exchangeRtt)
d4721e1a 767 {
66eaf2eb
BP
768 if (*rtt < *exchangeRtt)
769 {
770 g_hash_table_replace(analysisData->stats->exchangeRtt, rttKey, rtt);
771 }
772 else
773 {
774 free(rttKey);
775 free(rtt);
776 }
777 }
778 else
779 {
780 g_hash_table_insert(analysisData->stats->exchangeRtt, rttKey, rtt);
d4721e1a
BP
781 }
782 }
783 else
784 {
66eaf2eb
BP
785 free(rttKey);
786 free(rtt);
d4721e1a 787 }
cdce23b3
BP
788}
789
790
791/*
792 * Perform analysis on muliple events
793 *
76be6fc2
BP
794 * Sum the broadcast differential delays
795 *
cdce23b3
BP
796 * Args:
797 * syncState container for synchronization data
798 * broadcast structure containing the events
799 */
66eaf2eb
BP
800static void analyzeBroadcastEval(SyncState* const syncState, Broadcast* const
801 broadcast)
cdce23b3 802{
66eaf2eb 803 AnalysisDataEval* analysisData= syncState->analysisData;
76be6fc2 804
66eaf2eb 805 if (syncState->stats)
76be6fc2 806 {
66eaf2eb
BP
807 double sum= 0, squaresSum= 0;
808 double y;
cdce23b3 809
66eaf2eb
BP
810 g_queue_foreach(broadcast->events, &gfSum, &sum);
811 g_queue_foreach(broadcast->events, &gfSumSquares, &squaresSum);
76be6fc2 812
66eaf2eb
BP
813 analysisData->stats->broadcastNb++;
814 // Because of numerical errors, this can at times be < 0
815 y= squaresSum / g_queue_get_length(broadcast->events) - pow(sum /
816 g_queue_get_length(broadcast->events), 2.);
817 if (y > 0)
818 {
819 analysisData->stats->broadcastDiffSum+= sqrt(y);
820 }
821 }
76be6fc2 822
66eaf2eb 823 if (syncState->graphsStream)
76be6fc2 824 {
66eaf2eb
BP
825 unsigned int i, j;
826 GArray* events;
827 unsigned int eventNb= broadcast->events->length;
828
829 events= g_array_sized_new(FALSE, FALSE, sizeof(Event*), eventNb);
830 g_queue_foreach(broadcast->events, &gfAddEventToArray, events);
831
832 for (i= 0; i < eventNb; i++)
833 {
834 for (j= 0; j < eventNb; j++)
835 {
836 Event* eventI= g_array_index(events, Event*, i), * eventJ=
837 g_array_index(events, Event*, j);
838
839 if (eventI->traceNum < eventJ->traceNum)
840 {
841 updateBounds(analysisData->graphs->bounds, eventI, eventJ);
842 }
843 }
844 }
845
846 g_array_free(events, TRUE);
76be6fc2 847 }
cdce23b3
BP
848}
849
850
851/*
66eaf2eb
BP
852 * Finalize the factor calculations. Since this module does not really
853 * calculate factors, identity factors are returned. Instead, histograms are
854 * written out and histogram structures are freed.
cdce23b3
BP
855 *
856 * Args:
857 * syncState container for synchronization data.
858 *
859 * Returns:
d4721e1a 860 * Factors[traceNb] identity factors for each trace
cdce23b3
BP
861 */
862static GArray* finalizeAnalysisEval(SyncState* const syncState)
863{
864 GArray* factors;
865 unsigned int i;
4ee223e5
BP
866 AnalysisDataEval* analysisData= syncState->analysisData;
867
66eaf2eb 868 if (syncState->graphsStream && analysisData->graphs->histograms)
4ee223e5 869 {
66eaf2eb
BP
870 g_hash_table_foreach(analysisData->graphs->histograms,
871 &ghfWriteHistogram, &(struct WriteHistogramInfo) {.rttInfo=
872 analysisData->rttInfo, .graphsStream= syncState->graphsStream});
873 g_hash_table_destroy(analysisData->graphs->histograms);
874 analysisData->graphs->histograms= NULL;
4ee223e5 875 }
cdce23b3 876
66eaf2eb
BP
877 finalizeAnalysisEvalLP(syncState);
878
cdce23b3
BP
879 factors= g_array_sized_new(FALSE, FALSE, sizeof(Factors),
880 syncState->traceNb);
881 g_array_set_size(factors, syncState->traceNb);
882 for (i= 0; i < syncState->traceNb; i++)
883 {
884 Factors* e;
885
886 e= &g_array_index(factors, Factors, i);
887 e->drift= 1.;
888 e->offset= 0.;
889 }
890
891 return factors;
892}
893
894
895/*
896 * Print statistics related to analysis. Must be called after
897 * finalizeAnalysis.
898 *
899 * Args:
900 * syncState container for synchronization data.
901 */
902static void printAnalysisStatsEval(SyncState* const syncState)
903{
904 AnalysisDataEval* analysisData;
f109919b
BP
905 unsigned int i, j, k;
906 unsigned int totInversion= 0, totTooFast= 0, totNoInfo= 0, totTotal= 0;
907 int charNb;
cdce23b3
BP
908
909 if (!syncState->stats)
910 {
911 return;
912 }
913
914 analysisData= (AnalysisDataEval*) syncState->analysisData;
915
916 printf("Synchronization evaluation analysis stats:\n");
ffa21cfd
BP
917 if (analysisData->stats->broadcastNb)
918 {
919 printf("\tsum of broadcast differential delays: %g\n",
920 analysisData->stats->broadcastDiffSum);
467066ee 921 printf("\taverage broadcast differential delay: %g\n",
ffa21cfd
BP
922 analysisData->stats->broadcastDiffSum /
923 analysisData->stats->broadcastNb);
924 }
cdce23b3
BP
925
926 printf("\tIndividual evaluation:\n"
f109919b 927 "\t\tTrace pair Inversions Too fast No RTT info Total\n");
cdce23b3
BP
928
929 for (i= 0; i < syncState->traceNb; i++)
930 {
931 for (j= i + 1; j < syncState->traceNb; j++)
932 {
76be6fc2 933 MessageStats* messageStats;
f109919b
BP
934 struct {
935 unsigned int t1, t2;
936 } loopValues[]= {
937 {i, j},
938 {j, i}
939 };
940
941 for (k= 0; k < sizeof(loopValues) / sizeof(*loopValues); k++)
942 {
943 messageStats=
944 &analysisData->stats->messageStats[loopValues[k].t1][loopValues[k].t2];
945
946 printf("\t\t%3d - %-3d ", loopValues[k].t1, loopValues[k].t2);
947 printf("%u (%u%%)%n", messageStats->inversionNb, (unsigned
948 int) ceil((double) messageStats->inversionNb /
949 messageStats->total * 100), &charNb);
950 printf("%*s", 17 - charNb > 0 ? 17 - charNb + 1: 1, " ");
951 printf("%u (%u%%)%n", messageStats->tooFastNb, (unsigned int)
952 ceil((double) messageStats->tooFastNb /
953 messageStats->total * 100), &charNb);
954 printf("%*s%-10u %u\n", 17 - charNb > 0 ? 17 - charNb + 1:
955 1, " ", messageStats->noRTTInfoNb, messageStats->total);
956
957 totInversion+= messageStats->inversionNb;
958 totTooFast+= messageStats->tooFastNb;
959 totNoInfo+= messageStats->noRTTInfoNb;
960 totTotal+= messageStats->total;
961 }
cdce23b3
BP
962 }
963 }
d4721e1a 964
f109919b
BP
965 printf("\t\t total ");
966 printf("%u (%u%%)%n", totInversion, (unsigned int) ceil((double)
967 totInversion / totTotal * 100), &charNb);
968 printf("%*s", 17 - charNb > 0 ? 17 - charNb + 1: 1, " ");
969 printf("%u (%u%%)%n", totTooFast, (unsigned int) ceil((double) totTooFast
970 / totTotal * 100), &charNb);
971 printf("%*s%-10u %u\n", 17 - charNb > 0 ? 17 - charNb + 1: 1, " ",
972 totNoInfo, totTotal);
973
d4721e1a
BP
974 printf("\tRound-trip times:\n"
975 "\t\tHost pair RTT from exchanges RTTs from file (ms)\n");
976 g_hash_table_foreach(analysisData->stats->exchangeRtt,
977 &ghfPrintExchangeRtt, analysisData->rttInfo);
66eaf2eb
BP
978
979 printf("\tConvex hull factors comparisons:\n"
980 "\t\tTrace pair Factors type Differences (lp - chull)\n"
981 "\t\t a0 a1\n"
982 "\t\t Min Max Min Max\n");
983
984 for (i= 0; i < syncState->traceNb; i++)
985 {
986 for (j= 0; j < i; j++)
987 {
988 FactorsCHull* chFactors= &analysisData->stats->chFactorsArray[i][j];
989 FactorsCHull* lpFactors= &analysisData->stats->lpFactorsArray[i][j];
990
991 printf("\t\t%3d - %-3d ", i, j);
992 if (lpFactors->type == chFactors->type)
993 {
994 if (lpFactors->type == MIDDLE)
995 {
996 printf("%-13s %-10.4g %-10.4g %-10.4g %.4g\n",
997 approxNames[lpFactors->type],
998 lpFactors->min->offset - chFactors->min->offset,
999 lpFactors->max->offset - chFactors->max->offset,
1000 lpFactors->min->drift - chFactors->min->drift,
1001 lpFactors->max->drift - chFactors->max->drift);
1002 }
1003 else if (lpFactors->type == ABSENT)
1004 {
1005 printf("%s\n", approxNames[lpFactors->type]);
1006 }
1007 }
1008 else
1009 {
1010 printf("Different! %s and %s\n", approxNames[lpFactors->type],
1011 approxNames[chFactors->type]);
1012 }
1013 }
1014 }
d4721e1a
BP
1015}
1016
1017
1018/*
1019 * A GHFunc for g_hash_table_foreach()
1020 *
1021 * Args:
1022 * key: RttKey* where saddr < daddr
1023 * value: double*, RTT estimated from exchanges
1024 * user_data GHashTable* rttInfo
1025 */
66eaf2eb
BP
1026static void ghfPrintExchangeRtt(gpointer key, gpointer value, gpointer
1027 user_data)
d4721e1a
BP
1028{
1029 char addr1[16], addr2[16];
1030 struct RttKey* rttKey1= key;
1031 struct RttKey rttKey2= {rttKey1->daddr, rttKey1->saddr};
1032 double* fileRtt1, *fileRtt2;
1033 GHashTable* rttInfo= user_data;
1034
1035 convertIP(addr1, rttKey1->saddr);
1036 convertIP(addr2, rttKey1->daddr);
1037
1038 fileRtt1= g_hash_table_lookup(rttInfo, rttKey1);
1039 fileRtt2= g_hash_table_lookup(rttInfo, &rttKey2);
1040
1041 printf("\t\t(%15s, %-15s) %-18.3f ", addr1, addr2, *(double*) value * 1e3);
1042
1043 if (fileRtt1 || fileRtt2)
1044 {
1045 if (fileRtt1)
1046 {
1047 printf("%.3f", *fileRtt1 * 1e3);
1048 }
1049 if (fileRtt1 && fileRtt2)
1050 {
1051 printf(", ");
1052 }
1053 if (fileRtt2)
1054 {
1055 printf("%.3f", *fileRtt2 * 1e3);
1056 }
1057 }
1058 else
1059 {
1060 printf("-");
1061 }
1062 printf("\n");
cdce23b3 1063}
2bd4b3e4
BP
1064
1065
1066/*
1067 * A GHashFunc for g_hash_table_new()
1068 *
1069 * Args:
1070 * key struct RttKey*
1071 */
1072static guint ghfRttKeyHash(gconstpointer key)
1073{
1074 struct RttKey* rttKey;
1075 uint32_t a, b, c;
1076
1077 rttKey= (struct RttKey*) key;
1078
1079 a= rttKey->saddr;
1080 b= rttKey->daddr;
1081 c= 0;
1082 final(a, b, c);
1083
1084 return c;
1085}
1086
1087
1088/*
1089 * A GDestroyNotify function for g_hash_table_new_full()
1090 *
1091 * Args:
1092 * data: struct RttKey*
1093 */
1094static void gdnDestroyRttKey(gpointer data)
1095{
1096 free(data);
1097}
1098
1099
1100/*
1101 * A GDestroyNotify function for g_hash_table_new_full()
1102 *
1103 * Args:
1104 * data: double*
1105 */
1106static void gdnDestroyDouble(gpointer data)
1107{
1108 free(data);
1109}
1110
1111
1112/*
1113 * A GEqualFunc for g_hash_table_new()
1114 *
1115 * Args:
1116 * a, b RttKey*
1117 *
1118 * Returns:
1119 * TRUE if both values are equal
1120 */
1121static gboolean gefRttKeyEqual(gconstpointer a, gconstpointer b)
1122{
1123 const struct RttKey* rkA, * rkB;
1124
1125 rkA= (struct RttKey*) a;
1126 rkB= (struct RttKey*) b;
1127
1128 if (rkA->saddr == rkB->saddr && rkA->daddr == rkB->daddr)
1129 {
1130 return TRUE;
1131 }
1132 else
1133 {
1134 return FALSE;
1135 }
1136}
1137
1138
1139/*
1140 * Read a file contain minimum round trip time values and fill an array with
1141 * them. The file is formatted as such:
1142 * <host1 IP> <host2 IP> <RTT in milliseconds>
1143 * ip's should be in dotted quad format
1144 *
1145 * Args:
1146 * rttInfo: double* rttInfo[RttKey], empty table, will be filled
1147 * rttStream: stream from which to read
1148 */
1149static void readRttInfo(GHashTable* rttInfo, FILE* rttStream)
1150{
1151 char* line= NULL;
1152 size_t len;
1153 int retval;
1154
1155 positionStream(rttStream);
1156 retval= getline(&line, &len, rttStream);
1157 while(!feof(rttStream))
1158 {
1159 struct RttKey* rttKey;
1160 char saddrDQ[20], daddrDQ[20];
1161 double* rtt;
1162 char tmp;
1163 struct in_addr addr;
1164 unsigned int i;
1165 struct {
1166 char* dq;
1167 size_t offset;
1168 } loopValues[] = {
1169 {saddrDQ, offsetof(struct RttKey, saddr)},
1170 {daddrDQ, offsetof(struct RttKey, daddr)}
1171 };
1172
1173 if (retval == -1 && !feof(rttStream))
1174 {
1175 g_error(strerror(errno));
1176 }
1177
1178 if (line[retval - 1] == '\n')
1179 {
1180 line[retval - 1]= '\0';
1181 }
1182
1183 rtt= malloc(sizeof(double));
1184 retval= sscanf(line, " %19s %19s %lf %c", saddrDQ, daddrDQ, rtt,
1185 &tmp);
1186 if (retval == EOF)
1187 {
1188 g_error(strerror(errno));
1189 }
1190 else if (retval != 3)
1191 {
1192 g_error("Error parsing RTT file, line was '%s'", line);
1193 }
1194
1195 rttKey= malloc(sizeof(struct RttKey));
1196 for (i= 0; i < sizeof(loopValues) / sizeof(*loopValues); i++)
1197 {
1198 retval= inet_aton(loopValues[i].dq, &addr);
1199 if (retval == 0)
1200 {
1201 g_error("Error converting address '%s'", loopValues[i].dq);
1202 }
1203 *(uint32_t*) ((void*) rttKey + loopValues[i].offset)=
1204 addr.s_addr;
1205 }
1206
76be6fc2 1207 *rtt/= 1e3;
d4721e1a
BP
1208 g_debug("rttInfo, Inserting (%u, %u)->(%f)", rttKey->saddr,
1209 rttKey->daddr, *rtt);
2bd4b3e4
BP
1210 g_hash_table_insert(rttInfo, rttKey, rtt);
1211
1212 positionStream(rttStream);
1213 retval= getline(&line, &len, rttStream);
1214 }
1215
1216 if (line)
1217 {
1218 free(line);
1219 }
1220}
1221
1222
1223/*
1224 * Advance stream over empty space, empty lines and lines that begin with '#'
1225 *
1226 * Args:
1227 * stream: stream, at exit, will be over the first non-empty character
1228 * of a line of be at EOF
1229 */
1230static void positionStream(FILE* stream)
1231{
1232 int firstChar;
1233 ssize_t retval;
1234 char* line= NULL;
1235 size_t len;
1236
1237 do
1238 {
1239 firstChar= fgetc(stream);
1240 if (firstChar == (int) '#')
1241 {
1242 retval= getline(&line, &len, stream);
1243 if (retval == -1)
1244 {
1245 if (feof(stream))
1246 {
1247 goto outEof;
1248 }
1249 else
1250 {
1251 g_error(strerror(errno));
1252 }
1253 }
1254 }
1255 else if (firstChar == (int) '\n' || firstChar == (int) ' ' ||
1256 firstChar == (int) '\t')
1257 {}
1258 else if (firstChar == EOF)
1259 {
1260 goto outEof;
1261 }
1262 else
1263 {
1264 break;
1265 }
1266 } while (true);
1267 retval= ungetc(firstChar, stream);
1268 if (retval == EOF)
1269 {
1270 g_error("Error: ungetc()");
1271 }
1272
1273outEof:
1274 if (line)
1275 {
1276 free(line);
1277 }
1278}
76be6fc2
BP
1279
1280
1281/*
1282 * A GFunc for g_queue_foreach()
1283 *
1284 * Args:
1285 * data Event*, a UDP broadcast event
1286 * user_data double*, the running sum
1287 *
1288 * Returns:
1289 * Adds the time of the event to the sum
1290 */
1291static void gfSum(gpointer data, gpointer userData)
1292{
1293 Event* event= (Event*) data;
1294
1295 *(double*) userData+= event->wallTime.seconds + event->wallTime.nanosec /
1296 1e9;
1297}
1298
1299
1300/*
1301 * A GFunc for g_queue_foreach()
1302 *
1303 * Args:
1304 * data Event*, a UDP broadcast event
1305 * user_data double*, the running sum
1306 *
1307 * Returns:
1308 * Adds the square of the time of the event to the sum
1309 */
1310static void gfSumSquares(gpointer data, gpointer userData)
1311{
1312 Event* event= (Event*) data;
1313
1314 *(double*) userData+= pow(event->wallTime.seconds + event->wallTime.nanosec
1315 / 1e9, 2.);
1316}
4ee223e5
BP
1317
1318
e072e1ab
BP
1319/*
1320 * Update a struct Bins according to a new value
1321 *
1322 * Args:
1323 * bins: the structure containing bins to build a histrogram
1324 * value: the new value
1325 */
1326static void hitBin(struct Bins* const bins, const double value)
1327{
1328 unsigned int binN= binNum(value);
1329
1330 if (binN < bins->min)
1331 {
1332 bins->min= binN;
1333 }
1334 else if (binN > bins->max)
1335 {
1336 bins->max= binN;
1337 }
1338
1339 bins->total++;
1340
1341 bins->bin[binN]++;
1342}
1343
1344
4ee223e5
BP
1345/*
1346 * Figure out the bin in a histogram to which a value belongs.
1347 *
1348 * This uses exponentially sized bins that go from 0 to infinity.
1349 *
1350 * Args:
e072e1ab
BP
1351 * value: in the range -INFINITY to INFINITY
1352 *
1353 * Returns:
1354 * The number of the bin in a struct Bins.bin
4ee223e5
BP
1355 */
1356static unsigned int binNum(const double value)
1357{
e072e1ab 1358 if (value <= 0)
4ee223e5
BP
1359 {
1360 return 0;
1361 }
e072e1ab
BP
1362 else if (value < binEnd(1))
1363 {
1364 return 1;
1365 }
1366 else if (value >= binStart(BIN_NB - 1))
1367 {
1368 return BIN_NB - 1;
1369 }
4ee223e5
BP
1370 else
1371 {
e072e1ab 1372 return floor(log(value) / log(binBase)) + BIN_NB + 1;
4ee223e5
BP
1373 }
1374}
1375
1376
1377/*
e072e1ab
BP
1378 * Figure out the start of the interval of a bin in a histogram. See struct
1379 * Bins.
4ee223e5
BP
1380 *
1381 * This uses exponentially sized bins that go from 0 to infinity.
1382 *
1383 * Args:
1384 * binNum: bin number
e072e1ab
BP
1385 *
1386 * Return:
1387 * The start of the interval, this value is included in the interval (except
1388 * for -INFINITY, naturally)
4ee223e5
BP
1389 */
1390static double binStart(const unsigned int binNum)
1391{
e072e1ab 1392 g_assert_cmpuint(binNum, <, BIN_NB);
4ee223e5
BP
1393
1394 if (binNum == 0)
1395 {
e072e1ab
BP
1396 return -INFINITY;
1397 }
1398 else if (binNum == 1)
1399 {
1400 return 0.;
4ee223e5
BP
1401 }
1402 else
1403 {
e072e1ab 1404 return pow(binBase, (double) binNum - BIN_NB + 1);
4ee223e5
BP
1405 }
1406}
1407
1408
1409/*
e072e1ab
BP
1410 * Figure out the end of the interval of a bin in a histogram. See struct
1411 * Bins.
4ee223e5
BP
1412 *
1413 * This uses exponentially sized bins that go from 0 to infinity.
1414 *
1415 * Args:
1416 * binNum: bin number
e072e1ab
BP
1417 *
1418 * Return:
1419 * The end of the interval, this value is not included in the interval
4ee223e5
BP
1420 */
1421static double binEnd(const unsigned int binNum)
1422{
e072e1ab 1423 g_assert_cmpuint(binNum, <, BIN_NB);
4ee223e5 1424
e072e1ab 1425 if (binNum == 0)
4ee223e5 1426 {
e072e1ab
BP
1427 return 0.;
1428 }
1429 else if (binNum < BIN_NB - 1)
1430 {
1431 return pow(binBase, (double) binNum - BIN_NB + 2);
4ee223e5
BP
1432 }
1433 else
1434 {
1435 return INFINITY;
1436 }
1437}
467066ee
BP
1438
1439
1440/*
1441 * Return the total number of elements in the "normal" bins (not underflow or
1442 * overflow)
1443 *
1444 * Args:
1445 * bins: the structure containing bins to build a histrogram
1446 */
1447static uint32_t normalTotal(struct Bins* const bins)
1448{
1449 return bins->total - bins->bin[0] - bins->bin[BIN_NB - 1];
1450}
66eaf2eb
BP
1451
1452
1453/* Update the bounds between two traces
1454 *
1455 * Args:
1456 * bounds: the array containing all the trace-pair bounds
1457 * e1, e2: the two related events
1458 */
1459static void updateBounds(Bounds** const bounds, Event* const e1, Event* const e2)
1460{
1461 unsigned int traceI, traceJ;
1462 uint64_t messageTime;
1463 Bounds* tpBounds;
1464
1465 if (e1->traceNum < e2->traceNum)
1466 {
1467 traceI= e2->traceNum;
1468 traceJ= e1->traceNum;
1469 messageTime= e1->cpuTime;
1470 }
1471 else
1472 {
1473 traceI= e1->traceNum;
1474 traceJ= e2->traceNum;
1475 messageTime= e2->cpuTime;
1476 }
1477 tpBounds= &bounds[traceI][traceJ];
1478
1479 if (messageTime < tpBounds->min)
1480 {
1481 tpBounds->min= messageTime;
1482 }
1483 if (messageTime > tpBounds->max)
1484 {
1485 tpBounds->max= messageTime;
1486 }
1487}
1488
1489
1490#ifdef HAVE_LIBGLPK
1491/*
1492 * Create the linear programming problem containing the constraints defined by
1493 * two half-hulls. The objective function and optimization directions are not
1494 * written.
1495 *
1496 * Args:
1497 * syncState: container for synchronization data
1498 * i: first trace number
1499 * j: second trace number, garanteed to be larger than i
1500 * Returns:
1501 * A new glp_prob*, this problem must be freed by the caller with
1502 * glp_delete_prob()
1503 */
1504static glp_prob* lpCreateProblem(GQueue* const lowerHull, GQueue* const upperHull)
1505{
1506 unsigned int it;
1507 const int zero= 0;
1508 const double zeroD= 0.;
1509 glp_prob* lp= glp_create_prob();
1510 unsigned int hullPointNb= g_queue_get_length(lowerHull) +
1511 g_queue_get_length(upperHull);
1512 GArray* iArray= g_array_sized_new(FALSE, FALSE, sizeof(int), hullPointNb +
1513 1);
1514 GArray* jArray= g_array_sized_new(FALSE, FALSE, sizeof(int), hullPointNb +
1515 1);
1516 GArray* aArray= g_array_sized_new(FALSE, FALSE, sizeof(double),
1517 hullPointNb + 1);
1518 struct {
1519 GQueue* hull;
1520 struct LPAddRowInfo rowInfo;
1521 } loopValues[2]= {
1522 {lowerHull, {lp, GLP_UP, iArray, jArray, aArray}},
1523 {upperHull, {lp, GLP_LO, iArray, jArray, aArray}},
1524 };
1525
1526 // Create the LP problem
1527 glp_term_out(GLP_OFF);
1528 glp_add_rows(lp, hullPointNb);
1529 glp_add_cols(lp, 2);
1530
1531 glp_set_col_name(lp, 1, "a0");
1532 glp_set_col_bnds(lp, 1, GLP_FR, 0., 0.);
1533 glp_set_col_name(lp, 2, "a1");
1534 glp_set_col_bnds(lp, 2, GLP_LO, 0., 0.);
1535
1536 // Add row constraints
1537 g_array_append_val(iArray, zero);
1538 g_array_append_val(jArray, zero);
1539 g_array_append_val(aArray, zeroD);
1540
1541 for (it= 0; it < sizeof(loopValues) / sizeof(*loopValues); it++)
1542 {
1543 g_queue_foreach(loopValues[it].hull, &gfLPAddRow,
1544 &loopValues[it].rowInfo);
1545 }
1546
1547 g_assert_cmpuint(iArray->len, ==, jArray->len);
1548 g_assert_cmpuint(jArray->len, ==, aArray->len);
1549 g_assert_cmpuint(aArray->len - 1, ==, hullPointNb * 2);
1550
1551 glp_load_matrix(lp, aArray->len - 1, &g_array_index(iArray, int, 0),
1552 &g_array_index(jArray, int, 0), &g_array_index(aArray, double, 0));
1553
1554 glp_scale_prob(lp, GLP_SF_AUTO);
1555
1556 g_array_free(iArray, TRUE);
1557 g_array_free(jArray, TRUE);
1558 g_array_free(aArray, TRUE);
1559
1560 return lp;
1561}
1562
1563
1564/*
1565 * A GFunc for g_queue_foreach(). Add constraints and bounds for one row.
1566 *
1567 * Args:
1568 * data Point*, synchronization point for which to add an LP row
1569 * (a constraint)
1570 * user_data LPAddRowInfo*
1571 */
1572static void gfLPAddRow(gpointer data, gpointer user_data)
1573{
1574 Point* p= data;
1575 struct LPAddRowInfo* rowInfo= user_data;
1576 int indexes[2];
1577 double constraints[2];
1578
1579 indexes[0]= g_array_index(rowInfo->iArray, int, rowInfo->iArray->len - 1) + 1;
1580 indexes[1]= indexes[0];
1581
1582 if (rowInfo->boundType == GLP_UP)
1583 {
1584 glp_set_row_bnds(rowInfo->lp, indexes[0], GLP_UP, 0., p->y);
1585 }
1586 else if (rowInfo->boundType == GLP_LO)
1587 {
1588 glp_set_row_bnds(rowInfo->lp, indexes[0], GLP_LO, p->y, 0.);
1589 }
1590 else
1591 {
1592 g_assert_not_reached();
1593 }
1594
1595 g_array_append_vals(rowInfo->iArray, indexes, 2);
1596 indexes[0]= 1;
1597 indexes[1]= 2;
1598 g_array_append_vals(rowInfo->jArray, indexes, 2);
1599 constraints[0]= 1.;
1600 constraints[1]= p->x;
1601 g_array_append_vals(rowInfo->aArray, constraints, 2);
1602}
1603
1604
1605/*
1606 * Calculate min or max correction factors (as possible) using an LP problem.
1607 *
1608 * Args:
1609 * lp: A linear programming problem with constraints and bounds
1610 * initialized.
1611 * direction: The type of factors desired. Use GLP_MAX for max
1612 * approximation factors (a1, the drift or slope is the
1613 * largest) and GLP_MIN in the other case.
1614 *
1615 * Returns:
1616 * If the calculation was successful, a new Factors struct. Otherwise, NULL.
1617 * The calculation will fail if the hull assumptions are not respected.
1618 */
1619static Factors* calculateFactors(glp_prob* const lp, const int direction)
1620{
1621 int retval, status;
1622 Factors* factors;
1623
1624 glp_set_obj_coef(lp, 1, 0.);
1625 glp_set_obj_coef(lp, 2, 1.);
1626
1627 glp_set_obj_dir(lp, direction);
1628 retval= glp_simplex(lp, NULL);
1629 status= glp_get_status(lp);
1630
1631 if (retval == 0 && status == GLP_OPT)
1632 {
1633 factors= malloc(sizeof(Factors));
1634 factors->offset= glp_get_col_prim(lp, 1);
1635 factors->drift= glp_get_col_prim(lp, 2);
1636 }
1637 else
1638 {
1639 factors= NULL;
1640 }
1641
1642 return factors;
1643}
1644
1645
1646/*
1647 * Calculate min, max and approx correction factors (as possible) using an LP
1648 * problem.
1649 *
1650 * Args:
1651 * lp: A linear programming problem with constraints and bounds
1652 * initialized.
1653 *
1654 * Returns:
1655 * Please note that the approximation type may be MIDDLE, INCOMPLETE or
1656 * ABSENT. Unlike in analysis_chull, ABSENT is also used when the hulls do
1657 * not respect assumptions.
1658 */
1659static void calculateCompleteFactors(glp_prob* const lp, FactorsCHull* factors)
1660{
1661 factors->min= calculateFactors(lp, GLP_MIN);
1662 factors->max= calculateFactors(lp, GLP_MAX);
1663
1664 if (factors->min && factors->max)
1665 {
1666 factors->type= MIDDLE;
1667 calculateFactorsMiddle(factors);
1668 }
1669 else if (factors->min || factors->max)
1670 {
1671 factors->type= INCOMPLETE;
1672 factors->approx= NULL;
1673 }
1674 else
1675 {
1676 factors->type= ABSENT;
1677 factors->approx= NULL;
1678 }
1679}
1680
1681
1682/*
1683 * Create and initialize an array like AnalysisStatsCHull.allFactors
1684 *
1685 * Args:
1686 * traceNb: number of traces
1687 *
1688 * Returns:
1689 * A new array, which can be freed with freeAllFactors()
1690 */
1691static FactorsCHull** createAllFactors(const unsigned int traceNb)
1692{
1693 FactorsCHull** factorsArray;
1694 unsigned int i;
1695
1696 factorsArray= malloc(traceNb * sizeof(FactorsCHull*));
1697 for (i= 0; i < traceNb; i++)
1698 {
1699 factorsArray[i]= calloc((i + 1), sizeof(FactorsCHull));
1700
1701 factorsArray[i][i].type= EXACT;
1702 factorsArray[i][i].approx= malloc(sizeof(Factors));
1703 factorsArray[i][i].approx->drift= 1.;
1704 factorsArray[i][i].approx->offset= 0.;
1705 }
1706
1707 return factorsArray;
1708}
1709#endif
1710
1711
1712/*
1713 * Compute synchronization factors using a linear programming approach.
1714 * Compute the factors using analysis_chull. Compare the two.
1715 *
1716 * There are two definitions of this function. The empty one is used when the
1717 * solver library, glpk, is not available at build time. In that case, nothing
1718 * is actually produced.
1719 *
1720 * Args:
1721 * syncState: container for synchronization data
1722 */
1723#ifndef HAVE_LIBGLPK
1724static inline void finalizeAnalysisEvalLP(SyncState* const syncState)
1725{
1726}
1727#else
1728static void finalizeAnalysisEvalLP(SyncState* const syncState)
1729{
1730 unsigned int i, j;
1731 AnalysisDataEval* analysisData= syncState->analysisData;
1732 AnalysisDataCHull* chAnalysisData= analysisData->chullSS->analysisData;
1733 FactorsCHull** lpFactorsArray= createAllFactors(syncState->traceNb);
1734 FactorsCHull* lpFactors;
1735
1736 if (!syncState->stats && !syncState->graphsStream)
1737 {
1738 return;
1739 }
1740
1741 if ((syncState->graphsStream && analysisData->graphs->lps != NULL) ||
1742 (syncState->stats && analysisData->stats->chFactorsArray != NULL))
1743 {
1744 return;
1745 }
1746
1747 if (syncState->stats)
1748 {
1749 analysisData->stats->chFactorsArray=
1750 calculateAllFactors(analysisData->chullSS);
1751 analysisData->stats->lpFactorsArray= lpFactorsArray;
1752 }
1753
1754 if (syncState->graphsStream)
1755 {
1756 analysisData->graphs->lps= malloc(syncState->traceNb *
1757 sizeof(glp_prob**));
1758 for (i= 0; i < syncState->traceNb; i++)
1759 {
1760 analysisData->graphs->lps[i]= malloc(i * sizeof(glp_prob*));
1761 }
1762 analysisData->graphs->lpFactorsArray= lpFactorsArray;
1763 }
1764
1765 for (i= 0; i < syncState->traceNb; i++)
1766 {
1767 for (j= 0; j < i; j++)
1768 {
1769 glp_prob* lp;
1770
1771 // Create the LP problem
1772 lp= lpCreateProblem(chAnalysisData->hullArray[i][j],
1773 chAnalysisData->hullArray[j][i]);
1774
1775 // Use the LP problem to find the correction factors for this pair of
1776 // traces
1777 calculateCompleteFactors(lp, &lpFactorsArray[i][j]);
1778
1779 if (syncState->graphsStream)
1780 {
1781 analysisData->graphs->lps[i][j]= lp;
1782 }
1783 else
1784 {
1785 glp_delete_prob(lp);
1786 destroyFactorsCHull(lpFactors);
1787 }
1788 }
1789 }
1790
1791 g_array_free(analysisData->chullSS->analysisModule->finalizeAnalysis(analysisData->chullSS),
1792 TRUE);
1793}
1794#endif
1795
1796
1797/*
1798 * Compute synchronization accuracy information using a linear programming
1799 * approach. Write the neccessary data files and plot lines in the gnuplot
1800 * script.
1801 *
1802 * There are two definitions of this function. The empty one is used when the
1803 * solver library, glpk, is not available at build time. In that case, nothing
1804 * is actually produced.
1805 *
1806 * Args:
1807 * syncState: container for synchronization data
1808 * i: first trace number
1809 * j: second trace number, garanteed to be larger than i
1810 */
1811#ifndef HAVE_LIBGLPK
1812static inline void writeAccuracyGraphs(SyncState* const syncState, const unsigned int
1813 i, const unsigned int j)
1814{
1815}
1816#else
1817static void writeAccuracyGraphs(SyncState* const syncState, const unsigned int
1818 i, const unsigned int j)
1819{
1820 unsigned int it;
1821 AnalysisDataEval* analysisData= syncState->analysisData;
1822 AnalysisGraphsEval* graphs= analysisData->graphs;
1823 GQueue*** hullArray= ((AnalysisDataCHull*)
1824 analysisData->chullSS->analysisData)->hullArray;
1825 FactorsCHull* lpFactors= &graphs->lpFactorsArray[j][i];
1826 glp_prob* lp= graphs->lps[j][i];
1827
1828 if (lpFactors->type == MIDDLE)
1829 {
1830 int retval;
1831 char* cwd;
1832 char fileName[40];
1833 FILE* fp;
1834 double* xValues;
1835 unsigned int xBegin, xEnd;
1836 double interval;
1837 const unsigned int graphPointNb= 1000;
1838
1839 // Open the data file
1840 snprintf(fileName, 40, "analysis_eval_accuracy-%03u_and_%03u.data", i, j);
1841 fileName[sizeof(fileName) - 1]= '\0';
1842
1843 cwd= changeToGraphDir(syncState->graphsDir);
1844
1845 if ((fp= fopen(fileName, "w")) == NULL)
1846 {
1847 g_error(strerror(errno));
1848 }
1849 fprintf(fp, "#%-24s %-25s %-25s %-25s\n", "x", "middle", "min", "max");
1850
1851 retval= chdir(cwd);
1852 if (retval == -1)
1853 {
1854 g_error(strerror(errno));
1855 }
1856 free(cwd);
1857
1858 // Build the list of absisca values for the points in the accuracy graph
1859 g_assert_cmpuint(graphPointNb, >=, 4);
1860 xValues= malloc(graphPointNb * sizeof(double));
1861 xValues[0]= graphs->bounds[j][i].min;
1862 xValues[graphPointNb - 1]= graphs->bounds[j][i].max;
1863 xValues[1]= MIN(((Point*) g_queue_peek_head(hullArray[i][j]))->x,
1864 ((Point*) g_queue_peek_head(hullArray[j][i]))->x);
1865 xValues[graphPointNb - 2]= MAX(((Point*)
1866 g_queue_peek_tail(hullArray[i][j]))->x, ((Point*)
1867 g_queue_peek_tail(hullArray[j][i]))->x);
1868
1869 if (xValues[0] == xValues[1])
1870 {
1871 xBegin= 0;
1872 }
1873 else
1874 {
1875 xBegin= 1;
1876 }
1877 if (xValues[graphPointNb - 2] == xValues[graphPointNb - 1])
1878 {
1879 xEnd= graphPointNb - 1;
1880 }
1881 else
1882 {
1883 xEnd= graphPointNb - 2;
1884 }
1885 interval= (xValues[xEnd] - xValues[xBegin]) / (graphPointNb - 1);
1886
1887 for (it= xBegin; it <= xEnd; it++)
1888 {
1889 xValues[it]= xValues[xBegin] + interval * (it - xBegin);
1890 }
1891
1892 /* For each absisca value and each optimisation direction, solve the LP
1893 * and write a line in the data file */
1894 for (it= 0; it < graphPointNb; it++)
1895 {
1896 unsigned int it2;
1897 int directions[]= {GLP_MIN, GLP_MAX};
1898
1899 glp_set_obj_coef(lp, 1, 1.);
1900 glp_set_obj_coef(lp, 2, xValues[it]);
1901
1902 fprintf(fp, "%25.9f %25.9f", xValues[it], lpFactors->approx->offset
1903 + lpFactors->approx->drift * xValues[it]);
1904 for (it2= 0; it2 < sizeof(directions) / sizeof(*directions); it2++)
1905 {
1906 int status;
1907
1908 glp_set_obj_dir(lp, directions[it2]);
1909 retval= glp_simplex(lp, NULL);
1910 status= glp_get_status(lp);
1911
1912 g_assert(retval == 0 && status == GLP_OPT);
1913 fprintf(fp, " %25.9f", glp_get_obj_val(lp));
1914 }
1915 fprintf(fp, "\n");
1916 }
1917
1918 free(xValues);
1919 fclose(fp);
1920
1921 fprintf(syncState->graphsStream,
1922 "\t\"analysis_eval_accuracy-%1$03u_and_%2$03u.data\" "
1923 "using 1:(($3 - $2) / clock_freq_%2$u):(($4 - $2) / clock_freq_%2$u) "
1924 "title \"Synchronization accuracy\" "
1925 "with filledcurves linewidth 2 linetype 1 "
1926 "linecolor rgb \"black\" fill solid 0.25 noborder, \\\n", i,
1927 j);
1928 }
1929}
1930#endif
1931
1932
1933/*
1934 * Write the analysis-specific graph lines in the gnuplot script.
1935 *
1936 * Args:
1937 * syncState: container for synchronization data
1938 * i: first trace number
1939 * j: second trace number, garanteed to be larger than i
1940 */
1941static void writeAnalysisTraceTimePlotsEval(SyncState* const syncState, const
1942 unsigned int i, const unsigned int j)
1943{
1944 AnalysisDataEval* analysisData= syncState->analysisData;
1945 AnalysisGraphsEval* graphs= analysisData->graphs;
1946 GQueue*** hullArray= ((AnalysisDataCHull*)
1947 analysisData->chullSS->analysisData)->hullArray;
1948
1949 printf("Between %u and %u:\n", i, j);
1950 printf("\tbounds min %llu max %llu\n", graphs->bounds[j][i].min,
1951 graphs->bounds[j][i].max);
1952 printf("\tnumber of points in lower half-hull %u upper half-hull %u\n",
1953 g_queue_get_length(hullArray[j][i]),
1954 g_queue_get_length(hullArray[i][j]));
1955
1956 writeAccuracyGraphs(syncState, i, j);
1957}
1958
1959
1960static void writeAnalysisTraceTracePlotsEval(SyncState* const syncState, const
1961 unsigned int i, const unsigned int j)
1962{
1963 AnalysisDataEval* analysisData= syncState->analysisData;
1964
1965#ifdef HAVE_LIBGLPK
1966 fprintf(syncState->graphsStream,
1967 "\t\"analysis_eval_accuracy-%1$03u_and_%2$03u.data\" "
1968 "using 1:3:4 "
1969 "title \"Synchronization accuracy\" "
1970 "with filledcurves linewidth 2 linetype 1 "
1971 "linecolor rgb \"black\" fill solid 0.25 noborder, \\\n", i, j);
1972#endif
1973
1974 analysisData->chullSS->analysisModule->graphFunctions.writeTraceTracePlots(analysisData->chullSS,
1975 i, j);
1976}
This page took 0.101907 seconds and 4 git commands to generate.