From 48b641c1f6320eca569b2894f253d9d815250d45 Mon Sep 17 00:00:00 2001 From: Benjamin Poirier Date: Fri, 27 Nov 2009 11:09:44 -0500 Subject: [PATCH] Add a processing module that reads events from a text file This is particularly useful for unitests. Signed-off-by: Benjamin Poirier --- lttv/lttv/sync/Makefile.am | 1 + lttv/lttv/sync/README | 2 +- lttv/lttv/sync/event_analysis_chull.c | 2 +- .../sync/event_processing_lttng_standard.c | 16 - lttv/lttv/sync/event_processing_text.c | 476 ++++++++++++++++++ lttv/lttv/sync/event_processing_text.h | 32 ++ lttv/lttv/sync/sync_chain_unittest.c | 91 +++- 7 files changed, 585 insertions(+), 35 deletions(-) create mode 100644 lttv/lttv/sync/event_processing_text.c create mode 100644 lttv/lttv/sync/event_processing_text.h diff --git a/lttv/lttv/sync/Makefile.am b/lttv/lttv/sync/Makefile.am index cd08bb8d..a6b0b8a3 100644 --- a/lttv/lttv/sync/Makefile.am +++ b/lttv/lttv/sync/Makefile.am @@ -8,6 +8,7 @@ unittest_SOURCES = \ graph_functions.c\ sync_chain.c\ sync_chain_unittest.c\ + event_processing_text.c\ event_matching_broadcast.c\ event_matching_distributor.c\ event_matching_tcp.c\ diff --git a/lttv/lttv/sync/README b/lttv/lttv/sync/README index 4e6b19ce..6e76ec17 100644 --- a/lttv/lttv/sync/README +++ b/lttv/lttv/sync/README @@ -195,7 +195,7 @@ become relevant to have many modules at the same stage simultaneously. This will require some modifications. It is already partly supported at the matching stage through encapsulation of other matching modules. -sync_chain_unitest provides a fairly simple example of sync chain +sync_chain_unitest:main() provides a fairly simple example of sync chain implementation. ++ Stage 1: Event processing diff --git a/lttv/lttv/sync/event_analysis_chull.c b/lttv/lttv/sync/event_analysis_chull.c index d0dd0a70..e77eabd2 100644 --- a/lttv/lttv/sync/event_analysis_chull.c +++ b/lttv/lttv/sync/event_analysis_chull.c @@ -927,7 +927,7 @@ void calculateFactorsMiddle(FactorsCHull* const factors) bmin= factors->min->drift; bmax= factors->max->drift; - g_assert_cmpfloat(bmax, >, bmin); + g_assert_cmpfloat(bmax, >=, bmin); factors->approx= malloc(sizeof(Factors)); bhat= (bmax * bmin - 1. + sqrt(1. + pow(bmax, 2.) * pow(bmin, 2.) + diff --git a/lttv/lttv/sync/event_processing_lttng_standard.c b/lttv/lttv/sync/event_processing_lttng_standard.c index e6457fa4..76a754b8 100644 --- a/lttv/lttv/sync/event_processing_lttng_standard.c +++ b/lttv/lttv/sync/event_processing_lttng_standard.c @@ -700,14 +700,6 @@ static void writeProcessingGraphVariablesLTTVStandard(SyncState* const static void writeProcessingTraceTraceOptionsLTTVStandard(SyncState* const syncState, const unsigned int i, const unsigned int j) { - ProcessingDataLTTVStandard* processingData; - ProcessingGraphsLTTVStandard* traceI, * traceJ; - - processingData= (ProcessingDataLTTVStandard*) syncState->processingData; - - traceI= &processingData->graphs[i]; - traceJ= &processingData->graphs[j]; - fprintf(syncState->graphsStream, "set key inside right bottom\n" "set xlabel \"Clock %1$u\"\n" @@ -734,14 +726,6 @@ static void writeProcessingTraceTraceOptionsLTTVStandard(SyncState* const static void writeProcessingTraceTimeOptionsLTTVStandard(SyncState* const syncState, const unsigned int i, const unsigned int j) { - ProcessingDataLTTVStandard* processingData; - ProcessingGraphsLTTVStandard* traceI, * traceJ; - - processingData= (ProcessingDataLTTVStandard*) syncState->processingData; - - traceI= &processingData->graphs[i]; - traceJ= &processingData->graphs[j]; - fprintf(syncState->graphsStream, "set key inside right bottom\n" "set xlabel \"Clock %1$u\"\n" diff --git a/lttv/lttv/sync/event_processing_text.c b/lttv/lttv/sync/event_processing_text.c new file mode 100644 index 00000000..9dd5d302 --- /dev/null +++ b/lttv/lttv/sync/event_processing_text.c @@ -0,0 +1,476 @@ +/* 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 NANOSECONDS_PER_SECOND 1000000000 +#define CPU_FREQ 1e9 + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "sync_chain.h" + +#include "event_processing_text.h" + + +// Functions common to all processing modules +static void initProcessingText(SyncState* const syncState, ...); +static void destroyProcessingText(SyncState* const syncState); +static void finalizeProcessingText(SyncState* const syncState); +static void printProcessingStatsText(SyncState* const syncState); +static void writeProcessingTraceTimeOptionsText(SyncState* const syncState, + const unsigned int i, const unsigned int j); +static void writeProcessingTraceTraceOptionsText(SyncState* const syncState, + const unsigned int i, const unsigned int j); +static void writeProcessingGraphVariablesText(SyncState* const syncState, + const unsigned int i); + +// Functions specific to this module +static void registerProcessingText() __attribute__((constructor (102))); + +static unsigned int readTraceNb(FILE* testCase); +static void skipCommentLines(FILE* testCase); + + +static ProcessingModule processingModuleText = { + .name= "text", + .initProcessing= &initProcessingText, + .destroyProcessing= &destroyProcessingText, + .finalizeProcessing= &finalizeProcessingText, + .printProcessingStats= &printProcessingStatsText, + .graphFunctions= { + .writeVariables= &writeProcessingGraphVariablesText, + .writeTraceTraceOptions= &writeProcessingTraceTraceOptionsText, + .writeTraceTimeOptions= &writeProcessingTraceTimeOptionsText, + }, +}; + + +/* + * Processing Module registering function + */ +static void registerProcessingText() +{ + g_queue_push_tail(&processingModules, &processingModuleText); +} + + +/* + * Allocate and initialize data structures for synchronizing a traceset. + * Open test case file. + * + * Args: + * syncState: container for synchronization data. + * testCaseName: const char*, test case file name + */ +static void initProcessingText(SyncState* const syncState, ...) +{ + ProcessingDataText* processingData; + const char* testCaseName; + va_list ap; + + processingData= malloc(sizeof(ProcessingDataText)); + syncState->processingData= processingData; + va_start(ap, syncState); + testCaseName= va_arg(ap, const char*); + va_end(ap); + + processingData->testCase= fopen(testCaseName, "r"); + if (processingData->testCase == NULL) + { + g_error(strerror(errno)); + } + syncState->traceNb= readTraceNb(processingData->testCase); + + if (syncState->stats) + { + processingData->factors= NULL; + } +} + + +static void destroyProcessingText(SyncState* const syncState) +{ + ProcessingDataText* processingData= (ProcessingDataText*) + syncState->processingData; + + if (processingData == NULL) + { + return; + } + + if (syncState->stats && processingData->factors) + { + g_array_free(processingData->factors, TRUE); + } + + free(syncState->processingData); + syncState->processingData= NULL; +} + + +/* + * Read the test case file and make up events. Dispatch those events to the + * matching module. + * + * Args: + * syncState: container for synchronization data. + */ +static void finalizeProcessingText(SyncState* const syncState) +{ + size_t len; + int retval; + unsigned int* seq; + GArray* factors; + ProcessingDataText* processingData= (ProcessingDataText*) + syncState->processingData; + FILE* testCase= processingData->testCase; + char* line= NULL; + + 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; + unsigned int i; + + 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); + } + + if (sendTime < 0) + { + g_error("Error parsing test file, send time is negative, line was '%s'", line); + } + + if (recvTime < 0) + { + g_error("Error parsing test file, receive time is negative, line was '%s'", line); + } + + // Generate ouput and input events + { + unsigned int addressOffset; + struct { + unsigned int traceNum; + double time; + enum Direction direction; + } loopValues[]= { + {sender, sendTime, OUT}, + {receiver, recvTime, IN}, + }; + + /* addressOffset is added to a traceNum to convert it to an address so + * that the address is not plainly the same as the traceNb. */ + if (syncState->traceNb > 1) + { + addressOffset= pow(10, floor(log(syncState->traceNb - 1) / + log(10)) + 1); + } + else + { + addressOffset= 0; + } + + for (i= 0; i < sizeof(loopValues) / sizeof(*loopValues); i++) + { + Event* event; + + event= malloc(sizeof(Event)); + event->traceNum= loopValues[i].traceNum; + event->wallTime.seconds= floor(loopValues[i].time); + event->wallTime.nanosec= floor((loopValues[i].time - + floor(loopValues[i].time)) * NANOSECONDS_PER_SECOND); + event->cpuTime= round(loopValues[i].time * CPU_FREQ); + event->type= TCP; + event->destroy= &destroyTCPEvent; + event->event.tcpEvent= malloc(sizeof(TCPEvent)); + event->event.tcpEvent->direction= loopValues[i].direction; + event->event.tcpEvent->segmentKey= malloc(sizeof(SegmentKey)); + event->event.tcpEvent->segmentKey->ihl= 5; + event->event.tcpEvent->segmentKey->tot_len= 40; + event->event.tcpEvent->segmentKey->connectionKey.saddr= sender + + addressOffset; + event->event.tcpEvent->segmentKey->connectionKey.daddr= receiver + + addressOffset; + event->event.tcpEvent->segmentKey->connectionKey.source= 57645; + event->event.tcpEvent->segmentKey->connectionKey.dest= 80; + event->event.tcpEvent->segmentKey->seq= seq[sender]; + event->event.tcpEvent->segmentKey->ack_seq= 0; + event->event.tcpEvent->segmentKey->doff= 5; + event->event.tcpEvent->segmentKey->ack= 0; + event->event.tcpEvent->segmentKey->rst= 0; + event->event.tcpEvent->segmentKey->syn= 1; + event->event.tcpEvent->segmentKey->fin= 0; + + syncState->matchingModule->matchEvent(syncState, event); + } + } + + seq[sender]++; + + skipCommentLines(testCase); + retval= getline(&line, &len, testCase); + } + + free(seq); + + if (line) + { + free(line); + } + + factors= syncState->matchingModule->finalizeMatching(syncState); + if (syncState->stats) + { + processingData->factors= factors; + } + else + { + g_array_free(factors, TRUE); + } +} + + +/* + * Print statistics related to processing. Must be called after + * finalizeProcessing. + * + * Args: + * syncState container for synchronization data. + */ +static void printProcessingStatsText(SyncState* const syncState) +{ + unsigned int i; + + printf("Resulting synchronization factors:\n"); + for (i= 0; i < syncState->traceNb; i++) + { + Factors* factors= &g_array_index(((ProcessingDataText*) + syncState->processingData)->factors, Factors, i); + + printf("\ttrace %u drift= %g offset= %g (%f)\n", i, factors->drift, + factors->offset, factors->offset / CPU_FREQ); + } +} + + +/* + * 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); + } +} + + +/* + * Write the processing-specific variables in the gnuplot script. + * + * Args: + * syncState: container for synchronization data + * i: trace number + */ +static void writeProcessingGraphVariablesText(SyncState* const syncState, + const unsigned int i) +{ + fprintf(syncState->graphsStream, "clock_freq_%u= %.3f\n", i, CPU_FREQ); +} + + +/* + * Write the processing-specific options in the gnuplot script. + * + * Args: + * syncState: container for synchronization data + * i: first trace number + * j: second trace number, garanteed to be larger than i + */ +static void writeProcessingTraceTraceOptionsText(SyncState* const syncState, + const unsigned int i, const unsigned int j) +{ + fprintf(syncState->graphsStream, + "set key inside right bottom\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$d (s)\"\n" + "set x2range [GPVAL_X_MIN / clock_freq_%1$u : GPVAL_X_MAX / clock_freq_%1$u]\n" + "set x2tics\n" + "set y2label \"Clock %2$d (s)\"\n" + "set y2range [GPVAL_Y_MIN / clock_freq_%2$u : GPVAL_Y_MAX / clock_freq_%2$u]\n" + "set y2tics\n", i, j); +} + + +/* + * Write the processing-specific options in the gnuplot script. + * + * Args: + * syncState: container for synchronization data + * i: first trace number + * j: second trace number, garanteed to be larger than i + */ +static void writeProcessingTraceTimeOptionsText(SyncState* const syncState, + const unsigned int i, const unsigned int j) +{ + fprintf(syncState->graphsStream, + "set key inside right bottom\n" + "set xlabel \"Clock %1$u\"\n" + "set xtics nomirror\n" + "set ylabel \"time (s)\"\n" + "set ytics nomirror\n" + "set x2label \"Clock %1$d (s)\"\n" + "set x2range [GPVAL_X_MIN / clock_freq_%1$u : GPVAL_X_MAX / clock_freq_%1$u]\n" + "set x2tics\n", i); +} diff --git a/lttv/lttv/sync/event_processing_text.h b/lttv/lttv/sync/event_processing_text.h new file mode 100644 index 00000000..0fda7786 --- /dev/null +++ b/lttv/lttv/sync/event_processing_text.h @@ -0,0 +1,32 @@ +/* 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. + */ + +#ifndef EVENT_PROCESSING_TEXT_H +#define EVENT_PROCESSING_TEXT_H + +#include "event_processing.h" + +typedef struct +{ + // Factors factors[traceNb], only used for stats + GArray* factors; + + FILE* testCase; +} ProcessingDataText; + +#endif diff --git a/lttv/lttv/sync/sync_chain_unittest.c b/lttv/lttv/sync/sync_chain_unittest.c index 3a6de571..9b4b869c 100644 --- a/lttv/lttv/sync/sync_chain_unittest.c +++ b/lttv/lttv/sync/sync_chain_unittest.c @@ -42,7 +42,8 @@ struct OptionsInfo { GArray* longOptions; GString* optionString; - GQueue* index; + GQueue* longIndex; + GHashTable* shortIndex; }; @@ -52,6 +53,8 @@ static void gfPrintModuleOption(gpointer data, gpointer user_data); static void nullLog(const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data); static void gfAddModuleOption(gpointer data, gpointer user_data); +static guint ghfCharHash(gconstpointer key); +static gboolean gefCharEqual(gconstpointer a, gconstpointer b); static ModuleOption optionSyncStats= { @@ -244,34 +247,34 @@ const char* processOptions(const int argc, char* const argv[]) extern int optind, opterr, optopt; GArray* longOptions; GString* optionString; - GQueue* index; + GQueue* longIndex; + int longOption; + GHashTable* shortIndex; longOptions= g_array_sized_new(TRUE, FALSE, sizeof(struct option), g_queue_get_length(&moduleOptions)); optionString= g_string_new(""); - index= g_queue_new(); + longIndex= g_queue_new(); + shortIndex= g_hash_table_new(&ghfCharHash, &gefCharEqual); g_queue_foreach(&moduleOptions, &gfAddModuleOption, &(struct OptionsInfo) - {longOptions, optionString, index}); + {longOptions, optionString, longIndex, shortIndex}); do { int optionIndex= 0; + ModuleOption* moduleOption; + longOption= -1; c= getopt_long(argc, argv, optionString->str, (struct option*) longOptions->data, &optionIndex); - if (c >= 0 && c < g_queue_get_length(index)) + if (longOption >= 0 && longOption < g_queue_get_length(longIndex)) + { + moduleOption= g_queue_peek_nth(longIndex, longOption); + } + else if ((moduleOption= g_hash_table_lookup(shortIndex, &c)) != NULL) { - ModuleOption* moduleOption= g_queue_peek_nth(index, c); - - moduleOption->present= true; - - if (moduleOption->hasArg == REQUIRED_ARG || moduleOption->hasArg - == OPTIONAL_ARG) - { - moduleOption->arg= optarg; - } } else if (c == -1) { @@ -286,10 +289,23 @@ const char* processOptions(const int argc, char* const argv[]) { g_error("Option parse error"); } + + moduleOption->present= true; + + if (moduleOption->hasArg == REQUIRED_ARG) + { + moduleOption->arg= optarg; + } + if (moduleOption->hasArg == OPTIONAL_ARG && optarg) + { + moduleOption->arg= optarg; + } } while (c != -1); g_array_free(longOptions, TRUE); g_string_free(optionString, TRUE); + g_queue_free(longIndex); + g_hash_table_destroy(shortIndex); if (argc <= optind) { @@ -403,9 +419,50 @@ static void gfAddModuleOption(gpointer data, gpointer user_data) newOption.name= option->longName; newOption.has_arg= conversion[option->hasArg]; newOption.flag= NULL; - newOption.val= g_queue_get_length(optionsInfo->index); + newOption.val= g_queue_get_length(optionsInfo->longIndex); g_array_append_val(optionsInfo->longOptions, newOption); - g_string_append(optionsInfo->optionString, colons[option->hasArg]); - g_queue_push_tail(optionsInfo->index, option); + if (option->shortName) + { + g_string_append_c(optionsInfo->optionString, option->shortName); + g_string_append(optionsInfo->optionString, colons[option->hasArg]); + + g_hash_table_insert(optionsInfo->shortIndex, &option->shortName, + option); + } + g_queue_push_tail(optionsInfo->longIndex, option); +} + + +/* + * A GHashFunc for g_hash_table_new() + * + * Args: + * key char*, just one character + */ +static guint ghfCharHash(gconstpointer key) +{ + return *(char*) key; +} + + +/* + * A GEqualFunc for g_hash_table_new() + * + * Args: + * a, b char*, just one character each + * + * Returns: + * TRUE if both values are equal + */ +static gboolean gefCharEqual(gconstpointer a, gconstpointer b) +{ + if (*(char*) a == *(char*) b) + { + return TRUE; + } + else + { + return FALSE; + } } -- 2.34.1