Remove unused g_info definitions
[lttv.git] / lttv / lttv / sync / unittest.c
CommitLineData
84baf72b
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
19#define _GNU_SOURCE
20
21#ifdef HAVE_CONFIG_H
22#include <config.h>
23#endif
24
25#include <errno.h>
26#include <fcntl.h>
27#include <getopt.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <sys/resource.h>
31#include <sys/stat.h>
32#include <sys/time.h>
33#include <sys/types.h>
34#include <sys/stat.h>
35#include <unistd.h>
36
2bd4b3e4 37#include "sync_chain.h"
84baf72b
BP
38
39
84baf72b
BP
40static void timeDiff(struct timeval* const end, const struct timeval* const start);
41static void usage(const char* const programName);
42static gint gcfCompareAnalysis(gconstpointer a, gconstpointer b);
43static void gfAppendAnalysisName(gpointer data, gpointer user_data);
44static unsigned int readTraceNb(FILE* testCase);
45static void skipCommentLines(FILE* testCase);
46static void processEvents(SyncState* const syncState, FILE* testCase);
47static void nullLog(const gchar *log_domain, GLogLevelFlags log_level, const
48 gchar *message, gpointer user_data);
49
50GQueue processingModules= G_QUEUE_INIT;
51GQueue matchingModules= G_QUEUE_INIT;
52GQueue analysisModules= G_QUEUE_INIT;
53
54// time values in test case files will be scaled by this factor
55const double freq= 1e9;
56
57
58/*
59 * Create matching and analysis modules and feed them events read from a text
60 * file.
61 *
62 * Idealy, this would've been a processing module but sync_chain.c and
63 * ProcessingModule use some LTTV-specific types and functions. Unfortunately,
64 * there is some code duplication from sync_chain.c
65 *
66 */
67int main(int argc, char* argv[])
68{
69 int c;
70 extern char* optarg;
71 extern int optind, opterr, optopt;
72 bool optionSyncStats= false;
73 char* optionGraphsDir= NULL;
74 FILE* testCase= NULL;
75
76 SyncState* syncState;
77 struct timeval startTime, endTime;
78 struct rusage startUsage, endUsage;
79 GList* result;
80 char* cwd;
81 FILE* graphsStream;
82 int graphsFp;
83 GArray* factors;
84
85 int retval;
86
87 syncState= malloc(sizeof(SyncState));
88
89 g_assert(g_queue_get_length(&analysisModules) > 0);
90 syncState->analysisModule= g_queue_peek_head(&analysisModules);
91
92 do
93 {
94 int optionIndex= 0;
95
96 static struct option longOptions[]=
97 {
98 {"sync-stats", no_argument, 0, 's'},
99 {"sync-graphs", optional_argument, 0, 'g'},
100 {"sync-analysis", required_argument, 0, 'a'},
101 {0, 0, 0, 0}
102 };
103
104 c= getopt_long(argc, argv, "sg::a:", longOptions, &optionIndex);
105
106 switch (c)
107 {
108 case -1:
109 case 0:
110 break;
111
112 case 's':
113 if (!optionSyncStats)
114 {
115 gettimeofday(&startTime, 0);
116 getrusage(RUSAGE_SELF, &startUsage);
117 }
118 optionSyncStats= true;
119 break;
120
121 case 'g':
122 if (optarg)
123 {
124 printf("xxx:%s\n", optarg);
125 optionGraphsDir= malloc(strlen(optarg));
126 strcpy(optionGraphsDir, optarg);
127 }
128 else
129 {
130 optionGraphsDir= malloc(20);
131 retval= snprintf(optionGraphsDir, 20, "graphs-%d",
132 getpid());
133 if (retval > 20 - 1)
134 {
135 optionGraphsDir[20 - 1]= '\0';
136 }
137 }
138 break;
139
140 case 'a':
141 printf("xxx:%s\n", optarg);
142 result= g_queue_find_custom(&analysisModules, optarg,
143 &gcfCompareAnalysis);
144 if (result != NULL)
145 {
146 syncState->analysisModule= (AnalysisModule*) result->data;
147 }
148 else
149 {
150 g_error("Analysis module '%s' not found", optarg);
151 }
152 break;
153
154 case '?':
155 usage(argv[0]);
156 abort();
157
158 default:
159 g_error("Option parse error");
160 }
161 } while (c != -1);
162
163 if (argc <= optind)
164 {
165 fprintf(stderr, "Test file unspecified\n");
166 usage(argv[0]);
167 abort();
168 }
169
170 testCase= fopen(argv[optind], "r");
171 if (testCase == NULL)
172 {
173 g_error(strerror(errno));
174 }
175
176 // Initialize data structures
177 syncState->traceNb= readTraceNb(testCase);
178
179 if (optionSyncStats)
180 {
181 syncState->stats= true;
182 }
183 else
184 {
185 syncState->stats= false;
186 }
187
188 syncState->graphs= optionGraphsDir;
189
190 if (!optionSyncStats)
191 {
192 g_log_set_handler(NULL, G_LOG_LEVEL_DEBUG, nullLog, NULL);
193 }
194
195 // Identify and initialize matching and analysis modules
196 syncState->matchingData= NULL;
197 syncState->analysisData= NULL;
198
199 g_assert(g_queue_get_length(&matchingModules) == 1);
200 syncState->matchingModule= (MatchingModule*)
201 g_queue_peek_head(&matchingModules);
202
203 graphsStream= NULL;
204 if (syncState->graphs)
205 {
206 // Create the graph directory right away in case the module initialization
207 // functions have something to write in it.
208 cwd= changeToGraphDir(syncState->graphs);
209
210 if (syncState->matchingModule->writeMatchingGraphsPlots != NULL)
211 {
212 if ((graphsFp= open("graphs.gnu", O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR |
213 S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH
214 | S_IWOTH | S_IXOTH)) == -1)
215 {
216 g_error(strerror(errno));
217 }
218 if ((graphsStream= fdopen(graphsFp, "w")) == NULL)
219 {
220 g_error(strerror(errno));
221 }
222 }
223
224 retval= chdir(cwd);
225 if (retval == -1)
226 {
227 g_error(strerror(errno));
228 }
229 free(cwd);
230 }
231
232 syncState->matchingModule->initMatching(syncState);
233 syncState->analysisModule->initAnalysis(syncState);
234
235 // Process traceset
236 processEvents(syncState, testCase);
237
238 factors= syncState->matchingModule->finalizeMatching(syncState);
239
240 // Write graphs file
241 if (graphsStream != NULL)
242 {
243 unsigned int i, j;
244
245 fprintf(graphsStream,
246 "#!/usr/bin/gnuplot\n\n"
247 "#set terminal postscript eps color size 8in,6in\n");
248
249 // Cover the upper triangular matrix, i is the reference node.
250 for (i= 0; i < syncState->traceNb; i++)
251 {
252 for (j= i + 1; j < syncState->traceNb; j++)
253 {
254 long pos;
255
256 fprintf(graphsStream,
257 "\n#set output \"%03d-%03d.eps\"\n"
258 "plot \\\n", i, j);
259
260 syncState->matchingModule->writeMatchingGraphsPlots(graphsStream,
261 syncState, i, j);
262
263 // Remove the ", \\\n" from the last graph plot line
264 fflush(graphsStream);
265 pos= ftell(graphsStream);
266 if (ftruncate(fileno(graphsStream), pos - 4) == -1)
267 {
268 g_error(strerror(errno));
269 }
270 if (fseek(graphsStream, 0, SEEK_END) == -1)
271 {
272 g_error(strerror(errno));
273 }
274
275 fprintf(graphsStream,
276 "\nset output \"%1$03d-%2$03d.eps\"\n"
277 "set title \"\"\n"
278 "set xlabel \"Clock %1$u\"\n"
279 "set xtics nomirror\n"
280 "set ylabel \"Clock %2$u\"\n"
281 "set ytics nomirror\n"
282 "set x2label \"Clock %1$u (s)\"\n"
283 "set x2range [GPVAL_X_MIN / %3$.1f : GPVAL_X_MAX / %3$.1f]\n"
284 "set x2tics\n"
285 "set y2label \"Clock %2$u (s)\"\n"
286 "set y2range [GPVAL_Y_MIN / %3$.1f: GPVAL_Y_MAX / %3$.1f]\n"
287 "set y2tics\n"
288 "set key inside right bottom\n", i, j, freq);
289
290 syncState->matchingModule->writeMatchingGraphsOptions(graphsStream,
291 syncState, i, j);
292
293 fprintf(graphsStream, "replot\n\n"
294 "pause -1\n");
295 }
296 }
297
298 if (fclose(graphsStream) != 0)
299 {
300 g_error(strerror(errno));
301 }
302 }
303 if (optionGraphsDir)
304 {
305 free(optionGraphsDir);
306 }
307
308 if (optionSyncStats && syncState->matchingModule->printMatchingStats !=
309 NULL)
310 {
311 unsigned int i;
312
313 syncState->matchingModule->printMatchingStats(syncState);
314
315 printf("Resulting synchronization factors:\n");
316 for (i= 0; i < syncState->traceNb; i++)
317 {
318 Factors* traceFactors;
319
320 traceFactors= &g_array_index(factors, Factors, i);
321 printf("\ttrace %u drift= %g offset= %g\n", i,
322 traceFactors->drift, traceFactors->offset);
323 }
324 }
325
326 syncState->matchingModule->destroyMatching(syncState);
327 syncState->analysisModule->destroyAnalysis(syncState);
328
329 free(syncState);
330
331 if (optionSyncStats)
332 {
333 gettimeofday(&endTime, 0);
334 retval= getrusage(RUSAGE_SELF, &endUsage);
335
336 timeDiff(&endTime, &startTime);
337 timeDiff(&endUsage.ru_utime, &startUsage.ru_utime);
338 timeDiff(&endUsage.ru_stime, &startUsage.ru_stime);
339
340 printf("Synchronization time:\n");
341 printf("\treal time: %ld.%06ld\n", endTime.tv_sec, endTime.tv_usec);
342 printf("\tuser time: %ld.%06ld\n", endUsage.ru_utime.tv_sec,
343 endUsage.ru_utime.tv_usec);
344 printf("\tsystem time: %ld.%06ld\n", endUsage.ru_stime.tv_sec,
345 endUsage.ru_stime.tv_usec);
346 }
347
348 return EXIT_SUCCESS;
349}
350
351
352/*
353 * Print information about program options and arguments.
354 *
355 * Args:
356 * programName: name of the program, as contained in argv[0] for example
357 */
358static void usage(const char* const programName)
359{
360 GString* analysisModulesNames;
361
362 analysisModulesNames= g_string_new("");
363 g_queue_foreach(&analysisModules, &gfAppendAnalysisName,
364 analysisModulesNames);
365 // remove the last ", "
366 g_string_truncate(analysisModulesNames, analysisModulesNames->len - 2);
367
368 printf(
369 "%s [options] <test file>\n"
370 "Options:\n"
371 "\t-s, --sync-stats Print statistics and debug messages\n"
372 "\t-g, --sync-graphs[=OUPUT_DIR] Generate graphs\n"
373 "\t-a, --sync-analysis=MODULE_NAME Specify which module to use for analysis\n"
374 "\t Available modules: %s\n",
375 programName, analysisModulesNames->str);
376
377 g_string_free(analysisModulesNames, TRUE);
378}
379
380
381/*
382 * Calculate the elapsed time between two timeval values
383 *
384 * Args:
385 * end: end time, result is also stored in this structure
386 * start: start time
387 */
388static void timeDiff(struct timeval* const end, const struct timeval* const start)
389{
390 if (end->tv_usec >= start->tv_usec)
391 {
392 end->tv_sec-= start->tv_sec;
393 end->tv_usec-= start->tv_usec;
394 }
395 else
396 {
397 end->tv_sec= end->tv_sec - start->tv_sec - 1;
398 end->tv_usec= end->tv_usec - start->tv_usec + 1e6;
399 }
400}
401
402
403/*
404 * A GCompareFunc for g_slist_find_custom()
405 *
406 * Args:
407 * a: AnalysisModule*, element's data
408 * b: char*, user data to compare against
409 *
410 * Returns:
411 * 0 if the analysis module a's name is b
412 */
413static gint gcfCompareAnalysis(gconstpointer a, gconstpointer b)
414{
415 const AnalysisModule* analysisModule;
416 const char* name;
417
418 analysisModule= (const AnalysisModule*)a;
419 name= (const char*)b;
420
421 return strncmp(analysisModule->name, name, strlen(analysisModule->name) +
422 1);
423}
424
425
426/*
427 * Change to the directory used to hold graphs. Create it if necessary.
428 *
429 * Args:
430 * graph: name of directory
431 *
432 * Returns:
433 * The current working directory before the execution of the function. The
434 * string must be free'd by the caller.
435 */
436char* changeToGraphDir(char* const graphs)
437{
438 int retval;
439 char* cwd;
440
441 cwd= getcwd(NULL, 0);
442 if (cwd == NULL)
443 {
444 g_error(strerror(errno));
445 }
446 while ((retval= chdir(graphs)) != 0)
447 {
448 if (errno == ENOENT)
449 {
450 retval= mkdir(graphs, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP |
451 S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH);
452 if (retval != 0)
453 {
454 g_error(strerror(errno));
455 }
456 }
457 else
458 {
459 g_error(strerror(errno));
460 }
461 }
462
463 return cwd;
464}
465
466
467/*
468 * A GFunc for g_queue_foreach()
469 *
470 * Concatenate analysis module names.
471 *
472 * Args:
473 * data: AnalysisModule*
474 * user_data: GString*, concatenated names
475 */
476static void gfAppendAnalysisName(gpointer data, gpointer user_data)
477{
478 g_string_append((GString*) user_data, ((AnalysisModule*) data)->name);
479 g_string_append((GString*) user_data, ", ");
480}
481
482
483/*
484 * Read trace number from the test case stream. The trace number should be the
485 * first non-comment line and should be an unsigned int by itself on a line.
486 *
487 * Args:
488 * testCase: test case stream
489 *
490 * Returns:
491 * The trace number
492 */
493static unsigned int readTraceNb(FILE* testCase)
494{
495 unsigned int result;
496 int retval;
497 char* line= NULL;
498 size_t len;
499 char tmp;
500
501 skipCommentLines(testCase);
502 retval= getline(&line, &len, testCase);
503 if (retval == -1)
504 {
505 if (feof(testCase))
506 {
507 g_error("Unexpected end of file while looking for number of traces");
508 }
509 else
510 {
511 g_error(strerror(errno));
512 }
513 }
514 if (line[retval - 1] == '\n')
515 {
516 line[retval - 1]= '\0';
517 }
518
519 retval= sscanf(line, " %u %c", &result, &tmp);
520 if (retval == EOF || retval != 1)
521 {
522 g_error("Error parsing test file while looking for number of traces, line was '%s'", line);
523
524 // Not really needed but avoids warning from gcc
525 abort();
526 }
527
528 return result;
529}
530
531
532/*
533 * Advance testCase stream over empty space, empty lines and lines that begin
534 * with '#'
535 *
536 * Args:
537 * testCase: test case stream
538 */
539static void skipCommentLines(FILE* testCase)
540{
541 int firstChar;
542 ssize_t retval;
543 char* line= NULL;
544 size_t len;
545
546 do
547 {
548 firstChar= fgetc(testCase);
549 if (firstChar == (int) '#')
550 {
551 retval= getline(&line, &len, testCase);
552 if (retval == -1)
553 {
554 if (feof(testCase))
555 {
556 goto outEof;
557 }
558 else
559 {
560 g_error(strerror(errno));
561 }
562 }
563 }
564 else if (firstChar == (int) '\n' || firstChar == (int) ' ')
565 {}
566 else if (firstChar == EOF)
567 {
568 goto outEof;
569 }
570 else
571 {
572 break;
573 }
574 } while (true);
575 retval= ungetc(firstChar, testCase);
576 if (retval == EOF)
577 {
578 g_error("Error: ungetc()");
579 }
580
581outEof:
582 if (line)
583 {
584 free(line);
585 }
586}
587
588
589/*
590 * Make up events from the messages in the test case. Dispatch those events to
591 * the matching module.
592 */
593static void processEvents(SyncState* const syncState, FILE* testCase)
594{
595 char* line= NULL;
596 size_t len;
597 int retval;
598 unsigned int addressOffset;
599 unsigned int* seq;
600
601 // Trace numbers run from 0 to traceNb - 1. addressOffset is added to a
602 // traceNum to convert it to an address.
603 addressOffset= pow(10, floor(log(syncState->traceNb - 1) / log(10)) + 1);
604
605 seq= calloc(syncState->traceNb, sizeof(unsigned int));
606
607 skipCommentLines(testCase);
608 retval= getline(&line, &len, testCase);
609 while(!feof(testCase))
610 {
611 unsigned int sender, receiver;
612 double sendTime, recvTime;
613 char tmp;
10341d26 614 Event* event;
84baf72b
BP
615
616 if (retval == -1 && !feof(testCase))
617 {
618 g_error(strerror(errno));
619 }
620
621 if (line[len - 1] == '\n')
622 {
623 line[len - 1]= '\0';
624 }
625
626 retval= sscanf(line, " %u %u %lf %lf %c", &sender, &receiver,
627 &sendTime, &recvTime, &tmp);
628 if (retval == EOF)
629 {
630 g_error(strerror(errno));
631 }
632 else if (retval != 4)
633 {
634 g_error("Error parsing test file while looking for data point, line was '%s'", line);
635 }
636
637 if (sender + 1 > syncState->traceNb)
638 {
639 g_error("Error parsing test file, sender is out of range, line was '%s'", line);
640 }
641
642 if (receiver + 1 > syncState->traceNb)
643 {
644 g_error("Error parsing test file, receiver is out of range, line was '%s'", line);
645 }
646
647 // Output event
10341d26 648 event= malloc(sizeof(Event));
84baf72b 649 event->traceNum= sender;
10341d26
BP
650 event->time= round(sendTime * freq);
651 event->type= TCP;
652 event->destroy= &destroyTCPEvent;
653 event->event.tcpEvent= malloc(sizeof(TCPEvent));
654 event->event.tcpEvent->direction= OUT;
655 event->event.tcpEvent->segmentKey= malloc(sizeof(SegmentKey));
656 event->event.tcpEvent->segmentKey->ihl= 5;
657 event->event.tcpEvent->segmentKey->tot_len= 40;
658 event->event.tcpEvent->segmentKey->connectionKey.saddr= sender + addressOffset;
659 event->event.tcpEvent->segmentKey->connectionKey.daddr= receiver + addressOffset;
660 event->event.tcpEvent->segmentKey->connectionKey.source= 57645;
661 event->event.tcpEvent->segmentKey->connectionKey.dest= 80;
662 event->event.tcpEvent->segmentKey->seq= seq[sender];
663 event->event.tcpEvent->segmentKey->ack_seq= 0;
664 event->event.tcpEvent->segmentKey->doff= 5;
665 event->event.tcpEvent->segmentKey->ack= 0;
666 event->event.tcpEvent->segmentKey->rst= 0;
667 event->event.tcpEvent->segmentKey->syn= 1;
668 event->event.tcpEvent->segmentKey->fin= 0;
669
670 syncState->matchingModule->matchEvent(syncState, event);
84baf72b
BP
671
672 // Input event
10341d26 673 event= malloc(sizeof(Event));
84baf72b 674 event->traceNum= receiver;
10341d26
BP
675 event->time= round(recvTime * freq);
676 event->type= TCP;
677 event->destroy= &destroyTCPEvent;
678 event->event.tcpEvent= malloc(sizeof(TCPEvent));
679 event->event.tcpEvent->direction= IN;
680 event->event.tcpEvent->segmentKey= malloc(sizeof(SegmentKey));
681 event->event.tcpEvent->segmentKey->ihl= 5;
682 event->event.tcpEvent->segmentKey->tot_len= 40;
683 event->event.tcpEvent->segmentKey->connectionKey.saddr= sender + addressOffset;
684 event->event.tcpEvent->segmentKey->connectionKey.daddr= receiver + addressOffset;
685 event->event.tcpEvent->segmentKey->connectionKey.source= 57645;
686 event->event.tcpEvent->segmentKey->connectionKey.dest= 80;
687 event->event.tcpEvent->segmentKey->seq= seq[sender];
688 event->event.tcpEvent->segmentKey->ack_seq= 0;
689 event->event.tcpEvent->segmentKey->doff= 5;
690 event->event.tcpEvent->segmentKey->ack= 0;
691 event->event.tcpEvent->segmentKey->rst= 0;
692 event->event.tcpEvent->segmentKey->syn= 1;
693 event->event.tcpEvent->segmentKey->fin= 0;
694
695 syncState->matchingModule->matchEvent(syncState, event);
84baf72b
BP
696
697 seq[sender]++;
698
699 skipCommentLines(testCase);
700 retval= getline(&line, &len, testCase);
701 }
702
703 free(seq);
704
705 if (line)
706 {
707 free(line);
708 }
709}
710
711
712/*
713 * A Glib log function which does nothing.
714 */
715static void nullLog(const gchar *log_domain, GLogLevelFlags log_level, const
716 gchar *message, gpointer user_data)
717{}
This page took 0.047579 seconds and 4 git commands to generate.