From 84baf72b1a0b38d106c80c936261e9aed9b6324f Mon Sep 17 00:00:00 2001 From: Benjamin Poirier Date: Fri, 4 Sep 2009 14:44:33 -0400 Subject: [PATCH] Add a unittest program for clock synchronization modules 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 --- configure.ac | 53 +-- lttv/lttv/Makefile.am | 2 + lttv/lttv/sync/.gitignore | 4 + lttv/lttv/sync/Makefile.am | 11 + lttv/lttv/sync/testData/test1.txt | 7 + lttv/lttv/sync/testData/test10.txt | 7 + lttv/lttv/sync/testData/test11.txt | 16 + lttv/lttv/sync/testData/test12.txt | 13 + lttv/lttv/sync/testData/test2.txt | 12 + lttv/lttv/sync/testData/test3.txt | 15 + lttv/lttv/sync/testData/test4.txt | 12 + lttv/lttv/sync/testData/test5.txt | 12 + lttv/lttv/sync/testData/test6.txt | 12 + lttv/lttv/sync/testData/test7.txt | 12 + lttv/lttv/sync/testData/test8.txt | 6 + lttv/lttv/sync/testData/test9.txt | 7 + lttv/lttv/sync/unittest.c | 716 +++++++++++++++++++++++++++++ 17 files changed, 891 insertions(+), 26 deletions(-) create mode 100644 lttv/lttv/sync/.gitignore create mode 100644 lttv/lttv/sync/Makefile.am create mode 100644 lttv/lttv/sync/testData/test1.txt create mode 100644 lttv/lttv/sync/testData/test10.txt create mode 100644 lttv/lttv/sync/testData/test11.txt create mode 100644 lttv/lttv/sync/testData/test12.txt create mode 100644 lttv/lttv/sync/testData/test2.txt create mode 100644 lttv/lttv/sync/testData/test3.txt create mode 100644 lttv/lttv/sync/testData/test4.txt create mode 100644 lttv/lttv/sync/testData/test5.txt create mode 100644 lttv/lttv/sync/testData/test6.txt create mode 100644 lttv/lttv/sync/testData/test7.txt create mode 100644 lttv/lttv/sync/testData/test8.txt create mode 100644 lttv/lttv/sync/testData/test9.txt create mode 100644 lttv/lttv/sync/unittest.c diff --git a/configure.ac b/configure.ac index 7a948671..c7dba3c2 100644 --- a/configure.ac +++ b/configure.ac @@ -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 diff --git a/lttv/lttv/Makefile.am b/lttv/lttv/Makefile.am index a89b55ba..58fd5958 100644 --- a/lttv/lttv/Makefile.am +++ b/lttv/lttv/Makefile.am @@ -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 index 00000000..4d13a802 --- /dev/null +++ b/lttv/lttv/sync/.gitignore @@ -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 index 00000000..9a5c8b6c --- /dev/null +++ b/lttv/lttv/sync/Makefile.am @@ -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 index 00000000..a60aaea9 --- /dev/null +++ b/lttv/lttv/sync/testData/test1.txt @@ -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 index 00000000..96aeec08 --- /dev/null +++ b/lttv/lttv/sync/testData/test10.txt @@ -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 index 00000000..53c7a169 --- /dev/null +++ b/lttv/lttv/sync/testData/test11.txt @@ -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 index 00000000..9a26e9bd --- /dev/null +++ b/lttv/lttv/sync/testData/test12.txt @@ -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 index 00000000..9124b0f5 --- /dev/null +++ b/lttv/lttv/sync/testData/test2.txt @@ -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 index 00000000..78f149bf --- /dev/null +++ b/lttv/lttv/sync/testData/test3.txt @@ -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 index 00000000..31210dab --- /dev/null +++ b/lttv/lttv/sync/testData/test4.txt @@ -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 index 00000000..ff0f3259 --- /dev/null +++ b/lttv/lttv/sync/testData/test5.txt @@ -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 index 00000000..ce626a0c --- /dev/null +++ b/lttv/lttv/sync/testData/test6.txt @@ -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 index 00000000..6b6fd229 --- /dev/null +++ b/lttv/lttv/sync/testData/test7.txt @@ -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 index 00000000..45cb324f --- /dev/null +++ b/lttv/lttv/sync/testData/test8.txt @@ -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 index 00000000..9db76f75 --- /dev/null +++ b/lttv/lttv/sync/testData/test9.txt @@ -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 index 00000000..4ca2b932 --- /dev/null +++ b/lttv/lttv/sync/unittest.c @@ -0,0 +1,716 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2009 Benjamin Poirier + * + * 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 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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] \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) +{} -- 2.34.1