From 99d1328d6ede9d172cdd5588c26f4509588678b8 Mon Sep 17 00:00:00 2001
From: Cedric Roux <cedric.roux@eurecom.fr>
Date: Fri, 17 Jun 2016 15:16:49 +0200
Subject: [PATCH] add a very basic replay tracer

to be used in conjunction with another tracer
so conceptually it's more a tracee than a tracer
---
 common/utils/T/tracer/Makefile |   7 +-
 common/utils/T/tracer/replay.c | 178 +++++++++++++++++++++++++++++++++
 2 files changed, 183 insertions(+), 2 deletions(-)
 create mode 100644 common/utils/T/tracer/replay.c

diff --git a/common/utils/T/tracer/Makefile b/common/utils/T/tracer/Makefile
index 59644e1b3..38a660562 100644
--- a/common/utils/T/tracer/Makefile
+++ b/common/utils/T/tracer/Makefile
@@ -5,11 +5,14 @@ CFLAGS=-Wall -g -pthread -DT_TRACER -I.
 
 LIBS=-lX11 -lm -lpng -lXft
 
-all: record textlog enb vcd
+all: record replay textlog enb vcd
 
 record: utils.o record.o database.o config.o
 	$(CC) $(CFLAGS) -o record $^ $(LIBS)
 
+replay: utils.o replay.o
+	$(CC) $(CFLAGS) -o replay $^ $(LIBS)
+
 textlog: utils.o textlog.o database.o event.o handler.o config.o \
          event_selector.o view/view.a gui/gui.a logger/logger.a \
          filter/filter.a
@@ -43,7 +46,7 @@ filter/filter.a:
 	$(CC) $(CFLAGS) -c -o $@ $<
 
 clean:
-	rm -f *.o core tracer_remote textlog enb vcd record
+	rm -f *.o core tracer_remote textlog enb vcd record replay
 	cd gui && make clean
 	cd view && make clean
 	cd logger && make clean
diff --git a/common/utils/T/tracer/replay.c b/common/utils/T/tracer/replay.c
new file mode 100644
index 000000000..a64342f39
--- /dev/null
+++ b/common/utils/T/tracer/replay.c
@@ -0,0 +1,178 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <pthread.h>
+#include "utils.h"
+#include "../T_defs.h"
+
+#define DEFAULT_REMOTE_PORT 2021
+
+/* replay.c does not know anything about events - it just has the array
+ * is_on that grows each time replay.c sees a type that does not fit in
+ * there (the idea is to isolate replay.c as maximum by not using unneeded
+ * information)
+ */
+int *is_on;
+int is_on_size;
+
+/* this lock is used to protect access to is_on/is_on_size */
+pthread_mutex_t biglock = PTHREAD_MUTEX_INITIALIZER;
+
+void set_is_on_size(int size)
+{
+  is_on = realloc(is_on, size * sizeof(int)); if (is_on == NULL) abort();
+  memset(is_on + is_on_size, 0, (size - is_on_size) * sizeof(int));
+  is_on_size = size;
+}
+
+void lock(void)
+{
+  if (pthread_mutex_lock(&biglock)) abort();
+}
+
+void unlock(void)
+{
+  if (pthread_mutex_unlock(&biglock)) abort();
+}
+
+#define QUIT(x) do { printf("%s\n", x); exit(1); } while(0)
+
+void get_message(int s)
+{
+  char t;
+  int l;
+  int id;
+  int on;
+
+  if (read(s, &t, 1) != 1) QUIT("get_message fails");
+  lock();
+  switch (t) {
+  case 0:
+    /* toggle all those IDs */
+    if (read(s, &l, sizeof(int)) != sizeof(int)) QUIT("get_message fails");
+    while (l) {
+      if (read(s, &id, sizeof(int)) != sizeof(int)) QUIT("get_message fails");
+      if (id > is_on_size - 1) set_is_on_size(id + 1);
+      is_on[id] = 1 - is_on[id];
+      l--;
+    }
+    break;
+  case 1:
+    /* set IDs as given */
+    /* optimize? */
+    if (read(s, &l, sizeof(int)) != sizeof(int)) QUIT("get_message fails");
+    if (l > is_on_size) set_is_on_size(l);
+    id = 0;
+    while (l) {
+      if (read(s, &on, sizeof(int)) != sizeof(int))
+        QUIT("get_message fails");
+      is_on[id] = on;
+      id++;
+      l--;
+    }
+    break;
+  case 2: break; /* do nothing, this message is to wait for local tracer */
+  default: abort();
+  }
+  unlock();
+}
+
+void *get_message_thread(void *_socket)
+{
+  int socket = *(int *)_socket;
+
+  while (1) get_message(socket);
+
+  return NULL;
+}
+
+void usage(void)
+{
+  printf(
+"options:\n"
+"    -i <input file>           this option is mandatory\n"
+"    -p <port>                 wait connection on given port (default %d)\n",
+  DEFAULT_REMOTE_PORT
+  );
+  exit(1);
+}
+
+#define ERR printf("ERROR: read file %s failed\n", input_filename)
+
+int main(int n, char **v)
+{
+  int port = DEFAULT_REMOTE_PORT;
+  char *input_filename = NULL;
+  int i;
+  int socket;
+  FILE *in;
+  int do_send;
+
+  for (i = 1; i < n; i++) {
+    if (!strcmp(v[i], "-h") || !strcmp(v[i], "--help")) usage();
+    if (!strcmp(v[i], "-i"))
+      { if (i > n-2) usage(); input_filename = v[++i]; continue; }
+    if (!strcmp(v[i], "-p"))
+      { if (i > n-2) usage(); port = atoi(v[++i]); continue; }
+    usage();
+  }
+
+  if (input_filename == NULL) {
+    printf("ERROR: provide an input file (-i)\n");
+    exit(1);
+  }
+
+  in = fopen(input_filename, "r");
+  if (in == NULL) { perror(input_filename); abort(); }
+
+  socket = get_connection("0.0.0.0", port);
+
+  /* get first message to activate selected traces */
+  get_message(socket);
+
+  new_thread(get_message_thread, &socket);
+
+  while (1) {
+    int type;
+    int32_t length;
+    char v[T_BUFFER_MAX];
+    int vpos = 0;
+
+    /* read event from file */
+    if (fread(&length, 4, 1, in) != 1) break;
+    memcpy(v+vpos, &length, 4);
+    vpos += 4;
+#ifdef T_SEND_TIME
+    if (length < sizeof(struct timespec)) { ERR; break; }
+    if (fread(v+vpos, sizeof(struct timespec), 1, in) != 1) { ERR; break; }
+    vpos += sizeof(struct timespec);
+    length -= sizeof(struct timespec);
+#endif
+    if (length < sizeof(int)) { ERR; break; }
+    if (fread(&type, sizeof(int), 1, in) != 1) { ERR; break; }
+    memcpy(v+vpos, &type, sizeof(int));
+    vpos += sizeof(int);
+    length -= sizeof(int);
+    if (length) if (fread(v+vpos, length, 1, in) != 1) { ERR; break; }
+    vpos += length;
+
+    /* only send if configured to do so */
+    lock();
+    if (type < 0) do_send = 1;
+    else {
+      if (type > is_on_size - 1) set_is_on_size(type+1);
+      do_send = is_on[type];
+    }
+    unlock();
+
+    if (do_send)
+      if (socket_send(socket, v, vpos) != 0)
+        { printf("ERROR: socket writing failed\n"); abort(); }
+  }
+
+  fclose(in);
+  close(socket);
+
+  return 0;
+}
-- 
GitLab