1 /* This file is part of the Linux Trace Toolkit viewer
2 * Copyright (C) 2009 Benjamin Poirier <benjamin.poirier@polymtl.ca>
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;
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.
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,
25 #include <arpa/inet.h>
28 #include <netinet/in.h>
33 #include <sys/socket.h>
36 #include "sync_chain.h"
38 #include "event_analysis_eval.h"
41 // Functions common to all analysis modules
42 static void initAnalysisEval(SyncState
* const syncState
);
43 static void destroyAnalysisEval(SyncState
* const syncState
);
45 static void analyzeMessageEval(SyncState
* const syncState
, Message
* const
47 static void analyzeExchangeEval(SyncState
* const syncState
, Exchange
* const
49 static void analyzeBroadcastEval(SyncState
* const syncState
, Broadcast
* const
51 static GArray
* finalizeAnalysisEval(SyncState
* const syncState
);
52 static void printAnalysisStatsEval(SyncState
* const syncState
);
54 // Functions specific to this module
55 static void registerAnalysisEval() __attribute__((constructor (102)));
56 static guint
ghfRttKeyHash(gconstpointer key
);
57 static gboolean
gefRttKeyEqual(gconstpointer a
, gconstpointer b
);
58 static void gdnDestroyRttKey(gpointer data
);
59 static void gdnDestroyDouble(gpointer data
);
60 static void readRttInfo(GHashTable
* rttInfo
, FILE* rttFile
);
61 static void positionStream(FILE* stream
);
63 static void gfSum(gpointer data
, gpointer userData
);
64 static void gfSumSquares(gpointer data
, gpointer userData
);
67 static AnalysisModule analysisModuleEval
= {
69 .initAnalysis
= &initAnalysisEval
,
70 .destroyAnalysis
= &destroyAnalysisEval
,
71 .analyzeMessage
= &analyzeMessageEval
,
72 .analyzeExchange
= &analyzeExchangeEval
,
73 .analyzeBroadcast
= &analyzeBroadcastEval
,
74 .finalizeAnalysis
= &finalizeAnalysisEval
,
75 .printAnalysisStats
= &printAnalysisStatsEval
,
76 .writeAnalysisGraphsPlots
= NULL
,
77 .writeAnalysisGraphsOptions
= NULL
,
80 static ModuleOption optionEvalRttFile
= {
81 .longName
= "eval-rtt-file",
82 .hasArg
= REQUIRED_ARG
,
84 .optionHelp
= "specify the file containing rtt information",
90 * Analysis module registering function
92 static void registerAnalysisEval()
94 g_queue_push_tail(&analysisModules
, &analysisModuleEval
);
95 g_queue_push_tail(&moduleOptions
, &optionEvalRttFile
);
100 * Analysis init function
102 * This function is called at the beginning of a synchronization run for a set
106 * syncState container for synchronization data.
108 static void initAnalysisEval(SyncState
* const syncState
)
110 AnalysisDataEval
* analysisData
;
113 analysisData
= malloc(sizeof(AnalysisDataEval
));
114 syncState
->analysisData
= analysisData
;
116 analysisData
->rttInfo
= g_hash_table_new_full(&ghfRttKeyHash
,
117 &gefRttKeyEqual
, &gdnDestroyRttKey
, &gdnDestroyDouble
);
118 if (optionEvalRttFile
.arg
)
123 rttStream
= fopen(optionEvalRttFile
.arg
, "r");
124 if (rttStream
== NULL
)
126 g_error(strerror(errno
));
129 readRttInfo(analysisData
->rttInfo
, rttStream
);
131 retval
= fclose(rttStream
);
134 g_error(strerror(errno
));
138 if (syncState
->stats
)
140 analysisData
->stats
= calloc(1, sizeof(AnalysisStatsEval
));
141 analysisData
->stats
->broadcastDiffSum
= 0.;
143 analysisData
->stats
->messageStats
= malloc(syncState
->traceNb
*
144 sizeof(MessageStats
*));
145 for (i
= 0; i
< syncState
->traceNb
; i
++)
147 analysisData
->stats
->messageStats
[i
]= calloc(syncState
->traceNb
,
148 sizeof(MessageStats
));
155 * Analysis destroy function
157 * Free the analysis specific data structures
160 * syncState container for synchronization data.
162 static void destroyAnalysisEval(SyncState
* const syncState
)
165 AnalysisDataEval
* analysisData
;
167 analysisData
= (AnalysisDataEval
*) syncState
->analysisData
;
169 if (analysisData
== NULL
|| analysisData
->rttInfo
== NULL
)
174 g_hash_table_destroy(analysisData
->rttInfo
);
175 analysisData
->rttInfo
= NULL
;
177 if (syncState
->stats
)
179 for (i
= 0; i
< syncState
->traceNb
; i
++)
181 free(analysisData
->stats
->messageStats
[i
]);
183 free(analysisData
->stats
->messageStats
);
184 free(analysisData
->stats
);
187 free(syncState
->analysisData
);
188 syncState
->analysisData
= NULL
;
193 * Perform analysis on an event pair.
195 * Check if there is message inversion or messages that are too fast.
198 * syncState container for synchronization data
199 * message structure containing the events
201 static void analyzeMessageEval(SyncState
* const syncState
, Message
* const message
)
203 AnalysisDataEval
* analysisData
;
204 MessageStats
* messageStats
;
207 struct RttKey rttKey
;
209 if (!syncState
->stats
)
214 analysisData
= (AnalysisDataEval
*) syncState
->analysisData
;
216 &analysisData
->stats
->messageStats
[message
->outE
->traceNum
][message
->inE
->traceNum
];
218 messageStats
->total
++;
220 tt
= wallTimeSub(&message
->inE
->wallTime
, &message
->outE
->wallTime
);
223 messageStats
->inversionNb
++;
226 g_assert(message
->inE
->type
== UDP
);
227 rttKey
.saddr
= message
->inE
->event
.udpEvent
->datagramKey
->saddr
;
228 rttKey
.daddr
= message
->inE
->event
.udpEvent
->datagramKey
->daddr
;
229 rttInfo
= g_hash_table_lookup(analysisData
->rttInfo
, &rttKey
);
233 if (tt
< *rttInfo
/ 2.)
235 messageStats
->tooFastNb
++;
240 messageStats
->noRTTInfoNb
++;
246 * Perform analysis on multiple messages
251 * syncState container for synchronization data
252 * exchange structure containing the messages
254 static void analyzeExchangeEval(SyncState
* const syncState
, Exchange
* const exchange
)
256 AnalysisDataEval
* analysisData
;
258 analysisData
= (AnalysisDataEval
*) syncState
->analysisData
;
263 * Perform analysis on muliple events
265 * Sum the broadcast differential delays
268 * syncState container for synchronization data
269 * broadcast structure containing the events
271 static void analyzeBroadcastEval(SyncState
* const syncState
, Broadcast
* const broadcast
)
273 AnalysisDataEval
* analysisData
;
274 double sum
= 0, squaresSum
= 0;
277 if (!syncState
->stats
)
282 analysisData
= (AnalysisDataEval
*) syncState
->analysisData
;
284 g_queue_foreach(broadcast
->events
, &gfSum
, &sum
);
285 g_queue_foreach(broadcast
->events
, &gfSumSquares
, &squaresSum
);
287 analysisData
->stats
->broadcastNb
++;
288 // Because of numerical errors, this can at times be < 0
289 y
= squaresSum
/ g_queue_get_length(broadcast
->events
) - pow(sum
/
290 g_queue_get_length(broadcast
->events
), 2.);
293 analysisData
->stats
->broadcastDiffSum
+= sqrt(y
);
299 * Finalize the factor calculations
301 * Since this module does not really calculate factors, identity factors are
305 * syncState container for synchronization data.
308 * Factors[traceNb] synchronization factors for each trace
310 static GArray
* finalizeAnalysisEval(SyncState
* const syncState
)
315 factors
= g_array_sized_new(FALSE
, FALSE
, sizeof(Factors
),
317 g_array_set_size(factors
, syncState
->traceNb
);
318 for (i
= 0; i
< syncState
->traceNb
; i
++)
322 e
= &g_array_index(factors
, Factors
, i
);
332 * Print statistics related to analysis. Must be called after
336 * syncState container for synchronization data.
338 static void printAnalysisStatsEval(SyncState
* const syncState
)
340 AnalysisDataEval
* analysisData
;
343 if (!syncState
->stats
)
348 analysisData
= (AnalysisDataEval
*) syncState
->analysisData
;
350 printf("Synchronization evaluation analysis stats:\n");
351 printf("\tsum of broadcast differential delays: %g\n",
352 analysisData
->stats
->broadcastDiffSum
);
353 printf("\taverage broadcast differential delays: %g\n",
354 analysisData
->stats
->broadcastDiffSum
/
355 analysisData
->stats
->broadcastNb
);
357 printf("\tIndividual evaluation:\n"
358 "\t\tTrace pair Inversions Too fast (No RTT info) Total\n");
360 for (i
= 0; i
< syncState
->traceNb
; i
++)
362 for (j
= i
+ 1; j
< syncState
->traceNb
; j
++)
364 MessageStats
* messageStats
;
365 const char* format
= "\t\t%3d - %-3d %-10u %-10u %-10u %u\n";
367 messageStats
= &analysisData
->stats
->messageStats
[i
][j
];
369 printf(format
, i
, j
, messageStats
->inversionNb
, messageStats
->tooFastNb
,
370 messageStats
->noRTTInfoNb
, messageStats
->total
);
372 messageStats
= &analysisData
->stats
->messageStats
[j
][i
];
374 printf(format
, j
, i
, messageStats
->inversionNb
, messageStats
->tooFastNb
,
375 messageStats
->noRTTInfoNb
, messageStats
->total
);
382 * A GHashFunc for g_hash_table_new()
387 static guint
ghfRttKeyHash(gconstpointer key
)
389 struct RttKey
* rttKey
;
392 rttKey
= (struct RttKey
*) key
;
404 * A GDestroyNotify function for g_hash_table_new_full()
407 * data: struct RttKey*
409 static void gdnDestroyRttKey(gpointer data
)
416 * A GDestroyNotify function for g_hash_table_new_full()
421 static void gdnDestroyDouble(gpointer data
)
428 * A GEqualFunc for g_hash_table_new()
434 * TRUE if both values are equal
436 static gboolean
gefRttKeyEqual(gconstpointer a
, gconstpointer b
)
438 const struct RttKey
* rkA
, * rkB
;
440 rkA
= (struct RttKey
*) a
;
441 rkB
= (struct RttKey
*) b
;
443 if (rkA
->saddr
== rkB
->saddr
&& rkA
->daddr
== rkB
->daddr
)
455 * Read a file contain minimum round trip time values and fill an array with
456 * them. The file is formatted as such:
457 * <host1 IP> <host2 IP> <RTT in milliseconds>
458 * ip's should be in dotted quad format
461 * rttInfo: double* rttInfo[RttKey], empty table, will be filled
462 * rttStream: stream from which to read
464 static void readRttInfo(GHashTable
* rttInfo
, FILE* rttStream
)
470 positionStream(rttStream
);
471 retval
= getline(&line
, &len
, rttStream
);
472 while(!feof(rttStream
))
474 struct RttKey
* rttKey
;
475 char saddrDQ
[20], daddrDQ
[20];
484 {saddrDQ
, offsetof(struct RttKey
, saddr
)},
485 {daddrDQ
, offsetof(struct RttKey
, daddr
)}
488 if (retval
== -1 && !feof(rttStream
))
490 g_error(strerror(errno
));
493 if (line
[retval
- 1] == '\n')
495 line
[retval
- 1]= '\0';
498 rtt
= malloc(sizeof(double));
499 retval
= sscanf(line
, " %19s %19s %lf %c", saddrDQ
, daddrDQ
, rtt
,
503 g_error(strerror(errno
));
505 else if (retval
!= 3)
507 g_error("Error parsing RTT file, line was '%s'", line
);
510 rttKey
= malloc(sizeof(struct RttKey
));
511 for (i
= 0; i
< sizeof(loopValues
) / sizeof(*loopValues
); i
++)
513 retval
= inet_aton(loopValues
[i
].dq
, &addr
);
516 g_error("Error converting address '%s'", loopValues
[i
].dq
);
518 *(uint32_t*) ((void*) rttKey
+ loopValues
[i
].offset
)=
523 g_hash_table_insert(rttInfo
, rttKey
, rtt
);
525 positionStream(rttStream
);
526 retval
= getline(&line
, &len
, rttStream
);
537 * Advance stream over empty space, empty lines and lines that begin with '#'
540 * stream: stream, at exit, will be over the first non-empty character
541 * of a line of be at EOF
543 static void positionStream(FILE* stream
)
552 firstChar
= fgetc(stream
);
553 if (firstChar
== (int) '#')
555 retval
= getline(&line
, &len
, stream
);
564 g_error(strerror(errno
));
568 else if (firstChar
== (int) '\n' || firstChar
== (int) ' ' ||
569 firstChar
== (int) '\t')
571 else if (firstChar
== EOF
)
580 retval
= ungetc(firstChar
, stream
);
583 g_error("Error: ungetc()");
595 * A GFunc for g_queue_foreach()
598 * data Event*, a UDP broadcast event
599 * user_data double*, the running sum
602 * Adds the time of the event to the sum
604 static void gfSum(gpointer data
, gpointer userData
)
606 Event
* event
= (Event
*) data
;
608 *(double*) userData
+= event
->wallTime
.seconds
+ event
->wallTime
.nanosec
/
614 * A GFunc for g_queue_foreach()
617 * data Event*, a UDP broadcast event
618 * user_data double*, the running sum
621 * Adds the square of the time of the event to the sum
623 static void gfSumSquares(gpointer data
, gpointer userData
)
625 Event
* event
= (Event
*) data
;
627 *(double*) userData
+= pow(event
->wallTime
.seconds
+ event
->wallTime
.nanosec