--- /dev/null
+/* 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 NANOSECONDS_PER_SECOND 1000000000
+#define CPU_FREQ 1e9
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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);
+}
{
GArray* longOptions;
GString* optionString;
- GQueue* index;
+ GQueue* longIndex;
+ GHashTable* shortIndex;
};
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= {
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)
{
{
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)
{
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;
+ }
}