From 2198e311876daadcd60513296207e8f118f13436 Mon Sep 17 00:00:00 2001
From: Bartosz Podrygajlo <bartosz.podrygajlo@openairinterface.org>
Date: Fri, 3 Jan 2025 15:57:59 +0100
Subject: [PATCH] More updates to imscope IQ recorder

 - Added event parameter for recording which is reflected in the filename
 - Added event in the UE for DLSCH NACK
 - Added program name to the filename for ease of identification
 - Reworked spinlock to use atomics
---
 .../PHY/NR_UE_TRANSPORT/nr_dlsch_decoding.c   |  1 +
 openair1/PHY/TOOLS/imscope/CMakeLists.txt     |  2 +-
 openair1/PHY/TOOLS/imscope/imscope_internal.h |  2 +-
 openair1/PHY/TOOLS/imscope/imscope_record.cpp | 57 ++++++++++++++-----
 openair1/PHY/TOOLS/phy_scope_interface.h      | 10 ++--
 openair1/SCHED_NR/phy_procedures_nr_gNB.c     |  4 +-
 6 files changed, 53 insertions(+), 23 deletions(-)

diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_decoding.c b/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_decoding.c
index 4fb88ec969..a3138f5d7f 100644
--- a/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_decoding.c
+++ b/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_decoding.c
@@ -321,6 +321,7 @@ uint32_t nr_dlsch_decoding(PHY_VARS_NR_UE *phy_vars_ue,
       LOG_D(PHY, "DLSCH received nok \n");
       kpiStructure.nb_nack++;
       dlsch->last_iteration_cnt = dlsch->max_ldpc_iterations + 1;
+      UEdumpScopeData(phy_vars_ue, proc->nr_slot_rx, proc->frame_rx, "DLSCH_NACK");
     }
 
     uint8_t dmrs_Type = dlsch_config->dmrsConfigType;
diff --git a/openair1/PHY/TOOLS/imscope/CMakeLists.txt b/openair1/PHY/TOOLS/imscope/CMakeLists.txt
index 0b1ecf0835..060b288419 100644
--- a/openair1/PHY/TOOLS/imscope/CMakeLists.txt
+++ b/openair1/PHY/TOOLS/imscope/CMakeLists.txt
@@ -35,7 +35,7 @@ set_target_properties(imscope PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY
 configure_file(imgui.ini ${CMAKE_BINARY_DIR}/imscope-init.ini COPYONLY)
 
 add_library(imscope_record MODULE ../phy_scope_interface.c imscope_common.cpp imscope_record.cpp)
-target_link_libraries(imscope_record PRIVATE UTIL)
+target_link_libraries(imscope_record PRIVATE UTIL atomic)
 set_target_properties(imscope_record PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
 
 add_executable(imscope_iq_file_viewer imscope.cpp imscope_iq_file_viewer.cpp ../phy_scope_interface.c imscope_common.cpp)
diff --git a/openair1/PHY/TOOLS/imscope/imscope_internal.h b/openair1/PHY/TOOLS/imscope/imscope_internal.h
index f0f738bf80..7b0b04758d 100644
--- a/openair1/PHY/TOOLS/imscope/imscope_internal.h
+++ b/openair1/PHY/TOOLS/imscope/imscope_internal.h
@@ -152,7 +152,7 @@ void copyDataUnsafeWithOffset(enum scopeDataType type, void *dataIn, size_t size
 
 void unlockScopeData(enum scopeDataType type);
 
-void dumpScopeData(int slot, int frame);
+void dumpScopeData(int slot, int frame, const char *cause_string);
 
 void *imscope_record_thread(void *data_void_ptr);
 void *imscope_thread(void *data_void_ptr);
diff --git a/openair1/PHY/TOOLS/imscope/imscope_record.cpp b/openair1/PHY/TOOLS/imscope/imscope_record.cpp
index c67b4b0efe..a59366d504 100644
--- a/openair1/PHY/TOOLS/imscope/imscope_record.cpp
+++ b/openair1/PHY/TOOLS/imscope/imscope_record.cpp
@@ -22,6 +22,18 @@
 #include "imscope_internal.h"
 #include "openair1/PHY/TOOLS/phy_scope_interface.h"
 #include <sys/stat.h>
+#include <errno.h>
+#include <atomic>
+#include <new>
+
+#ifdef __cpp_lib_hardware_interference_size
+    using std::hardware_constructive_interference_size;
+    using std::hardware_destructive_interference_size;
+#else
+    // 64 bytes on x86-64 鈹� L1_CACHE_BYTES 鈹� L1_CACHE_SHIFT 鈹� __cacheline_aligned 鈹� ...
+    constexpr std::size_t hardware_constructive_interference_size = 64;
+    constexpr std::size_t hardware_destructive_interference_size = 64;
+#endif
 
 #define NUM_CACHED_DATA 4
 // Set maximum number of kilobytes written per recording session to 1GB. This is a safeguard against
@@ -29,8 +41,13 @@
 #define MAX_KBYTES_WRITTEN_PER_SESSION (1000 * 1000)
 
 ImScopeDataWrapper scope_array[EXTRA_SCOPE_TYPES];
-volatile int slot_to_dump = -1;
-volatile int frame_to_dump = -1;
+typedef struct alignas(hardware_destructive_interference_size) ImScopeDumpInstruction {
+  int slot;
+  int frame;
+  const char *cause;
+} ImScopeDumpInstruction;
+
+std::atomic<ImScopeDumpInstruction> dump_instruction;
 
 extern "C" void imscope_record_autoinit(void *dataptr)
 {
@@ -44,6 +61,8 @@ extern "C" void imscope_record_autoinit(void *dataptr)
     scope_array[i].data.meta = {-1, -1};
   }
 
+  dump_instruction.store({-1, -1, nullptr});
+
   scopeData_t *scope = (scopeData_t *)calloc(1, sizeof(scopeData_t));
   scope->copyData = copyDataThreadSafe;
   scope->tryLockScopeData = tryLockScopeData;
@@ -64,12 +83,11 @@ extern "C" void imscope_record_autoinit(void *dataptr)
   threadCreate(&thread, imscope_record_thread, dataptr, (char *)"imscope_record", -1, sched_get_priority_min(SCHED_RR));
 }
 
-void dumpScopeData(int slot, int frame) {
-  if (slot_to_dump == -1) {
-    LOG_E(PHY, "Will dump scope data\n");
-    frame_to_dump = frame;
-    slot_to_dump = slot;
-  }
+void dumpScopeData(int slot, int frame, const char *cause_string)
+{
+  ImScopeDumpInstruction to_dump = {slot, frame, cause_string};
+  ImScopeDumpInstruction expected = {-1, -1, nullptr};
+  dump_instruction.compare_exchange_weak(expected, to_dump);
 }
 
 void *imscope_record_thread(void *data_void_ptr)
@@ -87,15 +105,26 @@ void *imscope_record_thread(void *data_void_ptr)
   } else {
     LOG_A(PHY, "ImScope recorder thread starts, recording data to cwd\n");
   }
+
+  std::string program_name(program_invocation_name);
+  size_t last_slash = program_name.find_last_of("/");
+  if (last_slash != std::string::npos) {
+    program_name = program_name.substr(last_slash + 1);
+  }
+
   ImScopeData scope_cache[EXTRA_SCOPE_TYPES][NUM_CACHED_DATA];
   memset(scope_cache, 0, sizeof(scope_cache));
   uint scope_cache_index[EXTRA_SCOPE_TYPES] = {0};
   float kbytes_written = 0.0f;
   while (kbytes_written < MAX_KBYTES_WRITTEN_PER_SESSION) {
-    if (slot_to_dump != -1) {
-      std::string filename = std::string(path);
-      filename += has_dir ? "/" : "-";
-      filename += std::to_string(frame_to_dump) + std::string("-") + std::to_string(slot_to_dump) + std::string(".imscope");
+    ImScopeDumpInstruction dump = dump_instruction.load(std::memory_order::memory_order_relaxed);
+    if (dump.slot != -1) {
+      std::string filename;
+      if (has_dir) {
+        filename += std::string(path) + "/";
+      }
+      filename += program_name + std::string("-") + dump.cause + std::string("-");
+      filename += std::to_string(dump.frame) + std::string("-") + std::to_string(dump.slot) + std::string(".imscope");
       std::ofstream outf(filename, std::ios_base::out | std::ios_base::binary);
       if (!outf) {
         LOG_E(PHY, "Could not open file %s for writing\n", filename.c_str());
@@ -104,7 +133,7 @@ void *imscope_record_thread(void *data_void_ptr)
       for (auto i = 0U; i < EXTRA_SCOPE_TYPES; i++) {
         for (auto j = 0U; j < NUM_CACHED_DATA; j++) {
           ImScopeData &scope_data = scope_cache[i][j];
-          if (scope_data.meta.frame == frame_to_dump && scope_data.meta.slot == slot_to_dump) {
+          if (scope_data.meta.frame == dump.frame && scope_data.meta.slot == dump.slot) {
             scope_data.dump_to_file(outf);
           }
         }
@@ -113,7 +142,7 @@ void *imscope_record_thread(void *data_void_ptr)
       kbytes_written += pos / 1000.0f;
       outf.close();
       LOG_D(PHY, "ImScope done writing to %s\n", filename.c_str());
-      slot_to_dump = -1;
+      dump_instruction.store({-1, -1, nullptr}, std::memory_order::memory_order_relaxed);
     }
     for (auto i = 0U; i < EXTRA_SCOPE_TYPES; i++) {
       ImScopeDataWrapper &scope_data = scope_array[i];
diff --git a/openair1/PHY/TOOLS/phy_scope_interface.h b/openair1/PHY/TOOLS/phy_scope_interface.h
index def4eba959..1bdb52181a 100644
--- a/openair1/PHY/TOOLS/phy_scope_interface.h
+++ b/openair1/PHY/TOOLS/phy_scope_interface.h
@@ -112,7 +112,7 @@ typedef struct scopeData_s {
   bool (*tryLockScopeData)(enum scopeDataType type, int elementSz, int colSz, int lineSz, metadata *meta);
   void (*copyDataUnsafeWithOffset)(enum scopeDataType type, void *dataIn, size_t size, size_t offset, int copy_index);
   void (*unlockScopeData)(enum scopeDataType type);
-  void (*dumpScopeData)(int slot, int frame);
+  void (*dumpScopeData)(int slot, int frame, const char *cause_string);
 } scopeData_t;
 
 int load_softscope(char *exectype, void *initarg);
@@ -158,10 +158,10 @@ void copyData(void *, enum scopeDataType type, void *dataIn, int elementSz, int
     scope_data->unlockScopeData(type); \
   }
 
-#define gNBdumpScopeData(gnb, slot, frame) \
+#define gNBdumpScopeData(gnb, slot, frame, cause_string) \
   scopeData_t *scope_data = (scopeData_t *)gnb->scopeData; \
   if (scope_data && scope_data->dumpScopeData) { \
-    scope_data->dumpScopeData(slot, frame); \
+    scope_data->dumpScopeData(slot, frame, cause_string); \
   }
 
 #define UEScopeHasTryLock(ue) \
@@ -183,10 +183,10 @@ void copyData(void *, enum scopeDataType type, void *dataIn, int elementSz, int
     scope_data->unlockScopeData(type); \
   }
 
-#define UEdumpScopeData(ue, slot, frame) \
+#define UEdumpScopeData(ue, slot, frame, cause_string) \
   scopeData_t *scope_data = (scopeData_t *)ue->scopeData; \
   if (scope_data && scope_data->dumpScopeData) { \
-    scope_data->dumpScopeData(slot, frame); \
+    scope_data->dumpScopeData(slot, frame, cause_string); \
   } \
 
 #ifdef __cplusplus
diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c
index 7d1bfca240..7020386c6e 100644
--- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c
+++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c
@@ -439,7 +439,7 @@ static int nr_ulsch_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, boo
             ulsch_harq->ulsch_pdu.rb_size,
             ulsch_harq->TBS);
       nr_fill_indication(gNB, ulsch->frame, ulsch->slot, ULSCH_id, ulsch->harq_pid, 1, 0, crc, pdu);
-      gNBdumpScopeData(gNB, ulsch->slot, ulsch->frame);
+      gNBdumpScopeData(gNB, ulsch->slot, ulsch->frame, "ULSCH_NACK");
       ulsch->handled = 1;
       LOG_D(PHY, "ULSCH %d in error\n",ULSCH_id);
       ulsch->last_iteration_cnt = ulsch->max_ldpc_iterations + 1; // Setting to max_ldpc_iterations + 1 is sufficient given that this variable is only used for checking for failure
@@ -870,7 +870,7 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N
           nfapi_nr_rx_data_pdu_t *pdu = &UL_INFO->rx_ind.pdu_list[UL_INFO->rx_ind.number_of_pdus++];
           nr_fill_indication(gNB, frame_rx, slot_rx, ULSCH_id, ulsch->harq_pid, 1, 1, crc, pdu);
           pusch_DTX++;
-          gNBdumpScopeData(gNB, ulsch->slot, ulsch->frame);
+          gNBdumpScopeData(gNB, ulsch->slot, ulsch->frame, "ULSCH_DTX");
           continue;
         }
       } else {
-- 
2.26.2