Add a unittest program for clock synchronization modules
authorBenjamin Poirier <benjamin.poirier@polymtl.ca>
Fri, 4 Sep 2009 18:44:33 +0000 (14:44 -0400)
committerBenjamin Poirier <benjamin.poirier@polymtl.ca>
Fri, 18 Dec 2009 19:03:24 +0000 (14:03 -0500)
Allows to test matching and analysis modules with data read from text files.
Includes some sample data for simple good and bad (unsynchronizable) cases.

Signed-off-by: Benjamin Poirier <benjamin.poirier@polymtl.ca>
17 files changed:
configure.ac
lttv/lttv/Makefile.am
lttv/lttv/sync/.gitignore [new file with mode: 0644]
lttv/lttv/sync/Makefile.am [new file with mode: 0644]
lttv/lttv/sync/testData/test1.txt [new file with mode: 0644]
lttv/lttv/sync/testData/test10.txt [new file with mode: 0644]
lttv/lttv/sync/testData/test11.txt [new file with mode: 0644]
lttv/lttv/sync/testData/test12.txt [new file with mode: 0644]
lttv/lttv/sync/testData/test2.txt [new file with mode: 0644]
lttv/lttv/sync/testData/test3.txt [new file with mode: 0644]
lttv/lttv/sync/testData/test4.txt [new file with mode: 0644]
lttv/lttv/sync/testData/test5.txt [new file with mode: 0644]
lttv/lttv/sync/testData/test6.txt [new file with mode: 0644]
lttv/lttv/sync/testData/test7.txt [new file with mode: 0644]
lttv/lttv/sync/testData/test8.txt [new file with mode: 0644]
lttv/lttv/sync/testData/test9.txt [new file with mode: 0644]
lttv/lttv/sync/unittest.c [new file with mode: 0644]

index 7a94867171076f71400eb1fb59e9fc27dda71ab6..c7dba3c2345afe6cd8586c32363b1e21e7ae8327 100644 (file)
@@ -194,30 +194,31 @@ AC_SUBST(lttctlincludedir)
                 #lttv/modules/gui/tutorial/Makefile
                 #lttv/modules/gui/diskperformance/Makefile
 AC_CONFIG_FILES([Makefile
-                lttv/Makefile
-                lttv/lttv/Makefile
-                lttv/modules/Makefile
-                lttv/modules/text/Makefile
-                lttv/modules/gui/Makefile
-                lttv/modules/gui/lttvwindow/Makefile
-                lttv/modules/gui/interrupts/Makefile
-                lttv/modules/gui/lttvwindow/lttvwindow/Makefile
-                lttv/modules/gui/lttvwindow/pixmaps/Makefile
-                lttv/modules/gui/controlflow/Makefile
-                lttv/modules/gui/detailedevents/Makefile
-                lttv/modules/gui/statistics/Makefile
-                lttv/modules/gui/histogram/Makefile
-                 lttv/modules/gui/filter/Makefile
-                 lttv/modules/gui/tracecontrol/Makefile
-                 lttv/modules/gui/resourceview/Makefile
-                ltt/Makefile
-     doc/Makefile
-     doc/developer/Makefile
-     doc/developer/developer_guide/Makefile
-     doc/developer/developer_guide/docbook/Makefile
-     doc/developer/developer_guide/html/Makefile
-     doc/user/Makefile
-     doc/user/user_guide/Makefile
-     doc/user/user_guide/docbook/Makefile
-     doc/user/user_guide/html/Makefile])
+       lttv/Makefile
+       lttv/lttv/Makefile
+       lttv/lttv/sync/Makefile
+       lttv/modules/Makefile
+       lttv/modules/text/Makefile
+       lttv/modules/gui/Makefile
+       lttv/modules/gui/lttvwindow/Makefile
+       lttv/modules/gui/interrupts/Makefile
+       lttv/modules/gui/lttvwindow/lttvwindow/Makefile
+       lttv/modules/gui/lttvwindow/pixmaps/Makefile
+       lttv/modules/gui/controlflow/Makefile
+       lttv/modules/gui/detailedevents/Makefile
+       lttv/modules/gui/statistics/Makefile
+       lttv/modules/gui/histogram/Makefile
+       lttv/modules/gui/filter/Makefile
+       lttv/modules/gui/tracecontrol/Makefile
+       lttv/modules/gui/resourceview/Makefile
+       ltt/Makefile
+       doc/Makefile
+       doc/developer/Makefile
+       doc/developer/developer_guide/Makefile
+       doc/developer/developer_guide/docbook/Makefile
+       doc/developer/developer_guide/html/Makefile
+       doc/user/Makefile
+       doc/user/user_guide/Makefile
+       doc/user/user_guide/docbook/Makefile
+       doc/user/user_guide/html/Makefile])
 AC_OUTPUT
index a89b55ba365486994d433f2c320326a584dcb689..58fd59586badbaeda401fa484faef95e0d240d0f 100644 (file)
@@ -1,3 +1,5 @@
+SUBDIRS= sync
+
 AM_CFLAGS= $(PACKAGE_CFLAGS)
 LDADD = $(POPT_LIBS) $(M_LIBS) ${top_builddir}/ltt/liblttvtraceread.la
 
diff --git a/lttv/lttv/sync/.gitignore b/lttv/lttv/sync/.gitignore
new file mode 100644 (file)
index 0000000..4d13a80
--- /dev/null
@@ -0,0 +1,4 @@
+#
+# Generated files
+#
+unittest
diff --git a/lttv/lttv/sync/Makefile.am b/lttv/lttv/sync/Makefile.am
new file mode 100644 (file)
index 0000000..9a5c8b6
--- /dev/null
@@ -0,0 +1,11 @@
+AM_CFLAGS= $(PACKAGE_CFLAGS)
+LDADD = $(M_LIBS)
+
+check_PROGRAMS = unittest
+
+unittest_SOURCES = \
+       unittest.c\
+       data_structures_tcp.c\
+       event_matching_tcp.c\
+       event_analysis_linreg.c\
+       event_analysis_chull.c
diff --git a/lttv/lttv/sync/testData/test1.txt b/lttv/lttv/sync/testData/test1.txt
new file mode 100644 (file)
index 0000000..a60aaea
--- /dev/null
@@ -0,0 +1,7 @@
+# Most simple successful case, two hulls made of two points each
+2
+0      1       48.85   55.29
+0      1       65.50   71.83
+
+1      0       53.03   48.73
+1      0       71.57   67.99
diff --git a/lttv/lttv/sync/testData/test10.txt b/lttv/lttv/sync/testData/test10.txt
new file mode 100644 (file)
index 0000000..96aeec0
--- /dev/null
@@ -0,0 +1,7 @@
+# Cas dégénéré 7
+# un point, une ligne sans minimum
+2
+0      1       51.59   60.31
+
+1      0       58.01   54.59
+1      0       57.80   61.03
diff --git a/lttv/lttv/sync/testData/test11.txt b/lttv/lttv/sync/testData/test11.txt
new file mode 100644 (file)
index 0000000..53c7a16
--- /dev/null
@@ -0,0 +1,16 @@
+# Cas dégénéré 8
+# Two hulls made of four points each, some extra points, going downwards
+2
+0      1       46.56   72.92
+0      1       47.79   69.85
+0      1       53.76   69.77
+0      1       60.33   60.31
+0      1       61.05   56.64
+0      1       64.23   55.52
+
+1      0       70.15   44.36
+1      0       69.54   47.29
+1      0       60.83   47.83
+1      0       57.67   53.85
+1      0       56.64   60.26
+1      0       52.37   62.48
diff --git a/lttv/lttv/sync/testData/test12.txt b/lttv/lttv/sync/testData/test12.txt
new file mode 100644 (file)
index 0000000..9a26e9b
--- /dev/null
@@ -0,0 +1,13 @@
+# Factors of type Middle
+2
+0      1       107.83  274.40
+0      1       132.56  290.37
+0      1       144.80  299.13
+0      1       334.08  445.67
+0      1       500.88  574.71
+0      1       523.15  591.47
+
+1      0       275.78  130.90
+1      0       300.03  160.52
+1      0       432.85  333.79
+1      0       580.74  522.51
diff --git a/lttv/lttv/sync/testData/test2.txt b/lttv/lttv/sync/testData/test2.txt
new file mode 100644 (file)
index 0000000..9124b0f
--- /dev/null
@@ -0,0 +1,12 @@
+# Two hulls made of four points each
+2
+# send points, (x, y)
+0      1       47.83   55.52
+0      1       51.01   56.64
+0      1       64.27   69.85
+0      1       65.50   72.92
+# receive points (y, x)
+1      0       52.37   49.58
+1      0       56.64   51.80
+1      0       69.54   64.77
+1      0       70.15   67.70
diff --git a/lttv/lttv/sync/testData/test3.txt b/lttv/lttv/sync/testData/test3.txt
new file mode 100644 (file)
index 0000000..78f149b
--- /dev/null
@@ -0,0 +1,15 @@
+# Two hulls made of four points each, some extra points
+2
+0      1       47.83   58.16
+0      1       51.01   56.64
+0      1       51.73   60.31
+0      1       58.30   69.77
+0      1       64.27   69.85
+0      1       65.50   72.92
+
+1      0       52.37   49.58
+1      0       56.64   51.80
+1      0       57.67   58.21
+1      0       60.83   64.23
+1      0       69.54   64.77
+1      0       68.01   67.70
diff --git a/lttv/lttv/sync/testData/test4.txt b/lttv/lttv/sync/testData/test4.txt
new file mode 100644 (file)
index 0000000..31210da
--- /dev/null
@@ -0,0 +1,12 @@
+# Cas dégénéré 1
+# la droite de pente maximale n'est pas bornée
+2
+0      1       47.86   55.56
+0      1       51.00   56.63
+0      1       54.12   59.80
+0      1       54.53   62.14
+
+1      0       60.94   58.26
+1      0       63.87   59.25
+1      0       67.40   62.79
+1      0       68.21   65.59
diff --git a/lttv/lttv/sync/testData/test5.txt b/lttv/lttv/sync/testData/test5.txt
new file mode 100644 (file)
index 0000000..ff0f325
--- /dev/null
@@ -0,0 +1,12 @@
+# Cas dégénéré 2
+# la droite de pente minimale n'est pas bornée
+2
+0      1       58.74   65.01
+0      1       59.84   65.48
+0      1       61.82   67.49
+0      1       63.36   70.21
+
+1      0       52.36   49.57
+1      0       56.57   51.81
+1      0       59.83   55.10
+1      0       57.65   58.28
diff --git a/lttv/lttv/sync/testData/test6.txt b/lttv/lttv/sync/testData/test6.txt
new file mode 100644 (file)
index 0000000..ce626a0
--- /dev/null
@@ -0,0 +1,12 @@
+# Cas dégénéré 3
+# les enveloppes se croisent
+2
+0      1       47.82   55.47
+0      1       52.22   56.27
+0      1       64.19   68.21
+0      1       65.51   72.90
+
+1      0       52.33   49.65
+1      0       60.19   51.69
+1      0       69.70   58.30
+1      0       69.25   67.29
diff --git a/lttv/lttv/sync/testData/test7.txt b/lttv/lttv/sync/testData/test7.txt
new file mode 100644 (file)
index 0000000..6b6fd22
--- /dev/null
@@ -0,0 +1,12 @@
+# Cas dégénéré 4
+# les enveloppes sont inversées (cs sous cr)
+2
+0      1       49.16   53.07
+0      1       52.77   52.45
+0      1       66.50   64.77
+0      1       67.43   70.00
+
+1      0       54.99   48.25
+1      0       60.04   50.28
+1      0       72.48   62.63
+1      0       72.78   66.60
diff --git a/lttv/lttv/sync/testData/test8.txt b/lttv/lttv/sync/testData/test8.txt
new file mode 100644 (file)
index 0000000..45cb324
--- /dev/null
@@ -0,0 +1,6 @@
+# Cas dégénéré 5
+# seulement deux points
+2
+0      1       56.15   62.88
+
+1      0       56.01   59.98
diff --git a/lttv/lttv/sync/testData/test9.txt b/lttv/lttv/sync/testData/test9.txt
new file mode 100644 (file)
index 0000000..9db76f7
--- /dev/null
@@ -0,0 +1,7 @@
+# Cas dégénéré 6
+# un point, une ligne avec maximum et minimum
+2
+0      1       56.51   64.08
+
+1      0       58.01   54.59
+1      0       57.80   61.03
diff --git a/lttv/lttv/sync/unittest.c b/lttv/lttv/sync/unittest.c
new file mode 100644 (file)
index 0000000..4ca2b93
--- /dev/null
@@ -0,0 +1,716 @@
+/* This file is part of the Linux Trace Toolkit viewer
+ * Copyright (C) 2009 Benjamin Poirier <benjamin.poirier@polymtl.ca>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+#define _GNU_SOURCE
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "sync_chain.h"
+
+
+#ifndef g_info
+#define g_info(format...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, format)
+#endif
+
+
+static void timeDiff(struct timeval* const end, const struct timeval* const start);
+static void usage(const char* const programName);
+static gint gcfCompareAnalysis(gconstpointer a, gconstpointer b);
+static void gfAppendAnalysisName(gpointer data, gpointer user_data);
+static unsigned int readTraceNb(FILE* testCase);
+static void skipCommentLines(FILE* testCase);
+static void processEvents(SyncState* const syncState, FILE* testCase);
+static void nullLog(const gchar *log_domain, GLogLevelFlags log_level, const
+       gchar *message, gpointer user_data);
+
+GQueue processingModules= G_QUEUE_INIT;
+GQueue matchingModules= G_QUEUE_INIT;
+GQueue analysisModules= G_QUEUE_INIT;
+
+// time values in test case files will be scaled by this factor
+const double freq= 1e9;
+
+
+/*
+ * Create matching and analysis modules and feed them events read from a text
+ * file.
+ *
+ * Idealy, this would've been a processing module but sync_chain.c and
+ * ProcessingModule use some LTTV-specific types and functions. Unfortunately,
+ * there is some code duplication from sync_chain.c
+ *
+ */
+int main(int argc, char* argv[])
+{
+       int c;
+       extern char* optarg;
+       extern int optind, opterr, optopt;
+       bool optionSyncStats= false;
+       char* optionGraphsDir= NULL;
+       FILE* testCase= NULL;
+
+       SyncState* syncState;
+       struct timeval startTime, endTime;
+       struct rusage startUsage, endUsage;
+       GList* result;
+       char* cwd;
+       FILE* graphsStream;
+       int graphsFp;
+       GArray* factors;
+
+       int retval;
+
+       syncState= malloc(sizeof(SyncState));
+
+       g_assert(g_queue_get_length(&analysisModules) > 0);
+       syncState->analysisModule= g_queue_peek_head(&analysisModules);
+
+       do
+       {
+               int optionIndex= 0;
+
+               static struct option longOptions[]=
+               {
+                       {"sync-stats", no_argument, 0, 's'},
+                       {"sync-graphs", optional_argument, 0, 'g'},
+                       {"sync-analysis", required_argument, 0, 'a'},
+                       {0, 0, 0, 0}
+               };
+
+               c= getopt_long(argc, argv, "sg::a:", longOptions, &optionIndex);
+
+               switch (c)
+               {
+                       case -1:
+                       case 0:
+                               break;
+
+                       case 's':
+                               if (!optionSyncStats)
+                               {
+                                       gettimeofday(&startTime, 0);
+                                       getrusage(RUSAGE_SELF, &startUsage);
+                               }
+                               optionSyncStats= true;
+                               break;
+
+                       case 'g':
+                               if (optarg)
+                               {
+                                       printf("xxx:%s\n", optarg);
+                                       optionGraphsDir= malloc(strlen(optarg));
+                                       strcpy(optionGraphsDir, optarg);
+                               }
+                               else
+                               {
+                                       optionGraphsDir= malloc(20);
+                                       retval= snprintf(optionGraphsDir, 20, "graphs-%d",
+                                               getpid());
+                                       if (retval > 20 - 1)
+                                       {
+                                               optionGraphsDir[20 - 1]= '\0';
+                                       }
+                               }
+                               break;
+
+                       case 'a':
+                               printf("xxx:%s\n", optarg);
+                               result= g_queue_find_custom(&analysisModules, optarg,
+                                       &gcfCompareAnalysis);
+                               if (result != NULL)
+                               {
+                                       syncState->analysisModule= (AnalysisModule*) result->data;
+                               }
+                               else
+                               {
+                                       g_error("Analysis module '%s' not found", optarg);
+                               }
+                               break;
+
+                       case '?':
+                               usage(argv[0]);
+                               abort();
+
+                       default:
+                               g_error("Option parse error");
+               }
+       } while (c != -1);
+
+       if (argc <= optind)
+       {
+               fprintf(stderr, "Test file unspecified\n");
+               usage(argv[0]);
+               abort();
+       }
+
+       testCase= fopen(argv[optind], "r");
+       if (testCase == NULL)
+       {
+               g_error(strerror(errno));
+       }
+
+       // Initialize data structures
+       syncState->traceNb= readTraceNb(testCase);
+
+       if (optionSyncStats)
+       {
+               syncState->stats= true;
+       }
+       else
+       {
+               syncState->stats= false;
+       }
+
+       syncState->graphs= optionGraphsDir;
+
+       if (!optionSyncStats)
+       {
+               g_log_set_handler(NULL, G_LOG_LEVEL_DEBUG, nullLog, NULL);
+       }
+
+       // Identify and initialize matching and analysis modules
+       syncState->matchingData= NULL;
+       syncState->analysisData= NULL;
+
+       g_assert(g_queue_get_length(&matchingModules) == 1);
+       syncState->matchingModule= (MatchingModule*)
+               g_queue_peek_head(&matchingModules);
+
+       graphsStream= NULL;
+       if (syncState->graphs)
+       {
+               // Create the graph directory right away in case the module initialization
+               // functions have something to write in it.
+               cwd= changeToGraphDir(syncState->graphs);
+
+               if (syncState->matchingModule->writeMatchingGraphsPlots != NULL)
+               {
+                       if ((graphsFp= open("graphs.gnu", O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR |
+                                       S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH
+                                       | S_IWOTH | S_IXOTH)) == -1)
+                       {
+                               g_error(strerror(errno));
+                       }
+                       if ((graphsStream= fdopen(graphsFp, "w")) == NULL)
+                       {
+                               g_error(strerror(errno));
+                       }
+               }
+
+               retval= chdir(cwd);
+               if (retval == -1)
+               {
+                       g_error(strerror(errno));
+               }
+               free(cwd);
+       }
+
+       syncState->matchingModule->initMatching(syncState);
+       syncState->analysisModule->initAnalysis(syncState);
+
+       // Process traceset
+       processEvents(syncState, testCase);
+
+       factors= syncState->matchingModule->finalizeMatching(syncState);
+
+       // Write graphs file
+       if (graphsStream != NULL)
+       {
+               unsigned int i, j;
+
+               fprintf(graphsStream,
+                       "#!/usr/bin/gnuplot\n\n"
+                       "#set terminal postscript eps color size 8in,6in\n");
+
+               // Cover the upper triangular matrix, i is the reference node.
+               for (i= 0; i < syncState->traceNb; i++)
+               {
+                       for (j= i + 1; j < syncState->traceNb; j++)
+                       {
+                               long pos;
+
+                               fprintf(graphsStream,
+                                       "\n#set output \"%03d-%03d.eps\"\n"
+                                       "plot \\\n", i, j);
+
+                               syncState->matchingModule->writeMatchingGraphsPlots(graphsStream,
+                                       syncState, i, j);
+
+                               // Remove the ", \\\n" from the last graph plot line
+                               fflush(graphsStream);
+                               pos= ftell(graphsStream);
+                               if (ftruncate(fileno(graphsStream), pos - 4) == -1)
+                               {
+                                       g_error(strerror(errno));
+                               }
+                               if (fseek(graphsStream, 0, SEEK_END) == -1)
+                               {
+                                       g_error(strerror(errno));
+                               }
+
+                               fprintf(graphsStream,
+                                       "\nset output \"%1$03d-%2$03d.eps\"\n"
+                                       "set title \"\"\n"
+                                       "set xlabel \"Clock %1$u\"\n"
+                                       "set xtics nomirror\n"
+                                       "set ylabel \"Clock %2$u\"\n"
+                                       "set ytics nomirror\n"
+                                       "set x2label \"Clock %1$u (s)\"\n"
+                                       "set x2range [GPVAL_X_MIN / %3$.1f : GPVAL_X_MAX / %3$.1f]\n"
+                                       "set x2tics\n"
+                                       "set y2label \"Clock %2$u (s)\"\n"
+                                       "set y2range [GPVAL_Y_MIN / %3$.1f: GPVAL_Y_MAX / %3$.1f]\n"
+                                       "set y2tics\n"
+                                       "set key inside right bottom\n", i, j, freq);
+
+                               syncState->matchingModule->writeMatchingGraphsOptions(graphsStream,
+                                       syncState, i, j);
+
+                               fprintf(graphsStream, "replot\n\n"
+                                       "pause -1\n");
+                       }
+               }
+
+               if (fclose(graphsStream) != 0)
+               {
+                       g_error(strerror(errno));
+               }
+       }
+       if (optionGraphsDir)
+       {
+               free(optionGraphsDir);
+       }
+
+       if (optionSyncStats && syncState->matchingModule->printMatchingStats !=
+               NULL)
+       {
+               unsigned int i;
+
+               syncState->matchingModule->printMatchingStats(syncState);
+
+               printf("Resulting synchronization factors:\n");
+               for (i= 0; i < syncState->traceNb; i++)
+               {
+                       Factors* traceFactors;
+
+                       traceFactors= &g_array_index(factors, Factors, i);
+                       printf("\ttrace %u drift= %g offset= %g\n", i,
+                               traceFactors->drift, traceFactors->offset);
+               }
+       }
+
+       syncState->matchingModule->destroyMatching(syncState);
+       syncState->analysisModule->destroyAnalysis(syncState);
+
+       free(syncState);
+
+       if (optionSyncStats)
+       {
+               gettimeofday(&endTime, 0);
+               retval= getrusage(RUSAGE_SELF, &endUsage);
+
+               timeDiff(&endTime, &startTime);
+               timeDiff(&endUsage.ru_utime, &startUsage.ru_utime);
+               timeDiff(&endUsage.ru_stime, &startUsage.ru_stime);
+
+               printf("Synchronization time:\n");
+               printf("\treal time: %ld.%06ld\n", endTime.tv_sec, endTime.tv_usec);
+               printf("\tuser time: %ld.%06ld\n", endUsage.ru_utime.tv_sec,
+                       endUsage.ru_utime.tv_usec);
+               printf("\tsystem time: %ld.%06ld\n", endUsage.ru_stime.tv_sec,
+                       endUsage.ru_stime.tv_usec);
+       }
+
+       return EXIT_SUCCESS;
+}
+
+
+/*
+ * Print information about program options and arguments.
+ *
+ * Args:
+ *   programName:  name of the program, as contained in argv[0] for example
+ */
+static void usage(const char* const programName)
+{
+       GString* analysisModulesNames;
+
+       analysisModulesNames= g_string_new("");
+       g_queue_foreach(&analysisModules, &gfAppendAnalysisName,
+               analysisModulesNames);
+       // remove the last ", "
+       g_string_truncate(analysisModulesNames, analysisModulesNames->len - 2);
+
+       printf(
+               "%s [options] <test file>\n"
+               "Options:\n"
+               "\t-s, --sync-stats                 Print statistics and debug messages\n"
+               "\t-g, --sync-graphs[=OUPUT_DIR]    Generate graphs\n"
+               "\t-a, --sync-analysis=MODULE_NAME  Specify which module to use for analysis\n"
+               "\t                                 Available modules: %s\n",
+               programName, analysisModulesNames->str);
+
+       g_string_free(analysisModulesNames, TRUE);
+}
+
+
+/*
+ * Calculate the elapsed time between two timeval values
+ *
+ * Args:
+ *   end:          end time, result is also stored in this structure
+ *   start:        start time
+ */
+static void timeDiff(struct timeval* const end, const struct timeval* const start)
+{
+               if (end->tv_usec >= start->tv_usec)
+               {
+                       end->tv_sec-= start->tv_sec;
+                       end->tv_usec-= start->tv_usec;
+               }
+               else
+               {
+                       end->tv_sec= end->tv_sec - start->tv_sec - 1;
+                       end->tv_usec= end->tv_usec - start->tv_usec + 1e6;
+               }
+}
+
+
+/*
+ * A GCompareFunc for g_slist_find_custom()
+ *
+ * Args:
+ *   a:            AnalysisModule*, element's data
+ *   b:            char*, user data to compare against
+ *
+ * Returns:
+ *   0 if the analysis module a's name is b
+ */
+static gint gcfCompareAnalysis(gconstpointer a, gconstpointer b)
+{
+       const AnalysisModule* analysisModule;
+       const char* name;
+
+       analysisModule= (const AnalysisModule*)a;
+       name= (const char*)b;
+
+       return strncmp(analysisModule->name, name, strlen(analysisModule->name) +
+               1);
+}
+
+
+/*
+ * Change to the directory used to hold graphs. Create it if necessary.
+ *
+ * Args:
+ *   graph:        name of directory
+ *
+ * Returns:
+ *   The current working directory before the execution of the function. The
+ *   string must be free'd by the caller.
+ */
+char* changeToGraphDir(char* const graphs)
+{
+       int retval;
+       char* cwd;
+
+       cwd= getcwd(NULL, 0);
+       if (cwd == NULL)
+       {
+               g_error(strerror(errno));
+       }
+       while ((retval= chdir(graphs)) != 0)
+       {
+               if (errno == ENOENT)
+               {
+                       retval= mkdir(graphs, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP |
+                               S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH);
+                       if (retval != 0)
+                       {
+                               g_error(strerror(errno));
+                       }
+               }
+               else
+               {
+                       g_error(strerror(errno));
+               }
+       }
+
+       return cwd;
+}
+
+
+/*
+ * A GFunc for g_queue_foreach()
+ *
+ * Concatenate analysis module names.
+ *
+ * Args:
+ *   data:         AnalysisModule*
+ *   user_data:    GString*, concatenated names
+ */
+static void gfAppendAnalysisName(gpointer data, gpointer user_data)
+{
+       g_string_append((GString*) user_data, ((AnalysisModule*) data)->name);
+       g_string_append((GString*) user_data, ", ");
+}
+
+
+/*
+ * Read trace number from the test case stream. The trace number should be the
+ * first non-comment line and should be an unsigned int by itself on a line.
+ *
+ * Args:
+ *   testCase:     test case stream
+ *
+ * Returns:
+ *   The trace number
+ */
+static unsigned int readTraceNb(FILE* testCase)
+{
+       unsigned int result;
+       int retval;
+       char* line= NULL;
+       size_t len;
+       char tmp;
+
+       skipCommentLines(testCase);
+       retval= getline(&line, &len, testCase);
+       if (retval == -1)
+       {
+               if (feof(testCase))
+               {
+                       g_error("Unexpected end of file while looking for number of traces");
+               }
+               else
+               {
+                       g_error(strerror(errno));
+               }
+       }
+       if (line[retval - 1] == '\n')
+       {
+               line[retval - 1]= '\0';
+       }
+
+       retval= sscanf(line, " %u %c", &result, &tmp);
+       if (retval == EOF || retval != 1)
+       {
+               g_error("Error parsing test file while looking for number of traces, line was '%s'", line);
+
+               // Not really needed but avoids warning from gcc
+               abort();
+       }
+
+       return result;
+}
+
+
+/*
+ * Advance testCase stream over empty space, empty lines and lines that begin
+ * with '#'
+ *
+ * Args:
+ *   testCase:     test case stream
+ */
+static void skipCommentLines(FILE* testCase)
+{
+       int firstChar;
+       ssize_t retval;
+       char* line= NULL;
+       size_t len;
+
+       do
+       {
+               firstChar= fgetc(testCase);
+               if (firstChar == (int) '#')
+               {
+                       retval= getline(&line, &len, testCase);
+                       if (retval == -1)
+                       {
+                               if (feof(testCase))
+                               {
+                                       goto outEof;
+                               }
+                               else
+                               {
+                                       g_error(strerror(errno));
+                               }
+                       }
+               }
+               else if (firstChar == (int) '\n' || firstChar == (int) ' ')
+               {}
+               else if (firstChar == EOF)
+               {
+                       goto outEof;
+               }
+               else
+               {
+                       break;
+               }
+       } while (true);
+       retval= ungetc(firstChar, testCase);
+       if (retval == EOF)
+       {
+               g_error("Error: ungetc()");
+       }
+
+outEof:
+       if (line)
+       {
+               free(line);
+       }
+}
+
+
+/*
+ * Make up events from the messages in the test case. Dispatch those events to
+ * the matching module.
+ */
+static void processEvents(SyncState* const syncState, FILE* testCase)
+{
+       char* line= NULL;
+       size_t len;
+       int retval;
+       unsigned int addressOffset;
+       unsigned int* seq;
+
+       // Trace numbers run from 0 to traceNb - 1. addressOffset is added to a
+       // traceNum to convert it to an address.
+       addressOffset= pow(10, floor(log(syncState->traceNb - 1) / log(10)) + 1);
+
+       seq= calloc(syncState->traceNb, sizeof(unsigned int));
+
+       skipCommentLines(testCase);
+       retval= getline(&line, &len, testCase);
+       while(!feof(testCase))
+       {
+               unsigned int sender, receiver;
+               double sendTime, recvTime;
+               char tmp;
+               NetEvent* event;
+
+               if (retval == -1 && !feof(testCase))
+               {
+                       g_error(strerror(errno));
+               }
+
+               if (line[len - 1] == '\n')
+               {
+                       line[len - 1]= '\0';
+               }
+
+               retval= sscanf(line, " %u %u %lf %lf %c", &sender, &receiver,
+                       &sendTime, &recvTime, &tmp);
+               if (retval == EOF)
+               {
+                       g_error(strerror(errno));
+               }
+               else if (retval != 4)
+               {
+                       g_error("Error parsing test file while looking for data point, line was '%s'", line);
+               }
+
+               if (sender + 1 > syncState->traceNb)
+               {
+                       g_error("Error parsing test file, sender is out of range, line was '%s'", line);
+               }
+
+               if (receiver + 1 > syncState->traceNb)
+               {
+                       g_error("Error parsing test file, receiver is out of range, line was '%s'", line);
+               }
+
+               // Output event
+               event= malloc(sizeof(NetEvent));
+               event->traceNum= sender;
+               event->tsc= round(sendTime * freq);
+               event->skb= NULL;
+               event->packetKey= malloc(sizeof(PacketKey));
+               event->packetKey->ihl= 5;
+               event->packetKey->tot_len= 40;
+               event->packetKey->connectionKey.saddr= sender + addressOffset;
+               event->packetKey->connectionKey.daddr= receiver + addressOffset;
+               event->packetKey->connectionKey.source= 57645;
+               event->packetKey->connectionKey.dest= 80;
+               event->packetKey->seq= seq[sender];
+               event->packetKey->ack_seq= 0;
+               event->packetKey->doff= 5;
+               event->packetKey->ack= 0;
+               event->packetKey->rst= 0;
+               event->packetKey->syn= 1;
+               event->packetKey->fin= 0;
+
+               syncState->matchingModule->matchEvent(syncState, event, OUT);
+
+               // Input event
+               event= malloc(sizeof(NetEvent));
+               event->traceNum= receiver;
+               event->tsc= round(recvTime * freq);
+               event->skb= NULL;
+               event->packetKey= malloc(sizeof(PacketKey));
+               event->packetKey->ihl= 5;
+               event->packetKey->tot_len= 40;
+               event->packetKey->connectionKey.saddr= sender + addressOffset;
+               event->packetKey->connectionKey.daddr= receiver + addressOffset;
+               event->packetKey->connectionKey.source= 57645;
+               event->packetKey->connectionKey.dest= 80;
+               event->packetKey->seq= seq[sender];
+               event->packetKey->ack_seq= 0;
+               event->packetKey->doff= 5;
+               event->packetKey->ack= 0;
+               event->packetKey->rst= 0;
+               event->packetKey->syn= 1;
+               event->packetKey->fin= 0;
+
+               syncState->matchingModule->matchEvent(syncState, event, IN);
+
+               seq[sender]++;
+
+               skipCommentLines(testCase);
+               retval= getline(&line, &len, testCase);
+       }
+
+       free(seq);
+
+       if (line)
+       {
+               free(line);
+       }
+}
+
+
+/*
+ * A Glib log function which does nothing.
+ */
+static void nullLog(const gchar *log_domain, GLogLevelFlags log_level, const
+       gchar *message, gpointer user_data)
+{}
This page took 0.036179 seconds and 4 git commands to generate.