diff --git a/cmake_targets/CMakeLists.txt b/cmake_targets/CMakeLists.txt
index ccff80a055f66dae1e8552b2b0dcc3506cecb4fb..c23b0d1a91471d406910790e3c54dddb42dbc9d3 100644
--- a/cmake_targets/CMakeLists.txt
+++ b/cmake_targets/CMakeLists.txt
@@ -238,6 +238,7 @@ add_boolean_option(TEST_OMG            False "???")
 add_boolean_option(DEBUG_OMG           False "???")
 add_boolean_option(XFORMS              False "This adds the possibility to see the signal oscilloscope")
 add_boolean_option(PRINT_STATS         False "This adds the possibility to see the status")
+add_boolean_option(T_TRACER            False "Activate the T tracer, a debugging/monitoring framework" )
 
 add_boolean_option(DEBUG_CONSOLE False "makes debugging easier, disables stdout/stderr buffering")
 
@@ -1559,6 +1560,36 @@ endif (${XFORMS})
 
 set(CMAKE_MODULE_PATH "${OPENAIR_DIR}/cmake_targets/tools/MODULES" "${CMAKE_MODULE_PATH}")
 
+#include T directory even if the T is off because T macros are in the code
+#no matter what
+include_directories("${OPENAIR_DIR}/common/utils/T")
+
+if (${T_TRACER})
+  set(T_SOURCE
+      ${OPENAIR_DIR}/common/utils/T/T.c
+      ${OPENAIR_DIR}/common/utils/T/local_tracer.c)
+  set (T_LIB "rt")
+endif (${T_TRACER})
+
+#Some files in the T directory are generated.
+#This rule and the following deal with it.
+add_custom_command (
+  OUTPUT ${OPENAIR_DIR}/common/utils/T/T_IDs.h
+  COMMAND make
+  WORKING_DIRECTORY ${OPENAIR_DIR}/common/utils/T
+  DEPENDS ${OPENAIR_DIR}/common/utils/T/T_messages.txt
+  )
+
+#This rule is specifically needed to generate T files
+#before anything else in a project that uses the T.
+#See below, there are some 'add_dependencies' showing that.
+#Basically we create a custom target and we make other
+#targets depend on it. That forces cmake to generate
+#T files before anything else.
+add_custom_target (
+  generate_T
+  DEPENDS ${OPENAIR_DIR}/common/utils/T/T_IDs.h
+)
 
 # Hack on a test of asn1c version (already dirty)
 add_definitions(-DASN1_MINIMUM_VERSION=924)
@@ -1591,6 +1622,7 @@ add_executable(lte-softmodem
   ${RTAI_SOURCE}
   ${XFORMS_SOURCE}
   ${XFORMS_SOURCE_SOFTMODEM}
+  ${T_SOURCE}
   )
 
 target_link_libraries (lte-softmodem
@@ -1603,6 +1635,7 @@ target_link_libraries (lte-softmodem ${LIBXML2_LIBRARIES})
 target_link_libraries (lte-softmodem pthread m ${CONFIG_LIBRARIES} rt crypt ${CRYPTO_LIBRARIES} ${OPENSSL_LIBRARIES} ${NETTLE_LIBRARIES} sctp ${option_HW_lib} ${option_TP_lib} ${XFORMS_LIBRARIES} ) 
 target_link_libraries (lte-softmodem ${LIBBOOST_LIBRARIES})
 target_link_libraries (lte-softmodem ${LIB_LMS_LIBRARIES})
+target_link_libraries (lte-softmodem ${T_LIB})
 
 # lte-softmodem-nos1 is both eNB and UE implementation
 ###################################################
@@ -1627,6 +1660,7 @@ add_executable(lte-softmodem-nos1
   ${RTAI_SOURCE}
   ${XFORMS_SOURCE}
   ${XFORMS_SOURCE_SOFTMODEM}
+  ${T_SOURCE}
   )
 target_link_libraries (lte-softmodem-nos1
   -Wl,--start-group
@@ -1637,6 +1671,7 @@ target_link_libraries (lte-softmodem-nos1 ${LIBXML2_LIBRARIES})
 target_link_libraries (lte-softmodem-nos1 pthread m ${CONFIG_LIBRARIES} rt crypt ${CRYPTO_LIBRARIES} ${OPENSSL_LIBRARIES} ${NETTLE_LIBRARIES}  ${option_HW_lib}  ${option_TP_lib} ${XFORMS_LIBRARIES} )
 target_link_libraries (lte-softmodem-nos1 ${LIBBOOST_LIBRARIES})
 target_link_libraries (lte-softmodem-nos1  ${LIB_LMS_LIBRARIES})
+target_link_libraries (lte-softmodem ${T_LIB})
 
 # rrh
 ################################
@@ -1649,6 +1684,7 @@ add_executable(rrh_gw
   ${OPENAIR_TARGETS}/ARCH/COMMON/common_lib.c		
   ${HW_SOURCE}
   ${TRANSPORT_SOURCE}  
+  ${T_SOURCE}
   )
 target_include_directories(rrh_gw PRIVATE  ${OPENAIR_DIR}/common/utils/itti)
 target_link_libraries(rrh_gw
@@ -1658,6 +1694,7 @@ target_link_libraries(rrh_gw
 target_link_libraries (rrh_gw rt pthread m )
 target_link_libraries (rrh_gw  ${option_HW_lib} ${option_TP_lib} ${LIBBOOST_LIBRARIES} )
 target_link_libraries (rrh_gw ${LIB_LMS_LIBRARIES})
+target_link_libraries (rrh_gw ${T_LIB})
 
 Message("-- option_HW_lib=${option_HW_lib}")
 Message("-- HW_SOURCE=${HW_SOURCE}")
@@ -1723,6 +1760,7 @@ add_executable(oaisim
   ${HW_SOURCE}
   ${TRANSPORT_SOURCE}  
   ${XFORMS_SOURCE}
+  ${T_SOURCE}
 )
 
 
@@ -1737,6 +1775,7 @@ target_link_libraries (oaisim pthread m ${CONFIG_LIBRARIES} rt crypt ${CRYPTO_LI
   ${ATLAS_LIBRARIES} ${XFORMS_LIBRARIES} ${OPENPGM_LIBRARIES})
 #Force link with forms, regardless XFORMS option
 target_link_libraries (oaisim forms)
+target_link_libraries (oaisim ${T_LIB})
 
 
 
@@ -1762,6 +1801,7 @@ add_executable(oaisim_nos1
   ${HW_SOURCE}
   ${TRANSPORT_SOURCE}  
   ${XFORMS_SOURCE}
+  ${T_SOURCE}
 )
 target_include_directories(oaisim_nos1 PUBLIC  ${OPENAIR_TARGETS}/SIMU/USER)
 target_link_libraries (oaisim_nos1
@@ -1774,7 +1814,7 @@ target_link_libraries (oaisim_nos1 pthread m ${CONFIG_LIBRARIES} rt crypt ${CRYP
   ${ATLAS_LIBRARIES} ${XFORMS_LIBRARIES} ${OPENPGM_LIBRARIES})
 #Force link with forms, regardless XFORMS option
 target_link_libraries (oaisim_nos1 forms)
-
+target_link_libraries (oaisim_nos1 ${T_LIB})
 
 # Unitary tests for each piece of L1: example, mbmssim is MBMS L1 simulator
 #####################################
@@ -1784,10 +1824,11 @@ foreach(myExe dlsim ulsim pbchsim scansim mbmssim pdcchsim pucchsim prachsim syn
     ${OPENAIR_BIN_DIR}/messages_xml.h
     ${OPENAIR1_DIR}/SIMULATION/LTE_PHY/${myExe}.c
     ${XFORMS_SOURCE}
+    ${T_SOURCE}
     )
   target_link_libraries (${myExe}
     -Wl,--start-group SIMU UTIL SCHED_LIB PHY LFDS ${ITTI_LIB} -Wl,--end-group
-    pthread m rt ${CONFIG_LIBRARIES} ${ATLAS_LIBRARIES} ${XFORMS_LIBRARIES}
+    pthread m rt ${CONFIG_LIBRARIES} ${ATLAS_LIBRARIES} ${XFORMS_LIBRARIES} ${T_LIB}
     )
 endforeach(myExe)
 
@@ -1852,8 +1893,23 @@ endforeach(myExe)
 #../targets/TEST/PDCP/test_pdcp.c
 #../targets/TEST/PDCP/with_rlc/test_pdcp_rlc.c
 
-
-
+#ensure that the T header files are generated before targets depending on them
+if (${T_TRACER})
+  add_dependencies(lte-softmodem generate_T)
+  add_dependencies(lte-softmodem-nos1 generate_T)
+  add_dependencies(rrh_gw generate_T)
+  add_dependencies(oaisim generate_T)
+  add_dependencies(oaisim_nos1 generate_T)
+  add_dependencies(dlsim generate_T)
+  add_dependencies(ulsim generate_T)
+  add_dependencies(pbchsim generate_T)
+  add_dependencies(scansim generate_T)
+  add_dependencies(mbmssim generate_T)
+  add_dependencies(pdcchsim generate_T)
+  add_dependencies(pucchsim generate_T)
+  add_dependencies(prachsim generate_T)
+  add_dependencies(syncsim generate_T)
+endif (${T_TRACER})
 
 ##################################################
 # Generated specific cases is not regular code
diff --git a/cmake_targets/build_oai b/cmake_targets/build_oai
index 96adfa004099a85db33d7be6a9b967dabb362a72..d1306b804b3073f35c7b30a2bf8d15fbdc816793 100755
--- a/cmake_targets/build_oai
+++ b/cmake_targets/build_oai
@@ -58,6 +58,7 @@ CFLAGS_PROCESSOR_USER=""
 RUN_GROUP=0
 TEST_CASE_GROUP=""
 BUILD_DOXYGEN=0
+T_TRACER="False"
 trap handle_ctrl_c INT
 
 function print_help() {
@@ -128,6 +129,8 @@ Options
    Enable deadline scheduler of Linux kernel (>=3.14.x). 
 --disable-cpu-affinity
    Disables CPU Affinity between UHD/TX/RX Threads (Valid only when deadline scheduler is disabled). By defaulT, CPU Affinity is enabled when not using deadline scheduler. It is enabled only with >2 CPUs. For eNB, CPU_0-> Device library (UHD), CPU_1->TX Threads, CPU_2...CPU_MAX->Rx Threads. For UE, CPU_0->Device Library(UHD), CPU_1..CPU_MAX -> All the UE threads
+--T-tracer
+   Enables the T tracer.
 Usage (first build):
  oaisim (eNB + UE): ./build_oai -I -g --oaisim -x --install-system-files
  Eurecom EXMIMO + COTS UE : ./build_oai -I -g --eNB -x --install-system-files
@@ -270,6 +273,10 @@ function main() {
             CPU_AFFINITY_FLAG_USER="False"
             echo_info "Disabling CPU Affinity (only valid when not using deadline scheduler)"
             shift 1;;
+       --T-tracer)
+            T_TRACER="True"
+            echo_info "Enabling the T tracer"
+            shift 1;;
         -h | --help)
             print_help
             exit 1;;
@@ -434,6 +441,7 @@ function main() {
     echo "set(PACKAGE_NAME \"${lte_exec}\")"       >>  $cmake_file
     echo "set (DEADLINE_SCHEDULER \"${DEADLINE_SCHEDULER_FLAG_USER}\" )"    >>$cmake_file
     echo "set (CPU_AFFINITY \"${CPU_AFFINITY_FLAG_USER}\" )"      >>$cmake_file
+    echo "set ( T_TRACER $T_TRACER )"              >>  $cmake_file
     echo 'include(${CMAKE_CURRENT_SOURCE_DIR}/../CMakeLists.txt)' >> $cmake_file
     cd  $DIR/$lte_build_dir/build
     cmake ..
@@ -567,6 +575,7 @@ function main() {
     echo "set ( PRINT_STATS $PRINT_STATS )" >>  $cmake_file
     echo "set ( RRC_ASN1_VERSION \"${REL}\")" >>  $cmake_file
     echo "set ( ENABLE_VCD_FIFO $VCD_TIMING )" >>  $cmake_file
+    echo "set ( T_TRACER $T_TRACER )"          >>  $cmake_file
     echo 'include(${CMAKE_CURRENT_SOURCE_DIR}/../CMakeLists.txt)' >> $cmake_file
     [ "$CLEAN" = "1" ] && rm -rf $DIR/$oaisim_build_dir/build
     mkdir -p $DIR/$oaisim_build_dir/build
@@ -632,6 +641,7 @@ function main() {
     echo "set(XFORMS $XFORMS )" >>  $cmake_file
     echo "set(RRC_ASN1_VERSION \"${REL}\")" >>  $cmake_file
     echo "set(ENABLE_VCD_FIFO $VCD_TIMING )" >>  $cmake_file
+    echo "set ( T_TRACER $T_TRACER )"        >>  $cmake_file
     echo 'include(${CMAKE_CURRENT_SOURCE_DIR}/../CMakeLists.txt)' >> $cmake_file
     #[ "$CLEAN" = "1" ] && rm -rf $DIR/oaisim_mme_build_oai/build
     #mkdir -p $DIR/oaisim_mme_build_oai/build
@@ -662,6 +672,7 @@ function main() {
      echo 'set(PACKAGE_NAME "\"rrh_gw\"")'        >>  $cmake_file
      echo "set (DEADLINE_SCHEDULER \"${DEADLINE_SCHEDULER_FLAG_USER}\" )"    >>$cmake_file
      echo "set (CPU_AFFINITY \"${CPU_AFFINITY_FLAG_USER}\" )"    >>$cmake_file
+     echo "set ( T_TRACER $T_TRACER )"            >>  $cmake_file
      echo 'include(${CMAKE_CURRENT_SOURCE_DIR}/../CMakeLists.txt)' >> $cmake_file    
      cd $DIR/$rrh_build_dir/build
      cmake ..
diff --git a/common/utils/T/Makefile b/common/utils/T/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..dcb0d12fab9e638f49e812255e29cf61c058b755
--- /dev/null
+++ b/common/utils/T/Makefile
@@ -0,0 +1,23 @@
+CC=gcc
+CFLAGS=-Wall -g
+
+GENIDS=genids
+GENIDS_OBJS=genids.o
+
+all : $(GENIDS) T_messages.txt.h T_IDs.h
+
+$(GENIDS): $(GENIDS_OBJS)
+	$(CC) $(CFLAGS) -o $(GENIDS) $(GENIDS_OBJS)
+
+%.o: %.c
+	$(CC) $(CFLAGS) -c -o $@ $<
+
+T_messages.txt.h: T_messages.txt
+	xxd -i T_messages.txt > T_messages.txt.h
+
+T_IDs.h: $(GENIDS) T_messages.txt
+	./$(GENIDS) T_messages.txt T_IDs.h
+
+clean:
+	rm -f *.o $(GENIDS) core T_IDs.h T_messages.txt.h
+	cd tracer && make clean
diff --git a/common/utils/T/T.c b/common/utils/T/T.c
new file mode 100644
index 0000000000000000000000000000000000000000..786b3722ed21c98b2734a126b077ed42a3f2295c
--- /dev/null
+++ b/common/utils/T/T.c
@@ -0,0 +1,181 @@
+#include "T.h"
+#include "T_messages.txt.h"
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+
+#define QUIT(x) do { \
+  printf("T tracer: QUIT: %s\n", x); \
+  exit(1); \
+} while (0)
+
+/* array used to activate/disactivate a log */
+static int T_IDs[T_NUMBER_OF_IDS];
+int *T_active = T_IDs;
+
+static int T_socket;
+
+/* T_cache
+ * - the T macro picks up the head of freelist and marks it busy
+ * - the T sender thread periodically wakes up and sends what's to be sent
+ */
+volatile int _T_freelist_head;
+volatile int *T_freelist_head = &_T_freelist_head;
+T_cache_t *T_cache;
+
+static void get_message(int s)
+{
+  char t;
+  int l;
+  int id;
+  int is_on;
+
+  if (read(s, &t, 1) != 1) QUIT("get_message fails");
+printf("got mess %d\n", t);
+  switch (t) {
+  case 0:
+    /* toggle all those IDs */
+    /* optimze? (too much syscalls) */
+    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");
+      T_IDs[id] = 1 - T_IDs[id];
+      l--;
+    }
+    break;
+  case 1:
+    /* set IDs as given */
+    /* optimize? */
+    if (read(s, &l, sizeof(int)) != sizeof(int)) QUIT("get_message fails");
+    id = 0;
+    while (l) {
+      if (read(s, &is_on, sizeof(int)) != sizeof(int))
+        QUIT("get_message fails");
+      T_IDs[id] = is_on;
+      id++;
+      l--;
+    }
+    break;
+  case 2: break; /* do nothing, this message is to wait for local tracer */
+  }
+}
+
+static void *T_receive_thread(void *_)
+{
+  while (1) get_message(T_socket);
+  return NULL;
+}
+
+static void new_thread(void *(*f)(void *), void *data)
+{
+  pthread_t t;
+  pthread_attr_t att;
+
+  if (pthread_attr_init(&att))
+    { fprintf(stderr, "pthread_attr_init err\n"); exit(1); }
+  if (pthread_attr_setdetachstate(&att, PTHREAD_CREATE_DETACHED))
+    { fprintf(stderr, "pthread_attr_setdetachstate err\n"); exit(1); }
+  if (pthread_create(&t, &att, f, data))
+    { fprintf(stderr, "pthread_create err\n"); exit(1); }
+  if (pthread_attr_destroy(&att))
+    { fprintf(stderr, "pthread_attr_destroy err\n"); exit(1); }
+}
+
+/* defined in local_tracer.c */
+void T_local_tracer_main(int remote_port, int wait_for_tracer,
+    int local_socket);
+
+/* We monitor the tracee and the local tracer processes.
+ * When one dies we forcefully kill the other.
+ */
+#include <sys/types.h>
+#include <sys/wait.h>
+static void monitor_and_kill(int child1, int child2)
+{
+  int child;
+  int status;
+
+  child = wait(&status);
+  if (child == -1) perror("wait");
+  kill(child1, SIGKILL);
+  kill(child2, SIGKILL);
+  exit(0);
+}
+
+void T_init(int remote_port, int wait_for_tracer)
+{
+  int socket_pair[2];
+  int s;
+  int T_shm_fd;
+  unsigned char *buf;
+  int len;
+  int child1, child2;
+
+  if (socketpair(AF_UNIX, SOCK_STREAM, 0, socket_pair))
+    { perror("socketpair"); abort(); }
+
+  /* child1 runs the local tracer and child2 runs the tracee */
+
+  child1 = fork(); if (child1 == -1) abort();
+  if (child1 == 0) {
+    close(socket_pair[1]);
+    T_local_tracer_main(remote_port, wait_for_tracer, socket_pair[0]);
+    exit(0);
+  }
+  close(socket_pair[0]);
+
+  child2 = fork(); if (child2 == -1) abort();
+  if (child2 != 0) {
+    close(socket_pair[1]);
+    monitor_and_kill(child1, child2);
+  }
+
+  s = socket_pair[1];
+  /* wait for first message - initial list of active T events */
+  get_message(s);
+
+  T_socket = s;
+
+  /* setup shared memory */
+  T_shm_fd = shm_open(T_SHM_FILENAME, O_RDWR /*| O_SYNC*/, 0666);
+  shm_unlink(T_SHM_FILENAME);
+  if (T_shm_fd == -1) { perror(T_SHM_FILENAME); abort(); }
+  T_cache = mmap(NULL, T_CACHE_SIZE * sizeof(T_cache_t),
+                 PROT_READ | PROT_WRITE, MAP_SHARED, T_shm_fd, 0);
+  if (T_cache == NULL)
+    { perror(T_SHM_FILENAME); abort(); }
+  close(T_shm_fd);
+
+  new_thread(T_receive_thread, NULL);
+
+  /* trace T_message.txt
+   * Send several messages -1 with content followed by message -2.
+   * We can't use the T macro directly, events -1 and -2 are special.
+   */
+  buf = T_messages_txt;
+  len = T_messages_txt_len;
+  while (len) {
+    int send_size = len;
+    if (send_size > T_PAYLOAD_MAXSIZE - sizeof(int))
+      send_size = T_PAYLOAD_MAXSIZE - sizeof(int);
+    do {
+      T_LOCAL_DATA
+      T_HEADER(T_ID(-1));
+      T_PUT_buffer(1, ((T_buffer){addr:(buf), length:(len)}));
+      T_COMMIT();
+    } while (0);
+    buf += send_size;
+    len -= send_size;
+  }
+  do {
+    T_LOCAL_DATA
+    T_HEADER(T_ID(-2));
+    T_COMMIT();
+  } while (0);
+}
diff --git a/common/utils/T/T.h b/common/utils/T/T.h
new file mode 100644
index 0000000000000000000000000000000000000000..a5aacb4b4f8e7ba121eb29078992e9a890740829
--- /dev/null
+++ b/common/utils/T/T.h
@@ -0,0 +1,574 @@
+#ifndef _T_T_T_
+#define _T_T_T_
+
+#if T_TRACER
+
+#include <stdint.h>
+
+#include "T_defs.h"
+
+#ifdef T_SEND_TIME
+#include <time.h>
+#endif
+
+/* T message IDs */
+#include "T_IDs.h"
+
+/* known type - this is where you add new types */
+
+#define T_INT(x) int, (x)
+#define T_FLOAT(x) float, (x)
+#define T_BUFFER(x, len) buffer, ((T_buffer){addr:(x), length:(len)})
+#define T_STRING(x) string, (x)
+#define T_PRINTF(...) printf, (__VA_ARGS__)
+
+/* for each known type a T_PUT_XX macro is defined */
+
+#define T_PUT_int(argnum, val) \
+  do { \
+    int T_PUT_var = (val); \
+    T_CHECK_SIZE(sizeof(int), argnum); \
+    memcpy(T_LOCAL_buf + T_LOCAL_size, &T_PUT_var, sizeof(int)); \
+    T_LOCAL_size += sizeof(int); \
+  } while (0)
+
+#define T_PUT_ulong(argnum, val) \
+  do { \
+    unsigned long T_PUT_var = (val); \
+    T_CHECK_SIZE(sizeof(unsigned long), argnum); \
+    memcpy(T_LOCAL_buf + T_LOCAL_size, &T_PUT_var, sizeof(unsigned long)); \
+    T_LOCAL_size += sizeof(unsigned long); \
+  } while (0)
+
+#define T_PUT_float(argnum, val) \
+  do { \
+    float T_PUT_var = (val); \
+    T_CHECK_SIZE(sizeof(float), argnum); \
+    memcpy(T_LOCAL_buf + T_LOCAL_size, &T_PUT_var, sizeof(float)); \
+    T_LOCAL_size += sizeof(float); \
+  } while (0)
+
+#define T_PUT_buffer(argnum, val) \
+  do { \
+    T_buffer T_PUT_buffer_var = (val); \
+    T_PUT_int(argnum, T_PUT_buffer_var.length); \
+    T_CHECK_SIZE(T_PUT_buffer_var.length, argnum); \
+    memcpy(T_LOCAL_buf + T_LOCAL_size, T_PUT_buffer_var.addr, \
+           T_PUT_buffer_var.length); \
+    T_LOCAL_size += T_PUT_buffer_var.length; \
+  } while (0)
+
+#define T_PUT_string(argnum, val) \
+  do { \
+    char *T_PUT_var = (val); \
+    int T_PUT_len = strlen(T_PUT_var) + 1; \
+    T_CHECK_SIZE(T_PUT_len, argnum); \
+    memcpy(T_LOCAL_buf + T_LOCAL_size, T_PUT_var, T_PUT_len); \
+    T_LOCAL_size += T_PUT_len; \
+  } while (0)
+
+#define T_PUT_printf_deref(...) __VA_ARGS__
+
+#define T_PUT_printf(argnum, x) \
+  do { \
+    int T_PUT_len = snprintf(T_LOCAL_buf + T_LOCAL_size, \
+                             T_BUFFER_MAX - T_LOCAL_size, T_PUT_printf_deref x); \
+    if (T_PUT_len < 0) { \
+      printf("%s:%d:%s: you can't read this, or can you?", \
+               __FILE__, __LINE__, __FUNCTION__); \
+      abort(); \
+    } \
+    if (T_PUT_len >= T_BUFFER_MAX - T_LOCAL_size) { \
+      printf("%s:%d:%s: cannot put argument %d in T macro, not enough space" \
+               ", consider increasing T_BUFFER_MAX (%d)\n", \
+               __FILE__, __LINE__, __FUNCTION__, argnum, T_BUFFER_MAX); \
+      abort(); \
+    } \
+    T_LOCAL_size += T_PUT_len + 1; \
+  } while (0)
+
+/* structure type to detect that you pass a known type as first arg of T */
+struct T_header;
+
+/* to define message ID */
+#define T_ID(x) ((struct T_header *)(uintptr_t)(x))
+
+/* T macro tricks */
+
+#define TN(...) TN_N(__VA_ARGS__,33,32,31,30,29,28,27,26,25,24,23,22,21,\
+        20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0)(__VA_ARGS__)
+#define TN_N(n0,n1,n2,n3,n4,n5,n6,n7,n8,n9,n10,n11,n12,n13,n14,n15,n16,n17,\
+        n18,n19,n20,n21,n22,n23,n24,n25,n26,n27,n28,n29,n30,n31,n32,n,...) T##n
+#define T(...) TN(__VA_ARGS__)
+
+/* type used to send arbitrary buffer data */
+typedef struct {
+  void *addr;
+  int length;
+} T_buffer;
+
+extern volatile int *T_freelist_head;
+extern T_cache_t *T_cache;
+
+/* used at header of Tn, allocates buffer */
+#define T_LOCAL_DATA \
+  char *T_LOCAL_buf; \
+  int T_LOCAL_size = 0; \
+  int T_LOCAL_slot; \
+  T_LOCAL_slot = __sync_fetch_and_add(T_freelist_head, 1) \
+                 & (T_CACHE_SIZE - 1); \
+  (void)__sync_fetch_and_and(T_freelist_head, T_CACHE_SIZE - 1); \
+  if (T_cache[T_LOCAL_slot].busy) { \
+    printf("%s:%d:%s: T cache is full - consider increasing its size\n", \
+           __FILE__, __LINE__, __FUNCTION__); \
+    abort(); \
+  } \
+  T_LOCAL_buf = T_cache[T_LOCAL_slot].buffer;
+
+#define T_ACTIVE(x) T_active[(intptr_t)x]
+
+#define T_COMMIT() \
+  T_cache[T_LOCAL_slot].length = T_LOCAL_size; \
+  __sync_synchronize(); \
+  T_cache[T_LOCAL_slot].busy = 1; \
+
+#define T_CHECK_SIZE(len, argnum) \
+  if (T_LOCAL_size + (len) > T_BUFFER_MAX) { \
+    printf("%s:%d:%s: cannot put argument %d in T macro, not enough space" \
+             ", consider increasing T_BUFFER_MAX (%d)\n", \
+             __FILE__, __LINE__, __FUNCTION__, argnum, T_BUFFER_MAX); \
+    abort(); \
+  }
+
+#if 0
+#define T_PUT(type, var, argnum) \
+  do { \
+    if (T_LOCAL_size + sizeof(var) > T_BUFFER_MAX) { \
+      printf("%s:%d:%s: cannot put argument %d in T macro, not enough space" \
+               ", consider increasing T_BUFFER_MAX (%d)\n", \
+               __FILE__, __LINE__, __FUNCTION__, argnum, T_BUFFER_MAX); \
+      abort(); \
+    } \
+    memcpy(T_LOCAL_buf + T_LOCAL_size, &var, sizeof(var)); \
+    T_LOCAL_size += sizeof(var); \
+  } while (0)
+#endif
+
+#if 0
+#define T_PROCESS(x, argnum) \
+  do { \
+    T_PUT(typeof(x), x, argnum); \
+  } while (0)
+#endif
+
+#if 0
+#define T_PROCESS(x, argnum) \
+  do { \
+    if (__builtin_types_compatible_p(typeof(x), int)) \
+      { T_PUT(int, (intptr_t)(x), argnum); printf("int\n"); } \
+    else if (__builtin_types_compatible_p(typeof(x), short)) \
+      { T_PUT(short, (intptr_t)(x), argnum); printf("short\n"); } \
+    else if (__builtin_types_compatible_p(typeof(x), float)) \
+      { T_PUT(float, (x), argnum); printf("float\n"); } \
+    else if (__builtin_types_compatible_p(typeof(x), char *)) \
+      { T_PUT(char *, (char *)(intptr_t)(x), argnum); printf("char *\n"); } \
+    else if (__builtin_types_compatible_p(typeof(x), float *)) \
+      { T_PUT(float *, (float *)(intptr_t)(x), argnum); printf("float *\n"); } \
+    else if (__builtin_types_compatible_p(typeof(x), void *)) \
+      { T_PUT(void *, (void *)(intptr_t)(x), argnum); printf("void *\n"); } \
+    else { \
+      printf("%s:%d:%s: unsupported type for argument %d in T macro\n", \
+               __FILE__, __LINE__, __FUNCTION__, argnum); \
+      abort(); \
+    } \
+  } while (0)
+#endif
+
+#ifdef T_SEND_TIME
+
+#define T_HEADER(x) \
+  do { \
+    if (!__builtin_types_compatible_p(typeof(x), struct T_header *)) { \
+      printf("%s:%d:%s: " \
+             "bad use of T, pass a message ID as first parameter\n", \
+             __FILE__, __LINE__, __FUNCTION__); \
+      abort(); \
+    } \
+    struct timespec T_HEADER_time; \
+    if (clock_gettime(CLOCK_REALTIME, &T_HEADER_time)) abort(); \
+    memcpy(T_LOCAL_buf, &T_HEADER_time, sizeof(struct timespec)); \
+    T_LOCAL_size += sizeof(struct timespec); \
+    T_PUT_int(1, (int)(uintptr_t)(x)); \
+  } while (0)
+
+#else /* #ifdef T_SEND_TIME */
+
+#define T_HEADER(x) \
+  do { \
+    if (!__builtin_types_compatible_p(typeof(x), struct T_header *)) { \
+      printf("%s:%d:%s: " \
+             "bad use of T, pass a message ID as first parameter\n", \
+             __FILE__, __LINE__, __FUNCTION__); \
+      abort(); \
+    } \
+    T_PUT_int(1, (int)(uintptr_t)(x)); \
+  } while (0)
+
+#endif /* #ifdef T_SEND_TIME */
+
+#define T1(t) \
+  do { \
+    if (T_ACTIVE(t)) { \
+      T_LOCAL_DATA \
+      T_HEADER(t); \
+      T_COMMIT(); \
+    } \
+  } while (0)
+
+#define T3(t,t0,x0) \
+  do { \
+    if (T_ACTIVE(t)) { \
+      T_LOCAL_DATA \
+      T_HEADER(t); \
+      T_PUT_##t0(2, x0); \
+      T_COMMIT(); \
+    } \
+  } while (0)
+
+#define T5(t,t0,x0,t1,x1) \
+  do { \
+    if (T_ACTIVE(t)) { \
+      T_LOCAL_DATA \
+      T_HEADER(t); \
+      T_PUT_##t0(2, x0); \
+      T_PUT_##t1(3, x1); \
+      T_COMMIT(); \
+    } \
+  } while (0)
+
+#define T7(t,t0,x0,t1,x1,t2,x2) \
+  do { \
+    if (T_ACTIVE(t)) { \
+      T_LOCAL_DATA \
+      T_HEADER(t); \
+      T_PUT_##t0(2, x0); \
+      T_PUT_##t1(3, x1); \
+      T_PUT_##t2(4, x2); \
+      T_COMMIT(); \
+    } \
+  } while (0)
+
+#define T9(t,t0,x0,t1,x1,t2,x2,t3,x3) \
+  do { \
+    if (T_ACTIVE(t)) { \
+      T_LOCAL_DATA \
+      T_HEADER(t); \
+      T_PUT_##t0(2, x0); \
+      T_PUT_##t1(3, x1); \
+      T_PUT_##t2(4, x2); \
+      T_PUT_##t3(5, x3); \
+      T_COMMIT(); \
+    } \
+  } while (0)
+
+#define T11(t,t0,x0,t1,x1,t2,x2,t3,x3,t4,x4) \
+  do { \
+    if (T_ACTIVE(t)) { \
+      T_LOCAL_DATA \
+      T_HEADER(t); \
+      T_PUT_##t0(2, x0); \
+      T_PUT_##t1(3, x1); \
+      T_PUT_##t2(4, x2); \
+      T_PUT_##t3(5, x3); \
+      T_PUT_##t4(6, x4); \
+      T_COMMIT(); \
+    } \
+  } while (0)
+
+#define T13(t,t0,x0,t1,x1,t2,x2,t3,x3,t4,x4,t5,x5) \
+  do { \
+    if (T_ACTIVE(t)) { \
+      T_LOCAL_DATA \
+      T_HEADER(t); \
+      T_PUT_##t0(2, x0); \
+      T_PUT_##t1(3, x1); \
+      T_PUT_##t2(4, x2); \
+      T_PUT_##t3(5, x3); \
+      T_PUT_##t4(6, x4); \
+      T_PUT_##t5(7, x5); \
+      T_COMMIT(); \
+    } \
+  } while (0)
+
+#define T15(t,t0,x0,t1,x1,t2,x2,t3,x3,t4,x4,t5,x5,t6,x6) \
+  do { \
+    if (T_ACTIVE(t)) { \
+      T_LOCAL_DATA \
+      T_HEADER(t); \
+      T_PUT_##t0(2, x0); \
+      T_PUT_##t1(3, x1); \
+      T_PUT_##t2(4, x2); \
+      T_PUT_##t3(5, x3); \
+      T_PUT_##t4(6, x4); \
+      T_PUT_##t5(7, x5); \
+      T_PUT_##t6(8, x6); \
+      T_COMMIT(); \
+    } \
+  } while (0)
+
+#define T17(t,t0,x0,t1,x1,t2,x2,t3,x3,t4,x4,t5,x5,t6,x6,t7,x7) \
+  do { \
+    if (T_ACTIVE(t)) { \
+      T_LOCAL_DATA \
+      T_HEADER(t); \
+      T_PUT_##t0(2, x0); \
+      T_PUT_##t1(3, x1); \
+      T_PUT_##t2(4, x2); \
+      T_PUT_##t3(5, x3); \
+      T_PUT_##t4(6, x4); \
+      T_PUT_##t5(7, x5); \
+      T_PUT_##t6(8, x6); \
+      T_PUT_##t7(9, x7); \
+      T_COMMIT(); \
+    } \
+  } while (0)
+
+#define T19(t,t0,x0,t1,x1,t2,x2,t3,x3,t4,x4,t5,x5,t6,x6,t7,x7,t8,x8) \
+  do { \
+    if (T_ACTIVE(t)) { \
+      T_LOCAL_DATA \
+      T_HEADER(t); \
+      T_PUT_##t0(2, x0); \
+      T_PUT_##t1(3, x1); \
+      T_PUT_##t2(4, x2); \
+      T_PUT_##t3(5, x3); \
+      T_PUT_##t4(6, x4); \
+      T_PUT_##t5(7, x5); \
+      T_PUT_##t6(8, x6); \
+      T_PUT_##t7(9, x7); \
+      T_PUT_##t8(10, x8); \
+      T_COMMIT(); \
+    } \
+  } while (0)
+
+#define T21(t,t0,x0,t1,x1,t2,x2,t3,x3,t4,x4,t5,x5,t6,x6,t7,x7,t8,x8,t9,x9) \
+  do { \
+    if (T_ACTIVE(t)) { \
+      T_LOCAL_DATA \
+      T_HEADER(t); \
+      T_PUT_##t0(2, x0); \
+      T_PUT_##t1(3, x1); \
+      T_PUT_##t2(4, x2); \
+      T_PUT_##t3(5, x3); \
+      T_PUT_##t4(6, x4); \
+      T_PUT_##t5(7, x5); \
+      T_PUT_##t6(8, x6); \
+      T_PUT_##t7(9, x7); \
+      T_PUT_##t8(10, x8); \
+      T_PUT_##t9(11, x9); \
+      T_COMMIT(); \
+    } \
+  } while (0)
+
+#define T23(t,t0,x0,t1,x1,t2,x2,t3,x3,t4,x4,t5,x5,t6,x6,t7,x7,t8,x8,t9,x9,t10,x10) \
+  do { \
+    if (T_ACTIVE(t)) { \
+      T_LOCAL_DATA \
+      T_HEADER(t); \
+      T_PUT_##t0(2, x0); \
+      T_PUT_##t1(3, x1); \
+      T_PUT_##t2(4, x2); \
+      T_PUT_##t3(5, x3); \
+      T_PUT_##t4(6, x4); \
+      T_PUT_##t5(7, x5); \
+      T_PUT_##t6(8, x6); \
+      T_PUT_##t7(9, x7); \
+      T_PUT_##t8(10, x8); \
+      T_PUT_##t9(11, x9); \
+      T_PUT_##t10(12, x10); \
+      T_COMMIT(); \
+    } \
+  } while (0)
+
+#define T25(t,t0,x0,t1,x1,t2,x2,t3,x3,t4,x4,t5,x5,t6,x6,t7,x7,t8,x8,t9,x9,t10,x10,t11,x11) \
+  do { \
+    if (T_ACTIVE(t)) { \
+      T_LOCAL_DATA \
+      T_HEADER(t); \
+      T_PUT_##t0(2, x0); \
+      T_PUT_##t1(3, x1); \
+      T_PUT_##t2(4, x2); \
+      T_PUT_##t3(5, x3); \
+      T_PUT_##t4(6, x4); \
+      T_PUT_##t5(7, x5); \
+      T_PUT_##t6(8, x6); \
+      T_PUT_##t7(9, x7); \
+      T_PUT_##t8(10, x8); \
+      T_PUT_##t9(11, x9); \
+      T_PUT_##t10(12, x10); \
+      T_PUT_##t11(13, x11); \
+      T_COMMIT(); \
+    } \
+  } while (0)
+
+#define T27(t,t0,x0,t1,x1,t2,x2,t3,x3,t4,x4,t5,x5,t6,x6,t7,x7,t8,x8,t9,x9,t10,x10,t11,x11,t12,x12) \
+  do { \
+    if (T_ACTIVE(t)) { \
+      T_LOCAL_DATA \
+      T_HEADER(t); \
+      T_PUT_##t0(2, x0); \
+      T_PUT_##t1(3, x1); \
+      T_PUT_##t2(4, x2); \
+      T_PUT_##t3(5, x3); \
+      T_PUT_##t4(6, x4); \
+      T_PUT_##t5(7, x5); \
+      T_PUT_##t6(8, x6); \
+      T_PUT_##t7(9, x7); \
+      T_PUT_##t8(10, x8); \
+      T_PUT_##t9(11, x9); \
+      T_PUT_##t10(12, x10); \
+      T_PUT_##t11(13, x11); \
+      T_PUT_##t12(14, x12); \
+      T_COMMIT(); \
+    } \
+  } while (0)
+
+#define T29(t,t0,x0,t1,x1,t2,x2,t3,x3,t4,x4,t5,x5,t6,x6,t7,x7,t8,x8,t9,x9,t10,x10,t11,x11,t12,x12,t13,x13) \
+  do { \
+    if (T_ACTIVE(t)) { \
+      T_LOCAL_DATA \
+      T_HEADER(t); \
+      T_PUT_##t0(2, x0); \
+      T_PUT_##t1(3, x1); \
+      T_PUT_##t2(4, x2); \
+      T_PUT_##t3(5, x3); \
+      T_PUT_##t4(6, x4); \
+      T_PUT_##t5(7, x5); \
+      T_PUT_##t6(8, x6); \
+      T_PUT_##t7(9, x7); \
+      T_PUT_##t8(10, x8); \
+      T_PUT_##t9(11, x9); \
+      T_PUT_##t10(12, x10); \
+      T_PUT_##t11(13, x11); \
+      T_PUT_##t12(14, x12); \
+      T_PUT_##t13(15, x13); \
+      T_COMMIT(); \
+    } \
+  } while (0)
+
+#define T31(t,t0,x0,t1,x1,t2,x2,t3,x3,t4,x4,t5,x5,t6,x6,t7,x7,t8,x8,t9,x9,t10,x10,t11,x11,t12,x12,t13,x13,t14,x14) \
+  do { \
+    if (T_ACTIVE(t)) { \
+      T_LOCAL_DATA \
+      T_HEADER(t); \
+      T_PUT_##t0(2, x0); \
+      T_PUT_##t1(3, x1); \
+      T_PUT_##t2(4, x2); \
+      T_PUT_##t3(5, x3); \
+      T_PUT_##t4(6, x4); \
+      T_PUT_##t5(7, x5); \
+      T_PUT_##t6(8, x6); \
+      T_PUT_##t7(9, x7); \
+      T_PUT_##t8(10, x8); \
+      T_PUT_##t9(11, x9); \
+      T_PUT_##t10(12, x10); \
+      T_PUT_##t11(13, x11); \
+      T_PUT_##t12(14, x12); \
+      T_PUT_##t13(15, x13); \
+      T_PUT_##t14(16, x14); \
+      T_COMMIT(); \
+    } \
+  } while (0)
+
+#define T33(t,t0,x0,t1,x1,t2,x2,t3,x3,t4,x4,t5,x5,t6,x6,t7,x7,t8,x8,t9,x9,t10,x10,t11,x11,t12,x12,t13,x13,t14,x14,t15,x15) \
+  do { \
+    if (T_ACTIVE(t)) { \
+      T_LOCAL_DATA \
+      T_HEADER(t); \
+      T_PUT_##t0(2, x0); \
+      T_PUT_##t1(3, x1); \
+      T_PUT_##t2(4, x2); \
+      T_PUT_##t3(5, x3); \
+      T_PUT_##t4(6, x4); \
+      T_PUT_##t5(7, x5); \
+      T_PUT_##t6(8, x6); \
+      T_PUT_##t7(9, x7); \
+      T_PUT_##t8(10, x8); \
+      T_PUT_##t9(11, x9); \
+      T_PUT_##t10(12, x10); \
+      T_PUT_##t11(13, x11); \
+      T_PUT_##t12(14, x12); \
+      T_PUT_##t13(15, x13); \
+      T_PUT_##t14(16, x14); \
+      T_PUT_##t15(17, x15); \
+      T_COMMIT(); \
+    } \
+  } while (0)
+
+#define T_CALL_ERROR \
+  do { \
+    printf("%s:%d:%s: error calling T, you have to use T_INT() or T_XX()\n", \
+           __FILE__, __LINE__, __FUNCTION__); \
+  } while (0)
+
+#define T2(...) T_CALL_ERROR
+#define T4(...) T_CALL_ERROR
+#define T6(...) T_CALL_ERROR
+#define T8(...) T_CALL_ERROR
+#define T10(...) T_CALL_ERROR
+#define T12(...) T_CALL_ERROR
+#define T14(...) T_CALL_ERROR
+#define T16(...) T_CALL_ERROR
+#define T18(...) T_CALL_ERROR
+#define T20(...) T_CALL_ERROR
+#define T22(...) T_CALL_ERROR
+#define T24(...) T_CALL_ERROR
+#define T26(...) T_CALL_ERROR
+#define T28(...) T_CALL_ERROR
+#define T30(...) T_CALL_ERROR
+#define T32(...) T_CALL_ERROR
+
+/* special cases for VCD logs */
+
+#define T_VCD_VARIABLE(var, val) \
+  do { \
+    if (T_ACTIVE(((var) + VCD_FIRST_VARIABLE))) { \
+      if ((var) > VCD_NUM_VARIABLES) { \
+        printf("%s:%d:%s: VCD data out of synch for the T, contact" \
+               " the authors!\n", __FILE__, __LINE__, __FUNCTION__); \
+        abort(); \
+      } \
+      T_LOCAL_DATA \
+      T_HEADER(T_ID((var) + VCD_FIRST_VARIABLE)); \
+      T_PUT_ulong(1, (val)); \
+      T_COMMIT(); \
+    } \
+  } while (0)
+
+#define T_VCD_FUNCTION(fun, val) \
+  do { \
+    if (T_ACTIVE(((fun) + VCD_FIRST_FUNCTION))) { \
+      if ((fun) > VCD_NUM_FUNCTIONS) { \
+        printf("%s:%d:%s: VCD data out of synch for the T, contact" \
+               " the authors!\n", __FILE__, __LINE__, __FUNCTION__); \
+        abort(); \
+      } \
+      T_LOCAL_DATA \
+      T_HEADER(T_ID((fun) + VCD_FIRST_FUNCTION)); \
+      T_PUT_int(1, (val)); \
+      T_COMMIT(); \
+    } \
+  } while (0)
+
+extern int *T_active;
+
+void T_init(int remote_port, int wait_for_tracer);
+
+#else /* T_TRACER */
+
+/* if T_TRACER is not defined or is 0, the T is deactivated */
+#define T(...) /**/
+
+#endif /* T_TRACER */
+
+#endif /* _T_T_T_ */
diff --git a/common/utils/T/T_defs.h b/common/utils/T/T_defs.h
new file mode 100644
index 0000000000000000000000000000000000000000..b4fd47c0a5134f64257ef781f6a0daf9182182e9
--- /dev/null
+++ b/common/utils/T/T_defs.h
@@ -0,0 +1,43 @@
+#ifndef _T_defs_H_
+#define _T_defs_H_
+
+/* comment (and recompile everything) to not send time in events */
+#define T_SEND_TIME
+
+/* maximum number of arguments for the T macro */
+#define T_MAX_ARGS 16
+
+/* maximum size of a message - increase if needed */
+#define T_BUFFER_MAX (1024*64)
+
+/* size of the local cache for messages (must be pow(2,something)) */
+#define T_CACHE_SIZE (8192 * 2)
+
+/* maximum number of bytes a message can contain */
+#ifdef T_SEND_TIME
+#  define T_PAYLOAD_MAXSIZE (T_BUFFER_MAX-sizeof(int)-sizeof(struct timespec))
+#else
+#  define T_PAYLOAD_MAXSIZE (T_BUFFER_MAX-sizeof(int))
+#endif
+
+typedef struct {
+  volatile int busy;
+  char buffer[T_BUFFER_MAX];
+  int length;
+} T_cache_t;
+
+#define T_SHM_FILENAME "/T_shm_segment"
+
+/* number of VCD functions (to be kept up to date! see in T_messages.txt) */
+#define VCD_NUM_FUNCTIONS 139
+
+/* number of VCD variables (to be kept up to date! see in T_messages.txt) */
+#define VCD_NUM_VARIABLES 45
+
+/* first VCD function (to be kept up to date! see in T_messages.txt) */
+#define VCD_FIRST_FUNCTION    ((uintptr_t)T_VCD_FUNCTION_RT_SLEEP)
+
+/* first VCD variable (to be kept up to date! see in T_messages.txt) */
+#define VCD_FIRST_VARIABLE    ((uintptr_t)T_VCD_VARIABLE_FRAME_NUMBER_TX_ENB)
+
+#endif /* _T_defs_H_ */
diff --git a/common/utils/T/T_messages.txt b/common/utils/T/T_messages.txt
new file mode 100644
index 0000000000000000000000000000000000000000..ab3928f419c83a61bba3ff96059ae19291544fae
--- /dev/null
+++ b/common/utils/T/T_messages.txt
@@ -0,0 +1,1527 @@
+#general logs
+ID = ENB_MASTER_TICK
+    DESC = eNodeB master tick - one tick per ms, to be used as "reference clock", mostly for ticktime view
+    GROUP = ALL:GENERAL:ENB
+    FORMAT = int,eNB_ID : int,frame : int,subframe
+
+#PHY logs
+ID = ENB_UL_TICK
+    DESC = eNodeB uplink tick - one tick per ms at start of uplink processing
+    GROUP = ALL:PHY:GRAPHIC:ENB
+    FORMAT = int,eNB_ID : int,frame : int,subframe
+ID = ENB_DL_TICK
+    DESC = eNodeB downlink tick - one tick per ms at start of downlink processing
+    GROUP = ALL:PHY:GRAPHIC:ENB
+    FORMAT = int,eNB_ID : int,frame : int,subframe
+ID = ENB_DLSCH_UE_DCI
+    DESC = eNodeB downlink UE specific DCI as sent by the PHY layer
+    GROUP = ALL:PHY:GRAPHIC:ENB
+    FORMAT = int,eNB_ID : int,frame : int,subframe : int,UE_id : int,rnti : int,dci_format : int,harq_pid
+ID = ENB_DLSCH_UE_ACK
+    DESC = eNodeB downlink UE ACK as seen by the PHY layer in process_HARQ_feedback
+    GROUP = ALL:PHY:GRAPHIC:ENB
+    FORMAT = int,eNB_ID : int,frame : int,subframe : int,UE_id : int,rnti : int,harq_pid
+ID = ENB_DLSCH_UE_NACK
+    DESC = eNodeB downlink UE NACK as seen by the PHY layer in process_HARQ_feedback
+    GROUP = ALL:PHY:GRAPHIC:ENB
+    FORMAT = int,eNB_ID : int,frame : int,subframe : int,UE_id : int,rnti : int,harq_pid
+ID = ENB_ULSCH_UE_DCI
+    DESC = eNodeB uplink UE specific DCI as sent by the PHY layer
+    GROUP = ALL:PHY:GRAPHIC:ENB
+    FORMAT = int,eNB_ID : int,frame : int,subframe : int,UE_id : int,rnti : int,harq_pid
+ID = ENB_ULSCH_UE_NO_DCI_RETRANSMISSION
+    DESC = eNodeB uplink UE retransmission due to PHICH NACK (see generate_phich_top)
+    GROUP = ALL:PHY:GRAPHIC:ENB
+    FORMAT = int,eNB_ID : int,frame : int,subframe : int,UE_id : int,rnti : int,harq_pid
+ID = ENB_ULSCH_UE_ACK
+    DESC = eNodeB uplink UE ACK as seen by the PHY layer
+    GROUP = ALL:PHY:GRAPHIC:ENB
+    FORMAT = int,eNB_ID : int,frame : int,subframe : int,UE_id : int,rnti : int,harq_pid
+ID = ENB_ULSCH_UE_NACK
+    DESC = eNodeB uplink UE NACK as seen by the PHY layer
+    GROUP = ALL:PHY:GRAPHIC:ENB
+    FORMAT = int,eNB_ID : int,frame : int,subframe : int,UE_id : int,rnti : int,harq_pid
+ID = ENB_INPUT_SIGNAL
+    DESC = eNodeB received signal in the time domain for a duration of 1ms
+    GROUP = ALL:PHY:GRAPHIC:HEAVY:ENB
+    FORMAT = int,eNB_ID : int,frame : int,subframe : int,antenna : buffer,rxdata
+ID = ENB_UL_CHANNEL_ESTIMATE
+    DESC = eNodeB channel estimation in the time domain
+    GROUP = ALL:PHY:GRAPHIC:HEAVY:ENB
+    FORMAT = int,eNB_ID : int,UE_ID : int,frame : int,subframe : int,antenna : buffer,chest_t
+ID = PUSCH_IQ
+    DESC = eNodeB PUSCH received IQ data
+    GROUP = ALL:PHY:GRAPHIC:HEAVY:ENB
+    FORMAT = int,eNB_ID : int,UE_ID : int,frame : int,subframe : int,nb_rb : buffer,pusch_comp
+ID = PUCCH_1AB_IQ
+    DESC = eNodeB PUCCH received IQ data
+    GROUP = ALL:PHY:GRAPHIC:HEAVY:ENB
+    FORMAT = int,eNB_ID : int,UE_ID : int,frame : int,subframe : int,I : int,Q
+ID = PUCCH_1_ENERGY
+    DESC = eNodeB PUCCH 1 energy and threshold
+    GROUP = ALL:PHY:GRAPHIC:HEAVY:ENB
+    FORMAT = int,eNB_ID : int,UE_ID : int,frame : int,subframe : int,energy : int,threshold
+
+#MAC logs
+ID = ENB_MAC_UE_DL_SDU
+    DESC = MAC downlink SDU for an UE coming from RLC to MAC
+    GROUP = ALL:MAC:ENB
+    FORMAT = int,eNB_ID : int,CC_id : int,rnti : int,frame : int,subframe : int,harq_pid : int,lcid : int,length
+ID = ENB_MAC_UE_UL_SCHEDULE
+    DESC = MAC uplink UE scheduling decision
+    GROUP = ALL:MAC:ENB
+    FORMAT = int,eNB_ID : int,CC_id : int,rnti : int,frame : int,subframe : int,harq_pid : int,mcs : int,first_rb : int,nb_rb : int,TBS
+ID = ENB_MAC_UE_UL_SCHEDULE_RETRANSMISSION
+    DESC = MAC uplink UE scheduling retransmission decision
+    GROUP = ALL:MAC:ENB
+    FORMAT = int,eNB_ID : int,CC_id : int,rnti : int,frame : int,subframe : int,harq_pid : int,mcs : int,first_rb : int,nb_rb : int,round
+ID = ENB_MAC_UE_UL_PDU
+    DESC = MAC uplink UE received PDU
+    GROUP = ALL:MAC:ENB
+    FORMAT = int,eNB_ID : int,CC_id : int,rnti : int,frame : int,subframe : int,harq_pid : int,sdu_length : int,num_ce : int,num_sdu
+ID = ENB_MAC_UE_UL_SDU
+    DESC = MAC uplink UE received SDU
+    GROUP = ALL:MAC:ENB
+    FORMAT = int,eNB_ID : int,CC_id : int,rnti : int,frame : int,subframe : int,lcid : int,length
+ID = ENB_MAC_UE_UL_CE
+    DESC = MAC uplink UE received control element
+    GROUP = ALL:MAC:ENB
+    FORMAT = int,eNB_ID : int,CC_id : int,rnti : int,frame : int,subframe : int,ce
+
+#RLC logs
+ID = ENB_RLC_DL
+    DESC = RLC downlink data
+    GROUP = ALL:RLC:ENB
+    FORMAT = int,eNB_ID : int,rnti : int,rb_id : int,length
+ID = ENB_RLC_UL
+    DESC = RLC uplink data
+    GROUP = ALL:RLC:ENB
+    FORMAT = int,eNB_ID : int,rnti : int,rb_id : int,length
+ID = ENB_RLC_MAC_DL
+    DESC = RLC downlink data
+    GROUP = ALL:RLC:ENB
+    FORMAT = int,eNB_ID : int,rnti : int,rb_id : int,length
+ID = ENB_RLC_MAC_UL
+    DESC = RLC uplink data
+    GROUP = ALL:RLC:ENB
+    FORMAT = int,eNB_ID : int,rnti : int,rb_id : int,length
+
+#PDCP logs
+ID = ENB_PDCP_UL
+    DESC = PDCP uplink data
+    GROUP = ALL:PDCP:ENB
+    FORMAT = int,eNB_ID : int,rnti : int,rb_id : int,length
+ID = ENB_PDCP_DL
+    DESC = PDCP uplink data
+    GROUP = ALL:PDCP:ENB
+    FORMAT = int,eNB_ID : int,rnti : int,rb_id : int,length
+
+#RRC logs
+ID = ENB_RRC_CONNECTION_SETUP_COMPLETE
+    DESC = RRC connection setup complete
+    GROUP = ALL:RRC:ENB
+    FORMAT = int,eNB_ID : int,frame : int,subframe : int,rnti
+ID = ENB_RRC_SECURITY_MODE_COMMAND
+    DESC = RRC security mode command
+    GROUP = ALL:RRC:ENB
+    FORMAT = int,eNB_ID : int,frame : int,subframe : int,rnti
+ID = ENB_RRC_SECURITY_MODE_COMPLETE
+    DESC = RRC security mode complete
+    GROUP = ALL:RRC:ENB
+    FORMAT = int,eNB_ID : int,frame : int,subframe : int,rnti
+ID = ENB_RRC_SECURITY_MODE_FAILURE
+    DESC = RRC security mode failure
+    GROUP = ALL:RRC:ENB
+    FORMAT = int,eNB_ID : int,frame : int,subframe : int,rnti
+ID = ENB_RRC_UE_CAPABILITY_ENQUIRY
+    DESC = RRC UE capability enquiry
+    GROUP = ALL:RRC:ENB
+    FORMAT = int,eNB_ID : int,frame : int,subframe : int,rnti
+ID = ENB_RRC_UE_CAPABILITY_INFORMATION
+    DESC = RRC UE capability information
+    GROUP = ALL:RRC:ENB
+    FORMAT = int,eNB_ID : int,frame : int,subframe : int,rnti
+ID = ENB_RRC_CONNECTION_REQUEST
+    DESC = RRC connection request
+    GROUP = ALL:RRC:ENB
+    FORMAT = int,eNB_ID : int,frame : int,subframe : int,rnti
+ID = ENB_RRC_CONNECTION_REJECT
+    DESC = RRC connection reject
+    GROUP = ALL:RRC:ENB
+    FORMAT = int,eNB_ID : int,frame : int,subframe : int,rnti
+ID = ENB_RRC_CONNECTION_REESTABLISHMENT_REQUEST
+    DESC = RRC connection reestablishment request
+    GROUP = ALL:RRC:ENB
+    FORMAT = int,eNB_ID : int,frame : int,subframe : int,rnti
+ID = ENB_RRC_CONNECTION_REESTABLISHMENT_COMPLETE
+    DESC = RRC connection reestablishment complete
+    GROUP = ALL:RRC:ENB
+    FORMAT = int,eNB_ID : int,frame : int,subframe : int,rnti
+ID = ENB_RRC_CONNECTION_REESTABLISHMENT_REJECT
+    DESC = RRC connection reestablishment reject
+    GROUP = ALL:RRC:ENB
+    FORMAT = int,eNB_ID : int,frame : int,subframe : int,rnti
+ID = ENB_RRC_CONNECTION_RELEASE
+    DESC = RRC connection release
+    GROUP = ALL:RRC:ENB
+    FORMAT = int,eNB_ID : int,frame : int,subframe : int,rnti
+ID = ENB_RRC_CONNECTION_RECONFIGURATION
+    DESC = RRC connection reconfiguration
+    GROUP = ALL:RRC:ENB
+    FORMAT = int,eNB_ID : int,frame : int,subframe : int,rnti
+ID = ENB_RRC_MEASUREMENT_REPORT
+    DESC = RRC measurement report
+    GROUP = ALL:RRC:ENB
+    FORMAT = int,eNB_ID : int,frame : int,subframe : int,rnti
+ID = ENB_RRC_HANDOVER_PREPARATION_INFORMATION
+    DESC = RRC handover preparation information
+    GROUP = ALL:RRC:ENB
+    FORMAT = int,eNB_ID : int,frame : int,subframe : int,rnti
+ID = ENB_RRC_CONNECTION_RECONFIGURATION_COMPLETE
+    DESC = RRC connection reconfiguration complete
+    GROUP = ALL:RRC:ENB
+    FORMAT = int,eNB_ID : int,frame : int,subframe : int,rnti
+ID = ENB_RRC_CONNECTION_SETUP
+    DESC = RRC connection setup
+    GROUP = ALL:RRC:ENB
+    FORMAT = int,eNB_ID : int,frame : int,subframe : int,rnti
+ID = ENB_RRC_UL_CCCH_DATA_IN
+    DESC = RRC uplink CCCH data in
+    GROUP = ALL:RRC:ENB
+    FORMAT = int,eNB_ID : int,frame : int,subframe : int,rnti
+ID = ENB_RRC_UL_DCCH_DATA_IN
+    DESC = RRC uplink DCCH data in
+    GROUP = ALL:RRC:ENB
+    FORMAT = int,eNB_ID : int,frame : int,subframe : int,rnti
+ID = ENB_RRC_UL_HANDOVER_PREPARATION_TRANSFER
+    DESC = RRC uplink handover preparation transfer
+    GROUP = ALL:RRC:ENB
+    FORMAT = int,eNB_ID : int,frame : int,subframe : int,rnti
+ID = ENB_RRC_UL_INFORMATION_TRANSFER
+    DESC = RRC uplink information transfer
+    GROUP = ALL:RRC:ENB
+    FORMAT = int,eNB_ID : int,frame : int,subframe : int,rnti
+ID = ENB_RRC_COUNTER_CHECK_RESPONSE
+    DESC = RRC counter check response
+    GROUP = ALL:RRC:ENB
+    FORMAT = int,eNB_ID : int,frame : int,subframe : int,rnti
+ID = ENB_RRC_UE_INFORMATION_RESPONSE_R9
+    DESC = RRC UE information response r9
+    GROUP = ALL:RRC:ENB
+    FORMAT = int,eNB_ID : int,frame : int,subframe : int,rnti
+ID = ENB_RRC_PROXIMITY_INDICATION_R9
+    DESC = RRC proximity indication r9
+    GROUP = ALL:RRC:ENB
+    FORMAT = int,eNB_ID : int,frame : int,subframe : int,rnti
+ID = ENB_RRC_RECONFIGURATION_COMPLETE_R10
+    DESC = RRC reconfiguration complete r10
+    GROUP = ALL:RRC:ENB
+    FORMAT = int,eNB_ID : int,frame : int,subframe : int,rnti
+ID = ENB_RRC_MBMS_COUNTING_RESPONSE_R10
+    DESC = RRC MBMS counting response r10
+    GROUP = ALL:RRC:ENB
+    FORMAT = int,eNB_ID : int,frame : int,subframe : int,rnti
+ID = ENB_RRC_INTER_FREQ_RSTD_MEASUREMENT_INDICATION
+    DESC = RRC inter frequency RSTD measurement indication
+    GROUP = ALL:RRC:ENB
+    FORMAT = int,eNB_ID : int,frame : int,subframe : int,rnti
+ID = ENB_RRC_UNKNOW_MESSAGE
+    DESC = RRC unknown message
+    GROUP = ALL:RRC:ENB
+    FORMAT = int,eNB_ID : int,frame : int,subframe : int,rnti
+
+#legacy logs
+ID = LEGACY_MAC_INFO
+    DESC = MAC legacy logs - info level
+    GROUP = ALL:LEGACY_MAC:INFO:LEGACY
+    FORMAT = string,log
+ID = LEGACY_MAC_ERROR
+    DESC = MAC legacy logs - error level
+    GROUP = ALL:LEGACY_MAC:ERROR:LEGACY
+    FORMAT = string,log
+ID = LEGACY_MAC_WARNING
+    DESC = MAC legacy logs - warning level
+    GROUP = ALL:LEGACY_MAC:WARNING:LEGACY
+    FORMAT = string,log
+ID = LEGACY_MAC_DEBUG
+    DESC = MAC legacy logs - debug level
+    GROUP = ALL:LEGACY_MAC:DEBUG:LEGACY
+    FORMAT = string,log
+ID = LEGACY_MAC_TRACE
+    DESC = MAC legacy logs - trace level
+    GROUP = ALL:LEGACY_MAC:TRACE:LEGACY
+    FORMAT = string,log
+
+ID = LEGACY_PHY_INFO
+    DESC = PHY legacy logs - info level
+    GROUP = ALL:LEGACY_PHY:INFO:LEGACY
+    FORMAT = string,log
+ID = LEGACY_PHY_ERROR
+    DESC = PHY legacy logs - error level
+    GROUP = ALL:LEGACY_PHY:ERROR:LEGACY
+    FORMAT = string,log
+ID = LEGACY_PHY_WARNING
+    DESC = PHY legacy logs - warning level
+    GROUP = ALL:LEGACY_PHY:WARNING:LEGACY
+    FORMAT = string,log
+ID = LEGACY_PHY_DEBUG
+    DESC = PHY legacy logs - debug level
+    GROUP = ALL:LEGACY_PHY:DEBUG:LEGACY
+    FORMAT = string,log
+ID = LEGACY_PHY_TRACE
+    DESC = PHY legacy logs - trace level
+    GROUP = ALL:LEGACY_PHY:TRACE:LEGACY
+    FORMAT = string,log
+
+ID = LEGACY_S1AP_INFO
+    DESC = S1AP legacy logs - info level
+    GROUP = ALL:LEGACY_S1AP:INFO:LEGACY
+    FORMAT = string,log
+ID = LEGACY_S1AP_ERROR
+    DESC = S1AP legacy logs - error level
+    GROUP = ALL:LEGACY_S1AP:ERROR:LEGACY
+    FORMAT = string,log
+ID = LEGACY_S1AP_WARNING
+    DESC = S1AP legacy logs - warning level
+    GROUP = ALL:LEGACY_S1AP:WARNING:LEGACY
+    FORMAT = string,log
+ID = LEGACY_S1AP_DEBUG
+    DESC = S1AP legacy logs - debug level
+    GROUP = ALL:LEGACY_S1AP:DEBUG:LEGACY
+    FORMAT = string,log
+ID = LEGACY_S1AP_TRACE
+    DESC = S1AP legacy logs - trace level
+    GROUP = ALL:LEGACY_S1AP:TRACE:LEGACY
+    FORMAT = string,log
+
+ID = LEGACY_X2AP_INFO
+    DESC = X2AP legacy logs - info level
+    GROUP = ALL:LEGACY_X2AP:INFO:LEGACY
+    FORMAT = string,log
+ID = LEGACY_X2AP_ERROR
+    DESC = X2AP legacy logs - error level
+    GROUP = ALL:LEGACY_X2AP:ERROR:LEGACY
+    FORMAT = string,log
+ID = LEGACY_X2AP_WARNING
+    DESC = X2AP legacy logs - warning level
+    GROUP = ALL:LEGACY_X2AP:WARNING:LEGACY
+    FORMAT = string,log
+ID = LEGACY_X2AP_DEBUG
+    DESC = X2AP legacy logs - debug level
+    GROUP = ALL:LEGACY_X2AP:DEBUG:LEGACY
+    FORMAT = string,log
+ID = LEGACY_X2AP_TRACE
+    DESC = X2AP legacy logs - trace level
+    GROUP = ALL:LEGACY_X2AP:TRACE:LEGACY
+    FORMAT = string,log
+
+ID = LEGACY_RRC_INFO
+    DESC = RRC legacy logs - info level
+    GROUP = ALL:LEGACY_RRC:INFO:LEGACY
+    FORMAT = string,log
+ID = LEGACY_RRC_ERROR
+    DESC = RRC legacy logs - error level
+    GROUP = ALL:LEGACY_RRC:ERROR:LEGACY
+    FORMAT = string,log
+ID = LEGACY_RRC_WARNING
+    DESC = RRC legacy logs - warning level
+    GROUP = ALL:LEGACY_RRC:WARNING:LEGACY
+    FORMAT = string,log
+ID = LEGACY_RRC_DEBUG
+    DESC = RRC legacy logs - debug level
+    GROUP = ALL:LEGACY_RRC:DEBUG:LEGACY
+    FORMAT = string,log
+ID = LEGACY_RRC_TRACE
+    DESC = RRC legacy logs - trace level
+    GROUP = ALL:LEGACY_RRC:TRACE:LEGACY
+    FORMAT = string,log
+
+ID = LEGACY_RLC_INFO
+    DESC = RLC legacy logs - info level
+    GROUP = ALL:LEGACY_RLC:INFO:LEGACY
+    FORMAT = string,log
+ID = LEGACY_RLC_ERROR
+    DESC = RLC legacy logs - error level
+    GROUP = ALL:LEGACY_RLC:ERROR:LEGACY
+    FORMAT = string,log
+ID = LEGACY_RLC_WARNING
+    DESC = RLC legacy logs - warning level
+    GROUP = ALL:LEGACY_RLC:WARNING:LEGACY
+    FORMAT = string,log
+ID = LEGACY_RLC_DEBUG
+    DESC = RLC legacy logs - debug level
+    GROUP = ALL:LEGACY_RLC:DEBUG:LEGACY
+    FORMAT = string,log
+ID = LEGACY_RLC_TRACE
+    DESC = RLC legacy logs - trace level
+    GROUP = ALL:LEGACY_RLC:TRACE:LEGACY
+    FORMAT = string,log
+
+ID = LEGACY_PDCP_INFO
+    DESC = PDCP legacy logs - info level
+    GROUP = ALL:LEGACY_PDCP:INFO:LEGACY
+    FORMAT = string,log
+ID = LEGACY_PDCP_ERROR
+    DESC = PDCP legacy logs - error level
+    GROUP = ALL:LEGACY_PDCP:ERROR:LEGACY
+    FORMAT = string,log
+ID = LEGACY_PDCP_WARNING
+    DESC = PDCP legacy logs - warning level
+    GROUP = ALL:LEGACY_PDCP:WARNING:LEGACY
+    FORMAT = string,log
+ID = LEGACY_PDCP_DEBUG
+    DESC = PDCP legacy logs - debug level
+    GROUP = ALL:LEGACY_PDCP:DEBUG:LEGACY
+    FORMAT = string,log
+ID = LEGACY_PDCP_TRACE
+    DESC = PDCP legacy logs - trace level
+    GROUP = ALL:LEGACY_PDCP:TRACE:LEGACY
+    FORMAT = string,log
+
+ID = LEGACY_ENB_APP_INFO
+    DESC = ENB_APP legacy logs - info level
+    GROUP = ALL:LEGACY_ENB_APP:INFO:LEGACY
+    FORMAT = string,log
+ID = LEGACY_ENB_APP_ERROR
+    DESC = ENB_APP legacy logs - error level
+    GROUP = ALL:LEGACY_ENB_APP:ERROR:LEGACY
+    FORMAT = string,log
+ID = LEGACY_ENB_APP_WARNING
+    DESC = ENB_APP legacy logs - warning level
+    GROUP = ALL:LEGACY_ENB_APP:WARNING:LEGACY
+    FORMAT = string,log
+ID = LEGACY_ENB_APP_DEBUG
+    DESC = ENB_APP legacy logs - debug level
+    GROUP = ALL:LEGACY_ENB_APP:DEBUG:LEGACY
+    FORMAT = string,log
+ID = LEGACY_ENB_APP_TRACE
+    DESC = ENB_APP legacy logs - trace level
+    GROUP = ALL:LEGACY_ENB_APP:TRACE:LEGACY
+    FORMAT = string,log
+
+ID = LEGACY_SCTP_INFO
+    DESC = SCTP legacy logs - info level
+    GROUP = ALL:LEGACY_SCTP:INFO:LEGACY
+    FORMAT = string,log
+ID = LEGACY_SCTP_ERROR
+    DESC = SCTP legacy logs - error level
+    GROUP = ALL:LEGACY_SCTP:ERROR:LEGACY
+    FORMAT = string,log
+ID = LEGACY_SCTP_WARNING
+    DESC = SCTP legacy logs - warning level
+    GROUP = ALL:LEGACY_SCTP:WARNING:LEGACY
+    FORMAT = string,log
+ID = LEGACY_SCTP_DEBUG
+    DESC = SCTP legacy logs - debug level
+    GROUP = ALL:LEGACY_SCTP:DEBUG:LEGACY
+    FORMAT = string,log
+ID = LEGACY_SCTP_TRACE
+    DESC = SCTP legacy logs - trace level
+    GROUP = ALL:LEGACY_SCTP:TRACE:LEGACY
+    FORMAT = string,log
+
+ID = LEGACY_UDP__INFO
+    DESC = UDP_ legacy logs - info level
+    GROUP = ALL:LEGACY_UDP_:INFO:LEGACY
+    FORMAT = string,log
+ID = LEGACY_UDP__ERROR
+    DESC = UDP_ legacy logs - error level
+    GROUP = ALL:LEGACY_UDP_:ERROR:LEGACY
+    FORMAT = string,log
+ID = LEGACY_UDP__WARNING
+    DESC = UDP_ legacy logs - warning level
+    GROUP = ALL:LEGACY_UDP_:WARNING:LEGACY
+    FORMAT = string,log
+ID = LEGACY_UDP__DEBUG
+    DESC = UDP_ legacy logs - debug level
+    GROUP = ALL:LEGACY_UDP_:DEBUG:LEGACY
+    FORMAT = string,log
+ID = LEGACY_UDP__TRACE
+    DESC = UDP_ legacy logs - trace level
+    GROUP = ALL:LEGACY_UDP_:TRACE:LEGACY
+    FORMAT = string,log
+
+ID = LEGACY_NAS_INFO
+    DESC = NAS legacy logs - info level
+    GROUP = ALL:LEGACY_NAS:INFO:LEGACY
+    FORMAT = string,log
+ID = LEGACY_NAS_ERROR
+    DESC = NAS legacy logs - error level
+    GROUP = ALL:LEGACY_NAS:ERROR:LEGACY
+    FORMAT = string,log
+ID = LEGACY_NAS_WARNING
+    DESC = NAS legacy logs - warning level
+    GROUP = ALL:LEGACY_NAS:WARNING:LEGACY
+    FORMAT = string,log
+ID = LEGACY_NAS_DEBUG
+    DESC = NAS legacy logs - debug level
+    GROUP = ALL:LEGACY_NAS:DEBUG:LEGACY
+    FORMAT = string,log
+ID = LEGACY_NAS_TRACE
+    DESC = NAS legacy logs - trace level
+    GROUP = ALL:LEGACY_NAS:TRACE:LEGACY
+    FORMAT = string,log
+
+ID = LEGACY_HW_INFO
+    DESC = HW legacy logs - info level
+    GROUP = ALL:LEGACY_HW:INFO:LEGACY
+    FORMAT = string,log
+ID = LEGACY_HW_ERROR
+    DESC = HW legacy logs - error level
+    GROUP = ALL:LEGACY_HW:ERROR:LEGACY
+    FORMAT = string,log
+ID = LEGACY_HW_WARNING
+    DESC = HW legacy logs - warning level
+    GROUP = ALL:LEGACY_HW:WARNING:LEGACY
+    FORMAT = string,log
+ID = LEGACY_HW_DEBUG
+    DESC = HW legacy logs - debug level
+    GROUP = ALL:LEGACY_HW:DEBUG:LEGACY
+    FORMAT = string,log
+ID = LEGACY_HW_TRACE
+    DESC = HW legacy logs - trace level
+    GROUP = ALL:LEGACY_HW:TRACE:LEGACY
+    FORMAT = string,log
+
+ID = LEGACY_EMU_INFO
+    DESC = EMU legacy logs - info level
+    GROUP = ALL:LEGACY_EMU:INFO:LEGACY
+    FORMAT = string,log
+ID = LEGACY_EMU_ERROR
+    DESC = EMU legacy logs - error level
+    GROUP = ALL:LEGACY_EMU:ERROR:LEGACY
+    FORMAT = string,log
+ID = LEGACY_EMU_WARNING
+    DESC = EMU legacy logs - warning level
+    GROUP = ALL:LEGACY_EMU:WARNING:LEGACY
+    FORMAT = string,log
+ID = LEGACY_EMU_DEBUG
+    DESC = EMU legacy logs - debug level
+    GROUP = ALL:LEGACY_EMU:DEBUG:LEGACY
+    FORMAT = string,log
+ID = LEGACY_EMU_TRACE
+    DESC = EMU legacy logs - trace level
+    GROUP = ALL:LEGACY_EMU:TRACE:LEGACY
+    FORMAT = string,log
+
+ID = LEGACY_OTG_INFO
+    DESC = OTG legacy logs - info level
+    GROUP = ALL:LEGACY_OTG:INFO:LEGACY
+    FORMAT = string,log
+ID = LEGACY_OTG_ERROR
+    DESC = OTG legacy logs - error level
+    GROUP = ALL:LEGACY_OTG:ERROR:LEGACY
+    FORMAT = string,log
+ID = LEGACY_OTG_WARNING
+    DESC = OTG legacy logs - warning level
+    GROUP = ALL:LEGACY_OTG:WARNING:LEGACY
+    FORMAT = string,log
+ID = LEGACY_OTG_DEBUG
+    DESC = OTG legacy logs - debug level
+    GROUP = ALL:LEGACY_OTG:DEBUG:LEGACY
+    FORMAT = string,log
+ID = LEGACY_OTG_TRACE
+    DESC = OTG legacy logs - trace level
+    GROUP = ALL:LEGACY_OTG:TRACE:LEGACY
+    FORMAT = string,log
+
+ID = LEGACY_OCG_INFO
+    DESC = OCG legacy logs - info level
+    GROUP = ALL:LEGACY_OCG:INFO:LEGACY
+    FORMAT = string,log
+ID = LEGACY_OCG_ERROR
+    DESC = OCG legacy logs - error level
+    GROUP = ALL:LEGACY_OCG:ERROR:LEGACY
+    FORMAT = string,log
+ID = LEGACY_OCG_WARNING
+    DESC = OCG legacy logs - warning level
+    GROUP = ALL:LEGACY_OCG:WARNING:LEGACY
+    FORMAT = string,log
+ID = LEGACY_OCG_DEBUG
+    DESC = OCG legacy logs - debug level
+    GROUP = ALL:LEGACY_OCG:DEBUG:LEGACY
+    FORMAT = string,log
+ID = LEGACY_OCG_TRACE
+    DESC = OCG legacy logs - trace level
+    GROUP = ALL:LEGACY_OCG:TRACE:LEGACY
+    FORMAT = string,log
+
+ID = LEGACY_OCM_INFO
+    DESC = OCM legacy logs - info level
+    GROUP = ALL:LEGACY_OCM:INFO:LEGACY
+    FORMAT = string,log
+ID = LEGACY_OCM_ERROR
+    DESC = OCM legacy logs - error level
+    GROUP = ALL:LEGACY_OCM:ERROR:LEGACY
+    FORMAT = string,log
+ID = LEGACY_OCM_WARNING
+    DESC = OCM legacy logs - warning level
+    GROUP = ALL:LEGACY_OCM:WARNING:LEGACY
+    FORMAT = string,log
+ID = LEGACY_OCM_DEBUG
+    DESC = OCM legacy logs - debug level
+    GROUP = ALL:LEGACY_OCM:DEBUG:LEGACY
+    FORMAT = string,log
+ID = LEGACY_OCM_TRACE
+    DESC = OCM legacy logs - trace level
+    GROUP = ALL:LEGACY_OCM:TRACE:LEGACY
+    FORMAT = string,log
+
+ID = LEGACY_OIP_INFO
+    DESC = OIP legacy logs - info level
+    GROUP = ALL:LEGACY_OIP:INFO:LEGACY
+    FORMAT = string,log
+ID = LEGACY_OIP_ERROR
+    DESC = OIP legacy logs - error level
+    GROUP = ALL:LEGACY_OIP:ERROR:LEGACY
+    FORMAT = string,log
+ID = LEGACY_OIP_WARNING
+    DESC = OIP legacy logs - warning level
+    GROUP = ALL:LEGACY_OIP:WARNING:LEGACY
+    FORMAT = string,log
+ID = LEGACY_OIP_DEBUG
+    DESC = OIP legacy logs - debug level
+    GROUP = ALL:LEGACY_OIP:DEBUG:LEGACY
+    FORMAT = string,log
+ID = LEGACY_OIP_TRACE
+    DESC = OIP legacy logs - trace level
+    GROUP = ALL:LEGACY_OIP:TRACE:LEGACY
+    FORMAT = string,log
+
+ID = LEGACY_OMG_INFO
+    DESC = OMG legacy logs - info level
+    GROUP = ALL:LEGACY_OMG:INFO:LEGACY
+    FORMAT = string,log
+ID = LEGACY_OMG_ERROR
+    DESC = OMG legacy logs - error level
+    GROUP = ALL:LEGACY_OMG:ERROR:LEGACY
+    FORMAT = string,log
+ID = LEGACY_OMG_WARNING
+    DESC = OMG legacy logs - warning level
+    GROUP = ALL:LEGACY_OMG:WARNING:LEGACY
+    FORMAT = string,log
+ID = LEGACY_OMG_DEBUG
+    DESC = OMG legacy logs - debug level
+    GROUP = ALL:LEGACY_OMG:DEBUG:LEGACY
+    FORMAT = string,log
+ID = LEGACY_OMG_TRACE
+    DESC = OMG legacy logs - trace level
+    GROUP = ALL:LEGACY_OMG:TRACE:LEGACY
+    FORMAT = string,log
+
+ID = LEGACY_OPT_INFO
+    DESC = OPT legacy logs - info level
+    GROUP = ALL:LEGACY_OPT:INFO:LEGACY
+    FORMAT = string,log
+ID = LEGACY_OPT_ERROR
+    DESC = OPT legacy logs - error level
+    GROUP = ALL:LEGACY_OPT:ERROR:LEGACY
+    FORMAT = string,log
+ID = LEGACY_OPT_WARNING
+    DESC = OPT legacy logs - warning level
+    GROUP = ALL:LEGACY_OPT:WARNING:LEGACY
+    FORMAT = string,log
+ID = LEGACY_OPT_DEBUG
+    DESC = OPT legacy logs - debug level
+    GROUP = ALL:LEGACY_OPT:DEBUG:LEGACY
+    FORMAT = string,log
+ID = LEGACY_OPT_TRACE
+    DESC = OPT legacy logs - trace level
+    GROUP = ALL:LEGACY_OPT:TRACE:LEGACY
+    FORMAT = string,log
+
+ID = LEGACY_GTPU_INFO
+    DESC = GTPU legacy logs - info level
+    GROUP = ALL:LEGACY_GTPU:INFO:LEGACY
+    FORMAT = string,log
+ID = LEGACY_GTPU_ERROR
+    DESC = GTPU legacy logs - error level
+    GROUP = ALL:LEGACY_GTPU:ERROR:LEGACY
+    FORMAT = string,log
+ID = LEGACY_GTPU_WARNING
+    DESC = GTPU legacy logs - warning level
+    GROUP = ALL:LEGACY_GTPU:WARNING:LEGACY
+    FORMAT = string,log
+ID = LEGACY_GTPU_DEBUG
+    DESC = GTPU legacy logs - debug level
+    GROUP = ALL:LEGACY_GTPU:DEBUG:LEGACY
+    FORMAT = string,log
+ID = LEGACY_GTPU_TRACE
+    DESC = GTPU legacy logs - trace level
+    GROUP = ALL:LEGACY_GTPU:TRACE:LEGACY
+    FORMAT = string,log
+
+ID = LEGACY_TMR_INFO
+    DESC = TMR legacy logs - info level
+    GROUP = ALL:LEGACY_TMR:INFO:LEGACY
+    FORMAT = string,log
+ID = LEGACY_TMR_ERROR
+    DESC = TMR legacy logs - error level
+    GROUP = ALL:LEGACY_TMR:ERROR:LEGACY
+    FORMAT = string,log
+ID = LEGACY_TMR_WARNING
+    DESC = TMR legacy logs - warning level
+    GROUP = ALL:LEGACY_TMR:WARNING:LEGACY
+    FORMAT = string,log
+ID = LEGACY_TMR_DEBUG
+    DESC = TMR legacy logs - debug level
+    GROUP = ALL:LEGACY_TMR:DEBUG:LEGACY
+    FORMAT = string,log
+ID = LEGACY_TMR_TRACE
+    DESC = TMR legacy logs - trace level
+    GROUP = ALL:LEGACY_TMR:TRACE:LEGACY
+    FORMAT = string,log
+
+ID = LEGACY_OSA_INFO
+    DESC = OSA legacy logs - info level
+    GROUP = ALL:LEGACY_OSA:INFO:LEGACY
+    FORMAT = string,log
+ID = LEGACY_OSA_ERROR
+    DESC = OSA legacy logs - error level
+    GROUP = ALL:LEGACY_OSA:ERROR:LEGACY
+    FORMAT = string,log
+ID = LEGACY_OSA_WARNING
+    DESC = OSA legacy logs - warning level
+    GROUP = ALL:LEGACY_OSA:WARNING:LEGACY
+    FORMAT = string,log
+ID = LEGACY_OSA_DEBUG
+    DESC = OSA legacy logs - debug level
+    GROUP = ALL:LEGACY_OSA:DEBUG:LEGACY
+    FORMAT = string,log
+ID = LEGACY_OSA_TRACE
+    DESC = OSA legacy logs - trace level
+    GROUP = ALL:LEGACY_OSA:TRACE:LEGACY
+    FORMAT = string,log
+
+# this is a bad hack but I won't fix (function util_print_hex_octets
+# in openairinterface5g/openair2/LAYER2/PDCP_v10.1.0/pdcp_util.c
+# does funky things with the LOG_x macros but we work on the C pre-processor
+# level and this funkyness is not easily dealable with, so be it...)
+ID = LEGACY_component_INFO
+    DESC = component legacy logs - info level
+    GROUP = ALL:LEGACY_component:INFO:LEGACY
+    FORMAT = string,log
+ID = LEGACY_component_ERROR
+    DESC = component legacy logs - error level
+    GROUP = ALL:LEGACY_component:ERROR:LEGACY
+    FORMAT = string,log
+ID = LEGACY_component_WARNING
+    DESC = component legacy logs - warning level
+    GROUP = ALL:LEGACY_component:WARNING:LEGACY
+    FORMAT = string,log
+ID = LEGACY_component_DEBUG
+    DESC = component legacy logs - debug level
+    GROUP = ALL:LEGACY_component:DEBUG:LEGACY
+    FORMAT = string,log
+ID = LEGACY_component_TRACE
+    DESC = component legacy logs - trace level
+    GROUP = ALL:LEGACY_component:TRACE:LEGACY
+    FORMAT = string,log
+ID = LEGACY_componentP_INFO
+    DESC = componentP legacy logs - info level
+    GROUP = ALL:LEGACY_componentP:INFO:LEGACY
+    FORMAT = string,log
+ID = LEGACY_componentP_ERROR
+    DESC = componentP legacy logs - error level
+    GROUP = ALL:LEGACY_componentP:ERROR:LEGACY
+    FORMAT = string,log
+ID = LEGACY_componentP_WARNING
+    DESC = componentP legacy logs - warning level
+    GROUP = ALL:LEGACY_componentP:WARNING:LEGACY
+    FORMAT = string,log
+ID = LEGACY_componentP_DEBUG
+    DESC = componentP legacy logs - debug level
+    GROUP = ALL:LEGACY_componentP:DEBUG:LEGACY
+    FORMAT = string,log
+ID = LEGACY_componentP_TRACE
+    DESC = componentP legacy logs - trace level
+    GROUP = ALL:LEGACY_componentP:TRACE:LEGACY
+    FORMAT = string,log
+
+#needed?
+ID = LEGACY_CLI_INFO
+    DESC = CLI legacy logs - info level
+    GROUP = ALL:LEGACY_CLI:INFO:LEGACY
+    FORMAT = string,log
+ID = LEGACY_CLI_ERROR
+    DESC = CLI legacy logs - error level
+    GROUP = ALL:LEGACY_CLI:ERROR:LEGACY
+    FORMAT = string,log
+ID = LEGACY_CLI_WARNING
+    DESC = CLI legacy logs - warning level
+    GROUP = ALL:LEGACY_CLI:WARNING:LEGACY
+    FORMAT = string,log
+ID = LEGACY_CLI_DEBUG
+    DESC = CLI legacy logs - debug level
+    GROUP = ALL:LEGACY_CLI:DEBUG:LEGACY
+    FORMAT = string,log
+ID = LEGACY_CLI_TRACE
+    DESC = CLI legacy logs - trace level
+    GROUP = ALL:LEGACY_CLI:TRACE:LEGACY
+    FORMAT = string,log
+
+#for debug/test - not used
+ID = first
+ID = buf_test
+
+#VCD variables and functions
+
+#be careful! this must be synchronized with the code!
+#also keep up to date VCD_NUM_VARIABLES and VCD_NUM_FUNCTIONS in T_defs.h
+
+#to synchronize: copy/paste from openair2/UTIL/LOG/vcd_signal_dumper.h
+#the variables and functions name, replace "SIGNAL_DUMPER_VARIABLES" by
+#"VARIABLE" and "SIGNAL_DUMPER_FUNCTIONS" by "FUNCTION" (check that
+#everything is fine! for example we have
+#VCD_SIGNAL_DUMPER_VARIABLE_ITTI_SEND_MSG that has VARIABLE without S)
+#and then process with sed
+
+#to generate variables:
+#sed -e "s/  VCD_VARIABLE_\(.*\)/ID = VCD_VARIABLE_\1\n    DESC = VCD variable \1\n    GROUP = ALL:VCD:ENB\n    FORMAT = ulong,value/" < VCD >> T_messages.txt
+
+#to generate functions:
+#sed -e "s/  VCD_FUNCTION_\(.*\)/ID = VCD_FUNCTION_\1\n    DESC = VCD function \1\n    GROUP = ALL:VCD:ENB\n    FORMAT = int,value/" < VCD.functions >> T_messages.txt
+
+#you may want to manually edit groups for UE instead of eNB
+
+#then count functions and variables and update VCD_NUM_FUNCTIONS and
+#VCD_NUM_VARIABLES in T_defs.h
+
+#also verify that VCD_FIRST_FUNCTION and VCD_FIRST_VARIABLE are correct
+#in T_defs.h. They have to point to the first function and variable
+#as defined below. Note also that the order of the VCD functions
+#and variables must be the same as in the code.
+
+#variables
+
+ID = VCD_VARIABLE_FRAME_NUMBER_TX_ENB
+    DESC = VCD variable FRAME_NUMBER_TX_ENB
+    GROUP = ALL:VCD:ENB:VCD_VARIABLE
+    FORMAT = ulong,value
+ID = VCD_VARIABLE_FRAME_NUMBER_RX_ENB
+    DESC = VCD variable FRAME_NUMBER_RX_ENB
+    GROUP = ALL:VCD:ENB:VCD_VARIABLE
+    FORMAT = ulong,value
+ID = VCD_VARIABLE_RUNTIME_TX_ENB
+    DESC = VCD variable RUNTIME_TX_ENB
+    GROUP = ALL:VCD:ENB:VCD_VARIABLE
+    FORMAT = ulong,value
+ID = VCD_VARIABLE_RUNTIME_RX_ENB
+    DESC = VCD variable RUNTIME_RX_ENB
+    GROUP = ALL:VCD:ENB:VCD_VARIABLE
+    FORMAT = ulong,value
+ID = VCD_VARIABLE_FRAME_NUMBER_TX_UE
+    DESC = VCD variable FRAME_NUMBER_TX_UE
+    GROUP = ALL:VCD:UE:VCD_VARIABLE
+    FORMAT = ulong,value
+ID = VCD_VARIABLE_FRAME_NUMBER_RX_UE
+    DESC = VCD variable FRAME_NUMBER_RX_UE
+    GROUP = ALL:VCD:UE:VCD_VARIABLE
+    FORMAT = ulong,value
+ID = VCD_VARIABLE_SLOT_NUMBER_TX_UE
+    DESC = VCD variable SLOT_NUMBER_TX_UE
+    GROUP = ALL:VCD:UE:VCD_VARIABLE
+    FORMAT = ulong,value
+ID = VCD_VARIABLE_SLOT_NUMBER_RX_UE
+    DESC = VCD variable SLOT_NUMBER_RX_UE
+    GROUP = ALL:VCD:UE:VCD_VARIABLE
+    FORMAT = ulong,value
+ID = VCD_VARIABLE_SUBFRAME_NUMBER_TX_UE
+    DESC = VCD variable SUBFRAME_NUMBER_TX_UE
+    GROUP = ALL:VCD:UE:VCD_VARIABLE
+    FORMAT = ulong,value
+ID = VCD_VARIABLE_SUBFRAME_NUMBER_RX_UE
+    DESC = VCD variable SUBFRAME_NUMBER_RX_UE
+    GROUP = ALL:VCD:UE:VCD_VARIABLE
+    FORMAT = ulong,value
+ID = VCD_VARIABLE_MISSED_SLOTS_ENB
+    DESC = VCD variable MISSED_SLOTS_ENB
+    GROUP = ALL:VCD:ENB:VCD_VARIABLE
+    FORMAT = ulong,value
+ID = VCD_VARIABLE_DAQ_MBOX
+    DESC = VCD variable DAQ_MBOX
+    GROUP = ALL:VCD:ENB:VCD_VARIABLE
+    FORMAT = ulong,value
+ID = VCD_VARIABLE_UE_OFFSET_MBOX
+    DESC = VCD variable UE_OFFSET_MBOX
+    GROUP = ALL:VCD:UE:VCD_VARIABLE
+    FORMAT = ulong,value
+ID = VCD_VARIABLE_UE_RX_OFFSET
+    DESC = VCD variable UE_RX_OFFSET
+    GROUP = ALL:VCD:UE:VCD_VARIABLE
+    FORMAT = ulong,value
+ID = VCD_VARIABLE_DIFF
+    DESC = VCD variable DIFF
+    GROUP = ALL:VCD:ENB:VCD_VARIABLE
+    FORMAT = ulong,value
+ID = VCD_VARIABLE_HW_SUBFRAME
+    DESC = VCD variable HW_SUBFRAME
+    GROUP = ALL:VCD:ENB:VCD_VARIABLE
+    FORMAT = ulong,value
+ID = VCD_VARIABLE_HW_FRAME
+    DESC = VCD variable HW_FRAME
+    GROUP = ALL:VCD:ENB:VCD_VARIABLE
+    FORMAT = ulong,value
+ID = VCD_VARIABLE_HW_SUBFRAME_RX
+    DESC = VCD variable HW_SUBFRAME_RX
+    GROUP = ALL:VCD:ENB:VCD_VARIABLE
+    FORMAT = ulong,value
+ID = VCD_VARIABLE_HW_FRAME_RX
+    DESC = VCD variable HW_FRAME_RX
+    GROUP = ALL:VCD:ENB:VCD_VARIABLE
+    FORMAT = ulong,value
+ID = VCD_VARIABLE_TXCNT
+    DESC = VCD variable TXCNT
+    GROUP = ALL:VCD:ENB:VCD_VARIABLE
+    FORMAT = ulong,value
+ID = VCD_VARIABLE_RXCNT
+    DESC = VCD variable RXCNT
+    GROUP = ALL:VCD:ENB:VCD_VARIABLE
+    FORMAT = ulong,value
+ID = VCD_VARIABLE_TRX_TS
+    DESC = VCD variable TRX_TS
+    GROUP = ALL:VCD:ENB:VCD_VARIABLE
+    FORMAT = ulong,value
+ID = VCD_VARIABLE_TRX_TST
+    DESC = VCD variable TRX_TST
+    GROUP = ALL:VCD:ENB:VCD_VARIABLE
+    FORMAT = ulong,value
+ID = VCD_VARIABLE_TX_TS
+    DESC = VCD variable TX_TS
+    GROUP = ALL:VCD:ENB:VCD_VARIABLE
+    FORMAT = ulong,value
+ID = VCD_VARIABLE_RX_TS
+    DESC = VCD variable RX_TS
+    GROUP = ALL:VCD:ENB:VCD_VARIABLE
+    FORMAT = ulong,value
+ID = VCD_VARIABLE_RX_HWCNT
+    DESC = VCD variable RX_HWCNT
+    GROUP = ALL:VCD:ENB:VCD_VARIABLE
+    FORMAT = ulong,value
+ID = VCD_VARIABLE_RX_LHWCNT
+    DESC = VCD variable RX_LHWCNT
+    GROUP = ALL:VCD:ENB:VCD_VARIABLE
+    FORMAT = ulong,value
+ID = VCD_VARIABLE_TX_HWCNT
+    DESC = VCD variable TX_HWCNT
+    GROUP = ALL:VCD:ENB:VCD_VARIABLE
+    FORMAT = ulong,value
+ID = VCD_VARIABLE_TX_LHWCNT
+    DESC = VCD variable TX_LHWCNT
+    GROUP = ALL:VCD:ENB:VCD_VARIABLE
+    FORMAT = ulong,value
+ID = VCD_VARIABLE_RX_PCK
+    DESC = VCD variable RX_PCK
+    GROUP = ALL:VCD:ENB:VCD_VARIABLE
+    FORMAT = ulong,value
+ID = VCD_VARIABLE_TX_PCK
+    DESC = VCD variable TX_PCK
+    GROUP = ALL:VCD:ENB:VCD_VARIABLE
+    FORMAT = ulong,value
+ID = VCD_VARIABLE_RX_SEQ_NUM
+    DESC = VCD variable RX_SEQ_NUM
+    GROUP = ALL:VCD:ENB:VCD_VARIABLE
+    FORMAT = ulong,value
+ID = VCD_VARIABLE_RX_SEQ_NUM_PRV
+    DESC = VCD variable RX_SEQ_NUM_PRV
+    GROUP = ALL:VCD:ENB:VCD_VARIABLE
+    FORMAT = ulong,value
+ID = VCD_VARIABLE_TX_SEQ_NUM
+    DESC = VCD variable TX_SEQ_NUM
+    GROUP = ALL:VCD:ENB:VCD_VARIABLE
+    FORMAT = ulong,value
+ID = VCD_VARIABLE_CNT
+    DESC = VCD variable CNT
+    GROUP = ALL:VCD:ENB:VCD_VARIABLE
+    FORMAT = ulong,value
+ID = VCD_VARIABLE_DUMMY_DUMP
+    DESC = VCD variable DUMMY_DUMP
+    GROUP = ALL:VCD:ENB:VCD_VARIABLE
+    FORMAT = ulong,value
+ID = VCD_VARIABLE_ITTI_SEND_MSG
+    DESC = VCD variable ITTI_SEND_MSG
+    GROUP = ALL:VCD:ENB:VCD_VARIABLE
+    FORMAT = ulong,value
+ID = VCD_VARIABLE_ITTI_POLL_MSG
+    DESC = VCD variable ITTI_POLL_MSG
+    GROUP = ALL:VCD:ENB:VCD_VARIABLE
+    FORMAT = ulong,value
+ID = VCD_VARIABLE_ITTI_RECV_MSG
+    DESC = VCD variable ITTI_RECV_MSG
+    GROUP = ALL:VCD:ENB:VCD_VARIABLE
+    FORMAT = ulong,value
+ID = VCD_VARIABLE_ITTI_ALLOC_MSG
+    DESC = VCD variable ITTI_ALLOC_MSG
+    GROUP = ALL:VCD:ENB:VCD_VARIABLE
+    FORMAT = ulong,value
+ID = VCD_VARIABLE_MP_ALLOC
+    DESC = VCD variable MP_ALLOC
+    GROUP = ALL:VCD:ENB:VCD_VARIABLE
+    FORMAT = ulong,value
+ID = VCD_VARIABLE_MP_FREE
+    DESC = VCD variable MP_FREE
+    GROUP = ALL:VCD:ENB:VCD_VARIABLE
+    FORMAT = ulong,value
+ID = VCD_VARIABLE_UE_INST_CNT_RX
+    DESC = VCD variable UE_INST_CNT_RX
+    GROUP = ALL:VCD:UE:VCD_VARIABLE
+    FORMAT = ulong,value
+ID = VCD_VARIABLE_UE_INST_CNT_TX
+    DESC = VCD variable UE_INST_CNT_TX
+    GROUP = ALL:VCD:UE:VCD_VARIABLE
+    FORMAT = ulong,value
+
+#functions
+
+ID = VCD_FUNCTION_RT_SLEEP
+    DESC = VCD function RT_SLEEP
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_TRX_READ
+    DESC = VCD function TRX_READ
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_TRX_WRITE
+    DESC = VCD function TRX_WRITE
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_eNB_PROC_TX0
+    DESC = VCD function eNB_PROC_TX0
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_eNB_PROC_RX0
+    DESC = VCD function eNB_PROC_RX0
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_eNB_PROC_TX1
+    DESC = VCD function eNB_PROC_TX1
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_eNB_PROC_RX1
+    DESC = VCD function eNB_PROC_RX1
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_eNB_PROC_TX2
+    DESC = VCD function eNB_PROC_TX2
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_eNB_PROC_RX2
+    DESC = VCD function eNB_PROC_RX2
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_eNB_PROC_TX3
+    DESC = VCD function eNB_PROC_TX3
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_eNB_PROC_RX3
+    DESC = VCD function eNB_PROC_RX3
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_eNB_PROC_TX4
+    DESC = VCD function eNB_PROC_TX4
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_eNB_PROC_RX4
+    DESC = VCD function eNB_PROC_RX4
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_eNB_PROC_TX5
+    DESC = VCD function eNB_PROC_TX5
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_eNB_PROC_RX5
+    DESC = VCD function eNB_PROC_RX5
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_eNB_PROC_TX6
+    DESC = VCD function eNB_PROC_TX6
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_eNB_PROC_RX6
+    DESC = VCD function eNB_PROC_RX6
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_eNB_PROC_TX7
+    DESC = VCD function eNB_PROC_TX7
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_eNB_PROC_RX7
+    DESC = VCD function eNB_PROC_RX7
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_eNB_PROC_TX8
+    DESC = VCD function eNB_PROC_TX8
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_eNB_PROC_RX8
+    DESC = VCD function eNB_PROC_RX8
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_eNB_PROC_TX9
+    DESC = VCD function eNB_PROC_TX9
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_eNB_PROC_RX9
+    DESC = VCD function eNB_PROC_RX9
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_UE_THREAD_TX
+    DESC = VCD function UE_THREAD_TX
+    GROUP = ALL:VCD:UE:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_UE_THREAD_RX
+    DESC = VCD function UE_THREAD_RX
+    GROUP = ALL:VCD:UE:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_eNB_TX
+    DESC = VCD function eNB_TX
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_eNB_RX
+    DESC = VCD function eNB_RX
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_eNB_TRX
+    DESC = VCD function eNB_TRX
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_eNB_TM
+    DESC = VCD function eNB_TM
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_eNB_RX_SLEEP
+    DESC = VCD function eNB_RX_SLEEP
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_eNB_TX_SLEEP
+    DESC = VCD function eNB_TX_SLEEP
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_eNB_PROC_SLEEP
+    DESC = VCD function eNB_PROC_SLEEP
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_TRX_READ_RF
+    DESC = VCD function TRX_READ_RF
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_TRX_WRITE_RF
+    DESC = VCD function TRX_WRITE_RF
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_UE_SYNCH
+    DESC = VCD function UE_SYNCH
+    GROUP = ALL:VCD:UE:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_UE_SLOT_FEP
+    DESC = VCD function UE_SLOT_FEP
+    GROUP = ALL:VCD:UE:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_UE_RRC_MEASUREMENTS
+    DESC = VCD function UE_RRC_MEASUREMENTS
+    GROUP = ALL:VCD:UE:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_UE_GAIN_CONTROL
+    DESC = VCD function UE_GAIN_CONTROL
+    GROUP = ALL:VCD:UE:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_UE_ADJUST_SYNCH
+    DESC = VCD function UE_ADJUST_SYNCH
+    GROUP = ALL:VCD:UE:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_UE_MEASUREMENT_PROCEDURES
+    DESC = VCD function UE_MEASUREMENT_PROCEDURES
+    GROUP = ALL:VCD:UE:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_UE_PDCCH_PROCEDURES
+    DESC = VCD function UE_PDCCH_PROCEDURES
+    GROUP = ALL:VCD:UE:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_UE_PBCH_PROCEDURES
+    DESC = VCD function UE_PBCH_PROCEDURES
+    GROUP = ALL:VCD:UE:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_PHY_PROCEDURES_ENB_TX
+    DESC = VCD function PHY_PROCEDURES_ENB_TX
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_PHY_PROCEDURES_ENB_RX
+    DESC = VCD function PHY_PROCEDURES_ENB_RX
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_PHY_PROCEDURES_UE_TX
+    DESC = VCD function PHY_PROCEDURES_UE_TX
+    GROUP = ALL:VCD:UE:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_PHY_PROCEDURES_UE_RX
+    DESC = VCD function PHY_PROCEDURES_UE_RX
+    GROUP = ALL:VCD:UE:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_PHY_PROCEDURES_ENB_LTE
+    DESC = VCD function PHY_PROCEDURES_ENB_LTE
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_PHY_PROCEDURES_UE_LTE
+    DESC = VCD function PHY_PROCEDURES_UE_LTE
+    GROUP = ALL:VCD:UE:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_PDSCH_THREAD
+    DESC = VCD function PDSCH_THREAD
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_DLSCH_THREAD0
+    DESC = VCD function DLSCH_THREAD0
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_DLSCH_THREAD1
+    DESC = VCD function DLSCH_THREAD1
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_DLSCH_THREAD2
+    DESC = VCD function DLSCH_THREAD2
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_DLSCH_THREAD3
+    DESC = VCD function DLSCH_THREAD3
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_DLSCH_THREAD4
+    DESC = VCD function DLSCH_THREAD4
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_DLSCH_THREAD5
+    DESC = VCD function DLSCH_THREAD5
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_DLSCH_THREAD6
+    DESC = VCD function DLSCH_THREAD6
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_DLSCH_THREAD7
+    DESC = VCD function DLSCH_THREAD7
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_DLSCH_DECODING0
+    DESC = VCD function DLSCH_DECODING0
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_DLSCH_DECODING1
+    DESC = VCD function DLSCH_DECODING1
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_DLSCH_DECODING2
+    DESC = VCD function DLSCH_DECODING2
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_DLSCH_DECODING3
+    DESC = VCD function DLSCH_DECODING3
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_DLSCH_DECODING4
+    DESC = VCD function DLSCH_DECODING4
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_DLSCH_DECODING5
+    DESC = VCD function DLSCH_DECODING5
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_DLSCH_DECODING6
+    DESC = VCD function DLSCH_DECODING6
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_DLSCH_DECODING7
+    DESC = VCD function DLSCH_DECODING7
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_RX_PDCCH
+    DESC = VCD function RX_PDCCH
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_DCI_DECODING
+    DESC = VCD function DCI_DECODING
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_RX_PHICH
+    DESC = VCD function RX_PHICH
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_PHY_UE_CONFIG_SIB2
+    DESC = VCD function PHY_UE_CONFIG_SIB2
+    GROUP = ALL:VCD:UE:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_PHY_CONFIG_SIB1_ENB
+    DESC = VCD function PHY_CONFIG_SIB1_ENB
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_PHY_CONFIG_SIB2_ENB
+    DESC = VCD function PHY_CONFIG_SIB2_ENB
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_PHY_CONFIG_DEDICATED_ENB
+    DESC = VCD function PHY_CONFIG_DEDICATED_ENB
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_PHY_UE_COMPUTE_PRACH
+    DESC = VCD function PHY_UE_COMPUTE_PRACH
+    GROUP = ALL:VCD:UE:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_PHY_ENB_ULSCH_DECODING
+    DESC = VCD function PHY_ENB_ULSCH_DECODING
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_PHY_ENB_SFGEN
+    DESC = VCD function PHY_ENB_SFGEN
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_PHY_ENB_PRACH_RX
+    DESC = VCD function PHY_ENB_PRACH_RX
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_PHY_ENB_PDCCH_TX
+    DESC = VCD function PHY_ENB_PDCCH_TX
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_PHY_ENB_RS_TX
+    DESC = VCD function PHY_ENB_RS_TX
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_UE_GENERATE_PRACH
+    DESC = VCD function UE_GENERATE_PRACH
+    GROUP = ALL:VCD:UE:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_UE_ULSCH_MODULATION
+    DESC = VCD function UE_ULSCH_MODULATION
+    GROUP = ALL:VCD:UE:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_UE_ULSCH_ENCODING
+    DESC = VCD function UE_ULSCH_ENCODING
+    GROUP = ALL:VCD:UE:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_UE_ULSCH_SCRAMBLING
+    DESC = VCD function UE_ULSCH_SCRAMBLING
+    GROUP = ALL:VCD:UE:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_ENB_DLSCH_MODULATION
+    DESC = VCD function ENB_DLSCH_MODULATION
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_ENB_DLSCH_ENCODING
+    DESC = VCD function ENB_DLSCH_ENCODING
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_ENB_DLSCH_SCRAMBLING
+    DESC = VCD function ENB_DLSCH_SCRAMBLING
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_MACPHY_INIT
+    DESC = VCD function MACPHY_INIT
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_MACPHY_EXIT
+    DESC = VCD function MACPHY_EXIT
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_ENB_DLSCH_ULSCH_SCHEDULER
+    DESC = VCD function ENB_DLSCH_ULSCH_SCHEDULER
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_FILL_RAR
+    DESC = VCD function FILL_RAR
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_TERMINATE_RA_PROC
+    DESC = VCD function TERMINATE_RA_PROC
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_INITIATE_RA_PROC
+    DESC = VCD function INITIATE_RA_PROC
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_CANCEL_RA_PROC
+    DESC = VCD function CANCEL_RA_PROC
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_GET_DCI_SDU
+    DESC = VCD function GET_DCI_SDU
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_GET_DLSCH_SDU
+    DESC = VCD function GET_DLSCH_SDU
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_RX_SDU
+    DESC = VCD function RX_SDU
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_MRBCH_PHY_SYNC_FAILURE
+    DESC = VCD function MRBCH_PHY_SYNC_FAILURE
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_SR_INDICATION
+    DESC = VCD function SR_INDICATION
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_DLSCH_PREPROCESSOR
+    DESC = VCD function DLSCH_PREPROCESSOR
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_SCHEDULE_DLSCH
+    DESC = VCD function SCHEDULE_DLSCH
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_FILL_DLSCH_DCI
+    DESC = VCD function FILL_DLSCH_DCI
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_OUT_OF_SYNC_IND
+    DESC = VCD function OUT_OF_SYNC_IND
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_UE_DECODE_SI
+    DESC = VCD function UE_DECODE_SI
+    GROUP = ALL:VCD:UE:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_UE_DECODE_CCCH
+    DESC = VCD function UE_DECODE_CCCH
+    GROUP = ALL:VCD:UE:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_UE_DECODE_BCCH
+    DESC = VCD function UE_DECODE_BCCH
+    GROUP = ALL:VCD:UE:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_UE_SEND_SDU
+    DESC = VCD function UE_SEND_SDU
+    GROUP = ALL:VCD:UE:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_UE_GET_SDU
+    DESC = VCD function UE_GET_SDU
+    GROUP = ALL:VCD:UE:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_UE_GET_RACH
+    DESC = VCD function UE_GET_RACH
+    GROUP = ALL:VCD:UE:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_UE_PROCESS_RAR
+    DESC = VCD function UE_PROCESS_RAR
+    GROUP = ALL:VCD:UE:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_UE_SCHEDULER
+    DESC = VCD function UE_SCHEDULER
+    GROUP = ALL:VCD:UE:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_UE_GET_SR
+    DESC = VCD function UE_GET_SR
+    GROUP = ALL:VCD:UE:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_UE_SEND_MCH_SDU
+    DESC = VCD function UE_SEND_MCH_SDU
+    GROUP = ALL:VCD:UE:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_RLC_DATA_REQ
+    DESC = VCD function RLC_DATA_REQ
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_MAC_RLC_STATUS_IND
+    DESC = VCD function MAC_RLC_STATUS_IND
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_MAC_RLC_DATA_REQ
+    DESC = VCD function MAC_RLC_DATA_REQ
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_MAC_RLC_DATA_IND
+    DESC = VCD function MAC_RLC_DATA_IND
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_RLC_UM_TRY_REASSEMBLY
+    DESC = VCD function RLC_UM_TRY_REASSEMBLY
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_RLC_UM_CHECK_TIMER_DAR_TIME_OUT
+    DESC = VCD function RLC_UM_CHECK_TIMER_DAR_TIME_OUT
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_RLC_UM_RECEIVE_PROCESS_DAR
+    DESC = VCD function RLC_UM_RECEIVE_PROCESS_DAR
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_PDCP_RUN
+    DESC = VCD function PDCP_RUN
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_PDCP_DATA_REQ
+    DESC = VCD function PDCP_DATA_REQ
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_PDCP_DATA_IND
+    DESC = VCD function PDCP_DATA_IND
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_PDCP_APPLY_SECURITY
+    DESC = VCD function PDCP_APPLY_SECURITY
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_PDCP_VALIDATE_SECURITY
+    DESC = VCD function PDCP_VALIDATE_SECURITY
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_RRC_RX_TX
+    DESC = VCD function RRC_RX_TX
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_RRC_MAC_CONFIG
+    DESC = VCD function RRC_MAC_CONFIG
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_RRC_UE_DECODE_SIB1
+    DESC = VCD function RRC_UE_DECODE_SIB1
+    GROUP = ALL:VCD:UE:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_RRC_UE_DECODE_SI
+    DESC = VCD function RRC_UE_DECODE_SI
+    GROUP = ALL:VCD:UE:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_GTPV1U_ENB_TASK
+    DESC = VCD function GTPV1U_ENB_TASK
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_GTPV1U_PROCESS_UDP_REQ
+    DESC = VCD function GTPV1U_PROCESS_UDP_REQ
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_GTPV1U_PROCESS_TUNNEL_DATA_REQ
+    DESC = VCD function GTPV1U_PROCESS_TUNNEL_DATA_REQ
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_UDP_ENB_TASK
+    DESC = VCD function UDP_ENB_TASK
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_EMU_TRANSPORT
+    DESC = VCD function EMU_TRANSPORT
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_LOG_RECORD
+    DESC = VCD function LOG_RECORD
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_ITTI_ENQUEUE_MESSAGE
+    DESC = VCD function ITTI_ENQUEUE_MESSAGE
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_ITTI_DUMP_ENQUEUE_MESSAGE
+    DESC = VCD function ITTI_DUMP_ENQUEUE_MESSAGE
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_ITTI_DUMP_ENQUEUE_MESSAGE_MALLOC
+    DESC = VCD function ITTI_DUMP_ENQUEUE_MESSAGE_MALLOC
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_ITTI_RELAY_THREAD
+    DESC = VCD function ITTI_RELAY_THREAD
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
+ID = VCD_FUNCTION_TEST
+    DESC = VCD function TEST
+    GROUP = ALL:VCD:ENB:VCD_FUNCTION
+    FORMAT = int,value
diff --git a/common/utils/T/defs.h b/common/utils/T/defs.h
new file mode 100644
index 0000000000000000000000000000000000000000..69b379ce0b3c3963475622ec3ed6d66b472ed97b
--- /dev/null
+++ b/common/utils/T/defs.h
@@ -0,0 +1,30 @@
+#ifndef _TRACER_DEFS_H_
+#define _TRACER_DEFS_H_
+
+/* types of plots */
+#define PLOT_VS_TIME   0
+#define PLOT_IQ_POINTS 1
+#define PLOT_MINMAX    2
+
+void new_thread(void *(*f)(void *), void *data);
+
+/* ... is { int count; int type; char *color; } for 'nplots' plots */
+void *make_plot(int width, int height, char *title, int nplots, ...);
+void plot_set(void *plot, float *data, int len, int pos, int pp);
+void iq_plot_set(void *plot, short *data, int len, int pos, int pp);
+void iq_plot_set_sized(void *_plot, short *data, int len, int pp);
+void iq_plot_add_iq_point_loop(void *_plot, short i, short q, int pp);
+void iq_plot_add_energy_point_loop(void *_plot, int e, int pp);
+
+/* returns an opaque pointer - truly a 'database *', see t_data.c */
+void *parse_database(char *filename);
+void dump_database(void *database);
+void list_ids(void *database);
+void list_groups(void *database);
+void on_off(void *d, char *item, int *a, int onoff);
+
+void *forwarder(char *ip, int port);
+void forward(void *forwarder, char *buf, int size);
+void forward_start_client(void *forwarder, int socket);
+
+#endif /* _TRACER_DEFS_H_ */
diff --git a/common/utils/T/generate_Txx.c b/common/utils/T/generate_Txx.c
new file mode 100644
index 0000000000000000000000000000000000000000..a32b554a718fd27fc56cbc8249ffd5094af44cb5
--- /dev/null
+++ b/common/utils/T/generate_Txx.c
@@ -0,0 +1,25 @@
+#include <stdio.h>
+
+void print(int n)
+{
+  int i;
+  printf("#define T%d(t", n);
+  for(i=0; i<(n-1)/2; i++) printf(",t%d,x%d", i, i);
+  printf(") \\\n");
+  printf("  do { \\\n");
+  printf("    if (T_ACTIVE(t)) { \\\n");
+  printf("      T_LOCAL_DATA \\\n");
+  printf("      T_HEADER(t); \\\n");
+  for(i=0; i<(n-1)/2; i++) printf("      T_PUT_##t%d(%d, x%d); \\\n", i, i+2, i);
+  printf("      T_COMMIT(); \\\n");
+  printf("    } \\\n");
+  printf("  } while (0)\n");
+  printf("\n");
+}
+
+int main(void)
+{
+  int i;
+  for (i = 11; i <= 33; i+=2) print(i);
+  return 0;
+}
diff --git a/common/utils/T/genids.c b/common/utils/T/genids.c
new file mode 100644
index 0000000000000000000000000000000000000000..734c506368ac348ba9db9ae2eae8048fcec929d6
--- /dev/null
+++ b/common/utils/T/genids.c
@@ -0,0 +1,155 @@
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+char **unique_ids;
+int unique_ids_size;
+int unique_ids_maxsize;
+
+int cmp(const void *p1, const void *p2)
+{
+  return strcmp(*(char * const *)p1, *(char * const *)p2);
+}
+
+/* return 1 if s was not already known, 0 if it was */
+int new_unique_id(char *s)
+{
+  if (unique_ids_size)
+  if (bsearch(&s, unique_ids, unique_ids_size, sizeof(char *), cmp) != NULL) {
+    printf("FATAL: ID %s is not unique\n", s);
+    return 0;
+  }
+  if (unique_ids_size == unique_ids_maxsize) {
+    unique_ids_maxsize += 256;
+    unique_ids = realloc(unique_ids, unique_ids_maxsize * sizeof(char *));
+    if (unique_ids == NULL) { printf("out of memory\n"); abort(); }
+  }
+  unique_ids[unique_ids_size] = strdup(s);
+  if (unique_ids[unique_ids_size] == NULL)
+    { printf("out of memory\n"); abort(); }
+  unique_ids_size++;
+  qsort(unique_ids, unique_ids_size, sizeof(char *), cmp);
+  return 1;
+}
+
+char *bufname;
+int bufname_size;
+int bufname_maxsize;
+
+void putname(int c)
+{
+  if (bufname_size == bufname_maxsize) {
+    bufname_maxsize += 256;
+    bufname = realloc(bufname, bufname_maxsize);
+    if (bufname == NULL) { printf("memory allocation error\n"); exit(1); }
+  }
+  bufname[bufname_size] = c;
+  bufname_size++;
+}
+
+char *bufvalue;
+int bufvalue_size;
+int bufvalue_maxsize;
+
+void putvalue(int c)
+{
+  if (bufvalue_size == bufvalue_maxsize) {
+    bufvalue_maxsize += 256;
+    bufvalue = realloc(bufvalue, bufvalue_maxsize);
+    if (bufvalue == NULL) { printf("memory allocation error\n"); exit(1); }
+  }
+  bufvalue[bufvalue_size] = c;
+  bufvalue_size++;
+}
+
+void smash_spaces(FILE *f)
+{
+  int c;
+  while (1) {
+    c = fgetc(f);
+    if (isspace(c)) continue;
+    if (c == ' ') continue;
+    if (c == '\t') continue;
+    if (c == '\n') continue;
+    if (c == 10 || c == 13) continue;
+    if (c == '#') {
+      while (1) {
+        c = fgetc(f);
+        if (c == '\n' || c == EOF) break;
+      }
+      continue;
+    }
+    break;
+  }
+  if (c != EOF) ungetc(c, f);
+}
+
+void get_line(FILE *f, char **name, char **value)
+{
+  int c;
+  bufname_size = 0;
+  bufvalue_size = 0;
+  *name = NULL;
+  *value = NULL;
+  smash_spaces(f);
+  c = fgetc(f);
+  while (!(c == '=' || isspace(c) || c == EOF)) { putname(c); c = fgetc(f); }
+  if (c == EOF) return;
+  putname(0);
+  while (!(c == EOF || c == '=')) c = fgetc(f);
+  if (c == EOF) return;
+  smash_spaces(f);
+  c = fgetc(f);
+  while (!(c == 10 || c == 13 || c == EOF)) { putvalue(c); c = fgetc(f); }
+  putvalue(0);
+  if (bufname_size <= 1) return;
+  if (bufvalue_size <= 1) return;
+  *name = bufname;
+  *value = bufvalue;
+}
+
+int main(int n, char **v)
+{
+  FILE *in;
+  FILE *out;
+  char *name;
+  char *value;
+  char *in_name;
+  char *out_name;
+
+  if (n != 3) { printf("gimme <source> <dest>\n"); exit(1); }
+
+  n = 0;
+
+  in_name = v[1];
+  out_name = v[2];
+
+  in = fopen(in_name, "r"); if (in == NULL) { perror(in_name); exit(1); }
+  out = fopen(out_name, "w"); if (out == NULL) { perror(out_name); exit(1); }
+
+  fprintf(out, "/* generated file, do not edit by hand */\n\n");
+
+  while (1) {
+    get_line(in, &name, &value);
+    if (name == NULL) break;
+    printf("name '%s' value '%s'\n", name, value);
+    if (isspace(value[strlen(value)-1])) {
+      printf("bad value '%s' (no space at the end please!)\n", value);
+      unlink(out_name);
+      exit(1);
+    }
+    if (!strcmp(name, "ID")) {
+      if (!new_unique_id(value)) { unlink(out_name); exit(1); }
+      fprintf(out, "#define T_%s T_ID(%d)\n", value, n);
+      n++;
+    }
+  }
+  fprintf(out, "#define T_NUMBER_OF_IDS %d\n", n);
+
+  fclose(in);
+  fclose(out);
+
+  return 0;
+}
diff --git a/common/utils/T/local_tracer.c b/common/utils/T/local_tracer.c
new file mode 100644
index 0000000000000000000000000000000000000000..93a56a9e3d41aa2ecd8f41aa617fe55c292c055e
--- /dev/null
+++ b/common/utils/T/local_tracer.c
@@ -0,0 +1,344 @@
+#include <stdio.h>
+#include <string.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <inttypes.h>
+#include <signal.h>
+
+#include "T_defs.h"
+#include "T_IDs.h"
+
+static T_cache_t *T_cache;
+static int T_busylist_head;
+
+typedef struct databuf {
+  char *d;
+  int l;
+  struct databuf *next;
+} databuf;
+
+typedef struct {
+  int socket_local;
+  volatile int socket_remote;
+  int remote_port;
+  pthread_mutex_t lock;
+  pthread_cond_t cond;
+  databuf * volatile head, *tail;
+  uint64_t memusage;
+  uint64_t last_warning_memusage;
+} forward_data;
+
+/****************************************************************************/
+/*                      utility functions                                   */
+/****************************************************************************/
+
+static void new_thread(void *(*f)(void *), void *data)
+{
+  pthread_t t;
+  pthread_attr_t att;
+
+  if (pthread_attr_init(&att))
+    { fprintf(stderr, "pthread_attr_init err\n"); exit(1); }
+  if (pthread_attr_setdetachstate(&att, PTHREAD_CREATE_DETACHED))
+    { fprintf(stderr, "pthread_attr_setdetachstate err\n"); exit(1); }
+  if (pthread_attr_setstacksize(&att, 10000000))
+    { fprintf(stderr, "pthread_attr_setstacksize err\n"); exit(1); }
+  if (pthread_create(&t, &att, f, data))
+    { fprintf(stderr, "pthread_create err\n"); exit(1); }
+  if (pthread_attr_destroy(&att))
+    { fprintf(stderr, "pthread_attr_destroy err\n"); exit(1); }
+}
+
+static int get_connection(char *addr, int port)
+{
+  struct sockaddr_in a;
+  socklen_t alen;
+  int s, t;
+
+  printf("waiting for connection on %s:%d\n", addr, port);
+
+  s = socket(AF_INET, SOCK_STREAM, 0);
+  if (s == -1) { perror("socket"); exit(1); }
+  t = 1;
+  if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &t, sizeof(int)))
+    { perror("setsockopt"); exit(1); }
+
+  a.sin_family = AF_INET;
+  a.sin_port = htons(port);
+  a.sin_addr.s_addr = inet_addr(addr);
+
+  if (bind(s, (struct sockaddr *)&a, sizeof(a))) { perror("bind"); exit(1); }
+  if (listen(s, 5)) { perror("bind"); exit(1); }
+  alen = sizeof(a);
+  t = accept(s, (struct sockaddr *)&a, &alen);
+  if (t == -1) { perror("accept"); exit(1); }
+  close(s);
+
+  printf("connected\n");
+
+  return t;
+}
+
+/****************************************************************************/
+/*                      forward functions                                   */
+/****************************************************************************/
+
+static void *data_sender(void *_f)
+{
+  forward_data *f = _f;
+  databuf *cur;
+  char *buf, *b;
+  int size;
+
+wait:
+  if (pthread_mutex_lock(&f->lock)) abort();
+  while (f->head == NULL)
+    if (pthread_cond_wait(&f->cond, &f->lock)) abort();
+  cur = f->head;
+  buf = cur->d;
+  size = cur->l;
+  f->head = cur->next;
+  f->memusage -= size;
+  if (f->head == NULL) f->tail = NULL;
+  if (pthread_mutex_unlock(&f->lock)) abort();
+  free(cur);
+  goto process;
+
+process:
+  b = buf;
+  if (f->socket_remote != -1)
+  while (size) {
+    int l = write(f->socket_remote, b, size);
+    if (l <= 0) {
+      printf("forward error\n");
+      close(f->socket_remote);
+      f->socket_remote = -1;
+      break;
+    }
+    size -= l;
+    b += l;
+  }
+
+  free(buf);
+
+  goto wait;
+}
+
+static void *forward_remote_messages(void *_f)
+{
+#define PUT(x) do { \
+    if (bufsize == bufmaxsize) { \
+      bufmaxsize += 4096; \
+      buf = realloc(buf, bufmaxsize); \
+      if (buf == NULL) abort(); \
+    } \
+    buf[bufsize] = x; \
+    bufsize++; \
+  } while (0)
+#define PUT_BUF(x, l) do { \
+    char *zz = (char *)(x); \
+    int len = l; \
+    while (len) { PUT(*zz); zz++; len--; } \
+  } while (0)
+
+  forward_data *f = _f;
+  int from;
+  int to;
+  int l, len;
+  char *b;
+  char *buf = NULL;
+  int bufsize = 0;
+  int bufmaxsize = 0;
+  char t;
+
+again:
+
+  while (1) {
+    from = f->socket_remote;
+    to = f->socket_local;
+
+    bufsize = 0;
+
+    /* let's read and process messages */
+    len = read(from, &t, 1); if (len <= 0) goto dead;
+    PUT(t);
+
+    switch (t) {
+    case 0:
+    case 1:
+      /* message 0 and 1: get a length and then 'length' numbers */
+      if (read(from, &len, sizeof(int)) != sizeof(int)) goto dead;
+      PUT_BUF(&len, 4);
+      while (len) {
+        if (read(from, &l, sizeof(int)) != sizeof(int)) goto dead;
+        PUT_BUF(&l, 4);
+        len--;
+      }
+      break;
+
+    case 2: break;
+    default:
+      printf("%s:%d:%s: unhandled message type %d\n",
+          __FILE__, __LINE__, __FUNCTION__, t);
+      abort();
+    }
+
+    b = buf;
+    while (bufsize) {
+      l = write(to, b, bufsize);
+      if (l <= 0) abort();
+      bufsize -= l;
+      b += l;
+    }
+  }
+
+dead:
+  /* socket died, let's stop all traces and wait for another tracer */
+  /* TODO: be careful with those write, they might write less than wanted */
+  buf[0] = 1;
+  if (write(to, buf, 1) != 1) abort();
+  len = T_NUMBER_OF_IDS;
+  if (write(to, &len, sizeof(int)) != sizeof(int)) abort();
+  l = 0;
+  while (len) {
+    if (write(to, &l, sizeof(int)) != sizeof(int)) abort();
+    len--;
+  };
+
+  close(f->socket_remote);
+  f->socket_remote = get_connection("0.0.0.0", f->remote_port);
+  goto again;
+
+  return NULL;
+}
+
+static void *forwarder(int port, int s)
+{
+  forward_data *f;
+
+  f = malloc(sizeof(*f)); if (f == NULL) abort();
+
+  pthread_mutex_init(&f->lock, NULL);
+  pthread_cond_init(&f->cond, NULL);
+
+  f->socket_local = s;
+  f->head = f->tail = NULL;
+
+  f->memusage = 0;
+  f->last_warning_memusage = 0;
+
+  printf("waiting for remote tracer on port %d\n", port);
+
+  f->remote_port = port;
+  f->socket_remote = get_connection("0.0.0.0", port);
+
+  new_thread(data_sender, f);
+  new_thread(forward_remote_messages, f);
+
+  return f;
+}
+
+static void forward(void *_forwarder, char *buf, int size)
+{
+  forward_data *f = _forwarder;
+  int32_t ssize = size;
+  databuf *new;
+
+  new = malloc(sizeof(*new)); if (new == NULL) abort();
+
+  if (pthread_mutex_lock(&f->lock)) abort();
+
+  new->d = malloc(size + 4); if (new->d == NULL) abort();
+  /* put the size of the message at the head */
+  memcpy(new->d, &ssize, 4);
+  memcpy(new->d+4, buf, size);
+  new->l = size+4;
+  new->next = NULL;
+  if (f->head == NULL) f->head = new;
+  if (f->tail != NULL) f->tail->next = new;
+  f->tail = new;
+
+  f->memusage += size+4;
+  /* warn every 100MB */
+  if (f->memusage > f->last_warning_memusage &&
+      f->memusage - f->last_warning_memusage > 100000000) {
+    f->last_warning_memusage += 100000000;
+    printf("WARNING: memory usage is over %"PRIu64"MB\n",
+           f->last_warning_memusage / 1000000);
+  } else
+  if (f->memusage < f->last_warning_memusage &&
+      f->last_warning_memusage - f->memusage > 100000000) {
+    f->last_warning_memusage = (f->memusage/100000000) * 100000000;
+  }
+
+  if (pthread_cond_signal(&f->cond)) abort();
+  if (pthread_mutex_unlock(&f->lock)) abort();
+}
+
+/****************************************************************************/
+/*                      local functions                                     */
+/****************************************************************************/
+
+static void wait_message(void)
+{
+  while (T_cache[T_busylist_head].busy == 0) usleep(1000);
+}
+
+static void init_shm(void)
+{
+  int i;
+  int s = shm_open(T_SHM_FILENAME, O_RDWR | O_CREAT /*| O_SYNC*/, 0666);
+  if (s == -1) { perror(T_SHM_FILENAME); abort(); }
+  if (ftruncate(s, T_CACHE_SIZE * sizeof(T_cache_t)))
+    { perror(T_SHM_FILENAME); abort(); }
+  T_cache = mmap(NULL, T_CACHE_SIZE * sizeof(T_cache_t),
+                 PROT_READ | PROT_WRITE, MAP_SHARED, s, 0);
+  if (T_cache == NULL)
+    { perror(T_SHM_FILENAME); abort(); }
+  close(s);
+
+  /* let's garbage the memory to catch some potential problems
+   * (think multiprocessor sync issues, barriers, etc.)
+   */
+  memset(T_cache, 0x55, T_CACHE_SIZE * sizeof(T_cache_t));
+  for (i = 0; i < T_CACHE_SIZE; i++) T_cache[i].busy = 0;
+}
+
+void T_local_tracer_main(int remote_port, int wait_for_tracer,
+    int local_socket)
+{
+  int s;
+  int port = remote_port;
+  int dont_wait = wait_for_tracer ? 0 : 1;
+  void *f;
+
+  /* write on a socket fails if the other end is closed and we get SIGPIPE */
+  if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) abort();
+
+  init_shm();
+  s = local_socket;
+
+  if (dont_wait) {
+    char t = 2;
+    if (write(s, &t, 1) != 1) abort();
+  }
+
+  f = forwarder(port, s);
+
+  /* read messages */
+  while (1) {
+    wait_message();
+    __sync_synchronize();
+    forward(f, T_cache[T_busylist_head].buffer,
+            T_cache[T_busylist_head].length);
+    T_cache[T_busylist_head].busy = 0;
+    T_busylist_head++;
+    T_busylist_head &= T_CACHE_SIZE - 1;
+  }
+}
diff --git a/common/utils/T/plot.c b/common/utils/T/plot.c
new file mode 100644
index 0000000000000000000000000000000000000000..74473842883c7e968ecf3c84079c4645e18f2467
--- /dev/null
+++ b/common/utils/T/plot.c
@@ -0,0 +1,291 @@
+#include "defs.h"
+#include <X11/Xlib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <math.h>
+#include <unistd.h>
+#include <sys/select.h>
+#include <stdarg.h>
+
+typedef struct {
+  float *buf;
+  short *iqbuf;
+  int count;
+  int type;
+  volatile int iq_count;  /* for ULSCH IQ data */
+  int iq_insert_pos;
+  GC g;
+} data;
+
+typedef struct {
+  Display *d;
+  Window w;
+  Pixmap px;
+  GC bg;
+  int width;
+  int height;
+  pthread_mutex_t lock;
+  float zoom;
+  int timer_pipe[2];
+  data *p;             /* list of plots */
+  int nplots;
+} plot;
+
+static void *timer_thread(void *_p)
+{
+  plot *p = _p;
+  char c;
+
+  while (1) {
+    /* more or less 10Hz */
+    usleep(100*1000);
+    c = 1;
+    if (write(p->timer_pipe[1], &c, 1) != 1) abort();
+  }
+
+  return NULL;
+}
+
+static void *plot_thread(void *_p)
+{
+  float v;
+  float *s;
+  int i, j;
+  plot *p = _p;
+  int redraw = 0;
+  int replot = 0;
+  fd_set rset;
+  int xfd = ConnectionNumber(p->d);
+  int maxfd = xfd > p->timer_pipe[0] ? xfd : p->timer_pipe[0];
+  int pp;
+
+  while (1) {
+    while (XPending(p->d)) {
+      XEvent e;
+      XNextEvent(p->d, &e);
+      switch (e.type) {
+      case ButtonPress:
+        /* button 4: zoom out */
+        if (e.xbutton.button == 4) { p->zoom = p->zoom * 1.25; replot = 1; }
+        /* button 5: zoom in */
+        if (e.xbutton.button == 5) { p->zoom = p->zoom * 0.8; replot = 1; }
+        printf("zoom: %f\n", p->zoom);
+        break;
+      case Expose: redraw = 1; break;
+      }
+    }
+
+    if (replot == 1) {
+      replot = 0;
+      redraw = 1;
+
+      if (pthread_mutex_lock(&p->lock)) abort();
+
+      XFillRectangle(p->d, p->px, p->bg, 0, 0, p->width, p->height);
+
+      for (pp = 0; pp < p->nplots; pp++) {
+        if (p->p[pp].type == PLOT_MINMAX) {
+          s = p->p[pp].buf;
+          for (i = 0; i < 512; i++) {
+            int min = *s;
+            int max = *s;
+            for (j = 0; j < p->p[pp].count/512; j++, s++) {
+              if (*s < min) min = *s;
+              if (*s > max) max = *s;
+            }
+            XDrawLine(p->d, p->px, p->p[pp].g, i, 100-min, i, 100-max);
+          }
+        } else if (p->p[pp].type == PLOT_VS_TIME) {
+          for (i = 0; i < p->p[pp].count; i++)
+            p->p[pp].buf[i] =
+                10*log10(1.0+(float)(p->p[pp].iqbuf[2*i]*p->p[pp].iqbuf[2*i]+
+                p->p[pp].iqbuf[2*i+1]*p->p[pp].iqbuf[2*i+1]));
+          s = p->p[pp].buf;
+          for (i = 0; i < 512; i++) {
+            v = 0;
+            for (j = 0; j < p->p[pp].count/512; j++, s++) v += *s;
+            v /= p->p[pp].count/512;
+            XDrawLine(p->d, p->px, p->p[pp].g, i, 100, i, 100-v);
+          }
+        } else if (p->p[pp].type == PLOT_IQ_POINTS) {
+          XPoint pts[p->p[pp].iq_count];
+          int count = p->p[pp].iq_count;
+          for (i = 0; i < count; i++) {
+            pts[i].x = p->p[pp].iqbuf[2*i]*p->zoom/20+50;
+            pts[i].y = -p->p[pp].iqbuf[2*i+1]*p->zoom/20+50;
+          }
+          XDrawPoints(p->d, p->px, p->p[pp].g, pts, count, CoordModeOrigin);
+        }
+      }
+
+      if (pthread_mutex_unlock(&p->lock)) abort();
+    }
+
+    if (redraw) {
+      redraw = 0;
+      XCopyArea(p->d, p->px, p->w, DefaultGC(p->d, DefaultScreen(p->d)),
+                0, 0, p->width, p->height, 0, 0);
+    }
+
+    XFlush(p->d);
+
+    FD_ZERO(&rset);
+    FD_SET(p->timer_pipe[0], &rset);
+    FD_SET(xfd, &rset);
+    if (select(maxfd+1, &rset, NULL, NULL, NULL) == -1) abort();
+    if (FD_ISSET(p->timer_pipe[0], &rset)) {
+      char b[512];
+      if (read(p->timer_pipe[0], b, 512) <= 0) abort();
+      replot = 1;
+    }
+  }
+
+  return NULL;
+}
+
+void *make_plot(int width, int height, char *title, int nplots, ...)
+{
+  plot *p;
+  Display *d;
+  Window w;
+  Pixmap pm;
+  int i;
+  va_list ap;
+  XGCValues gcv;
+
+  p = malloc(sizeof(*p)); if (p == NULL) abort();
+
+  d = XOpenDisplay(0); if (d == NULL) abort();
+  w = XCreateSimpleWindow(d, DefaultRootWindow(d), 0, 0, width, height,
+        0, WhitePixel(d, DefaultScreen(d)), WhitePixel(d, DefaultScreen(d)));
+  XSelectInput(d, w, ExposureMask | ButtonPressMask);
+  XMapWindow(d, w);
+
+  {
+    XSetWindowAttributes att;
+    att.backing_store = Always;
+    XChangeWindowAttributes(d, w, CWBackingStore, &att);
+  }
+
+  XStoreName(d, w, title);
+
+  p->bg = XCreateGC(d, w, 0, NULL);
+  XCopyGC(d, DefaultGC(d, DefaultScreen(d)), -1L, p->bg);
+  gcv.foreground = WhitePixel(d, DefaultScreen(d));
+  XChangeGC(d, p->bg, GCForeground, &gcv);
+
+  pm = XCreatePixmap(d, w, width, height, DefaultDepth(d, DefaultScreen(d)));
+
+  p->width = width;
+  p->height = height;
+  p->p = malloc(nplots * sizeof(data)); if (p->p == NULL) abort();
+
+  va_start(ap, nplots);
+  for (i = 0; i < nplots; i++) {
+    int count;
+    int type;
+    char *color;
+    XColor rcol, scol;
+
+    count = va_arg(ap, int);
+    type = va_arg(ap, int);
+    color = va_arg(ap, char *);
+
+    p->p[i].g = XCreateGC(d, w, 0, NULL);
+    XCopyGC(d, DefaultGC(d, DefaultScreen(d)), -1L, p->p[i].g);
+    if (XAllocNamedColor(d, DefaultColormap(d, DefaultScreen(d)),
+                         color, &scol, &rcol)) {
+      gcv.foreground = scol.pixel;
+      XChangeGC(d, p->p[i].g, GCForeground, &gcv);
+    } else {
+      printf("could not allocate color '%s'\n", color);
+      abort();
+    }
+
+    if (type == PLOT_VS_TIME) {
+      p->p[i].buf = malloc(sizeof(float) * count);
+      if (p->p[i].buf == NULL) abort();
+      p->p[i].iqbuf = malloc(sizeof(short) * count * 2);
+      if(p->p[i].iqbuf==NULL)abort();
+    } else if (type == PLOT_MINMAX) {
+      p->p[i].buf = malloc(sizeof(float) * count);
+      if (p->p[i].buf == NULL) abort();
+      p->p[i].iqbuf = NULL;
+    } else {
+      p->p[i].buf = NULL;
+      p->p[i].iqbuf = malloc(sizeof(short) * count * 2);
+      if(p->p[i].iqbuf==NULL)abort();
+    }
+    p->p[i].count = count;
+    p->p[i].type = type;
+    p->p[i].iq_count = 0;
+    p->p[i].iq_insert_pos = 0;
+  }
+  va_end(ap);
+
+  p->d = d;
+  p->w = w;
+  p->px = pm;
+
+  p->zoom = 1;
+  p->nplots = nplots;
+
+  pthread_mutex_init(&p->lock, NULL);
+
+  if (pipe(p->timer_pipe)) abort();
+
+  new_thread(plot_thread, p);
+  new_thread(timer_thread, p);
+
+  return p;
+}
+
+void plot_set(void *_plot, float *data, int len, int pos, int pp)
+{
+  plot *p = _plot;
+  if (pthread_mutex_lock(&p->lock)) abort();
+  memcpy(p->p[pp].buf + pos, data, len * sizeof(float));
+  if (pthread_mutex_unlock(&p->lock)) abort();
+}
+
+void iq_plot_set(void *_plot, short *data, int count, int pos, int pp)
+{
+  plot *p = _plot;
+  if (pthread_mutex_lock(&p->lock)) abort();
+  memcpy(p->p[pp].iqbuf + pos * 2, data, count * 2 * sizeof(short));
+  if (pthread_mutex_unlock(&p->lock)) abort();
+}
+
+void iq_plot_set_sized(void *_plot, short *data, int count, int pp)
+{
+  plot *p = _plot;
+  if (pthread_mutex_lock(&p->lock)) abort();
+  memcpy(p->p[pp].iqbuf, data, count * 2 * sizeof(short));
+  p->p[pp].iq_count = count;
+  if (pthread_mutex_unlock(&p->lock)) abort();
+}
+
+void iq_plot_add_iq_point_loop(void *_plot, short i, short q, int pp)
+{
+  plot *p = _plot;
+  if (pthread_mutex_lock(&p->lock)) abort();
+  p->p[pp].iqbuf[p->p[pp].iq_insert_pos*2] = i;
+  p->p[pp].iqbuf[p->p[pp].iq_insert_pos*2+1] = q;
+  if (p->p[pp].iq_count != p->p[pp].count) p->p[pp].iq_count++;
+  p->p[pp].iq_insert_pos++;
+  if (p->p[pp].iq_insert_pos == p->p[pp].count) p->p[pp].iq_insert_pos = 0;
+  if (pthread_mutex_unlock(&p->lock)) abort();
+}
+
+void iq_plot_add_energy_point_loop(void *_plot, int e, int pp)
+{
+  plot *p = _plot;
+  if (pthread_mutex_lock(&p->lock)) abort();
+  p->p[pp].buf[p->p[pp].iq_insert_pos] = e;
+  if (p->p[pp].iq_count != p->p[pp].count) p->p[pp].iq_count++;
+  p->p[pp].iq_insert_pos++;
+  if (p->p[pp].iq_insert_pos == p->p[pp].count) p->p[pp].iq_insert_pos = 0;
+  if (pthread_mutex_unlock(&p->lock)) abort();
+}
diff --git a/common/utils/T/tracee/Makefile b/common/utils/T/tracee/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..703d49d029ae914638b0c1fd2ba7e56dbc349216
--- /dev/null
+++ b/common/utils/T/tracee/Makefile
@@ -0,0 +1,14 @@
+CC=gcc
+CFLAGS=-Wall -g -pthread -DT_TRACER -I.
+
+PROG=tracee
+OBJS=tracee.o ../T.o
+
+$(PROG): $(OBJS)
+	$(CC) $(CFLAGS) -o $(PROG) $(OBJS) -lrt
+
+tracee.o: tracee.c
+	$(CC) $(CFLAGS) -c -o $@ $<
+
+clean:
+	rm -f *.o $(PROG) core
diff --git a/common/utils/T/tracee/README b/common/utils/T/tracee/README
new file mode 100644
index 0000000000000000000000000000000000000000..f18dda85ac3ad3ad2e129acc7f986893655d93dd
--- /dev/null
+++ b/common/utils/T/tracee/README
@@ -0,0 +1 @@
+this is a very basic tracee, used to debug the tracer
diff --git a/common/utils/T/tracee/tracee.c b/common/utils/T/tracee/tracee.c
new file mode 100644
index 0000000000000000000000000000000000000000..86cf3012aa2df061d9596556db8e8ea7e6cd78cb
--- /dev/null
+++ b/common/utils/T/tracee/tracee.c
@@ -0,0 +1,16 @@
+#include "../T.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int main(void)
+{
+  int frame = 0;
+  T_connect_to_tracer("127.0.0.1", 2020);
+  while (1) {
+    getchar();
+    T(T_PUCCH_1AB_IQ, T_INT(0), T_INT(0), T_INT(frame), T_INT(0), T_INT(0), T_INT(0));
+    frame++;
+  }
+  return 0;
+}
diff --git a/common/utils/T/tracer/Makefile b/common/utils/T/tracer/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..bf0f72710cd04b4c4cb4a170a1a89fa51be5f2d8
--- /dev/null
+++ b/common/utils/T/tracer/Makefile
@@ -0,0 +1,47 @@
+CC=gcc
+CFLAGS=-Wall -g -pthread -DT_TRACER -I.
+
+#CFLAGS += -O3 -ffast-math -fomit-frame-pointer
+
+LIBS=-lX11 -lm -lpng -lXft
+
+all: textlog enb vcd
+
+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
+	$(CC) $(CFLAGS) -o textlog $^ $(LIBS)
+
+enb: utils.o enb.o database.o event.o handler.o config.o \
+         event_selector.o view/view.a gui/gui.a logger/logger.a \
+         filter/filter.a
+	$(CC) $(CFLAGS) -o enb $^ $(LIBS)
+
+vcd: utils.o vcd.o database.o event.o handler.o config.o \
+         event_selector.o view/view.a gui/gui.a logger/logger.a \
+         filter/filter.a
+	$(CC) $(CFLAGS) -o vcd $^ $(LIBS)
+
+.PHONY: all gui/gui.a view/view.a logger/logger.a filter/filter.a
+
+gui/gui.a:
+	cd gui && make
+
+view/view.a:
+	cd view && make
+
+logger/logger.a:
+	cd logger && make
+
+filter/filter.a:
+	cd filter && make
+
+%.o: %.c
+	$(CC) $(CFLAGS) -c -o $@ $<
+
+clean:
+	rm -f *.o core tracer_remote textlog enb vcd
+	cd gui && make clean
+	cd view && make clean
+	cd logger && make clean
+	cd filter && make clean
diff --git a/common/utils/T/tracer/config.c b/common/utils/T/tracer/config.c
new file mode 100644
index 0000000000000000000000000000000000000000..f88966ed79a111b2ab2d09d7110ae9da0c3781ea
--- /dev/null
+++ b/common/utils/T/tracer/config.c
@@ -0,0 +1,62 @@
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static char *local;
+static int local_size;
+static char *remote;
+static int remote_size;
+
+static char *PUT(char *to, int tosize, char c)
+{
+  if ((tosize & 4095) == 0) {
+    to = realloc(to, tosize + 4096); if (to == NULL) abort();
+  }
+  to[tosize] = c;
+  return to;
+}
+
+void  clear_remote_config(void)
+{
+  free(remote);
+  remote = NULL;
+  remote_size = 0;
+}
+
+void append_received_config_chunk(char *buf, int length)
+{
+  int buflen = *(int *)buf;
+  if (buflen != length - sizeof(int)) {
+    printf("ERROR: bad trace -1, should not happen...\n");
+    abort();
+  }
+  buf += sizeof(int);
+  while (buflen) {
+    remote = PUT(remote, remote_size, *buf);
+    remote_size++;
+    buf++;
+    buflen--;
+  }
+}
+
+void load_config_file(char *filename)
+{
+  int c;
+  FILE *f = fopen(filename, "r");
+  if (f == NULL) { perror(filename); abort(); }
+  while (1) {
+    c = fgetc(f); if (c == EOF) break;
+    local = PUT(local, local_size, c);
+    local_size++;
+  }
+  fclose(f);
+}
+
+void verify_config(void)
+{
+  if (local_size != remote_size || memcmp(local, remote, local_size) != 0) {
+    printf("ERROR: local and remote T_messages.txt not identical\n");
+    abort();
+  }
+}
diff --git a/common/utils/T/tracer/config.h b/common/utils/T/tracer/config.h
new file mode 100644
index 0000000000000000000000000000000000000000..f90869eb6621f89c5be80fc012e4edc7102a4652
--- /dev/null
+++ b/common/utils/T/tracer/config.h
@@ -0,0 +1,9 @@
+#ifndef _CONFIG_H_
+#define _CONFIG_H_
+
+void clear_remote_config(void);
+void append_received_config_chunk(char *buf, int length);
+void load_config_file(char *filename);
+void verify_config(void);
+
+#endif /* _CONFIG_H_ */
diff --git a/common/utils/T/tracer/database.c b/common/utils/T/tracer/database.c
new file mode 100644
index 0000000000000000000000000000000000000000..f0908948e23cb81ac2809faa14b8856f53492075
--- /dev/null
+++ b/common/utils/T/tracer/database.c
@@ -0,0 +1,503 @@
+#include "database.h"
+#include "utils.h"
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+
+typedef struct {
+  char *name;
+  char *desc;
+  char **groups;
+  int  gsize;
+  char **arg_type;
+  char **arg_name;
+  int  asize;
+  int  id;
+} id;
+
+typedef struct {
+  char *name;
+  char **ids;
+  int size;
+} group;
+
+typedef struct {
+  char *name;
+  id *i;
+  int isize;
+  group *g;
+  int gsize;
+  int *id_to_pos;
+} database;
+
+typedef struct {
+  char *data;
+  int size;
+  int maxsize;
+} buffer;
+
+typedef struct {
+  buffer name;
+  buffer value;
+} parser;
+
+void put(buffer *b, int c)
+{
+  if (b->size == b->maxsize) {
+    b->maxsize += 256;
+    b->data = realloc(b->data, b->maxsize);
+    if (b->data == NULL) { printf("memory allocation error\n"); exit(1); }
+  }
+  b->data[b->size] = c;
+  b->size++;
+}
+
+void smash_spaces(FILE *f)
+{
+  int c;
+  while (1) {
+    c = fgetc(f);
+    if (isspace(c)) continue;
+    if (c == ' ') continue;
+    if (c == '\t') continue;
+    if (c == '\n') continue;
+    if (c == 10 || c == 13) continue;
+    if (c == '#') {
+      while (1) {
+        c = fgetc(f);
+        if (c == '\n' || c == EOF) break;
+      }
+      continue;
+    }
+    break;
+  }
+  if (c != EOF) ungetc(c, f);
+}
+
+void get_line(parser *p, FILE *f, char **name, char **value)
+{
+  int c;
+  p->name.size = 0;
+  p->value.size = 0;
+  *name = NULL;
+  *value = NULL;
+  smash_spaces(f);
+  c = fgetc(f);
+  while (!(c == '=' || isspace(c) || c == EOF))
+    { put(&p->name, c); c = fgetc(f); }
+  if (c == EOF) return;
+  put(&p->name, 0);
+  while (!(c == EOF || c == '=')) c = fgetc(f);
+  if (c == EOF) return;
+  smash_spaces(f);
+  c = fgetc(f);
+  while (!(c == 10 || c == 13 || c == EOF))
+    { put(&p->value, c); c = fgetc(f); }
+  put(&p->value, 0);
+  if (p->name.size <= 1) return;
+  if (p->value.size <= 1) return;
+  *name = p->name.data;
+  *value = p->value.data;
+}
+
+int group_cmp(const void *_p1, const void *_p2)
+{
+  const group *p1 = _p1;
+  const group *p2 = _p2;
+  return strcmp(p1->name, p2->name);
+}
+
+int id_cmp(const void *_p1, const void *_p2)
+{
+  const id *p1 = _p1;
+  const id *p2 = _p2;
+  return strcmp(p1->name, p2->name);
+}
+
+int string_cmp(const void *_p1, const void *_p2)
+{
+  char * const *p1 = _p1;
+  char * const *p2 = _p2;
+  return strcmp(*p1, *p2);
+}
+
+id *add_id(database *r, char *idname, int i)
+{
+  if (bsearch(&(id){name:idname}, r->i, r->isize, sizeof(id), id_cmp) != NULL)
+    { printf("ERROR: ID '%s' declared more than once\n", idname); exit(1); }
+  if ((r->isize & 1023) == 0) {
+    r->i = realloc(r->i, (r->isize + 1024) * sizeof(id));
+    if (r->i == NULL) { printf("out of memory\n"); exit(1); }
+  }
+  r->i[r->isize].name = strdup(idname);
+  if (r->i[r->isize].name == NULL) { printf("out of memory\n"); exit(1); }
+  r->i[r->isize].desc = NULL;
+  r->i[r->isize].groups = NULL;
+  r->i[r->isize].gsize = 0;
+  r->i[r->isize].arg_type = NULL;
+  r->i[r->isize].arg_name = NULL;
+  r->i[r->isize].asize = 0;
+  r->i[r->isize].id = i;
+  r->isize++;
+  qsort(r->i, r->isize, sizeof(id), id_cmp);
+  return (id*)bsearch(&(id){name:idname}, r->i, r->isize, sizeof(id), id_cmp);
+}
+
+group *get_group(database *r, char *group_name)
+{
+  group *ret;
+
+  ret = bsearch(&(group){name:group_name},
+                r->g, r->gsize, sizeof(group), group_cmp);
+  if (ret != NULL) return ret;
+
+  if ((r->gsize & 1023) == 0) {
+    r->g = realloc(r->g, (r->gsize + 1024) * sizeof(group));
+    if (r->g == NULL) abort();
+  }
+  r->g[r->gsize].name = strdup(group_name);
+  if (r->g[r->gsize].name == NULL) abort();
+  r->g[r->gsize].ids = NULL;
+  r->g[r->gsize].size = 0;
+  r->gsize++;
+
+  qsort(r->g, r->gsize, sizeof(group), group_cmp);
+
+  return bsearch(&(group){name:group_name},
+                 r->g, r->gsize, sizeof(group), group_cmp);
+}
+
+void group_add_id(group *g, char *id)
+{
+  if ((g->size & 1023) == 0) {
+    g->ids = realloc(g->ids, (g->size + 1024) * sizeof(char *));
+    if (g->ids == NULL) abort();
+  }
+  g->ids[g->size] = id;
+  g->size++;
+}
+
+void id_add_group(id *i, char *group)
+{
+  char *g = bsearch(&group, i->groups, i->gsize, sizeof(char *), string_cmp);
+  if (g != NULL) return;
+
+  if ((i->gsize & 1023) == 0) {
+    i->groups = realloc(i->groups, (i->gsize+1024) * sizeof(char *));
+    if (i->groups == NULL) abort();
+  }
+  i->groups[i->gsize] = group;
+  i->gsize++;
+  qsort(i->groups, i->gsize, sizeof(char *), string_cmp);
+}
+
+void add_groups(database *r, id *i, char *groups)
+{
+  group *g;
+  if (i == NULL) {printf("ERROR: GROUP line before ID line\n");exit(1);}
+  while (1) {
+    char *start = groups;
+    char *end = start;
+    while (!isspace(*end) && *end != ':' && *end != 0) end++;
+    if (end == start) {
+      printf("bad group line: groups are seperated by ':'\n");
+      abort();
+    }
+    if (*end == 0) end = NULL; else *end = 0;
+
+    g = get_group(r, start);
+    group_add_id(g, i->name);
+    id_add_group(i, g->name);
+
+    if (end == NULL) break;
+    end++;
+    while ((isspace(*end) || *end == ':') && *end != 0) end++;
+    if (*end == 0) break;
+    groups = end;
+  }
+}
+
+void add_desc(id *i, char *desc)
+{
+  if (i == NULL) {printf("ERROR: DESC line before ID line\n");exit(1);}
+  i->desc = strdup(desc); if (i->desc == NULL) abort();
+}
+
+char *format_get_next_token(char **cur)
+{
+  char *start;
+  char *end;
+  char *ret;
+
+  start = *cur;
+
+  /* remove spaces */
+  while (*start && isspace(*start)) start ++;
+  if (*start == 0) return NULL;
+
+  /* special cases: ',' and ':' */
+  if (*start == ',' || *start == ':') { end = start + 1; goto special; }
+
+  end = start;
+
+  /* go to end of token */
+  while (*end && !isspace(*end) && *end != ',' && *end != ':') end++;
+
+special:
+  ret = malloc(end-start+1); if (ret == NULL) abort();
+  memcpy(ret, start, end-start);
+  ret[end-start] = 0;
+
+  *cur = end;
+  return ret;
+}
+
+void add_format(id *id, char *format)
+{
+  char *cur = format;
+  char *type;
+  char *name;
+  char *sep;
+  while (1) {
+    /* parse next type/name: expected " <type> , <name> :" */
+    /* get type */
+    type = format_get_next_token(&cur);
+    if (type == NULL) break;
+    /* get comma */
+    sep = format_get_next_token(&cur);
+    if (sep == NULL || strcmp(sep, ",") != 0) goto error;
+    free(sep);
+    /* get name */
+    name = format_get_next_token(&cur);
+    if (name == NULL) goto error;
+    /* if there is a next token it has to be : */
+    sep = format_get_next_token(&cur);
+    if (sep != NULL && strcmp(sep, ":") != 0) goto error;
+    free(sep);
+
+    /* add type/name */
+    if (id->asize % 64 == 0) {
+      id->arg_type = realloc(id->arg_type, (id->asize + 64) * sizeof(char *));
+      if (id->arg_type == NULL) abort();
+      id->arg_name = realloc(id->arg_name, (id->asize + 64) * sizeof(char *));
+      if (id->arg_name == NULL) abort();
+    }
+    id->arg_type[id->asize] = type;
+    id->arg_name[id->asize] = name;
+    id->asize++;
+  }
+  return;
+
+error:
+  printf("bad format '%s'\n", format);
+  abort();
+}
+
+void *parse_database(char *filename)
+{
+  FILE *in;
+  parser p;
+  database *r;
+  char *name, *value;
+  id *last_id = NULL;
+  int i;
+
+  r = calloc(1, sizeof(*r)); if (r == NULL) abort();
+  memset(&p, 0, sizeof(p));
+
+  r->name = strdup(filename); if (r->name == NULL) abort();
+
+  in = fopen(filename, "r"); if (in == NULL) { perror(filename); abort(); }
+
+  i = 0;
+
+  while (1) {
+    get_line(&p, in, &name, &value);
+    if (name == NULL) break;
+//printf("%s %s\n", name, value);
+    if (!strcmp(name, "ID")) { last_id = add_id(r, value, i); i++; }
+    if (!strcmp(name, "GROUP")) add_groups(r, last_id, value);
+    if (!strcmp(name, "DESC")) add_desc(last_id, value);
+    if (!strcmp(name, "FORMAT")) add_format(last_id, value);
+  }
+
+  fclose(in);
+  free(p.name.data);
+  free(p.value.data);
+
+  /* setup id_to_pos */
+  r->id_to_pos = malloc(sizeof(int) * r->isize);
+  if (r->id_to_pos == NULL) abort();
+  for (i = 0; i < r->isize; i++)
+    r->id_to_pos[r->i[i].id] = i;
+
+  return r;
+}
+
+void dump_database(void *_d)
+{
+  database *d = _d;
+  int i;
+
+  printf("database %s: %d IDs, %d GROUPs\n", d->name, d->isize, d->gsize);
+  for (i = 0; i < d->isize; i++) {
+    int j;
+    printf("ID %s [%s] [in %d group%s]\n",
+           d->i[i].name, d->i[i].desc ? d->i[i].desc : "",
+           d->i[i].gsize, d->i[i].gsize > 1 ? "s" : "");
+    for (j = 0; j < d->i[i].gsize; j++)
+      printf("    in GROUP: %s\n", d->i[i].groups[j]);
+    if (d->i[i].asize == 0) printf("    no FORMAT\n");
+    else {
+      int j;
+      printf("    FORMAT: ");
+      for (j = 0; j < d->i[i].asize; j++)
+        printf("'%s' , '%s' %s", d->i[i].arg_type[j], d->i[i].arg_name[j],
+               j == d->i[i].asize-1 ? "" : " : ");
+      printf("\n");
+    }
+  }
+  for (i = 0; i < d->gsize; i++) {
+    int j;
+    printf("GROUP %s [size %d]\n", d->g[i].name, d->g[i].size);
+    for (j = 0; j < d->g[i].size; j++)
+      printf("  contains ID: %s\n", d->g[i].ids[j]);
+  }
+}
+
+void list_ids(void *_d)
+{
+  database *d = _d;
+  int i;
+  for (i = 0; i < d->isize; i++) printf("%s\n", d->i[i].name);
+}
+
+void list_groups(void *_d)
+{
+  database *d = _d;
+  int i;
+  for (i = 0; i < d->gsize; i++) printf("%s\n", d->g[i].name);
+}
+
+static int onoff_id(database *d, char *name, int *a, int onoff)
+{
+  id *i;
+  i = bsearch(&(id){name:name}, d->i, d->isize, sizeof(id), id_cmp);
+  if (i == NULL) return 0;
+  a[i->id] = onoff;
+  printf("turning %s %s\n", onoff ? "ON" : "OFF", name);
+  return 1;
+}
+
+static int onoff_group(database *d, char *name, int *a, int onoff)
+{
+  group *g;
+  int i;
+  g = bsearch(&(group){name:name}, d->g, d->gsize, sizeof(group), group_cmp);
+  if (g == NULL) return 0;
+  for (i = 0; i < g->size; i++) onoff_id(d, g->ids[i], a, onoff);
+  return 1;
+}
+
+void on_off(void *_d, char *item, int *a, int onoff)
+{
+  int done;
+  database *d = _d;
+  int i;
+  if (item == NULL) {
+    for (i = 0; i < d->isize; i++) a[i] = onoff;
+    printf("turning %s all traces\n", onoff ? "ON" : "OFF");
+    return;
+  }
+  done = onoff_group(d, item, a, onoff);
+  done += onoff_id(d, item, a, onoff);
+  if (done == 0) {
+    printf("ERROR: ID/group '%s' not found in database\n", item);
+    exit(1);
+  }
+}
+
+char *event_name_from_id(void *_database, int id)
+{
+  database *d = _database;
+  return d->i[d->id_to_pos[id]].name;
+}
+
+int event_id_from_name(void *_database, char *name)
+{
+  database *d = _database;
+  id *i = (id*)bsearch(&(id){name:name}, d->i, d->isize, sizeof(id), id_cmp);
+  if (i == NULL)
+    { printf("%s:%d: '%s' not found\n", __FILE__, __LINE__, name); abort(); }
+  return i->id;
+}
+
+database_event_format get_format(void *_database, int event_id)
+{
+  database *d = _database;
+  database_event_format ret;
+
+  if (event_id < 0 || event_id >= d->isize) {
+    printf("%s:%d: bad event ID (%d)\n", __FILE__, __LINE__, event_id);
+    abort();
+  }
+
+  ret.type = d->i[d->id_to_pos[event_id]].arg_type;
+  ret.name = d->i[d->id_to_pos[event_id]].arg_name;
+  ret.count = d->i[d->id_to_pos[event_id]].asize;
+
+  return ret;
+}
+
+int number_of_ids(void *_d)
+{
+  database *d = _d;
+  return d->isize;
+}
+
+int database_get_ids(void *_d, char ***ids)
+{
+  database *d = _d;
+  int i;
+  *ids = malloc(d->isize * sizeof(char **)); if (*ids == NULL) abort();
+  for (i = 0; i < d->isize; i++) (*ids)[i] = d->i[i].name;
+  return d->isize;
+}
+
+int database_get_groups(void *_d, char ***groups)
+{
+  database *d = _d;
+  int i;
+  *groups = malloc(d->gsize * sizeof(char **)); if (*groups == NULL) abort();
+  for (i = 0; i < d->gsize; i++) (*groups)[i] = d->g[i].name;
+  return d->gsize;
+}
+
+int database_pos_to_id(void *_d, int pos)
+{
+  database *d = _d;
+  return d->i[pos].id;
+}
+
+void database_get_generic_description(void *_d, int id,
+    char **name, char **desc)
+{
+  database *d = _d;
+  int pos = d->id_to_pos[id];
+  OBUF o;
+  int i;
+  *name = strdup(d->i[pos].name); if (*name == NULL) abort();
+  o.osize = o.omaxsize = 0;
+  o.obuf = NULL;
+  PUTS(&o, *name);
+  for (i = 0; i < d->i[pos].asize; i++) {
+    PUTC(&o, ' ');
+    PUTS(&o, d->i[pos].arg_name[i]);
+    PUTS(&o, " [");
+    PUTS(&o, d->i[pos].arg_name[i]);
+    PUTS(&o, "]");
+  }
+  PUTC(&o, 0);
+  *desc = o.obuf;
+}
diff --git a/common/utils/T/tracer/database.h b/common/utils/T/tracer/database.h
new file mode 100644
index 0000000000000000000000000000000000000000..c5cab05d9f7be0da0cc8d50fcf3fda243aec556a
--- /dev/null
+++ b/common/utils/T/tracer/database.h
@@ -0,0 +1,31 @@
+#ifndef _DATABASE_H_
+#define _DATABASE_H_
+
+/* returns an opaque pointer - truly a 'database *', see database.c */
+void *parse_database(char *filename);
+void dump_database(void *database);
+void list_ids(void *database);
+void list_groups(void *database);
+void on_off(void *d, char *item, int *a, int onoff);
+char *event_name_from_id(void *database, int id);
+int event_id_from_name(void *database, char *name);
+int number_of_ids(void *database);
+int database_get_ids(void *database, char ***ids);
+int database_get_groups(void *database, char ***groups);
+int database_pos_to_id(void *database, int pos);
+void database_get_generic_description(void *database, int id,
+    char **name, char **desc);
+
+/****************************************************************************/
+/* get format of an event                                                   */
+/****************************************************************************/
+
+typedef struct {
+  char **type;
+  char **name;
+  int count;
+} database_event_format;
+
+database_event_format get_format(void *database, int event_id);
+
+#endif /* _DATABASE_H_ */
diff --git a/common/utils/T/tracer/defs.h b/common/utils/T/tracer/defs.h
new file mode 100644
index 0000000000000000000000000000000000000000..443fabdde8423ed744d1739f54acc82cef1c5c50
--- /dev/null
+++ b/common/utils/T/tracer/defs.h
@@ -0,0 +1,24 @@
+#ifndef _TRACER_DEFS_H_
+#define _TRACER_DEFS_H_
+
+/* types of plots */
+#define PLOT_VS_TIME   0
+#define PLOT_IQ_POINTS 1
+#define PLOT_MINMAX    2
+
+void new_thread(void *(*f)(void *), void *data);
+
+/* ... is { int count; int type; char *color; } for 'nplots' plots */
+void *make_plot(int width, int height, char *title, int nplots, ...);
+void plot_set(void *plot, float *data, int len, int pos, int pp);
+void iq_plot_set(void *plot, short *data, int len, int pos, int pp);
+void iq_plot_set_sized(void *_plot, short *data, int len, int pp);
+void iq_plot_add_iq_point_loop(void *_plot, short i, short q, int pp);
+void iq_plot_add_energy_point_loop(void *_plot, int e, int pp);
+
+/* T gui functions */
+void t_gui_start(void);
+void t_gui_set_input_signal(int eNB, int frame, int subframe, int antenna,
+    int size, void *buf);
+
+#endif /* _TRACER_DEFS_H_ */
diff --git a/common/utils/T/tracer/enb.c b/common/utils/T/tracer/enb.c
new file mode 100644
index 0000000000000000000000000000000000000000..8e8f3d8e7277844028d50c2951974e1363cb098d
--- /dev/null
+++ b/common/utils/T/tracer/enb.c
@@ -0,0 +1,573 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <signal.h>
+#include "database.h"
+#include "event.h"
+#include "handler.h"
+#include "logger/logger.h"
+#include "view/view.h"
+#include "gui/gui.h"
+#include "filter/filter.h"
+#include "utils.h"
+#include "../T_defs.h"
+#include "event_selector.h"
+#include "openair_logo.h"
+#include "config.h"
+
+typedef struct {
+  view *phyview;
+  view *macview;
+  view *rlcview;
+  view *pdcpview;
+  view *rrcview;
+  view *legacy;
+} enb_gui;
+
+typedef struct {
+  int socket;
+  int *is_on;
+  int nevents;
+  pthread_mutex_t lock;
+} enb_data;
+
+#define DEFAULT_REMOTE_IP "127.0.0.1"
+#define DEFAULT_REMOTE_PORT 2021
+
+void is_on_changed(void *_d)
+{
+  enb_data *d = _d;
+  char t;
+
+  if (pthread_mutex_lock(&d->lock)) abort();
+
+  if (d->socket == -1) goto no_connection;
+
+  t = 1;
+  if (socket_send(d->socket, &t, 1) == -1 ||
+      socket_send(d->socket, &d->nevents, sizeof(int)) == -1 ||
+      socket_send(d->socket, d->is_on, d->nevents * sizeof(int)) == -1)
+    goto connection_dies;
+
+no_connection:
+  if (pthread_mutex_unlock(&d->lock)) abort();
+  return;
+
+connection_dies:
+  close(d->socket);
+  d->socket = -1;
+  if (pthread_mutex_unlock(&d->lock)) abort();
+}
+
+void usage(void)
+{
+  printf(
+"options:\n"
+"    -d <database file>        this option is mandatory\n"
+"    -on <GROUP or ID>         turn log ON for given GROUP or ID\n"
+"    -off <GROUP or ID>        turn log OFF for given GROUP or ID\n"
+"    -ON                       turn all logs ON\n"
+"    -OFF                      turn all logs OFF\n"
+"                              note: you may pass several -on/-off/-ON/-OFF,\n"
+"                                    they will be processed in order\n"
+"                                    by default, all is off\n"
+"    -ip <host>                connect to given IP address (default %s)\n"
+"    -p <port>                 connect to given port (default %d)\n"
+"    -debug-gui                activate GUI debug logs\n",
+  DEFAULT_REMOTE_IP,
+  DEFAULT_REMOTE_PORT
+  );
+  exit(1);
+}
+
+static void *gui_thread(void *_g)
+{
+  gui *g = _g;
+  gui_loop(g);
+  return NULL;
+}
+
+static filter *ticktime_filter(void *database, char *event, int i)
+{
+  /* filter is "harq_pid == i && UE_id == 0 && eNB_id == 0" */
+  return
+    filter_and(
+      filter_eq(filter_evarg(database, event, "harq_pid"), filter_int(i)),
+      filter_and(
+        filter_eq(filter_evarg(database, event, "UE_id"), filter_int(0)),
+        filter_eq(filter_evarg(database, event, "eNB_ID"), filter_int(0))));
+}
+
+static void enb_main_gui(enb_gui *e, gui *g, event_handler *h, void *database)
+{
+  widget *main_window;
+  widget *top_container;
+  widget *line, *col;
+  widget *logo;
+  widget *input_signal_plot;
+  logger *input_signal_log;
+  view *input_signal_view;
+  widget *timeline_plot;
+  logger *timelog;
+  view *timeview;
+  view *subview;
+  widget *text;
+  view *textview;
+  int i;
+
+  main_window = new_toplevel_window(g, 800, 600, "eNB tracer");
+  top_container = new_container(g, VERTICAL);
+  widget_add_child(g, main_window, top_container, -1);
+
+  line = new_container(g, HORIZONTAL);
+  widget_add_child(g, top_container, line, -1);
+  logo = new_image(g, openair_logo_png, openair_logo_png_len);
+  widget_add_child(g, line, logo, -1);
+  input_signal_plot = new_xy_plot(g, 256, 55, "input signal", 20);
+  widget_add_child(g, line, input_signal_plot, -1);
+  xy_plot_set_range(g, input_signal_plot, 0, 7680*10, 20, 70);
+  input_signal_log = new_framelog(h, database,
+      "ENB_INPUT_SIGNAL", "subframe", "rxdata");
+  /* a skip value of 10 means to process 1 frame over 10, that is
+   * more or less 10 frames per second
+   */
+  framelog_set_skip(input_signal_log, 10);
+  input_signal_view = new_view_xy(7680*10, 10,
+      g, input_signal_plot, new_color(g, "#0c0c72"));
+  logger_add_view(input_signal_log, input_signal_view);
+
+  /* downlink/uplink UE DCIs */
+  widget_add_child(g, top_container,
+      new_label(g,"DL/UL TICK/DCI/ACK/NACK "), -1);
+  line = new_container(g, HORIZONTAL);
+  widget_add_child(g, top_container, line, -1);
+  timeline_plot = new_timeline(g, 512, 8, 5);
+  widget_add_child(g, line, timeline_plot, -1);
+  container_set_child_growable(g, line, timeline_plot, 1);
+  for (i = 0; i < 8; i++)
+    timeline_set_subline_background_color(g, timeline_plot, i,
+        new_color(g, i==0 || i==4 ? "#aaf" : "#eee"));
+  timeview = new_view_time(3600, 10, g, timeline_plot);
+  /* DL tick logging */
+  timelog = new_timelog(h, database, "ENB_DL_TICK");
+  subview = new_subview_time(timeview, 0, new_color(g, "#77c"), 3600*1000);
+  logger_add_view(timelog, subview);
+  /* DL DCI logging */
+  timelog = new_timelog(h, database, "ENB_DLSCH_UE_DCI");
+  subview = new_subview_time(timeview, 1, new_color(g, "#228"), 3600*1000);
+  logger_add_view(timelog, subview);
+  /* DL ACK */
+  timelog = new_timelog(h, database, "ENB_DLSCH_UE_ACK");
+  subview = new_subview_time(timeview, 2, new_color(g, "#282"), 3600*1000);
+  logger_add_view(timelog, subview);
+  /* DL NACK */
+  timelog = new_timelog(h, database, "ENB_DLSCH_UE_NACK");
+  subview = new_subview_time(timeview, 3, new_color(g, "#f22"), 3600*1000);
+  logger_add_view(timelog, subview);
+
+  /* UL tick logging */
+  timelog = new_timelog(h, database, "ENB_UL_TICK");
+  subview = new_subview_time(timeview, 4, new_color(g, "#77c"), 3600*1000);
+  logger_add_view(timelog, subview);
+  /* UL DCI logging */
+  timelog = new_timelog(h, database, "ENB_ULSCH_UE_DCI");
+  subview = new_subview_time(timeview, 5, new_color(g, "#228"), 3600*1000);
+  logger_add_view(timelog, subview);
+  /* UL retransmission without DCI logging */
+  timelog = new_timelog(h, database, "ENB_ULSCH_UE_NO_DCI_RETRANSMISSION");
+  subview = new_subview_time(timeview, 5, new_color(g, "#f22"), 3600*1000);
+  logger_add_view(timelog, subview);
+  /* UL ACK */
+  timelog = new_timelog(h, database, "ENB_ULSCH_UE_ACK");
+  subview = new_subview_time(timeview, 6, new_color(g, "#282"), 3600*1000);
+  logger_add_view(timelog, subview);
+  /* UL NACK */
+  timelog = new_timelog(h, database, "ENB_ULSCH_UE_NACK");
+  subview = new_subview_time(timeview, 7, new_color(g, "#f22"), 3600*1000);
+  logger_add_view(timelog, subview);
+
+  /* harq processes' ticktime view */
+  widget_add_child(g, top_container,
+      new_label(g,"DL/UL HARQ (x8) "), -1);
+  line = new_container(g, HORIZONTAL);
+  widget_add_child(g, top_container, line, -1);
+  timeline_plot = new_timeline(g, 512, 2*8+2, 3);
+  widget_add_child(g, line, timeline_plot, -1);
+  container_set_child_growable(g, line, timeline_plot, 1);
+  for (i = 0; i < 2*8+2; i++)
+    timeline_set_subline_background_color(g, timeline_plot, i,
+        new_color(g, i==0 || i==9 ? "#ddd" : (i%9)&1 ? "#e6e6e6" : "#eee"));
+  timeview = new_view_ticktime(10, g, timeline_plot);
+  ticktime_set_tick(timeview,
+      new_ticklog(h, database, "ENB_MASTER_TICK", "frame", "subframe"));
+  /* tick */
+  timelog = new_ticklog(h, database, "ENB_MASTER_TICK", "frame", "subframe");
+  /* tick on DL view */
+  subview = new_subview_ticktime(timeview, 0, new_color(g,"#bbb"), 3600*1000);
+  logger_add_view(timelog, subview);
+  /* tick on UL view */
+  subview = new_subview_ticktime(timeview, 9, new_color(g,"#bbb"), 3600*1000);
+  logger_add_view(timelog, subview);
+  /* DL harq pids */
+  for (i = 0; i < 8; i++) {
+    timelog = new_ticklog(h, database, "ENB_DLSCH_UE_DCI",
+        "frame", "subframe");
+    subview = new_subview_ticktime(timeview, i+1,
+        new_color(g,"#55f"), 3600*1000);
+    logger_add_view(timelog, subview);
+    logger_set_filter(timelog,
+        ticktime_filter(database, "ENB_DLSCH_UE_DCI", i));
+  }
+  /* DL ACK */
+  for (i = 0; i < 8; i++) {
+    timelog = new_ticklog(h, database, "ENB_DLSCH_UE_ACK",
+        "frame", "subframe");
+    subview = new_subview_ticktime(timeview, i+1,
+        new_color(g,"#282"), 3600*1000);
+    logger_add_view(timelog, subview);
+    logger_set_filter(timelog,
+        ticktime_filter(database, "ENB_DLSCH_UE_ACK", i));
+  }
+  /* DL NACK */
+  for (i = 0; i < 8; i++) {
+    timelog = new_ticklog(h, database, "ENB_DLSCH_UE_NACK",
+        "frame", "subframe");
+    subview = new_subview_ticktime(timeview, i+1,
+        new_color(g,"#f22"), 3600*1000);
+    logger_add_view(timelog, subview);
+    logger_set_filter(timelog,
+        ticktime_filter(database, "ENB_DLSCH_UE_NACK", i));
+  }
+  /* UL harq pids */
+  for (i = 0; i < 8; i++) {
+    /* first transmission */
+    timelog = new_ticklog(h, database, "ENB_ULSCH_UE_DCI",
+        "frame", "subframe");
+    subview = new_subview_ticktime(timeview, i+9+1,
+        new_color(g,"#55f"), 3600*1000);
+    logger_add_view(timelog, subview);
+    logger_set_filter(timelog,
+        ticktime_filter(database, "ENB_ULSCH_UE_DCI", i));
+    /* retransmission */
+    timelog = new_ticklog(h, database, "ENB_ULSCH_UE_NO_DCI_RETRANSMISSION",
+        "frame", "subframe");
+    subview = new_subview_ticktime(timeview, i+9+1,
+        new_color(g,"#99f"), 3600*1000);
+    logger_add_view(timelog, subview);
+    logger_set_filter(timelog,
+        ticktime_filter(database, "ENB_ULSCH_UE_NO_DCI_RETRANSMISSION", i));
+  }
+  /* UL ACK */
+  for (i = 0; i < 8; i++) {
+    timelog = new_ticklog(h, database, "ENB_ULSCH_UE_ACK",
+        "frame", "subframe");
+    subview = new_subview_ticktime(timeview, i+9+1,
+        new_color(g,"#282"), 3600*1000);
+    logger_add_view(timelog, subview);
+    logger_set_filter(timelog,
+        ticktime_filter(database, "ENB_ULSCH_UE_ACK", i));
+  }
+  /* UL NACK */
+  for (i = 0; i < 8; i++) {
+    timelog = new_ticklog(h, database, "ENB_ULSCH_UE_NACK",
+        "frame", "subframe");
+    subview = new_subview_ticktime(timeview, i+9+1,
+        new_color(g,"#f22"), 3600*1000);
+    logger_add_view(timelog, subview);
+    logger_set_filter(timelog,
+        ticktime_filter(database, "ENB_ULSCH_UE_NACK", i));
+  }
+
+  /* phy/mac/rlc/pdcp/rrc textlog */
+  line = new_container(g, HORIZONTAL);
+  widget_add_child(g, top_container, line, -1);
+  container_set_child_growable(g, top_container, line, 1);
+
+  /* phy */
+  col = new_container(g, VERTICAL);
+  widget_add_child(g, line, col, -1);
+  container_set_child_growable(g, line, col, 1);
+  widget_add_child(g, col, new_label(g, "PHY"), -1);
+  text = new_textlist(g, 100, 10, new_color(g, "#afa"));
+  widget_add_child(g, col, text, -1);
+  container_set_child_growable(g, col, text, 1);
+  textview = new_view_textlist(10000, 10, g, text);
+  e->phyview = textview;
+
+  /* mac */
+  col = new_container(g, VERTICAL);
+  widget_add_child(g, line, col, -1);
+  container_set_child_growable(g, line, col, 1);
+  widget_add_child(g, col, new_label(g, "MAC"), -1);
+  text = new_textlist(g, 100, 10, new_color(g, "#adf"));
+  widget_add_child(g, col, text, -1);
+  container_set_child_growable(g, col, text, 1);
+  textview = new_view_textlist(10000, 10, g, text);
+  e->macview = textview;
+
+  line = new_container(g, HORIZONTAL);
+  widget_add_child(g, top_container, line, -1);
+  container_set_child_growable(g, top_container, line, 1);
+
+  /* rlc */
+  col = new_container(g, VERTICAL);
+  widget_add_child(g, line, col, -1);
+  container_set_child_growable(g, line, col, 1);
+  widget_add_child(g, col, new_label(g, "RLC"), -1);
+  text = new_textlist(g, 100, 10, new_color(g, "#aff"));
+  widget_add_child(g, col, text, -1);
+  container_set_child_growable(g, col, text, 1);
+  textview = new_view_textlist(10000, 10, g, text);
+  e->rlcview = textview;
+
+  /* pdcp */
+  col = new_container(g, VERTICAL);
+  widget_add_child(g, line, col, -1);
+  container_set_child_growable(g, line, col, 1);
+  widget_add_child(g, col, new_label(g, "PDCP"), -1);
+  text = new_textlist(g, 100, 10, new_color(g, "#ed9"));
+  widget_add_child(g, col, text, -1);
+  container_set_child_growable(g, col, text, 1);
+  textview = new_view_textlist(10000, 10, g, text);
+  e->pdcpview = textview;
+
+  line = new_container(g, HORIZONTAL);
+  widget_add_child(g, top_container, line, -1);
+  container_set_child_growable(g, top_container, line, 1);
+
+  /* rrc */
+  col = new_container(g, VERTICAL);
+  widget_add_child(g, line, col, -1);
+  container_set_child_growable(g, line, col, 1);
+  widget_add_child(g, col, new_label(g, "RRC"), -1);
+  text = new_textlist(g, 100, 10, new_color(g, "#fdb"));
+  widget_add_child(g, col, text, -1);
+  container_set_child_growable(g, col, text, 1);
+  textview = new_view_textlist(10000, 10, g, text);
+  e->rrcview = textview;
+
+  /* legacy logs (LOG_I, LOG_D, ...) */
+  widget_add_child(g, top_container, new_label(g, "LEGACY"), -1);
+  text = new_textlist(g, 100, 10, new_color(g, "#eeb"));
+  widget_add_child(g, top_container, text, -1);
+  container_set_child_growable(g, top_container, text, 1);
+  e->legacy = new_view_textlist(10000, 10, g, text);
+}
+
+void view_add_log(view *v, char *log, event_handler *h, void *database,
+    int *is_on)
+{
+  logger *textlog;
+  char *name, *desc;
+
+  database_get_generic_description(database,
+      event_id_from_name(database, log), &name, &desc);
+  textlog = new_textlog(h, database, name, desc);
+  logger_add_view(textlog, v);
+  free(name);
+  free(desc);
+
+  on_off(database, log, is_on, 1);
+}
+
+int main(int n, char **v)
+{
+  extern int volatile gui_logd;
+  char *database_filename = NULL;
+  void *database;
+  char *ip = DEFAULT_REMOTE_IP;
+  int port = DEFAULT_REMOTE_PORT;
+  char **on_off_name;
+  int *on_off_action;
+  int on_off_n = 0;
+  int *is_on;
+  int number_of_events;
+  int i;
+  event_handler *h;
+  gui *g;
+  enb_gui eg;
+  enb_data enb_data;
+
+  /* write on a socket fails if the other end is closed and we get SIGPIPE */
+  if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) abort();
+
+  on_off_name = malloc(n * sizeof(char *)); if (on_off_name == NULL) abort();
+  on_off_action = malloc(n * sizeof(int)); if (on_off_action == NULL) abort();
+
+  for (i = 1; i < n; i++) {
+    if (!strcmp(v[i], "-h") || !strcmp(v[i], "--help")) usage();
+    if (!strcmp(v[i], "-d"))
+      { if (i > n-2) usage(); database_filename = v[++i]; continue; }
+    if (!strcmp(v[i], "-ip")) { if (i > n-2) usage(); ip = v[++i]; continue; }
+    if (!strcmp(v[i], "-p"))
+      { if (i > n-2) usage(); port = atoi(v[++i]); continue; }
+    if (!strcmp(v[i], "-on")) { if (i > n-2) usage();
+      on_off_name[on_off_n]=v[++i]; on_off_action[on_off_n++]=1; continue; }
+    if (!strcmp(v[i], "-off")) { if (i > n-2) usage();
+      on_off_name[on_off_n]=v[++i]; on_off_action[on_off_n++]=0; continue; }
+    if (!strcmp(v[i], "-ON"))
+      { on_off_name[on_off_n]=NULL; on_off_action[on_off_n++]=1; continue; }
+    if (!strcmp(v[i], "-OFF"))
+      { on_off_name[on_off_n]=NULL; on_off_action[on_off_n++]=0; continue; }
+    if (!strcmp(v[i], "-debug-gui")) { gui_logd = 1; continue; }
+    usage();
+  }
+
+  if (database_filename == NULL) {
+    printf("ERROR: provide a database file (-d)\n");
+    exit(1);
+  }
+
+  database = parse_database(database_filename);
+
+  load_config_file(database_filename);
+
+  number_of_events = number_of_ids(database);
+  is_on = calloc(number_of_events, sizeof(int));
+  if (is_on == NULL) abort();
+
+  h = new_handler(database);
+
+  g = gui_init();
+  new_thread(gui_thread, g);
+
+  enb_main_gui(&eg, g, h, database);
+
+  for (i = 0; i < number_of_events; i++) {
+    logger *textlog;
+    char *name, *desc;
+    database_get_generic_description(database, i, &name, &desc);
+    if (strncmp(name, "LEGACY_", 7) != 0) continue;
+    textlog = new_textlog(h, database, name, desc);
+    logger_add_view(textlog, eg.legacy);
+    free(name);
+    free(desc);
+  }
+
+  on_off(database, "ENB_INPUT_SIGNAL", is_on, 1);
+  on_off(database, "ENB_DL_TICK", is_on, 1);
+  on_off(database, "ENB_DLSCH_UE_DCI", is_on, 1);
+  on_off(database, "ENB_DLSCH_UE_ACK", is_on, 1);
+  on_off(database, "ENB_DLSCH_UE_NACK", is_on, 1);
+  on_off(database, "ENB_UL_TICK", is_on, 1);
+  on_off(database, "ENB_ULSCH_UE_DCI", is_on, 1);
+  on_off(database, "ENB_ULSCH_UE_NO_DCI_RETRANSMISSION", is_on, 1);
+  on_off(database, "ENB_ULSCH_UE_ACK", is_on, 1);
+  on_off(database, "ENB_ULSCH_UE_NACK", is_on, 1);
+  on_off(database, "ENB_MASTER_TICK", is_on, 1);
+
+  on_off(database, "LEGACY_RRC_INFO", is_on, 1);
+  on_off(database, "LEGACY_RRC_ERROR", is_on, 1);
+  on_off(database, "LEGACY_RRC_WARNING", is_on, 1);
+
+  view_add_log(eg.phyview, "ENB_DLSCH_UE_DCI", h, database, is_on);
+  view_add_log(eg.phyview, "ENB_DLSCH_UE_ACK", h, database, is_on);
+  view_add_log(eg.phyview, "ENB_DLSCH_UE_NACK", h, database, is_on);
+  view_add_log(eg.phyview, "ENB_ULSCH_UE_DCI", h, database, is_on);
+  view_add_log(eg.phyview, "ENB_ULSCH_UE_NO_DCI_RETRANSMISSION",
+      h, database, is_on);
+  view_add_log(eg.phyview, "ENB_ULSCH_UE_ACK", h, database, is_on);
+  view_add_log(eg.phyview, "ENB_ULSCH_UE_NACK", h, database, is_on);
+
+  view_add_log(eg.macview, "ENB_MAC_UE_DL_SDU", h, database, is_on);
+  view_add_log(eg.macview, "ENB_MAC_UE_UL_SCHEDULE", h, database, is_on);
+  view_add_log(eg.macview, "ENB_MAC_UE_UL_SCHEDULE_RETRANSMISSION",
+      h, database, is_on);
+  view_add_log(eg.macview, "ENB_MAC_UE_UL_PDU", h, database, is_on);
+  view_add_log(eg.macview, "ENB_MAC_UE_UL_SDU", h, database, is_on);
+  view_add_log(eg.macview, "ENB_MAC_UE_UL_CE", h, database, is_on);
+
+  view_add_log(eg.rlcview, "ENB_RLC_DL", h, database, is_on);
+  view_add_log(eg.rlcview, "ENB_RLC_UL", h, database, is_on);
+  view_add_log(eg.rlcview, "ENB_RLC_MAC_DL", h, database, is_on);
+  view_add_log(eg.rlcview, "ENB_RLC_MAC_UL", h, database, is_on);
+
+  view_add_log(eg.pdcpview, "ENB_PDCP_UL", h, database, is_on);
+  view_add_log(eg.pdcpview, "ENB_PDCP_DL", h, database, is_on);
+
+  view_add_log(eg.rrcview, "ENB_RRC_CONNECTION_SETUP_COMPLETE",
+      h, database, is_on);
+  view_add_log(eg.rrcview, "ENB_RRC_SECURITY_MODE_COMMAND",
+      h, database, is_on);
+  view_add_log(eg.rrcview, "ENB_RRC_UE_CAPABILITY_ENQUIRY",
+      h, database, is_on);
+  view_add_log(eg.rrcview, "ENB_RRC_CONNECTION_REJECT",
+      h, database, is_on);
+  view_add_log(eg.rrcview, "ENB_RRC_CONNECTION_REESTABLISHMENT_REJECT",
+      h, database, is_on);
+  view_add_log(eg.rrcview, "ENB_RRC_CONNECTION_RELEASE",
+      h, database, is_on);
+  view_add_log(eg.rrcview, "ENB_RRC_CONNECTION_RECONFIGURATION",
+      h, database, is_on);
+  view_add_log(eg.rrcview, "ENB_RRC_MEASUREMENT_REPORT",
+      h, database, is_on);
+  view_add_log(eg.rrcview, "ENB_RRC_HANDOVER_PREPARATION_INFORMATION",
+      h, database, is_on);
+  view_add_log(eg.rrcview, "ENB_RRC_CONNECTION_RECONFIGURATION_COMPLETE",
+      h, database, is_on);
+  view_add_log(eg.rrcview, "ENB_RRC_CONNECTION_SETUP",
+      h, database, is_on);
+  view_add_log(eg.rrcview, "ENB_RRC_UL_CCCH_DATA_IN",
+      h, database, is_on);
+  view_add_log(eg.rrcview, "ENB_RRC_UL_DCCH_DATA_IN",
+      h, database, is_on);
+  view_add_log(eg.rrcview, "ENB_RRC_SECURITY_MODE_COMPLETE",
+      h, database, is_on);
+  view_add_log(eg.rrcview, "ENB_RRC_SECURITY_MODE_FAILURE",
+      h, database, is_on);
+  view_add_log(eg.rrcview, "ENB_RRC_UE_CAPABILITY_INFORMATION",
+      h, database, is_on);
+  view_add_log(eg.rrcview, "ENB_RRC_CONNECTION_REQUEST",
+      h, database, is_on);
+  view_add_log(eg.rrcview, "ENB_RRC_CONNECTION_REESTABLISHMENT_REQUEST",
+      h, database, is_on);
+  view_add_log(eg.rrcview, "ENB_RRC_CONNECTION_REESTABLISHMENT_COMPLETE",
+      h, database, is_on);
+  view_add_log(eg.rrcview, "ENB_RRC_UL_HANDOVER_PREPARATION_TRANSFER",
+      h, database, is_on);
+  view_add_log(eg.rrcview, "ENB_RRC_UL_INFORMATION_TRANSFER",
+      h, database, is_on);
+  view_add_log(eg.rrcview, "ENB_RRC_COUNTER_CHECK_RESPONSE",
+      h, database, is_on);
+  view_add_log(eg.rrcview, "ENB_RRC_UE_INFORMATION_RESPONSE_R9",
+      h, database, is_on);
+  view_add_log(eg.rrcview, "ENB_RRC_PROXIMITY_INDICATION_R9",
+      h, database, is_on);
+  view_add_log(eg.rrcview, "ENB_RRC_RECONFIGURATION_COMPLETE_R10",
+      h, database, is_on);
+  view_add_log(eg.rrcview, "ENB_RRC_MBMS_COUNTING_RESPONSE_R10",
+      h, database, is_on);
+  view_add_log(eg.rrcview, "ENB_RRC_INTER_FREQ_RSTD_MEASUREMENT_INDICATION",
+      h, database, is_on);
+  view_add_log(eg.rrcview, "ENB_RRC_UNKNOW_MESSAGE",
+      h, database, is_on);
+
+  for (i = 0; i < on_off_n; i++)
+    on_off(database, on_off_name[i], is_on, on_off_action[i]);
+
+  enb_data.socket = -1;
+  enb_data.is_on = is_on;
+  enb_data.nevents = number_of_events;
+  if (pthread_mutex_init(&enb_data.lock, NULL)) abort();
+  setup_event_selector(g, database, is_on, is_on_changed, &enb_data);
+
+restart:
+  clear_remote_config();
+  enb_data.socket = connect_to(ip, port);
+
+  /* send the first message - activate selected traces */
+  is_on_changed(&enb_data);
+
+  /* read messages */
+  while (1) {
+    char v[T_BUFFER_MAX];
+    event e;
+    e = get_event(enb_data.socket, v, database);
+    if (e.type == -1) goto restart;
+    handle_event(h, e);
+  }
+
+  return 0;
+}
diff --git a/common/utils/T/tracer/event.c b/common/utils/T/tracer/event.c
new file mode 100644
index 0000000000000000000000000000000000000000..0917c010b3852a95a6e0fa6f56e48315c1d19b2b
--- /dev/null
+++ b/common/utils/T/tracer/event.c
@@ -0,0 +1,106 @@
+#include "event.h"
+#include "database.h"
+#include "utils.h"
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+event get_event(int socket, char *event_buffer, void *database)
+{
+#ifdef T_SEND_TIME
+  struct timespec t;
+#endif
+  int type;
+  int32_t length;
+
+  /* Events type -1 and -2 are special: the tracee sends its version
+   * of T_messages.txt using those events.
+   * We have to check that the local version of T_messages.txt is identical
+   * to the one the tracee uses. We don't report those events to the
+   * application.
+   */
+
+again:
+  if (fullread(socket, &length, 4) == -1) goto read_error;
+#ifdef T_SEND_TIME
+  if (fullread(socket, &t, sizeof(struct timespec)) == -1) goto read_error;
+  length -= sizeof(struct timespec);
+#endif
+  if (fullread(socket, &type, sizeof(int)) == -1) goto read_error;
+  length -= sizeof(int);
+  if (fullread(socket, event_buffer, length) == -1) goto read_error;
+
+  if (type == -1) append_received_config_chunk(event_buffer, length);
+  if (type == -2) verify_config();
+
+  if (type == -1 || type == -2) goto again;
+
+#ifdef T_SEND_TIME
+  return new_event(t, type, length, event_buffer, database);
+#else
+  return new_event(type, length, event_buffer, database);
+#endif
+
+read_error:
+  return (event){type: -1};
+}
+
+#ifdef T_SEND_TIME
+event new_event(struct timespec sending_time, int type,
+    int length, char *buffer, void *database)
+#else
+event new_event(int type, int length, char *buffer, void *database)
+#endif
+{
+  database_event_format f;
+  event e;
+  int i;
+  int offset;
+
+#ifdef T_SEND_TIME
+  e.sending_time = sending_time;
+#endif
+  e.type = type;
+  e.buffer = buffer;
+
+  f = get_format(database, type);
+
+  e.ecount = f.count;
+
+  offset = 0;
+
+  /* setup offsets */
+  /* TODO: speedup (no strcmp, string event to include length at head) */
+  for (i = 0; i < f.count; i++) {
+    //e.e[i].offset = offset;
+    if (!strcmp(f.type[i], "int")) {
+      e.e[i].type = EVENT_INT;
+      e.e[i].i = *(int *)(&buffer[offset]);
+      offset += 4;
+    } else if (!strcmp(f.type[i], "ulong")) {
+      e.e[i].type = EVENT_ULONG;
+      e.e[i].ul = *(unsigned long *)(&buffer[offset]);
+      offset += sizeof(unsigned long);
+    } else if (!strcmp(f.type[i], "string")) {
+      e.e[i].type = EVENT_STRING;
+      e.e[i].s = &buffer[offset];
+      while (buffer[offset]) offset++;
+      offset++;
+    } else if (!strcmp(f.type[i], "buffer")) {
+      int len;
+      e.e[i].type = EVENT_BUFFER;
+      len = *(int *)(&buffer[offset]);
+      e.e[i].bsize = len;
+      e.e[i].b = &buffer[offset+sizeof(int)];
+      offset += len+sizeof(int);
+    } else {
+      printf("unhandled type '%s'\n", f.type[i]);
+      abort();
+    }
+  }
+
+  if (e.ecount==0) { printf("FORMAT not set in event %d\n", type); abort(); }
+
+  return e;
+}
diff --git a/common/utils/T/tracer/event.h b/common/utils/T/tracer/event.h
new file mode 100644
index 0000000000000000000000000000000000000000..6272baf1a8f13216959db18951b606690315d46e
--- /dev/null
+++ b/common/utils/T/tracer/event.h
@@ -0,0 +1,49 @@
+#ifndef _EVENT_H_
+#define _EVENT_H_
+
+#include "../T_defs.h"
+#ifdef T_SEND_TIME
+#include <time.h>
+#endif
+
+enum event_arg_type {
+  EVENT_INT,
+  EVENT_ULONG,
+  EVENT_STRING,
+  EVENT_BUFFER
+};
+
+typedef struct {
+  enum event_arg_type type;
+  //int offset;
+  union {
+    int i;
+    unsigned long ul;
+    char *s;
+    struct {
+      int bsize;
+      void *b;
+    };
+  };
+} event_arg;
+
+typedef struct {
+#ifdef T_SEND_TIME
+  struct timespec sending_time;
+#endif
+  int type;
+  char *buffer;
+  event_arg e[T_MAX_ARGS];
+  int ecount;
+} event;
+
+event get_event(int s, char *v, void *d);
+
+#ifdef T_SEND_TIME
+event new_event(struct timespec sending_time, int type,
+    int length, char *buffer, void *database);
+#else
+event new_event(int type, int length, char *buffer, void *database);
+#endif
+
+#endif /* _EVENT_H_ */
diff --git a/common/utils/T/tracer/event_selector.c b/common/utils/T/tracer/event_selector.c
new file mode 100644
index 0000000000000000000000000000000000000000..a03846c0566a98faef440f7eb991793209d676e4
--- /dev/null
+++ b/common/utils/T/tracer/event_selector.c
@@ -0,0 +1,231 @@
+#include "event_selector.h"
+#include "gui/gui.h"
+#include "database.h"
+#include "utils.h"
+#include <stdlib.h>
+#include <string.h>
+
+struct event_selector {
+  int *is_on;
+  int *is_on_paused;    /* when pausing, is_on is set to all 0, this one
+                         * is used to copy back data when un-pausing */
+  int red;
+  int green;
+  gui *g;
+  widget *events;
+  widget *groups;
+  void *database;
+  int nevents;
+  int ngroups;
+  int paused;
+  /* those three widgets used to pause/unpause reception of events */
+  widget *parent_widget;
+  widget *normal_widget;
+  widget *pause_widget;
+  void (*change_callback)(void *change_callback_data);
+  void *change_callback_data;
+};
+
+static void scroll(void *private, gui *g,
+    char *notification, widget *w, void *notification_data)
+{
+  int visible_lines;
+  int start_line;
+  int number_of_lines;
+  int new_line;
+  int inc;
+
+  textlist_state(g, w, &visible_lines, &start_line, &number_of_lines);
+  inc = 10;
+  if (inc > visible_lines - 2) inc = visible_lines - 2;
+  if (inc < 1) inc = 1;
+  if (!strcmp(notification, "scrollup")) inc = -inc;
+
+  new_line = start_line + inc;
+  if (new_line > number_of_lines - visible_lines)
+    new_line = number_of_lines - visible_lines;
+  if (new_line < 0) new_line = 0;
+
+  textlist_set_start_line(g, w, new_line);
+}
+
+static void click(void *private, gui *g,
+    char *notification, widget *w, void *notification_data)
+{
+  int *d = notification_data;
+  struct event_selector *this = private;
+  int set_on;
+  int line = d[0];
+  int button = d[1];
+  char *text;
+  int color;
+  int i;
+
+  /* notification_data depends on the kind of widget */
+  if (w == this->pause_widget) {
+    line = 0;
+    button = d[0];
+  } else {
+    line = d[0];
+    button = d[1];
+  }
+
+  /* middle-button toggles - redo with SPACE when keyboard is processed */
+  if (button == 2) {
+    if (this->paused == 0) {
+      widget_del_child(g, this->parent_widget, this->normal_widget);
+      widget_add_child(g, this->parent_widget, this->pause_widget, 0);
+      container_set_child_growable(g, this->parent_widget,
+          this->pause_widget, 1);
+      /* pause */
+      memcpy(this->is_on_paused, this->is_on, this->nevents * sizeof(int));
+      memset(this->is_on, 0, this->nevents * sizeof(int));
+      this->change_callback(this->change_callback_data);
+    } else {
+      widget_del_child(g, this->parent_widget, this->pause_widget);
+      widget_add_child(g, this->parent_widget, this->normal_widget, 0);
+      container_set_child_growable(g, this->parent_widget,
+          this->normal_widget, 1);
+      /* un-pause */
+      memcpy(this->is_on, this->is_on_paused, this->nevents * sizeof(int));
+      this->change_callback(this->change_callback_data);
+    }
+    this->paused = 1 - this->paused;
+    return;
+  }
+
+  if (w == this->pause_widget) return;
+
+  if (button != 1 && button != 3) return;
+
+  if (button == 1) set_on = 1; else set_on = 0;
+
+  if (w == this->events)
+    textlist_get_line(this->g, this->events, line, &text, &color);
+  else
+    textlist_get_line(this->g, this->groups, line, &text, &color);
+
+  on_off(this->database, text, this->is_on, set_on);
+
+  for (i = 0; i < this->nevents; i++)
+    textlist_set_color(this->g, this->events, i,
+        this->is_on[database_pos_to_id(this->database, i)] ?
+            this->green : this->red);
+
+  for (i = 0; i < this->ngroups; i++)
+    textlist_set_color(this->g, this->groups, i, FOREGROUND_COLOR);
+  if (w == this->groups)
+    textlist_set_color(this->g, this->groups, line,
+        set_on ? this->green : this->red);
+
+  this->change_callback(this->change_callback_data);
+}
+
+event_selector *setup_event_selector(gui *g, void *database, int *is_on,
+    void (*change_callback)(void *), void *change_callback_data)
+{
+  struct event_selector *ret;
+  widget *win;
+  widget *win_container;
+  widget *main_container;
+  widget *container;
+  widget *left, *right;
+  widget *events, *groups;
+  widget *pause_container;
+  char **ids;
+  char **gps;
+  int n;
+  int i;
+  int red, green;
+
+  ret = calloc(1, sizeof(struct event_selector)); if (ret == NULL) abort();
+
+  red = new_color(g, "#c93535");
+  green = new_color(g, "#2f9e2a");
+
+  win = new_toplevel_window(g, 470, 300, "event selector");
+  win_container = new_container(g, VERTICAL);
+  widget_add_child(g, win, win_container, -1);
+
+  main_container = new_container(g, VERTICAL);
+  widget_add_child(g, win_container, main_container, -1);
+  container_set_child_growable(g, win_container, main_container, 1);
+
+  container = new_container(g, HORIZONTAL);
+  widget_add_child(g, main_container, container, -1);
+  container_set_child_growable(g, main_container, container, 1);
+  widget_add_child(g, main_container,
+      new_label(g, "mouse scroll to scroll - "
+                   "left click to activate - "
+                   "right click to deactivate"), -1);
+
+  left = new_container(g, VERTICAL);
+  right = new_container(g, VERTICAL);
+  widget_add_child(g, container, left, -1);
+  widget_add_child(g, container, right, -1);
+  container_set_child_growable(g, container, left, 1);
+  container_set_child_growable(g, container, right, 1);
+
+  widget_add_child(g, left, new_label(g, "Events"), -1);
+  widget_add_child(g, right, new_label(g, "Groups"), -1);
+
+  events = new_textlist(g, 235, 10, new_color(g, "#b3c1e1"));
+  groups = new_textlist(g, 235, 10, new_color(g, "#edd6cb"));
+
+  widget_add_child(g, left, events, -1);
+  widget_add_child(g, right, groups, -1);
+  container_set_child_growable(g, left, events, 1);
+  container_set_child_growable(g, right, groups, 1);
+
+  pause_container = new_positioner(g);
+  widget_add_child(g, pause_container,
+      new_label(g,
+          "events' reception paused - click middle button to resume"), -1);
+  label_set_clickable(g, pause_container, 1);
+
+  n = database_get_ids(database, &ids);
+  for (i = 0; i < n; i++) {
+    textlist_add(g, events, ids[i], -1,
+        is_on[database_pos_to_id(database, i)] ? green : red);
+  }
+  free(ids);
+
+  ret->nevents = n;
+
+  ret->is_on_paused = calloc(n, sizeof(int));
+  if (ret->is_on_paused == NULL) abort();
+
+  n = database_get_groups(database, &gps);
+  for (i = 0; i < n; i++) {
+    textlist_add(g, groups, gps[i], -1, FOREGROUND_COLOR);
+  }
+  free(gps);
+
+  ret->ngroups = n;
+
+  ret->g = g;
+  ret->is_on = is_on;
+  ret->red = red;
+  ret->green = green;
+  ret->events = events;
+  ret->groups = groups;
+  ret->database = database;
+  ret->change_callback = change_callback;
+  ret->change_callback_data = change_callback_data;
+
+  ret->parent_widget = win_container;
+  ret->normal_widget = main_container;
+  ret->pause_widget = pause_container;
+
+  register_notifier(g, "scrollup", events, scroll, ret);
+  register_notifier(g, "scrolldown", events, scroll, ret);
+  register_notifier(g, "click", events, click, ret);
+
+  register_notifier(g, "scrollup", groups, scroll, ret);
+  register_notifier(g, "scrolldown", groups, scroll, ret);
+  register_notifier(g, "click", groups, click, ret);
+
+  register_notifier(g, "click", pause_container, click, ret);
+
+  return ret;
+}
diff --git a/common/utils/T/tracer/event_selector.h b/common/utils/T/tracer/event_selector.h
new file mode 100644
index 0000000000000000000000000000000000000000..dedd79965c5096a5e2dce8ad13d61f11fbdda42e
--- /dev/null
+++ b/common/utils/T/tracer/event_selector.h
@@ -0,0 +1,11 @@
+#ifndef _EVENT_SELECTOR_H_
+#define _EVENT_SELECTOR_H_
+
+#include "gui/gui.h"
+
+typedef void event_selector;
+
+event_selector *setup_event_selector(gui *g, void *database, int *is_on,
+    void (*change_callback)(void *), void *change_callback_data);
+
+#endif /* _EVENT_SELECTOR_H_ */
diff --git a/common/utils/T/tracer/filter/Makefile b/common/utils/T/tracer/filter/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..fb1427ca6ac101188c2fee0625314e80e635fa3b
--- /dev/null
+++ b/common/utils/T/tracer/filter/Makefile
@@ -0,0 +1,13 @@
+CC=gcc
+CFLAGS=-Wall -g -pthread -I..
+
+OBJS=filter.o
+
+filter.a: $(OBJS)
+	ar cr filter.a $(OBJS)
+
+%.o: %.c
+	$(CC) $(CFLAGS) -o $@ -c $<
+
+clean:
+	rm -f *.a *.o
diff --git a/common/utils/T/tracer/filter/filter.c b/common/utils/T/tracer/filter/filter.c
new file mode 100644
index 0000000000000000000000000000000000000000..b09b01259d91bf96dd8d886aee433000897d909c
--- /dev/null
+++ b/common/utils/T/tracer/filter/filter.c
@@ -0,0 +1,125 @@
+#include "filter.h"
+#include "event.h"
+#include "database.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct filter {
+  union {
+    struct { struct filter *a, *b; } op2;
+    int v;
+    struct { int event_type; int arg_index; } evarg;
+  } v;
+
+  int (*eval)(struct filter *this, event e);
+};
+
+/****************************************************************************/
+/*                     evaluation functions                                 */
+/****************************************************************************/
+
+int eval_and(struct filter *f, event e)
+{
+  if (f->v.op2.a->eval(f->v.op2.a, e) == 0) return 0;
+  return f->v.op2.b->eval(f->v.op2.b, e);
+}
+
+int eval_eq(struct filter *f, event e)
+{
+  int a = f->v.op2.a->eval(f->v.op2.a, e);
+  int b = f->v.op2.b->eval(f->v.op2.b, e);
+  return a == b;
+}
+
+int eval_int(struct filter *f, event e)
+{
+  return f->v.v;
+}
+
+int eval_evarg(struct filter *f, event e)
+{
+  if (e.type != f->v.evarg.event_type) {
+    printf("%s:%d:%s: bad event type\n", __FILE__, __LINE__, __FUNCTION__);
+    abort();
+  }
+  if (e.e[f->v.evarg.arg_index].type != EVENT_INT) {
+    printf("%s:%d:%s: bad event argtype; has to be 'int'\n",
+        __FILE__, __LINE__, __FUNCTION__);
+    abort();
+  }
+  return e.e[f->v.evarg.arg_index].i;
+}
+
+/****************************************************************************/
+/*                     filter construction functions                        */
+/****************************************************************************/
+
+filter *filter_and(filter *a, filter *b)
+{
+  struct filter *ret = calloc(1, sizeof(struct filter));
+  if (ret == NULL) abort();
+  ret->eval = eval_and;
+  ret->v.op2.a = a;
+  ret->v.op2.b = b;
+  return ret;
+}
+
+filter *filter_eq(filter *a, filter *b)
+{
+  struct filter *ret = calloc(1, sizeof(struct filter));
+  if (ret == NULL) abort();
+  ret->eval = eval_eq;
+  ret->v.op2.a = a;
+  ret->v.op2.b = b;
+  return ret;
+}
+
+filter *filter_int(int v)
+{
+  struct filter *ret = calloc(1, sizeof(struct filter));
+  if (ret == NULL) abort();
+  ret->eval = eval_int;
+  ret->v.v = v;
+  return ret;
+}
+
+filter *filter_evarg(void *database, char *event_name, char *varname)
+{
+  struct filter *ret;
+  int event_id;
+  database_event_format f;
+  int i;
+
+  ret = calloc(1, sizeof(struct filter)); if (ret == NULL) abort();
+
+  event_id = event_id_from_name(database, event_name);
+  f = get_format(database, event_id);
+
+  ret->eval = eval_evarg;
+  ret->v.evarg.event_type = event_id;
+  ret->v.evarg.arg_index = -1;
+
+  for (i = 0; i < f.count; i++) {
+    if (strcmp(f.name[i], varname) != 0) continue;
+    ret->v.evarg.arg_index = i;
+    break;
+  }
+  if (ret->v.evarg.arg_index == -1) {
+    printf("%s:%d:%s: event '%s' has no argument '%s'\n",
+        __FILE__, __LINE__, __FUNCTION__, event_name, varname);
+    abort();
+  }
+
+  return ret;
+}
+
+/****************************************************************************/
+/*                     eval function                                        */
+/****************************************************************************/
+
+int filter_eval(filter *_f, event e)
+{
+  struct filter *f = _f;
+  return f->eval(f, e);
+}
diff --git a/common/utils/T/tracer/filter/filter.h b/common/utils/T/tracer/filter/filter.h
new file mode 100644
index 0000000000000000000000000000000000000000..eba02d7fb7f9975709c60f167c267c1c3ce43c6b
--- /dev/null
+++ b/common/utils/T/tracer/filter/filter.h
@@ -0,0 +1,15 @@
+#ifndef _FILTER_H_
+#define _FILTER_H_
+
+#include "event.h"
+
+typedef void filter;
+
+filter *filter_and(filter *a, filter *b);
+filter *filter_eq(filter *a, filter *b);
+filter *filter_int(int v);
+filter *filter_evarg(void *database, char *event_name, char *varname);
+
+int filter_eval(filter *f, event e);
+
+#endif /* _FILTER_H_ */
diff --git a/common/utils/T/tracer/gui.c b/common/utils/T/tracer/gui.c
new file mode 100644
index 0000000000000000000000000000000000000000..46e06d2e1f0579fbabc4473e9d035acd847350bf
--- /dev/null
+++ b/common/utils/T/tracer/gui.c
@@ -0,0 +1,102 @@
+#include "defs.h"
+#include "gui/gui.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <string.h>
+#include <math.h>
+
+static struct {
+  gui *g;
+  widget *input_signal;      /* CC_id 0 antenna 0 */
+  volatile int input_signal_length;  /* unit: byte */
+  void *input_signal_iq;
+  pthread_mutex_t input_signal_lock;
+} eNB_data;
+
+static void *gui_thread(void *g)
+{
+  gui_loop(g);
+  exit(0);
+}
+
+static void *input_signal_plotter(void *_)
+{
+  short *iqbuf;
+  float *x;
+  float *y;
+  int i;
+  int length = eNB_data.input_signal_length / 4;
+
+  x = calloc(1, sizeof(float) * eNB_data.input_signal_length / 4);
+  y = calloc(1, sizeof(float) * eNB_data.input_signal_length / 4);
+  if (x == NULL || y == NULL) abort();
+
+  while (1) {
+    usleep(100 * 1000);
+
+    if (pthread_mutex_lock(&eNB_data.input_signal_lock)) abort();
+
+    if (length * 4 != eNB_data.input_signal_length) {
+      free(x);
+      free(y);
+      x = calloc(1, sizeof(float) * eNB_data.input_signal_length / 4);
+      y = calloc(1, sizeof(float) * eNB_data.input_signal_length / 4);
+      if (x == NULL || y == NULL) abort();
+      length = eNB_data.input_signal_length / 4;
+    }
+
+    iqbuf = eNB_data.input_signal_iq;
+
+    for (i = 0; i < length; i++) {
+      x[i] = i;
+      y[i] = 10*log10(1.0+(float)(iqbuf[2*i]*iqbuf[2*i]+
+                                  iqbuf[2*i+1]*iqbuf[2*i+1]));
+    }
+
+    xy_plot_set_points(eNB_data.g, eNB_data.input_signal, 0,
+        length, x, y);
+
+    if (pthread_mutex_unlock(&eNB_data.input_signal_lock)) abort();
+  }
+}
+
+void t_gui_start(void)
+{
+  gui *g = gui_init();
+
+  widget *win = new_toplevel_window(g, 550, 140, "input signal");
+  widget *plot = new_xy_plot(g, 512, 100, "eNB 0 input signal", 20);
+  widget_add_child(g, win, plot, -1);
+  xy_plot_set_range(g, plot, 0, 76800, 30, 70);
+  xy_plot_new_plot(g, plot, FOREGROUND_COLOR);
+
+  eNB_data.input_signal = plot;
+  eNB_data.input_signal_length = 76800 * 4;
+  eNB_data.input_signal_iq = calloc(1, 76800 * 4);
+  if (eNB_data.input_signal_iq == NULL) abort();
+  pthread_mutex_init(&eNB_data.input_signal_lock, NULL);
+
+  eNB_data.g = g;
+
+  new_thread(gui_thread, g);
+  new_thread(input_signal_plotter, NULL);
+}
+
+void t_gui_set_input_signal(int eNB, int frame, int subframe, int antenna,
+    int size, void *buf)
+{
+  if (pthread_mutex_lock(&eNB_data.input_signal_lock)) abort();
+
+  if (eNB_data.input_signal_length != size * 10) {
+    free(eNB_data.input_signal_iq);
+    eNB_data.input_signal_length = size * 10;
+    eNB_data.input_signal_iq = calloc(1, eNB_data.input_signal_length);
+    if (eNB_data.input_signal_iq == NULL) abort();
+  }
+
+  memcpy((char *)eNB_data.input_signal_iq + subframe * size, buf, size);
+
+  if (pthread_mutex_unlock(&eNB_data.input_signal_lock)) abort();
+}
diff --git a/common/utils/T/tracer/gui/Makefile b/common/utils/T/tracer/gui/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..53f2077eaccb2ae7aab01ee1ff5c5635cd207395
--- /dev/null
+++ b/common/utils/T/tracer/gui/Makefile
@@ -0,0 +1,18 @@
+CC=gcc
+CFLAGS=-Wall -g -pthread -I/usr/include/X11/Xft -I/usr/include/freetype2
+
+OBJS=init.o loop.o toplevel_window.o x.o container.o widget.o \
+     gui.o label.o event.o xy_plot.o textlist.o notify.o positioner.o \
+     timeline.o space.o image.o
+
+gui.a: $(OBJS)
+	ar cr gui.a $(OBJS)
+
+test: test.o gui.a
+	$(CC) -o test $(OBJS) test.o -lX11 -pthread -lm
+
+%.o: %.c
+	$(CC) $(CFLAGS) -o $@ -c $<
+
+clean:
+	rm -f *.a *.o test
diff --git a/common/utils/T/tracer/gui/container.c b/common/utils/T/tracer/gui/container.c
new file mode 100644
index 0000000000000000000000000000000000000000..8de43479332390a91294c62ff71ca8bd9ef5db9f
--- /dev/null
+++ b/common/utils/T/tracer/gui/container.c
@@ -0,0 +1,339 @@
+#include "gui.h"
+#include "gui_defs.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define MAX(a, b) ((a)>(b)?(a):(b))
+
+static void repack(gui *g, widget *_this)
+{
+  LOGD("REPACK container %p\n", _this);
+  struct container_widget *this = _this;
+  this->hints_are_valid = 0;
+  return this->common.parent->repack(g, this->common.parent);
+}
+
+static void add_child(gui *g, widget *_this, widget *child, int position)
+{
+  LOGD("ADD_CHILD container\n");
+  struct container_widget *this = _this;
+
+  this->hints_are_valid = 0;
+  widget_add_child_internal(g, this, child, position);
+
+  /* initially not growable */
+  this->growable = realloc(this->growable, (this->nchildren+1)*sizeof(int));
+  if (this->growable == NULL) abort();
+
+  if (position == -1) position = this->nchildren;
+
+  memmove(this->growable + position+1, this->growable + position,
+          (this->nchildren - position) * sizeof(int));
+
+  this->growable[position] = 0;
+
+  this->nchildren++;
+}
+
+static void del_child(gui *g, widget *_this, widget *child)
+{
+  LOGD("DEL_CHILD container\n");
+  struct container_widget *this = _this;
+  int position = widget_get_child_position(g, _this, child);
+
+  this->hints_are_valid = 0;
+  widget_del_child_internal(g, this, child);
+
+  memmove(this->growable + position, this->growable + position+1,
+          (this->nchildren - position - 1) * sizeof(int));
+
+  this->growable = realloc(this->growable, (this->nchildren-1)*sizeof(int));
+  if (this->nchildren != 1 && this->growable == NULL) abort();
+
+  this->nchildren--;
+}
+
+static void compute_vertical_hints(struct gui *g,
+    struct container_widget *this)
+{
+  struct widget_list *l;
+  int cwidth, cheight;
+  int allocated_width = 0, allocated_height = 0;
+
+  /* get largest width */
+  l = this->common.children;
+  while (l) {
+    l->item->hints(g, l->item, &cwidth, &cheight);
+    if (cwidth > allocated_width) allocated_width = cwidth;
+    allocated_height += cheight;
+    l = l->next;
+  }
+  this->hint_width = allocated_width;
+  this->hint_height = allocated_height;
+  this->hints_are_valid = 1;
+}
+
+static void compute_horizontal_hints(struct gui *g,
+    struct container_widget *this)
+{
+  struct widget_list *l;
+  int cwidth, cheight;
+  int allocated_width = 0, allocated_height = 0;
+
+  /* get largest height */
+  l = this->common.children;
+  while (l) {
+    l->item->hints(g, l->item, &cwidth, &cheight);
+    if (cheight > allocated_height) allocated_height = cheight;
+    allocated_width += cwidth;
+    l = l->next;
+  }
+  this->hint_width = allocated_width;
+  this->hint_height = allocated_height;
+  this->hints_are_valid = 1;
+}
+
+static void vertical_allocate(gui *_gui, widget *_this,
+    int x, int y, int width, int height)
+{
+  LOGD("ALLOCATE container vertical %p\n", _this);
+  int cy = 0;
+  int cwidth, cheight;
+  struct gui *g = _gui;
+  struct container_widget *this = _this;
+  struct widget_list *l;
+  int over_pixels = 0;
+  int i;
+
+  if (this->hints_are_valid == 1) goto hints_ok;
+
+  compute_vertical_hints(g, this);
+
+hints_ok:
+
+  this->common.x = x;
+  this->common.y = y;
+  this->common.width = width;
+  this->common.height = height;
+
+  /* TODO: some pixels won't be allocated, take care of it? */
+  if (height > this->hint_height) {
+    int ngrowable = 0;
+    for (i = 0; i < this->nchildren; i++) if (this->growable[i]) ngrowable++;
+    if (ngrowable)
+      over_pixels = (height - this->hint_height) / ngrowable;
+  }
+
+  /* allocate */
+  l = this->common.children;
+  i = 0;
+  while (l) {
+    int allocated_height;
+    l->item->hints(g, l->item, &cwidth, &cheight);
+    allocated_height = cheight + (this->growable[i] ? over_pixels : 0);
+    l->item->allocate(g, l->item, this->common.x, this->common.y + cy,
+        MAX(width, cwidth), allocated_height);
+    cy += allocated_height;
+    l = l->next;
+    i++;
+  }
+
+//  if (cy != this->hint_height) ERR("reachable?\n");
+}
+
+static void horizontal_allocate(gui *_gui, widget *_this,
+    int x, int y, int width, int height)
+{
+  LOGD("ALLOCATE container horizontal %p\n", _this);
+  int cx = 0;
+  int cwidth, cheight;
+  struct gui *g = _gui;
+  struct container_widget *this = _this;
+  struct widget_list *l;
+  int over_pixels = 0;
+  int i;
+
+  if (this->hints_are_valid == 1) goto hints_ok;
+
+  compute_horizontal_hints(g, this);
+
+hints_ok:
+
+  this->common.x = x;
+  this->common.y = y;
+  this->common.width = width;
+  this->common.height = height;
+
+  /* TODO: some pixels won't be allocated, take care of it? */
+  if (width > this->hint_width) {
+    int ngrowable = 0;
+    for (i = 0; i < this->nchildren; i++) if (this->growable[i]) ngrowable++;
+    if (ngrowable)
+      over_pixels = (width - this->hint_width) / ngrowable;
+  }
+
+  /* allocate */
+  l = this->common.children;
+  i = 0;
+  while (l) {
+    int allocated_width;
+    l->item->hints(g, l->item, &cwidth, &cheight);
+    allocated_width = cwidth + (this->growable[i] ? over_pixels : 0);
+    l->item->allocate(g, l->item, this->common.x + cx, this->common.y,
+        allocated_width, MAX(height, cheight)/* this->hint_height */);
+    cx += allocated_width;
+    l = l->next;
+    i++;
+  }
+
+//  if (cx != this->hint_width) ERR("reachable?\n");
+}
+
+static void vertical_hints(gui *_gui, widget *_w, int *width, int *height)
+{
+  LOGD("HINTS container vertical %p\n", _w);
+  struct gui *g = _gui;
+  struct container_widget *this = _w;
+
+  if (this->hints_are_valid) {
+    *width = this->hint_width;
+    *height = this->hint_height;
+    return;
+  }
+
+  compute_vertical_hints(g, this);
+
+  *width = this->hint_width;
+  *height = this->hint_height;
+}
+
+static void horizontal_hints(gui *_gui, widget *_w, int *width, int *height)
+{
+  LOGD("HINTS container horizontal %p\n", _w);
+  struct gui *g = _gui;
+  struct container_widget *this = _w;
+
+  if (this->hints_are_valid) {
+    *width = this->hint_width;
+    *height = this->hint_height;
+    return;
+  }
+
+  compute_horizontal_hints(g, this);
+
+  *width = this->hint_width;
+  *height = this->hint_height;
+}
+
+static void horizontal_button(gui *_g, widget *_this, int x, int y,
+    int key_modifiers, int button, int up)
+{
+  LOGD("BUTTON container horizontal %p xy %d %d button %d up %d\n", _this, x, y, button, up);
+  struct gui *g = _g;
+  struct container_widget *this = _this;
+  struct widget_list *l;
+
+  l = this->common.children;
+  while (l) {
+    if (l->item->x <= x && x < l->item->x + l->item->width) {
+      l->item->button(g, l->item, x, y, key_modifiers, button, up);
+      break;
+    }
+    l = l->next;
+  }
+}
+
+static void vertical_button(gui *_g, widget *_this, int x, int y,
+    int key_modifiers, int button, int up)
+{
+  LOGD("BUTTON container vertical %p xy %d %d button %d up %d\n", _this, x, y, button, up);
+  struct gui *g = _g;
+  struct container_widget *this = _this;
+  struct widget_list *l;
+
+  l = this->common.children;
+  while (l) {
+    if (l->item->y <= y && y < l->item->y + l->item->height) {
+      l->item->button(g, l->item, x, y, key_modifiers, button, up);
+      break;
+    }
+    l = l->next;
+  }
+}
+
+static void paint(gui *_gui, widget *_this)
+{
+  LOGD("PAINT container\n");
+  struct gui *g = _gui;
+  struct widget *this = _this;
+  struct widget_list *l;
+
+  l = this->children;
+  while (l) {
+    l->item->paint(g, l->item);
+    l = l->next;
+  }
+}
+
+widget *new_container(gui *_gui, int vertical)
+{
+  struct gui *g = _gui;
+  struct container_widget *w;
+
+  glock(g);
+
+  w = new_widget(g, CONTAINER, sizeof(struct container_widget));
+
+  w->vertical = vertical;
+  w->hints_are_valid = 0;
+
+  w->common.paint     = paint;
+  w->common.add_child = add_child;
+  w->common.del_child = del_child;
+  w->common.repack    = repack;
+
+  if (vertical) {
+    w->common.allocate  = vertical_allocate;
+    w->common.hints     = vertical_hints;
+    w->common.button    = vertical_button;
+  } else {
+    w->common.allocate  = horizontal_allocate;
+    w->common.hints     = horizontal_hints;
+    w->common.button    = horizontal_button;
+  }
+
+  gunlock(g);
+
+  return w;
+}
+
+/*************************************************************************/
+/*                             public functions                          */
+/*************************************************************************/
+
+void container_set_child_growable(gui *_gui, widget *_this,
+    widget *child, int growable)
+{
+  gui *g = _gui;
+  struct container_widget *this = _this;
+  struct widget_list *lcur;
+  int i;
+
+  glock(g);
+
+  lcur = this->common.children;
+  i = 0;
+  while (lcur) {
+    if (lcur->item == child) break;
+    lcur = lcur->next;
+    i++;
+  }
+  if (lcur == NULL) ERR("%s:%d: child not found\n", __FILE__, __LINE__);
+
+  this->growable[i] = growable;
+
+  send_event(g, REPACK, this->common.id);
+
+  gunlock(g);
+}
diff --git a/common/utils/T/tracer/gui/event.c b/common/utils/T/tracer/gui/event.c
new file mode 100644
index 0000000000000000000000000000000000000000..842318fcea5aff8e468b835429ff19bab3de45ad
--- /dev/null
+++ b/common/utils/T/tracer/gui/event.c
@@ -0,0 +1,209 @@
+#include "gui.h"
+#include "gui_defs.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdarg.h>
+
+/*****************************************************************/
+/*                       generic functions                       */
+/*****************************************************************/
+
+static void event_list_append(struct gui *g, struct event *e)
+{
+  struct event_list *new;
+
+  new = calloc(1, sizeof(struct event_list));
+  if (new == NULL) OOM;
+
+  new->item = e;
+
+  if (g->queued_events == NULL) {
+    g->queued_events = new;
+    new->last = new;
+    return;
+  }
+
+  g->queued_events->last->next = new;
+  g->queued_events->last = new;
+}
+
+static void free_event(struct event *e)
+{
+  switch (e->type) {
+  case REPACK: /* nothing */ break;
+  case DIRTY: /* nothing */ break;
+  }
+  free(e);
+}
+
+static int events_equal(struct event *e1, struct event *e2)
+{
+  if (e1->type != e2->type) return 0;
+  switch (e1->type) {
+  case REPACK: {
+    struct repack_event *re1 = (struct repack_event *)e1;
+    struct repack_event *re2 = (struct repack_event *)e2;
+    return re1->id == re2->id;
+  }
+  case DIRTY: {
+    struct dirty_event *re1 = (struct dirty_event *)e1;
+    struct dirty_event *re2 = (struct dirty_event *)e2;
+    return re1->id == re2->id;
+  }}
+  /* unreachable */
+  abort();
+}
+
+/*****************************************************************/
+/*                         sending events                        */
+/*****************************************************************/
+
+static event *new_event_repack(int id)
+{
+  struct repack_event *ret;
+  ret = calloc(1, sizeof(struct repack_event));
+  if (ret == NULL) OOM;
+  ret->id = id;
+  return ret;
+}
+
+static event *new_event_dirty(int id)
+{
+  struct dirty_event *ret;
+  ret = calloc(1, sizeof(struct dirty_event));
+  if (ret == NULL) OOM;
+  ret->id = id;
+  return ret;
+}
+
+static void compress_event_list(struct gui *g)
+{
+  struct event *last;
+  struct event_list *cur;
+  /* basic compression, to be refined */
+
+  /* pickup last event and remove every copy of it found before
+   * if it's DIRTY or REPACK
+   */
+  last = g->queued_events->last->item;
+  if (last->type == DIRTY || last->type == REPACK) {
+    cur = g->queued_events;
+    while (cur->item != last) {
+      if (cur->item != NULL && events_equal(cur->item, last)) {
+        free_event(cur->item);
+        cur->item = NULL;
+      }
+      cur = cur->next;
+    }
+  }
+}
+
+void send_event(gui *_gui, enum event_type type, ...)
+{
+  LOGD("send_event %d\n", type);
+  struct gui *g = _gui;
+  int do_write = 0;
+  va_list ap;
+  struct event *e;
+
+  if (g->queued_events == NULL) do_write = 1;
+
+  va_start(ap, type);
+
+  switch (type) {
+  case REPACK: {
+    int id;
+    id = va_arg(ap, int);
+    e = new_event_repack(id);
+    break;
+  }
+  case DIRTY: {
+    int id;
+    id = va_arg(ap, int);
+    e = new_event_dirty(id);
+    break;
+  }
+  }
+
+  va_end(ap);
+
+  e->type = type;
+
+  event_list_append(g, e);
+  compress_event_list(g);
+
+  if (do_write) {
+    char c = 1;
+    if (write(g->event_pipe[1], &c, 1) != 1)
+      ERR("error writing to pipe: %s\n", strerror(errno));
+  }
+}
+
+/*****************************************************************/
+/*                      processing events                        */
+/*****************************************************************/
+
+static void repack_event(struct gui *g, int id)
+{
+  struct widget *w = find_widget(g, id);
+  if (w == NULL) { WARN("widget id %d not found\n", id); return; }
+  w->repack(g, w);
+}
+
+/* TODO: put that function somewhere else? */
+static struct toplevel_window_widget *get_toplevel_window(struct widget *w)
+{
+  while (w != NULL) {
+    if (w->type == TOPLEVEL_WINDOW)
+      return (struct toplevel_window_widget *)w;
+    w = w->parent;
+  }
+  return NULL;
+}
+
+static void dirty_event(struct gui *g, int id)
+{
+  struct widget *w = find_widget(g, id);
+  struct toplevel_window_widget *win;
+  if (w == NULL) { WARN("widget id %d not found\n", id); return; }
+  win = get_toplevel_window(w);
+  if (win == NULL)
+    { WARN("widget id %d not contained in a window\n", id); return; }
+  g->xwin = win->x;
+  w->clear(g, w);
+  w->paint(g, w);
+  g->xwin = NULL;
+  g->repainted = 1;
+}
+
+static void process_event(struct gui *g, struct event *e)
+{
+  LOGD("processing event type %d\n", e->type);
+  switch (e->type) {
+  case REPACK: repack_event(g, ((struct repack_event *)e)->id); break;
+  case DIRTY: dirty_event(g, ((struct dirty_event *)e)->id); break;
+  }
+}
+
+/* TODO: events' compression */
+void gui_events(gui *_gui)
+{
+  struct gui *g = _gui;
+
+  LOGD("gui_events START: head %p\n", g->queued_events);
+
+  while (g->queued_events) {
+    struct event_list *cur = g->queued_events;
+    g->queued_events = cur->next;
+    if (g->queued_events) g->queued_events->last = cur->last;
+    if (cur->item != NULL) {
+      process_event(g, cur->item);
+      free_event(cur->item);
+    }
+    free(cur);
+  }
+  LOGD("gui_events DONE\n");
+}
diff --git a/common/utils/T/tracer/gui/gui.c b/common/utils/T/tracer/gui/gui.c
new file mode 100644
index 0000000000000000000000000000000000000000..f0d7dcbcb1faf04a7157cb7f763937403281d250
--- /dev/null
+++ b/common/utils/T/tracer/gui/gui.c
@@ -0,0 +1,34 @@
+#include "gui.h"
+#include "gui_defs.h"
+#include "x.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+
+int volatile gui_logd;
+
+void glock(gui *_gui)
+{
+  struct gui *g = _gui;
+  if (pthread_mutex_lock(g->lock)) ERR("mutex error\n");
+}
+
+void gunlock(gui *_gui)
+{
+  struct gui *g = _gui;
+  if (pthread_mutex_unlock(g->lock)) ERR("mutex error\n");
+}
+
+int new_color(gui *_gui, char *color)
+{
+  struct gui *g = _gui;
+  int ret;
+
+  glock(g);
+
+  ret = x_new_color(g->x, color);
+
+  gunlock(g);
+
+  return ret;
+}
diff --git a/common/utils/T/tracer/gui/gui.h b/common/utils/T/tracer/gui/gui.h
new file mode 100644
index 0000000000000000000000000000000000000000..673514f138ec88c58fa4a88b6487cbb3db9f0578
--- /dev/null
+++ b/common/utils/T/tracer/gui/gui.h
@@ -0,0 +1,105 @@
+#ifndef _GUI_H_
+#define _GUI_H_
+
+/* defines the public API of the GUI */
+
+typedef void gui;
+typedef void widget;
+
+#define HORIZONTAL 0
+#define VERTICAL   1
+
+#define BACKGROUND_COLOR 0
+#define FOREGROUND_COLOR 1
+
+#define DEFAULT_FONT 0
+
+/* key modifiers */
+#define KEY_SHIFT   (1<<0)
+#define KEY_CONTROL (1<<1)
+#define KEY_ALT     (1<<2)
+
+gui *gui_init(void);
+
+/* position = -1 to put at the end */
+void widget_add_child(gui *gui, widget *parent, widget *child, int position);
+void widget_del_child(gui *gui, widget *parent, widget *child);
+void widget_dirty(gui *gui, widget *this);
+
+widget *new_toplevel_window(gui *gui, int width, int height, char *title);
+widget *new_container(gui *gui, int vertical);
+widget *new_positioner(gui *gui);
+widget *new_label(gui *gui, const char *text);
+widget *new_xy_plot(gui *gui, int width, int height, char *label,
+    int vruler_width);
+widget *new_textlist(gui *gui, int width, int nlines, int background_color);
+widget *new_timeline(gui *gui, int width, int number_of_sublines,
+    int subline_height);
+widget *new_space(gui *gui, int width, int height);
+widget *new_image(gui *gui, unsigned char *data, int length);
+
+void label_set_clickable(gui *gui, widget *label, int clickable);
+
+void container_set_child_growable(gui *_gui, widget *_this,
+    widget *child, int growable);
+
+int xy_plot_new_plot(gui *gui, widget *this, int color);
+void xy_plot_set_range(gui *gui, widget *this,
+    float xmin, float xmax, float ymin, float ymax);
+void xy_plot_set_points(gui *gui, widget *this,
+    int plot, int npoints, float *x, float *y);
+void xy_plot_get_dimensions(gui *gui, widget *this, int *width, int *height);
+
+void textlist_add(gui *gui, widget *this, const char *text, int position,
+    int color);
+void textlist_del(gui *gui, widget *this, int position);
+void textlist_add_silent(gui *gui, widget *this, const char *text,
+    int position, int color);
+void textlist_del_silent(gui *gui, widget *this, int position);
+void textlist_state(gui *_gui, widget *_this,
+    int *visible_lines, int *start_line, int *number_of_lines);
+void textlist_set_start_line(gui *gui, widget *this, int line);
+void textlist_get_line(gui *gui, widget *this, int line,
+    char **text, int *color);
+void textlist_set_color(gui *gui, widget *this, int line, int color);
+
+void timeline_clear(gui *gui, widget *this);
+void timeline_clear_silent(gui *gui, widget *this);
+void timeline_add_points(gui *gui, widget *this, int subline, int color,
+    int *x, int len);
+void timeline_add_points_silent(gui *gui, widget *this, int subline,
+    int color, int *x, int len);
+void timeline_set_subline_background_color(gui *gui, widget *this,
+    int subline, int color);
+void timeline_get_width(gui *gui, widget *this, int *width);
+
+void gui_loop(gui *gui);
+
+void glock(gui *gui);
+void gunlock(gui *gui);
+
+int new_color(gui *gui, char *color);
+
+/* notifications */
+/* known notifications:
+ * - textlist:
+ *      - scrollup   { void *: NULL }
+ *      - scrolldown { void *: NULL }
+ *      - click      { int [2]: line, button }
+ * - label:
+ *      - click      { int: button } (if enabled)
+ * - timeline
+ *      - resize     { int: width }
+ *      - scrollup   { int [3]: x, y, key_modifiers }
+ *      - scrolldown { int [3]: x, y, key_modifiers }
+ *      - click      { int: button }
+ */
+
+/* same type as in gui_defs.h */
+typedef void (*notifier)(void *private, gui *g,
+    char *notification, widget *w, void *notification_data);
+unsigned long register_notifier(gui *g, char *notification, widget *w,
+    notifier handler, void *private);
+void unregister_notifier(gui *g, unsigned long notifier_id);
+
+#endif /* _GUI_H_ */
diff --git a/common/utils/T/tracer/gui/gui_defs.h b/common/utils/T/tracer/gui/gui_defs.h
new file mode 100644
index 0000000000000000000000000000000000000000..ec72bb0e2bb14e8951f8bf7e5d3acda960109de8
--- /dev/null
+++ b/common/utils/T/tracer/gui/gui_defs.h
@@ -0,0 +1,255 @@
+#ifndef _GUI_DEFS_H_
+#define _GUI_DEFS_H_
+
+/* defines the private API of the GUI */
+
+extern int volatile gui_logd;
+#define LOGD(...) do { if (gui_logd) printf(__VA_ARGS__); } while (0)
+
+/*************************************************************************/
+/*                            logging macros                             */
+/*************************************************************************/
+
+#define ERR(...) \
+  do { \
+    printf("%s:%d:%s: ERROR: ", __FILE__, __LINE__, __FUNCTION__); \
+    printf(__VA_ARGS__); \
+    abort(); \
+  } while (0)
+
+#define WARN(...) \
+  do { \
+    printf("%s:%d:%s: WARNING: ", __FILE__, __LINE__, __FUNCTION__); \
+    printf(__VA_ARGS__); \
+  } while (0)
+
+#define OOM ERR("out of memory\n")
+
+/*************************************************************************/
+/*                             widgets                                   */
+/*************************************************************************/
+
+enum widget_type {
+  TOPLEVEL_WINDOW, CONTAINER, POSITIONER, TEXT_LIST, XY_PLOT, BUTTON, LABEL,
+  TIMELINE, SPACE, IMAGE
+};
+
+struct widget_list;
+
+struct widget {
+  enum widget_type type;
+  int id;
+  int x;            /* allocated x after packing */
+  int y;            /* allocated y after packing */
+  int width;        /* allocated width after packing */
+  int height;       /* allocated height after packing */
+  struct widget_list *children;
+  struct widget *parent;
+  void (*repack)(gui *g, widget *this);
+  void (*add_child)(gui *g, widget *this, widget *child, int position);
+  void (*del_child)(gui *g, widget *this, widget *child);
+  void (*allocate)(gui *g, widget *this, int x, int y, int width, int height);
+  void (*hints)(gui *g, widget *this, int *width, int *height);
+  void (*paint)(gui *g, widget *this);
+  void (*clear)(gui *g, widget *this);
+  /* user input */
+  void (*button)(gui *g, widget *this, int x, int y, int key_modifiers,
+      int button, int up);
+};
+
+struct widget_list {
+  struct widget *item;
+  struct widget_list *next;
+  //struct widget_list *prev;  /* unused? */
+  struct widget_list *last;  /* valid only for the head of the list */
+};
+
+struct toplevel_window_widget {
+  struct widget common;
+  void *x;                /* opaque X data (type x_window), used in x.c */
+};
+
+struct container_widget {
+  struct widget common;
+  int vertical;
+  int hints_are_valid;     /* used to cache hints values */
+  int hint_width;          /* cached hint values - invalid if */
+  int hint_height;         /* repack_was_called == 1          */
+  int *growable;
+  int nchildren;
+};
+
+struct positioner_widget {
+  struct widget common;
+};
+
+struct textlist_widget {
+  struct widget common;
+  char **text;
+  int *color;
+  int text_count;
+  int wanted_width;
+  int wanted_nlines;    /* number of lines of text the user wants to see */
+  int allocated_nlines; /* actual number of visible lines */
+  int starting_line;    /* points to the first visible line of text */
+  int line_height;
+  int baseline;
+  int background_color;
+};
+
+struct xy_plot_plot {
+  float *x;
+  float *y;
+  int npoints;
+  int color;
+};
+
+struct xy_plot_widget {
+  struct widget common;
+  char *label;
+  int label_width;
+  int label_height;
+  int label_baseline;
+  int vrule_width;       /* the width of the vertical ruler text zone */
+  float xmin, xmax;
+  float ymin, ymax;
+  int wanted_width;
+  int wanted_height;
+  struct xy_plot_plot *plots;
+  int nplots;
+};
+
+struct timeline_subline {
+  int *color;                  /* length = width of timeline widget
+                                * value = -1 if no color
+                                */
+  int width;
+  int background;              /* background color of the subline */
+};
+
+struct timeline_widget {
+  struct widget common;
+  int n;                         /* number of sublines */
+  struct timeline_subline *s;
+  int subline_height;
+  int wanted_width;
+};
+
+struct button_widget {
+  struct widget common;
+};
+
+struct label_widget {
+  struct widget common;
+  const char *t;
+  int color;
+  int width;         /* as given by the graphic's backend */
+  int height;        /* as given by the graphic's backend */
+  int baseline;      /* as given by the graphic's backend */
+};
+
+struct space_widget {
+  struct widget common;
+  int wanted_width;
+  int wanted_height;
+};
+
+struct image_widget {
+  struct widget common;
+  int width;
+  int height;
+  void *x;             /* opaque X data (type x_image), used in x.c */
+};
+
+/*************************************************************************/
+/*                             events                                    */
+/*************************************************************************/
+
+typedef void event;
+
+enum event_type {
+  DIRTY, REPACK
+};
+
+struct event {
+  enum event_type type;
+};
+
+struct event_list {
+  struct event *item;
+  struct event_list *next;
+  struct event_list *last;
+};
+
+struct dirty_event {
+  struct event common;
+  int id;
+};
+
+struct repack_event {
+  struct event common;
+  int id;
+};
+
+/*************************************************************************/
+/*                           notifications                               */
+/*************************************************************************/
+
+/* same type as in gui.h */
+typedef void (*notifier)(void *private, gui *g,
+    char *notification, widget *w, void *notification_data);
+
+struct notifier {
+  notifier handler;
+  unsigned long id;
+  char *notification;
+  widget *w;
+  void *private;
+  /* done is used bu gui_notify */
+  int done;
+};
+
+/*************************************************************************/
+/*                          main structure                               */
+/*************************************************************************/
+
+struct gui {
+  void                *lock;
+  void                *x; /* opaque X data (type x_connection), used in x.c */
+  struct widget_list  *toplevel;
+  struct event_list   *queued_events;
+  int                 event_pipe[2];
+  int                 next_id;         /* tells what is the ID of
+                                          the next created widget */
+  int                 repainted;       /* set to 1 when some widget has
+                                        * been repainted (TODO: can be any,
+                                        * to be optimized) */
+  void                *xwin;           /* set by a toplevel_window when
+                                        * it paints itself, to be used
+                                        * by its children */
+  struct notifier     *notifiers;
+  int                 notifiers_count;
+  unsigned long       next_notifier_id;
+};
+
+/*************************************************************************/
+/*                            internal functions                         */
+/*************************************************************************/
+
+widget *new_widget(struct gui *g, enum widget_type type, int size);
+void widget_add_child_internal(
+    gui *_gui, widget *parent, widget *child, int position);
+void widget_del_child_internal(gui *_gui, widget *parent, widget *child);
+int widget_get_child_position(gui *_gui, widget *parent, widget *child);
+
+const char *widget_name(enum widget_type type);
+
+void send_event(gui *gui, enum event_type type, ...);
+void gui_events(gui *gui);
+
+struct widget *find_widget(struct gui *g, int id);
+
+void gui_notify(struct gui *g, char *notification, widget *w,
+    void *notification_data);
+
+#endif /* _GUI_DEFS_H_ */
diff --git a/common/utils/T/tracer/gui/image.c b/common/utils/T/tracer/gui/image.c
new file mode 100644
index 0000000000000000000000000000000000000000..df9a7b8296ae40ea19221a4b1f8d65c20a899178
--- /dev/null
+++ b/common/utils/T/tracer/gui/image.c
@@ -0,0 +1,114 @@
+#include "gui.h"
+#include "gui_defs.h"
+#include "x.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <png.h>
+
+static void paint(gui *_gui, widget *_w)
+{
+  struct gui *g = _gui;
+  struct image_widget *w = _w;
+  LOGD("PAINT image %p\n", w);
+  x_draw_image(g->x, g->xwin, w->x, w->common.x, w->common.y);
+}
+
+static void hints(gui *_gui, widget *_w, int *width, int *height)
+{
+  struct image_widget *w = _w;
+  LOGD("HINTS image %p\n", w);
+  *width = w->width;
+  *height = w->height;
+}
+
+struct png_reader {
+  unsigned char *data;
+  int size;
+  int pos;
+};
+
+static void png_readfn(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+  struct png_reader *r = png_get_io_ptr(png_ptr);
+  if (length > r->size - r->pos) png_error(png_ptr, "bad png image");
+  memcpy(data, r->data + r->pos, length);
+  r->pos += length;
+}
+
+static void load_image(struct gui *g, struct image_widget *w,
+    unsigned char *data, int length)
+{
+  png_structp png_ptr;
+  png_infop info_ptr;
+  png_bytepp image;
+  int width, height, bit_depth, color_type, channels;
+  unsigned char *img_data;
+  struct png_reader r;
+  int i;
+
+  /* unpack PNG data */
+
+  r.data = data;
+  r.size = length;
+  r.pos = 0;
+
+  png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+  if (png_ptr == NULL) abort();
+
+  info_ptr = png_create_info_struct(png_ptr);
+  if (info_ptr == NULL) abort();
+
+  if (setjmp(png_jmpbuf(png_ptr))) abort();
+
+  png_set_read_fn(png_ptr, &r, png_readfn);
+
+  png_read_png(png_ptr, info_ptr,
+      PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_EXPAND | PNG_TRANSFORM_PACKING |
+      PNG_TRANSFORM_GRAY_TO_RGB | PNG_TRANSFORM_BGR, NULL);
+
+  image = png_get_rows(png_ptr, info_ptr);
+
+  width = png_get_image_width(png_ptr, info_ptr);
+  height = png_get_image_height(png_ptr, info_ptr);
+  bit_depth = png_get_bit_depth(png_ptr, info_ptr);
+  color_type = png_get_color_type(png_ptr, info_ptr);
+  channels = png_get_channels(png_ptr, info_ptr);
+
+  if (width < 1 || width > 1000 || height < 1 || height > 1000 ||
+      bit_depth != 8 || color_type != PNG_COLOR_TYPE_RGBA || channels != 4)
+    { printf("bad image\n"); abort(); }
+
+  img_data = malloc(4 * width * height); if (img_data == NULL) abort();
+  for (i = 0; i < height; i++)
+    memcpy(img_data+i*4*width, image[i], width*4);
+
+  png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+
+  /* create the X image */
+  w->x = x_create_image(g->x, img_data, width, height);
+
+  free(img_data);
+
+  w->width = width;
+  w->height = height;
+}
+
+widget *new_image(gui *_gui, unsigned char *data, int length)
+{
+  struct gui *g = _gui;
+  struct image_widget *w;
+
+  glock(g);
+
+  w = new_widget(g, IMAGE, sizeof(struct image_widget));
+
+  load_image(g, w, data, length);
+
+  w->common.paint = paint;
+  w->common.hints = hints;
+
+  gunlock(g);
+
+  return w;
+}
diff --git a/common/utils/T/tracer/gui/init.c b/common/utils/T/tracer/gui/init.c
new file mode 100644
index 0000000000000000000000000000000000000000..c6d5e3e435366b574b3495ee41b9c6df45621744
--- /dev/null
+++ b/common/utils/T/tracer/gui/init.c
@@ -0,0 +1,34 @@
+#include "gui.h"
+#include "gui_defs.h"
+#include "x.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+
+gui *gui_init(void)
+{
+  struct gui *ret;
+
+  ret = calloc(1, sizeof(struct gui));
+  if (ret == NULL) OOM;
+
+  ret->lock = malloc(sizeof(pthread_mutex_t));
+  if (ret->lock == NULL) OOM;
+  if (pthread_mutex_init(ret->lock, NULL))
+    ERR("mutex initialization failed\n");
+
+  if (pipe(ret->event_pipe))
+    ERR("%s\n", strerror(errno));
+
+  /* lock not necessary but there for consistency (when instrumenting x.c
+   * we need the gui to be locked when calling any function in x.c)
+   */
+  glock(ret);
+  ret->x = x_open();
+  gunlock(ret);
+
+  return ret;
+}
diff --git a/common/utils/T/tracer/gui/label.c b/common/utils/T/tracer/gui/label.c
new file mode 100644
index 0000000000000000000000000000000000000000..f649fa520147cb1bde0099bd9bab2f1330813423
--- /dev/null
+++ b/common/utils/T/tracer/gui/label.c
@@ -0,0 +1,83 @@
+#include "gui.h"
+#include "gui_defs.h"
+#include "x.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static void paint(gui *_gui, widget *_w)
+{
+  struct gui *g = _gui;
+  struct label_widget *l = _w;
+  LOGD("PAINT label '%s'\n", l->t);
+  x_draw_string(g->x, g->xwin, DEFAULT_FONT, l->color,
+      l->common.x, l->common.y + l->baseline, l->t);
+}
+
+static void hints(gui *_gui, widget *_w, int *width, int *height)
+{
+  struct label_widget *l = _w;
+  LOGD("HINTS label '%s'\n", l->t);
+  *width = l->width;
+  *height = l->height;
+}
+
+widget *new_label(gui *_gui, const char *label)
+{
+  struct gui *g = _gui;
+  struct label_widget *w;
+
+  glock(g);
+
+  w = new_widget(g, LABEL, sizeof(struct label_widget));
+
+  w->t = strdup(label);
+  if (w->t == NULL) OOM;
+  w->color = FOREGROUND_COLOR;
+
+  x_text_get_dimensions(g->x, DEFAULT_FONT, label,
+      &w->width, &w->height, &w->baseline);
+
+  w->common.paint = paint;
+  w->common.hints = hints;
+
+  gunlock(g);
+
+  return w;
+}
+
+static void button(gui *gui, widget *_this, int x, int y,
+    int key_modifiers, int button, int up)
+{
+  LOGD("BUTTON label %p xy %d %d button %d up %d\n", _this, x, y, button, up);
+
+  if (up != 0) return;
+
+  gui_notify(gui, "click", _this, &button);
+}
+
+/* we could use default_button, but it's in widget.c, so, well... */
+static void no_button(gui *gui, widget *_this, int x, int y,
+    int key_modifiers, int button, int up)
+{
+  /* do nothing */
+}
+
+/*************************************************************************/
+/*                             public functions                          */
+/*************************************************************************/
+
+void label_set_clickable(gui *_g, widget *_this, int clickable)
+{
+  struct gui *g = _g;
+  struct label_widget *this = _this;
+
+  glock(g);
+
+  if (clickable)
+    this->common.button = button;
+  else
+    this->common.button = no_button;
+
+  gunlock(g);
+}
diff --git a/common/utils/T/tracer/gui/loop.c b/common/utils/T/tracer/gui/loop.c
new file mode 100644
index 0000000000000000000000000000000000000000..8ceb3c06e1c4936dc508fea8e1ce8dc33b0d99c3
--- /dev/null
+++ b/common/utils/T/tracer/gui/loop.c
@@ -0,0 +1,66 @@
+#include "gui.h"
+#include "gui_defs.h"
+#include "x.h"
+#include <sys/select.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+void gui_loop(gui *_gui)
+{
+  struct gui *g = _gui;
+  int xfd;
+  int eventfd;
+  int maxfd;
+  fd_set rd;
+
+  /* lock not necessary but there for consistency (when instrumenting x.c
+   * we need the gui to be locked when calling any function in x.c)
+   */
+  glock(g);
+  xfd = x_connection_fd(g->x);
+  gunlock(g);
+  eventfd = g->event_pipe[0];
+
+  if (eventfd > xfd) maxfd = eventfd;
+  else               maxfd = xfd;
+
+  while (1) {
+    glock(g);
+    x_flush(g->x);
+    gunlock(g);
+    FD_ZERO(&rd);
+    FD_SET(xfd, &rd);
+    FD_SET(eventfd, &rd);
+    if (select(maxfd+1, &rd, NULL, NULL, NULL) == -1)
+      ERR("select: %s\n", strerror(errno));
+
+    glock(g);
+
+    if (FD_ISSET(xfd, &rd))
+      x_events(g);
+
+    if (FD_ISSET(eventfd, &rd)) {
+      char c[256];
+      if (read(eventfd, c, 256)); /* for no gcc warnings */
+    }
+
+    gui_events(g);
+
+    if (g->repainted) {
+      struct widget_list *cur;
+      g->repainted = 0;
+      cur = g->toplevel;
+      while (cur) {
+        struct toplevel_window_widget *w =
+            (struct toplevel_window_widget *)cur->item;
+        x_draw(g->x, w->x);
+        cur = cur->next;
+      }
+    }
+
+    gunlock(g);
+  }
+}
diff --git a/common/utils/T/tracer/gui/notify.c b/common/utils/T/tracer/gui/notify.c
new file mode 100644
index 0000000000000000000000000000000000000000..7ff40069411a1bd9bae7d0106b686c41b04b6ef6
--- /dev/null
+++ b/common/utils/T/tracer/gui/notify.c
@@ -0,0 +1,116 @@
+#include "gui.h"
+#include "gui_defs.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+unsigned long register_notifier(gui *_g, char *notification, widget *w,
+    notifier handler, void *private)
+{
+  struct gui *g = _g;
+  unsigned long ret;
+
+  glock(g);
+
+  if (g->next_notifier_id == 2UL * 1024 * 1024 * 1024)
+    ERR("%s:%d: report a bug\n", __FILE__, __LINE__);
+
+  g->notifiers = realloc(g->notifiers,
+      (g->notifiers_count+1) * sizeof(struct notifier));
+  if (g->notifiers == NULL) abort();
+
+  ret = g->next_notifier_id;
+
+  g->notifiers[g->notifiers_count].handler = handler;
+  g->notifiers[g->notifiers_count].id = g->next_notifier_id;
+  g->next_notifier_id++;
+  g->notifiers[g->notifiers_count].notification = strdup(notification);
+  if (g->notifiers[g->notifiers_count].notification == NULL) abort();
+  g->notifiers[g->notifiers_count].w = w;
+  g->notifiers[g->notifiers_count].private = private;
+  /* initialize done to 1 so as not to call the handler if it's created
+   * by the call of another one that is in progress
+   */
+  g->notifiers[g->notifiers_count].done = 1;
+
+  g->notifiers_count++;
+
+  gunlock(g);
+
+  return ret;
+}
+
+void unregister_notifier(gui *_g, unsigned long notifier_id)
+{
+  struct gui *g = _g;
+  int i;
+
+  glock(g);
+
+  for (i = 0; i < g->notifiers_count; i++)
+    if (g->notifiers[i].id == notifier_id) break;
+
+  if (i == g->notifiers_count)
+    ERR("%s:%d: notifier_id %ld not found\n", __FILE__,__LINE__,notifier_id);
+
+  free(g->notifiers[i].notification);
+
+  memmove(g->notifiers + i, g->notifiers + i + 1,
+      (g->notifiers_count-1 - i) * sizeof(struct notifier));
+
+  g->notifiers_count--;
+  g->notifiers = realloc(g->notifiers,
+      g->notifiers_count * sizeof(struct notifier));
+  if (g->notifiers == NULL) abort();
+
+  gunlock(g);
+}
+
+/* called with lock ON */
+void gui_notify(struct gui *g, char *notification, widget *w,
+    void *notification_data)
+{
+  void *private;
+  notifier handler;
+  int i;
+
+  /* this function is not re-entrant, for the moment keep as is
+   * and if the need is there, we'll make a new thread to handle
+   * notifications (or something)
+   * for now let's crash in case of recursive call
+   */
+  static int inside = 0;
+  if (inside) ERR("%s:%d: BUG! contact the authors\n", __FILE__, __LINE__);
+  inside = 1;
+
+  /* clear all handlers */
+  /* TODO: speedup */
+  for (i = 0; i < g->notifiers_count; i++) g->notifiers[i].done = 0;
+
+  /* calling the handler may modify the list of notifiers, we
+   * need to be careful here
+   */
+loop:
+  for (i = 0; i < g->notifiers_count; i++) {
+    if (g->notifiers[i].done == 1 ||
+        g->notifiers[i].w != w    ||
+        strcmp(g->notifiers[i].notification, notification) != 0)
+      continue;
+    break;
+  }
+  if (i == g->notifiers_count) goto done;
+
+  g->notifiers[i].done = 1;
+
+  handler = g->notifiers[i].handler;
+  private = g->notifiers[i].private;
+
+  gunlock(g);
+  handler(private, g, notification, w, notification_data);
+  glock(g);
+
+  goto loop;
+
+done:
+  inside = 0;
+}
diff --git a/common/utils/T/tracer/gui/positioner.c b/common/utils/T/tracer/gui/positioner.c
new file mode 100644
index 0000000000000000000000000000000000000000..673ffb32ded28295032e76e96626a179f11d8d23
--- /dev/null
+++ b/common/utils/T/tracer/gui/positioner.c
@@ -0,0 +1,93 @@
+#include "gui.h"
+#include "gui_defs.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static void add_child(gui *g, widget *_this, widget *child, int position)
+{
+  LOGD("ADD_CHILD positioner\n");
+  struct positioner_widget *this = _this;
+  widget_add_child_internal(g, this, child, position);
+}
+
+static void del_child(gui *g, widget *_this, widget *child)
+{
+  LOGD("DEL_CHILD positioner\n");
+  struct positioner_widget *this = _this;
+  widget_del_child_internal(g, this, child);
+}
+
+static void allocate(
+    gui *_g, widget *_this, int x, int y, int width, int height)
+{
+  LOGD("ALLOCATE positioner %p\n", _this);
+  struct gui *g = _g;
+  struct positioner_widget *this = _this;
+  struct widget_list *l = this->common.children;
+  int cwidth, cheight;
+
+  this->common.x = x;
+  this->common.y = y;
+  this->common.width = width;
+  this->common.height = height;
+
+  if (l != NULL) {
+    l->item->hints(g, l->item, &cwidth, &cheight);
+    l->item->allocate(g, l->item, x+(width-cwidth)/2, y+(height-cheight)/2,
+        cwidth, cheight);
+  }
+}
+
+static void hints(gui *_gui, widget *_w, int *width, int *height)
+{
+  LOGD("HINTS positioner %p\n", _w);
+  struct gui *g = _gui;
+  struct positioner_widget *this = _w;
+  struct widget_list *l = this->common.children;
+  if (l != NULL)
+    l->item->hints(g, l->item, width, height);
+  else { *width = *height = 1; }
+}
+
+static void button(gui *_g, widget *_this, int x, int y,
+    int key_modifiers, int button, int up)
+{
+  LOGD("BUTTON positioner %p xy %d %d button %d up %d\n", _this, x, y, button, up);
+  struct gui *g = _g;
+  struct positioner_widget *this = _this;
+  struct widget_list *l = this->common.children;
+  if (l != NULL)
+    l->item->button(g, l->item, x, y, key_modifiers, button, up);
+}
+
+static void paint(gui *_gui, widget *_this)
+{
+  LOGD("PAINT positioner\n");
+  struct gui *g = _gui;
+  struct widget *this = _this;
+  struct widget_list *l = this->children;
+  if (l != NULL)
+    l->item->paint(g, l->item);
+}
+
+widget *new_positioner(gui *_gui)
+{
+  struct gui *g = _gui;
+  struct positioner_widget *w;
+
+  glock(g);
+
+  w = new_widget(g, POSITIONER, sizeof(struct positioner_widget));
+
+  w->common.paint     = paint;
+  w->common.add_child = add_child;
+  w->common.del_child = del_child;
+  w->common.allocate  = allocate;
+  w->common.hints     = hints;
+  w->common.button    = button;
+
+  gunlock(g);
+
+  return w;
+}
diff --git a/common/utils/T/tracer/gui/space.c b/common/utils/T/tracer/gui/space.c
new file mode 100644
index 0000000000000000000000000000000000000000..c98600b88dc6edecac53d1e0417d533a312c1c4e
--- /dev/null
+++ b/common/utils/T/tracer/gui/space.c
@@ -0,0 +1,36 @@
+#include "gui.h"
+#include "gui_defs.h"
+#include <stdio.h>
+
+static void paint(gui *_gui, widget *_w)
+{
+  /* nothing */
+}
+
+static void hints(gui *_gui, widget *_w, int *width, int *height)
+{
+  struct space_widget *w = _w;
+  LOGD("HINTS space %p\n", w);
+  *width = w->wanted_width;
+  *height = w->wanted_height;
+}
+
+widget *new_space(gui *_gui, int width, int height)
+{
+  struct gui *g = _gui;
+  struct space_widget *w;
+
+  glock(g);
+
+  w = new_widget(g, SPACE, sizeof(struct space_widget));
+
+  w->wanted_width = width;
+  w->wanted_height = height;
+
+  w->common.paint = paint;
+  w->common.hints = hints;
+
+  gunlock(g);
+
+  return w;
+}
diff --git a/common/utils/T/tracer/gui/test.c b/common/utils/T/tracer/gui/test.c
new file mode 100644
index 0000000000000000000000000000000000000000..f64d02347e3383eb7b039156fc07db4585df5d6a
--- /dev/null
+++ b/common/utils/T/tracer/gui/test.c
@@ -0,0 +1,46 @@
+#include "gui.h"
+
+int main(void)
+{
+  gui *g;
+  widget *w, *c1, *c2, *l, *plot, *tl;
+  int tlcol;
+
+  g = gui_init();
+
+  c1 = new_container(g, VERTICAL);
+  c2 = new_container(g, HORIZONTAL);
+
+  l = new_label(g, "this is a good label");
+  widget_add_child(g, c2, l, 0);
+  l = new_label(g, "this is another good label");
+  widget_add_child(g, c2, l, -1);
+
+  l = new_label(g, "OH! WHAT A LABEL!");
+  widget_add_child(g, c1, l, -1);
+
+  widget_add_child(g, c1, c2, 0);
+
+  plot = new_xy_plot(g, 100, 100, "xy plot test", 30);
+#if 0
+  c2 = new_container(g, HORIZONTAL);
+  widget_add_child(g, c2, plot, -1);
+  widget_add_child(g, c1, c2, -1);
+#else
+  widget_add_child(g, c1, plot, -1);
+#endif
+
+  tlcol = new_color(g, "#ddf");
+  tl = new_textlist(g, 300, 10, tlcol);
+  widget_add_child(g, c1, tl, -1);
+
+  textlist_add(g, tl, "hello", -1, FOREGROUND_COLOR);
+  textlist_add(g, tl, "world", -1, FOREGROUND_COLOR);
+
+  w = new_toplevel_window(g, 500, 400, "test window");
+  widget_add_child(g, w, c1, 0);
+
+  gui_loop(g);
+
+  return 0;
+}
diff --git a/common/utils/T/tracer/gui/textlist.c b/common/utils/T/tracer/gui/textlist.c
new file mode 100644
index 0000000000000000000000000000000000000000..896cba5b9e8b6949321af0789214780552fede12
--- /dev/null
+++ b/common/utils/T/tracer/gui/textlist.c
@@ -0,0 +1,248 @@
+#include "gui.h"
+#include "gui_defs.h"
+#include "x.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static void paint(gui *_gui, widget *_this)
+{
+  struct gui *g = _gui;
+  struct textlist_widget *this = _this;
+  int i, j;
+  LOGD("PAINT textlist %p xywh %d %d %d %d starting line %d allocated nlines %d text_count %d\n", _this, this->common.x, this->common.y, this->common.width, this->common.height, this->starting_line, this->allocated_nlines, this->text_count);
+  x_fill_rectangle(g->x, g->xwin, this->background_color,
+      this->common.x, this->common.y,
+      this->common.width, this->common.height);
+  for (i = 0, j = this->starting_line;
+       i < this->allocated_nlines && j < this->text_count; i++, j++)
+    x_draw_clipped_string(g->x, g->xwin, DEFAULT_FONT, this->color[j],
+        this->common.x,
+        this->common.y + i * this->line_height + this->baseline,
+        this->text[j],
+        this->common.x, this->common.y,
+        this->common.width, this->common.height);
+}
+
+static void hints(gui *_gui, widget *_w, int *width, int *height)
+{
+  struct textlist_widget *w = _w;
+  *width = w->wanted_width;
+  *height = w->wanted_nlines * w->line_height;
+  LOGD("HINTS textlist wh %d %d\n", *width, *height);
+}
+
+static void allocate(
+    gui *gui, widget *_this, int x, int y, int width, int height)
+{
+  struct textlist_widget *this = _this;
+  this->common.x = x;
+  this->common.y = y;
+  this->common.width = width;
+  this->common.height = height;
+  this->allocated_nlines = height / this->line_height;
+  LOGD("ALLOCATE textlist %p xywh %d %d %d %d nlines %d\n", this, x, y, width, height, this->allocated_nlines);
+}
+
+static void button(gui *_g, widget *_this, int x, int y,
+    int key_modifiers, int button, int up)
+{
+  struct gui *g = _g;
+  struct textlist_widget *this = _this;
+  LOGD("BUTTON textlist %p xy %d %d button %d up %d\n", _this, x, y, button, up);
+  y -= this->common.y;
+  x -= this->common.x;
+  /* scroll up */
+  if (button == 4 && up == 0) {
+    gui_notify(g, "scrollup", _this, NULL);
+  }
+  /* scroll down */
+  if (button == 5 && up == 0) {
+    gui_notify(g, "scrolldown", _this, NULL);
+  }
+  /* button 1/2/3 click */
+  if (button >= 1 && button <= 3 && up == 0) {
+    int line = this->starting_line + y / this->line_height;
+    if (line >= 0 && line < this->text_count)
+      gui_notify(g, "click", _this, (int[2]){ line, button });
+  }
+}
+
+widget *new_textlist(gui *_gui, int width, int nlines, int bgcol)
+{
+  struct gui *g = _gui;
+  struct textlist_widget *w;
+  int dummy;
+
+  glock(g);
+
+  w = new_widget(g, TEXT_LIST, sizeof(struct textlist_widget));
+
+  w->wanted_nlines = nlines;
+  x_text_get_dimensions(g->x, DEFAULT_FONT, ".",
+      &dummy, &w->line_height, &w->baseline);
+  w->background_color = bgcol;
+  w->wanted_width = width;
+
+  w->common.paint = paint;
+  w->common.hints = hints;
+  w->common.allocate = allocate;
+
+  w->common.button = button;
+
+  gunlock(g);
+
+  return w;
+}
+
+/*************************************************************************/
+/*                             public functions                          */
+/*************************************************************************/
+
+static void _textlist_add(gui *_gui, widget *_this, const char *text,
+    int position, int color, int silent)
+{
+  struct gui *g = _gui;
+  struct textlist_widget *this = _this;
+
+  glock(g);
+
+  if (position < 0) position = this->text_count;
+  if (position > this->text_count) position = this->text_count;
+
+  this->text_count++;
+  this->text = realloc(this->text, this->text_count * sizeof(char *));
+  if (this->text == NULL) OOM;
+  this->color = realloc(this->color, this->text_count * sizeof(int));
+  if (this->color == NULL) OOM;
+
+  memmove(this->text + position + 1, this->text + position,
+          (this->text_count-1 - position) * sizeof(char *));
+  memmove(this->color + position + 1, this->color + position,
+          (this->text_count-1 - position) * sizeof(int));
+
+  this->text[position] = strdup(text); if (this->text[position] == NULL) OOM;
+  this->color[position] = color;
+
+  if (!silent) send_event(g, DIRTY, this->common.id);
+
+  gunlock(g);
+}
+
+static void _textlist_del(gui *_gui, widget *_this, int position, int silent)
+{
+  struct gui *g = _gui;
+  struct textlist_widget *this = _this;
+
+  glock(g);
+
+  /* TODO: useful check? */
+  if (this->text_count == 0) goto done;
+
+  if (position < 0) position = this->text_count;
+  if (position > this->text_count-1) position = this->text_count-1;
+
+  free(this->text[position]);
+
+  memmove(this->text + position, this->text + position + 1,
+          (this->text_count-1 - position) * sizeof(char *));
+  memmove(this->color + position, this->color + position + 1,
+          (this->text_count-1 - position) * sizeof(int));
+
+  this->text_count--;
+  this->text = realloc(this->text, this->text_count * sizeof(char *));
+  if (this->text == NULL) OOM;
+  this->color = realloc(this->color, this->text_count * sizeof(int));
+  if (this->color == NULL) OOM;
+
+  if (!silent) send_event(g, DIRTY, this->common.id);
+
+done:
+  gunlock(g);
+}
+
+void textlist_add(gui *gui, widget *this, const char *text, int position,
+    int color)
+{
+  _textlist_add(gui, this, text, position, color, 0);
+}
+
+void textlist_del(gui *gui, widget *this, int position)
+{
+  _textlist_del(gui, this, position, 0);
+}
+
+void textlist_add_silent(gui *gui, widget *this, const char *text,
+    int position, int color)
+{
+  _textlist_add(gui, this, text, position, color, 1);
+}
+
+void textlist_del_silent(gui *gui, widget *this, int position)
+{
+  _textlist_del(gui, this, position, 1);
+}
+
+void textlist_state(gui *_gui, widget *_this,
+    int *visible_lines, int *start_line, int *number_of_lines)
+{
+  struct gui *g = _gui;
+  struct textlist_widget *this = _this;
+
+  glock(g);
+
+  *visible_lines   = this->allocated_nlines;
+  *start_line      = this->starting_line;
+  *number_of_lines = this->text_count;
+
+  gunlock(g);
+}
+
+void textlist_set_start_line(gui *_gui, widget *_this, int line)
+{
+  struct gui *g = _gui;
+  struct textlist_widget *this = _this;
+
+  glock(g);
+
+  this->starting_line = line;
+
+  send_event(g, DIRTY, this->common.id);
+
+  gunlock(g);
+}
+
+void textlist_get_line(gui *_gui, widget *_this, int line,
+    char **text, int *color)
+{
+  struct gui *g = _gui;
+  struct textlist_widget *this = _this;
+
+  glock(g);
+
+  if (line < 0 || line >= this->text_count) {
+    *text = NULL;
+    *color = -1;
+  } else {
+    *text = this->text[line];
+    *color = this->color[line];
+  }
+
+  gunlock(g);
+}
+
+void textlist_set_color(gui *_gui, widget *_this, int line, int color)
+{
+  struct gui *g = _gui;
+  struct textlist_widget *this = _this;
+
+  glock(g);
+
+  if (line >= 0 && line < this->text_count) {
+    this->color[line] = color;
+
+    send_event(g, DIRTY, this->common.id);
+  }
+
+  gunlock(g);
+}
diff --git a/common/utils/T/tracer/gui/timeline.c b/common/utils/T/tracer/gui/timeline.c
new file mode 100644
index 0000000000000000000000000000000000000000..d2373ff60a4007f856c4349b339438852446b1bf
--- /dev/null
+++ b/common/utils/T/tracer/gui/timeline.c
@@ -0,0 +1,214 @@
+#include "gui.h"
+#include "gui_defs.h"
+#include "x.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+static void paint(gui *_gui, widget *_this)
+{
+  struct gui *g = _gui;
+  struct timeline_widget *this = _this;
+  int i;
+  int j;
+
+  for (i = 0; i < this->n; i++) {
+    x_fill_rectangle(g->x, g->xwin, this->s[i].background,
+        this->common.x, this->common.y + i * this->subline_height,
+        this->common.width, this->subline_height);
+    for (j = 0; j < this->s[i].width; j++)
+      if (this->s[i].color[j] != -1)
+        x_draw_line(g->x, g->xwin, this->s[i].color[j],
+            this->common.x + j, this->common.y + i * this->subline_height,
+            this->common.x + j, this->common.y + this->subline_height -1
+                + i * this->subline_height);
+  }
+
+  LOGD("PAINT timeline xywh %d %d %d %d\n", this->common.x, this->common.y, this->common.width, this->common.height);
+}
+
+static void hints(gui *_gui, widget *_w, int *width, int *height)
+{
+  struct timeline_widget *w = _w;
+  *width = w->wanted_width;
+  *height = w->n * w->subline_height;
+  LOGD("HINTS timeline wh %d %d\n", *width, *height);
+}
+
+static void allocate(gui *_gui, widget *_this,
+    int x, int y, int width, int height)
+{
+  struct timeline_widget *this = _this;
+  int i;
+  int j;
+  this->common.x = x;
+  this->common.y = y;
+  this->common.width = width;
+  this->common.height = height;
+  LOGD("ALLOCATE timeline %p xywh %d %d %d %d\n", this, x, y, width, height);
+  for (i = 0; i < this->n; i++) {
+    this->s[i].width = width;
+    this->s[i].color = realloc(this->s[i].color, width * sizeof(int));
+    if (this->s[i].color == NULL) abort();
+    for (j = 0; j < width; j++) this->s[i].color[j] = -1;
+  }
+  gui_notify(_gui, "resize", _this, &width);
+}
+
+static void button(gui *_g, widget *_this, int x, int y,
+    int key_modifiers, int button, int up)
+{
+  struct gui *g = _g;
+  struct timeline_widget *w = _this;
+  int d[3];
+  LOGD("BUTTON timeline %p xy %d %d button %d up %d\n", _this, x, y, button, up);
+  /* scroll up */
+  if (button == 4 && up == 0) {
+    d[0] = x - w->common.x;
+    d[1] = y - w->common.y;
+    d[2] = key_modifiers;
+    gui_notify(g, "scrollup", _this, d);
+  }
+  /* scroll down */
+  if (button == 5 && up == 0) {
+    d[0] = x - w->common.x;
+    d[1] = y - w->common.y;
+    d[2] = key_modifiers;
+    gui_notify(g, "scrolldown", _this, d);
+  }
+  /* button 1/2/3 */
+  if ((button == 1 || button == 2 || button == 3) && up == 0) {
+    gui_notify(g, "click", _this, &button);
+  }
+}
+
+/*************************************************************************/
+/*                           creation function                           */
+/*************************************************************************/
+
+widget *new_timeline(gui *_gui, int width, int number_of_sublines,
+    int subline_height)
+{
+  struct gui *g = _gui;
+  struct timeline_widget *w;
+  int i;
+  int j;
+
+  glock(g);
+
+  w = new_widget(g, TIMELINE, sizeof(struct timeline_widget));
+
+  w->wanted_width = width;
+  w->n = number_of_sublines;
+  w->s = calloc(w->n, sizeof(struct timeline_subline)); if (w->s == NULL) OOM;
+  w->subline_height = subline_height;
+
+  /* initialize colors */
+  for (i = 0; i < w->n; i++) {
+    w->s[i].width = width;
+    w->s[i].color = calloc(width, sizeof(int));
+    if (w->s[i].color == NULL) abort();
+    for (j = 0; j < width; j++) w->s[i].color[j] = -1;
+    w->s[i].background = BACKGROUND_COLOR;
+  }
+
+  w->common.paint = paint;
+  w->common.hints = hints;
+  w->common.allocate = allocate;
+  w->common.button = button;
+
+  gunlock(g);
+
+  return w;
+}
+
+/*************************************************************************/
+/*                           public functions                            */
+/*************************************************************************/
+
+static void _timeline_clear(gui *_gui, widget *_this, int silent)
+{
+  struct gui *g = _gui;
+  struct timeline_widget *this = _this;
+  int i;
+  int j;
+
+  glock(g);
+
+  for (i = 0; i < this->n; i++)
+    for (j = 0; j < this->s[i].width; j++)
+      this->s[i].color[j] = -1;
+
+  if (silent == 0)
+    send_event(g, DIRTY, this->common.id);
+
+  gunlock(g);
+}
+
+void timeline_clear(gui *_gui, widget *_this)
+{
+  _timeline_clear(_gui, _this, 0);
+}
+
+void timeline_clear_silent(gui *_gui, widget *_this)
+{
+  _timeline_clear(_gui, _this, 1);
+}
+
+static void _timeline_add_points(gui *_gui, widget *_this, int subline,
+    int color, int *x, int len, int silent)
+{
+  struct gui *g = _gui;
+  struct timeline_widget *this = _this;
+  int i;
+
+  glock(g);
+
+  for (i = 0; i < len; i++) {
+    if (x[i] >= this->s[subline].width) { WARN("out of bounds\n"); continue; }
+    this->s[subline].color[x[i]] = color;
+  }
+
+  if (silent == 0)
+    send_event(g, DIRTY, this->common.id);
+
+  gunlock(g);
+}
+
+void timeline_add_points(gui *_gui, widget *_this, int subline, int color,
+    int *x, int len)
+{
+  _timeline_add_points(_gui, _this, subline, color, x, len, 0);
+}
+
+void timeline_add_points_silent(gui *_gui, widget *_this, int subline,
+    int color, int *x, int len)
+{
+  _timeline_add_points(_gui, _this, subline, color, x, len, 1);
+}
+
+void timeline_set_subline_background_color(gui *_gui, widget *_this,
+    int subline, int color)
+{
+  struct gui *g = _gui;
+  struct timeline_widget *this = _this;
+
+  glock(g);
+
+  this->s[subline].background = color;
+
+  send_event(g, DIRTY, this->common.id);
+
+  gunlock(g);
+}
+
+void timeline_get_width(gui *_gui, widget *_this, int *width)
+{
+  struct gui *g = _gui;
+  struct timeline_widget *this = _this;
+
+  glock(g);
+
+  *width = this->common.width == 0 ? this->wanted_width : this->common.width;
+
+  gunlock(g);
+}
diff --git a/common/utils/T/tracer/gui/toplevel_window.c b/common/utils/T/tracer/gui/toplevel_window.c
new file mode 100644
index 0000000000000000000000000000000000000000..d2436c84f9a0b2dcea2442d07c2ba528d294bd57
--- /dev/null
+++ b/common/utils/T/tracer/gui/toplevel_window.c
@@ -0,0 +1,97 @@
+#include "gui.h"
+#include "gui_defs.h"
+#include "x.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+/**********************************************************************/
+/*                         callback functions                         */
+/**********************************************************************/
+
+static void repack(gui *g, widget *_this)
+{
+  LOGD("REPACK toplevel_window\n");
+  struct toplevel_window_widget *this = _this;
+  if (this->common.children == NULL) ERR("toplevel window has no child\n");
+  if (this->common.children->next != NULL)
+    ERR("toplevel window has too much children\n");
+  this->common.children->item->allocate(g, this->common.children->item,
+      0 /* x */, 0 /* y */, this->common.width, this->common.height);
+  send_event(g, DIRTY, this->common.id);
+}
+
+static void add_child(gui *_gui, widget *_this, widget *child, int position)
+{
+  LOGD("ADD_CHILD toplevel_window\n");
+  struct widget *this = _this;
+  if (this->children != NULL) {
+    WARN("toplevel window already has a child\n");
+    return;
+  }
+  widget_add_child_internal(_gui, _this, child, 0); /* this does the REPACK */
+}
+
+/* called when the underlying window is resized by the user or the system */
+static void allocate(
+    gui *_gui, widget *_this, int x, int y, int width, int height)
+{
+  LOGD("ALLOCATE toplevel_window\n");
+  struct toplevel_window_widget *this = _this;
+  this->common.width = width;
+  this->common.height = height;
+//  repack(_gui, _this);
+  send_event(_gui, REPACK, this->common.id);
+}
+
+static void paint(gui *_gui, widget *_this)
+{
+  struct gui *g = _gui;
+  struct toplevel_window_widget *this = _this;
+  LOGD("PAINT toplevel_window (%d %d)\n", this->common.width, this->common.height);
+  x_fill_rectangle(g->x, this->x, BACKGROUND_COLOR,
+      0, 0, this->common.width, this->common.height);
+  g->xwin = this->x;
+  this->common.children->item->paint(_gui, this->common.children->item);
+  g->xwin = NULL;    /* TODO: remove? it's just in case */
+}
+
+static void button(gui *_g, widget *_this, int x, int y,
+    int key_modifiers, int button, int up)
+{
+  struct gui *g = _g;
+  struct toplevel_window_widget *this = _this;
+  g->xwin = this->x;
+  this->common.children->item->button(_g, this->common.children->item,
+      x, y, key_modifiers, button, up);
+  g->xwin = NULL;    /* TODO: remove? it's just in case */
+}
+
+/**********************************************************************/
+/*                              creation                              */
+/**********************************************************************/
+
+widget *new_toplevel_window(gui *_gui, int width, int height, char *title)
+{
+  struct gui *g = _gui;
+  struct toplevel_window_widget *w;
+
+  glock(g);
+
+  w = new_widget(g, TOPLEVEL_WINDOW, sizeof(struct toplevel_window_widget));
+
+  w->common.width  = width;
+  w->common.height = height;
+
+  w->x = x_create_window(g->x, width, height, title);
+
+  w->common.repack    = repack;
+  w->common.add_child = add_child;
+  w->common.allocate  = allocate;
+  w->common.paint     = paint;
+
+  w->common.button    = button;
+
+  gunlock(g);
+
+  return w;
+}
diff --git a/common/utils/T/tracer/gui/widget.c b/common/utils/T/tracer/gui/widget.c
new file mode 100644
index 0000000000000000000000000000000000000000..362d2e1e4898c39960033fe206518d902a3ba897
--- /dev/null
+++ b/common/utils/T/tracer/gui/widget.c
@@ -0,0 +1,322 @@
+#include "gui.h"
+#include "gui_defs.h"
+#include "x.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+
+static void default_clear(gui *gui, widget *_this);
+static void default_repack(gui *gui, widget *_this);
+static void default_allocate(
+    gui *gui, widget *_this, int x, int y, int width, int height);
+static void default_add_child(
+    gui *_gui, widget *_this, widget *child, int position);
+static void default_del_child(gui *_gui, widget *_this, widget *child);
+static void default_hints(gui *g, widget *this, int *width, int *height);
+static void default_button(gui *gui, widget *_this, int x, int y,
+    int key_modifiers, int button, int up);
+
+static void toplevel_list_append(struct gui *g, struct widget *e)
+{
+  struct widget_list *new;
+
+  new = calloc(1, sizeof(struct widget_list));
+  if (new == NULL) OOM;
+
+  new->item = e;
+
+  if (g->toplevel == NULL) {
+    g->toplevel = new;
+    new->last = new;
+    return;
+  }
+
+  g->toplevel->last->next = new;
+  g->toplevel->last = new;
+}
+
+widget *new_widget(struct gui *g, enum widget_type type, int size)
+{
+  struct widget *ret;
+
+  //glock(g);
+
+  ret = calloc(1, size);
+  if (ret == NULL) OOM;
+
+  ret->clear     = default_clear;
+  ret->repack    = default_repack;
+  ret->add_child = default_add_child;
+  ret->del_child = default_del_child;
+  ret->allocate  = default_allocate;
+  ret->hints     = default_hints;
+  ret->button    = default_button;
+  /* there is no default paint, on purpose */
+
+  ret->type      = type;
+  ret->id        = g->next_id;
+  g->next_id++;
+  ret->width     = 0;
+  ret->height    = 0;
+
+  /* add toplevel windows to g->toplevel */
+  if (type == TOPLEVEL_WINDOW)
+    toplevel_list_append(g, ret);
+
+  //gunlock(g);
+
+  return ret;
+}
+
+/*************************************************************************/
+/*                          internal functions                           */
+/*************************************************************************/
+
+void widget_add_child_internal(
+    gui *_gui, widget *parent, widget *child, int position)
+{
+  struct widget *p = parent;
+  struct widget *c = child;
+  struct widget_list *new;
+  struct widget_list *prev, *cur;
+  int i;
+
+  new = calloc(1, sizeof(struct widget_list));
+  if (new == NULL) OOM;
+
+  new->item = c;
+  c->parent = p;
+
+  prev = NULL;
+  cur = p->children;
+
+  for (i = 0; position < 0 || i < position; i++) {
+    if (cur == NULL) break;
+    prev = cur;
+    cur = cur->next;
+  }
+
+  /* TODO: warn/err if i != position+1? */
+
+  if (prev == NULL) {
+    /* new is at head */
+    new->next = p->children;
+    if (p->children != NULL) new->last = p->children->last;
+    else                     new->last = new;
+    p->children = new;
+    goto repack;
+  }
+
+  if (cur == NULL) {
+    /* new is at tail */
+    prev->next = new;
+    p->children->last = new;
+    goto repack;
+  }
+
+  /* new is between two existing items */
+  prev->next = new;
+  new->next = cur;
+
+repack:
+  send_event(_gui, REPACK, p->id);
+}
+
+void widget_del_child_internal(gui *_gui, widget *parent, widget *child)
+{
+  struct widget *p = parent;
+  struct widget *c = child;
+  struct widget_list *prev, *cur;
+
+  c->parent = NULL;
+
+  prev = NULL;
+  cur = p->children;
+
+  while (cur != NULL && cur->item != c) {
+    prev = cur;
+    cur = cur->next;
+  }
+
+  if (cur == NULL) ERR("child not found\n");
+
+  if (prev == NULL) {
+    /* child is at head */
+    p->children = cur->next;
+    if (p->children != NULL) p->children->last = cur->last;
+    goto done;
+  }
+
+  if (cur->next == NULL) {
+    /* child is last (and prev is != NULL) */
+    prev->next = NULL;
+    p->children->last = prev;
+    goto done;
+  }
+
+  /* child is between two existing items */
+  prev->next = cur->next;
+
+done:
+  free(cur);
+  send_event(_gui, REPACK, p->id);
+}
+
+int widget_get_child_position(gui *_gui, widget *parent, widget *child)
+{
+  struct widget *p = parent;
+  struct widget *c = child;
+  struct widget_list *cur;
+  int i = 0;
+
+  cur = p->children;
+
+  while (cur != NULL && cur->item != c) {
+    cur = cur->next;
+    i++;
+  }
+
+  if (cur == NULL) return -1;
+  return i;
+}
+
+/*************************************************************************/
+/*                           default functions                           */
+/*************************************************************************/
+
+static void default_clear(gui *_gui, widget *_this)
+{
+  struct gui *g = _gui;
+  struct widget *this = _this;
+  x_fill_rectangle(g->x, g->xwin, BACKGROUND_COLOR,
+      this->x, this->y, this->width, this->height);
+}
+
+static void default_repack(gui *gui, widget *_this)
+{
+  struct widget *this = _this;
+  return this->parent->repack(gui, this->parent);
+}
+
+static void default_add_child(
+    gui *_gui, widget *_this, widget *child, int position)
+{
+  struct widget *this = _this;
+  WARN("cannot add child to widget %s\n", widget_name(this->type));
+}
+
+static void default_del_child( gui *_gui, widget *_this, widget *child)
+{
+  struct widget *this = _this;
+  WARN("cannot del child from widget %s\n", widget_name(this->type));
+}
+
+static void default_allocate(
+    gui *gui, widget *_this, int x, int y, int width, int height)
+{
+  struct widget *this = _this;
+  this->x = x;
+  this->y = y;
+  this->width = width;
+  this->height = height;
+}
+
+static void default_hints(gui *g, widget *this, int *width, int *height)
+{
+  *width = 1;
+  *height = 1;
+}
+
+static void default_button(gui *gui, widget *_this, int x, int y,
+    int key_modifiers, int button, int up)
+{
+  /* nothing */
+}
+
+/*************************************************************************/
+/*                             utils functions                           */
+/*************************************************************************/
+
+void widget_add_child(gui *_gui, widget *parent, widget *child, int position)
+{
+  struct widget *this = parent;
+  glock(_gui);
+  this->add_child(_gui, parent, child, position);
+  gunlock(_gui);
+}
+
+void widget_del_child(gui *_gui, widget *parent, widget *child)
+{
+  struct widget *this = parent;
+  glock(_gui);
+  this->del_child(_gui, parent, child);
+  gunlock(_gui);
+}
+
+void widget_dirty(gui *_gui, widget *_this)
+{
+  struct gui *g = _gui;
+  struct widget *this = _this;
+  glock(g);
+  send_event(g, DIRTY, this->id);
+  gunlock(g);
+}
+
+static const char *names[] = {
+  "TOPLEVEL_WINDOW", "CONTAINER", "POSITIONER", "TEXT_LIST",
+  "XY_PLOT", "BUTTON", "LABEL", "TIMELINE", "SPACE", "IMAGE"
+};
+const char *widget_name(enum widget_type type)
+{
+  switch (type) {
+  case TOPLEVEL_WINDOW:
+  case CONTAINER:
+  case POSITIONER:
+  case TEXT_LIST:
+  case XY_PLOT:
+  case BUTTON:
+  case LABEL:
+  case TIMELINE:
+  case SPACE:
+  case IMAGE:
+    return names[type];
+  }
+  return "UNKNOWN (error)";
+}
+
+/*************************************************************************/
+/*                             find a widget                             */
+/*************************************************************************/
+
+/* TODO: optimize traversal and also use a cache */
+struct widget *_find_widget(struct widget *c, int id)
+{
+  struct widget_list *l;
+  struct widget *ret;
+  if (c == NULL) return NULL;
+  if (c->id == id) return c;
+  l = c->children;
+  while (l) {
+    ret = _find_widget(l->item, id);
+    if (ret != NULL) return ret;
+    l = l->next;
+  }
+  return NULL;
+}
+
+struct widget *find_widget(struct gui *g, int id)
+{
+  struct widget_list *l;
+  struct widget *ret;
+
+  l = g->toplevel;
+
+  while (l) {
+    ret = _find_widget(l->item, id);
+    if (ret != NULL) return ret;
+    l = l->next;
+  }
+
+  return NULL;
+}
diff --git a/common/utils/T/tracer/gui/x.c b/common/utils/T/tracer/gui/x.c
new file mode 100644
index 0000000000000000000000000000000000000000..af3d05cc14b68936fa6be574e35b8e53f0201e21
--- /dev/null
+++ b/common/utils/T/tracer/gui/x.c
@@ -0,0 +1,435 @@
+#include "x.h"
+#include "x_defs.h"
+#include "gui_defs.h"
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int x_connection_fd(x_connection *_x)
+{
+  struct x_connection *x = _x;
+  return ConnectionNumber(x->d);
+}
+
+static GC create_gc(Display *d, char *color)
+{
+  GC ret = XCreateGC(d, DefaultRootWindow(d), 0, NULL);
+  XGCValues gcv;
+  XColor rcol, scol;
+
+  XCopyGC(d, DefaultGC(d, DefaultScreen(d)), -1L, ret);
+  if (XAllocNamedColor(d, DefaultColormap(d, DefaultScreen(d)),
+                      color, &scol, &rcol)) {
+    gcv.foreground = scol.pixel;
+    XChangeGC(d, ret, GCForeground, &gcv);
+  } else ERR("X: could not allocate color '%s'\n", color);
+
+  return ret;
+}
+
+int x_new_color(x_connection *_x, char *color)
+{
+  struct x_connection *x = _x;
+  x->ncolors++;
+
+  x->colors = realloc(x->colors, x->ncolors * sizeof(GC));
+  if (x->colors == NULL) OOM;
+  x->colors[x->ncolors-1] = create_gc(x->d, color);
+
+  x->xft_colors = realloc(x->xft_colors, x->ncolors * sizeof(XftColor));
+  if (x->xft_colors == NULL) OOM;
+  if (XftColorAllocName(x->d, DefaultVisual(x->d, DefaultScreen(x->d)),
+      DefaultColormap(x->d, DefaultScreen(x->d)),
+      color, &x->xft_colors[x->ncolors-1]) == False)
+    ERR("could not allocate color '%s'\n", color);
+
+  return x->ncolors - 1;
+}
+
+int x_new_font(x_connection *_x, char *font)
+{
+  struct x_connection *x = _x;
+  /* TODO: allocate fonts only once */
+  x->nfonts++;
+  x->fonts = realloc(x->fonts, x->nfonts * sizeof(XftFont *));
+  if (x->fonts == NULL) OOM;
+  x->fonts[x->nfonts-1] = XftFontOpenName(x->d, DefaultScreen(x->d), font);
+  if (x->fonts[x->nfonts-1] == NULL)
+    ERR("failed allocating font '%s'\n", font);
+  return x->nfonts - 1;
+}
+
+x_connection *x_open(void)
+{
+  struct x_connection *ret;
+
+  ret = calloc(1, sizeof(struct x_connection));
+  if (ret == NULL) OOM;
+
+  ret->d = XOpenDisplay(0);
+  LOGD("XOpenDisplay display %p return x_connection %p\n", ret->d, ret);
+  if (ret->d == NULL) ERR("error calling XOpenDisplay: no X? you root?\n");
+
+  x_new_color(ret, "white");    /* background color */
+  x_new_color(ret, "black");    /* foreground color */
+
+  x_new_font(ret, "sans-8");
+
+  return ret;
+}
+
+x_window *x_create_window(x_connection *_x, int width, int height,
+    char *title)
+{
+  struct x_connection *x = _x;
+  struct x_window *ret;
+
+  ret = calloc(1, sizeof(struct x_window));
+  if (ret == NULL) OOM;
+
+  ret->w = XCreateSimpleWindow(x->d, DefaultRootWindow(x->d), 0, 0,
+      width, height, 0, WhitePixel(x->d, DefaultScreen(x->d)),
+      WhitePixel(x->d, DefaultScreen(x->d)));
+  ret->width = width;
+  ret->height = height;
+
+  XStoreName(x->d, ret->w, title);
+
+  ret->p = XCreatePixmap(x->d, ret->w, width, height,
+      DefaultDepth(x->d, DefaultScreen(x->d)));
+  XFillRectangle(x->d, ret->p, x->colors[BACKGROUND_COLOR],
+      0, 0, width, height);
+
+  ret->xft = XftDrawCreate(x->d, ret->p,
+      DefaultVisual(x->d, DefaultScreen(x->d)),
+      DefaultColormap(x->d, DefaultScreen(x->d)));
+  if (ret->xft == NULL) ERR("XftDrawCreate failed\n");
+
+  /* enable backing store */
+  {
+    XSetWindowAttributes att;
+    att.backing_store = Always;
+    XChangeWindowAttributes(x->d, ret->w, CWBackingStore, &att);
+  }
+
+  XSelectInput(x->d, ret->w,
+      KeyPressMask      |
+      ButtonPressMask   |
+      ButtonReleaseMask |
+      PointerMotionMask |
+      ExposureMask      |
+      StructureNotifyMask);
+
+  XMapWindow(x->d, ret->w);
+
+#if 0
+  /* wait for window to be mapped */
+  LOGD("wait for map\n");
+  while (1) {
+    XEvent ev;
+    //XWindowEvent(x->d, ret->w, StructureNotifyMask, &ev);
+    XWindowEvent(x->d, ret->w, ExposureMask, &ev);
+    LOGD("got ev %d\n", ev.type);
+    //if (ev.type == MapNotify) break;
+    if (ev.type == Expose) break;
+  }
+  LOGD("XXX create connection %p window %p (win id %d pixmap %d) w h %d %d\n", x, ret, (int)ret->w, (int)ret->p, width, height);
+#endif
+
+  return ret;
+}
+
+x_image *x_create_image(x_connection *_x, unsigned char *data,
+    int width, int height)
+{
+  struct x_connection *x = _x;
+  struct x_image *ret;
+  XImage *ximage;
+  XVisualInfo *vs;
+  XVisualInfo template;
+  int nvs;
+  Visual *v;
+
+  ret = calloc(1, sizeof(struct x_image)); if (ret == NULL) OOM;
+
+  template.class = TrueColor;
+  template.depth = 24;
+  template.red_mask = 0xff0000;
+  template.green_mask = 0x00ff00;
+  template.blue_mask = 0x0000ff;
+  template.bits_per_rgb = 8;
+
+  vs = XGetVisualInfo(x->d, VisualDepthMask | VisualClassMask |
+      VisualRedMaskMask | VisualGreenMaskMask | VisualBlueMaskMask |
+      VisualBitsPerRGBMask, &template, &nvs);
+  if (vs == NULL || nvs == 0) ERR("no good visual found\n");
+  v = vs[0].visual;
+  XFree(vs);
+
+  ximage = XCreateImage(x->d, v, 24, ZPixmap, 0,
+      (char*)data, width, height, 32, 0);
+  if (ximage == NULL) ERR("image creation failed\n");
+
+  ret->p = XCreatePixmap(x->d, DefaultRootWindow(x->d), width, height, 24);
+
+  XPutImage(x->d, ret->p, DefaultGC(x->d, DefaultScreen(x->d)),
+      ximage, 0, 0, 0, 0, width, height);
+
+  /* TODO: be sure it's fine to set data to NULL */
+  ximage->data = NULL;
+  XDestroyImage(ximage);
+
+  ret->width = width;
+  ret->height = height;
+
+  return ret;
+}
+
+static struct toplevel_window_widget *find_x_window(struct gui *g, Window id)
+{
+  struct widget_list *cur;
+  struct toplevel_window_widget *w;
+  struct x_window *xw;
+  cur = g->toplevel;
+  while (cur) {
+    w = (struct toplevel_window_widget *)cur->item;
+    xw = w->x;
+    if (xw->w == id) return w;
+    cur = cur->next;
+  }
+  return NULL;
+}
+
+void x_events(gui *_gui)
+{
+  struct gui *g = _gui;
+  struct widget_list *cur;
+  struct x_connection *x = g->x;
+  struct toplevel_window_widget *w;
+
+  LOGD("x_events START\n");
+  /* preprocessing (to "compress" events) */
+  cur = g->toplevel;
+  while (cur) {
+    struct x_window *xw;
+    w = (struct toplevel_window_widget *)cur->item;
+    xw = w->x;
+    xw->redraw = 0;
+    xw->repaint = 0;
+    xw->resize = 0;
+    cur = cur->next;
+  }
+
+  while (XPending(x->d)) {
+    XEvent ev;
+    XNextEvent(x->d, &ev);
+    LOGD("XEV %d\n", ev.type);
+    switch (ev.type) {
+    case MapNotify:
+    case Expose:
+      if ((w = find_x_window(g, ev.xexpose.window)) != NULL) {
+        struct x_window *xw = w->x;
+        xw->redraw = 1;
+      }
+      break;
+    case ConfigureNotify:
+      if ((w = find_x_window(g, ev.xconfigure.window)) != NULL) {
+        struct x_window *xw = w->x;
+        xw->resize = 1;
+        xw->new_width = ev.xconfigure.width;
+        xw->new_height = ev.xconfigure.height;
+        if (xw->new_width < 10) xw->new_width = 10;
+        if (xw->new_height < 10) xw->new_height = 10;
+        LOGD("ConfigureNotify %d %d\n", ev.xconfigure.width, ev.xconfigure.height);
+      }
+      break;
+    case ButtonPress:
+      if ((w = find_x_window(g, ev.xbutton.window)) != NULL) {
+        int key_modifiers = 0;
+        if (ev.xbutton.state & ShiftMask)   key_modifiers |= KEY_SHIFT;
+        if (ev.xbutton.state & Mod1Mask)    key_modifiers |= KEY_ALT;
+        if (ev.xbutton.state & ControlMask) key_modifiers |= KEY_CONTROL;
+        w->common.button(g, w, ev.xbutton.x, ev.xbutton.y, key_modifiers,
+            ev.xbutton.button, 0);
+      }
+      break;
+    case ButtonRelease:
+      if ((w = find_x_window(g, ev.xbutton.window)) != NULL) {
+        int key_modifiers = 0;
+        if (ev.xbutton.state & ShiftMask)   key_modifiers |= KEY_SHIFT;
+        if (ev.xbutton.state & Mod1Mask)    key_modifiers |= KEY_ALT;
+        if (ev.xbutton.state & ControlMask) key_modifiers |= KEY_CONTROL;
+        w->common.button(g, w, ev.xbutton.x, ev.xbutton.y, key_modifiers,
+            ev.xbutton.button, 1);
+      }
+      break;
+#if 0
+    case MapNotify:
+      if ((w = find_x_window(g, ev.xmap.window)) != NULL) {
+        struct x_window *xw = w->x;
+        xw->repaint = 1;
+      }
+      break;
+#endif
+    default: if (gui_logd) WARN("TODO: X event type %d\n", ev.type); break;
+    }
+  }
+
+  /* postprocessing */
+  LOGD("post processing\n");
+  cur = g->toplevel;
+  while (cur) {
+    struct toplevel_window_widget *w =
+        (struct toplevel_window_widget *)cur->item;
+    struct x_window *xw = w->x;
+    if (xw->resize) {
+      LOGD("resize old %d %d new %d %d\n", xw->width, xw->height, xw->new_width, xw->new_height);
+      if (xw->width != xw->new_width || xw->height != xw->new_height) {
+        w->common.allocate(g, w, 0, 0, xw->new_width, xw->new_height);
+        xw->width = xw->new_width;
+        xw->height = xw->new_height;
+        XftDrawDestroy(xw->xft);
+        XFreePixmap(x->d, xw->p);
+        xw->p = XCreatePixmap(x->d, xw->w, xw->width, xw->height,
+            DefaultDepth(x->d, DefaultScreen(x->d)));
+        XFillRectangle(x->d, xw->p, x->colors[BACKGROUND_COLOR],
+            0, 0, xw->width, xw->height);
+        xw->xft = XftDrawCreate(x->d, xw->p,
+            DefaultVisual(x->d, DefaultScreen(x->d)),
+            DefaultColormap(x->d, DefaultScreen(x->d)));
+        if (xw->xft == NULL) ERR("XftDrawCreate failed\n");
+
+        //xw->repaint = 1;
+      }
+    }
+    if (xw->repaint) {
+      w->common.paint(g, w);
+      xw->redraw = 1;
+    }
+    if (xw->redraw) {
+      struct x_connection *x = g->x;
+      LOGD("XCopyArea w h %d %d\n", xw->width, xw->height);
+      XCopyArea(x->d, xw->p, xw->w, x->colors[1],
+          0, 0, xw->width, xw->height, 0, 0);
+    }
+    cur = cur->next;
+  }
+  LOGD("x_events DONE\n");
+}
+
+void x_flush(x_connection *_x)
+{
+  struct x_connection *x = _x;
+  XFlush(x->d);
+}
+
+void x_text_get_dimensions(x_connection *_c, int font, const char *t,
+    int *width, int *height, int *baseline)
+{
+  struct x_connection *c = _c;
+  XGlyphInfo ext;
+
+  XftTextExtentsUtf8(c->d, c->fonts[font], (FcChar8 *)t, strlen(t), &ext);
+
+  *width = ext.width;
+  *height = c->fonts[font]->height;
+  *baseline = c->fonts[font]->ascent;
+}
+
+/***********************************************************************/
+/*                    public drawing functions                         */
+/***********************************************************************/
+
+void x_draw_line(x_connection *_c, x_window *_w, int color,
+    int x1, int y1, int x2, int y2)
+{
+  struct x_connection *c = _c;
+  struct x_window *w = _w;
+  XDrawLine(c->d, w->p, c->colors[color], x1, y1, x2, y2);
+}
+
+void x_draw_rectangle(x_connection *_c, x_window *_w, int color,
+    int x, int y, int width, int height)
+{
+  struct x_connection *c = _c;
+  struct x_window *w = _w;
+  XDrawRectangle(c->d, w->p, c->colors[color], x, y, width, height);
+}
+
+void x_fill_rectangle(x_connection *_c, x_window *_w, int color,
+    int x, int y, int width, int height)
+{
+  struct x_connection *c = _c;
+  struct x_window *w = _w;
+  XFillRectangle(c->d, w->p, c->colors[color], x, y, width, height);
+}
+
+void x_draw_string(x_connection *_c, x_window *_w, int font, int color,
+    int x, int y, const char *t)
+{
+  struct x_connection *c = _c;
+  struct x_window *w = _w;
+  int tlen = strlen(t);
+  XftDrawStringUtf8(w->xft, &c->xft_colors[color], c->fonts[font],
+      x, y, (const unsigned char *)t, tlen);
+}
+
+void x_draw_clipped_string(x_connection *_c, x_window *_w, int font,
+    int color, int x, int y, const char *t,
+    int clipx, int clipy, int clipwidth, int clipheight)
+{
+  struct x_window *w = _w;
+
+  XRectangle clip = { clipx, clipy, clipwidth, clipheight };
+  if (XftDrawSetClipRectangles(w->xft, 0, 0, &clip, 1) == False) abort();
+  x_draw_string(_c, _w, font, color, x, y, t);
+  if (XftDrawSetClip(w->xft, NULL) == False) abort();
+}
+
+void x_draw_image(x_connection *_c, x_window *_w, x_image *_img, int x, int y)
+{
+  struct x_connection *c = _c;
+  struct x_window *w = _w;
+  struct x_image *img = _img;
+
+  XCopyArea(c->d, img->p, w->p, DefaultGC(c->d, DefaultScreen(c->d)),
+      0, 0, img->width, img->height, x, y);
+}
+
+void x_draw(x_connection *_c, x_window *_w)
+{
+  struct x_connection *c = _c;
+  struct x_window *w = _w;
+  LOGD("x_draw XCopyArea w h %d %d display %p window %d pixmap %d\n", w->width, w->height, c->d, (int)w->w, (int)w->p);
+  XCopyArea(c->d, w->p, w->w, c->colors[1], 0, 0, w->width, w->height, 0, 0);
+}
+
+/* those two special functions are to plot many points
+ * first call x_add_point many times then x_plot_points once
+ */
+void x_add_point(x_connection *_c, int x, int y)
+{
+  struct x_connection *c = _c;
+
+  if (c->pts_size == c->pts_maxsize) {
+    c->pts_maxsize += 65536;
+    c->pts = realloc(c->pts, c->pts_maxsize * sizeof(XPoint));
+    if (c->pts == NULL) OOM;
+  }
+
+  c->pts[c->pts_size].x = x;
+  c->pts[c->pts_size].y = y;
+  c->pts_size++;
+}
+
+void x_plot_points(x_connection *_c, x_window *_w, int color)
+{
+  struct x_connection *c = _c;
+  LOGD("x_plot_points %d points\n", c->pts_size);
+  struct x_window *w = _w;
+  XDrawPoints(c->d, w->p, c->colors[color], c->pts, c->pts_size,
+      CoordModeOrigin);
+  c->pts_size = 0;
+}
diff --git a/common/utils/T/tracer/gui/x.h b/common/utils/T/tracer/gui/x.h
new file mode 100644
index 0000000000000000000000000000000000000000..d749d9acc4d9532891eea1931ed0447a197e9231
--- /dev/null
+++ b/common/utils/T/tracer/gui/x.h
@@ -0,0 +1,61 @@
+#ifndef _X_H_
+#define _X_H_
+
+/* public X interface */
+
+typedef void x_connection;
+typedef void x_window;
+typedef void x_image;
+
+x_connection *x_open(void);
+
+x_window *x_create_window(x_connection *x, int width, int height,
+    char *title);
+
+x_image *x_create_image(x_connection *x, unsigned char *data,
+    int width, int height);
+
+int x_connection_fd(x_connection *x);
+
+void x_flush(x_connection *x);
+
+int x_new_color(x_connection *x, char *color);
+int x_new_font(x_connection *x, char *font);
+
+/* for x_events, we pass the gui */
+#include "gui.h"
+void x_events(gui *gui);
+
+void x_text_get_dimensions(x_connection *, int font, const char *t,
+                           int *width, int *height, int *baseline);
+
+/* drawing functions */
+
+void x_draw_line(x_connection *c, x_window *w, int color,
+    int x1, int y1, int x2, int y2);
+
+void x_draw_rectangle(x_connection *c, x_window *w, int color,
+    int x, int y, int width, int height);
+
+void x_fill_rectangle(x_connection *c, x_window *w, int color,
+    int x, int y, int width, int height);
+
+void x_draw_string(x_connection *_c, x_window *_w, int font, int color,
+    int x, int y, const char *t);
+
+void x_draw_clipped_string(x_connection *_c, x_window *_w, int font,
+    int color, int x, int y, const char *t,
+    int clipx, int clipy, int clipwidth, int clipheight);
+
+void x_draw_image(x_connection *c, x_window *w, x_image *img, int x, int y);
+
+/* specials functions to plot many points
+ * you call several times x_add_point() then x_plot_points()
+ */
+void x_add_point(x_connection *c, int x, int y);
+void x_plot_points(x_connection *c, x_window *w, int color);
+
+/* this function copies the pixmap to the window */
+void x_draw(x_connection *c, x_window *w);
+
+#endif /* _X_H_ */
diff --git a/common/utils/T/tracer/gui/x_defs.h b/common/utils/T/tracer/gui/x_defs.h
new file mode 100644
index 0000000000000000000000000000000000000000..d0595ef51de128e191ae1c2a931cb422ef1b1cff
--- /dev/null
+++ b/common/utils/T/tracer/gui/x_defs.h
@@ -0,0 +1,37 @@
+#ifndef _X_DEFS_H_
+#define _X_DEFS_H_
+
+#include <X11/Xlib.h>
+#include <Xft.h>
+
+struct x_connection {
+  Display *d;
+  GC *colors;
+  XftColor *xft_colors;
+  int ncolors;
+  XPoint *pts;
+  int pts_size;
+  int pts_maxsize;
+  XftFont **fonts;
+  int nfonts;
+};
+
+struct x_window {
+  Window w;
+  Pixmap p;
+  int width;
+  int height;
+  XftDraw *xft;
+  /* below: internal data used for X events handling */
+  int redraw;
+  int repaint;
+  int resize, new_width, new_height;
+};
+
+struct x_image {
+  Pixmap p;
+  int width;
+  int height;
+};
+
+#endif /* _X_DEFS_H_ */
diff --git a/common/utils/T/tracer/gui/xy_plot.c b/common/utils/T/tracer/gui/xy_plot.c
new file mode 100644
index 0000000000000000000000000000000000000000..7d20a027a155524481d6aab45c2ada8361b83180
--- /dev/null
+++ b/common/utils/T/tracer/gui/xy_plot.c
@@ -0,0 +1,292 @@
+#include "gui.h"
+#include "gui_defs.h"
+#include "x.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+static void paint(gui *_gui, widget *_this)
+{
+  struct gui *g = _gui;
+  struct xy_plot_widget *this = _this;
+  int wanted_plot_width, allocated_plot_width;
+  int wanted_plot_height, allocated_plot_height;
+  float pxsize;
+  float ticdist;
+  float tic;
+  float ticstep;
+  int k, kmin, kmax;
+  float allocated_xmin, allocated_xmax;
+  float allocated_ymin, allocated_ymax;
+  float center;
+  int i;
+  int n;
+
+# define FLIP(v) (-(v) + allocated_plot_height-1)
+
+  LOGD("PAINT xy plot xywh %d %d %d %d\n", this->common.x, this->common.y, this->common.width, this->common.height);
+
+//x_draw_rectangle(g->x, g->xwin, 1, this->common.x, this->common.y, this->common.width, this->common.height);
+
+  wanted_plot_width = this->wanted_width;
+  allocated_plot_width = this->common.width - this->vrule_width;
+  wanted_plot_height = this->wanted_height;
+  allocated_plot_height = this->common.height - this->label_height * 2;
+
+  /* plot zone */
+  /* TODO: refine height - height of hrule text may be != from label */
+  x_draw_rectangle(g->x, g->xwin, 1,
+      this->common.x + this->vrule_width,
+      this->common.y,
+      this->common.width - this->vrule_width -1, /* -1 to see right border */
+      this->common.height - this->label_height * 2);
+
+  /* horizontal tics */
+  pxsize = (this->xmax - this->xmin) / wanted_plot_width;
+  ticdist = 100;
+  tic = floor(log10(ticdist * pxsize));
+  ticstep = powf(10, tic);
+  center = (this->xmax + this->xmin) / 2;
+  allocated_xmin = center - ((this->xmax - this->xmin) *
+                             allocated_plot_width / wanted_plot_width) / 2;
+  allocated_xmax = center + ((this->xmax - this->xmin) *
+                             allocated_plot_width / wanted_plot_width) / 2;
+  /* adjust tic if too tight */
+  LOGD("pre x ticstep %g\n", ticstep);
+  while (1) {
+    if (ticstep / (allocated_xmax - allocated_xmin)
+                * (allocated_plot_width - 1) > 40) break;
+    ticstep *= 2;
+  }
+  LOGD("post x ticstep %g\n", ticstep);
+  LOGD("xmin/max %g %g width wanted allocated %d %d alloc xmin/max %g %g ticstep %g\n", this->xmin, this->xmax, wanted_plot_width, allocated_plot_width, allocated_xmin, allocated_xmax, ticstep);
+  kmin = ceil(allocated_xmin / ticstep);
+  kmax = floor(allocated_xmax / ticstep);
+  for (k = kmin; k <= kmax; k++) {
+/*
+    (k * ticstep - allocated_xmin) / (allocated_max - allocated_xmin) =
+    (x - 0) / (allocated_plot_width-1 - 0)
+ */
+    char v[64];
+    int vwidth, dummy;
+    float x = (k * ticstep - allocated_xmin) /
+              (allocated_xmax - allocated_xmin) *
+              (allocated_plot_width - 1);
+    x_draw_line(g->x, g->xwin, FOREGROUND_COLOR,
+        this->common.x + this->vrule_width + x,
+        this->common.y + this->common.height - this->label_height * 2,
+        this->common.x + this->vrule_width + x,
+        this->common.y + this->common.height - this->label_height * 2 - 5);
+    sprintf(v, "%g", k * ticstep);
+    x_text_get_dimensions(g->x, DEFAULT_FONT, v, &vwidth, &dummy, &dummy);
+    x_draw_string(g->x, g->xwin, DEFAULT_FONT, FOREGROUND_COLOR,
+        this->common.x + this->vrule_width + x - vwidth/2,
+        this->common.y + this->common.height - this->label_height * 2 +
+            this->label_baseline,
+        v);
+    LOGD("tic k %d val %g x %g\n", k, k * ticstep, x);
+  }
+
+  /* vertical tics */
+  pxsize = (this->ymax - this->ymin) / wanted_plot_height;
+  ticdist = 30;
+  tic = floor(log10(ticdist * pxsize));
+  ticstep = powf(10, tic);
+  center = (this->ymax + this->ymin) / 2;
+  allocated_ymin = center - ((this->ymax - this->ymin) *
+                             allocated_plot_height / wanted_plot_height) / 2;
+  allocated_ymax = center + ((this->ymax - this->ymin) *
+                             allocated_plot_height / wanted_plot_height) / 2;
+  /* adjust tic if too tight */
+  LOGD("pre y ticstep %g\n", ticstep);
+  while (1) {
+    if (ticstep / (allocated_ymax - allocated_ymin)
+                * (allocated_plot_height - 1) > 20) break;
+    ticstep *= 2;
+  }
+  LOGD("post y ticstep %g\n", ticstep);
+  LOGD("ymin/max %g %g height wanted allocated %d %d alloc ymin/max %g %g ticstep %g\n", this->ymin, this->ymax, wanted_plot_height, allocated_plot_height, allocated_ymin, allocated_ymax, ticstep);
+  kmin = ceil(allocated_ymin / ticstep);
+  kmax = floor(allocated_ymax / ticstep);
+  for (k = kmin; k <= kmax; k++) {
+    char v[64];
+    int vwidth, dummy;
+    float y = (k * ticstep - allocated_ymin) /
+              (allocated_ymax - allocated_ymin) *
+              (allocated_plot_height - 1);
+    sprintf(v, "%g", k * ticstep);
+    x_text_get_dimensions(g->x, DEFAULT_FONT, v, &vwidth, &dummy, &dummy);
+    x_draw_line(g->x, g->xwin, FOREGROUND_COLOR,
+        this->common.x + this->vrule_width,
+        this->common.y + FLIP(y),
+        this->common.x + this->vrule_width + 5,
+        this->common.y + FLIP(y));
+    x_draw_string(g->x, g->xwin, DEFAULT_FONT, FOREGROUND_COLOR,
+        this->common.x + this->vrule_width - vwidth - 2,
+        this->common.y + FLIP(y) - this->label_height/2+this->label_baseline,
+        v);
+  }
+
+  /* label at bottom, in the middle */
+  x_draw_string(g->x, g->xwin, DEFAULT_FONT, FOREGROUND_COLOR,
+      this->common.x + (this->common.width - this->label_width) / 2,
+      this->common.y + this->common.height - this->label_height
+          + this->label_baseline,
+      this->label);
+
+  for (n = 0; n < this->nplots; n++) {
+    /* points */
+    float ax, bx, ay, by;
+    ax = (allocated_plot_width-1) / (allocated_xmax - allocated_xmin);
+    bx = -ax * allocated_xmin;
+    ay = (allocated_plot_height-1) / (allocated_ymax - allocated_ymin);
+    by = -ay * allocated_ymin;
+    for (i = 0; i < this->plots[n].npoints; i++) {
+      int x, y;
+      x = ax * this->plots[n].x[i] + bx;
+      y = ay * this->plots[n].y[i] + by;
+      if (x >= 0 && x < allocated_plot_width &&
+          y >= 0 && y < allocated_plot_height)
+        x_add_point(g->x,
+            this->common.x + this->vrule_width + x,
+            this->common.y + FLIP(y));
+    }
+    x_plot_points(g->x, g->xwin, this->plots[n].color);
+  }
+}
+
+static void hints(gui *_gui, widget *_w, int *width, int *height)
+{
+  struct xy_plot_widget *w = _w;
+  *width = w->wanted_width + w->vrule_width;
+  *height = w->wanted_height + w->label_height * 2; /* TODO: refine */
+  LOGD("HINTS xy plot wh %d %d (vrule_width %d) (wanted wh %d %d)\n", *width, *height, w->vrule_width, w->wanted_width, w->wanted_height);
+}
+
+widget *new_xy_plot(gui *_gui, int width, int height, char *label,
+    int vruler_width)
+{
+  struct gui *g = _gui;
+  struct xy_plot_widget *w;
+
+  glock(g);
+
+  w = new_widget(g, XY_PLOT, sizeof(struct xy_plot_widget));
+
+  w->label = strdup(label); if (w->label == NULL) OOM;
+  /* TODO: be sure calling X there is valid wrt "global model" (we are
+   * not in the "gui thread") */
+  x_text_get_dimensions(g->x, DEFAULT_FONT, label,
+      &w->label_width, &w->label_height, &w->label_baseline);
+  LOGD("XY PLOT label wh %d %d\n", w->label_width, w->label_height);
+
+  w->wanted_width = width;
+  w->wanted_height = height;
+  w->vrule_width = vruler_width;
+
+  w->xmin = -1;
+  w->xmax = 1;
+  w->ymin = -1;
+  w->ymax = 1;
+  w->plots = NULL;
+  w->nplots = 0;
+
+  w->common.paint = paint;
+  w->common.hints = hints;
+
+  gunlock(g);
+
+  return w;
+}
+
+/*************************************************************************/
+/*                           public functions                            */
+/*************************************************************************/
+
+int xy_plot_new_plot(gui *_gui, widget *_this, int color)
+{
+  int ret;
+  struct gui *g = _gui;
+  struct xy_plot_widget *this = _this;
+
+  glock(g);
+
+  ret = this->nplots;
+
+  this->nplots++;
+  this->plots = realloc(this->plots,
+      this->nplots * sizeof(struct xy_plot_plot));
+  if (this->plots == NULL) abort();
+
+  this->plots[ret].x = NULL;
+  this->plots[ret].y = NULL;
+  this->plots[ret].npoints = 0;
+  this->plots[ret].color = color;
+
+  gunlock(g);
+
+  return ret;
+}
+
+void xy_plot_set_range(gui *_gui, widget *_this,
+    float xmin, float xmax, float ymin, float ymax)
+{
+  struct gui *g = _gui;
+  struct xy_plot_widget *this = _this;
+
+  glock(g);
+
+  this->xmin = xmin;
+  this->xmax = xmax;
+  this->ymin = ymin;
+  this->ymax = ymax;
+
+  send_event(g, DIRTY, this->common.id);
+
+  gunlock(g);
+}
+
+void xy_plot_set_points(gui *_gui, widget *_this, int plot,
+    int npoints, float *x, float *y)
+{
+  struct gui *g = _gui;
+  struct xy_plot_widget *this = _this;
+
+  glock(g);
+
+  if (npoints != this->plots[plot].npoints) {
+    free(this->plots[plot].x);
+    free(this->plots[plot].y);
+    this->plots[plot].x = calloc(npoints, sizeof(float));
+    if (this->plots[plot].x == NULL) abort();
+    this->plots[plot].y = calloc(npoints, sizeof(float));
+    if (this->plots[plot].y == NULL) abort();
+    this->plots[plot].npoints = npoints;
+  }
+
+  memcpy(this->plots[plot].x, x, npoints * sizeof(float));
+  memcpy(this->plots[plot].y, y, npoints * sizeof(float));
+
+  send_event(g, DIRTY, this->common.id);
+
+  gunlock(g);
+}
+
+void xy_plot_get_dimensions(gui *_gui, widget *_this, int *width, int *height)
+{
+  struct gui *g = _gui;
+  struct xy_plot_widget *this = _this;
+
+  glock(g);
+
+  if (this->common.width == 0 || this->common.height == 0) {
+    *width = this->wanted_width;
+    *height = this->wanted_height;
+  } else {
+    *width = this->common.width - this->vrule_width;
+    *height = this->common.height - this->label_height * 2;
+  }
+
+  gunlock(g);
+}
diff --git a/common/utils/T/tracer/handler.c b/common/utils/T/tracer/handler.c
new file mode 100644
index 0000000000000000000000000000000000000000..1ee9db1adee05d299c164abfddf96e6908e759b8
--- /dev/null
+++ b/common/utils/T/tracer/handler.c
@@ -0,0 +1,82 @@
+#include "handler.h"
+#include "event.h"
+#include "database.h"
+#include <stdlib.h>
+#include <stdio.h>
+
+typedef void (*handler_function)(void *p, event e);
+
+struct handler_data {
+  handler_function f;
+  void *p;
+  unsigned long id;
+};
+
+struct handler_list {
+  struct handler_data *f;
+  int size;
+  int maxsize;
+};
+
+/* internal definition of an event handler */
+struct _event_handler {
+  void *database;
+  struct handler_list *events;
+  unsigned long next_id;
+};
+
+void handle_event(event_handler *_h, event e)
+{
+  struct _event_handler *h = _h;
+  int i;
+  for (i = 0; i < h->events[e.type].size; i++)
+    h->events[e.type].f[i].f(h->events[e.type].f[i].p, e);
+}
+
+event_handler *new_handler(void *database)
+{
+  struct _event_handler *ret = calloc(1, sizeof(struct _event_handler));
+  if (ret == NULL) abort();
+
+  ret->database = database;
+
+  ret->events = calloc(number_of_ids(database), sizeof(struct handler_list));
+  if (ret->events == NULL) abort();
+
+  ret->next_id = 1;
+
+  return ret;
+}
+
+unsigned long register_handler_function(event_handler *_h, int event_id,
+    void (*f)(void *, event), void *p)
+{
+  struct _event_handler *h = _h;
+  unsigned long ret = h->next_id;
+  struct handler_list *l;
+
+  h->next_id++;
+  if (h->next_id == 2UL * 1024 * 1024 * 1024)
+    { printf("%s:%d: this is bad...\n", __FILE__, __LINE__); abort(); }
+
+  l = &h->events[event_id];
+  if (l->size == l->maxsize) {
+    l->maxsize += 16;
+    l->f = realloc(l->f, l->maxsize * sizeof(struct handler_data));
+    if (l->f == NULL) abort();
+  }
+  l->f[l->size].f = f;
+  l->f[l->size].p = p;
+  l->f[l->size].id = ret;
+
+  l->size++;
+
+  return ret;
+}
+
+void remove_handler_function(event_handler *h, int event_id,
+    unsigned long handler_id)
+{
+  printf("%s:%d: TODO\n", __FILE__, __LINE__);
+  abort();
+}
diff --git a/common/utils/T/tracer/handler.h b/common/utils/T/tracer/handler.h
new file mode 100644
index 0000000000000000000000000000000000000000..5934e1704c52ff3ee9b5fb2239e2f2a602bc5bb9
--- /dev/null
+++ b/common/utils/T/tracer/handler.h
@@ -0,0 +1,16 @@
+#ifndef _HANDLER_H_
+#define _HANDLER_H_
+
+typedef void event_handler;
+
+#include "event.h"
+
+event_handler *new_handler(void *database);
+void handle_event(event_handler *h, event e);
+
+unsigned long register_handler_function(event_handler *_h, int event_id,
+    void (*f)(void *, event), void *p);
+void remove_handler_function(event_handler *h, int event_id,
+    unsigned long handler_id);
+
+#endif /* _HANDLER_H_ */
diff --git a/common/utils/T/tracer/logger/Makefile b/common/utils/T/tracer/logger/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..e5391008380ccb32410dcc92a3a0788bd3ad8a45
--- /dev/null
+++ b/common/utils/T/tracer/logger/Makefile
@@ -0,0 +1,13 @@
+CC=gcc
+CFLAGS=-Wall -g -pthread -I..
+
+OBJS=logger.o textlog.o framelog.o ttilog.o timelog.o ticklog.o
+
+logger.a: $(OBJS)
+	ar cr logger.a $(OBJS)
+
+%.o: %.c
+	$(CC) $(CFLAGS) -o $@ -c $<
+
+clean:
+	rm -f *.a *.o
diff --git a/common/utils/T/tracer/logger/framelog.c b/common/utils/T/tracer/logger/framelog.c
new file mode 100644
index 0000000000000000000000000000000000000000..057960c38246b98b0e4c7be1225ec02dc9238495
--- /dev/null
+++ b/common/utils/T/tracer/logger/framelog.c
@@ -0,0 +1,146 @@
+#include "logger.h"
+#include "logger_defs.h"
+#include "handler.h"
+#include "database.h"
+#include "filter/filter.h"
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+
+struct framelog {
+  struct logger common;
+  void *database;
+  int subframe_arg;
+  int buffer_arg;
+  float *x;
+  float *buffer;
+  int blength;
+  int skip_delay;       /* one frame over 'skip_delay' is truly processed
+                         * 0 to disable (process all data)
+                         */
+  int skip_current;     /* internal data for the skip mechanism */
+  int skip_on;          /* internal data for the skip mechanism */
+};
+
+static void _event(void *p, event e)
+{
+  struct framelog *l = p;
+  int i;
+  int subframe;
+  void *buffer;
+  int bsize;
+  int nsamples;
+
+  if (l->common.filter != NULL && filter_eval(l->common.filter, e) == 0)
+    return;
+
+  subframe = e.e[l->subframe_arg].i;
+  buffer = e.e[l->buffer_arg].b;
+  bsize = e.e[l->buffer_arg].bsize;
+
+  if (l->skip_delay != 0) {
+    if (subframe == 0) {
+      l->skip_current++;
+      if (l->skip_current >= l->skip_delay) {
+        l->skip_on = 0;
+        l->skip_current = 0;
+      } else
+        l->skip_on = 1;
+    }
+  }
+  if (l->skip_on) return;
+
+  nsamples = bsize / (2*sizeof(int16_t));
+
+  if (l->blength != nsamples * 10) {
+    l->blength = nsamples * 10;
+    free(l->x);
+    free(l->buffer);
+    l->x = calloc(l->blength, sizeof(float));
+    if (l->x == NULL) abort();
+    l->buffer = calloc(l->blength, sizeof(float));
+    if (l->buffer == NULL) abort();
+    /* update 'x' */
+    for (i = 0; i < l->blength; i++)
+      l->x[i] = i;
+    /* update 'length' of views */
+    for (i = 0; i < l->common.vsize; i++)
+      l->common.v[i]->set(l->common.v[i], "length", l->blength);
+  }
+
+  /* TODO: compute the LOGs in the plotter (too much useless computations) */
+  for (i = 0; i < nsamples; i++) {
+    int I = ((int16_t *)buffer)[i*2];
+    int Q = ((int16_t *)buffer)[i*2+1];
+    l->buffer[subframe * nsamples + i] = 10*log10(1.0+(float)(I*I+Q*Q));
+  }
+
+  if (subframe == 9)
+    for (i = 0; i < l->common.vsize; i++)
+      l->common.v[i]->append(l->common.v[i], l->x, l->buffer, l->blength);
+}
+
+logger *new_framelog(event_handler *h, void *database,
+    char *event_name, char *subframe_varname, char *buffer_varname)
+{
+  struct framelog *ret;
+  int event_id;
+  database_event_format f;
+  int i;
+
+  ret = calloc(1, sizeof(struct framelog)); if (ret == NULL) abort();
+
+  ret->common.event_name = strdup(event_name);
+  if (ret->common.event_name == NULL) abort();
+  ret->database = database;
+
+  event_id = event_id_from_name(database, event_name);
+
+  ret->common.handler_id = register_handler_function(h,event_id,_event,ret);
+
+  f = get_format(database, event_id);
+
+  /* look for subframe and buffer args */
+  ret->subframe_arg = -1;
+  ret->buffer_arg = -1;
+  for (i = 0; i < f.count; i++) {
+    if (!strcmp(f.name[i], subframe_varname)) ret->subframe_arg = i;
+    if (!strcmp(f.name[i], buffer_varname)) ret->buffer_arg = i;
+  }
+  if (ret->subframe_arg == -1) {
+    printf("%s:%d: subframe argument '%s' not found in event '%s'\n",
+        __FILE__, __LINE__, subframe_varname, event_name);
+    abort();
+  }
+  if (ret->buffer_arg == -1) {
+    printf("%s:%d: buffer argument '%s' not found in event '%s'\n",
+        __FILE__, __LINE__, buffer_varname, event_name);
+    abort();
+  }
+  if (strcmp(f.type[ret->subframe_arg], "int") != 0) {
+    printf("%s:%d: argument '%s' has wrong type (should be 'int')\n",
+        __FILE__, __LINE__, subframe_varname);
+    abort();
+  }
+  if (strcmp(f.type[ret->buffer_arg], "buffer") != 0) {
+    printf("%s:%d: argument '%s' has wrong type (should be 'buffer')\n",
+        __FILE__, __LINE__, buffer_varname);
+    abort();
+  }
+
+  return ret;
+}
+
+/****************************************************************************/
+/*                             public functions                             */
+/****************************************************************************/
+
+void framelog_set_skip(logger *_this, int skip_delay)
+{
+  struct framelog *l = _this;
+  /* TODO: protect with a lock? */
+  l->skip_delay = skip_delay;
+  l->skip_current = 0;
+  l->skip_on = 0;
+}
diff --git a/common/utils/T/tracer/logger/logger.c b/common/utils/T/tracer/logger/logger.c
new file mode 100644
index 0000000000000000000000000000000000000000..ab6fb8bc625e97bc0f5b7ea848a8c25c422b463e
--- /dev/null
+++ b/common/utils/T/tracer/logger/logger.c
@@ -0,0 +1,17 @@
+#include "logger.h"
+#include "logger_defs.h"
+#include <stdlib.h>
+
+void logger_add_view(logger *_l, view *v)
+{
+  struct logger *l = _l;
+  l->vsize++;
+  l->v = realloc(l->v, l->vsize * sizeof(view *)); if (l->v == NULL) abort();
+  l->v[l->vsize-1] = v;
+}
+
+void logger_set_filter(logger *_l, void *filter)
+{
+  struct logger *l = _l;
+  l->filter = filter;
+}
diff --git a/common/utils/T/tracer/logger/logger.h b/common/utils/T/tracer/logger/logger.h
new file mode 100644
index 0000000000000000000000000000000000000000..e3e4b97aa5e4e85844376c61663036d3f7a78b5b
--- /dev/null
+++ b/common/utils/T/tracer/logger/logger.h
@@ -0,0 +1,24 @@
+#ifndef _LOGGER_H_
+#define _LOGGER_H_
+
+typedef void logger;
+
+logger *new_framelog(void *event_handler, void *database,
+    char *event_name, char *subframe_varname, char *buffer_varname);
+logger *new_textlog(void *event_handler, void *database,
+    char *event_name, char *format);
+logger *new_ttilog(void *event_handler, void *database,
+    char *event_name, char *frame_varname, char *subframe_varname,
+    char *data_varname, int convert_to_dB);
+logger *new_timelog(void *event_handler, void *database, char *event_name);
+logger *new_ticklog(void *event_handler, void *database,
+    char *event_name, char *frame_name, char *subframe_name);
+
+void framelog_set_skip(logger *_this, int skip_delay);
+
+#include "view/view.h"
+
+void logger_add_view(logger *l, view *v);
+void logger_set_filter(logger *l, void *filter);
+
+#endif /* _LOGGER_H_ */
diff --git a/common/utils/T/tracer/logger/logger_defs.h b/common/utils/T/tracer/logger/logger_defs.h
new file mode 100644
index 0000000000000000000000000000000000000000..8e8d6a2d3e8e59d939114e8f490c7c79185667d1
--- /dev/null
+++ b/common/utils/T/tracer/logger/logger_defs.h
@@ -0,0 +1,16 @@
+#ifndef _LOGGER_DEFS_H_
+#define _LOGGER_DEFS_H_
+
+#include "view/view.h"
+
+struct logger {
+  char *event_name;
+  unsigned long handler_id;
+  /* list of views */
+  view **v;
+  int vsize;
+  /* filter - NULL if no filter set */
+  void *filter;
+};
+
+#endif /* _LOGGER_DEFS_H_ */
diff --git a/common/utils/T/tracer/logger/textlog.c b/common/utils/T/tracer/logger/textlog.c
new file mode 100644
index 0000000000000000000000000000000000000000..e80af7813e014c5174a01b83853a58d55179cd7c
--- /dev/null
+++ b/common/utils/T/tracer/logger/textlog.c
@@ -0,0 +1,198 @@
+#include "logger.h"
+#include "logger_defs.h"
+#include "handler.h"
+#include "database.h"
+#include "view/view.h"
+#include "utils.h"
+#include "filter/filter.h"
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+enum format_item_type {
+  INSTRING,
+  INT, ULONG, STRING, BUFFER };
+
+struct format_item {
+  enum format_item_type type;
+  union {
+    /* INSTRING */
+    char *s;
+    /* others */
+    int event_arg;
+  };
+};
+
+struct textlog {
+  struct logger common;
+  char *format;
+  void *database;
+  /* parsed format string */
+  struct format_item *f;
+  int fsize;
+  /* local output buffer */
+  OBUF o;
+};
+
+static void _event(void *p, event e)
+{
+  struct textlog *l = p;
+  int i;
+#ifdef T_SEND_TIME
+  struct tm *t;
+  char tt[64];
+#endif
+
+  if (l->common.filter != NULL && filter_eval(l->common.filter, e) == 0)
+    return;
+
+  l->o.osize = 0;
+
+#ifdef T_SEND_TIME
+  t = localtime(&e.sending_time.tv_sec);
+  /* round tv_nsec to nearest millisecond */
+  sprintf(tt, "%2.2d:%2.2d:%2.2d.%9.9ld: ", t->tm_hour, t->tm_min, t->tm_sec,
+      e.sending_time.tv_nsec);
+  PUTS(&l->o, tt);
+#endif
+
+  for (i = 0; i < l->fsize; i++)
+  switch(l->f[i].type) {
+  case INSTRING: PUTS(&l->o, l->f[i].s); break;
+  case INT:      PUTI(&l->o, e.e[l->f[i].event_arg].i); break;
+  case ULONG:    PUTUL(&l->o, e.e[l->f[i].event_arg].ul); break;
+  case STRING:   PUTS_CLEAN(&l->o, e.e[l->f[i].event_arg].s); break;
+  case BUFFER:
+    PUTS(&l->o, "{buffer size:");
+    PUTI(&l->o, e.e[l->f[i].event_arg].bsize);
+    PUTS(&l->o, "}");
+    break;
+  }
+  PUTC(&l->o, 0);
+
+  for (i = 0; i < l->common.vsize; i++)
+    l->common.v[i]->append(l->common.v[i], l->o.obuf);
+}
+
+enum chunk_type { C_ERROR, C_STRING, C_ARG_NAME, C_EVENT_NAME };
+struct chunk {
+  enum chunk_type type;
+  char *s;
+  enum format_item_type it;
+  int event_arg;
+};
+
+/* TODO: speed it up? */
+static int find_argument(char *name, database_event_format f,
+    enum format_item_type *it, int *event_arg)
+{
+  int i;
+  for (i = 0; i < f.count; i++) if (!strcmp(name, f.name[i])) break;
+  if (i == f.count) return 0;
+  *event_arg = i;
+  if (!strcmp(f.type[i], "int"))         *it = INT;
+  else if (!strcmp(f.type[i], "ulong"))  *it = ULONG;
+  else if (!strcmp(f.type[i], "string")) *it = STRING;
+  else if (!strcmp(f.type[i], "buffer")) *it = BUFFER;
+  else return 0;
+  return 1;
+}
+
+static struct chunk next_chunk(char **s, database_event_format f)
+{
+  char *cur = *s;
+  char *name;
+  enum format_item_type it;
+  int event_arg;
+
+  /* argument in [ ] */
+  if (*cur == '[') {
+    *cur = 0;
+    cur++;
+    name = cur;
+    /* no \ allowed there */
+    while (*cur && *cur != ']' && *cur != '\\') cur++;
+    if (*cur != ']') goto error;
+    *cur = 0;
+    cur++;
+    *s = cur;
+    if (find_argument(name, f, &it, &event_arg) == 0) goto error;
+    return (struct chunk){type:C_ARG_NAME, s:name, it:it, event_arg:event_arg};
+  }
+
+  /* { } is name of event (anything in between is smashed) */
+  if (*cur == '{') {
+    *cur = 0;
+    cur++;
+    while (*cur && *cur != '}') cur++;
+    if (*cur != '}') goto error;
+    *cur = 0;
+    cur++;
+    *s = cur;
+    return (struct chunk){type:C_EVENT_NAME};
+  }
+
+  /* anything but [ and { is raw string */
+  /* TODO: deal with \ */
+  name = cur;
+  while (*cur && *cur != '[' && *cur != '{') cur++;
+  *s = cur;
+  return (struct chunk){type:C_STRING, s:name};
+
+error:
+  return (struct chunk){type:C_ERROR};
+}
+
+logger *new_textlog(event_handler *h, void *database,
+    char *event_name, char *format)
+{
+  struct textlog *ret;
+  int event_id;
+  database_event_format f;
+  char *cur;
+
+  ret = calloc(1, sizeof(struct textlog)); if (ret == NULL) abort();
+
+  ret->common.event_name = strdup(event_name);
+  if (ret->common.event_name == NULL) abort();
+  ret->format = strdup(format); if (ret->format == NULL) abort();
+  ret->database = database;
+
+  event_id = event_id_from_name(database, event_name);
+
+  ret->common.handler_id = register_handler_function(h,event_id,_event,ret);
+
+  f = get_format(database, event_id);
+
+  /* we won't get more than strlen(format) "chunks" */
+  ret->f = malloc(sizeof(struct format_item) * strlen(format));
+  if (ret->f == NULL) abort();
+
+  cur = ret->format;
+
+  while (*cur) {
+    struct chunk c = next_chunk(&cur, f);
+    switch (c.type) {
+    case C_ERROR: goto error;
+    case C_STRING:
+      ret->f[ret->fsize].type = INSTRING;
+      ret->f[ret->fsize].s = c.s;
+      break;
+    case C_ARG_NAME:
+      ret->f[ret->fsize].type = c.it;
+      ret->f[ret->fsize].event_arg = c.event_arg;
+      break;
+    case C_EVENT_NAME:
+      ret->f[ret->fsize].type = INSTRING;
+      ret->f[ret->fsize].s = ret->common.event_name;
+      break;
+    }
+    ret->fsize++;
+  }
+
+  return ret;
+
+error:
+  printf("%s:%d: bad format '%s'\n", __FILE__, __LINE__, format);
+  abort();
+}
diff --git a/common/utils/T/tracer/logger/ticklog.c b/common/utils/T/tracer/logger/ticklog.c
new file mode 100644
index 0000000000000000000000000000000000000000..7a3bc45338858a953182fcb81d99d86a73b7a3de
--- /dev/null
+++ b/common/utils/T/tracer/logger/ticklog.c
@@ -0,0 +1,84 @@
+#include "logger.h"
+#include "logger_defs.h"
+#include "event.h"
+#include "database.h"
+#include "handler.h"
+#include "filter/filter.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct ticklog {
+  struct logger common;
+  void *database;
+  int frame_arg;
+  int subframe_arg;
+};
+
+static void _event(void *p, event e)
+{
+  struct ticklog *l = p;
+  int i;
+  int frame;
+  int subframe;
+
+  if (l->common.filter != NULL && filter_eval(l->common.filter, e) == 0)
+    return;
+
+  frame = e.e[l->frame_arg].i;
+  subframe = e.e[l->subframe_arg].i;
+
+  for (i = 0; i < l->common.vsize; i++)
+    l->common.v[i]->append(l->common.v[i], e.sending_time, frame, subframe);
+}
+
+logger *new_ticklog(event_handler *h, void *database,
+    char *event_name, char *frame_varname, char *subframe_varname)
+{
+  struct ticklog *ret;
+  int event_id;
+  database_event_format f;
+  int i;
+
+  ret = calloc(1, sizeof(struct ticklog)); if (ret == NULL) abort();
+
+  ret->common.event_name = strdup(event_name);
+  if (ret->common.event_name == NULL) abort();
+  ret->database = database;
+
+  event_id = event_id_from_name(database, event_name);
+
+  ret->common.handler_id = register_handler_function(h,event_id,_event,ret);
+
+  f = get_format(database, event_id);
+
+  /* look for frame and subframe args */
+  ret->frame_arg = -1;
+  ret->subframe_arg = -1;
+  for (i = 0; i < f.count; i++) {
+    if (!strcmp(f.name[i], frame_varname)) ret->frame_arg = i;
+    if (!strcmp(f.name[i], subframe_varname)) ret->subframe_arg = i;
+  }
+  if (ret->frame_arg == -1) {
+    printf("%s:%d: frame argument '%s' not found in event '%s'\n",
+        __FILE__, __LINE__, frame_varname, event_name);
+    abort();
+  }
+  if (ret->subframe_arg == -1) {
+    printf("%s:%d: subframe argument '%s' not found in event '%s'\n",
+        __FILE__, __LINE__, subframe_varname, event_name);
+    abort();
+  }
+  if (strcmp(f.type[ret->frame_arg], "int") != 0) {
+    printf("%s:%d: argument '%s' has wrong type (should be 'int')\n",
+        __FILE__, __LINE__, frame_varname);
+    abort();
+  }
+  if (strcmp(f.type[ret->subframe_arg], "int") != 0) {
+    printf("%s:%d: argument '%s' has wrong type (should be 'int')\n",
+        __FILE__, __LINE__, subframe_varname);
+    abort();
+  }
+
+  return ret;
+}
diff --git a/common/utils/T/tracer/logger/timelog.c b/common/utils/T/tracer/logger/timelog.c
new file mode 100644
index 0000000000000000000000000000000000000000..b474c8200d16cbdac17c6be4ed847b0d11bfe19a
--- /dev/null
+++ b/common/utils/T/tracer/logger/timelog.c
@@ -0,0 +1,41 @@
+#include "logger.h"
+#include "logger_defs.h"
+#include "event.h"
+#include "database.h"
+#include "handler.h"
+#include "filter/filter.h"
+#include <stdlib.h>
+#include <string.h>
+
+struct timelog {
+  struct logger common;
+};
+
+static void _event(void *p, event e)
+{
+  struct timelog *l = p;
+  int i;
+
+  if (l->common.filter != NULL && filter_eval(l->common.filter, e) == 0)
+    return;
+
+  for (i = 0; i < l->common.vsize; i++)
+    l->common.v[i]->append(l->common.v[i], e.sending_time);
+}
+
+logger *new_timelog(event_handler *h, void *database, char *event_name)
+{
+  struct timelog *ret;
+  int event_id;
+
+  ret = calloc(1, sizeof(struct timelog)); if (ret == NULL) abort();
+
+  ret->common.event_name = strdup(event_name);
+  if (ret->common.event_name == NULL) abort();
+
+  event_id = event_id_from_name(database, event_name);
+
+  ret->common.handler_id = register_handler_function(h,event_id,_event,ret);
+
+  return ret;
+}
diff --git a/common/utils/T/tracer/logger/ttilog.c b/common/utils/T/tracer/logger/ttilog.c
new file mode 100644
index 0000000000000000000000000000000000000000..29e22fbf10e97e8df1e32929320108e3601d1f24
--- /dev/null
+++ b/common/utils/T/tracer/logger/ttilog.c
@@ -0,0 +1,111 @@
+#include "logger.h"
+#include "logger_defs.h"
+#include "event.h"
+#include "database.h"
+#include "handler.h"
+#include "filter/filter.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+struct ttilog {
+  struct logger common;
+  void *database;
+  int frame_arg;
+  int subframe_arg;
+  int data_arg;
+  int convert_to_dB;
+};
+
+static void _event(void *p, event e)
+{
+  struct ttilog *l = p;
+  int i;
+  int frame;
+  int subframe;
+  float value;
+
+  if (l->common.filter != NULL && filter_eval(l->common.filter, e) == 0)
+    return;
+
+  frame = e.e[l->frame_arg].i;
+  subframe = e.e[l->subframe_arg].i;
+  switch (e.e[l->data_arg].type) {
+  case EVENT_INT: value = e.e[l->data_arg].i; break;
+  case EVENT_ULONG: value = e.e[l->data_arg].ul; break;
+  default: printf("%s:%d: unsupported type\n", __FILE__, __LINE__); abort();
+  }
+
+  if (l->convert_to_dB) value = 10 * log10(value);
+
+  for (i = 0; i < l->common.vsize; i++)
+    l->common.v[i]->append(l->common.v[i], frame, subframe, value);
+}
+
+logger *new_ttilog(event_handler *h, void *database,
+    char *event_name, char *frame_varname, char *subframe_varname,
+    char *data_varname, int convert_to_dB)
+{
+  struct ttilog *ret;
+  int event_id;
+  database_event_format f;
+  int i;
+
+  ret = calloc(1, sizeof(struct ttilog)); if (ret == NULL) abort();
+
+  ret->common.event_name = strdup(event_name);
+  if (ret->common.event_name == NULL) abort();
+  ret->database = database;
+  ret->convert_to_dB = convert_to_dB;
+
+  event_id = event_id_from_name(database, event_name);
+
+  ret->common.handler_id = register_handler_function(h,event_id,_event,ret);
+
+  f = get_format(database, event_id);
+
+  /* look for frame, subframe and data args */
+  ret->frame_arg = -1;
+  ret->subframe_arg = -1;
+  ret->data_arg = -1;
+  for (i = 0; i < f.count; i++) {
+    if (!strcmp(f.name[i], frame_varname)) ret->frame_arg = i;
+    if (!strcmp(f.name[i], subframe_varname)) ret->subframe_arg = i;
+    if (!strcmp(f.name[i], data_varname)) ret->data_arg = i;
+  }
+  if (ret->frame_arg == -1) {
+    printf("%s:%d: frame argument '%s' not found in event '%s'\n",
+        __FILE__, __LINE__, frame_varname, event_name);
+    abort();
+  }
+  if (ret->subframe_arg == -1) {
+    printf("%s:%d: subframe argument '%s' not found in event '%s'\n",
+        __FILE__, __LINE__, subframe_varname, event_name);
+    abort();
+  }
+  if (ret->data_arg == -1) {
+    printf("%s:%d: data argument '%s' not found in event '%s'\n",
+        __FILE__, __LINE__, data_varname, event_name);
+    abort();
+  }
+  if (strcmp(f.type[ret->frame_arg], "int") != 0) {
+    printf("%s:%d: argument '%s' has wrong type (should be 'int')\n",
+        __FILE__, __LINE__, frame_varname);
+    abort();
+  }
+  if (strcmp(f.type[ret->subframe_arg], "int") != 0) {
+    printf("%s:%d: argument '%s' has wrong type (should be 'int')\n",
+        __FILE__, __LINE__, subframe_varname);
+    abort();
+  }
+  if (strcmp(f.type[ret->data_arg], "int") != 0 &&
+      strcmp(f.type[ret->data_arg], "float") != 0) {
+    printf("%s:%d: argument '%s' has wrong type"
+           " (should be 'int' or 'float')\n",
+        __FILE__, __LINE__, data_varname);
+    abort();
+  }
+
+  return ret;
+}
diff --git a/common/utils/T/tracer/openair_logo.h b/common/utils/T/tracer/openair_logo.h
new file mode 100644
index 0000000000000000000000000000000000000000..4a98c3d871623984c99236d276fd68a8183da879
--- /dev/null
+++ b/common/utils/T/tracer/openair_logo.h
@@ -0,0 +1,395 @@
+/* this file (minus this comment) was obtained by running:
+ *   xxd -i openair_logo.png > openair_logo.h
+ */
+unsigned char openair_logo_png[] = {
+  0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
+  0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x28,
+  0x08, 0x06, 0x00, 0x00, 0x00, 0x08, 0x78, 0x5f, 0x43, 0x00, 0x00, 0x00,
+  0x06, 0x62, 0x4b, 0x47, 0x44, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0xa0,
+  0xbd, 0xa7, 0x93, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00,
+  0x00, 0x0b, 0x13, 0x00, 0x00, 0x0b, 0x13, 0x01, 0x00, 0x9a, 0x9c, 0x18,
+  0x00, 0x00, 0x00, 0x07, 0x74, 0x49, 0x4d, 0x45, 0x07, 0xe0, 0x05, 0x17,
+  0x0e, 0x30, 0x2a, 0xd1, 0xdd, 0x83, 0x5b, 0x00, 0x00, 0x11, 0xc5, 0x49,
+  0x44, 0x41, 0x54, 0x68, 0xde, 0xed, 0x9b, 0x79, 0x70, 0x94, 0xe7, 0x7d,
+  0xc7, 0x3f, 0xcf, 0xf3, 0xbe, 0xbb, 0x2b, 0xed, 0xea, 0xbe, 0xef, 0x03,
+  0x24, 0x10, 0x08, 0x8c, 0xb9, 0x31, 0x06, 0x73, 0xd4, 0xd8, 0x4e, 0x38,
+  0x62, 0x63, 0x77, 0x92, 0x90, 0xcb, 0x9d, 0x34, 0x19, 0x27, 0xce, 0xa4,
+  0xed, 0x8c, 0xd3, 0x78, 0xd2, 0x64, 0xd2, 0x36, 0x9d, 0x34, 0xa9, 0xdb,
+  0xa6, 0x99, 0xb6, 0x49, 0x67, 0x82, 0xe3, 0x24, 0x76, 0x9a, 0x34, 0x76,
+  0x5c, 0x1f, 0x18, 0x8f, 0x31, 0xc4, 0xc6, 0x06, 0xcc, 0x69, 0x21, 0xb0,
+  0x05, 0x08, 0x1d, 0x80, 0x2e, 0x74, 0xae, 0xa4, 0xbd, 0xb4, 0xbb, 0xef,
+  0xfb, 0x3c, 0xfd, 0x63, 0x57, 0x2b, 0xd6, 0x12, 0x58, 0x60, 0x32, 0x74,
+  0xb0, 0x9f, 0x99, 0x1d, 0x69, 0x9f, 0xe7, 0xf9, 0x3d, 0xd7, 0xf7, 0x77,
+  0x3f, 0xcf, 0x0a, 0xad, 0xb5, 0xe6, 0x43, 0x5a, 0x94, 0xd6, 0xf4, 0x07,
+  0xc3, 0xf8, 0xa3, 0x51, 0x3a, 0x47, 0xfc, 0xf4, 0x05, 0x82, 0x0c, 0xf8,
+  0x43, 0x98, 0x52, 0x50, 0xec, 0x49, 0xa5, 0x38, 0xdd, 0x4d, 0x71, 0xba,
+  0x87, 0x34, 0x97, 0x93, 0x74, 0x97, 0x13, 0x21, 0xc4, 0x1f, 0x7d, 0x4d,
+  0xe2, 0xc3, 0x0a, 0xc8, 0xde, 0xce, 0x3e, 0xde, 0xea, 0x1e, 0x60, 0x45,
+  0x51, 0x2e, 0x45, 0x9e, 0x14, 0x72, 0x52, 0x9c, 0xa4, 0x3b, 0x1d, 0x38,
+  0x0d, 0x03, 0x04, 0x44, 0x6d, 0x9b, 0x50, 0xc4, 0xc2, 0x1f, 0x8e, 0x30,
+  0x1c, 0x0a, 0xd1, 0xed, 0x1d, 0xa1, 0x36, 0x3f, 0x97, 0x19, 0x05, 0xb9,
+  0x1f, 0x01, 0x72, 0x3d, 0xcb, 0x68, 0x24, 0xca, 0x7f, 0x34, 0xb6, 0x30,
+  0x37, 0x27, 0x83, 0x7b, 0x67, 0x96, 0x5e, 0x15, 0x6d, 0x63, 0x47, 0x0f,
+  0x03, 0x23, 0x3e, 0x6e, 0x9f, 0x5d, 0x4d, 0x8a, 0xc3, 0xf1, 0x11, 0x20,
+  0x1f, 0xb4, 0xbc, 0x7c, 0xbe, 0x97, 0x0e, 0x9f, 0x9f, 0x7b, 0x67, 0x94,
+  0x51, 0xe0, 0x76, 0x5d, 0xd3, 0x18, 0xc1, 0x48, 0x84, 0x53, 0x1d, 0xdd,
+  0xe4, 0x78, 0xdc, 0x54, 0x17, 0x15, 0x7c, 0x04, 0xc8, 0xb5, 0x96, 0x86,
+  0xfe, 0x61, 0x0e, 0x5c, 0x1c, 0xe4, 0xe1, 0xf9, 0x33, 0xaf, 0xcb, 0x78,
+  0xa7, 0x2e, 0x74, 0x91, 0x9b, 0x91, 0x46, 0x41, 0x56, 0xe6, 0x75, 0x5d,
+  0xa7, 0xfc, 0x30, 0x80, 0xb1, 0xaf, 0x67, 0x90, 0x97, 0xcf, 0xf7, 0x4d,
+  0x0b, 0x8c, 0xd1, 0x70, 0x04, 0x7f, 0x38, 0xfa, 0xbe, 0xfd, 0xe6, 0x54,
+  0x94, 0xd2, 0x33, 0xe8, 0x65, 0x60, 0x78, 0xe4, 0xba, 0xae, 0xd5, 0xbc,
+  0xd9, 0xc1, 0x08, 0x5a, 0x36, 0xfb, 0x7b, 0x86, 0xf8, 0xd6, 0x92, 0x59,
+  0x97, 0xed, 0xd3, 0xe1, 0x0b, 0xf2, 0xf8, 0x89, 0xb3, 0x48, 0x65, 0x33,
+  0x33, 0xc3, 0x83, 0x43, 0x08, 0x46, 0x82, 0x21, 0xd2, 0x1d, 0x06, 0x1b,
+  0xea, 0x66, 0x90, 0xeb, 0x71, 0x4f, 0x49, 0xb7, 0x60, 0x66, 0x15, 0x0d,
+  0x67, 0x5a, 0xc8, 0xf0, 0xb8, 0x71, 0x5e, 0x27, 0x9b, 0x72, 0xd3, 0xab,
+  0xac, 0x7f, 0x6a, 0x38, 0xcb, 0xa6, 0xaa, 0x22, 0xe6, 0x66, 0xa7, 0x4f,
+  0x6a, 0x3b, 0x37, 0x1a, 0x64, 0xe7, 0xb9, 0x6e, 0xb2, 0x9d, 0x0e, 0xb6,
+  0xd6, 0x96, 0xe3, 0x32, 0x92, 0x15, 0x86, 0x65, 0x2b, 0x1a, 0x3b, 0xba,
+  0x51, 0xb6, 0xa2, 0xb6, 0x28, 0x8f, 0xac, 0x29, 0x80, 0x09, 0x85, 0xc3,
+  0xf4, 0x0d, 0x0e, 0x53, 0x59, 0x52, 0xf8, 0x91, 0x84, 0xbc, 0x5f, 0x79,
+  0x77, 0xc8, 0x87, 0x37, 0x6c, 0x4d, 0x09, 0x46, 0x87, 0x2f, 0xc4, 0x4f,
+  0xdf, 0x69, 0xe3, 0x6f, 0x97, 0xcd, 0x21, 0xd5, 0x34, 0xa6, 0x3e, 0x1c,
+  0x43, 0xb2, 0xb8, 0xaa, 0x8c, 0xa8, 0x6d, 0xd3, 0xd8, 0x7a, 0x9e, 0xb9,
+  0x95, 0xa5, 0xb8, 0x5d, 0xc9, 0xce, 0x40, 0xaa, 0xcb, 0xc5, 0xa8, 0xdf,
+  0x8f, 0x52, 0xf9, 0x48, 0x79, 0x1d, 0x2c, 0x80, 0xbe, 0x89, 0xcb, 0x5f,
+  0xee, 0x3b, 0xa9, 0xb5, 0x9a, 0x5c, 0xdf, 0x1f, 0x0a, 0xeb, 0x07, 0x77,
+  0x1f, 0x9b, 0x54, 0xef, 0x1d, 0x0b, 0xeb, 0xd6, 0xa1, 0x11, 0x7d, 0xde,
+  0x3b, 0xaa, 0xfd, 0xe1, 0x48, 0x52, 0x9b, 0xd2, 0x4a, 0xbf, 0x71, 0xfc,
+  0x1d, 0x1d, 0x89, 0x46, 0x27, 0xd1, 0x05, 0x82, 0x21, 0x7d, 0xa6, 0xa5,
+  0xed, 0xba, 0xac, 0xf9, 0xa6, 0x95, 0x90, 0xd1, 0x88, 0x45, 0x65, 0x7a,
+  0x2a, 0x4c, 0x11, 0x5c, 0x3f, 0xdd, 0xd2, 0xc5, 0xbf, 0xae, 0x9a, 0x9f,
+  0x54, 0xf7, 0xc3, 0xa3, 0xa7, 0x29, 0x48, 0x71, 0xb0, 0xa2, 0x30, 0x07,
+  0x5b, 0xc0, 0xa1, 0xf6, 0x0e, 0xa4, 0xb2, 0x59, 0x3b, 0xa7, 0x36, 0xa6,
+  0xdb, 0x11, 0xac, 0x9c, 0x3f, 0x87, 0xd3, 0xed, 0xe7, 0xa9, 0x9f, 0x59,
+  0x9d, 0x44, 0xeb, 0x4e, 0x4d, 0x21, 0x1c, 0x8d, 0x60, 0xdb, 0x0a, 0xc3,
+  0x90, 0xfc, 0x67, 0x63, 0x1b, 0xbf, 0x69, 0xef, 0x27, 0x64, 0xab, 0xd8,
+  0xf4, 0xd2, 0x60, 0xd7, 0xc7, 0x17, 0x91, 0xe3, 0x8a, 0x1d, 0xf7, 0xe3,
+  0xef, 0x9c, 0xe3, 0x17, 0x67, 0x7b, 0xc9, 0x4e, 0x75, 0xf1, 0xcd, 0x85,
+  0xd5, 0xdc, 0x5e, 0x98, 0x79, 0xf3, 0xab, 0xac, 0xe1, 0x48, 0x74, 0x4a,
+  0x55, 0x15, 0xb2, 0x6d, 0xbc, 0x91, 0x28, 0xd9, 0xae, 0x09, 0x23, 0xfc,
+  0x58, 0x43, 0x33, 0x9b, 0x67, 0x94, 0x32, 0x37, 0x67, 0xa2, 0x7f, 0x6d,
+  0x5e, 0x36, 0x43, 0xfe, 0x20, 0x0d, 0x2d, 0xed, 0x2c, 0xac, 0x89, 0x01,
+  0x60, 0x48, 0x49, 0xa6, 0xc7, 0x43, 0x24, 0x1a, 0x9d, 0x64, 0xc4, 0x0b,
+  0x72, 0x73, 0x09, 0x04, 0x83, 0x64, 0xa4, 0xa7, 0xd1, 0x36, 0x1a, 0xa4,
+  0x69, 0x38, 0xcc, 0x33, 0x1b, 0xea, 0x09, 0x5a, 0x0a, 0x1b, 0xf0, 0x98,
+  0x12, 0xb4, 0x66, 0xcb, 0x8b, 0x87, 0x79, 0x7b, 0x34, 0xc2, 0xf7, 0x97,
+  0xd5, 0xd0, 0x33, 0xea, 0xe7, 0xae, 0x17, 0x8e, 0xf0, 0xe4, 0x5d, 0x8b,
+  0xd8, 0x5a, 0x99, 0x73, 0x73, 0x03, 0x32, 0x12, 0x89, 0x92, 0xee, 0x9c,
+  0xbc, 0xbd, 0x5f, 0x9e, 0xee, 0x60, 0x55, 0xf1, 0x44, 0xfa, 0xe3, 0xdf,
+  0x1a, 0x5b, 0x59, 0x92, 0x9f, 0x93, 0x04, 0xc6, 0x78, 0xc9, 0x49, 0x73,
+  0x13, 0xcc, 0x48, 0xa7, 0x7f, 0xc8, 0x4b, 0x7e, 0x4e, 0x36, 0x00, 0x69,
+  0xee, 0x54, 0x02, 0x81, 0x20, 0xce, 0xf7, 0xc4, 0x1f, 0x99, 0xe9, 0xe9,
+  0xf8, 0xfc, 0x7e, 0x74, 0x9a, 0x07, 0x7f, 0xd4, 0x26, 0xc3, 0x65, 0x72,
+  0xbc, 0x6f, 0x04, 0xbf, 0xd2, 0x3c, 0x30, 0xa3, 0x18, 0x97, 0x21, 0x19,
+  0x0c, 0x8e, 0x71, 0xc6, 0x1f, 0x66, 0x43, 0x65, 0x11, 0x9f, 0x9f, 0x55,
+  0x0c, 0xc0, 0x17, 0xeb, 0x2b, 0x89, 0xe8, 0x0f, 0x41, 0x1c, 0xd2, 0x13,
+  0x1c, 0x23, 0x63, 0x0a, 0x57, 0xf4, 0xe5, 0x0b, 0xbd, 0xdc, 0x56, 0x18,
+  0xe3, 0x46, 0x0d, 0x1c, 0xe9, 0x1f, 0x66, 0x5d, 0x59, 0x5e, 0xcc, 0xab,
+  0xd2, 0x9a, 0xed, 0xa7, 0x3a, 0xd9, 0x7e, 0xaa, 0x13, 0x3b, 0xee, 0x7c,
+  0x96, 0x15, 0xe4, 0xd1, 0x76, 0xa1, 0x73, 0xc2, 0x88, 0xa7, 0xb8, 0x18,
+  0x1e, 0xf5, 0x4d, 0x0e, 0xe8, 0x0c, 0x09, 0x08, 0x34, 0x10, 0x88, 0x5a,
+  0x78, 0x43, 0x21, 0x5e, 0xec, 0x18, 0x62, 0x5f, 0xe7, 0x20, 0x8b, 0x9f,
+  0x39, 0xc0, 0x6f, 0xdb, 0x07, 0x70, 0x4a, 0x81, 0x00, 0x0c, 0x21, 0xe8,
+  0x1d, 0xf1, 0xb1, 0xf8, 0x99, 0xb7, 0xd8, 0xb8, 0xf3, 0x18, 0x47, 0x07,
+  0xfc, 0x37, 0x3f, 0x20, 0x1e, 0xc3, 0x24, 0xcb, 0x35, 0x19, 0x90, 0xd9,
+  0x59, 0x69, 0x38, 0xa4, 0x48, 0xe4, 0xb5, 0xee, 0xad, 0x2e, 0x4e, 0xb4,
+  0xf5, 0x06, 0x23, 0xbc, 0x70, 0xae, 0x8f, 0x1d, 0xe7, 0xfb, 0x39, 0xed,
+  0x0d, 0x24, 0xea, 0x73, 0x33, 0x33, 0x12, 0xff, 0x3b, 0x4c, 0x13, 0xa7,
+  0x39, 0x59, 0xf2, 0xa4, 0x90, 0x38, 0x1c, 0x26, 0x42, 0x08, 0xfe, 0xe2,
+  0xd6, 0x99, 0xfc, 0x6e, 0xc3, 0xad, 0xbc, 0xb6, 0x65, 0x09, 0xbb, 0x3e,
+  0xb1, 0x8c, 0xfb, 0xcb, 0xb3, 0xf8, 0x61, 0xc3, 0xb9, 0x84, 0x37, 0x27,
+  0x80, 0x5c, 0x4f, 0x2a, 0xdf, 0x5e, 0x3c, 0x83, 0x40, 0x34, 0xca, 0xf6,
+  0xa6, 0xee, 0x9b, 0xdf, 0x86, 0xdc, 0x5e, 0x9c, 0x33, 0x75, 0x5c, 0x72,
+  0x5b, 0xfd, 0x84, 0x9a, 0x71, 0x3a, 0xf8, 0xd3, 0x99, 0x25, 0x89, 0xef,
+  0xa5, 0x1e, 0x17, 0x2f, 0x7c, 0x6c, 0xd1, 0x24, 0x9a, 0x9a, 0xea, 0xca,
+  0x89, 0x83, 0x97, 0x92, 0xd2, 0x92, 0xa2, 0xc9, 0x2e, 0xb2, 0x69, 0x90,
+  0x95, 0x99, 0x81, 0xd2, 0x9a, 0x1d, 0x6d, 0x17, 0x39, 0xe8, 0x0d, 0xb1,
+  0xa1, 0x2c, 0x17, 0x8d, 0x26, 0x60, 0xd9, 0xb8, 0x1d, 0x4e, 0x24, 0x60,
+  0x4a, 0xc1, 0xc5, 0xe0, 0x18, 0xd2, 0x30, 0x59, 0x5d, 0x94, 0x85, 0x29,
+  0x48, 0x30, 0xc8, 0xa4, 0xc0, 0x30, 0x14, 0x0a, 0xd2, 0xd1, 0xd1, 0x85,
+  0xdf, 0xef, 0xc7, 0x30, 0x0c, 0x72, 0x73, 0x73, 0x28, 0x2b, 0x2b, 0x9b,
+  0x34, 0x79, 0x67, 0x67, 0x27, 0x7d, 0x7d, 0x7d, 0xc9, 0x11, 0xa6, 0x10,
+  0xa4, 0xa5, 0xa5, 0x51, 0x56, 0x56, 0x4e, 0x6a, 0x6a, 0xca, 0x24, 0x9a,
+  0xf6, 0xf6, 0x76, 0xbc, 0x5e, 0xef, 0x94, 0x87, 0xa4, 0x94, 0xa2, 0xb8,
+  0xb8, 0x98, 0xd2, 0xd2, 0x58, 0xf6, 0xb5, 0xbf, 0xbf, 0x9f, 0x8e, 0x8e,
+  0x0e, 0x4a, 0x4b, 0x4b, 0x29, 0x2c, 0x2c, 0xbc, 0xd4, 0x45, 0xa7, 0xb9,
+  0xb9, 0x99, 0x40, 0x20, 0x80, 0xd3, 0xe9, 0xa4, 0xbe, 0xbe, 0xfe, 0xaa,
+  0xee, 0x28, 0x0e, 0x5c, 0x1c, 0xe6, 0xf8, 0xa0, 0x8f, 0x95, 0x85, 0x59,
+  0xdc, 0x9a, 0x37, 0xd9, 0x66, 0x74, 0x07, 0xc2, 0x3c, 0xdd, 0x7a, 0x11,
+  0x29, 0x04, 0x1b, 0xca, 0x73, 0xa9, 0xcb, 0xf2, 0x4c, 0xd0, 0x0d, 0x8c,
+  0xa2, 0x92, 0x24, 0x42, 0x50, 0x90, 0xe2, 0x64, 0x79, 0x61, 0x26, 0xe5,
+  0x69, 0xc9, 0xfb, 0x7d, 0x77, 0x60, 0x84, 0x47, 0xde, 0x3a, 0xc3, 0xbb,
+  0xc3, 0x21, 0x9c, 0x02, 0x8a, 0xd3, 0xdd, 0xfc, 0xe4, 0x8e, 0x7a, 0xe6,
+  0x67, 0xbb, 0xd9, 0xd1, 0xda, 0xc3, 0x3f, 0x34, 0xb4, 0x33, 0x10, 0xb6,
+  0x89, 0xda, 0x36, 0x17, 0x02, 0x63, 0x6c, 0xa8, 0x28, 0x61, 0xd7, 0xc6,
+  0x5b, 0x92, 0x25, 0xe4, 0xb5, 0xd7, 0x5e, 0xa7, 0xb1, 0xb1, 0x71, 0xd2,
+  0x22, 0x0d, 0xc3, 0x60, 0xf3, 0xe6, 0x4d, 0x54, 0x55, 0x55, 0x01, 0xd0,
+  0xd2, 0xd2, 0xca, 0x73, 0xcf, 0x3d, 0x87, 0x69, 0x5e, 0x5e, 0xb8, 0xe6,
+  0xcd, 0x9b, 0xc7, 0x9d, 0x77, 0xfe, 0x49, 0xe2, 0xfb, 0xd1, 0xa3, 0x47,
+  0xd9, 0xbb, 0xf7, 0x0d, 0x0c, 0x63, 0xea, 0x00, 0xcc, 0xb6, 0x6d, 0x56,
+  0xac, 0x58, 0x4e, 0x69, 0x69, 0x29, 0xd1, 0x68, 0x94, 0x27, 0x9e, 0xf8,
+  0x05, 0x4e, 0xa7, 0x13, 0x87, 0xc3, 0xc1, 0xb6, 0x6d, 0x9f, 0x26, 0x3b,
+  0x3b, 0x3b, 0x01, 0xdc, 0xf1, 0xe3, 0x8d, 0xf4, 0xf4, 0xf4, 0x90, 0x99,
+  0x99, 0x49, 0x7d, 0x7d, 0xfd, 0xb4, 0xc1, 0xd8, 0xdb, 0x33, 0xc4, 0xdd,
+  0x3b, 0x8e, 0xe1, 0x36, 0x0d, 0xa2, 0x4a, 0x73, 0xf4, 0xfe, 0x15, 0xcc,
+  0x8e, 0x1f, 0xf8, 0x78, 0x39, 0xef, 0x0f, 0xf1, 0xe8, 0xa1, 0xb3, 0xb8,
+  0x0c, 0x41, 0x5e, 0x8a, 0x83, 0xba, 0x2c, 0x0f, 0xbb, 0x3b, 0x07, 0xf9,
+  0xe4, 0xee, 0x46, 0xa6, 0xca, 0x67, 0x8c, 0xb3, 0xc2, 0x7d, 0xd5, 0x85,
+  0x6c, 0x5f, 0x3b, 0xb1, 0x96, 0xfa, 0xbc, 0x4c, 0x76, 0x6e, 0x5e, 0xc6,
+  0x38, 0xaf, 0x0b, 0x21, 0x12, 0x7d, 0x37, 0xcd, 0x2c, 0x66, 0xe3, 0x8c,
+  0x62, 0x34, 0xfa, 0x92, 0x71, 0x26, 0x98, 0xca, 0xb4, 0x6d, 0x9b, 0x17,
+  0x5e, 0x78, 0x91, 0xd6, 0xd6, 0x56, 0x84, 0x10, 0x54, 0x54, 0x94, 0x53,
+  0x54, 0x54, 0x44, 0x24, 0x12, 0xa5, 0xad, 0xad, 0x8d, 0x40, 0x20, 0xc0,
+  0xef, 0x7f, 0xff, 0x2c, 0x5b, 0xb7, 0xde, 0x47, 0x75, 0x75, 0x35, 0x3e,
+  0x9f, 0x0f, 0x21, 0x04, 0x4a, 0x29, 0x66, 0xcc, 0x98, 0x41, 0x41, 0x41,
+  0x01, 0x5a, 0x2b, 0x6c, 0x5b, 0xd1, 0xd3, 0xd3, 0x43, 0x57, 0x57, 0x17,
+  0x8d, 0x8d, 0x8d, 0x8c, 0x8e, 0xfa, 0xb8, 0xf7, 0xde, 0x2d, 0x48, 0x29,
+  0xf1, 0x7a, 0x87, 0x13, 0x34, 0x75, 0x75, 0x75, 0x64, 0x65, 0x65, 0x72,
+  0x69, 0xc6, 0x46, 0x29, 0x4d, 0x69, 0x69, 0x4c, 0x12, 0xfd, 0x7e, 0x7f,
+  0xa2, 0x2d, 0x1c, 0x0e, 0xb3, 0x67, 0xcf, 0x6b, 0x3c, 0xf0, 0xc0, 0xd6,
+  0x0f, 0xac, 0xc2, 0xbe, 0xf6, 0xe6, 0x69, 0x52, 0xe2, 0xa9, 0x11, 0x01,
+  0xfc, 0xd5, 0xfe, 0x33, 0xec, 0xdc, 0x98, 0xac, 0x9e, 0xf4, 0x7b, 0xfe,
+  0x02, 0x0c, 0x86, 0xa3, 0x68, 0x1d, 0x33, 0xf8, 0x6b, 0x8b, 0x73, 0x58,
+  0x51, 0x98, 0x89, 0xa5, 0x35, 0x96, 0xd2, 0x9c, 0x1c, 0xf2, 0xb1, 0xab,
+  0x63, 0x90, 0xff, 0x6e, 0xe9, 0xe1, 0x1d, 0xaf, 0x8f, 0x5d, 0x1b, 0x97,
+  0x90, 0x11, 0xf7, 0xec, 0x44, 0x1c, 0x88, 0x29, 0xf3, 0x55, 0x22, 0x19,
+  0x84, 0x24, 0xd5, 0x37, 0x3a, 0x3a, 0x4a, 0x47, 0x47, 0x07, 0x86, 0x61,
+  0x24, 0x38, 0x7b, 0x7c, 0xa0, 0x45, 0x8b, 0x16, 0xf2, 0xd4, 0x53, 0xbf,
+  0x46, 0x6b, 0xcd, 0xab, 0xaf, 0xee, 0xe6, 0xcb, 0x5f, 0xfe, 0x52, 0xa2,
+  0x4d, 0x29, 0x45, 0x4d, 0xcd, 0x4c, 0xe6, 0xcd, 0x9b, 0x97, 0xc4, 0xe9,
+  0x3b, 0x77, 0xbe, 0x4c, 0x6b, 0x6b, 0x2b, 0xed, 0xed, 0xed, 0x84, 0xc3,
+  0x61, 0x52, 0x53, 0x53, 0x13, 0x34, 0xb6, 0x6d, 0x53, 0x5f, 0x3f, 0x97,
+  0xca, 0xca, 0xca, 0xcb, 0x27, 0xd7, 0x2e, 0xd9, 0x84, 0x94, 0x92, 0xf3,
+  0xe7, 0xcf, 0x71, 0xe2, 0xc4, 0x49, 0x6e, 0xb9, 0x65, 0xfe, 0x35, 0x83,
+  0xb1, 0xb7, 0xdb, 0xcb, 0x05, 0x7f, 0x08, 0x43, 0x08, 0x6c, 0xad, 0x31,
+  0xa5, 0xe0, 0x8d, 0x8b, 0x5e, 0xce, 0x8e, 0x04, 0xa9, 0xcd, 0x74, 0x5f,
+  0x39, 0xd9, 0x17, 0x3f, 0x38, 0x4b, 0x69, 0xee, 0x2c, 0xcb, 0xe1, 0xeb,
+  0xf3, 0x93, 0xd7, 0xfe, 0x9d, 0xc3, 0x67, 0xf9, 0xf1, 0xc9, 0x0b, 0x9c,
+  0x1d, 0x0e, 0x72, 0xa0, 0x77, 0x98, 0x7b, 0xca, 0xf3, 0x3e, 0x58, 0xfa,
+  0x5d, 0x29, 0x85, 0x6d, 0xdb, 0x00, 0x14, 0x16, 0x16, 0x24, 0x1d, 0x48,
+  0x76, 0x76, 0x36, 0x5a, 0x6b, 0x84, 0x10, 0x04, 0x83, 0xc1, 0xe9, 0xa4,
+  0x61, 0x88, 0x46, 0x63, 0xa9, 0xeb, 0x71, 0x8f, 0x63, 0x2a, 0xee, 0xb8,
+  0x9a, 0x62, 0x18, 0x06, 0xfb, 0xf7, 0xef, 0x23, 0x18, 0x0c, 0x5d, 0x5b,
+  0x6a, 0x08, 0x78, 0xaa, 0xb9, 0x1b, 0x43, 0x08, 0x94, 0xd6, 0x3c, 0xb2,
+  0xa0, 0x8a, 0x90, 0x65, 0xe3, 0x90, 0x82, 0xff, 0x6a, 0xba, 0xf0, 0x81,
+  0x25, 0x2f, 0x60, 0xd9, 0x89, 0x7d, 0xa5, 0x1a, 0x1f, 0xdc, 0x69, 0x35,
+  0x93, 0x8d, 0xab, 0x9e, 0x16, 0xe7, 0x4e, 0x78, 0x16, 0x31, 0xf2, 0xa1,
+  0xa1, 0x21, 0x4e, 0x9d, 0x3a, 0x4d, 0x53, 0x53, 0x13, 0x81, 0x40, 0x80,
+  0x68, 0x34, 0xca, 0xaa, 0x55, 0xb7, 0x93, 0x92, 0x92, 0x32, 0x69, 0x0c,
+  0x9f, 0x2f, 0xc0, 0xe8, 0xe8, 0x08, 0x4a, 0x69, 0xb4, 0x56, 0x64, 0x66,
+  0x66, 0x5d, 0x36, 0x29, 0xe7, 0x74, 0x3a, 0xb1, 0x2c, 0x8b, 0xb1, 0xb1,
+  0x30, 0x47, 0x8e, 0x1c, 0x61, 0xcd, 0x9a, 0x3b, 0xae, 0xfe, 0xc0, 0xa2,
+  0x36, 0x7b, 0xba, 0x06, 0x01, 0x58, 0x55, 0x9c, 0xcd, 0xb7, 0x17, 0xcd,
+  0xe4, 0x7f, 0xdb, 0xfb, 0x38, 0xe7, 0x0b, 0xf1, 0x87, 0xae, 0x21, 0x7c,
+  0x51, 0x8b, 0x74, 0xc7, 0xd5, 0x3b, 0x9b, 0xaf, 0x74, 0x0c, 0xf0, 0xfd,
+  0xb7, 0xdb, 0x38, 0x39, 0xe4, 0xc7, 0xd2, 0x9a, 0xf5, 0x45, 0x39, 0xac,
+  0x29, 0xc9, 0xb9, 0x71, 0xd9, 0x5e, 0xd3, 0x34, 0xd9, 0xbd, 0x7b, 0x0f,
+  0xcf, 0x3f, 0xff, 0x02, 0x5a, 0x6b, 0xa4, 0x94, 0x78, 0x3c, 0x1e, 0x0a,
+  0x0b, 0x0b, 0x58, 0xb1, 0xe2, 0x36, 0xaa, 0xab, 0xab, 0xa6, 0xa4, 0x79,
+  0xe9, 0xa5, 0x9d, 0xd8, 0xb6, 0x8d, 0x10, 0x82, 0x50, 0x28, 0xc4, 0x37,
+  0xbe, 0xf1, 0x08, 0x69, 0x69, 0x9e, 0x29, 0xe7, 0x28, 0x2c, 0x2c, 0xc4,
+  0xb2, 0xa2, 0xf4, 0xf4, 0x5c, 0xe4, 0xd0, 0xa1, 0x43, 0xd4, 0xd6, 0xd6,
+  0x5c, 0x75, 0x46, 0xf5, 0x77, 0x2d, 0x17, 0xe9, 0x0e, 0x86, 0xf1, 0x98,
+  0x06, 0x5b, 0xab, 0x0b, 0x11, 0x02, 0x3e, 0x5b, 0x5b, 0xc2, 0x3f, 0xbc,
+  0xdd, 0xca, 0x69, 0x6f, 0x90, 0xe3, 0x03, 0x3e, 0x56, 0x17, 0x67, 0x4f,
+  0xef, 0xae, 0x02, 0xc1, 0x33, 0x6d, 0xbd, 0x3c, 0xfc, 0x66, 0x13, 0xde,
+  0xb0, 0x85, 0x43, 0x0a, 0x72, 0x53, 0x1c, 0x7c, 0x69, 0x4e, 0x19, 0x3f,
+  0x5c, 0x31, 0xeb, 0xc6, 0xa7, 0xdf, 0x23, 0x91, 0x08, 0x85, 0x85, 0x05,
+  0x14, 0x17, 0x17, 0x53, 0x55, 0x55, 0x4d, 0x7e, 0x7e, 0x1e, 0x59, 0x59,
+  0x97, 0xe7, 0x78, 0xa5, 0x14, 0x95, 0x95, 0xe5, 0xa4, 0xa6, 0xba, 0x01,
+  0x4d, 0x24, 0x12, 0xc1, 0x71, 0x05, 0xee, 0x34, 0x4d, 0x93, 0x75, 0xeb,
+  0xd6, 0xf2, 0xb3, 0x9f, 0x6d, 0xc7, 0xe5, 0x72, 0xb1, 0x63, 0xc7, 0x0e,
+  0xdc, 0x6e, 0xcf, 0x55, 0xad, 0xf1, 0x9b, 0x87, 0x9a, 0x71, 0x9b, 0x06,
+  0x52, 0x08, 0xb6, 0xd5, 0xc6, 0x82, 0xc0, 0xad, 0x33, 0x0a, 0x78, 0xac,
+  0xb1, 0x1d, 0x5b, 0x6b, 0xfe, 0xe6, 0xf0, 0x59, 0xde, 0xf8, 0xc4, 0xb2,
+  0x69, 0x8d, 0xe5, 0x34, 0x04, 0x0f, 0xbd, 0xd1, 0x84, 0xad, 0x35, 0x45,
+  0x6e, 0x27, 0xdf, 0x5b, 0x5a, 0xcb, 0x1d, 0x25, 0xd9, 0x54, 0xa7, 0xa7,
+  0x5e, 0xb7, 0xf8, 0xc9, 0xbc, 0x56, 0xfd, 0x6e, 0x59, 0x16, 0x9b, 0x36,
+  0x6d, 0xa4, 0xae, 0xae, 0x6e, 0xda, 0x34, 0xb6, 0x6d, 0xb3, 0x72, 0xe5,
+  0x4a, 0xca, 0xcb, 0xcb, 0xa7, 0x7b, 0x35, 0x40, 0x4e, 0x4e, 0x0e, 0xb7,
+  0xdf, 0xbe, 0x92, 0xc3, 0x87, 0x8f, 0x10, 0x0c, 0x86, 0x08, 0x04, 0x82,
+  0xd3, 0x8e, 0x3d, 0x9e, 0x38, 0xdd, 0xc5, 0x98, 0xad, 0x30, 0xa5, 0xc0,
+  0xed, 0x30, 0x78, 0x68, 0x6f, 0x13, 0x11, 0x15, 0xfb, 0x6e, 0x6b, 0x8d,
+  0x21, 0x04, 0x07, 0xfb, 0x46, 0x38, 0xd2, 0x37, 0xc2, 0xd2, 0x82, 0xf7,
+  0xbf, 0x1b, 0xb7, 0x94, 0xe6, 0xcf, 0xea, 0x4a, 0xf8, 0x59, 0x53, 0x17,
+  0xde, 0x70, 0x94, 0x53, 0x5e, 0x3f, 0x5f, 0x98, 0x5d, 0x72, 0x5d, 0x03,
+  0xda, 0xf7, 0x95, 0xff, 0xf1, 0xcd, 0x4f, 0x75, 0xb1, 0x68, 0x59, 0xd6,
+  0x55, 0x4f, 0xa8, 0x94, 0xba, 0x6a, 0x9a, 0x65, 0xcb, 0x96, 0x91, 0x99,
+  0x99, 0x79, 0x59, 0x5b, 0x36, 0xa5, 0xf4, 0x2a, 0xc5, 0x6f, 0x5b, 0x2f,
+  0xe2, 0x90, 0x02, 0x4b, 0x69, 0x2e, 0x8c, 0x86, 0xf8, 0xe5, 0xe9, 0x4e,
+  0x7e, 0xd3, 0xdc, 0xcd, 0x93, 0xa7, 0xbb, 0xe8, 0x0f, 0x45, 0xb0, 0xb5,
+  0xc6, 0x63, 0x1a, 0x7c, 0xbf, 0xa1, 0x6d, 0x7a, 0x0c, 0xa5, 0x35, 0x8f,
+  0xad, 0x98, 0xcd, 0x9a, 0x92, 0x6c, 0x94, 0x86, 0x1f, 0x9f, 0xbc, 0xc0,
+  0xd7, 0xf6, 0x9d, 0xba, 0xae, 0x80, 0x98, 0x2e, 0x97, 0x0b, 0xb7, 0xdb,
+  0x4d, 0x28, 0x14, 0x62, 0xff, 0xfe, 0xfd, 0x54, 0x54, 0x54, 0x90, 0x91,
+  0x91, 0x81, 0x52, 0x8a, 0x86, 0x86, 0x06, 0x94, 0x52, 0x28, 0xa5, 0xa8,
+  0xad, 0xad, 0xbd, 0x2e, 0x13, 0xda, 0xb6, 0x9d, 0x18, 0x33, 0x89, 0x33,
+  0xa4, 0xbc, 0xa2, 0x71, 0x5f, 0xbe, 0x7c, 0x39, 0xbb, 0x76, 0xed, 0x9a,
+  0xb6, 0x0d, 0xe9, 0x0a, 0x84, 0x69, 0x18, 0x18, 0x45, 0x03, 0x1f, 0xab,
+  0xc8, 0xe3, 0x89, 0x75, 0xf3, 0xb0, 0x2f, 0xe1, 0x29, 0xa7, 0x14, 0xdc,
+  0xf3, 0xd2, 0x31, 0x1a, 0x07, 0x7d, 0x34, 0x0e, 0xf8, 0xe8, 0x0c, 0x8c,
+  0x31, 0x5d, 0x05, 0xf1, 0xfc, 0x3d, 0x0b, 0xb9, 0xfb, 0xa5, 0x63, 0x1c,
+  0xee, 0x1d, 0xe1, 0xc9, 0xe6, 0x6e, 0x66, 0x65, 0x7a, 0xf8, 0xfa, 0xfc,
+  0x8a, 0xeb, 0x03, 0x48, 0x5a, 0x5a, 0x1a, 0x6b, 0xd6, 0xac, 0x61, 0xd7,
+  0xae, 0x5d, 0x04, 0x83, 0x21, 0xb6, 0x6f, 0x7f, 0x9c, 0x9c, 0x9c, 0x1c,
+  0xa2, 0xd1, 0x28, 0x3e, 0x5f, 0x2c, 0xab, 0x59, 0x5a, 0x5a, 0xc2, 0x86,
+  0x0d, 0x77, 0x5e, 0xf3, 0x24, 0x4a, 0x29, 0xb4, 0xd6, 0x98, 0xa6, 0xc9,
+  0x9e, 0x3d, 0x7f, 0x98, 0x14, 0xe5, 0xdb, 0xb6, 0xcd, 0xad, 0xb7, 0x2e,
+  0x60, 0xc9, 0x92, 0x25, 0x57, 0x88, 0xfe, 0xeb, 0x69, 0x69, 0x69, 0xa5,
+  0xa5, 0xe5, 0x2c, 0xa6, 0x69, 0xbe, 0xaf, 0xa4, 0xfd, 0xe2, 0x74, 0x17,
+  0xc1, 0xa8, 0x4d, 0x8a, 0x69, 0xf0, 0x99, 0xda, 0x62, 0x3c, 0x53, 0x5c,
+  0xd3, 0x7e, 0xb1, 0xae, 0x94, 0xaf, 0xef, 0x3b, 0x4d, 0xdf, 0x58, 0x84,
+  0xd7, 0xbb, 0xbc, 0xcc, 0xca, 0x72, 0x27, 0x45, 0xe0, 0x57, 0x04, 0xe5,
+  0xee, 0x85, 0xdc, 0xb3, 0xf3, 0x18, 0x6f, 0xf7, 0x8f, 0xf2, 0xd7, 0x07,
+  0xcf, 0x20, 0x05, 0x7c, 0x6d, 0x5e, 0xc5, 0xf5, 0x51, 0x59, 0x75, 0x75,
+  0xb3, 0xf9, 0xea, 0x57, 0x1f, 0x62, 0xe9, 0xd2, 0x25, 0xa4, 0xa4, 0xa4,
+  0x30, 0x32, 0x32, 0x42, 0x30, 0x18, 0xa4, 0x20, 0xbf, 0x80, 0xcd, 0x9b,
+  0x37, 0xb3, 0x6d, 0xdb, 0xb6, 0x84, 0x0b, 0xab, 0xb5, 0x4e, 0xfa, 0x4c,
+  0xa7, 0xac, 0x5e, 0xbd, 0x8a, 0xf4, 0xf4, 0x74, 0xb4, 0xd6, 0xf8, 0xfd,
+  0x7e, 0x86, 0x87, 0x87, 0x27, 0x7d, 0xc6, 0x42, 0xa1, 0xf8, 0xf8, 0x5c,
+  0x76, 0xfc, 0x4d, 0x9b, 0x3e, 0x4e, 0x41, 0x41, 0x21, 0x4a, 0x69, 0xe6,
+  0xce, 0x9d, 0x73, 0x45, 0xf5, 0xf5, 0x83, 0x86, 0x76, 0x4c, 0x29, 0x71,
+  0x9b, 0x92, 0x75, 0xa5, 0x53, 0xbb, 0xa3, 0xab, 0x8b, 0xb3, 0xc9, 0x70,
+  0x9a, 0x18, 0x42, 0xf2, 0xdd, 0xa3, 0x2d, 0xa4, 0x1a, 0x12, 0xa5, 0x35,
+  0x4a, 0x5f, 0x1a, 0xb5, 0x6b, 0xb4, 0x26, 0xa9, 0x0e, 0x20, 0xc5, 0x94,
+  0xbc, 0xbe, 0x65, 0x29, 0x15, 0x69, 0x29, 0xb8, 0xa4, 0xc1, 0x77, 0x8e,
+  0xb4, 0xf0, 0x54, 0x73, 0xf7, 0x07, 0x06, 0x44, 0x04, 0x7c, 0x83, 0xda,
+  0xb6, 0x2d, 0x94, 0xb6, 0x30, 0xe2, 0x1b, 0x0c, 0xf8, 0x7d, 0x18, 0x12,
+  0x4c, 0xd3, 0x41, 0x24, 0x12, 0xc6, 0xb2, 0x22, 0x68, 0xad, 0xd0, 0x4a,
+  0x13, 0xb5, 0xa2, 0xd8, 0xd1, 0x28, 0x5a, 0xdb, 0x48, 0x19, 0x7b, 0x07,
+  0x8b, 0x56, 0x09, 0x29, 0x40, 0xab, 0x58, 0xdf, 0xd8, 0xc9, 0xc6, 0x0f,
+  0x55, 0xe3, 0x30, 0x4d, 0x82, 0xa1, 0x20, 0xca, 0xb6, 0xe3, 0xdb, 0x14,
+  0xb1, 0xbf, 0xf1, 0x76, 0x29, 0x24, 0x86, 0x61, 0xa0, 0xb4, 0xc6, 0x8a,
+  0x07, 0x97, 0x42, 0x08, 0x4c, 0xc3, 0x84, 0xf1, 0xb4, 0x9e, 0x10, 0x18,
+  0x86, 0x41, 0x34, 0x12, 0xa1, 0x7c, 0xc6, 0x2d, 0xe4, 0x16, 0x4f, 0xed,
+  0x6a, 0xaa, 0x78, 0xaa, 0xc3, 0x52, 0x0a, 0x43, 0x88, 0x49, 0xaf, 0x49,
+  0xde, 0x6b, 0x17, 0xb4, 0x86, 0x90, 0xad, 0x48, 0x77, 0x18, 0x58, 0xf1,
+  0x58, 0xcc, 0x8c, 0x67, 0x60, 0xc7, 0x01, 0xba, 0xb4, 0x6e, 0x2a, 0x63,
+  0xaf, 0xb4, 0x66, 0xcc, 0x56, 0x89, 0xd4, 0xc9, 0x35, 0xab, 0x2c, 0xad,
+  0x35, 0x1a, 0x85, 0x56, 0x8a, 0xa8, 0x6d, 0x61, 0x2b, 0x1b, 0x69, 0x08,
+  0x94, 0x6d, 0x13, 0x0a, 0x85, 0xd0, 0xda, 0x46, 0x29, 0x3b, 0x01, 0x88,
+  0x40, 0x23, 0x24, 0x08, 0x25, 0x50, 0xda, 0x42, 0xdb, 0x3a, 0x0e, 0x88,
+  0x1d, 0xe7, 0x6a, 0x95, 0x00, 0x05, 0x0d, 0x4a, 0x2b, 0x34, 0x60, 0x45,
+  0x42, 0x71, 0x90, 0x6c, 0x40, 0xa1, 0x11, 0x08, 0x4d, 0xec, 0xb0, 0x75,
+  0x6c, 0xe3, 0xb6, 0x15, 0x0b, 0xad, 0xa5, 0x14, 0x38, 0xa4, 0x40, 0x6b,
+  0x8d, 0x1d, 0x89, 0xa0, 0xb1, 0x27, 0x0e, 0x30, 0xae, 0x54, 0x0c, 0xe3,
+  0xf2, 0xef, 0xa0, 0x9a, 0x87, 0x03, 0xbc, 0xd5, 0x3b, 0xcc, 0x67, 0x67,
+  0x95, 0xd0, 0x19, 0x18, 0x23, 0x64, 0xd9, 0xcc, 0xcd, 0x4e, 0xa3, 0x71,
+  0xd0, 0xc7, 0x81, 0x8b, 0xc3, 0x6c, 0xae, 0xcc, 0xe7, 0xc5, 0xf3, 0xfd,
+  0x49, 0xa0, 0x6c, 0xab, 0x2d, 0xe6, 0xed, 0x81, 0x00, 0x07, 0x2e, 0x0e,
+  0x23, 0x85, 0x60, 0xcc, 0xb2, 0x79, 0x78, 0x5e, 0x05, 0xbe, 0xa8, 0xcd,
+  0x33, 0xad, 0x17, 0xb1, 0xb5, 0xa6, 0x30, 0xd5, 0xc5, 0x03, 0x33, 0x63,
+  0xd9, 0x67, 0x5f, 0xd4, 0xe2, 0xe9, 0xd6, 0x5e, 0xc6, 0xec, 0x18, 0xf3,
+  0x95, 0xa7, 0xa7, 0xb0, 0xa5, 0xb2, 0x00, 0x0d, 0x3c, 0x7f, 0xae, 0x97,
+  0x8d, 0x15, 0x05, 0x49, 0x69, 0xf5, 0x86, 0x81, 0x51, 0x8e, 0xf4, 0x8d,
+  0xf0, 0xa9, 0xda, 0x62, 0x32, 0xe2, 0xae, 0xfe, 0xab, 0x9d, 0x83, 0x34,
+  0x0f, 0x07, 0x10, 0x42, 0xa0, 0xd1, 0x3c, 0x5c, 0x5f, 0x81, 0xe9, 0x49,
+  0xff, 0xe3, 0xbe, 0xe6, 0xbe, 0x96, 0xf2, 0x64, 0xdb, 0x20, 0x0f, 0xbe,
+  0xf6, 0x2e, 0xa6, 0x10, 0x68, 0x31, 0x2e, 0x4d, 0xc9, 0xf9, 0x10, 0xeb,
+  0x74, 0x2b, 0xa8, 0xe6, 0x09, 0xce, 0x32, 0x24, 0xed, 0x9f, 0x59, 0x4d,
+  0x99, 0x27, 0x85, 0xc3, 0x7d, 0x23, 0x7c, 0xfb, 0x48, 0x0b, 0x0f, 0xd6,
+  0x95, 0x72, 0xf7, 0x8e, 0xa3, 0x8c, 0x46, 0x2d, 0x5a, 0x3e, 0x7d, 0x07,
+  0x7b, 0xbb, 0x87, 0x78, 0xe4, 0x60, 0x33, 0x1b, 0x2b, 0xf2, 0x69, 0x19,
+  0x09, 0xf2, 0x6a, 0xd7, 0x20, 0x29, 0x52, 0xb2, 0xb2, 0x28, 0x0b, 0xa7,
+  0x94, 0x2c, 0x7f, 0xf6, 0x20, 0xf7, 0x56, 0x15, 0x52, 0xea, 0x71, 0x11,
+  0xb0, 0x6c, 0x94, 0xd6, 0xfc, 0xf3, 0xf1, 0x73, 0xfc, 0xaa, 0xb9, 0x8b,
+  0xcf, 0xcf, 0x2a, 0xe1, 0xad, 0xde, 0x61, 0xbe, 0x79, 0xf0, 0x0c, 0x87,
+  0xef, 0xbf, 0x8d, 0x57, 0x3a, 0x06, 0x78, 0xf4, 0x50, 0x33, 0x5f, 0x98,
+  0x55, 0x42, 0x44, 0xa9, 0xc4, 0x0a, 0xf7, 0xf5, 0x78, 0xb9, 0x6f, 0xc7,
+  0x31, 0xd6, 0x57, 0x14, 0xb0, 0x67, 0x4b, 0xcc, 0x26, 0x3e, 0xb4, 0xb7,
+  0x89, 0xc6, 0x21, 0x1f, 0x8b, 0xf3, 0x33, 0xf8, 0xd6, 0xaf, 0xdf, 0xe4,
+  0xdf, 0x57, 0xd5, 0xf1, 0xc9, 0x9a, 0x62, 0xee, 0x7a, 0xee, 0x30, 0x0f,
+  0xcc, 0x2a, 0xa1, 0xd4, 0xe3, 0xfa, 0xff, 0x73, 0x41, 0x35, 0xd4, 0x77,
+  0x1e, 0xad, 0xed, 0x84, 0x29, 0x95, 0x02, 0x22, 0xfd, 0x7d, 0x3c, 0x98,
+  0x37, 0xc6, 0x54, 0x1a, 0x42, 0x03, 0xa6, 0xe1, 0x20, 0x33, 0xaf, 0x2a,
+  0x26, 0x85, 0xf1, 0x92, 0xe3, 0x72, 0x90, 0xeb, 0x72, 0xc6, 0xf2, 0x5f,
+  0x42, 0xc4, 0x32, 0xbb, 0x1a, 0xdc, 0xa6, 0x41, 0x87, 0x7f, 0x8c, 0xfb,
+  0x77, 0x35, 0xf0, 0xe9, 0x9a, 0x12, 0x52, 0x0d, 0x49, 0x45, 0x7a, 0x0a,
+  0xff, 0xb2, 0x72, 0x36, 0x5f, 0x79, 0xa3, 0x89, 0x2c, 0x97, 0x83, 0x7f,
+  0x5c, 0x1e, 0xf3, 0x20, 0x53, 0x0d, 0x83, 0x47, 0x17, 0x56, 0xb3, 0x38,
+  0x7f, 0xe2, 0x86, 0xd0, 0x90, 0x82, 0xca, 0xf4, 0x54, 0x7e, 0xb0, 0x62,
+  0x16, 0x27, 0x87, 0x7c, 0x2c, 0x79, 0xe6, 0x20, 0x61, 0x5b, 0x21, 0x04,
+  0xcc, 0xca, 0xf4, 0xf0, 0xd8, 0x6d, 0xb3, 0xdf, 0x93, 0x55, 0x3e, 0xc5,
+  0xdd, 0xd5, 0x85, 0x1c, 0xea, 0x1b, 0xe1, 0xc4, 0xa0, 0x8f, 0x5b, 0x72,
+  0xd3, 0x79, 0xb6, 0xbd, 0x97, 0x03, 0x5b, 0x97, 0x53, 0x93, 0xe1, 0xe6,
+  0xbb, 0x8b, 0x67, 0x22, 0x85, 0xc0, 0x56, 0x1a, 0xa7, 0xcb, 0x9c, 0x34,
+  0xdf, 0x0d, 0x05, 0x44, 0x6b, 0xcd, 0xbb, 0x47, 0x77, 0x20, 0x44, 0xb2,
+  0x8e, 0x5f, 0xec, 0x30, 0x58, 0x96, 0x26, 0x2e, 0xe3, 0xb1, 0xd9, 0x94,
+  0x54, 0x2d, 0x20, 0xbf, 0xb6, 0x66, 0x7a, 0x1e, 0x1e, 0xf0, 0xb9, 0x59,
+  0x25, 0xfc, 0xbe, 0xbd, 0x37, 0x61, 0x23, 0x2f, 0x05, 0x57, 0x5d, 0xe2,
+  0x38, 0x08, 0x01, 0x9f, 0xd9, 0x73, 0x82, 0x2c, 0xa7, 0xc9, 0xcf, 0xd7,
+  0xcd, 0x63, 0x6e, 0x76, 0x5a, 0xa2, 0x6d, 0xdb, 0xee, 0x13, 0x1c, 0xe8,
+  0x1d, 0xe6, 0xb7, 0x1b, 0x16, 0x50, 0xe2, 0x71, 0x21, 0x85, 0xa0, 0xc9,
+  0xeb, 0x67, 0xc1, 0xd3, 0x07, 0xf8, 0xca, 0xdc, 0x72, 0x1e, 0xaa, 0x2f,
+  0xe7, 0xad, 0xde, 0x61, 0xce, 0xf9, 0x43, 0x3c, 0xbe, 0xb6, 0x9e, 0x4f,
+  0xed, 0x3e, 0xc1, 0x13, 0x67, 0xba, 0xf8, 0xd1, 0xca, 0x3a, 0x10, 0x24,
+  0xe6, 0xce, 0x4f, 0x8d, 0x31, 0x4d, 0xc4, 0x56, 0x58, 0x4a, 0xf3, 0xd9,
+  0x3d, 0x27, 0xc8, 0x74, 0x99, 0x3c, 0x7b, 0xd7, 0x42, 0x4a, 0x3c, 0xae,
+  0x1b, 0x0d, 0x88, 0xcd, 0x82, 0xdb, 0x92, 0xef, 0x3a, 0x1c, 0x52, 0xf2,
+  0xc0, 0x2b, 0xc7, 0x71, 0x3b, 0x8c, 0xcb, 0x82, 0x98, 0xda, 0x66, 0xe2,
+  0xe8, 0x7a, 0xe7, 0x12, 0x23, 0xae, 0xb9, 0xaf, 0xba, 0x90, 0x4f, 0x54,
+  0x4d, 0xfd, 0xf3, 0x80, 0x5b, 0xf3, 0x32, 0x58, 0x5a, 0x90, 0xc9, 0xc3,
+  0x6f, 0x9e, 0x22, 0xd5, 0x94, 0x57, 0x58, 0x0f, 0xfc, 0x74, 0xf5, 0x5c,
+  0xee, 0x28, 0xc9, 0x9e, 0x04, 0xde, 0x2d, 0x39, 0x69, 0x1c, 0xeb, 0x1f,
+  0x65, 0x6d, 0x49, 0x76, 0x62, 0xce, 0x9a, 0x4c, 0x37, 0x87, 0xb7, 0xae,
+  0x20, 0x1a, 0x77, 0xc1, 0x7f, 0x7e, 0xaa, 0x0b, 0x0d, 0xfc, 0xf9, 0xde,
+  0x26, 0xc6, 0x6c, 0xc5, 0x73, 0xe7, 0xfa, 0xf8, 0xd1, 0xca, 0x3a, 0x2c,
+  0xa5, 0x13, 0xf6, 0x64, 0x77, 0xe7, 0x20, 0x11, 0x5b, 0x71, 0x77, 0x79,
+  0x1e, 0x86, 0x10, 0xfc, 0xe4, 0x3d, 0xf3, 0xdd, 0x50, 0x40, 0x84, 0x30,
+  0x48, 0xcf, 0x2a, 0x4c, 0xca, 0x0a, 0x9c, 0x1c, 0xf0, 0xf1, 0x87, 0x61,
+  0x41, 0x86, 0xf3, 0xf2, 0x07, 0x27, 0x7c, 0x01, 0x60, 0xe2, 0x11, 0x42,
+  0x54, 0x29, 0x3e, 0x57, 0x5b, 0x9a, 0xc4, 0xe9, 0x46, 0xec, 0x16, 0x28,
+  0xa6, 0x02, 0x6d, 0xc5, 0x57, 0xeb, 0x2b, 0xf9, 0xd5, 0x99, 0x6e, 0xda,
+  0x46, 0x43, 0x97, 0x5c, 0xc3, 0xc6, 0xae, 0x62, 0xc7, 0x4b, 0x9a, 0xc3,
+  0xe0, 0xa9, 0xb3, 0x3d, 0xbc, 0xde, 0x3d, 0x44, 0xd0, 0xb2, 0xf9, 0xde,
+  0xb2, 0xda, 0x78, 0x1f, 0x78, 0x74, 0xd1, 0x0c, 0x0e, 0xf4, 0x8d, 0x70,
+  0xff, 0x2b, 0xc7, 0x79, 0xe9, 0xe3, 0x8b, 0x11, 0x62, 0xc2, 0xeb, 0x72,
+  0x48, 0x89, 0x37, 0x1c, 0x65, 0x77, 0xd7, 0x20, 0x2f, 0xde, 0xb3, 0x88,
+  0x35, 0x71, 0xd0, 0x2a, 0x9e, 0xdc, 0xcb, 0xce, 0xf3, 0xfd, 0x7c, 0x69,
+  0x4e, 0x19, 0x5b, 0x5e, 0x6e, 0x60, 0x7d, 0x49, 0x0e, 0xff, 0xd3, 0x76,
+  0x91, 0xbf, 0x5b, 0x52, 0x83, 0x21, 0x63, 0xde, 0xdf, 0x53, 0x67, 0xbb,
+  0x79, 0xbd, 0x7b, 0x08, 0x0d, 0xfc, 0xfd, 0xd2, 0x9a, 0x1b, 0xfb, 0xd8,
+  0xba, 0xfb, 0x5c, 0x23, 0xde, 0x81, 0x8e, 0xa4, 0x2b, 0xd1, 0xa1, 0x31,
+  0x8b, 0x36, 0x5f, 0x68, 0x12, 0x87, 0x26, 0x24, 0xc8, 0x99, 0xca, 0xac,
+  0x05, 0xeb, 0x93, 0x62, 0x14, 0xad, 0xa1, 0x3e, 0x27, 0x2d, 0xe1, 0xde,
+  0xf6, 0x06, 0xc3, 0xb4, 0xfb, 0xc6, 0x58, 0x51, 0x98, 0xc9, 0x91, 0xbe,
+  0x11, 0x8a, 0xdd, 0x2e, 0xca, 0xd2, 0x52, 0xe8, 0xf0, 0x8f, 0xe1, 0x8b,
+  0x5a, 0x09, 0x55, 0xd4, 0x3c, 0x1c, 0xc0, 0x90, 0x82, 0x99, 0x19, 0xb1,
+  0x80, 0xf0, 0xdd, 0x21, 0x3f, 0xfd, 0xa1, 0x08, 0x3a, 0x0e, 0xf2, 0xfa,
+  0xd2, 0x5c, 0x3a, 0xfc, 0x63, 0x78, 0xc3, 0x51, 0x16, 0xe5, 0x67, 0x60,
+  0x6b, 0xcd, 0xde, 0x6e, 0x2f, 0x4b, 0xf2, 0x33, 0x18, 0xb3, 0x15, 0x9d,
+  0xfe, 0x31, 0x16, 0xc5, 0xf5, 0x7f, 0xd0, 0xb2, 0x39, 0xd8, 0x3b, 0xc2,
+  0xfa, 0x4b, 0x62, 0x9e, 0x53, 0xde, 0x00, 0xa0, 0x99, 0x93, 0x9d, 0xc6,
+  0x89, 0x41, 0x1f, 0xde, 0x70, 0x94, 0xaa, 0x74, 0x37, 0x95, 0xe9, 0xb1,
+  0x98, 0xee, 0x68, 0xff, 0x28, 0xfe, 0xa8, 0x15, 0x8b, 0xbd, 0x80, 0xf5,
+  0xa5, 0x39, 0x37, 0x0e, 0x10, 0xad, 0x15, 0x67, 0x4f, 0xbc, 0xc6, 0x88,
+  0x37, 0x39, 0x98, 0x0a, 0x59, 0x2a, 0xe9, 0xbe, 0x39, 0x39, 0x6e, 0xd6,
+  0xe4, 0xe4, 0x55, 0x50, 0x33, 0x7f, 0xed, 0x15, 0x52, 0xe4, 0xd3, 0xa8,
+  0x13, 0x53, 0xd5, 0x8b, 0x2b, 0x46, 0xe9, 0xd3, 0x1b, 0xe3, 0x72, 0xf7,
+  0x8d, 0x5c, 0x79, 0xbd, 0x22, 0x6e, 0xd0, 0xc4, 0x0d, 0xfe, 0x39, 0x82,
+  0x6d, 0x45, 0x92, 0x0e, 0x5f, 0x69, 0x58, 0xb7, 0xe3, 0x18, 0x41, 0xdb,
+  0xc6, 0x10, 0x32, 0xa1, 0x52, 0x0c, 0x29, 0xd1, 0x42, 0x60, 0x0a, 0x81,
+  0x34, 0x0d, 0x4c, 0xd3, 0x81, 0x94, 0x02, 0xa7, 0x90, 0x08, 0xa1, 0x71,
+  0x08, 0x89, 0x29, 0x63, 0xed, 0xa6, 0x10, 0x98, 0x52, 0x20, 0xa5, 0xc0,
+  0x10, 0x02, 0x87, 0x20, 0x51, 0x6f, 0x88, 0x98, 0xd7, 0x64, 0x08, 0x19,
+  0x7b, 0x7e, 0x13, 0x57, 0x6d, 0x26, 0x02, 0x43, 0x82, 0x21, 0x0d, 0x0c,
+  0x21, 0x30, 0x0c, 0x30, 0x90, 0x31, 0x3a, 0x34, 0x86, 0x90, 0x13, 0xb4,
+  0xd2, 0x48, 0x3c, 0xe7, 0x31, 0x20, 0x56, 0x2f, 0x04, 0x32, 0x3e, 0xe6,
+  0xf8, 0xbc, 0x32, 0x51, 0x1f, 0x03, 0x25, 0x36, 0xbf, 0x8e, 0xed, 0x47,
+  0xc4, 0xd6, 0x27, 0x11, 0xf1, 0xf5, 0x80, 0x88, 0xc7, 0x5f, 0x37, 0xd4,
+  0x86, 0x18, 0xa6, 0x33, 0xe9, 0x7b, 0xfb, 0x68, 0x88, 0x8e, 0x90, 0xcd,
+  0x98, 0x52, 0x18, 0x09, 0x30, 0xc0, 0x90, 0x1a, 0x25, 0xc1, 0x29, 0x80,
+  0xa8, 0x85, 0x29, 0x15, 0xd2, 0x90, 0x38, 0xa5, 0xc0, 0x14, 0xe0, 0x94,
+  0x32, 0x01, 0x84, 0x63, 0x1c, 0x10, 0xc3, 0xc0, 0x21, 0x62, 0x34, 0x86,
+  0x90, 0x38, 0x25, 0x48, 0x62, 0x6d, 0x46, 0xbc, 0xdf, 0xa5, 0x60, 0x99,
+  0x02, 0x4c, 0x43, 0xc7, 0x0e, 0x4e, 0x09, 0x0c, 0x14, 0x26, 0xe3, 0x6d,
+  0xb1, 0x88, 0xdf, 0x21, 0x05, 0x52, 0xc6, 0x40, 0x70, 0x48, 0x89, 0x8c,
+  0x03, 0x62, 0x0a, 0x81, 0x21, 0x63, 0xb4, 0x52, 0x49, 0x0c, 0x19, 0x4b,
+  0xed, 0x1b, 0x82, 0x58, 0x7f, 0x40, 0x09, 0x1d, 0x03, 0x4c, 0x80, 0x8a,
+  0x83, 0x65, 0xa0, 0xd1, 0x0a, 0xb4, 0x88, 0x8d, 0xa3, 0x15, 0xfc, 0x1f,
+  0xd7, 0xc0, 0x3b, 0xab, 0xdb, 0x65, 0xb1, 0x8a, 0x00, 0x00, 0x00, 0x00,
+  0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+};
+unsigned int openair_logo_png_len = 4664;
diff --git a/common/utils/T/tracer/openair_logo.png b/common/utils/T/tracer/openair_logo.png
new file mode 100644
index 0000000000000000000000000000000000000000..8b3dcfe46ef222f6b21f7d7517914b3e6019873f
Binary files /dev/null and b/common/utils/T/tracer/openair_logo.png differ
diff --git a/common/utils/T/tracer/plot.c b/common/utils/T/tracer/plot.c
new file mode 100644
index 0000000000000000000000000000000000000000..74473842883c7e968ecf3c84079c4645e18f2467
--- /dev/null
+++ b/common/utils/T/tracer/plot.c
@@ -0,0 +1,291 @@
+#include "defs.h"
+#include <X11/Xlib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <math.h>
+#include <unistd.h>
+#include <sys/select.h>
+#include <stdarg.h>
+
+typedef struct {
+  float *buf;
+  short *iqbuf;
+  int count;
+  int type;
+  volatile int iq_count;  /* for ULSCH IQ data */
+  int iq_insert_pos;
+  GC g;
+} data;
+
+typedef struct {
+  Display *d;
+  Window w;
+  Pixmap px;
+  GC bg;
+  int width;
+  int height;
+  pthread_mutex_t lock;
+  float zoom;
+  int timer_pipe[2];
+  data *p;             /* list of plots */
+  int nplots;
+} plot;
+
+static void *timer_thread(void *_p)
+{
+  plot *p = _p;
+  char c;
+
+  while (1) {
+    /* more or less 10Hz */
+    usleep(100*1000);
+    c = 1;
+    if (write(p->timer_pipe[1], &c, 1) != 1) abort();
+  }
+
+  return NULL;
+}
+
+static void *plot_thread(void *_p)
+{
+  float v;
+  float *s;
+  int i, j;
+  plot *p = _p;
+  int redraw = 0;
+  int replot = 0;
+  fd_set rset;
+  int xfd = ConnectionNumber(p->d);
+  int maxfd = xfd > p->timer_pipe[0] ? xfd : p->timer_pipe[0];
+  int pp;
+
+  while (1) {
+    while (XPending(p->d)) {
+      XEvent e;
+      XNextEvent(p->d, &e);
+      switch (e.type) {
+      case ButtonPress:
+        /* button 4: zoom out */
+        if (e.xbutton.button == 4) { p->zoom = p->zoom * 1.25; replot = 1; }
+        /* button 5: zoom in */
+        if (e.xbutton.button == 5) { p->zoom = p->zoom * 0.8; replot = 1; }
+        printf("zoom: %f\n", p->zoom);
+        break;
+      case Expose: redraw = 1; break;
+      }
+    }
+
+    if (replot == 1) {
+      replot = 0;
+      redraw = 1;
+
+      if (pthread_mutex_lock(&p->lock)) abort();
+
+      XFillRectangle(p->d, p->px, p->bg, 0, 0, p->width, p->height);
+
+      for (pp = 0; pp < p->nplots; pp++) {
+        if (p->p[pp].type == PLOT_MINMAX) {
+          s = p->p[pp].buf;
+          for (i = 0; i < 512; i++) {
+            int min = *s;
+            int max = *s;
+            for (j = 0; j < p->p[pp].count/512; j++, s++) {
+              if (*s < min) min = *s;
+              if (*s > max) max = *s;
+            }
+            XDrawLine(p->d, p->px, p->p[pp].g, i, 100-min, i, 100-max);
+          }
+        } else if (p->p[pp].type == PLOT_VS_TIME) {
+          for (i = 0; i < p->p[pp].count; i++)
+            p->p[pp].buf[i] =
+                10*log10(1.0+(float)(p->p[pp].iqbuf[2*i]*p->p[pp].iqbuf[2*i]+
+                p->p[pp].iqbuf[2*i+1]*p->p[pp].iqbuf[2*i+1]));
+          s = p->p[pp].buf;
+          for (i = 0; i < 512; i++) {
+            v = 0;
+            for (j = 0; j < p->p[pp].count/512; j++, s++) v += *s;
+            v /= p->p[pp].count/512;
+            XDrawLine(p->d, p->px, p->p[pp].g, i, 100, i, 100-v);
+          }
+        } else if (p->p[pp].type == PLOT_IQ_POINTS) {
+          XPoint pts[p->p[pp].iq_count];
+          int count = p->p[pp].iq_count;
+          for (i = 0; i < count; i++) {
+            pts[i].x = p->p[pp].iqbuf[2*i]*p->zoom/20+50;
+            pts[i].y = -p->p[pp].iqbuf[2*i+1]*p->zoom/20+50;
+          }
+          XDrawPoints(p->d, p->px, p->p[pp].g, pts, count, CoordModeOrigin);
+        }
+      }
+
+      if (pthread_mutex_unlock(&p->lock)) abort();
+    }
+
+    if (redraw) {
+      redraw = 0;
+      XCopyArea(p->d, p->px, p->w, DefaultGC(p->d, DefaultScreen(p->d)),
+                0, 0, p->width, p->height, 0, 0);
+    }
+
+    XFlush(p->d);
+
+    FD_ZERO(&rset);
+    FD_SET(p->timer_pipe[0], &rset);
+    FD_SET(xfd, &rset);
+    if (select(maxfd+1, &rset, NULL, NULL, NULL) == -1) abort();
+    if (FD_ISSET(p->timer_pipe[0], &rset)) {
+      char b[512];
+      if (read(p->timer_pipe[0], b, 512) <= 0) abort();
+      replot = 1;
+    }
+  }
+
+  return NULL;
+}
+
+void *make_plot(int width, int height, char *title, int nplots, ...)
+{
+  plot *p;
+  Display *d;
+  Window w;
+  Pixmap pm;
+  int i;
+  va_list ap;
+  XGCValues gcv;
+
+  p = malloc(sizeof(*p)); if (p == NULL) abort();
+
+  d = XOpenDisplay(0); if (d == NULL) abort();
+  w = XCreateSimpleWindow(d, DefaultRootWindow(d), 0, 0, width, height,
+        0, WhitePixel(d, DefaultScreen(d)), WhitePixel(d, DefaultScreen(d)));
+  XSelectInput(d, w, ExposureMask | ButtonPressMask);
+  XMapWindow(d, w);
+
+  {
+    XSetWindowAttributes att;
+    att.backing_store = Always;
+    XChangeWindowAttributes(d, w, CWBackingStore, &att);
+  }
+
+  XStoreName(d, w, title);
+
+  p->bg = XCreateGC(d, w, 0, NULL);
+  XCopyGC(d, DefaultGC(d, DefaultScreen(d)), -1L, p->bg);
+  gcv.foreground = WhitePixel(d, DefaultScreen(d));
+  XChangeGC(d, p->bg, GCForeground, &gcv);
+
+  pm = XCreatePixmap(d, w, width, height, DefaultDepth(d, DefaultScreen(d)));
+
+  p->width = width;
+  p->height = height;
+  p->p = malloc(nplots * sizeof(data)); if (p->p == NULL) abort();
+
+  va_start(ap, nplots);
+  for (i = 0; i < nplots; i++) {
+    int count;
+    int type;
+    char *color;
+    XColor rcol, scol;
+
+    count = va_arg(ap, int);
+    type = va_arg(ap, int);
+    color = va_arg(ap, char *);
+
+    p->p[i].g = XCreateGC(d, w, 0, NULL);
+    XCopyGC(d, DefaultGC(d, DefaultScreen(d)), -1L, p->p[i].g);
+    if (XAllocNamedColor(d, DefaultColormap(d, DefaultScreen(d)),
+                         color, &scol, &rcol)) {
+      gcv.foreground = scol.pixel;
+      XChangeGC(d, p->p[i].g, GCForeground, &gcv);
+    } else {
+      printf("could not allocate color '%s'\n", color);
+      abort();
+    }
+
+    if (type == PLOT_VS_TIME) {
+      p->p[i].buf = malloc(sizeof(float) * count);
+      if (p->p[i].buf == NULL) abort();
+      p->p[i].iqbuf = malloc(sizeof(short) * count * 2);
+      if(p->p[i].iqbuf==NULL)abort();
+    } else if (type == PLOT_MINMAX) {
+      p->p[i].buf = malloc(sizeof(float) * count);
+      if (p->p[i].buf == NULL) abort();
+      p->p[i].iqbuf = NULL;
+    } else {
+      p->p[i].buf = NULL;
+      p->p[i].iqbuf = malloc(sizeof(short) * count * 2);
+      if(p->p[i].iqbuf==NULL)abort();
+    }
+    p->p[i].count = count;
+    p->p[i].type = type;
+    p->p[i].iq_count = 0;
+    p->p[i].iq_insert_pos = 0;
+  }
+  va_end(ap);
+
+  p->d = d;
+  p->w = w;
+  p->px = pm;
+
+  p->zoom = 1;
+  p->nplots = nplots;
+
+  pthread_mutex_init(&p->lock, NULL);
+
+  if (pipe(p->timer_pipe)) abort();
+
+  new_thread(plot_thread, p);
+  new_thread(timer_thread, p);
+
+  return p;
+}
+
+void plot_set(void *_plot, float *data, int len, int pos, int pp)
+{
+  plot *p = _plot;
+  if (pthread_mutex_lock(&p->lock)) abort();
+  memcpy(p->p[pp].buf + pos, data, len * sizeof(float));
+  if (pthread_mutex_unlock(&p->lock)) abort();
+}
+
+void iq_plot_set(void *_plot, short *data, int count, int pos, int pp)
+{
+  plot *p = _plot;
+  if (pthread_mutex_lock(&p->lock)) abort();
+  memcpy(p->p[pp].iqbuf + pos * 2, data, count * 2 * sizeof(short));
+  if (pthread_mutex_unlock(&p->lock)) abort();
+}
+
+void iq_plot_set_sized(void *_plot, short *data, int count, int pp)
+{
+  plot *p = _plot;
+  if (pthread_mutex_lock(&p->lock)) abort();
+  memcpy(p->p[pp].iqbuf, data, count * 2 * sizeof(short));
+  p->p[pp].iq_count = count;
+  if (pthread_mutex_unlock(&p->lock)) abort();
+}
+
+void iq_plot_add_iq_point_loop(void *_plot, short i, short q, int pp)
+{
+  plot *p = _plot;
+  if (pthread_mutex_lock(&p->lock)) abort();
+  p->p[pp].iqbuf[p->p[pp].iq_insert_pos*2] = i;
+  p->p[pp].iqbuf[p->p[pp].iq_insert_pos*2+1] = q;
+  if (p->p[pp].iq_count != p->p[pp].count) p->p[pp].iq_count++;
+  p->p[pp].iq_insert_pos++;
+  if (p->p[pp].iq_insert_pos == p->p[pp].count) p->p[pp].iq_insert_pos = 0;
+  if (pthread_mutex_unlock(&p->lock)) abort();
+}
+
+void iq_plot_add_energy_point_loop(void *_plot, int e, int pp)
+{
+  plot *p = _plot;
+  if (pthread_mutex_lock(&p->lock)) abort();
+  p->p[pp].buf[p->p[pp].iq_insert_pos] = e;
+  if (p->p[pp].iq_count != p->p[pp].count) p->p[pp].iq_count++;
+  p->p[pp].iq_insert_pos++;
+  if (p->p[pp].iq_insert_pos == p->p[pp].count) p->p[pp].iq_insert_pos = 0;
+  if (pthread_mutex_unlock(&p->lock)) abort();
+}
diff --git a/common/utils/T/tracer/textlog.c b/common/utils/T/tracer/textlog.c
new file mode 100644
index 0000000000000000000000000000000000000000..f3d0b34831dbe8e72d2f9553f4672fbdd6af436b
--- /dev/null
+++ b/common/utils/T/tracer/textlog.c
@@ -0,0 +1,194 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <signal.h>
+#include "database.h"
+#include "event.h"
+#include "handler.h"
+#include "logger/logger.h"
+#include "view/view.h"
+#include "gui/gui.h"
+#include "utils.h"
+#include "../T_defs.h"
+#include "event_selector.h"
+#include "config.h"
+
+#define DEFAULT_REMOTE_IP "127.0.0.1"
+#define DEFAULT_REMOTE_PORT 2021
+
+typedef struct {
+  int socket;
+  int *is_on;
+  int nevents;
+  pthread_mutex_t lock;
+} textlog_data;
+
+void is_on_changed(void *_d)
+{
+  textlog_data *d = _d;
+  char t;
+
+  if (pthread_mutex_lock(&d->lock)) abort();
+
+  if (d->socket == -1) goto no_connection;
+
+  t = 1;
+  if (socket_send(d->socket, &t, 1) == -1 ||
+      socket_send(d->socket, &d->nevents, sizeof(int)) == -1 ||
+      socket_send(d->socket, d->is_on, d->nevents * sizeof(int)) == -1)
+    abort();
+
+no_connection:
+  if (pthread_mutex_unlock(&d->lock)) abort();
+}
+
+void usage(void)
+{
+  printf(
+"options:\n"
+"    -d <database file>        this option is mandatory\n"
+"    -on <GROUP or ID>         turn log ON for given GROUP or ID\n"
+"    -off <GROUP or ID>        turn log OFF for given GROUP or ID\n"
+"    -ON                       turn all logs ON\n"
+"    -OFF                      turn all logs OFF\n"
+"                              note: you may pass several -on/-off/-ON/-OFF,\n"
+"                                    they will be processed in order\n"
+"                                    by default, all is off\n"
+"    -ip <host>                connect to given IP address (default %s)\n"
+"    -p <port>                 connect to given port (default %d)\n"
+"    -x                        GUI output\n"
+"    -debug-gui                activate GUI debug logs\n"
+"    -no-gui                   disable GUI entirely\n",
+  DEFAULT_REMOTE_IP,
+  DEFAULT_REMOTE_PORT
+  );
+  exit(1);
+}
+
+static void *gui_thread(void *_g)
+{
+  gui *g = _g;
+  gui_loop(g);
+  return NULL;
+}
+
+int main(int n, char **v)
+{
+  extern int volatile gui_logd;
+  char *database_filename = NULL;
+  void *database;
+  char *ip = DEFAULT_REMOTE_IP;
+  int port = DEFAULT_REMOTE_PORT;
+  char **on_off_name;
+  int *on_off_action;
+  int on_off_n = 0;
+  int *is_on;
+  int number_of_events;
+  int i;
+  event_handler *h;
+  logger *textlog;
+  gui *g;
+  int gui_mode = 0;
+  view *out;
+  int gui_active = 1;
+  textlog_data textlog_data;
+
+  /* write on a socket fails if the other end is closed and we get SIGPIPE */
+  if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) abort();
+
+  on_off_name = malloc(n * sizeof(char *)); if (on_off_name == NULL) abort();
+  on_off_action = malloc(n * sizeof(int)); if (on_off_action == NULL) abort();
+
+  for (i = 1; i < n; i++) {
+    if (!strcmp(v[i], "-h") || !strcmp(v[i], "--help")) usage();
+    if (!strcmp(v[i], "-d"))
+      { if (i > n-2) usage(); database_filename = v[++i]; continue; }
+    if (!strcmp(v[i], "-ip")) { if (i > n-2) usage(); ip = v[++i]; continue; }
+    if (!strcmp(v[i], "-p"))
+      { if (i > n-2) usage(); port = atoi(v[++i]); continue; }
+    if (!strcmp(v[i], "-on")) { if (i > n-2) usage();
+      on_off_name[on_off_n]=v[++i]; on_off_action[on_off_n++]=1; continue; }
+    if (!strcmp(v[i], "-off")) { if (i > n-2) usage();
+      on_off_name[on_off_n]=v[++i]; on_off_action[on_off_n++]=0; continue; }
+    if (!strcmp(v[i], "-ON"))
+      { on_off_name[on_off_n]=NULL; on_off_action[on_off_n++]=1; continue; }
+    if (!strcmp(v[i], "-OFF"))
+      { on_off_name[on_off_n]=NULL; on_off_action[on_off_n++]=0; continue; }
+    if (!strcmp(v[i], "-x")) { gui_mode = 1; continue; }
+    if (!strcmp(v[i], "-debug-gui")) { gui_logd = 1; continue; }
+    if (!strcmp(v[i], "-no-gui")) { gui_active = 0; continue; }
+    usage();
+  }
+
+  if (gui_active == 0) gui_mode = 0;
+
+  if (database_filename == NULL) {
+    printf("ERROR: provide a database file (-d)\n");
+    exit(1);
+  }
+
+  database = parse_database(database_filename);
+
+  load_config_file(database_filename);
+
+  number_of_events = number_of_ids(database);
+  is_on = calloc(number_of_events, sizeof(int));
+  if (is_on == NULL) abort();
+
+  h = new_handler(database);
+
+  if (gui_active) {
+    g = gui_init();
+    new_thread(gui_thread, g);
+  }
+
+  if (gui_mode) {
+    widget *w, *win;
+//    w = new_textlist(g, 600, 20, 0);
+    w = new_textlist(g, 800, 50, BACKGROUND_COLOR);
+    win = new_toplevel_window(g, 800, 50*12, "textlog");
+    widget_add_child(g, win, w, -1);
+    out = new_view_textlist(1000, 10, g, w);
+    //tout = new_view_textlist(7, 4, g, w);
+  } else {
+    out = new_view_stdout();
+  }
+
+  for (i = 0; i < number_of_events; i++) {
+    char *name, *desc;
+    database_get_generic_description(database, i, &name, &desc);
+    textlog = new_textlog(h, database, name, desc);
+//        "ENB_UL_CHANNEL_ESTIMATE",
+//        "ev: {} eNB_id [eNB_ID] frame [frame] subframe [subframe]");
+    logger_add_view(textlog, out);
+    free(name);
+    free(desc);
+  }
+
+  for (i = 0; i < on_off_n; i++)
+    on_off(database, on_off_name[i], is_on, on_off_action[i]);
+
+  textlog_data.socket = -1;
+  textlog_data.is_on = is_on;
+  textlog_data.nevents = number_of_events;
+  if (pthread_mutex_init(&textlog_data.lock, NULL)) abort();
+  if (gui_active)
+    setup_event_selector(g, database, is_on, is_on_changed, &textlog_data);
+
+  textlog_data.socket = connect_to(ip, port);
+
+  /* send the first message - activate selected traces */
+  is_on_changed(&textlog_data);
+
+  /* read messages */
+  while (1) {
+    char v[T_BUFFER_MAX];
+    event e;
+    e = get_event(textlog_data.socket, v, database);
+    if (e.type == -1) abort();
+    handle_event(h, e);
+  }
+
+  return 0;
+}
diff --git a/common/utils/T/tracer/utils.c b/common/utils/T/tracer/utils.c
new file mode 100644
index 0000000000000000000000000000000000000000..3ebc000c671b62799c330775e0804e9ba8279d95
--- /dev/null
+++ b/common/utils/T/tracer/utils.c
@@ -0,0 +1,198 @@
+#include "utils.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <time.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+void new_thread(void *(*f)(void *), void *data)
+{
+  pthread_t t;
+  pthread_attr_t att;
+
+  if (pthread_attr_init(&att))
+    { fprintf(stderr, "pthread_attr_init err\n"); exit(1); }
+  if (pthread_attr_setdetachstate(&att, PTHREAD_CREATE_DETACHED))
+    { fprintf(stderr, "pthread_attr_setdetachstate err\n"); exit(1); }
+  if (pthread_attr_setstacksize(&att, 10000000))
+    { fprintf(stderr, "pthread_attr_setstacksize err\n"); exit(1); }
+  if (pthread_create(&t, &att, f, data))
+    { fprintf(stderr, "pthread_create err\n"); exit(1); }
+  if (pthread_attr_destroy(&att))
+    { fprintf(stderr, "pthread_attr_destroy err\n"); exit(1); }
+}
+
+void sleepms(int ms)
+{
+  struct timespec t;
+
+  t.tv_sec = ms / 1000;
+  t.tv_nsec = (ms % 1000) * 1000000L;
+
+  /* TODO: deal with EINTR */
+  if (nanosleep(&t, NULL)) abort();
+}
+
+/****************************************************************************/
+/* list                                                                     */
+/****************************************************************************/
+
+list *list_remove_head(list *l)
+{
+  list *ret;
+  if (l == NULL) return NULL;
+  ret = l->next;
+  if (ret != NULL) ret->last = l->last;
+  free(l);
+  return ret;
+}
+
+list *list_append(list *l, void *data)
+{
+  list *new = calloc(1, sizeof(list));
+  if (new == NULL) abort();
+  new->data = data;
+  if (l == NULL) {
+    new->last = new;
+    return new;
+  }
+  l->last->next = new;
+  l->last = new;
+  return l;
+}
+
+/****************************************************************************/
+/* socket                                                                   */
+/****************************************************************************/
+
+int socket_send(int socket, void *buffer, int size)
+{
+  char *x = buffer;
+  int ret;
+  while (size) {
+    ret = write(socket, x, size);
+    if (ret <= 0) return -1;
+    size -= ret;
+    x += ret;
+  }
+  return 0;
+}
+
+int get_connection(char *addr, int port)
+{
+  struct sockaddr_in a;
+  socklen_t alen;
+  int s, t;
+
+  printf("waiting for connection on %s:%d\n", addr, port);
+
+  s = socket(AF_INET, SOCK_STREAM, 0);
+  if (s == -1) { perror("socket"); exit(1); }
+  t = 1;
+  if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &t, sizeof(int)))
+    { perror("setsockopt"); exit(1); }
+
+  a.sin_family = AF_INET;
+  a.sin_port = htons(port);
+  a.sin_addr.s_addr = inet_addr(addr);
+
+  if (bind(s, (struct sockaddr *)&a, sizeof(a))) { perror("bind"); exit(1); }
+  if (listen(s, 5)) { perror("bind"); exit(1); }
+  alen = sizeof(a);
+  t = accept(s, (struct sockaddr *)&a, &alen);
+  if (t == -1) { perror("accept"); exit(1); }
+  close(s);
+
+  printf("connected\n");
+
+  return t;
+}
+
+int fullread(int fd, void *_buf, int count)
+{
+  char *buf = _buf;
+  int ret = 0;
+  int l;
+  while (count) {
+    l = read(fd, buf, count);
+    if (l <= 0) return -1;
+    count -= l;
+    buf += l;
+    ret += l;
+  }
+  return ret;
+}
+
+int connect_to(char *addr, int port)
+{
+  int s;
+  struct sockaddr_in a;
+
+  printf("connecting to %s:%d\n", addr, port);
+
+again:
+  s = socket(AF_INET, SOCK_STREAM, 0);
+  if (s == -1) { perror("socket"); exit(1); }
+
+  a.sin_family = AF_INET;
+  a.sin_port = htons(port);
+  a.sin_addr.s_addr = inet_addr(addr);
+
+  if (connect(s, (struct sockaddr *)&a, sizeof(a)) == -1) {
+    perror("connect");
+    close(s);
+    printf("trying again in 1s\n");
+    sleep(1);
+    goto again;
+  }
+
+  return s;
+}
+
+/****************************************************************************/
+/* buffer                                                                   */
+/****************************************************************************/
+
+void PUTC(OBUF *o, char c)
+{
+  if (o->osize == o->omaxsize) {
+    o->omaxsize += 512;
+    o->obuf = realloc(o->obuf, o->omaxsize);
+    if (o->obuf == NULL) abort();
+  }
+  o->obuf[o->osize] = c;
+  o->osize++;
+}
+
+void PUTS(OBUF *o, char *s)
+{
+  while (*s) PUTC(o, *s++);
+}
+
+static int clean(char c)
+{
+  if (!isprint(c)) c = ' ';
+  return c;
+}
+
+void PUTS_CLEAN(OBUF *o, char *s)
+{
+  while (*s) PUTC(o, clean(*s++));
+}
+
+void PUTI(OBUF *o, int i)
+{
+  char s[64];
+  sprintf(s, "%d", i);
+  PUTS(o, s);
+}
+
+void PUTUL(OBUF *o, unsigned long l)
+{
+  char s[128];
+  sprintf(s, "%ld", l);
+  PUTS(o, s);
+}
diff --git a/common/utils/T/tracer/utils.h b/common/utils/T/tracer/utils.h
new file mode 100644
index 0000000000000000000000000000000000000000..e15a8d2fbad5c6e910e3ba3e21c6b0cc219a9952
--- /dev/null
+++ b/common/utils/T/tracer/utils.h
@@ -0,0 +1,46 @@
+#ifndef _UTILS_H_
+#define _UTILS_H_
+
+void new_thread(void *(*f)(void *), void *data);
+void sleepms(int ms);
+
+/****************************************************************************/
+/* list                                                                     */
+/****************************************************************************/
+
+typedef struct list {
+  struct list *last, *next;
+  void *data;
+} list;
+
+list *list_remove_head(list *l);
+list *list_append(list *l, void *data);
+
+/****************************************************************************/
+/* socket                                                                   */
+/****************************************************************************/
+
+/* socket_send: return 0 if okay, -1 on error */
+int socket_send(int socket, void *buffer, int size);
+int get_connection(char *addr, int port);
+/* fullread: return length read if okay (that is: 'count'), -1 on error */
+int fullread(int fd, void *_buf, int count);
+int connect_to(char *addr, int port);
+
+/****************************************************************************/
+/* buffer                                                                   */
+/****************************************************************************/
+
+typedef struct {
+  int osize;
+  int omaxsize;
+  char *obuf;
+} OBUF;
+
+void PUTC(OBUF *o, char c);
+void PUTS(OBUF *o, char *s);
+void PUTS_CLEAN(OBUF *o, char *s);
+void PUTI(OBUF *o, int i);
+void PUTUL(OBUF *o, unsigned long i);
+
+#endif /* _UTILS_H_ */
diff --git a/common/utils/T/tracer/vcd.c b/common/utils/T/tracer/vcd.c
new file mode 100644
index 0000000000000000000000000000000000000000..45aa4e26e2ae332586a143c1ebf680fcd34c9d1d
--- /dev/null
+++ b/common/utils/T/tracer/vcd.c
@@ -0,0 +1,204 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <signal.h>
+#include "database.h"
+#include "event.h"
+#include "handler.h"
+#include "logger/logger.h"
+#include "view/view.h"
+#include "gui/gui.h"
+#include "utils.h"
+#include "../T_defs.h"
+#include "event_selector.h"
+#include "config.h"
+
+typedef struct {
+  int socket;
+  int *is_on;
+  int nevents;
+  pthread_mutex_t lock;
+} vcd_data;
+
+void is_on_changed(void *_d)
+{
+  vcd_data *d = _d;
+  char t;
+
+  if (pthread_mutex_lock(&d->lock)) abort();
+
+  if (d->socket == -1) goto no_connection;
+
+  t = 1;
+  if (socket_send(d->socket, &t, 1) == -1 ||
+      socket_send(d->socket, &d->nevents, sizeof(int)) == -1 ||
+      socket_send(d->socket, d->is_on, d->nevents * sizeof(int)) == -1)
+    abort();
+
+no_connection:
+  if (pthread_mutex_unlock(&d->lock)) abort();
+}
+
+#define DEFAULT_REMOTE_IP "127.0.0.1"
+#define DEFAULT_REMOTE_PORT 2021
+
+void usage(void)
+{
+  printf(
+"options:\n"
+"    -d <database file>        this option is mandatory\n"
+"    -on <GROUP or ID>         turn log ON for given GROUP or ID\n"
+"    -off <GROUP or ID>        turn log OFF for given GROUP or ID\n"
+"    -ON                       turn all logs ON\n"
+"    -OFF                      turn all logs OFF\n"
+"                              note: you may pass several -on/-off/-ON/-OFF,\n"
+"                                    they will be processed in order\n"
+"                                    by default, all is off\n"
+"    -ip <host>                connect to given IP address (default %s)\n"
+"    -p <port>                 connect to given port (default %d)\n"
+"    -debug-gui                activate GUI debug logs\n",
+  DEFAULT_REMOTE_IP,
+  DEFAULT_REMOTE_PORT
+  );
+  exit(1);
+}
+
+static void *gui_thread(void *_g)
+{
+  gui *g = _g;
+  gui_loop(g);
+  return NULL;
+}
+
+static void vcd_main_gui(gui *g, event_handler *h, void *database)
+{
+  int i, j;
+  int n;
+  int nb_functions = 0;
+  char **ids;
+  widget *win;
+  widget *container;
+  widget *w;
+  view *timeview;
+  view *subview;
+  logger *timelog;
+
+  /* get number of vcd functions - look for all events VCD_FUNCTION_xxx */
+  n = database_get_ids(database, &ids);
+  for (i = 0; i < n; i++) {
+    if (strncmp(ids[i], "VCD_FUNCTION_", 13) != 0) continue;
+    nb_functions++;
+  }
+
+  win = new_toplevel_window(g, 1000, 5 * nb_functions, "VCD tracer");
+  container = new_container(g, VERTICAL);
+  widget_add_child(g, win, container, -1);
+
+  w = new_timeline(g, 1000, nb_functions, 5);
+  widget_add_child(g, container, w, -1);
+  for (i = 0; i < nb_functions; i++)
+    timeline_set_subline_background_color(g, w, i,
+        new_color(g, i & 1 ? "#ddd" : "#eee"));
+  timeview = new_view_time(3600, 10, g, w);
+  i = 0;
+  for (j = 0; j < n; j++) {
+    if (strncmp(ids[j], "VCD_FUNCTION_", 13) != 0) continue;
+    timelog = new_timelog(h, database, ids[j]);
+    subview = new_subview_time(timeview, i, FOREGROUND_COLOR, 3600*1000);
+    logger_add_view(timelog, subview);
+    i++;
+  }
+
+  free(ids);
+}
+
+int main(int n, char **v)
+{
+  extern int volatile gui_logd;
+  char *database_filename = NULL;
+  void *database;
+  char *ip = DEFAULT_REMOTE_IP;
+  int port = DEFAULT_REMOTE_PORT;
+  char **on_off_name;
+  int *on_off_action;
+  int on_off_n = 0;
+  int *is_on;
+  int number_of_events;
+  int i;
+  event_handler *h;
+  gui *g;
+  vcd_data vcd_data;
+
+  /* write on a socket fails if the other end is closed and we get SIGPIPE */
+  if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) abort();
+
+  on_off_name = malloc(n * sizeof(char *)); if (on_off_name == NULL) abort();
+  on_off_action = malloc(n * sizeof(int)); if (on_off_action == NULL) abort();
+
+  for (i = 1; i < n; i++) {
+    if (!strcmp(v[i], "-h") || !strcmp(v[i], "--help")) usage();
+    if (!strcmp(v[i], "-d"))
+      { if (i > n-2) usage(); database_filename = v[++i]; continue; }
+    if (!strcmp(v[i], "-ip")) { if (i > n-2) usage(); ip = v[++i]; continue; }
+    if (!strcmp(v[i], "-p"))
+      { if (i > n-2) usage(); port = atoi(v[++i]); continue; }
+    if (!strcmp(v[i], "-on")) { if (i > n-2) usage();
+      on_off_name[on_off_n]=v[++i]; on_off_action[on_off_n++]=1; continue; }
+    if (!strcmp(v[i], "-off")) { if (i > n-2) usage();
+      on_off_name[on_off_n]=v[++i]; on_off_action[on_off_n++]=0; continue; }
+    if (!strcmp(v[i], "-ON"))
+      { on_off_name[on_off_n]=NULL; on_off_action[on_off_n++]=1; continue; }
+    if (!strcmp(v[i], "-OFF"))
+      { on_off_name[on_off_n]=NULL; on_off_action[on_off_n++]=0; continue; }
+    if (!strcmp(v[i], "-debug-gui")) { gui_logd = 1; continue; }
+    usage();
+  }
+
+  if (database_filename == NULL) {
+    printf("ERROR: provide a database file (-d)\n");
+    exit(1);
+  }
+
+  database = parse_database(database_filename);
+
+  load_config_file(database_filename);
+
+  number_of_events = number_of_ids(database);
+  is_on = calloc(number_of_events, sizeof(int));
+  if (is_on == NULL) abort();
+
+  h = new_handler(database);
+
+  g = gui_init();
+  new_thread(gui_thread, g);
+
+  vcd_main_gui(g, h, database);
+
+  on_off(database, "VCD_FUNCTION", is_on, 1);
+
+  for (i = 0; i < on_off_n; i++)
+    on_off(database, on_off_name[i], is_on, on_off_action[i]);
+
+  vcd_data.socket = -1;
+  vcd_data.is_on = is_on;
+  vcd_data.nevents = number_of_events;
+  if (pthread_mutex_init(&vcd_data.lock, NULL)) abort();
+  setup_event_selector(g, database, is_on, is_on_changed, &vcd_data);
+
+  vcd_data.socket = connect_to(ip, port);
+
+  /* send the first message - activate selected traces */
+  is_on_changed(&vcd_data);
+
+  /* read messages */
+  while (1) {
+    char v[T_BUFFER_MAX];
+    event e;
+    e = get_event(vcd_data.socket, v, database);
+    if (e.type == -1) abort();
+    handle_event(h, e);
+  }
+
+  return 0;
+}
diff --git a/common/utils/T/tracer/view/Makefile b/common/utils/T/tracer/view/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..32b267ec24d60c25bf1650e5b60250579c6be871
--- /dev/null
+++ b/common/utils/T/tracer/view/Makefile
@@ -0,0 +1,13 @@
+CC=gcc
+CFLAGS=-Wall -g -pthread -I.. -I../logger
+
+OBJS=stdout.o textlist.o xy.o tti.o time.o ticktime.o
+
+view.a: $(OBJS)
+	ar cr view.a $(OBJS)
+
+%.o: %.c
+	$(CC) $(CFLAGS) -o $@ -c $<
+
+clean:
+	rm -f *.a *.o
diff --git a/common/utils/T/tracer/view/stdout.c b/common/utils/T/tracer/view/stdout.c
new file mode 100644
index 0000000000000000000000000000000000000000..93223e59d81d2591a9a317f75c84cd7f57b32d33
--- /dev/null
+++ b/common/utils/T/tracer/view/stdout.c
@@ -0,0 +1,35 @@
+#include "view.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <pthread.h>
+
+struct stdout {
+  view common;
+  pthread_mutex_t lock;
+};
+
+static void clear(view *this)
+{
+  /* do nothing */
+}
+
+static void append(view *_this, char *s)
+{
+  struct stdout *this = (struct stdout *)_this;
+  if (pthread_mutex_lock(&this->lock)) abort();
+  printf("%s\n", s);
+  if (pthread_mutex_unlock(&this->lock)) abort();
+}
+
+view *new_view_stdout(void)
+{
+  struct stdout *ret = calloc(1, sizeof(struct stdout));
+  if (ret == NULL) abort();
+
+  ret->common.clear = clear;
+  ret->common.append = (void (*)(view *, ...))append;
+
+  if (pthread_mutex_init(&ret->lock, NULL)) abort();
+
+  return (view *)ret;
+}
diff --git a/common/utils/T/tracer/view/textlist.c b/common/utils/T/tracer/view/textlist.c
new file mode 100644
index 0000000000000000000000000000000000000000..5227900e4ef36d241b8fd62c2233d0d7ba9a2620
--- /dev/null
+++ b/common/utils/T/tracer/view/textlist.c
@@ -0,0 +1,154 @@
+#include "view.h"
+#include "../utils.h"
+#include "gui/gui.h"
+#include <stdlib.h>
+#include <pthread.h>
+#include <string.h>
+
+struct textlist {
+  view common;
+  gui *g;
+  widget *w;
+  int maxsize;
+  int cursize;
+  float refresh_rate;
+  int autoscroll;
+  pthread_mutex_t lock;
+  list * volatile to_append;
+};
+
+static void _append(struct textlist *this, char *s, int *deleted)
+{
+  if (this->cursize == this->maxsize) {
+    textlist_del_silent(this->g, this->w, 0);
+    this->cursize--;
+    (*deleted)++;
+  }
+  textlist_add_silent(this->g, this->w, s, -1, FOREGROUND_COLOR);
+  this->cursize++;
+}
+
+static void *textlist_thread(void *_this)
+{
+  struct textlist *this = _this;
+  int dirty;
+  int deleted;
+  int visible_lines, start_line, number_of_lines;
+
+  while (1) {
+    if (pthread_mutex_lock(&this->lock)) abort();
+    dirty = this->to_append == NULL ? 0 : 1;
+    deleted = 0;
+    while (this->to_append != NULL) {
+      char *s = this->to_append->data;
+      this->to_append = list_remove_head(this->to_append);
+      _append(this, s, &deleted);
+      free(s);
+    }
+    if (dirty) {
+      textlist_state(this->g, this->w, &visible_lines, &start_line,
+          &number_of_lines);
+      if (this->autoscroll)
+        start_line = number_of_lines - visible_lines;
+      else
+        start_line -= deleted;
+      if (start_line < 0) start_line = 0;
+      textlist_set_start_line(this->g, this->w, start_line);
+      /* this call is not necessary, but if things change in textlist... */
+      widget_dirty(this->g, this->w);
+    }
+    if (pthread_mutex_unlock(&this->lock)) abort();
+    sleepms(1000/this->refresh_rate);
+  }
+
+  return 0;
+}
+
+static void clear(view *this)
+{
+  /* TODO */
+}
+
+static void append(view *_this, char *s)
+{
+  struct textlist *this = (struct textlist *)_this;
+  char *dup;
+
+  if (pthread_mutex_lock(&this->lock)) abort();
+  dup = strdup(s); if (dup == NULL) abort();
+  this->to_append = list_append(this->to_append, dup);
+  if (pthread_mutex_unlock(&this->lock)) abort();
+}
+
+static void scroll(void *private, gui *g,
+    char *notification, widget *w, void *notification_data)
+{
+  struct textlist *this = private;
+  int visible_lines;
+  int start_line;
+  int number_of_lines;
+  int new_line;
+  int inc;
+
+  if (pthread_mutex_lock(&this->lock)) abort();
+
+  textlist_state(g, w, &visible_lines, &start_line, &number_of_lines);
+  inc = 10;
+  if (inc > visible_lines - 2) inc = visible_lines - 2;
+  if (inc < 1) inc = 1;
+  if (!strcmp(notification, "scrollup")) inc = -inc;
+
+  new_line = start_line + inc;
+  if (new_line > number_of_lines - visible_lines)
+    new_line = number_of_lines - visible_lines;
+  if (new_line < 0) new_line = 0;
+
+  textlist_set_start_line(g, w, new_line);
+
+  if (new_line + visible_lines < number_of_lines)
+    this->autoscroll = 0;
+  else
+    this->autoscroll = 1;
+
+  if (pthread_mutex_unlock(&this->lock)) abort();
+}
+
+static void click(void *private, gui *g,
+    char *notification, widget *w, void *notification_data)
+{
+  struct textlist *this = private;
+  int *d = notification_data;
+  int button = d[1];
+
+  if (pthread_mutex_lock(&this->lock)) abort();
+
+  if (button == 1) this->autoscroll = 1 - this->autoscroll;
+
+  if (pthread_mutex_unlock(&this->lock)) abort();
+}
+
+view *new_view_textlist(int maxsize, float refresh_rate, gui *g, widget *w)
+{
+  struct textlist *ret = calloc(1, sizeof(struct textlist));
+  if (ret == NULL) abort();
+
+  ret->common.clear = clear;
+  ret->common.append = (void (*)(view *, ...))append;
+
+  ret->cursize = 0;
+  ret->maxsize = maxsize;
+  ret->refresh_rate = refresh_rate;
+  ret->g = g;
+  ret->w = w;
+  ret->autoscroll = 1;
+
+  if (pthread_mutex_init(&ret->lock, NULL)) abort();
+
+  register_notifier(g, "scrollup", w, scroll, ret);
+  register_notifier(g, "scrolldown", w, scroll, ret);
+  register_notifier(g, "click", w, click, ret);
+
+  new_thread(textlist_thread, ret);
+
+  return (view *)ret;
+}
diff --git a/common/utils/T/tracer/view/ticktime.c b/common/utils/T/tracer/view/ticktime.c
new file mode 100644
index 0000000000000000000000000000000000000000..1e1c7352a07a1acafe1f57681e8925a099fcd6ef
--- /dev/null
+++ b/common/utils/T/tracer/view/ticktime.c
@@ -0,0 +1,411 @@
+#include "view.h"
+#include "../utils.h"
+#include "logger.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <string.h>
+
+/* TODO: some code is identical/almost identical to time.c, merge/factorize */
+
+/****************************************************************************/
+/*                               tick timeview                              */
+/****************************************************************************/
+
+struct plot {
+  struct timespec *tick;
+  int ticksize;
+  int tickmaxsize;
+  int tickstart;
+  int line;
+  int color;
+};
+
+struct ticktime {
+  view common;
+  gui *g;
+  widget *w;
+  float refresh_rate;
+  pthread_mutex_t lock;
+  struct plot *p;
+  int psize;
+  double pixel_length;        /* unit: nanosecond (maximum 1 hour/pixel) */
+  struct timespec latest_time;
+  struct timespec start_time;
+  int autoscroll;
+  struct timespec tick_latest_time;
+  int tick_latest_frame;
+  int tick_latest_subframe;
+  void *clock_ticktime;      /* data for the clock tick, see below */
+};
+
+/* TODO: put that function somewhere else (utils.c) */
+static struct timespec time_add(struct timespec a, struct timespec b)
+{
+  struct timespec ret;
+  ret.tv_sec = a.tv_sec + b.tv_sec;
+  ret.tv_nsec = a.tv_nsec + b.tv_nsec;
+  if (ret.tv_nsec > 1000000000) {
+    ret.tv_sec++;
+    ret.tv_nsec -= 1000000000;
+  }
+  return ret;
+}
+
+/* TODO: put that function somewhere else (utils.c) */
+static struct timespec time_sub(struct timespec a, struct timespec b)
+{
+  struct timespec ret;
+  if (a.tv_nsec < b.tv_nsec) {
+    ret.tv_nsec = (int64_t)a.tv_nsec - (int64_t)b.tv_nsec + 1000000000;
+    ret.tv_sec = a.tv_sec - b.tv_sec - 1;
+  } else {
+    ret.tv_nsec = a.tv_nsec - b.tv_nsec;
+    ret.tv_sec = a.tv_sec - b.tv_sec;
+  }
+  return ret;
+}
+
+/* TODO: put that function somewhere else (utils.c) */
+static struct timespec nano_to_time(int64_t n)
+{
+  struct timespec ret;
+  ret.tv_sec = n / 1000000000;
+  ret.tv_nsec = n % 1000000000;
+  return ret;
+}
+
+/* TODO: put that function somewhere else (utils.c) */
+static int time_cmp(struct timespec a, struct timespec b)
+{
+  if (a.tv_sec < b.tv_sec) return -1;
+  if (a.tv_sec > b.tv_sec) return 1;
+  if (a.tv_nsec < b.tv_nsec) return -1;
+  if (a.tv_nsec > b.tv_nsec) return 1;
+  return 0;
+}
+
+static int interval_empty(struct ticktime *this, int sub,
+    struct timespec start, struct timespec end)
+{
+  int a, b, mid;
+  int i;
+
+  if (this->p[sub].ticksize == 0) return 1;
+
+  /* look for a tick larger than start and smaller than end */
+  a = 0;
+  b = this->p[sub].ticksize - 1;
+  while (b >= a) {
+    mid = (a+b) / 2;
+    i = (this->p[sub].tickstart + mid) % this->p[sub].ticksize;
+    if (time_cmp(this->p[sub].tick[i], start) < 0) a = mid + 1;
+    else if (time_cmp(this->p[sub].tick[i], end) > 0) b = mid - 1;
+    else return 0;
+  }
+  return 1;
+}
+
+static void *ticktime_thread(void *_this)
+{
+  struct ticktime *this = _this;
+  int width;
+  int l;
+  int i;
+  struct timespec tstart;
+  struct timespec tnext;
+  struct plot *p;
+  int64_t pixel_length;
+
+  while (1) {
+    if (pthread_mutex_lock(&this->lock)) abort();
+
+    timeline_get_width(this->g, this->w, &width);
+    timeline_clear_silent(this->g, this->w);
+
+    /* TODO: optimize? */
+
+    /* use rounded pixel_length */
+    pixel_length = this->pixel_length;
+
+    if (this->autoscroll) {
+      tnext = time_add(this->latest_time,
+          (struct timespec){tv_sec:0,tv_nsec:1});
+      tstart = time_sub(tnext, nano_to_time(pixel_length * width));
+      this->start_time = tstart;
+    } else {
+      tstart = this->start_time;
+      tnext = time_add(tstart, nano_to_time(pixel_length * width));
+    }
+
+    for (l = 0; l < this->psize; l++) {
+      for (i = 0; i < width; i++) {
+        struct timespec tick_start, tick_end;
+        tick_start = time_add(tstart, nano_to_time(pixel_length * i));
+        tick_end = time_add(tick_start, nano_to_time(pixel_length-1));
+        if (interval_empty(this, l, tick_start, tick_end))
+          continue;
+        p = &this->p[l];
+        /* TODO: only one call */
+        int x[3] = {i==0?i:i-1, i, i==width-1?i:i+1};
+        timeline_add_points_silent(this->g, this->w, p->line, p->color, x, 3);
+      }
+    }
+
+    widget_dirty(this->g, this->w);
+
+    if (pthread_mutex_unlock(&this->lock)) abort();
+    sleepms(1000 / this->refresh_rate);
+  }
+
+  return 0;
+}
+
+static void scroll(void *private, gui *g,
+    char *notification, widget *w, void *notification_data)
+{
+  struct ticktime *this = private;
+  int *d = notification_data;
+  int x = d[0];
+  int key_modifiers = d[2];
+  double mul = 1.2;
+  double pixel_length;
+  int64_t old_px_len_rounded;
+  struct timespec t;
+  int scroll_px;
+  int width;
+
+  if (pthread_mutex_lock(&this->lock)) abort();
+
+  old_px_len_rounded = this->pixel_length;
+
+  /* scroll if control+wheel, zoom otherwise */
+
+  if (key_modifiers & KEY_CONTROL) {
+    timeline_get_width(this->g, this->w, &width);
+    if (width < 2) width = 2;
+    scroll_px = 100;
+    if (scroll_px > width - 1) scroll_px = width - 1;
+    if (!strcmp(notification, "scrolldown"))
+      this->start_time = time_add(this->start_time,
+          nano_to_time(scroll_px * old_px_len_rounded));
+    else
+      this->start_time = time_sub(this->start_time,
+          nano_to_time(scroll_px * old_px_len_rounded));
+    goto end;
+  }
+
+  if (!strcmp(notification, "scrollup")) mul = 1 / mul;
+
+again:
+  pixel_length = this->pixel_length * mul;
+  if (pixel_length < 1) pixel_length = 1;
+  if (pixel_length > (double)3600 * 1000000000)
+    pixel_length = (double)3600 * 1000000000;
+
+  this->pixel_length = pixel_length;
+
+  /* due to rounding, we may need to zoom by more than 1.2 with
+   * very close lookup, otherwise the user zooming command won't
+   * be visible (say length is 2.7, zoom in, new length is 2.25,
+   * and rounding is 2, same value, no change, no feedback to user => bad)
+   * TODO: make all this cleaner
+   */
+  if (pixel_length != 1 && pixel_length != (double)3600 * 1000000000 &&
+      (int64_t)pixel_length == old_px_len_rounded)
+    goto again;
+
+  t = time_add(this->start_time, nano_to_time(x * old_px_len_rounded));
+  this->start_time = time_sub(t, nano_to_time(x * (int64_t)pixel_length));
+
+end:
+  if (pthread_mutex_unlock(&this->lock)) abort();
+}
+
+static void click(void *private, gui *g,
+    char *notification, widget *w, void *notification_data)
+{
+  struct ticktime *this = private;
+  int *d = notification_data;
+  int button = *d;
+
+  if (button == 3) this->autoscroll = 0;
+  if (button == 1) this->autoscroll = 1;
+}
+
+view *new_view_ticktime(float refresh_rate, gui *g, widget *w)
+{
+  struct ticktime *ret = calloc(1, sizeof(struct ticktime));
+  if (ret == NULL) abort();
+
+  ret->refresh_rate = refresh_rate;
+  ret->g = g;
+  ret->w = w;
+
+  ret->p = NULL;
+  ret->psize = 0;
+
+  ret->autoscroll = 1;
+
+  ret->tick_latest_time.tv_sec = 1;
+
+  /* default pixel length: 10ms */
+  ret->pixel_length = 10 * 1000000;
+
+  register_notifier(g, "scrollup", w, scroll, ret);
+  register_notifier(g, "scrolldown", w, scroll, ret);
+  register_notifier(g, "click", w, click, ret);
+
+  if (pthread_mutex_init(&ret->lock, NULL)) abort();
+
+  new_thread(ticktime_thread, ret);
+
+  return (view *)ret;
+}
+
+/****************************************************************************/
+/*                          subticktimeview                                 */
+/****************************************************************************/
+
+struct subticktime {
+  view common;
+  struct ticktime *parent;
+  int line;
+  int color;
+  int subview;
+};
+
+static void append(view *_this, struct timespec t, int frame, int subframe)
+{
+  struct subticktime *this = (struct subticktime *)_this;
+  struct ticktime    *ticktime = this->parent;
+  struct plot        *p = &ticktime->p[this->subview];
+  int                i;
+  struct timespec    swap;
+  int64_t            diff;
+
+  if (pthread_mutex_lock(&ticktime->lock)) abort();
+
+  /* get time with respect to latest known tick time */
+  diff = (frame*10 + subframe) -
+      (ticktime->tick_latest_frame*10 + ticktime->tick_latest_subframe);
+  if (diff > 1024*10/2) diff -= 1024*10;
+  else if (diff < -1024*10/2) diff += 1024*10;
+  if (diff < 0)
+    t = time_sub(ticktime->tick_latest_time, nano_to_time(-diff * 1000000));
+  else
+    t = time_add(ticktime->tick_latest_time, nano_to_time(diff * 1000000));
+
+  if (p->ticksize < p->tickmaxsize) {
+    p->tick[p->ticksize] = t;
+    p->ticksize++;
+  } else {
+    p->tick[p->tickstart] = t;
+    p->tickstart = (p->tickstart + 1) % p->ticksize;
+  }
+
+  /* due to adjustment of the time, array may not be ordered anymore */
+  for (i = p->ticksize-2; i >= 0; i--) {
+    int prev = (p->tickstart + i) % p->ticksize;
+    int cur = (prev + 1) % p->ticksize;
+    if (time_cmp(p->tick[prev], p->tick[cur]) <= 0) break;
+    swap = p->tick[prev];
+    p->tick[prev] = p->tick[cur];
+    p->tick[cur] = swap;
+  }
+
+  if (time_cmp(ticktime->latest_time, t) < 0)
+    ticktime->latest_time = t;
+
+  if (pthread_mutex_unlock(&ticktime->lock)) abort();
+}
+
+view *new_subview_ticktime(view *_time, int line, int color, int size)
+{
+  struct ticktime *ticktime = (struct ticktime *)_time;
+  struct subticktime *ret = calloc(1, sizeof(struct subticktime));
+  if (ret == NULL) abort();
+
+  ret->common.append = (void (*)(view *, ...))append;
+
+  if (pthread_mutex_lock(&ticktime->lock)) abort();
+
+  ret->parent = ticktime;
+  ret->line = line;
+  ret->color = color;
+  ret->subview = ticktime->psize;
+
+  ticktime->p = realloc(ticktime->p,
+      (ticktime->psize + 1) * sizeof(struct plot));
+  if (ticktime->p == NULL) abort();
+  ticktime->p[ticktime->psize].tick = calloc(size, sizeof(struct timespec));
+  if (ticktime->p[ticktime->psize].tick == NULL) abort();
+  ticktime->p[ticktime->psize].ticksize = 0;
+  ticktime->p[ticktime->psize].tickmaxsize = size;
+  ticktime->p[ticktime->psize].tickstart = 0;
+  ticktime->p[ticktime->psize].line = line;
+  ticktime->p[ticktime->psize].color = color;
+
+  ticktime->psize++;
+
+  if (pthread_mutex_unlock(&ticktime->lock)) abort();
+
+  return (view *)ret;
+}
+
+/****************************************************************************/
+/*                               clock tick                                 */
+/****************************************************************************/
+
+struct clock_ticktime {
+  view common;
+  struct ticktime *parent;
+};
+
+static void clock_append(view *_this, struct timespec t,
+    int frame, int subframe)
+{
+  struct clock_ticktime *this = (struct clock_ticktime *)_this;
+  struct ticktime *tt = this->parent;
+  int64_t diff;
+
+  if (subframe == 10) { subframe = 0; frame = (frame + 1) % 1024; }
+
+  if (pthread_mutex_lock(&tt->lock)) abort();
+
+  /* get time relative to latest known tick time */
+  /* In normal conditions diff is 1 but if the user pauses reception of events
+   * it may be anything. Let's take only positive values.
+   */
+  diff = (frame*10 + subframe) -
+      (tt->tick_latest_frame*10 + tt->tick_latest_subframe);
+  if (diff < 0) diff += 1024*10;
+  tt->tick_latest_time = time_add(tt->tick_latest_time,
+      nano_to_time(diff * 1000000));
+  tt->tick_latest_frame = frame;
+  tt->tick_latest_subframe = subframe;
+
+  if (time_cmp(tt->latest_time, tt->tick_latest_time) < 0)
+    tt->latest_time = tt->tick_latest_time;
+
+  if (pthread_mutex_unlock(&tt->lock)) abort();
+}
+
+void ticktime_set_tick(view *_ticktime, void *logger)
+{
+  struct ticktime *ticktime = (struct ticktime *)_ticktime;
+  struct clock_ticktime *n;
+
+  if (pthread_mutex_lock(&ticktime->lock)) abort();
+
+  free(ticktime->clock_ticktime);
+  n = ticktime->clock_ticktime = calloc(1, sizeof(struct clock_ticktime));
+  if (n == NULL) abort();
+
+  n->common.append = (void (*)(view *, ...))clock_append;
+  n->parent = ticktime;
+
+  logger_add_view(logger, (view *)n);
+
+  if (pthread_mutex_unlock(&ticktime->lock)) abort();
+}
diff --git a/common/utils/T/tracer/view/time.c b/common/utils/T/tracer/view/time.c
new file mode 100644
index 0000000000000000000000000000000000000000..287e7b1c7be029af0a815febb271938d888857a4
--- /dev/null
+++ b/common/utils/T/tracer/view/time.c
@@ -0,0 +1,322 @@
+#include "view.h"
+#include "../utils.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <string.h>
+
+/****************************************************************************/
+/*                              timeview                                    */
+/****************************************************************************/
+
+struct plot {
+  struct timespec *tick;
+  int ticksize;
+  int tickmaxsize;
+  int tickstart;
+  int line;
+  int color;
+};
+
+struct time {
+  view common;
+  gui *g;
+  widget *w;
+  float refresh_rate;
+  pthread_mutex_t lock;
+  struct plot *p;
+  int psize;
+  double pixel_length;        /* unit: nanosecond (maximum 1 hour/pixel) */
+  struct timespec latest_time;
+  struct timespec start_time;
+  int autoscroll;
+};
+
+/* TODO: put that function somewhere else (utils.c) */
+static struct timespec time_add(struct timespec a, struct timespec b)
+{
+  struct timespec ret;
+  ret.tv_sec = a.tv_sec + b.tv_sec;
+  ret.tv_nsec = a.tv_nsec + b.tv_nsec;
+  if (ret.tv_nsec > 1000000000) {
+    ret.tv_sec++;
+    ret.tv_nsec -= 1000000000;
+  }
+  return ret;
+}
+
+/* TODO: put that function somewhere else (utils.c) */
+static struct timespec time_sub(struct timespec a, struct timespec b)
+{
+  struct timespec ret;
+  if (a.tv_nsec < b.tv_nsec) {
+    ret.tv_nsec = (int64_t)a.tv_nsec - (int64_t)b.tv_nsec + 1000000000;
+    ret.tv_sec = a.tv_sec - b.tv_sec - 1;
+  } else {
+    ret.tv_nsec = a.tv_nsec - b.tv_nsec;
+    ret.tv_sec = a.tv_sec - b.tv_sec;
+  }
+  return ret;
+}
+
+/* TODO: put that function somewhere else (utils.c) */
+static struct timespec nano_to_time(int64_t n)
+{
+  struct timespec ret;
+  ret.tv_sec = n / 1000000000;
+  ret.tv_nsec = n % 1000000000;
+  return ret;
+}
+
+/* TODO: put that function somewhere else (utils.c) */
+static int time_cmp(struct timespec a, struct timespec b)
+{
+  if (a.tv_sec < b.tv_sec) return -1;
+  if (a.tv_sec > b.tv_sec) return 1;
+  if (a.tv_nsec < b.tv_nsec) return -1;
+  if (a.tv_nsec > b.tv_nsec) return 1;
+  return 0;
+}
+
+static int interval_empty(struct time *this, int sub,
+    struct timespec start, struct timespec end)
+{
+  int a, b, mid;
+  int i;
+
+  if (this->p[sub].ticksize == 0) return 1;
+
+  /* look for a tick larger than start and smaller than end */
+  a = 0;
+  b = this->p[sub].ticksize - 1;
+  while (b >= a) {
+    mid = (a+b) / 2;
+    i = (this->p[sub].tickstart + mid) % this->p[sub].ticksize;
+    if (time_cmp(this->p[sub].tick[i], start) < 0) a = mid + 1;
+    else if (time_cmp(this->p[sub].tick[i], end) > 0) b = mid - 1;
+    else return 0;
+  }
+  return 1;
+}
+
+static void *time_thread(void *_this)
+{
+  struct time *this = _this;
+  int width;
+  int l;
+  int i;
+  struct timespec tstart;
+  struct timespec tnext;
+  struct plot *p;
+  int64_t pixel_length;
+
+  while (1) {
+    if (pthread_mutex_lock(&this->lock)) abort();
+
+    timeline_get_width(this->g, this->w, &width);
+    timeline_clear_silent(this->g, this->w);
+
+    /* TODO: optimize? */
+
+    /* use rounded pixel_length */
+    pixel_length = this->pixel_length;
+
+    if (this->autoscroll) {
+      tnext = time_add(this->latest_time,
+          (struct timespec){tv_sec:0,tv_nsec:1});
+      tstart = time_sub(tnext, nano_to_time(pixel_length * width));
+      this->start_time = tstart;
+    } else {
+      tstart = this->start_time;
+      tnext = time_add(tstart, nano_to_time(pixel_length * width));
+    }
+
+    for (l = 0; l < this->psize; l++) {
+      for (i = 0; i < width; i++) {
+        struct timespec tick_start, tick_end;
+        tick_start = time_add(tstart, nano_to_time(pixel_length * i));
+        tick_end = time_add(tick_start, nano_to_time(pixel_length-1));
+        if (interval_empty(this, l, tick_start, tick_end))
+          continue;
+        p = &this->p[l];
+        /* TODO: only one call */
+        timeline_add_points_silent(this->g, this->w, p->line, p->color, &i, 1);
+      }
+    }
+
+    widget_dirty(this->g, this->w);
+
+    if (pthread_mutex_unlock(&this->lock)) abort();
+    sleepms(1000 / this->refresh_rate);
+  }
+
+  return 0;
+}
+
+static void scroll(void *private, gui *g,
+    char *notification, widget *w, void *notification_data)
+{
+  struct time *this = private;
+  int *d = notification_data;
+  int x = d[0];
+  int key_modifiers = d[2];
+  double mul = 1.2;
+  double pixel_length;
+  int64_t old_px_len_rounded;
+  struct timespec t;
+  int scroll_px;
+  int width;
+
+  if (pthread_mutex_lock(&this->lock)) abort();
+
+  old_px_len_rounded = this->pixel_length;
+
+  /* scroll if control+wheel, zoom otherwise */
+
+  if (key_modifiers & KEY_CONTROL) {
+    timeline_get_width(this->g, this->w, &width);
+    if (width < 2) width = 2;
+    scroll_px = 100;
+    if (scroll_px > width - 1) scroll_px = width - 1;
+    if (!strcmp(notification, "scrolldown"))
+      this->start_time = time_add(this->start_time,
+          nano_to_time(scroll_px * old_px_len_rounded));
+    else
+      this->start_time = time_sub(this->start_time,
+          nano_to_time(scroll_px * old_px_len_rounded));
+    goto end;
+  }
+
+  if (!strcmp(notification, "scrollup")) mul = 1 / mul;
+
+again:
+  pixel_length = this->pixel_length * mul;
+  if (pixel_length < 1) pixel_length = 1;
+  if (pixel_length > (double)3600 * 1000000000)
+    pixel_length = (double)3600 * 1000000000;
+
+  this->pixel_length = pixel_length;
+
+  /* due to rounding, we may need to zoom by more than 1.2 with
+   * very close lookup, otherwise the user zooming command won't
+   * be visible (say length is 2.7, zoom in, new length is 2.25,
+   * and rounding is 2, same value, no change, no feedback to user => bad)
+   * TODO: make all this cleaner
+   */
+  if (pixel_length != 1 && pixel_length != (double)3600 * 1000000000 &&
+      (int64_t)pixel_length == old_px_len_rounded)
+    goto again;
+
+  t = time_add(this->start_time, nano_to_time(x * old_px_len_rounded));
+  this->start_time = time_sub(t, nano_to_time(x * (int64_t)pixel_length));
+
+end:
+  if (pthread_mutex_unlock(&this->lock)) abort();
+}
+
+static void click(void *private, gui *g,
+    char *notification, widget *w, void *notification_data)
+{
+  struct time *this = private;
+  int *d = notification_data;
+  int button = *d;
+
+  if (button == 3) this->autoscroll = 0;
+  if (button == 1) this->autoscroll = 1;
+}
+
+view *new_view_time(int number_of_seconds, float refresh_rate,
+    gui *g, widget *w)
+{
+  struct time *ret = calloc(1, sizeof(struct time));
+  if (ret == NULL) abort();
+
+  ret->refresh_rate = refresh_rate;
+  ret->g = g;
+  ret->w = w;
+
+  ret->p = NULL;
+  ret->psize = 0;
+
+  ret->autoscroll = 1;
+
+  /* default pixel length: 10ms */
+  ret->pixel_length = 10 * 1000000;
+
+  register_notifier(g, "scrollup", w, scroll, ret);
+  register_notifier(g, "scrolldown", w, scroll, ret);
+  register_notifier(g, "click", w, click, ret);
+
+  if (pthread_mutex_init(&ret->lock, NULL)) abort();
+
+  new_thread(time_thread, ret);
+
+  return (view *)ret;
+}
+
+/****************************************************************************/
+/*                            subtimeview                                   */
+/****************************************************************************/
+
+struct subtime {
+  view common;
+  struct time *parent;
+  int line;
+  int color;
+  int subview;
+};
+
+static void append(view *_this, struct timespec t)
+{
+  struct subtime *this = (struct subtime *)_this;
+  struct time    *time = this->parent;
+  struct plot    *p = &time->p[this->subview];
+
+  if (pthread_mutex_lock(&time->lock)) abort();
+
+  if (p->ticksize < p->tickmaxsize) {
+    p->tick[p->ticksize] = t;
+    p->ticksize++;
+  } else {
+    p->tick[p->tickstart] = t;
+    p->tickstart = (p->tickstart + 1) % p->ticksize;
+  }
+
+  if (time_cmp(time->latest_time, t) < 0)
+    time->latest_time = t;
+
+  if (pthread_mutex_unlock(&time->lock)) abort();
+}
+
+view *new_subview_time(view *_time, int line, int color, int size)
+{
+  struct time *time = (struct time *)_time;
+  struct subtime *ret = calloc(1, sizeof(struct subtime));
+  if (ret == NULL) abort();
+
+  ret->common.append = (void (*)(view *, ...))append;
+
+  if (pthread_mutex_lock(&time->lock)) abort();
+
+  ret->parent = time;
+  ret->line = line;
+  ret->color = color;
+  ret->subview = time->psize;
+
+  time->p = realloc(time->p,
+      (time->psize + 1) * sizeof(struct plot));
+  if (time->p == NULL) abort();
+  time->p[time->psize].tick = calloc(size, sizeof(struct timespec));
+  if (time->p[time->psize].tick == NULL) abort();
+  time->p[time->psize].ticksize = 0;
+  time->p[time->psize].tickmaxsize = size;
+  time->p[time->psize].tickstart = 0;
+  time->p[time->psize].line = line;
+  time->p[time->psize].color = color;
+
+  time->psize++;
+
+  if (pthread_mutex_unlock(&time->lock)) abort();
+
+  return (view *)ret;
+}
diff --git a/common/utils/T/tracer/view/tti.c b/common/utils/T/tracer/view/tti.c
new file mode 100644
index 0000000000000000000000000000000000000000..ebf9fc657cb5f1cf581d93dcb7553254e9dfbf66
--- /dev/null
+++ b/common/utils/T/tracer/view/tti.c
@@ -0,0 +1,121 @@
+#include "view.h"
+#include "../utils.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <stdarg.h>
+#include <string.h>
+
+struct tti {
+  view common;
+  gui *g;
+  widget *w;
+  int plot;
+  float refresh_rate;
+  pthread_mutex_t lock;
+  float data[1024*10];
+  int valid[1024*10];
+  float xout[1024*10];
+  float yout[1024*10];
+  int last_insert_point;
+};
+
+static int far_enough(int i, int last_insert, int plot_width)
+{
+  int p1;
+  int p2;
+  int hole_size_px;
+  int hole_size_tti;
+  hole_size_px = 10;
+  hole_size_tti = 10240 * hole_size_px / plot_width;
+  p1 = last_insert;
+  p2 = (last_insert + hole_size_tti) % (1024*10);
+  if (p1 < p2) {
+    return !(i > p1 && i < p2);
+  }
+  return i > p2 && i <= p1;
+}
+
+static void *tti_thread(void *_this)
+{
+  struct tti *this = _this;
+  int i;
+  int length;
+  int plot_width;
+  int plot_height;
+
+  while (1) {
+    if (pthread_mutex_lock(&this->lock)) abort();
+    xy_plot_get_dimensions(this->g, this->w, &plot_width, &plot_height);
+    length = 0;
+    /* TODO: optimize */
+    for (i = 0; i < 1024*10; i++)
+      /* do not take points too close after last insertion point */
+      if (this->valid[i] &&
+          far_enough(i, this->last_insert_point, plot_width)) {
+        this->xout[length] = i;
+        this->yout[length] = this->data[i];
+        length++;
+      }
+    xy_plot_set_points(this->g, this->w, this->plot,
+        length, this->xout, this->yout);
+    if (pthread_mutex_unlock(&this->lock)) abort();
+    sleepms(1000/this->refresh_rate);
+  }
+
+  return 0;
+}
+
+static void clear(view *this)
+{
+  /* TODO */
+}
+
+static void append(view *_this, int frame, int subframe, double value)
+{
+  struct tti *this = (struct tti *)_this;
+  int i;
+  int index = frame * 10 + subframe;
+
+  if (pthread_mutex_lock(&this->lock)) abort();
+
+  /* TODO: optimize */
+  /* clear all between last insert point and current one
+   * this may be wrong if delay between two append is
+   * greater than 1024 frames (something like that)
+   */
+  i = (this->last_insert_point + 1) % (1024*10);
+  while (i != index) {
+    this->valid[i] = 0;
+    i = (i + 1) % (1024*10);
+  }
+
+  this->data[index] = value;
+  this->valid[index] = 1;
+
+  this->last_insert_point = index;
+
+  if (pthread_mutex_unlock(&this->lock)) abort();
+}
+
+view *new_view_tti(float refresh_rate, gui *g, widget *w, int color)
+{
+  struct tti *ret = calloc(1, sizeof(struct tti));
+  if (ret == NULL) abort();
+
+  ret->common.clear = clear;
+  ret->common.append = (void (*)(view *, ...))append;
+
+  ret->refresh_rate = refresh_rate;
+  ret->g = g;
+  ret->w = w;
+  ret->plot = xy_plot_new_plot(g, w, color);
+
+  ret->last_insert_point = 0;
+
+  if (pthread_mutex_init(&ret->lock, NULL)) abort();
+
+  new_thread(tti_thread, ret);
+
+  return (view *)ret;
+}
diff --git a/common/utils/T/tracer/view/view.h b/common/utils/T/tracer/view/view.h
new file mode 100644
index 0000000000000000000000000000000000000000..cbc5e21b73d95fb3b15524977663a6054846fc5f
--- /dev/null
+++ b/common/utils/T/tracer/view/view.h
@@ -0,0 +1,27 @@
+#ifndef _VIEW_H_
+#define _VIEW_H_
+
+#include "gui/gui.h"
+
+/* defines the public API of views */
+
+typedef struct view {
+  void (*clear)(struct view *this);
+  void (*append)(struct view *this, ...);
+  void (*set)(struct view *this, char *name, ...);
+} view;
+
+view *new_view_stdout(void);
+view *new_view_textlist(int maxsize, float refresh_rate, gui *g, widget *w);
+view *new_view_xy(int length, float refresh_rate, gui *g, widget *w,
+    int color);
+view *new_view_tti(float refresh_rate, gui *g, widget *w,
+    int color);
+view *new_view_time(int number_of_seconds, float refresh_rate,
+    gui *g, widget *w);
+view *new_subview_time(view *time, int line, int color, int size);
+view *new_view_ticktime(float refresh_rate, gui *g, widget *w);
+view *new_subview_ticktime(view *ticktime, int line, int color, int size);
+void ticktime_set_tick(view *ticktime, void *logger);
+
+#endif /* _VIEW_H_ */
diff --git a/common/utils/T/tracer/view/xy.c b/common/utils/T/tracer/view/xy.c
new file mode 100644
index 0000000000000000000000000000000000000000..1407a16a0e44614c8e86b05569712d738439cfc2
--- /dev/null
+++ b/common/utils/T/tracer/view/xy.c
@@ -0,0 +1,116 @@
+#include "view.h"
+#include "../utils.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <stdarg.h>
+#include <string.h>
+
+struct xy {
+  view common;
+  gui *g;
+  widget *w;
+  int plot;
+  float refresh_rate;
+  pthread_mutex_t lock;
+  int length;
+  float *x;
+  float *y;
+  int insert_point;
+};
+
+static void *xy_thread(void *_this)
+{
+  struct xy *this = _this;
+
+  while (1) {
+    if (pthread_mutex_lock(&this->lock)) abort();
+    xy_plot_set_points(this->g, this->w, this->plot,
+        this->length, this->x, this->y);
+    if (pthread_mutex_unlock(&this->lock)) abort();
+    sleepms(1000/this->refresh_rate);
+  }
+
+  return 0;
+}
+
+static void clear(view *this)
+{
+  /* TODO */
+}
+
+static void append(view *_this, float *x, float *y, int length)
+{
+  struct xy *this = (struct xy *)_this;
+  int i;
+  int ip;
+
+  if (pthread_mutex_lock(&this->lock)) abort();
+
+  ip = this->insert_point;
+
+  /* TODO: optimize the copy */
+  for (i = 0; i < length; i++) {
+    this->x[ip] = x[i];
+    this->y[ip] = y[i];
+    ip++; if (ip == this->length) ip = 0;
+  }
+
+  this->insert_point = ip;
+
+  if (pthread_mutex_unlock(&this->lock)) abort();
+}
+
+static void set(view *_this, char *name, ...)
+{
+  struct xy *this = (struct xy *)_this;
+  va_list ap;
+
+  if (!strcmp(name, "length")) {
+    if (pthread_mutex_lock(&this->lock)) abort();
+
+    va_start(ap, name);
+
+    free(this->x);
+    free(this->y);
+    this->length = va_arg(ap, int);
+    this->x = calloc(this->length, sizeof(float)); if (this->x==NULL)abort();
+    this->y = calloc(this->length, sizeof(float)); if (this->y==NULL)abort();
+    this->insert_point = 0;
+
+    va_end(ap);
+
+    if (pthread_mutex_unlock(&this->lock)) abort();
+    return;
+  }
+
+  printf("%s:%d: unkown setting '%s'\n", __FILE__, __LINE__, name);
+  abort();
+}
+
+view *new_view_xy(int length, float refresh_rate, gui *g, widget *w,
+    int color)
+{
+  struct xy *ret = calloc(1, sizeof(struct xy));
+  if (ret == NULL) abort();
+
+  ret->common.clear = clear;
+  ret->common.append = (void (*)(view *, ...))append;
+  ret->common.set = set;
+
+  ret->refresh_rate = refresh_rate;
+  ret->g = g;
+  ret->w = w;
+  ret->plot = xy_plot_new_plot(g, w, color);
+
+  ret->length = length;
+  ret->x = calloc(length, sizeof(float)); if (ret->x == NULL) abort();
+  ret->y = calloc(length, sizeof(float)); if (ret->y == NULL) abort();
+  ret->insert_point = 0;
+
+  if (pthread_mutex_init(&ret->lock, NULL)) abort();
+
+  new_thread(xy_thread, ret);
+
+  return (view *)ret;
+}
diff --git a/common/utils/itti/intertask_interface.c b/common/utils/itti/intertask_interface.c
index 572c207b3fc7e639671a584b3b6c11241b521ca6..6789dadce60629b14ea493b9148ba2e6882ddd4e 100644
--- a/common/utils/itti/intertask_interface.c
+++ b/common/utils/itti/intertask_interface.c
@@ -58,6 +58,10 @@
 # include "vcd_signal_dumper.h"
 #endif
 
+#if T_TRACER
+#include "T.h"
+#endif
+
 /* Includes "intertask_interface_init.h" to check prototype coherence, but
  * disable threads and messages information generation.
  */
diff --git a/common/utils/itti/intertask_interface_dump.c b/common/utils/itti/intertask_interface_dump.c
index 491bff59b0fcc5aab29116be171daa710504c365..30d92e3a292595ff036cff3fc869ec23aec01081 100644
--- a/common/utils/itti/intertask_interface_dump.c
+++ b/common/utils/itti/intertask_interface_dump.c
@@ -64,6 +64,10 @@
 #include "vcd_signal_dumper.h"
 #endif
 
+#if T_TRACER
+#include "T.h"
+#endif
+
 static const int itti_dump_debug = 0; // 0x8 | 0x4 | 0x2;
 
 #ifdef RTAI
diff --git a/common/utils/itti/memory_pools.c b/common/utils/itti/memory_pools.c
index 97ec7d8e3886823b5458db6ccaff071400c68777..a7d3448352e569423ad56b9c4dc3bb4c992c7c45 100644
--- a/common/utils/itti/memory_pools.c
+++ b/common/utils/itti/memory_pools.c
@@ -37,6 +37,11 @@
 # include "vcd_signal_dumper.h"
 #endif
 
+#if T_TRACER
+#include <string.h>
+#include "T.h"
+#endif
+
 /*------------------------------------------------------------------------------*/
 const static int mp_debug = 0;
 
diff --git a/openair1/PHY/LTE_ESTIMATION/lte_ul_channel_estimation.c b/openair1/PHY/LTE_ESTIMATION/lte_ul_channel_estimation.c
index 1eb663ec89e844a2adc171ecdc818e912a97d81c..2bfa6e3f60fbb3997c080e2f27da0acd871011db 100644
--- a/openair1/PHY/LTE_ESTIMATION/lte_ul_channel_estimation.c
+++ b/openair1/PHY/LTE_ESTIMATION/lte_ul_channel_estimation.c
@@ -31,6 +31,7 @@
 #include "PHY/sse_intrin.h"
 //#define DEBUG_CH
 
+#include "T.h"
 
 // For Channel Estimation in Distributed Alamouti Scheme
 //static int16_t temp_out_ifft[2048*4] __attribute__((aligned(16)));
@@ -331,6 +332,13 @@ int32_t lte_ul_channel_estimation(PHY_VARS_eNB *phy_vars_eNB,
 	break;
       }
 
+#if T_TRACER
+      if (aa == 0)
+        T(T_ENB_UL_CHANNEL_ESTIMATE, T_INT(eNB_id), T_INT(UE_id),
+          T_INT(phy_vars_eNB->proc[sched_subframe].frame_rx), T_INT(subframe),
+          T_INT(0), T_BUFFER(ul_ch_estimates_time[0], 512  * 4));
+#endif
+
 #ifdef DEBUG_CH
 
       if (aa==0) {
diff --git a/openair1/PHY/LTE_TRANSPORT/phich.c b/openair1/PHY/LTE_TRANSPORT/phich.c
index 8b3a6c875884ae2c617af166dc7a6b09d502d1fc..c9ce0cbb9a2524e3a5ea21be497e07f192a11ba8 100644
--- a/openair1/PHY/LTE_TRANSPORT/phich.c
+++ b/openair1/PHY/LTE_TRANSPORT/phich.c
@@ -46,6 +46,8 @@
 #include "ARCH/CBMIMO1/DEVICE_DRIVER/extern.h"
 #endif
 
+#include "T.h"
+
 //#define DEBUG_PHICH 1
 
 //extern unsigned short pcfich_reg[4];
@@ -1504,6 +1506,8 @@ void generate_phich_top(PHY_VARS_eNB *phy_vars_eNB,
         if ((ulsch_eNB[UE_id]->harq_processes[harq_pid]->dci_alloc == 0) &&
             (ulsch_eNB[UE_id]->harq_processes[harq_pid]->rar_alloc == 0) ) {
           if (ulsch_eNB[UE_id]->harq_processes[harq_pid]->phich_ACK==0 ) {
+            T(T_ENB_ULSCH_UE_NO_DCI_RETRANSMISSION, T_INT(phy_vars_eNB->Mod_id), T_INT(phy_vars_eNB->proc[sched_subframe].frame_tx),
+              T_INT(subframe), T_INT(UE_id), T_INT(ulsch_eNB[UE_id]->rnti), T_INT(harq_pid));
             LOG_D(PHY,"[eNB %d][PUSCH %d] frame %d, subframe %d : PHICH NACK / (no format0 DCI) Setting subframe_scheduling_flag\n",
                   phy_vars_eNB->Mod_id,harq_pid,phy_vars_eNB->proc[sched_subframe].frame_tx,subframe);
             ulsch_eNB[UE_id]->harq_processes[harq_pid]->subframe_scheduling_flag = 1;
diff --git a/openair1/PHY/LTE_TRANSPORT/proto.h b/openair1/PHY/LTE_TRANSPORT/proto.h
index 69de638672dd5144771372fd3873c4f9e4936e05..411c058c3e26e1f94bbc4786e196663e20c426c5 100644
--- a/openair1/PHY/LTE_TRANSPORT/proto.h
+++ b/openair1/PHY/LTE_TRANSPORT/proto.h
@@ -1665,6 +1665,7 @@ uint32_t rx_pucch(PHY_VARS_eNB *phy_vars_eNB,
 		  uint16_t n2_pucch,
 		  uint8_t shortened_format,
 		  uint8_t *payload,
+		  int     frame,
 		  uint8_t subframe,
 		  uint8_t pucch1_thres);
 
diff --git a/openair1/PHY/LTE_TRANSPORT/pucch.c b/openair1/PHY/LTE_TRANSPORT/pucch.c
index e723eb67a48c096ca079e16362b11473007cfd78..9dfc1bb358c3afb7fc4715ba9127ba3e5b6b8a46 100644
--- a/openair1/PHY/LTE_TRANSPORT/pucch.c
+++ b/openair1/PHY/LTE_TRANSPORT/pucch.c
@@ -44,6 +44,8 @@
 #include "UTIL/LOG/log.h"
 #include "UTIL/LOG/vcd_signal_dumper.h"
 
+#include "T.h"
+
 //uint8_t ncs_cell[20][7];
 //#define DEBUG_PUCCH_TX
 //#define DEBUG_PUCCH_RX
@@ -440,6 +442,7 @@ uint32_t rx_pucch(PHY_VARS_eNB *phy_vars_eNB,
 		  uint16_t n2_pucch,
 		  uint8_t shortened_format,
 		  uint8_t *payload,
+		  int     frame,
 		  uint8_t subframe,
 		  uint8_t pucch1_thres)
 {
@@ -796,6 +799,9 @@ uint32_t rx_pucch(PHY_VARS_eNB *phy_vars_eNB,
     phy_vars_eNB->pucch1_stats_thres[UE_id][(subframe<<10)+phy_vars_eNB->pucch1_stats_cnt[UE_id][subframe]] = sigma2_dB+pucch1_thres;
     phy_vars_eNB->pucch1_stats_cnt[UE_id][subframe] = (phy_vars_eNB->pucch1_stats_cnt[UE_id][subframe]+1)&1023;
 
+    T(T_PUCCH_1_ENERGY, T_INT(phy_vars_eNB->Mod_id), T_INT(UE_id), T_INT(frame), T_INT(subframe),
+      T_INT(stat_max), T_INT(sigma2_dB+pucch1_thres));
+
     /*
     if (phy_vars_eNB->pucch1_stats_cnt[UE_id][subframe] == 0) {
       write_output("pucch_debug.m","pucch_energy",
@@ -1037,6 +1043,8 @@ uint32_t rx_pucch(PHY_VARS_eNB *phy_vars_eNB,
 	phy_vars_eNB->pucch1ab_stats[UE_id][(subframe<<11) + 1+2*(phy_vars_eNB->pucch1ab_stats_cnt[UE_id][subframe])] = (stat_im);
 	phy_vars_eNB->pucch1ab_stats_cnt[UE_id][subframe] = (phy_vars_eNB->pucch1ab_stats_cnt[UE_id][subframe]+1)&1023;
 
+      /* frame not available here - set to -1 for the moment */
+      T(T_PUCCH_1AB_IQ, T_INT(phy_vars_eNB->Mod_id), T_INT(UE_id), T_INT(-1), T_INT(subframe), T_INT(stat_re), T_INT(stat_im));
 
 	  
       *payload = (stat_re<0) ? 1 : 0;
diff --git a/openair1/PHY/LTE_TRANSPORT/ulsch_demodulation.c b/openair1/PHY/LTE_TRANSPORT/ulsch_demodulation.c
index e52cdf9567acf17fef5beadb45f031ddd8b06c60..cc79488e37dd9a22166d8fdbb2f76ec65817050b 100644
--- a/openair1/PHY/LTE_TRANSPORT/ulsch_demodulation.c
+++ b/openair1/PHY/LTE_TRANSPORT/ulsch_demodulation.c
@@ -45,6 +45,8 @@
 //#define DEBUG_ULSCH
 #include "PHY/sse_intrin.h"
 
+#include "T.h"
+
 //extern char* namepointer_chMag ;
 //eren
 //extern int **ulchmag_eren;
@@ -1615,7 +1617,7 @@ void rx_ulsch(PHY_VARS_eNB *phy_vars_eNB,
   rx_power_correction = 1;
 
   if (ulsch[UE_id]->harq_processes[harq_pid]->nb_rb == 0) {
-    LOG_E(PHY,"PUSCH (%d/%x) nb_rb=0!\n", harq_pid,ulsch[UE_id]->rnti,harq_pid);
+    LOG_E(PHY,"PUSCH (%d/%x) nb_rb=0!\n", harq_pid,ulsch[UE_id]->rnti);
     return;
   }
 
@@ -1838,6 +1840,11 @@ void rx_ulsch(PHY_VARS_eNB *phy_vars_eNB,
 #endif
 
 
+  T(T_PUSCH_IQ, T_INT(eNB_id), T_INT(UE_id), T_INT(phy_vars_eNB->proc[sched_subframe].frame_rx),
+    T_INT(subframe), T_INT(ulsch[UE_id]->harq_processes[harq_pid]->nb_rb),
+    T_BUFFER(eNB_pusch_vars->rxdataF_comp[eNB_id][0],
+             2 * /* ulsch[UE_id]->harq_processes[harq_pid]->nb_rb */ frame_parms->N_RB_UL *12*frame_parms->symbols_per_tti*2));
+
   llrp = (int16_t*)&eNB_pusch_vars->llr[0];
 
   for (l=0; l<frame_parms->symbols_per_tti-ulsch[UE_id]->harq_processes[harq_pid]->srs_active; l++) {
diff --git a/openair1/SCHED/phy_procedures_lte_eNb.c b/openair1/SCHED/phy_procedures_lte_eNb.c
index 5b38d495ec13f94ccbb35c5e4e957bb363e3f106..5979b1ce66203d1cc20011b95fa697c4a17333f0 100755
--- a/openair1/SCHED/phy_procedures_lte_eNb.c
+++ b/openair1/SCHED/phy_procedures_lte_eNb.c
@@ -56,6 +56,8 @@
 #include "UTIL/LOG/log.h"
 #include "UTIL/LOG/vcd_signal_dumper.h"
 
+#include "T.h"
+
 #include "assertions.h"
 #include "msc.h"
 
@@ -596,6 +598,8 @@ void phy_procedures_eNB_TX(unsigned char sched_subframe,PHY_VARS_eNB *phy_vars_e
   VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_ENB_TX,1);
   start_meas(&phy_vars_eNB->phy_proc_tx);
 
+  T(T_ENB_DL_TICK, T_INT(phy_vars_eNB->Mod_id), T_INT(frame), T_INT(subframe));
+
   for (i=0; i<NUMBER_OF_UE_MAX; i++) {
     // If we've dropped the UE, go back to PRACH mode for this UE
 
@@ -1093,6 +1097,10 @@ void phy_procedures_eNB_TX(unsigned char sched_subframe,PHY_VARS_eNB *phy_vars_e
               phy_vars_eNB->Mod_id,DCI_pdu->dci_alloc[i].rnti,phy_vars_eNB->dlsch_eNB[(uint8_t)UE_id][0]->current_harq_pid,phy_vars_eNB->proc[sched_subframe].frame_tx,subframe);
 
 
+        T(T_ENB_DLSCH_UE_DCI, T_INT(phy_vars_eNB->Mod_id), T_INT(frame), T_INT(subframe), T_INT(UE_id),
+          T_INT(DCI_pdu->dci_alloc[i].rnti), T_INT(DCI_pdu->dci_alloc[i].format),
+          T_INT(phy_vars_eNB->dlsch_eNB[(int)UE_id][0]->current_harq_pid));
+
         phy_vars_eNB->dlsch_eNB[(uint8_t)UE_id][0]->nCCE[subframe] = DCI_pdu->dci_alloc[i].firstCCE;
 
 	LOG_D(PHY,"[eNB %"PRIu8"] Frame %d subframe %d : CCE resource for ue DCI (PDSCH %"PRIx16")  => %"PRIu8"/%u\n",phy_vars_eNB->Mod_id,phy_vars_eNB->proc[sched_subframe].frame_tx,subframe,
@@ -1143,6 +1151,9 @@ void phy_procedures_eNB_TX(unsigned char sched_subframe,PHY_VARS_eNB *phy_vars_e
       else
 	UE_id = i;
 
+      T(T_ENB_ULSCH_UE_DCI, T_INT(phy_vars_eNB->Mod_id), T_INT(frame), T_INT(subframe), T_INT(UE_id),
+        T_INT(DCI_pdu->dci_alloc[i].rnti), T_INT(harq_pid));
+
       if (UE_id<0) {
         LOG_E(PHY,"[eNB %"PRIu8"] Frame %d: Unknown UE_id for rnti %"PRIx16"\n",phy_vars_eNB->Mod_id,phy_vars_eNB->proc[sched_subframe].frame_tx,DCI_pdu->dci_alloc[i].rnti);
         mac_exit_wrapper("Invalid UE id (< 0) detected");
@@ -1867,6 +1878,9 @@ void process_HARQ_feedback(uint8_t UE_id,
                   dlsch->rnti,dl_harq_pid[m],M,m,mp,dlsch_harq_proc->round);
 #endif
 
+            T(T_ENB_DLSCH_UE_NACK, T_INT(phy_vars_eNB->Mod_id), T_INT(frame), T_INT(subframe), T_INT(UE_id), T_INT(dlsch->rnti),
+              T_INT(dl_harq_pid[m]));
+
             if (dlsch_harq_proc->round == 0)
               ue_stats->dlsch_NAK_round0++;
 
@@ -1900,6 +1914,10 @@ void process_HARQ_feedback(uint8_t UE_id,
             LOG_D(PHY,"[eNB %d][PDSCH %x/%d] ACK Received in round %d, resetting process\n",phy_vars_eNB->Mod_id,
                   dlsch->rnti,dl_harq_pid[m],dlsch_harq_proc->round);
 #endif
+
+            T(T_ENB_DLSCH_UE_ACK, T_INT(phy_vars_eNB->Mod_id), T_INT(frame), T_INT(subframe), T_INT(UE_id), T_INT(dlsch->rnti),
+              T_INT(dl_harq_pid[m]));
+
             ue_stats->dlsch_ACK[dl_harq_pid[m]][dlsch_harq_proc->round]++;
 
             // Received ACK so set round to 0 and set dlsch_harq_pid IDLE
@@ -2303,6 +2321,7 @@ void pucch_procedures(const unsigned char sched_subframe,PHY_VARS_eNB *phy_vars_
 				  0, // n2_pucch
 				  0, // shortened format, should be use_srs flag, later
 				  &SR_payload,
+                                  frame,
 				  subframe,
 				  PUCCH1_THRES);
 
@@ -2364,6 +2383,7 @@ void pucch_procedures(const unsigned char sched_subframe,PHY_VARS_eNB *phy_vars_
                                0, //n2_pucch
                                0, // shortened format
                                pucch_payload0,
+                               frame,
                                subframe,
                                PUCCH1a_THRES);
 
@@ -2375,6 +2395,7 @@ void pucch_procedures(const unsigned char sched_subframe,PHY_VARS_eNB *phy_vars_
 			       0, //n2_pucch
 			       0, // shortened format
 			       pucch_payload0,
+                               frame,
 			       subframe,
 			       PUCCH1a_THRES);
 	  }
@@ -2436,6 +2457,7 @@ void pucch_procedures(const unsigned char sched_subframe,PHY_VARS_eNB *phy_vars_
 				    0, //n2_pucch
 				    0, // shortened format
 				    pucch_payload0,
+                                    frame,
 				    subframe,
 				    PUCCH1a_THRES);
             else {
@@ -2467,6 +2489,7 @@ void pucch_procedures(const unsigned char sched_subframe,PHY_VARS_eNB *phy_vars_
                                    0, // n2_pucch
                                    0, // shortened format
                                    pucch_payload0,
+                                   frame,
                                    subframe,
                                    PUCCH1a_THRES);
               else {
@@ -2490,6 +2513,7 @@ void pucch_procedures(const unsigned char sched_subframe,PHY_VARS_eNB *phy_vars_
                                    0, //n2_pucch
                                    0, // shortened format
                                    pucch_payload1,
+                                   frame,
                                    subframe,
                                    PUCCH1a_THRES);
               else {
@@ -2724,6 +2748,11 @@ void phy_procedures_eNB_RX(const unsigned char sched_subframe,PHY_VARS_eNB *phy_
   LOG_D(PHY,"[eNB %d] Frame %d: Doing phy_procedures_eNB_RX(%d)\n",phy_vars_eNB->Mod_id,frame, subframe);
 #endif
 
+  T(T_ENB_UL_TICK, T_INT(phy_vars_eNB->Mod_id), T_INT(frame), T_INT(subframe));
+
+  T(T_ENB_INPUT_SIGNAL, T_INT(phy_vars_eNB->Mod_id), T_INT(frame), T_INT(subframe), T_INT(0),
+    T_BUFFER(&phy_vars_eNB->lte_eNB_common_vars.rxdata[0][0][subframe*phy_vars_eNB->lte_frame_parms.samples_per_tti],
+             phy_vars_eNB->lte_frame_parms.samples_per_tti * 4));
 
   phy_vars_eNB->rb_mask_ul[0]=0;
   phy_vars_eNB->rb_mask_ul[1]=0;
@@ -2955,6 +2984,8 @@ void phy_procedures_eNB_RX(const unsigned char sched_subframe,PHY_VARS_eNB *phy_
 	VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_ENB_ULSCH_MSG3,0);
 
       if (ret == (1+MAX_TURBO_ITERATIONS)) {
+        T(T_ENB_ULSCH_UE_NACK, T_INT(phy_vars_eNB->Mod_id), T_INT(frame), T_INT(subframe), T_INT(i), T_INT(phy_vars_eNB->ulsch_eNB[i]->rnti),
+          T_INT(harq_pid));
 
         phy_vars_eNB->eNB_UE_stats[i].ulsch_round_errors[harq_pid][phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->round]++;
         phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->phich_active = 1;
@@ -3058,6 +3089,9 @@ void phy_procedures_eNB_RX(const unsigned char sched_subframe,PHY_VARS_eNB *phy_
         }
       }  // ulsch in error
       else {
+        T(T_ENB_ULSCH_UE_ACK, T_INT(phy_vars_eNB->Mod_id), T_INT(frame), T_INT(subframe), T_INT(i), T_INT(phy_vars_eNB->ulsch_eNB[i]->rnti),
+          T_INT(harq_pid));
+
         if (phy_vars_eNB->ulsch_eNB[i]->Msg3_flag == 1) {
 	  LOG_D(PHY,"[eNB %d][PUSCH %d] Frame %d subframe %d ULSCH received, setting round to 0, PHICH ACK\n",
 		phy_vars_eNB->Mod_id,harq_pid,
diff --git a/openair1/SIMULATION/LTE_PHY/pucchsim.c b/openair1/SIMULATION/LTE_PHY/pucchsim.c
index fa96ac584338633f266594fa79f47d8c51dc0c8c..c5b356918807a49f9f5acdaf3e7a798d73a677d1 100644
--- a/openair1/SIMULATION/LTE_PHY/pucchsim.c
+++ b/openair1/SIMULATION/LTE_PHY/pucchsim.c
@@ -565,6 +565,7 @@ int main(int argc, char **argv)
                         n2_pucch,
                         0, //shortened_format,
                         &pucch_payload_rx, //payload,
+                        0 /* frame not defined, let's pass 0 */,
                         subframe,
                         pucch1_thres);
 
diff --git a/openair2/LAYER2/MAC/eNB_scheduler_dlsch.c b/openair2/LAYER2/MAC/eNB_scheduler_dlsch.c
index 4d8e32b63c353d1d73fc2169179c38a77dd569da..81e1df4dc52457d55724e3ab378951ec3270e916 100644
--- a/openair2/LAYER2/MAC/eNB_scheduler_dlsch.c
+++ b/openair2/LAYER2/MAC/eNB_scheduler_dlsch.c
@@ -65,6 +65,8 @@
 # include "intertask_interface.h"
 #endif
 
+#include "T.h"
+
 #define ENABLE_MAC_PAYLOAD_DEBUG
 //#define DEBUG_eNB_SCHEDULER 1
 
@@ -833,6 +835,9 @@ schedule_ue_spec(
                                 DCCH,
                                 (char *)&dlsch_buffer[sdu_lengths[0]]);
 
+            T(T_ENB_MAC_UE_DL_SDU, T_INT(module_idP), T_INT(CC_id), T_INT(rnti), T_INT(frameP), T_INT(subframeP),
+              T_INT(harq_pid), T_INT(DCCH), T_INT(sdu_lengths[0]));
+
             LOG_D(MAC,"[eNB %d][DCCH] CC_id %d Got %d bytes from RLC\n",module_idP,CC_id,sdu_lengths[0]);
             sdu_length_total = sdu_lengths[0];
             sdu_lcids[0] = DCCH;
@@ -880,6 +885,9 @@ schedule_ue_spec(
                                        DCCH+1,
                                        (char *)&dlsch_buffer[sdu_lengths[0]]);
 
+            T(T_ENB_MAC_UE_DL_SDU, T_INT(module_idP), T_INT(CC_id), T_INT(rnti), T_INT(frameP), T_INT(subframeP),
+              T_INT(harq_pid), T_INT(DCCH+1), T_INT(sdu_lengths[num_sdus]));
+
             sdu_lcids[num_sdus] = DCCH1;
             sdu_length_total += sdu_lengths[num_sdus];
             header_len_dcch += 2;
@@ -924,6 +932,9 @@ schedule_ue_spec(
                                       DTCH,
                                       (char*)&dlsch_buffer[sdu_length_total]);
 
+            T(T_ENB_MAC_UE_DL_SDU, T_INT(module_idP), T_INT(CC_id), T_INT(rnti), T_INT(frameP), T_INT(subframeP),
+              T_INT(harq_pid), T_INT(DTCH), T_INT(sdu_lengths[num_sdus]));
+
             LOG_D(MAC,"[eNB %d][USER-PLANE DEFAULT DRB] CC_id %d Got %d bytes for DTCH %d \n",
                   module_idP,CC_id,sdu_lengths[num_sdus],DTCH);
             sdu_lcids[num_sdus] = DTCH;
diff --git a/openair2/LAYER2/MAC/eNB_scheduler_ulsch.c b/openair2/LAYER2/MAC/eNB_scheduler_ulsch.c
index 2973bedd00ee8432bdd255f4e1e66dfad5384604..92327572a6df87cee6870837283664910a3332f4 100644
--- a/openair2/LAYER2/MAC/eNB_scheduler_ulsch.c
+++ b/openair2/LAYER2/MAC/eNB_scheduler_ulsch.c
@@ -63,6 +63,8 @@
 # include "intertask_interface.h"
 #endif
 
+#include "T.h"
+
 #define ENABLE_MAC_PAYLOAD_DEBUG
 #define DEBUG_eNB_SCHEDULER 1
 
@@ -126,7 +128,8 @@ void rx_sdu(
 
   payload_ptr = parse_ulsch_header(sduP,&num_ce,&num_sdu,rx_ces,rx_lcids,rx_lengths,sdu_lenP);
 
-
+  T(T_ENB_MAC_UE_UL_PDU, T_INT(enb_mod_idP), T_INT(CC_idP), T_INT(rntiP), T_INT(frameP), T_INT(subframeP),
+    T_INT(harq_pidP), T_INT(sdu_lenP), T_INT(num_ce), T_INT(num_sdu));
 
   eNB->eNB_stats[CC_idP].ulsch_bytes_rx=sdu_lenP;
   eNB->eNB_stats[CC_idP].total_ulsch_bytes_rx+=sdu_lenP;
@@ -134,6 +137,9 @@ void rx_sdu(
   // control element
   for (i=0; i<num_ce; i++) {
 
+    T(T_ENB_MAC_UE_UL_CE, T_INT(enb_mod_idP), T_INT(CC_idP), T_INT(rntiP), T_INT(frameP), T_INT(subframeP),
+      T_INT(rx_ces[i]));
+
     switch (rx_ces[i]) { // implement and process BSR + CRNTI +
     case POWER_HEADROOM:
       if (UE_id != -1) {
@@ -305,6 +311,9 @@ void rx_sdu(
   for (i=0; i<num_sdu; i++) {
     LOG_D(MAC,"SDU Number %d MAC Subheader SDU_LCID %d, length %d\n",i,rx_lcids[i],rx_lengths[i]);
 
+    T(T_ENB_MAC_UE_UL_SDU, T_INT(enb_mod_idP), T_INT(CC_idP), T_INT(rntiP), T_INT(frameP), T_INT(subframeP),
+      T_INT(rx_lcids[i]), T_INT(rx_lengths[i]));
+
     switch (rx_lcids[i]) {
     case CCCH :
       if (rx_lengths[i] > CCCH_PAYLOAD_SIZE_MAX) {
@@ -1091,6 +1100,10 @@ void schedule_ulsch_rnti(module_id_t   module_idP,
 	    
           }
 	  else {
+            T(T_ENB_MAC_UE_UL_SCHEDULE, T_INT(module_idP), T_INT(CC_id), T_INT(rnti), T_INT(frameP),
+              T_INT(subframeP), T_INT(harq_pid), T_INT(mcs), T_INT(first_rb[CC_id]), T_INT(rb_table[rb_table_index]),
+              T_INT(TBS));
+
             LOG_D(MAC,"[eNB %d][PUSCH %d/%x] CC_id %d Frame %d subframeP %d Scheduled (PHICH) UE %d (mcs %d, first rb %d, nb_rb %d, rb_table_index %d, TBS %d, harq_pid %d,round %d)\n",
                   module_idP,harq_pid,rnti,CC_id,frameP,subframeP,UE_id,mcs,
                   first_rb[CC_id],rb_table[rb_table_index],
@@ -1107,6 +1120,10 @@ void schedule_ulsch_rnti(module_id_t   module_idP,
 
             }
 
+            T(T_ENB_MAC_UE_UL_SCHEDULE_RETRANSMISSION, T_INT(module_idP), T_INT(CC_id), T_INT(rnti), T_INT(frameP),
+              T_INT(subframeP), T_INT(harq_pid), T_INT(mcs), T_INT(first_rb[CC_id]), T_INT(rb_table[rb_table_index]),
+              T_INT(round));
+
             LOG_I(MAC,"[eNB %d][PUSCH %d/%x] CC_id %d Frame %d subframeP %d Scheduled UE retransmission (mcs %d, first rb %d, nb_rb %d, harq_pid %d, round %d)\n",
                   module_idP,UE_id,rnti,CC_id,frameP,subframeP,mcs,
                   first_rb[CC_id],UE_template->nb_rb_ul[harq_pid],
diff --git a/openair2/LAYER2/PDCP_v10.1.0/pdcp.c b/openair2/LAYER2/PDCP_v10.1.0/pdcp.c
index 495eb78ac9244eacb7b0ad02a3cffb335e7d971d..d8fa42a19cc3f8a82e5d88fc3d531515287271d9 100755
--- a/openair2/LAYER2/PDCP_v10.1.0/pdcp.c
+++ b/openair2/LAYER2/PDCP_v10.1.0/pdcp.c
@@ -111,6 +111,11 @@ boolean_t pdcp_data_req(
   VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PDCP_DATA_REQ,VCD_FUNCTION_IN);
   CHECK_CTXT_ARGS(ctxt_pP);
 
+#if T_TRACER
+  if (ctxt_pP->enb_flag != ENB_FLAG_NO)
+    T(T_ENB_PDCP_DL, T_INT(ctxt_pP->module_id), T_INT(ctxt_pP->rnti), T_INT(rb_idP), T_INT(sdu_buffer_sizeP));
+#endif
+
   if (modeP == PDCP_TRANSMISSION_MODE_TRANSPARENT) {
     AssertError (rb_idP < NB_RB_MBMS_MAX, return FALSE, "RB id is too high (%u/%d) %u %u!\n", rb_idP, NB_RB_MBMS_MAX, ctxt_pP->module_id, ctxt_pP->rnti);
   } else {
@@ -458,6 +463,11 @@ pdcp_data_ind(
   LOG_F(PDCP,"\n");
 #endif
 
+#if T_TRACER
+  if (ctxt_pP->enb_flag != ENB_FLAG_NO)
+    T(T_ENB_PDCP_UL, T_INT(ctxt_pP->module_id), T_INT(ctxt_pP->rnti), T_INT(rb_idP), T_INT(sdu_buffer_sizeP));
+#endif
+
   if (MBMS_flagP) {
     AssertError (rb_idP < NB_RB_MBMS_MAX, return FALSE,
                  "RB id is too high (%u/%d) %u rnti %x!\n",
diff --git a/openair2/LAYER2/RLC/rlc.c b/openair2/LAYER2/RLC/rlc.c
index 1397387f22a372d794282d597bb3f1e25a74295e..af0fe468754f620a67d0bcdc3e9a53824bcef680 100644
--- a/openair2/LAYER2/RLC/rlc.c
+++ b/openair2/LAYER2/RLC/rlc.c
@@ -359,6 +359,11 @@ rlc_op_status_t rlc_data_req     (const protocol_ctxt_t* const ctxt_pP,
 
 #endif
 
+#if T_TRACER
+  if (ctxt_pP->enb_flag)
+    T(T_ENB_RLC_DL, T_INT(ctxt_pP->module_id), T_INT(ctxt_pP->rnti), T_INT(rb_idP), T_INT(sdu_sizeP));
+#endif
+
   if (MBMS_flagP) {
     AssertFatal (rb_idP < NB_RB_MBMS_MAX, "RB id is too high (%u/%d)!\n", rb_idP, NB_RB_MBMS_MAX);
   } else {
@@ -571,6 +576,12 @@ void rlc_data_ind     (
   rlc_util_print_hex_octets(RLC, (unsigned char*)sdu_pP->data, sdu_sizeP);
 #endif
 
+#if T_TRACER
+  if (ctxt_pP->enb_flag)
+    T(T_ENB_RLC_UL, T_INT(ctxt_pP->module_id), T_INT(ctxt_pP->rnti), T_INT(rb_idP), T_INT(sdu_sizeP));
+#endif
+
+
   pdcp_data_ind (
     ctxt_pP,
     srb_flagP,
diff --git a/openair2/LAYER2/RLC/rlc_mac.c b/openair2/LAYER2/RLC/rlc_mac.c
index 73f323f14b2013844ca2d7239436c1a011925cc1..3bde40888fe1ebc3fbc6d571c3ebf4296ddddb05 100644
--- a/openair2/LAYER2/RLC/rlc_mac.c
+++ b/openair2/LAYER2/RLC/rlc_mac.c
@@ -221,6 +221,11 @@ tbs_size_t mac_rlc_data_req(
     ;
   }
 
+#if T_TRACER
+  if (enb_flagP)
+    T(T_ENB_RLC_MAC_DL, T_INT(module_idP), T_INT(rntiP), T_INT(channel_idP), T_INT(ret_tb_size));
+#endif
+
   VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_MAC_RLC_DATA_REQ,VCD_FUNCTION_OUT);
   return ret_tb_size;
 }
@@ -276,6 +281,10 @@ void mac_rlc_data_ind     (
 
 #endif
 
+#if T_TRACER
+  if (enb_flagP)
+    T(T_ENB_RLC_MAC_UL, T_INT(module_idP), T_INT(rntiP), T_INT(channel_idP), T_INT(tb_sizeP));
+#endif
 
   if (MBMS_flagP) {
     if (BOOL_NOT(enb_flagP)) {
diff --git a/openair2/RRC/LITE/rrc_eNB.c b/openair2/RRC/LITE/rrc_eNB.c
index e67f0af0d308753b1f7ee2aabc96cf499cdba1c8..c85e61a0a435b1203dfc46b2f40c172766700abb 100644
--- a/openair2/RRC/LITE/rrc_eNB.c
+++ b/openair2/RRC/LITE/rrc_eNB.c
@@ -63,6 +63,8 @@
 #include "platform_types.h"
 #include "msc.h"
 
+#include "T.h"
+
 //#ifdef Rel10
 #include "MeasResults.h"
 //#endif
@@ -858,6 +860,9 @@ rrc_eNB_process_RRCConnectionSetupComplete(
         PROTOCOL_RRC_CTXT_UE_FMT" [RAPROC] Logical Channel UL-DCCH, " "processing RRCConnectionSetupComplete from UE\n",
         PROTOCOL_RRC_CTXT_UE_ARGS(ctxt_pP));
 
+  T(T_ENB_RRC_CONNECTION_SETUP_COMPLETE, T_INT(ctxt_pP->module_id), T_INT(ctxt_pP->frame),
+    T_INT(ctxt_pP->subframe), T_INT(ctxt_pP->rnti));
+
 #if defined(ENABLE_USE_MME)
 
   if (EPC_MODE_ENABLED == 1) {
@@ -888,6 +893,9 @@ rrc_eNB_generate_SecurityModeCommand(
   uint8_t                             buffer[100];
   uint8_t                             size;
 
+  T(T_ENB_RRC_SECURITY_MODE_COMMAND, T_INT(ctxt_pP->module_id), T_INT(ctxt_pP->frame),
+    T_INT(ctxt_pP->subframe), T_INT(ctxt_pP->rnti));
+
   size = do_SecurityModeCommand(
            ctxt_pP,
            buffer,
@@ -952,6 +960,9 @@ rrc_eNB_generate_UECapabilityEnquiry(
   uint8_t                             buffer[100];
   uint8_t                             size;
 
+  T(T_ENB_RRC_UE_CAPABILITY_ENQUIRY, T_INT(ctxt_pP->module_id), T_INT(ctxt_pP->frame),
+    T_INT(ctxt_pP->subframe), T_INT(ctxt_pP->rnti));
+
   size = do_UECapabilityEnquiry(
            ctxt_pP,
            buffer,
@@ -1004,6 +1015,9 @@ rrc_eNB_generate_RRCConnectionReject(
   int                                 cnt;
 #endif
 
+  T(T_ENB_RRC_CONNECTION_REJECT, T_INT(ctxt_pP->module_id), T_INT(ctxt_pP->frame),
+    T_INT(ctxt_pP->subframe), T_INT(ctxt_pP->rnti));
+
   eNB_rrc_inst[ctxt_pP->module_id].carrier[CC_id].Srb0.Tx_buffer.payload_size =
     do_RRCConnectionReject(ctxt_pP->module_id,
                           (uint8_t*) eNB_rrc_inst[ctxt_pP->module_id].carrier[CC_id].Srb0.Tx_buffer.Payload);
@@ -1047,6 +1061,9 @@ rrc_eNB_generate_RRCConnectionReestablishmentReject(
   int                                 cnt;
 #endif
 
+  T(T_ENB_RRC_CONNECTION_REESTABLISHMENT_REJECT, T_INT(ctxt_pP->module_id), T_INT(ctxt_pP->frame),
+    T_INT(ctxt_pP->subframe), T_INT(ctxt_pP->rnti));
+
   eNB_rrc_inst[ctxt_pP->module_id].carrier[CC_id].Srb0.Tx_buffer.payload_size =
     do_RRCConnectionReestablishmentReject(ctxt_pP->module_id,
                           (uint8_t*) eNB_rrc_inst[ctxt_pP->module_id].carrier[CC_id].Srb0.Tx_buffer.Payload);
@@ -1089,6 +1106,9 @@ rrc_eNB_generate_RRCConnectionRelease(
   uint8_t                             buffer[RRC_BUF_SIZE];
   uint16_t                            size;
 
+  T(T_ENB_RRC_CONNECTION_RELEASE, T_INT(ctxt_pP->module_id), T_INT(ctxt_pP->frame),
+    T_INT(ctxt_pP->subframe), T_INT(ctxt_pP->rnti));
+
   memset(buffer, 0, RRC_BUF_SIZE);
 
   size = do_RRCConnectionRelease(ctxt_pP->module_id, buffer,rrc_eNB_get_next_transaction_identifier(ctxt_pP->module_id));
@@ -1216,6 +1236,10 @@ rrc_eNB_generate_defaultRRCConnectionReconfiguration(
   }
 
 #endif
+
+  T(T_ENB_RRC_CONNECTION_RECONFIGURATION, T_INT(ctxt_pP->module_id), T_INT(ctxt_pP->frame),
+    T_INT(ctxt_pP->subframe), T_INT(ctxt_pP->rnti));
+
   // Configure SRB2
   /// SRB2
   SRB2_config = CALLOC(1, sizeof(*SRB2_config));
@@ -1843,6 +1867,9 @@ rrc_eNB_process_MeasurementReport(
 )
 //-----------------------------------------------------------------------------
 {
+  T(T_ENB_RRC_MEASUREMENT_REPORT, T_INT(ctxt_pP->module_id), T_INT(ctxt_pP->frame),
+    T_INT(ctxt_pP->subframe), T_INT(ctxt_pP->rnti));
+
 
   LOG_I(RRC, "[eNB %d] Frame %d: Process Measurement Report From UE %x (Measurement Id %d)\n",
         ctxt_pP->module_id, ctxt_pP->frame, ctxt_pP->rnti, (int)measResults2->measId);
@@ -1912,6 +1939,9 @@ rrc_eNB_generate_HandoverPreparationInformation(
      RadioResourceConfigDedicated_t *radioResourceConfigDedicated = CALLOC(1,sizeof(RadioResourceConfigDedicated_t));
    */
 
+  T(T_ENB_RRC_HANDOVER_PREPARATION_INFORMATION, T_INT(ctxt_pP->module_id), T_INT(ctxt_pP->frame),
+    T_INT(ctxt_pP->subframe), T_INT(ctxt_pP->rnti));
+
   handoverInfo->as_config.antennaInfoCommon.antennaPortsCount = 0;    //Not used 0- but check value
   handoverInfo->as_config.sourceDl_CarrierFreq = 36090;   //Verify!
 
@@ -2002,6 +2032,9 @@ rrc_eNB_process_handoverPreparationInformation(
 )
 //-----------------------------------------------------------------------------
 {
+  T(T_ENB_RRC_HANDOVER_PREPARATION_INFORMATION, T_INT(ctxt_pP->module_id), T_INT(ctxt_pP->frame),
+    T_INT(ctxt_pP->subframe), T_INT(ctxt_pP->rnti));
+
 
   LOG_I(RRC,
         "[eNB %d] Frame %d : Logical Channel UL-DCCH, processing RRCHandoverPreparationInformation, sending RRCConnectionReconfiguration to UE %d \n",
@@ -2077,6 +2110,9 @@ rrc_eNB_generate_RRCConnectionReconfiguration_handover(
 )
 //-----------------------------------------------------------------------------
 {
+  T(T_ENB_RRC_CONNECTION_RECONFIGURATION, T_INT(ctxt_pP->module_id), T_INT(ctxt_pP->frame),
+    T_INT(ctxt_pP->subframe), T_INT(ctxt_pP->rnti));
+
 
   uint8_t                             buffer[RRC_BUF_SIZE];
   uint16_t                            size;
@@ -3033,6 +3069,9 @@ rrc_eNB_process_RRCConnectionReconfigurationComplete(
   DRB_ToAddModList_t*                 DRB_configList = ue_context_pP->ue_context.DRB_configList;
   //SRB_ToAddModList_t*                 SRB_configList = ue_context_pP->ue_context.SRB_configList;
 
+  T(T_ENB_RRC_CONNECTION_RECONFIGURATION_COMPLETE, T_INT(ctxt_pP->module_id), T_INT(ctxt_pP->frame),
+    T_INT(ctxt_pP->subframe), T_INT(ctxt_pP->rnti));
+
 #if defined(ENABLE_SECURITY)
 
   /* Derive the keys from kenb */
@@ -3293,6 +3332,9 @@ rrc_eNB_generate_RRCConnectionSetup(
   SRB_ToAddMod_t                     *SRB1_config;
   int                                 cnt;
 
+  T(T_ENB_RRC_CONNECTION_SETUP, T_INT(ctxt_pP->module_id), T_INT(ctxt_pP->frame),
+    T_INT(ctxt_pP->subframe), T_INT(ctxt_pP->rnti));
+
   SRB_configList = &ue_context_pP->ue_context.SRB_configList;
   eNB_rrc_inst[ctxt_pP->module_id].carrier[CC_id].Srb0.Tx_buffer.payload_size =
     do_RRCConnectionSetup(ctxt_pP,
@@ -3567,6 +3609,9 @@ rrc_eNB_decode_ccch(
   uint64_t                                      random_value = 0;
   int                                           stmsi_received = 0;
 
+  T(T_ENB_RRC_UL_CCCH_DATA_IN, T_INT(ctxt_pP->module_id), T_INT(ctxt_pP->frame),
+    T_INT(ctxt_pP->subframe), T_INT(ctxt_pP->rnti));
+
   //memset(ul_ccch_msg,0,sizeof(UL_CCCH_Message_t));
 
   LOG_D(RRC, PROTOCOL_RRC_CTXT_UE_FMT" Decoding UL CCCH %x.%x.%x.%x.%x.%x (%p)\n",
@@ -3637,6 +3682,9 @@ rrc_eNB_decode_ccch(
       break;
 
     case UL_CCCH_MessageType__c1_PR_rrcConnectionReestablishmentRequest:
+      T(T_ENB_RRC_CONNECTION_REESTABLISHMENT_REQUEST, T_INT(ctxt_pP->module_id), T_INT(ctxt_pP->frame),
+        T_INT(ctxt_pP->subframe), T_INT(ctxt_pP->rnti));
+
 #ifdef RRC_MSG_PRINT
       LOG_F(RRC,"[MSG] RRC Connection Reestablishement Request\n");
 
@@ -3679,6 +3727,9 @@ rrc_eNB_decode_ccch(
       break;
 
     case UL_CCCH_MessageType__c1_PR_rrcConnectionRequest:
+      T(T_ENB_RRC_CONNECTION_REQUEST, T_INT(ctxt_pP->module_id), T_INT(ctxt_pP->frame),
+        T_INT(ctxt_pP->subframe), T_INT(ctxt_pP->rnti));
+
 #ifdef RRC_MSG_PRINT
       LOG_F(RRC,"[MSG] RRC Connection Request\n");
 
@@ -3914,6 +3965,9 @@ rrc_eNB_decode_dcch(
   int i;
   struct rrc_eNB_ue_context_s*        ue_context_p = NULL;
 
+  T(T_ENB_RRC_UL_DCCH_DATA_IN, T_INT(ctxt_pP->module_id), T_INT(ctxt_pP->frame),
+    T_INT(ctxt_pP->subframe), T_INT(ctxt_pP->rnti));
+
   if ((Srb_id != 1) && (Srb_id != 2)) {
     LOG_E(RRC, PROTOCOL_RRC_CTXT_UE_FMT" Received message on SRB%d, should not have ...\n",
           PROTOCOL_RRC_CTXT_UE_ARGS(ctxt_pP),
@@ -4061,6 +4115,9 @@ rrc_eNB_decode_dcch(
       break;
 
     case UL_DCCH_MessageType__c1_PR_rrcConnectionReestablishmentComplete:
+      T(T_ENB_RRC_CONNECTION_REESTABLISHMENT_COMPLETE, T_INT(ctxt_pP->module_id), T_INT(ctxt_pP->frame),
+        T_INT(ctxt_pP->subframe), T_INT(ctxt_pP->rnti));
+
 #ifdef RRC_MSG_PRINT
       LOG_F(RRC,"[MSG] RRC Connection Reestablishment Complete\n");
 
@@ -4135,6 +4192,9 @@ rrc_eNB_decode_dcch(
       break;
 
     case UL_DCCH_MessageType__c1_PR_securityModeComplete:
+      T(T_ENB_RRC_SECURITY_MODE_COMPLETE, T_INT(ctxt_pP->module_id), T_INT(ctxt_pP->frame),
+        T_INT(ctxt_pP->subframe), T_INT(ctxt_pP->rnti));
+
 #ifdef RRC_MSG_PRINT
       LOG_F(RRC,"[MSG] RRC Security Mode Complete\n");
 
@@ -4177,6 +4237,9 @@ rrc_eNB_decode_dcch(
       break;
 
     case UL_DCCH_MessageType__c1_PR_securityModeFailure:
+      T(T_ENB_RRC_SECURITY_MODE_FAILURE, T_INT(ctxt_pP->module_id), T_INT(ctxt_pP->frame),
+        T_INT(ctxt_pP->subframe), T_INT(ctxt_pP->rnti));
+
 #ifdef RRC_MSG_PRINT
       LOG_F(RRC,"[MSG] RRC Security Mode Failure\n");
 
@@ -4214,6 +4277,9 @@ rrc_eNB_decode_dcch(
       break;
 
     case UL_DCCH_MessageType__c1_PR_ueCapabilityInformation:
+      T(T_ENB_RRC_UE_CAPABILITY_INFORMATION, T_INT(ctxt_pP->module_id), T_INT(ctxt_pP->frame),
+        T_INT(ctxt_pP->subframe), T_INT(ctxt_pP->rnti));
+
 #ifdef RRC_MSG_PRINT
       LOG_F(RRC,"[MSG] RRC UECapablility Information \n");
 
@@ -4276,9 +4342,15 @@ rrc_eNB_decode_dcch(
       break;
 
     case UL_DCCH_MessageType__c1_PR_ulHandoverPreparationTransfer:
+      T(T_ENB_RRC_UL_HANDOVER_PREPARATION_TRANSFER, T_INT(ctxt_pP->module_id), T_INT(ctxt_pP->frame),
+        T_INT(ctxt_pP->subframe), T_INT(ctxt_pP->rnti));
+
       break;
 
     case UL_DCCH_MessageType__c1_PR_ulInformationTransfer:
+      T(T_ENB_RRC_UL_INFORMATION_TRANSFER, T_INT(ctxt_pP->module_id), T_INT(ctxt_pP->frame),
+        T_INT(ctxt_pP->subframe), T_INT(ctxt_pP->rnti));
+
 #ifdef RRC_MSG_PRINT
       LOG_F(RRC,"[MSG] RRC UL Information Transfer \n");
 
@@ -4312,27 +4384,48 @@ rrc_eNB_decode_dcch(
       break;
 
     case UL_DCCH_MessageType__c1_PR_counterCheckResponse:
+      T(T_ENB_RRC_COUNTER_CHECK_RESPONSE, T_INT(ctxt_pP->module_id), T_INT(ctxt_pP->frame),
+        T_INT(ctxt_pP->subframe), T_INT(ctxt_pP->rnti));
+
       break;
 
 #ifdef Rel10
 
     case UL_DCCH_MessageType__c1_PR_ueInformationResponse_r9:
+      T(T_ENB_RRC_UE_INFORMATION_RESPONSE_R9, T_INT(ctxt_pP->module_id), T_INT(ctxt_pP->frame),
+        T_INT(ctxt_pP->subframe), T_INT(ctxt_pP->rnti));
+
       break;
 
     case UL_DCCH_MessageType__c1_PR_proximityIndication_r9:
+      T(T_ENB_RRC_PROXIMITY_INDICATION_R9, T_INT(ctxt_pP->module_id), T_INT(ctxt_pP->frame),
+        T_INT(ctxt_pP->subframe), T_INT(ctxt_pP->rnti));
+
       break;
 
     case UL_DCCH_MessageType__c1_PR_rnReconfigurationComplete_r10:
+      T(T_ENB_RRC_RECONFIGURATION_COMPLETE_R10, T_INT(ctxt_pP->module_id), T_INT(ctxt_pP->frame),
+        T_INT(ctxt_pP->subframe), T_INT(ctxt_pP->rnti));
+
       break;
 
     case UL_DCCH_MessageType__c1_PR_mbmsCountingResponse_r10:
+      T(T_ENB_RRC_MBMS_COUNTING_RESPONSE_R10, T_INT(ctxt_pP->module_id), T_INT(ctxt_pP->frame),
+        T_INT(ctxt_pP->subframe), T_INT(ctxt_pP->rnti));
+
       break;
 
     case UL_DCCH_MessageType__c1_PR_interFreqRSTDMeasurementIndication_r10:
+      T(T_ENB_RRC_INTER_FREQ_RSTD_MEASUREMENT_INDICATION, T_INT(ctxt_pP->module_id), T_INT(ctxt_pP->frame),
+        T_INT(ctxt_pP->subframe), T_INT(ctxt_pP->rnti));
+
       break;
 #endif
 
     default:
+      T(T_ENB_RRC_UNKNOW_MESSAGE, T_INT(ctxt_pP->module_id), T_INT(ctxt_pP->frame),
+        T_INT(ctxt_pP->subframe), T_INT(ctxt_pP->rnti));
+
       LOG_E(RRC, PROTOCOL_RRC_CTXT_UE_FMT" Unknown message %s:%u\n",
             PROTOCOL_RRC_CTXT_UE_ARGS(ctxt_pP),
             __FILE__, __LINE__);
diff --git a/openair2/UTIL/LOG/log.h b/openair2/UTIL/LOG/log.h
index c6abbfa001033107c8e1c57671f6e000d8514c0e..dce5a190e4d454b3fe8f60ed4fc1a9ada4e6ebdc 100755
--- a/openair2/UTIL/LOG/log.h
+++ b/openair2/UTIL/LOG/log.h
@@ -147,26 +147,40 @@ extern "C" {
 
 // debugging macros
 #ifdef USER_MODE
-#define LOG_G(c, x...) logIt(c, LOG_EMERG, x)
-#define LOG_A(c, x...) logIt(c, LOG_ALERT, x)
-#define LOG_C(c, x...) logIt(c, LOG_CRIT,  x)
-#define LOG_E(c, x...) logIt(c, LOG_ERR, x)
-#define LOG_W(c, x...) logIt(c, LOG_WARNING, x)
-#define LOG_N(c, x...) logIt(c, LOG_NOTICE, x)
-#define LOG_I(c, x...) logIt(c, LOG_INFO, x)
-#define LOG_D(c, x...) logIt(c, LOG_DEBUG, x)
-#define LOG_F(c, x...) logIt(c, LOG_FILE, x)  // log to a file, useful for the MSC chart generation
-#define LOG_T(c, x...) logIt(c, LOG_TRACE, x)
-#else
-#define LOG_G(c, x...) printk(x)
-#define LOG_A(c, x...) printk(x)
-#define LOG_C(c, x...) printk(x)
-#define LOG_E(c, x...) printk(x)
-#define LOG_W(c, x...) printk(x)
-#define LOG_N(c, x...) printk(x)
-#define LOG_I(c, x...) printk(x)
-#define LOG_D(c, x...) printk(x)
-#define LOG_T(c, x...) printk(x)
+#  if T_TRACER
+#    include "T.h"
+#    define LOG_I(c, x...) T(T_LEGACY_ ## c ## _INFO, T_PRINTF(x))
+#    define LOG_W(c, x...) T(T_LEGACY_ ## c ## _WARNING, T_PRINTF(x))
+#    define LOG_E(c, x...) T(T_LEGACY_ ## c ## _ERROR, T_PRINTF(x))
+#    define LOG_D(c, x...) T(T_LEGACY_ ## c ## _DEBUG, T_PRINTF(x))
+#    define LOG_T(c, x...) T(T_LEGACY_ ## c ## _TRACE, T_PRINTF(x))
+#    define LOG_G(c, x...) /* */
+#    define LOG_A(c, x...) /* */
+#    define LOG_C(c, x...) /* */
+#    define LOG_N(c, x...) /* */
+#    define LOG_F(c, x...) /* */
+#  else /* T_TRACER */
+#    define LOG_G(c, x...) logIt(c, LOG_EMERG, x)
+#    define LOG_A(c, x...) logIt(c, LOG_ALERT, x)
+#    define LOG_C(c, x...) logIt(c, LOG_CRIT,  x)
+#    define LOG_E(c, x...) logIt(c, LOG_ERR, x)
+#    define LOG_W(c, x...) logIt(c, LOG_WARNING, x)
+#    define LOG_N(c, x...) logIt(c, LOG_NOTICE, x)
+#    define LOG_I(c, x...) logIt(c, LOG_INFO, x)
+#    define LOG_D(c, x...) logIt(c, LOG_DEBUG, x)
+#    define LOG_F(c, x...) logIt(c, LOG_FILE, x)  // log to a file, useful for the MSC chart generation
+#    define LOG_T(c, x...) logIt(c, LOG_TRACE, x)
+#  endif /* T_TRACER */
+#else /* USER_MODE */
+#  define LOG_G(c, x...) printk(x)
+#  define LOG_A(c, x...) printk(x)
+#  define LOG_C(c, x...) printk(x)
+#  define LOG_E(c, x...) printk(x)
+#  define LOG_W(c, x...) printk(x)
+#  define LOG_N(c, x...) printk(x)
+#  define LOG_I(c, x...) printk(x)
+#  define LOG_D(c, x...) printk(x)
+#  define LOG_T(c, x...) printk(x)
 #endif
 /* @}*/
 
diff --git a/openair2/UTIL/LOG/vcd_signal_dumper.h b/openair2/UTIL/LOG/vcd_signal_dumper.h
index a9e433936872a721559e82b04c2ad76c22d28277..06292a5212ba4d88230b74941fbc3e6f17c155d1 100644
--- a/openair2/UTIL/LOG/vcd_signal_dumper.h
+++ b/openair2/UTIL/LOG/vcd_signal_dumper.h
@@ -375,6 +375,16 @@ void vcd_signal_dumper_dump_function_by_name(vcd_signal_dump_functions  function
 
 extern int ouput_vcd;
 
+#if T_TRACER
+
+#define VCD_SIGNAL_DUMPER_INIT(x)         /* nothing */
+#define VCD_SIGNAL_DUMPER_CLOSE()         /* nothing */
+#define VCD_SIGNAL_DUMPER_CREATE_HEADER() /* nothing */
+#define VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(var, val) T_VCD_VARIABLE(var, val)
+#define VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(var, val) T_VCD_FUNCTION(var, val)
+
+#else /* T_TRACER */
+
 #if defined(ENABLE_VCD)
    #define VCD_SIGNAL_DUMPER_INIT(aRgUmEnT)                   vcd_signal_dumper_init(aRgUmEnT)
    #define VCD_SIGNAL_DUMPER_CLOSE()                          vcd_signal_dumper_close()
@@ -389,5 +399,7 @@ extern int ouput_vcd;
    #define VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(vAr1,vAr2)
 #endif
 
+#endif /* T_TRACER */
+
 #endif /* !defined (VCD_SIGNAL_DUMPER_H_) */
 
diff --git a/targets/RT/USER/lte-softmodem.c b/targets/RT/USER/lte-softmodem.c
index 5b5899198a8d743746dffcf00d1217f540d9c276..ca97ad0e225acc5ca7f4205d30a3c7e7209dbf09 100644
--- a/targets/RT/USER/lte-softmodem.c
+++ b/targets/RT/USER/lte-softmodem.c
@@ -51,6 +51,9 @@
 #include <execinfo.h>
 #include <getopt.h>
 #include <sys/sysinfo.h>
+
+#include "T.h"
+
 #include "rt_wrapper.h"
 
 #undef MALLOC //there are two conflicting definitions, so we better make sure we don't use it at all
@@ -483,8 +486,13 @@ void help (void) {
   printf("  -U Set the lte softmodem as a UE\n");
   printf("  -W Enable L2 wireshark messages on localhost \n");
   printf("  -V Enable VCD (generated file will be located atopenair_dump_eNB.vcd, read it with target/RT/USER/eNB.gtkw\n");
-  printf("  -x Set the transmission mode, valid options: 1 \n"RESET);    
-
+  printf("  -x Set the transmission mode, valid options: 1 \n");
+#if T_TRACER
+  printf("  --T_port [port]    use given port\n");
+  printf("  --T_nowait         don't wait for tracer, start immediately\n");
+#endif
+  printf(RESET);
+  fflush(stdout);
 }
 void exit_fun(const char* s)
 {
@@ -2177,6 +2185,8 @@ static void* eNB_thread( void* arg )
 #else
       int sf = hw_subframe;
 #endif
+      /* TODO: is it the right place for master tick? */
+      T(T_ENB_MASTER_TICK, T_INT(0), T_INT(frame % 1024), T_INT(sf));
       if (frame>50) {
 	for (int CC_id=0; CC_id<MAX_NUM_CCs; CC_id++) {
 #ifdef EXMIMO
@@ -2317,7 +2327,12 @@ static void get_options (int argc, char **argv)
     LONG_OPTION_MAXPOWER,
     LONG_OPTION_DUMP_FRAME,
     LONG_OPTION_LOOPMEMORY,
-    LONG_OPTION_PHYTEST
+    LONG_OPTION_PHYTEST,
+
+#if T_TRACER
+    LONG_OPTION_T_PORT,
+    LONG_OPTION_T_NOWAIT,
+#endif
   };
 
   static const struct option long_options[] = {
@@ -2336,6 +2351,12 @@ static void get_options (int argc, char **argv)
     {"ue-dump-frame", no_argument, NULL, LONG_OPTION_DUMP_FRAME},
     {"loop-memory", required_argument, NULL, LONG_OPTION_LOOPMEMORY},
     {"phy-test", no_argument, NULL, LONG_OPTION_PHYTEST},
+
+#if T_TRACER
+    {"T_port",                 required_argument, 0, LONG_OPTION_T_PORT},
+    {"T_nowait",               no_argument,       0, LONG_OPTION_T_NOWAIT},
+#endif
+
     {NULL, 0, NULL, 0}
   };
 
@@ -2423,6 +2444,21 @@ static void get_options (int argc, char **argv)
       phy_test = 1;
       break;
       
+#if T_TRACER
+    case LONG_OPTION_T_PORT: {
+      extern int T_port;
+      if (optarg == NULL) abort();  /* should not happen */
+      T_port = atoi(optarg);
+      break;
+    }
+
+    case LONG_OPTION_T_NOWAIT: {
+      extern int T_wait;
+      T_wait = 0;
+      break;
+    }
+#endif
+
     case 'A':
       timing_advance = atoi (optarg);
       break;
@@ -2783,6 +2819,11 @@ static void get_options (int argc, char **argv)
   }
 }
 
+#if T_TRACER
+int T_wait = 1;       /* by default we wait for the tracer */
+int T_port = 2021;    /* default port to listen to to wait for the tracer */
+#endif
+
 int main( int argc, char **argv )
 {
   int i,aa,card=0;
@@ -2852,6 +2893,10 @@ int main( int argc, char **argv )
   else
     openair0_cfg[0].configFilename = rf_config_file;
   
+#if T_TRACER
+  T_init(T_port, T_wait);
+#endif
+
   // initialize the log (see log.h for details)
   set_glog(glog_level, glog_verbosity);
 
diff --git a/targets/SIMU/USER/oaisim.c b/targets/SIMU/USER/oaisim.c
index 63d13f20fc229d4fc8625ee9764448dba9861b58..dfbf8f9676c6db683c6312ef150041ea288ea791 100644
--- a/targets/SIMU/USER/oaisim.c
+++ b/targets/SIMU/USER/oaisim.c
@@ -113,6 +113,8 @@ char smbv_ip[16];
 # include "create_tasks.h"
 #endif
 
+#include "T.h"
+
 /*
  DCI0_5MHz_TDD0_t          UL_alloc_pdu;
  DCI1A_5MHz_TDD_1_6_t      CCCH_alloc_pdu;
@@ -245,6 +247,10 @@ help (void)
   printf ("-Y Set the global log verbosity (none, low, medium, high, full) \n");
   printf ("-z Set the cooperation flag (0 for no cooperation, 1 for delay diversity and 2 for distributed alamouti\n");
   printf ("-Z Reserved\n");
+#if T_TRACER
+  printf ("--T_port [port]    use given port\n");
+  printf ("--T_nowait         don't wait for tracer, start immediately\n");
+#endif
 }
 
 pthread_t log_thread;
@@ -739,7 +745,8 @@ l2l1_task (void *args_p)
                  + oai_emulation.info.nb_enb_local));
              eNB_inst++) {
           if (oai_emulation.info.cli_start_enb[eNB_inst] != 0) {
-            if ((slot & 1) == 0)
+            if ((slot & 1) == 0) {
+              T(T_ENB_MASTER_TICK, T_INT(eNB_inst), T_INT(frame % 1024), T_INT(slot/2));
               LOG_D(EMU,
                     "PHY procedures eNB %d for frame %d, slot %d (subframe TX %d, RX %d) TDD %d/%d Nid_cell %d\n",
                     eNB_inst,
@@ -750,6 +757,7 @@ l2l1_task (void *args_p)
                     PHY_vars_eNB_g[eNB_inst][0]->lte_frame_parms.frame_type,
                     PHY_vars_eNB_g[eNB_inst][0]->lte_frame_parms.tdd_config,
                     PHY_vars_eNB_g[eNB_inst][0]->lte_frame_parms.Nid_cell);
+            }
 
 #ifdef OPENAIR2
                         //Application: traffic gen
@@ -1247,6 +1255,11 @@ l2l1_task (void *args_p)
   return NULL;
 }
 
+#if T_TRACER
+int T_wait = 1;       /* by default we wait for the tracer */
+int T_port = 2021;    /* default port to listen to to wait for the tracer */
+#endif
+
 /*------------------------------------------------------------------------------*/
 int
 main (int argc, char **argv)
@@ -1263,6 +1276,7 @@ main (int argc, char **argv)
   int node_id;
   int port,Process_Flag=0,wgt,Channel_Flag=0,temp;
 #endif
+
   //default parameters
   oai_emulation.info.n_frames = MAX_FRAME_NUMBER; //1024;          //10;
   oai_emulation.info.n_frames_flag = 0; //fixme
@@ -1279,6 +1293,10 @@ main (int argc, char **argv)
   // get command-line options
   get_simulation_options (argc, argv); //Command-line options
 
+#if T_TRACER
+  T_init(T_port, T_wait);
+#endif
+
   // Initialize VCD LOG module
   VCD_SIGNAL_DUMPER_INIT (oai_emulation.info.vcd_file);
 
diff --git a/targets/SIMU/USER/oaisim_functions.c b/targets/SIMU/USER/oaisim_functions.c
index b87142c3e5c0e6c96ab26735578934e4a725d311..f8043b2cfd710c11f5a41cea72ae73387ef37721 100644
--- a/targets/SIMU/USER/oaisim_functions.c
+++ b/targets/SIMU/USER/oaisim_functions.c
@@ -212,6 +212,11 @@ void get_simulation_options(int argc, char *argv[])
 
     LONG_OPTION_PHYTEST,
     LONG_OPTION_XFORMS,
+
+#if T_TRACER
+    LONG_OPTION_T_PORT,
+    LONG_OPTION_T_NOWAIT,
+#endif
   };
 
   static struct option long_options[] = {
@@ -246,6 +251,11 @@ void get_simulation_options(int argc, char *argv[])
     {"phy-test", no_argument, NULL, LONG_OPTION_PHYTEST},
     {"xforms",                 no_argument,       0, LONG_OPTION_XFORMS},
 
+#if T_TRACER
+    {"T_port",                 required_argument, 0, LONG_OPTION_T_PORT},
+    {"T_nowait",               no_argument,       0, LONG_OPTION_T_NOWAIT},
+#endif
+
     {NULL, 0, NULL, 0}
   };
 
@@ -413,6 +423,21 @@ void get_simulation_options(int argc, char *argv[])
       xforms=1;
       break;
 
+#if T_TRACER
+    case LONG_OPTION_T_PORT: {
+      extern int T_port;
+      if (optarg == NULL) abort();  /* should not happen */
+      T_port = atoi(optarg);
+      break;
+    }
+
+    case LONG_OPTION_T_NOWAIT: {
+      extern int T_wait;
+      T_wait = 0;
+      break;
+    }
+#endif
+
     case 'a':
       abstraction_flag = 1;
       break;