From 91329928016106bb57999c0c2c4d1c1741f5d07b Mon Sep 17 00:00:00 2001
From: Robert Schmidt <robert.schmidt@openairinterface.org>
Date: Mon, 3 Jul 2023 17:34:48 +0200
Subject: [PATCH] T: add tutorial for new trace

---
 common/utils/T/DOC/T.md                    |   1 +
 common/utils/T/DOC/T/howto_new_trace.md    |  95 ++++++++++
 common/utils/T/DOC/T/howto_new_trace.patch | 210 +++++++++++++++++++++
 3 files changed, 306 insertions(+)
 create mode 100644 common/utils/T/DOC/T/howto_new_trace.md
 create mode 100644 common/utils/T/DOC/T/howto_new_trace.patch

diff --git a/common/utils/T/DOC/T.md b/common/utils/T/DOC/T.md
index acc06a057f..e1ba0bae34 100644
--- a/common/utils/T/DOC/T.md
+++ b/common/utils/T/DOC/T.md
@@ -17,3 +17,4 @@ It is made of two main parts:
 * [MAC PDUs and wireshark](./T/wireshark.md)
 * [The *enb* tracer](./T/enb.md)
 * [The *to_vcd* tracer](./T/to_vcd.md)
+* [Tutorial: how to create a new trace](./T/howto_new_trace.md)
diff --git a/common/utils/T/DOC/T/howto_new_trace.md b/common/utils/T/DOC/T/howto_new_trace.md
new file mode 100644
index 0000000000..b8dafb4c49
--- /dev/null
+++ b/common/utils/T/DOC/T/howto_new_trace.md
@@ -0,0 +1,95 @@
+# T tracer - a simple tutorial
+
+There is a diff file with all modifications:
+[howto_new_trace.patch](./howto_new_trace.patch).
+We use tag 2023.w28
+To use the patch, clone the repository,
+then in the top directory of openairinterface:
+```
+git checkout 2023.w28
+patch -p1 < common/utils/T/DOC/T/howto_new_trace.patch
+```
+
+Then compile nr-softmodem as usual.
+
+## 1 - create a new trace
+
+As an example, let's add a new trace to dump LDPC decoding success.
+
+In the file `common/utils/T/T_messages.txt`
+we add those two lines:
+
+```
+ID = LDPC_OK
+    FORMAT = int,segment : int,nb_segments : int,offset : buffer,data
+```
+
+- You create an ID for the new trace.
+- You give the format as a list of variables with there types.
+
+Then, in the file `openair1/SCHED_NR/phy_procedures_nr_gNB.c`,
+in function `nr_postDecode()`, we add, at the place where we want to trace:
+
+```
+    T(T_LDPC_OK,
+      T_INT(rdata->segment_r),
+      T_INT(rdata->nbSegments),
+      T_INT(rdata->offset),
+      T_BUFFER(ulsch_harq->c[r], rdata->Kr_bytes - (ulsch_harq->F>>3) -((ulsch_harq->C>1)?3:0)));
+```
+
+And that's all, the code is instrumented, a trace is created,
+we now need a tracer to process the trace.
+
+## 2. create a tracer
+
+See `trace_ldpc.c` in the attached diff. Read it, there is documentation.
+To compile, just do `make trace_ldpc` in `common/utils/T/tracer`
+To use: `cd common/utils/T/tracer; ./trace_ldpc -d ../T_messages.txt`
+
+Your tracer can do whatever it wants with the data.
+
+The Makefile has been modified to compile it. See the diff.
+
+Use it as a basis for your own tracer.
+You can also look at the other tracers, especially `textlog.c`
+and `macpdu2wireshark.c` which are the most useful for me to
+trace/debug the nr-softmodem.
+
+## 3. available types in a trace
+
+You trace the code with:
+
+```
+    T(T_trace_name, T_xxx(variable1), T_xxx(variables2), ...);
+```
+
+`T_xxx` can be:
+
+- `T_INT(variable)`
+- `T_FLOAT(variable)`                  (not tested much, may fail)
+- `T_BUFFER(variable, length)`
+- `T_STRING(variable)`
+- `T_PRINTF(format, arg1, ... argn)`
+
+What is traced is obvious from the name. `T_INT` traces an int for example.
+
+corresponding type to put in `T_messages.txt`
+
+- `T_INT`: int
+- `T_FLOAT`: float
+- `T_BUFFER`: buffer
+- `T_STRING`: string
+- `T_PRINTF`: string
+
+And that's all.
+You can add as many traces as you want, just add them in `T_messages.txt`,
+put the `T()` macro in the code where you want to trace, and modify your
+tracer to deal with it.
+
+An alternative to writing your own tracer could be to use `textlog`
+with `-full` command line and process the text with some external
+tools.
+
+It is also possible to record the events in a file using the tracer
+`record` and then use `replay` to process the recorded file later.
diff --git a/common/utils/T/DOC/T/howto_new_trace.patch b/common/utils/T/DOC/T/howto_new_trace.patch
new file mode 100644
index 0000000000..47bbc66138
--- /dev/null
+++ b/common/utils/T/DOC/T/howto_new_trace.patch
@@ -0,0 +1,210 @@
+diff --git a/common/utils/T/T_messages.txt b/common/utils/T/T_messages.txt
+index c535f36f5e..1632131ceb 100644
+--- a/common/utils/T/T_messages.txt
++++ b/common/utils/T/T_messages.txt
+@@ -4,6 +4,9 @@ ID = ENB_MASTER_TICK
+     GROUP = ALL:GENERAL:ENB
+     FORMAT = int,eNB_ID : int,frame : int,subframe
+ 
++ID = LDPC_OK
++    FORMAT = int,segment : int,nb_segments : int,offset : buffer,data
++
+ #PHY logs
+ ID = ENB_PHY_UL_TICK
+     DESC = eNodeB uplink tick - one tick per ms at start of uplink processing
+diff --git a/common/utils/T/tracer/CMakeLists.txt b/common/utils/T/tracer/CMakeLists.txt
+index 02e0e2dc9b..db438c42d3 100644
+--- a/common/utils/T/tracer/CMakeLists.txt
++++ b/common/utils/T/tracer/CMakeLists.txt
+@@ -57,6 +57,10 @@ target_link_libraries(gnb PRIVATE tracer_utils tracer_filter tracer_gui
+                       tracer_logger tracer_view tracer_events)
+ target_link_libraries(gnb PRIVATE png)
+ 
++add_executable(trace_ldpc trace_ldpc.c)
++target_link_libraries(trace_ldpc PRIVATE tracer_utils tracer_filter
++                      tracer_logger tracer_view)
++
+ add_subdirectory(filter)
+ add_subdirectory(gui)
+ add_subdirectory(logger)
+@@ -66,4 +70,4 @@ add_custom_target(T_tools)
+ add_dependencies(T_tools
+                  record replay extract_config textlog enb ue vcd macpdu2wireshark
+                  extract_input_subframe extract_output_subframe to_vcd extract multi
+-                 gnb)
++                 gnb trace_ldpc)
+diff --git a/common/utils/T/tracer/Makefile b/common/utils/T/tracer/Makefile
+index d3c959a689..d13ff1e996 100644
+--- a/common/utils/T/tracer/Makefile
++++ b/common/utils/T/tracer/Makefile
+@@ -7,4 +7,4 @@ clean:
+ 	rm -rf tracer_build
+ 	rm record replay extract_config textlog enb ue vcd macpdu2wireshark \
+ 		extract_input_subframe extract_output_subframe to_vcd extract multi \
+-		gnb
++		gnb trace_ldpc
+diff --git a/common/utils/T/tracer/trace_ldpc.c b/common/utils/T/tracer/trace_ldpc.c
+new file mode 100644
+index 0000000000..00baf34774
+--- /dev/null
++++ b/common/utils/T/tracer/trace_ldpc.c
+@@ -0,0 +1,143 @@
++#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 "utils.h"
++#include "event_selector.h"
++#include "config.h"
++
++/* this function sends the activated traces to the nr-softmodem */
++void activate_traces(int socket, int number_of_events, int *is_on)
++{
++  char t = 1;
++  if (socket_send(socket, &t, 1) == -1 ||
++      socket_send(socket, &number_of_events, sizeof(int)) == -1 ||
++      socket_send(socket, is_on, number_of_events * sizeof(int)) == -1)
++    abort();
++}
++
++void usage(void)
++{
++  printf(
++"options:\n"
++"    -d <database file>        this option is mandatory\n"
++"    -ip <host>                connect to given IP address (default %s)\n"
++"    -p <port>                 connect to given port (default %d)\n",
++  DEFAULT_REMOTE_IP,
++  DEFAULT_REMOTE_PORT
++  );
++  exit(1);
++}
++
++int main(int n, char **v)
++{
++  char *database_filename = NULL;
++  void *database;
++  char *ip = DEFAULT_REMOTE_IP;
++  int port = DEFAULT_REMOTE_PORT;
++  int *is_on;
++  int number_of_events;
++  int i;
++  int socket;
++  int ldpc_ok_id;
++  database_event_format f;
++  int segment;
++  int nb_segments;
++  int offset;
++  int data;
++
++  /* write on a socket fails if the other end is closed and we get SIGPIPE */
++  if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) abort();
++
++  /* parse command line options */
++  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; }
++    usage();
++  }
++
++  if (database_filename == NULL) {
++    printf("ERROR: provide a database file (-d)\n");
++    exit(1);
++  }
++
++  /* load the database T_messages.txt */
++  database = parse_database(database_filename);
++  load_config_file(database_filename);
++
++  /* an array of int for all the events defined in the database is needed */
++  number_of_events = number_of_ids(database);
++  is_on = calloc(number_of_events, sizeof(int));
++  if (is_on == NULL) abort();
++
++  /* activate the LDPC_OK trace in this array */
++  on_off(database, "LDPC_OK", is_on, 1);
++
++  /* connect to the nr-softmodem */
++  socket = connect_to(ip, port);
++
++  /* activate the trace LDPC_OK in the nr-softmodem */
++  activate_traces(socket, number_of_events, is_on);
++
++  free(is_on);
++
++  /* get the format of the LDPC_OK trace */
++  ldpc_ok_id = event_id_from_name(database, "LDPC_OK");
++  f = get_format(database, ldpc_ok_id);
++
++/* this macro looks for a particular element and checks its type */
++#define G(var_name, var_type, var) \
++  if (!strcmp(f.name[i], var_name)) { \
++    if (strcmp(f.type[i], var_type)) { printf("bad type for %s\n", var_name); exit(1); } \
++    var = i; \
++    continue; \
++  }
++
++  /* get the elements of the LDPC_OK trace
++   * the value is an index in the event, see below
++   */
++  for (i = 0; i < f.count; i++) {
++    G("segment",     "int",    segment);
++    G("nb_segments", "int",    nb_segments);
++    G("offset",      "int",    offset);
++    G("data",        "buffer", data);
++  }
++
++  /* a buffer needed to receive events from the nr-softmodem */
++  OBUF ebuf = { osize: 0, omaxsize: 0, obuf: NULL };
++
++  /* read events */
++  while (1) {
++    event e;
++    e = get_event(socket, &ebuf, database);
++    if (e.type == -1) break;
++    if (e.type == ldpc_ok_id) {
++      /* this is how to access the elements of the LDPC_OK trace.
++       * we use e.e[<element>] and then the correct suffix, here
++       * it's .i for the integer and .b for the buffer and .bsize
++       * for the buffer size
++       * see in event.h the structure event_arg
++       */
++      unsigned char *buf = e.e[data].b;
++      printf("get LDPC_OK event segment %d nb_segments %d offset %d buffer length %d = [",
++             e.e[segment].i,
++             e.e[nb_segments].i,
++             e.e[offset].i,
++             e.e[data].bsize);
++      for (i = 0; i < e.e[data].bsize; i++)
++        printf(" %2.2x", buf[i]);
++      printf("]\n");
++    }
++  }
++
++  return 0;
++}
+diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c
+index f33106ecaa..129b3606b6 100644
+--- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c
++++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c
+@@ -255,6 +255,11 @@ static void nr_postDecode(PHY_VARS_gNB *gNB, notifiedFIFO_elt_t *req)
+         ulsch_harq->processedSegments,
+         rdata->nbSegments);
+   if (decodeSuccess) {
++    T(T_LDPC_OK,
++      T_INT(rdata->segment_r),
++      T_INT(rdata->nbSegments),
++      T_INT(rdata->offset),
++      T_BUFFER(ulsch_harq->c[r], rdata->Kr_bytes - (ulsch_harq->F >> 3) - ((ulsch_harq->C > 1) ? 3 : 0)));
+     memcpy(ulsch_harq->b + rdata->offset, ulsch_harq->c[r], rdata->Kr_bytes - (ulsch_harq->F >> 3) - ((ulsch_harq->C > 1) ? 3 : 0));
+ 
+   } else {
-- 
2.26.2