Fix warnings int the lttv/sync directory
[lttv.git] / lttv / lttv / sync / event_analysis_eval.c
1 /* This file is part of the Linux Trace Toolkit viewer
2 * Copyright (C) 2009, 2010 Benjamin Poirier <benjamin.poirier@polymtl.ca>
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation, either version 2.1 of the License, or (at
7 * your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
12 * License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #define _GNU_SOURCE
19 #define _ISOC99_SOURCE
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #include <arpa/inet.h>
26 #include <errno.h>
27 #include <math.h>
28 #include <netinet/in.h>
29 #include <stddef.h>
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <sys/socket.h>
34 #include <unistd.h>
35
36 #include "lookup3.h"
37 #include "sync_chain.h"
38
39 #include "event_analysis_eval.h"
40
41
42 struct WriteHistogramInfo
43 {
44 GHashTable* rttInfo;
45 FILE* graphsStream;
46 };
47
48 // Functions common to all analysis modules
49 static void initAnalysisEval(SyncState* const syncState);
50 static void destroyAnalysisEval(SyncState* const syncState);
51
52 static void analyzeMessageEval(SyncState* const syncState, Message* const
53 message);
54 static void analyzeExchangeEval(SyncState* const syncState, Exchange* const
55 exchange);
56 static void analyzeBroadcastEval(SyncState* const syncState, Broadcast* const
57 broadcast);
58 static AllFactors* finalizeAnalysisEval(SyncState* const syncState);
59 static void printAnalysisStatsEval(SyncState* const syncState);
60 static void writeAnalysisTraceTimeBackPlotsEval(SyncState* const syncState,
61 const unsigned int i, const unsigned int j);
62 static void writeAnalysisTraceTimeForePlotsEval(SyncState* const syncState,
63 const unsigned int i, const unsigned int j);
64
65 // Functions specific to this module
66 static guint ghfRttKeyHash(gconstpointer key);
67 static gboolean gefRttKeyEqual(gconstpointer a, gconstpointer b);
68 static void gdnDestroyRttKey(gpointer data);
69 static void gdnDestroyDouble(gpointer data);
70 static void readRttInfo(GHashTable* rttInfo, FILE* rttFile);
71 static void positionStream(FILE* stream);
72
73 static void gfSum(gpointer data, gpointer userData);
74 static void gfSumSquares(gpointer data, gpointer userData);
75 static void ghfPrintExchangeRtt(gpointer key, gpointer value, gpointer
76 user_data);
77
78 static void hitBin(struct Bins* const bins, const double value);
79 static unsigned int binNum(const double value) __attribute__((pure));
80 static double binStart(const unsigned int binNum) __attribute__((pure));
81 static double binEnd(const unsigned int binNum) __attribute__((pure));
82 static uint32_t normalTotal(struct Bins* const bins) __attribute__((const));
83
84 static AnalysisHistogramEval* constructAnalysisHistogramEval(const char* const
85 graphsDir, const struct RttKey* const rttKey);
86 static void destroyAnalysisHistogramEval(AnalysisHistogramEval* const
87 histogram);
88 static void gdnDestroyAnalysisHistogramEval(gpointer data);
89 static void ghfWriteHistogram(gpointer key, gpointer value, gpointer
90 user_data);
91 static void dumpBinToFile(const struct Bins* const bins, FILE* const file);
92 static void writeHistogram(FILE* graphsStream, const struct RttKey* rttKey,
93 double* minRtt, AnalysisHistogramEval* const histogram);
94
95 static void updateBounds(Bounds** const bounds, Event* const e1, Event* const
96 e2);
97
98
99 // initialized in registerAnalysisEval()
100 double binBase;
101
102 static AnalysisModule analysisModuleEval= {
103 .name= "eval",
104 .initAnalysis= &initAnalysisEval,
105 .destroyAnalysis= &destroyAnalysisEval,
106 .analyzeMessage= &analyzeMessageEval,
107 .analyzeExchange= &analyzeExchangeEval,
108 .analyzeBroadcast= &analyzeBroadcastEval,
109 .finalizeAnalysis= &finalizeAnalysisEval,
110 .printAnalysisStats= &printAnalysisStatsEval,
111 .graphFunctions= {
112 .writeTraceTimeBackPlots= &writeAnalysisTraceTimeBackPlotsEval,
113 .writeTraceTimeForePlots= &writeAnalysisTraceTimeForePlotsEval,
114 }
115 };
116
117 static ModuleOption optionEvalRttFile= {
118 .longName= "eval-rtt-file",
119 .hasArg= REQUIRED_ARG,
120 .optionHelp= "specify the file containing RTT information",
121 .argHelp= "FILE",
122 };
123
124
125 /*
126 * Analysis module registering function
127 */
128 void registerAnalysisEval()
129 {
130 binBase= exp10(6. / (BIN_NB - 3));
131
132 g_queue_push_tail(&analysisModules, &analysisModuleEval);
133 g_queue_push_tail(&moduleOptions, &optionEvalRttFile);
134 }
135
136
137 /*
138 * Analysis init function
139 *
140 * This function is called at the beginning of a synchronization run for a set
141 * of traces.
142 *
143 * Args:
144 * syncState container for synchronization data.
145 */
146 static void initAnalysisEval(SyncState* const syncState)
147 {
148 AnalysisDataEval* analysisData;
149 unsigned int i, j;
150
151 analysisData= malloc(sizeof(AnalysisDataEval));
152 syncState->analysisData= analysisData;
153
154 analysisData->rttInfo= g_hash_table_new_full(&ghfRttKeyHash,
155 &gefRttKeyEqual, &gdnDestroyRttKey, &gdnDestroyDouble);
156 if (optionEvalRttFile.arg)
157 {
158 FILE* rttStream;
159 int retval;
160
161 rttStream= fopen(optionEvalRttFile.arg, "r");
162 if (rttStream == NULL)
163 {
164 g_error("%s", strerror(errno));
165 }
166
167 readRttInfo(analysisData->rttInfo, rttStream);
168
169 retval= fclose(rttStream);
170 if (retval == EOF)
171 {
172 g_error("%s", strerror(errno));
173 }
174 }
175
176 if (syncState->stats)
177 {
178 analysisData->stats= calloc(1, sizeof(AnalysisStatsEval));
179 analysisData->stats->broadcastRangeMin= INFINITY;
180 analysisData->stats->broadcastRangeMax= -INFINITY;
181
182 analysisData->stats->messageStats= malloc(syncState->traceNb *
183 sizeof(MessageStats*));
184 for (i= 0; i < syncState->traceNb; i++)
185 {
186 analysisData->stats->messageStats[i]= calloc(syncState->traceNb,
187 sizeof(MessageStats));
188 }
189
190 analysisData->stats->exchangeRtt=
191 g_hash_table_new_full(&ghfRttKeyHash, &gefRttKeyEqual,
192 &gdnDestroyRttKey, &gdnDestroyDouble);
193 }
194
195 if (syncState->graphsStream)
196 {
197 AnalysisGraphsEval* graphs= malloc(sizeof(AnalysisGraphsEval));
198 GList* result;
199
200 analysisData->graphs= graphs;
201
202 graphs->histograms= g_hash_table_new_full(&ghfRttKeyHash,
203 &gefRttKeyEqual, &gdnDestroyRttKey,
204 &gdnDestroyAnalysisHistogramEval);
205
206 graphs->bounds= malloc(syncState->traceNb * sizeof(Bounds*));
207 for (i= 0; i < syncState->traceNb; i++)
208 {
209 graphs->bounds[i]= malloc(i * sizeof(Bounds));
210 for (j= 0; j < i; j++)
211 {
212 graphs->bounds[i][j].min= UINT64_MAX;
213 graphs->bounds[i][j].max= 0;
214 }
215 }
216
217 graphs->chullSS= (SyncState*) malloc(sizeof(SyncState));
218 memcpy(graphs->chullSS, syncState, sizeof(SyncState));
219 graphs->chullSS->analysisData= NULL;
220 result= g_queue_find_custom(&analysisModules, "chull", &gcfCompareAnalysis);
221 g_assert(result != NULL);
222 graphs->chullSS->analysisModule= (AnalysisModule*) result->data;
223 graphs->chullSS->analysisModule->initAnalysis(graphs->chullSS);
224 }
225 }
226
227
228 /*
229 * Create and open files used to store histogram points to generate graphs.
230 * Create data structures to store histogram points during analysis.
231 *
232 * Args:
233 * graphsDir: folder where to write files
234 * rttKey: host pair, make sure saddr < daddr
235 */
236 static AnalysisHistogramEval* constructAnalysisHistogramEval(const char* const
237 graphsDir, const struct RttKey* const rttKey)
238 {
239 int retval;
240 unsigned int i;
241 char* cwd;
242 char name[60], saddr[16], daddr[16];
243 AnalysisHistogramEval* histogram= calloc(1, sizeof(*histogram));
244 const struct {
245 size_t pointsOffset;
246 const char* fileName;
247 const char* host1, *host2;
248 } loopValues[]= {
249 {offsetof(AnalysisHistogramEval, ttSendPoints),
250 "analysis_eval_tt-%s_to_%s.data", saddr, daddr},
251 {offsetof(AnalysisHistogramEval, ttRecvPoints),
252 "analysis_eval_tt-%s_to_%s.data", daddr, saddr},
253 {offsetof(AnalysisHistogramEval, hrttPoints),
254 "analysis_eval_hrtt-%s_and_%s.data", saddr, daddr},
255 };
256
257 histogram->ttSendBins.min= BIN_NB - 1;
258 histogram->ttRecvBins.min= BIN_NB - 1;
259 histogram->hrttBins.min= BIN_NB - 1;
260
261 convertIP(saddr, rttKey->saddr);
262 convertIP(daddr, rttKey->daddr);
263
264 cwd= changeToGraphsDir(graphsDir);
265
266 for (i= 0; i < sizeof(loopValues) / sizeof(*loopValues); i++)
267 {
268 retval= snprintf(name, sizeof(name), loopValues[i].fileName,
269 loopValues[i].host1, loopValues[i].host2);
270 if (retval > sizeof(name) - 1)
271 {
272 name[sizeof(name) - 1]= '\0';
273 }
274 if ((*(FILE**)((void*) histogram + loopValues[i].pointsOffset)=
275 fopen(name, "w")) == NULL)
276 {
277 g_error("%s", strerror(errno));
278 }
279 }
280
281 retval= chdir(cwd);
282 if (retval == -1)
283 {
284 g_error("%s", strerror(errno));
285 }
286 free(cwd);
287
288 return histogram;
289 }
290
291
292 /*
293 * Close files used to store histogram points to generate graphs.
294 *
295 * Args:
296 * graphsDir: folder where to write files
297 * rttKey: host pair, make sure saddr < daddr
298 */
299 static void destroyAnalysisHistogramEval(AnalysisHistogramEval* const
300 histogram)
301 {
302 unsigned int i;
303 int retval;
304 const struct {
305 size_t pointsOffset;
306 } loopValues[]= {
307 {offsetof(AnalysisHistogramEval, ttSendPoints)},
308 {offsetof(AnalysisHistogramEval, ttRecvPoints)},
309 {offsetof(AnalysisHistogramEval, hrttPoints)},
310 };
311
312 for (i= 0; i < sizeof(loopValues) / sizeof(*loopValues); i++)
313 {
314 retval= fclose(*(FILE**)((void*) histogram + loopValues[i].pointsOffset));
315 if (retval != 0)
316 {
317 g_error("%s", strerror(errno));
318 }
319 }
320
321 free(histogram);
322 }
323
324
325 /*
326 * A GDestroyNotify function for g_hash_table_new_full()
327 *
328 * Args:
329 * data: AnalysisHistogramEval*
330 */
331 static void gdnDestroyAnalysisHistogramEval(gpointer data)
332 {
333 destroyAnalysisHistogramEval(data);
334 }
335
336
337 /*
338 * A GHFunc for g_hash_table_foreach()
339 *
340 * Args:
341 * key: RttKey* where saddr < daddr
342 * value: AnalysisHistogramEval*
343 * user_data struct WriteHistogramInfo*
344 */
345 static void ghfWriteHistogram(gpointer key, gpointer value, gpointer user_data)
346 {
347 double* rtt1, * rtt2;
348 struct RttKey* rttKey= key;
349 struct RttKey oppositeRttKey= {.saddr= rttKey->daddr, .daddr=
350 rttKey->saddr};
351 AnalysisHistogramEval* histogram= value;
352 struct WriteHistogramInfo* info= user_data;
353
354 rtt1= g_hash_table_lookup(info->rttInfo, rttKey);
355 rtt2= g_hash_table_lookup(info->rttInfo, &oppositeRttKey);
356
357 if (rtt1 == NULL)
358 {
359 rtt1= rtt2;
360 }
361 else if (rtt2 != NULL)
362 {
363 rtt1= MIN(rtt1, rtt2);
364 }
365
366 dumpBinToFile(&histogram->ttSendBins, histogram->ttSendPoints);
367 dumpBinToFile(&histogram->ttRecvBins, histogram->ttRecvPoints);
368 dumpBinToFile(&histogram->hrttBins, histogram->hrttPoints);
369 writeHistogram(info->graphsStream, rttKey, rtt1, histogram);
370 }
371
372
373 /*
374 * Write the content of one bin in a histogram point file
375 *
376 * Args:
377 * bin: array of values that make up a histogram
378 * file: FILE*, write to this file
379 */
380 static void dumpBinToFile(const struct Bins* const bins, FILE* const file)
381 {
382 unsigned int i;
383
384 // The first and last bins are skipped, see struct Bins
385 for (i= 1; i < BIN_NB - 1; i++)
386 {
387 if (bins->bin[i] > 0)
388 {
389 fprintf(file, "%20.9f %20.9f %20.9f\n", (binStart(i) + binEnd(i))
390 / 2., (double) bins->bin[i] / ((binEnd(i) - binStart(i)) *
391 bins->total), binEnd(i) - binStart(i));
392 }
393 }
394 }
395
396
397 /*
398 * Write the analysis-specific plot in the gnuplot script.
399 *
400 * Args:
401 * graphsStream: write to this file
402 * rttKey: must be sorted such that saddr < daddr
403 * minRtt: if available, else NULL
404 * histogram: struct that contains the bins for the pair of traces
405 * identified by rttKey
406 */
407 static void writeHistogram(FILE* graphsStream, const struct RttKey* rttKey,
408 double* minRtt, AnalysisHistogramEval* const histogram)
409 {
410 char saddr[16], daddr[16];
411
412 convertIP(saddr, rttKey->saddr);
413 convertIP(daddr, rttKey->daddr);
414
415 fprintf(graphsStream,
416 "\nreset\n"
417 "set output \"histogram-%s-%s.eps\"\n"
418 "set title \"\"\n"
419 "set xlabel \"Message Latency (s)\"\n"
420 "set ylabel \"Proportion of messages per second\"\n", saddr, daddr);
421
422 if (minRtt != NULL)
423 {
424 fprintf(graphsStream,
425 "set arrow from %.9f, 0 rto 0, graph 1 "
426 "nohead linetype 3 linewidth 3 linecolor rgb \"black\"\n", *minRtt
427 / 2);
428 }
429
430 if (normalTotal(&histogram->ttSendBins) ||
431 normalTotal(&histogram->ttRecvBins) ||
432 normalTotal(&histogram->hrttBins))
433 {
434 fprintf(graphsStream, "plot \\\n");
435
436 if (normalTotal(&histogram->hrttBins))
437 {
438 fprintf(graphsStream,
439 "\t\"analysis_eval_hrtt-%s_and_%s.data\" "
440 "title \"RTT/2\" with linespoints linetype 1 linewidth 2 "
441 "linecolor rgb \"black\" pointtype 6 pointsize 1,\\\n",
442 saddr, daddr);
443 }
444
445 if (normalTotal(&histogram->ttSendBins))
446 {
447 fprintf(graphsStream,
448 "\t\"analysis_eval_tt-%1$s_to_%2$s.data\" "
449 "title \"%1$s to %2$s\" with linespoints linetype 4 linewidth 2 "
450 "linecolor rgb \"gray60\" pointtype 6 pointsize 1,\\\n",
451 saddr, daddr);
452 }
453
454 if (normalTotal(&histogram->ttRecvBins))
455 {
456 fprintf(graphsStream,
457 "\t\"analysis_eval_tt-%1$s_to_%2$s.data\" "
458 "title \"%1$s to %2$s\" with linespoints linetype 4 linewidth 2 "
459 "linecolor rgb \"gray30\" pointtype 6 pointsize 1,\\\n",
460 daddr, saddr);
461 }
462
463 // Remove the ",\\\n" from the last graph plot line
464 if (ftruncate(fileno(graphsStream), ftell(graphsStream) - 3) == -1)
465 {
466 g_error("%s", strerror(errno));
467 }
468 if (fseek(graphsStream, 0, SEEK_END) == -1)
469 {
470 g_error("%s", strerror(errno));
471 }
472 fprintf(graphsStream, "\n");
473 }
474 }
475
476
477 /*
478 * Analysis destroy function
479 *
480 * Free the analysis specific data structures
481 *
482 * Args:
483 * syncState container for synchronization data.
484 */
485 static void destroyAnalysisEval(SyncState* const syncState)
486 {
487 unsigned int i;
488 AnalysisDataEval* analysisData;
489
490 analysisData= (AnalysisDataEval*) syncState->analysisData;
491
492 if (analysisData == NULL)
493 {
494 return;
495 }
496
497 g_hash_table_destroy(analysisData->rttInfo);
498
499 if (syncState->stats)
500 {
501 AnalysisStatsEval* stats= analysisData->stats;
502
503 for (i= 0; i < syncState->traceNb; i++)
504 {
505 free(stats->messageStats[i]);
506 }
507 free(stats->messageStats);
508
509 g_hash_table_destroy(stats->exchangeRtt);
510
511 free(stats);
512 }
513
514 if (syncState->graphsStream)
515 {
516 AnalysisGraphsEval* graphs= analysisData->graphs;
517
518 if (graphs->histograms)
519 {
520 g_hash_table_destroy(graphs->histograms);
521 }
522
523 for (i= 0; i < syncState->traceNb; i++)
524 {
525 free(graphs->bounds[i]);
526 }
527 free(graphs->bounds);
528
529 graphs->chullSS->analysisModule->destroyAnalysis(graphs->chullSS);
530 free(graphs->chullSS);
531
532 free(graphs);
533 }
534
535 free(syncState->analysisData);
536 syncState->analysisData= NULL;
537 }
538
539
540 /*
541 * Perform analysis on an event pair.
542 *
543 * Check if there is message inversion or messages that are too fast.
544 *
545 * Args:
546 * syncState container for synchronization data
547 * message structure containing the events
548 */
549 static void analyzeMessageEval(SyncState* const syncState, Message* const
550 message)
551 {
552 AnalysisDataEval* analysisData= syncState->analysisData;
553 MessageStats* messageStats = NULL; /* for gcc */
554 double* rtt;
555 double tt;
556 struct RttKey rttKey;
557
558 g_assert(message->inE->type == TCP);
559
560 if (syncState->stats)
561 {
562 messageStats=
563 &analysisData->stats->messageStats[message->outE->traceNum][message->inE->traceNum];
564 messageStats->total++;
565 }
566
567 tt= wallTimeSub(&message->inE->wallTime, &message->outE->wallTime);
568 if (tt <= 0)
569 {
570 if (syncState->stats)
571 {
572 messageStats->inversionNb++;
573 }
574 }
575 else if (syncState->graphsStream)
576 {
577 struct RttKey rttKey= {
578 .saddr=MIN(message->inE->event.tcpEvent->segmentKey->connectionKey.saddr,
579 message->inE->event.tcpEvent->segmentKey->connectionKey.daddr),
580 .daddr=MAX(message->inE->event.tcpEvent->segmentKey->connectionKey.saddr,
581 message->inE->event.tcpEvent->segmentKey->connectionKey.daddr),
582 };
583 AnalysisHistogramEval* histogram=
584 g_hash_table_lookup(analysisData->graphs->histograms, &rttKey);
585
586 if (histogram == NULL)
587 {
588 struct RttKey* tableKey= malloc(sizeof(*tableKey));
589
590 histogram= constructAnalysisHistogramEval(syncState->graphsDir, &rttKey);
591 memcpy(tableKey, &rttKey, sizeof(*tableKey));
592 g_hash_table_insert(analysisData->graphs->histograms, tableKey, histogram);
593 }
594
595 if (message->inE->event.udpEvent->datagramKey->saddr <
596 message->inE->event.udpEvent->datagramKey->daddr)
597 {
598 hitBin(&histogram->ttSendBins, tt);
599 }
600 else
601 {
602 hitBin(&histogram->ttRecvBins, tt);
603 }
604 }
605
606 if (syncState->stats)
607 {
608 rttKey.saddr=
609 message->inE->event.tcpEvent->segmentKey->connectionKey.saddr;
610 rttKey.daddr=
611 message->inE->event.tcpEvent->segmentKey->connectionKey.daddr;
612 rtt= g_hash_table_lookup(analysisData->rttInfo, &rttKey);
613 g_debug("rttInfo, looking up (%u, %u)->(%f)", rttKey.saddr,
614 rttKey.daddr, rtt ? *rtt : NAN);
615
616 if (rtt)
617 {
618 g_debug("rttInfo, tt: %f rtt / 2: %f", tt, *rtt / 2.);
619 if (tt < *rtt / 2.)
620 {
621 messageStats->tooFastNb++;
622 }
623 }
624 else
625 {
626 messageStats->noRTTInfoNb++;
627 }
628 }
629
630 if (syncState->graphsStream)
631 {
632 updateBounds(analysisData->graphs->bounds, message->inE,
633 message->outE);
634
635 analysisData->graphs->chullSS->analysisModule->analyzeMessage(analysisData->graphs->chullSS,
636 message);
637 }
638 }
639
640
641 /*
642 * Perform analysis on multiple messages
643 *
644 * Measure the RTT
645 *
646 * Args:
647 * syncState container for synchronization data
648 * exchange structure containing the messages
649 */
650 static void analyzeExchangeEval(SyncState* const syncState, Exchange* const
651 exchange)
652 {
653 AnalysisDataEval* analysisData= syncState->analysisData;
654 Message* m1= g_queue_peek_tail(exchange->acks);
655 Message* m2= exchange->message;
656 struct RttKey* rttKey;
657 double* rtt, * exchangeRtt;
658
659 g_assert(m1->inE->type == TCP);
660
661 // (T2 - T1) - (T3 - T4)
662 rtt= malloc(sizeof(double));
663 *rtt= wallTimeSub(&m1->inE->wallTime, &m1->outE->wallTime) -
664 wallTimeSub(&m2->outE->wallTime, &m2->inE->wallTime);
665
666 rttKey= malloc(sizeof(struct RttKey));
667 rttKey->saddr=
668 MIN(m1->inE->event.tcpEvent->segmentKey->connectionKey.saddr,
669 m1->inE->event.tcpEvent->segmentKey->connectionKey.daddr);
670 rttKey->daddr=
671 MAX(m1->inE->event.tcpEvent->segmentKey->connectionKey.saddr,
672 m1->inE->event.tcpEvent->segmentKey->connectionKey.daddr);
673
674 if (syncState->graphsStream)
675 {
676 AnalysisHistogramEval* histogram=
677 g_hash_table_lookup(analysisData->graphs->histograms, rttKey);
678
679 if (histogram == NULL)
680 {
681 struct RttKey* tableKey= malloc(sizeof(*tableKey));
682
683 histogram= constructAnalysisHistogramEval(syncState->graphsDir,
684 rttKey);
685 memcpy(tableKey, rttKey, sizeof(*tableKey));
686 g_hash_table_insert(analysisData->graphs->histograms, tableKey,
687 histogram);
688 }
689
690 hitBin(&histogram->hrttBins, *rtt / 2);
691 }
692
693 if (syncState->stats)
694 {
695 exchangeRtt= g_hash_table_lookup(analysisData->stats->exchangeRtt,
696 rttKey);
697
698 if (exchangeRtt)
699 {
700 if (*rtt < *exchangeRtt)
701 {
702 g_hash_table_replace(analysisData->stats->exchangeRtt, rttKey, rtt);
703 }
704 else
705 {
706 free(rttKey);
707 free(rtt);
708 }
709 }
710 else
711 {
712 g_hash_table_insert(analysisData->stats->exchangeRtt, rttKey, rtt);
713 }
714 }
715 else
716 {
717 free(rttKey);
718 free(rtt);
719 }
720 }
721
722
723 /*
724 * Perform analysis on muliple events
725 *
726 * Sum the broadcast differential delays
727 *
728 * Args:
729 * syncState container for synchronization data
730 * broadcast structure containing the events
731 */
732 static void analyzeBroadcastEval(SyncState* const syncState, Broadcast* const
733 broadcast)
734 {
735 AnalysisDataEval* analysisData= syncState->analysisData;
736
737 if (syncState->stats)
738 {
739 double sum= 0, squaresSum= 0;
740 double y;
741
742 g_queue_foreach(broadcast->events, &gfSum, &sum);
743 g_queue_foreach(broadcast->events, &gfSumSquares, &squaresSum);
744
745 analysisData->stats->broadcastNb++;
746 // Because of numerical errors, this can at times be < 0
747 y= squaresSum / g_queue_get_length(broadcast->events) - pow(sum /
748 g_queue_get_length(broadcast->events), 2.);
749 if (y > 0)
750 {
751 analysisData->stats->broadcastStdevSum+= sqrt(y);
752 }
753
754 if (syncState->traceNb == 2 && g_queue_get_length(broadcast->events)
755 == 2)
756 {
757 Event* e0, * e1;
758 double dd;
759
760 e0= g_queue_peek_head(broadcast->events);
761 e1= g_queue_peek_tail(broadcast->events);
762 if (e0->traceNum > e1->traceNum)
763 {
764 Event* tmp;
765
766 tmp= e0;
767 e0= e1;
768 e1= tmp;
769 }
770
771 dd= wallTimeSub(&e1->wallTime, &e0->wallTime);
772
773 analysisData->stats->broadcastPairNb++;
774 if (dd < analysisData->stats->broadcastRangeMin)
775 {
776 analysisData->stats->broadcastRangeMin= dd;
777 }
778 if (dd > analysisData->stats->broadcastRangeMax)
779 {
780 analysisData->stats->broadcastRangeMax= dd;
781 }
782
783 analysisData->stats->broadcastSum+= dd;
784 analysisData->stats->broadcastSumSquares+= pow(dd, 2);
785 }
786 }
787
788 if (syncState->graphsStream)
789 {
790 unsigned int i, j;
791 GArray* events;
792 unsigned int eventNb= broadcast->events->length;
793
794 events= g_array_sized_new(FALSE, FALSE, sizeof(Event*), eventNb);
795 g_queue_foreach(broadcast->events, &gfAddEventToArray, events);
796
797 for (i= 0; i < eventNb; i++)
798 {
799 for (j= 0; j < eventNb; j++)
800 {
801 Event* eventI= g_array_index(events, Event*, i), * eventJ=
802 g_array_index(events, Event*, j);
803
804 if (eventI->traceNum < eventJ->traceNum)
805 {
806 updateBounds(analysisData->graphs->bounds, eventI, eventJ);
807 }
808 }
809 }
810
811 g_array_free(events, TRUE);
812 }
813 }
814
815
816 /*
817 * Finalize the factor calculations. Since this module does not really
818 * calculate factors, absent factors are returned. Instead, histograms are
819 * written out and histogram structures are freed.
820 *
821 * Args:
822 * syncState container for synchronization data.
823 *
824 * Returns:
825 * AllFactors* synchronization factors for each trace pair
826 */
827 static AllFactors* finalizeAnalysisEval(SyncState* const syncState)
828 {
829 AnalysisDataEval* analysisData= syncState->analysisData;
830
831 /* This function may be run twice because of matching_distributor. This
832 * check is there to make sure the next block is run only once.
833 */
834 if (syncState->graphsStream && analysisData->graphs->histograms)
835 {
836 g_hash_table_foreach(analysisData->graphs->histograms,
837 &ghfWriteHistogram, &(struct WriteHistogramInfo) {.rttInfo=
838 analysisData->rttInfo, .graphsStream= syncState->graphsStream});
839 g_hash_table_destroy(analysisData->graphs->histograms);
840 analysisData->graphs->histograms= NULL;
841
842 if (syncState->graphsStream)
843 {
844 SyncState* chullSS= analysisData->graphs->chullSS;
845
846 freeAllFactors(chullSS->analysisModule->finalizeAnalysis(chullSS),
847 chullSS->traceNb);
848 }
849 }
850
851 return createAllFactors(syncState->traceNb);
852 }
853
854
855 /*
856 * Print statistics related to analysis. Must be called after
857 * finalizeAnalysis.
858 *
859 * Args:
860 * syncState container for synchronization data.
861 */
862 static void printAnalysisStatsEval(SyncState* const syncState)
863 {
864 AnalysisDataEval* analysisData;
865 unsigned int i, j, k;
866 unsigned int totInversion= 0, totTooFast= 0, totNoInfo= 0, totTotal= 0;
867 int charNb;
868
869 if (!syncState->stats)
870 {
871 return;
872 }
873
874 analysisData= (AnalysisDataEval*) syncState->analysisData;
875
876 printf("Synchronization evaluation analysis stats:\n");
877 if (analysisData->stats->broadcastNb)
878 {
879 printf("\tBroadcast differential delay:\n");
880 printf("\t\tsum of standard deviations: %g\n",
881 analysisData->stats->broadcastStdevSum);
882 printf("\t\taverage standard deviation: %g\n",
883 analysisData->stats->broadcastStdevSum /
884 analysisData->stats->broadcastNb);
885
886 if (syncState->traceNb == 2)
887 {
888 printf("\t\tdifferential delay range: [ %g .. %g ]\n",
889 analysisData->stats->broadcastRangeMin,
890 analysisData->stats->broadcastRangeMax);
891 printf("\t\tdifferential delay average: %g\n",
892 analysisData->stats->broadcastSum /
893 analysisData->stats->broadcastPairNb);
894 printf("\t\tdifferential delay standard deviation: %g\n",
895 sqrt(analysisData->stats->broadcastSumSquares /
896 analysisData->stats->broadcastPairNb -
897 pow(analysisData->stats->broadcastSum /
898 analysisData->stats->broadcastPairNb, 2)));
899 }
900 }
901
902 printf("\tIndividual evaluation:\n"
903 "\t\tTrace pair Inversions Too fast No RTT info Total\n");
904
905 for (i= 0; i < syncState->traceNb; i++)
906 {
907 for (j= i + 1; j < syncState->traceNb; j++)
908 {
909 MessageStats* messageStats;
910 struct {
911 unsigned int t1, t2;
912 } loopValues[]= {
913 {i, j},
914 {j, i}
915 };
916
917 for (k= 0; k < sizeof(loopValues) / sizeof(*loopValues); k++)
918 {
919 messageStats=
920 &analysisData->stats->messageStats[loopValues[k].t1][loopValues[k].t2];
921
922 printf("\t\t%3d - %-3d ", loopValues[k].t1, loopValues[k].t2);
923 printf("%u (%.2f%%)%n", messageStats->inversionNb, (double)
924 messageStats->inversionNb / messageStats->total * 100,
925 &charNb);
926 printf("%*s", 17 - charNb > 0 ? 17 - charNb + 1: 1, " ");
927 printf("%u (%.2f%%)%n", messageStats->tooFastNb, (double)
928 messageStats->tooFastNb / messageStats->total * 100,
929 &charNb);
930 printf("%*s%-10u %u\n", 17 - charNb > 0 ? 17 - charNb + 1:
931 1, " ", messageStats->noRTTInfoNb, messageStats->total);
932
933 totInversion+= messageStats->inversionNb;
934 totTooFast+= messageStats->tooFastNb;
935 totNoInfo+= messageStats->noRTTInfoNb;
936 totTotal+= messageStats->total;
937 }
938 }
939 }
940
941 printf("\t\t total ");
942 printf("%u (%.2f%%)%n", totInversion, (double) totInversion / totTotal *
943 100, &charNb);
944 printf("%*s", 17 - charNb > 0 ? 17 - charNb + 1: 1, " ");
945 printf("%u (%.2f%%)%n", totTooFast, (double) totTooFast / totTotal * 100,
946 &charNb);
947 printf("%*s%-10u %u\n", 17 - charNb > 0 ? 17 - charNb + 1: 1, " ",
948 totNoInfo, totTotal);
949
950 printf("\tRound-trip times:\n"
951 "\t\tHost pair RTT from exchanges RTTs from file (ms)\n");
952 g_hash_table_foreach(analysisData->stats->exchangeRtt,
953 &ghfPrintExchangeRtt, analysisData->rttInfo);
954 }
955
956
957 /*
958 * A GHFunc for g_hash_table_foreach()
959 *
960 * Args:
961 * key: RttKey* where saddr < daddr
962 * value: double*, RTT estimated from exchanges
963 * user_data GHashTable* rttInfo
964 */
965 static void ghfPrintExchangeRtt(gpointer key, gpointer value, gpointer
966 user_data)
967 {
968 char addr1[16], addr2[16];
969 struct RttKey* rttKey1= key;
970 struct RttKey rttKey2= {rttKey1->daddr, rttKey1->saddr};
971 double* fileRtt1, *fileRtt2;
972 GHashTable* rttInfo= user_data;
973
974 convertIP(addr1, rttKey1->saddr);
975 convertIP(addr2, rttKey1->daddr);
976
977 fileRtt1= g_hash_table_lookup(rttInfo, rttKey1);
978 fileRtt2= g_hash_table_lookup(rttInfo, &rttKey2);
979
980 printf("\t\t(%15s, %-15s) %-18.3f ", addr1, addr2, *(double*) value * 1e3);
981
982 if (fileRtt1 || fileRtt2)
983 {
984 if (fileRtt1)
985 {
986 printf("%.3f", *fileRtt1 * 1e3);
987 }
988 if (fileRtt1 && fileRtt2)
989 {
990 printf(", ");
991 }
992 if (fileRtt2)
993 {
994 printf("%.3f", *fileRtt2 * 1e3);
995 }
996 }
997 else
998 {
999 printf("-");
1000 }
1001 printf("\n");
1002 }
1003
1004
1005 /*
1006 * A GHashFunc for g_hash_table_new()
1007 *
1008 * Args:
1009 * key struct RttKey*
1010 */
1011 static guint ghfRttKeyHash(gconstpointer key)
1012 {
1013 struct RttKey* rttKey;
1014 uint32_t a, b, c;
1015
1016 rttKey= (struct RttKey*) key;
1017
1018 a= rttKey->saddr;
1019 b= rttKey->daddr;
1020 c= 0;
1021 final(a, b, c);
1022
1023 return c;
1024 }
1025
1026
1027 /*
1028 * A GDestroyNotify function for g_hash_table_new_full()
1029 *
1030 * Args:
1031 * data: struct RttKey*
1032 */
1033 static void gdnDestroyRttKey(gpointer data)
1034 {
1035 free(data);
1036 }
1037
1038
1039 /*
1040 * A GDestroyNotify function for g_hash_table_new_full()
1041 *
1042 * Args:
1043 * data: double*
1044 */
1045 static void gdnDestroyDouble(gpointer data)
1046 {
1047 free(data);
1048 }
1049
1050
1051 /*
1052 * A GEqualFunc for g_hash_table_new()
1053 *
1054 * Args:
1055 * a, b RttKey*
1056 *
1057 * Returns:
1058 * TRUE if both values are equal
1059 */
1060 static gboolean gefRttKeyEqual(gconstpointer a, gconstpointer b)
1061 {
1062 const struct RttKey* rkA, * rkB;
1063
1064 rkA= (struct RttKey*) a;
1065 rkB= (struct RttKey*) b;
1066
1067 if (rkA->saddr == rkB->saddr && rkA->daddr == rkB->daddr)
1068 {
1069 return TRUE;
1070 }
1071 else
1072 {
1073 return FALSE;
1074 }
1075 }
1076
1077
1078 /*
1079 * Read a file contain minimum round trip time values and fill an array with
1080 * them. The file is formatted as such:
1081 * <host1 IP> <host2 IP> <RTT in milliseconds>
1082 * ip's should be in dotted quad format
1083 *
1084 * Args:
1085 * rttInfo: double* rttInfo[RttKey], empty table, will be filled
1086 * rttStream: stream from which to read
1087 */
1088 static void readRttInfo(GHashTable* rttInfo, FILE* rttStream)
1089 {
1090 char* line= NULL;
1091 size_t len;
1092 int retval;
1093
1094 positionStream(rttStream);
1095 retval= getline(&line, &len, rttStream);
1096 while(!feof(rttStream))
1097 {
1098 struct RttKey* rttKey;
1099 char saddrDQ[20], daddrDQ[20];
1100 double* rtt;
1101 char tmp;
1102 struct in_addr addr;
1103 unsigned int i;
1104 struct {
1105 char* dq;
1106 size_t offset;
1107 } loopValues[] = {
1108 {saddrDQ, offsetof(struct RttKey, saddr)},
1109 {daddrDQ, offsetof(struct RttKey, daddr)}
1110 };
1111
1112 if (retval == -1 && !feof(rttStream))
1113 {
1114 g_error("%s", strerror(errno));
1115 }
1116
1117 if (line[retval - 1] == '\n')
1118 {
1119 line[retval - 1]= '\0';
1120 }
1121
1122 rtt= malloc(sizeof(double));
1123 retval= sscanf(line, " %19s %19s %lf %c", saddrDQ, daddrDQ, rtt,
1124 &tmp);
1125 if (retval == EOF)
1126 {
1127 g_error("%s", strerror(errno));
1128 }
1129 else if (retval != 3)
1130 {
1131 g_error("Error parsing RTT file, line was '%s'", line);
1132 }
1133
1134 rttKey= malloc(sizeof(struct RttKey));
1135 for (i= 0; i < sizeof(loopValues) / sizeof(*loopValues); i++)
1136 {
1137 retval= inet_aton(loopValues[i].dq, &addr);
1138 if (retval == 0)
1139 {
1140 g_error("Error converting address '%s'", loopValues[i].dq);
1141 }
1142 *(uint32_t*) ((void*) rttKey + loopValues[i].offset)=
1143 addr.s_addr;
1144 }
1145
1146 *rtt/= 1e3;
1147 g_debug("rttInfo, Inserting (%u, %u)->(%f)", rttKey->saddr,
1148 rttKey->daddr, *rtt);
1149 g_hash_table_insert(rttInfo, rttKey, rtt);
1150
1151 positionStream(rttStream);
1152 retval= getline(&line, &len, rttStream);
1153 }
1154
1155 if (line)
1156 {
1157 free(line);
1158 }
1159 }
1160
1161
1162 /*
1163 * Advance stream over empty space, empty lines and lines that begin with '#'
1164 *
1165 * Args:
1166 * stream: stream, at exit, will be over the first non-empty character
1167 * of a line of be at EOF
1168 */
1169 static void positionStream(FILE* stream)
1170 {
1171 int firstChar;
1172 ssize_t retval;
1173 char* line= NULL;
1174 size_t len;
1175
1176 do
1177 {
1178 firstChar= fgetc(stream);
1179 if (firstChar == (int) '#')
1180 {
1181 retval= getline(&line, &len, stream);
1182 if (retval == -1)
1183 {
1184 if (feof(stream))
1185 {
1186 goto outEof;
1187 }
1188 else
1189 {
1190 g_error("%s", strerror(errno));
1191 }
1192 }
1193 }
1194 else if (firstChar == (int) '\n' || firstChar == (int) ' ' ||
1195 firstChar == (int) '\t')
1196 {}
1197 else if (firstChar == EOF)
1198 {
1199 goto outEof;
1200 }
1201 else
1202 {
1203 break;
1204 }
1205 } while (true);
1206 retval= ungetc(firstChar, stream);
1207 if (retval == EOF)
1208 {
1209 g_error("Error: ungetc()");
1210 }
1211
1212 outEof:
1213 if (line)
1214 {
1215 free(line);
1216 }
1217 }
1218
1219
1220 /*
1221 * A GFunc for g_queue_foreach()
1222 *
1223 * Args:
1224 * data Event*, a UDP broadcast event
1225 * user_data double*, the running sum
1226 *
1227 * Returns:
1228 * Adds the time of the event to the sum
1229 */
1230 static void gfSum(gpointer data, gpointer userData)
1231 {
1232 Event* event= (Event*) data;
1233
1234 *(double*) userData+= event->wallTime.seconds + event->wallTime.nanosec /
1235 1e9;
1236 }
1237
1238
1239 /*
1240 * A GFunc for g_queue_foreach()
1241 *
1242 * Args:
1243 * data Event*, a UDP broadcast event
1244 * user_data double*, the running sum
1245 *
1246 * Returns:
1247 * Adds the square of the time of the event to the sum
1248 */
1249 static void gfSumSquares(gpointer data, gpointer userData)
1250 {
1251 Event* event= (Event*) data;
1252
1253 *(double*) userData+= pow(event->wallTime.seconds + event->wallTime.nanosec
1254 / 1e9, 2.);
1255 }
1256
1257
1258 /*
1259 * Update a struct Bins according to a new value
1260 *
1261 * Args:
1262 * bins: the structure containing bins to build a histrogram
1263 * value: the new value
1264 */
1265 static void hitBin(struct Bins* const bins, const double value)
1266 {
1267 unsigned int binN= binNum(value);
1268
1269 if (binN < bins->min)
1270 {
1271 bins->min= binN;
1272 }
1273 else if (binN > bins->max)
1274 {
1275 bins->max= binN;
1276 }
1277
1278 bins->total++;
1279
1280 bins->bin[binN]++;
1281 }
1282
1283
1284 /*
1285 * Figure out the bin in a histogram to which a value belongs.
1286 *
1287 * This uses exponentially sized bins that go from 0 to infinity.
1288 *
1289 * Args:
1290 * value: in the range -INFINITY to INFINITY
1291 *
1292 * Returns:
1293 * The number of the bin in a struct Bins.bin
1294 */
1295 static unsigned int binNum(const double value)
1296 {
1297 if (value <= 0)
1298 {
1299 return 0;
1300 }
1301 else if (value < binEnd(1))
1302 {
1303 return 1;
1304 }
1305 else if (value >= binStart(BIN_NB - 1))
1306 {
1307 return BIN_NB - 1;
1308 }
1309 else
1310 {
1311 return floor(log(value) / log(binBase)) + BIN_NB + 1;
1312 }
1313 }
1314
1315
1316 /*
1317 * Figure out the start of the interval of a bin in a histogram. See struct
1318 * Bins.
1319 *
1320 * This uses exponentially sized bins that go from 0 to infinity.
1321 *
1322 * Args:
1323 * binNum: bin number
1324 *
1325 * Return:
1326 * The start of the interval, this value is included in the interval (except
1327 * for -INFINITY, naturally)
1328 */
1329 static double binStart(const unsigned int binNum)
1330 {
1331 g_assert_cmpuint(binNum, <, BIN_NB);
1332
1333 if (binNum == 0)
1334 {
1335 return -INFINITY;
1336 }
1337 else if (binNum == 1)
1338 {
1339 return 0.;
1340 }
1341 else
1342 {
1343 return pow(binBase, (double) binNum - BIN_NB + 1);
1344 }
1345 }
1346
1347
1348 /*
1349 * Figure out the end of the interval of a bin in a histogram. See struct
1350 * Bins.
1351 *
1352 * This uses exponentially sized bins that go from 0 to infinity.
1353 *
1354 * Args:
1355 * binNum: bin number
1356 *
1357 * Return:
1358 * The end of the interval, this value is not included in the interval
1359 */
1360 static double binEnd(const unsigned int binNum)
1361 {
1362 g_assert_cmpuint(binNum, <, BIN_NB);
1363
1364 if (binNum == 0)
1365 {
1366 return 0.;
1367 }
1368 else if (binNum < BIN_NB - 1)
1369 {
1370 return pow(binBase, (double) binNum - BIN_NB + 2);
1371 }
1372 else
1373 {
1374 return INFINITY;
1375 }
1376 }
1377
1378
1379 /*
1380 * Return the total number of elements in the "normal" bins (not underflow or
1381 * overflow)
1382 *
1383 * Args:
1384 * bins: the structure containing bins to build a histrogram
1385 */
1386 static uint32_t normalTotal(struct Bins* const bins)
1387 {
1388 return bins->total - bins->bin[0] - bins->bin[BIN_NB - 1];
1389 }
1390
1391
1392 /* Update the bounds between two traces
1393 *
1394 * Args:
1395 * bounds: the array containing all the trace-pair bounds
1396 * e1, e2: the two related events
1397 */
1398 static void updateBounds(Bounds** const bounds, Event* const e1, Event* const
1399 e2)
1400 {
1401 unsigned int traceI, traceJ;
1402 uint64_t messageTime;
1403 Bounds* tpBounds;
1404
1405 if (e1->traceNum < e2->traceNum)
1406 {
1407 traceI= e2->traceNum;
1408 traceJ= e1->traceNum;
1409 messageTime= e1->cpuTime;
1410 }
1411 else
1412 {
1413 traceI= e1->traceNum;
1414 traceJ= e2->traceNum;
1415 messageTime= e2->cpuTime;
1416 }
1417 tpBounds= &bounds[traceI][traceJ];
1418
1419 if (messageTime < tpBounds->min)
1420 {
1421 tpBounds->min= messageTime;
1422 }
1423 if (messageTime > tpBounds->max)
1424 {
1425 tpBounds->max= messageTime;
1426 }
1427 }
1428
1429
1430 /*
1431 * Write the analysis-specific graph lines in the gnuplot script.
1432 *
1433 * Args:
1434 * syncState: container for synchronization data
1435 * i: first trace number
1436 * j: second trace number, garanteed to be larger than i
1437 */
1438 static void writeAnalysisTraceTimeBackPlotsEval(SyncState* const syncState,
1439 const unsigned int i, const unsigned int j)
1440 {
1441 SyncState* chullSS= ((AnalysisDataEval*)
1442 syncState->analysisData)->graphs->chullSS;
1443 const GraphFunctions* graphFunctions=
1444 &chullSS->analysisModule->graphFunctions;
1445
1446 if (graphFunctions->writeTraceTimeBackPlots != NULL)
1447 {
1448 graphFunctions->writeTraceTimeBackPlots(chullSS, i, j);
1449 }
1450 }
1451
1452
1453 /*
1454 * Write the analysis-specific graph lines in the gnuplot script.
1455 *
1456 * Args:
1457 * syncState: container for synchronization data
1458 * i: first trace number
1459 * j: second trace number, garanteed to be larger than i
1460 */
1461 static void writeAnalysisTraceTimeForePlotsEval(SyncState* const syncState,
1462 const unsigned int i, const unsigned int j)
1463 {
1464 SyncState* chullSS= ((AnalysisDataEval*)
1465 syncState->analysisData)->graphs->chullSS;
1466 const GraphFunctions* graphFunctions=
1467 &chullSS->analysisModule->graphFunctions;
1468
1469 if (graphFunctions->writeTraceTimeForePlots != NULL)
1470 {
1471 graphFunctions->writeTraceTimeForePlots(chullSS, i, j);
1472 }
1473 }
This page took 0.060047 seconds and 4 git commands to generate.