LCOV - code coverage report
Current view: top level - felix-star/src - statistics.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 82 209 39.2 %
Date: 2025-06-10 03:23:28 Functions: 7 8 87.5 %

          Line data    Source code
       1             : #include <stdio.h>
       2             : #include <stdlib.h>
       3             : #include <sys/stat.h>
       4             : #include <sys/types.h>
       5             : #include <fcntl.h>
       6             : #include <unistd.h>
       7             : #include <errno.h>
       8             : #include <string.h>
       9             : #include <signal.h>
      10             : 
      11             : #include "statistics.h"
      12             : #include "log.h"
      13             : #include "fifo.h"
      14             : #include "felix/felix_fid.h"
      15             : #include "netio/netio.h"
      16             : #include "flx_api.h"
      17             : #include "app.h"
      18             : 
      19             : #include "jWrite.h"
      20             : 
      21             : const char* requested_regs[5] = {"TTC_L1ID_MONITOR", "TTC_ECR_MONITOR_VALUE", "TTC_TTYPE_MONITOR_VALUE", "TTC_BCR_PERIODICITY_MONITOR_VALUE", "TTC_BCR_COUNTER_VALUE"};
      22             : const char* is_vars[5] = {"xl1id", "ecrs_from_ttc", "ttype_from_ttc", "bcr_mismatch", "bcr"};
      23             : int available_regs[5] = {0,0,0,0,0};
      24             : uint64_t current_bcr_mismatch = 0;
      25             : int bcr_periodicity_available = 0;
      26             : 
      27          46 : void statistics_init(struct felix_statistics* stats, char* fifoname, netio_tag_t fid, struct flx* flx, struct config* cfg, struct cmem_buffer* buf)
      28             : {
      29          46 :   memset(stats->buf, 0, STATISTICS_MAX_SIZE);
      30             : 
      31          46 :   stats->flx = flx;
      32          46 :   stats->cfg = cfg;
      33          46 :   stats->buffer = buf;
      34             :  
      35          46 :   if (fifoname){
      36           2 :     stats->fifoname = strdup(fifoname);
      37           2 :     stats->fid = fid;
      38           2 :     stats->fd = open_fifo(fifoname);
      39           2 :     int rc;
      40           2 :     int pipe_sz = fcntl(stats->fd, F_SETPIPE_SZ, STATISTICS_MAX_SIZE);
      41           2 :     LOG_INFO("Maximum stats buffer size = %d", pipe_sz);
      42           2 :     const char sep[2] = ".";
      43           2 :     rc = gethostname(stats->hostname_long, 256);
      44           2 :     if (rc < 0) {memset(stats->hostname_long, '\0', 256);}
      45           2 :     else {stats->hostname = strtok(stats->hostname_long, sep);}
      46             :   }
      47          46 : }
      48             : 
      49             : 
      50          44 : void statistics_reg_init(struct felix_statistics* stats){
      51         264 :   for(uint i = 0; i < 5; i++){
      52         220 :     if (flx_cfg_check_option(stats->flx, requested_regs[i])){
      53           0 :       available_regs[i] = 1;
      54             :     }
      55             :   }
      56          44 :   if(stats->cfg->warn_bcr_period && flx_cfg_check_option(stats->flx, "TTC_BCR_PERIODICITY_MONITOR_VALUE")){
      57           0 :     bcr_periodicity_available = 1;
      58             :   }
      59          44 : }
      60             : 
      61             : 
      62          44 : void statistics_mode_init(struct felix_statistics* stats){
      63          44 :   stats->mode = flx_get_mode(stats->flx);
      64          44 : }
      65             : 
      66             : 
      67           0 : double get_dma_occupancy(struct felix_statistics* stats){
      68           0 :   uint64_t rd_ptr = (uint64_t)flx_get_read_ptr(stats->flx, stats->buffer);
      69           0 :   uint64_t wr_ptr = (uint64_t)flx_get_write_ptr(stats->flx, stats->buffer);
      70           0 :   int even_dma_bits = flx_dma_cmp_even_bits(stats->flx);
      71           0 :   uint64_t size = stats->buffer->size;
      72           0 :   double available;
      73           0 :   if (wr_ptr == rd_ptr){
      74           0 :     available = even_dma_bits ? stats->buffer->size : 0;
      75           0 :   } else if (wr_ptr > rd_ptr){
      76           0 :     available = even_dma_bits ? (size - (wr_ptr - rd_ptr)) : -1073741824.;
      77             :   } else {
      78           0 :     available = !even_dma_bits ? (rd_ptr - wr_ptr) : -1073741824.;
      79             :   }
      80           0 :   return (available/1024./1024.);
      81             : }
      82             : 
      83             : 
      84         515 : void statistics_write_tohost(struct felix_statistics* stats, struct tohost_stats_data* data, struct elinktable* elinktable)
      85             : {
      86             :   // Calculate the duration
      87         515 :   struct timespec current_ts;
      88         515 :   clock_gettime(CLOCK_MONOTONIC_RAW, &current_ts);
      89         515 :   double seconds = current_ts.tv_sec - data->last_ts.tv_sec
      90         515 :                    + 1e-9*(current_ts.tv_nsec - data->last_ts.tv_nsec);
      91             : 
      92             :   //if no fifo just compute the rates and reset counters
      93         515 :   if (!stats->fifoname){
      94        1312 :     for(unsigned i=0; i<elinktable->num_elinks; i++) {
      95         797 :       local_elink_t elink = elinktable->elinks[i];
      96         797 :       struct elink_entry* entry = &elinktable->entries[elink];
      97         797 :       entry->received_chunk_rate =
      98         797 :           (entry->counters.received_chunks - entry->last_counters.received_chunks)/1000./seconds;
      99         797 :       entry->last_counters.received_chunks = entry->counters.received_chunks;
     100             :     }
     101         515 :     data->last_ts = current_ts;
     102         515 :     return;
     103             :   }
     104             : 
     105           0 :   char ts[72];
     106           0 :   struct timespec now;
     107           0 :   clock_gettime(CLOCK_REALTIME, &now);
     108           0 :   struct tm *tm = localtime(&now.tv_sec);
     109             :   // sprintf(ts, "%02d/%02d/%04d %02d:%02d:%02d", tm->tm_mday, tm->tm_mon + 1, tm->tm_year+1900, tm->tm_hour, tm->tm_min, tm->tm_sec);
     110           0 :   strftime(ts, 72, "%d/%m/%Y %H:%M:%S", tm);
     111             : 
     112           0 :   char sfid[30];
     113           0 :   char timestamp[30];
     114           0 :   char device[30];
     115           0 :   char gfid[30];
     116             : 
     117             :   //BUSY status
     118           0 :   uint64_t busy = flx_cfg_get_option(stats->flx, "TTC_DEC_CTRL_BUSY_OUTPUT_STATUS");
     119             : 
     120             :   //ERS message for non-periodic BCR
     121           0 :   if(bcr_periodicity_available){
     122           0 :     current_bcr_mismatch = flx_cfg_get_option(stats->flx, "TTC_BCR_PERIODICITY_MONITOR_VALUE");
     123             :   }
     124           0 :   struct jWriteControl jwc;
     125           0 :   snprintf(timestamp, 30, "%lu.%09lu", now.tv_sec, now.tv_nsec);
     126           0 :   snprintf(device, 30, "%d", stats->cfg->device);
     127           0 :   snprintf(gfid, 30, "%lu", stats->fid);
     128             :   // GENERAL STATISTICS /////////////
     129             :   // STATISTICS_MAX_SIZE-1 to guarantee null-terminmation (jWrite does not account for it)
     130           0 :   jwOpen(&jwc, stats->buf, STATISTICS_MAX_SIZE-1, JW_OBJECT, JW_COMPACT);
     131           0 :   stats->buf[STATISTICS_MAX_SIZE-1] = 0; //clean possible newline from previous round
     132           0 :   jwObj_ulong(&jwc, "fid", stats->fid);
     133           0 :   jwObj_string(&jwc, "hostname", stats->hostname);
     134           0 :   jwObj_string(&jwc, "device", device);
     135           0 :   jwObj_string(&jwc, "ts", ts);
     136           0 :   jwObj_string(&jwc, "timestamp", timestamp);
     137           0 :   jwObj_string(&jwc, "app", "tohost");
     138             : 
     139             :   // GLOBAL
     140           0 :   jwObj_object(&jwc, "global");
     141           0 :     jwObj_double(&jwc, "throughput[Gbps]",
     142           0 :         (data->counters.bytes_processed - data->last_counters.bytes_processed)*8/1000./1000./1000./seconds); //Gb/s
     143           0 :     data->last_counters.bytes_processed = data->counters.bytes_processed;
     144           0 :     jwObj_double(&jwc, "blockrate[kHz]",
     145           0 :         (data->counters.blocks_processed - data->last_counters.blocks_processed)/1000./seconds); //kBlocks/s
     146           0 :     data->last_counters.blocks_processed = data->counters.blocks_processed;
     147           0 :     jwObj_double(&jwc, "chunkrate[kHz]",
     148           0 :         (data->counters.chunks_processed - data->last_counters.chunks_processed)/1000./seconds); //kChunks/s
     149           0 :     data->last_counters.chunks_processed = data->counters.chunks_processed;
     150           0 :     jwObj_ulong(&jwc, "interrupts", (data->counters.interrupts - data->last_counters.interrupts));
     151           0 :     data->last_counters.interrupts = data->counters.interrupts;
     152             :     //netio completion objects
     153           0 :     jwObj_ulong(&jwc, "daq_co", app.fm_socket.completion_stack.available_objects);
     154           0 :     jwObj_ulong(&jwc, "dcs_co", app.dcs_usocket.completion_stack.available_objects);
     155           0 :     jwObj_ulong(&jwc, "ttc_co", 0);
     156             :     //netio pages
     157           0 :     jwObj_ulong(&jwc, "daq_pages", app.daq_min_pages);
     158           0 :     jwObj_ulong(&jwc, "dcs_pages", app.dcs_min_pages);
     159           0 :     jwObj_ulong(&jwc, "ttc_pages", app.ttc_min_pages);
     160           0 :     jwObj_ulong(&jwc, "daq_subs", stats->daq_sub_table->num_subscriptions);
     161           0 :     jwObj_ulong(&jwc, "dcs_subs", (stats->dcs_sub_table == NULL) ? 0 : stats->dcs_sub_table->num_subscriptions);
     162           0 :     jwObj_ulong(&jwc, "buffer_events",
     163           0 :         (data->counters.buffer_available_events - data->last_counters.buffer_available_events));
     164           0 :     data->last_counters.buffer_available_events = data->counters.buffer_available_events;
     165           0 :     jwObj_ulong(&jwc, "polls", (data->counters.polls - data->last_counters.polls));
     166           0 :     data->last_counters.polls = data->counters.polls;
     167           0 :     jwObj_ulong(&jwc, "bcr_periodicity", current_bcr_mismatch);
     168           0 :     jwObj_double(&jwc, "available_buf", get_dma_occupancy(stats));
     169           0 :     jwObj_ulong(&jwc, "busy", busy);
     170             : 
     171           0 :     for (int i =0; i < 5; i++){
     172           0 :       if(available_regs[i]){
     173           0 :         jwObj_ulong(&jwc, is_vars[i], flx_cfg_get_option(stats->flx, requested_regs[i]));
     174             :       } else {
     175           0 :         jwObj_ulong(&jwc, is_vars[i], 0);
     176             :       }
     177             :     }
     178           0 :   jwEnd(&jwc);
     179             : 
     180             :   //PER-ELINK
     181           0 :   jwObj_object(&jwc, "stats");
     182           0 :   jwObj_array(&jwc, "elink");
     183           0 :     jwArr_string(&jwc, "chunks");
     184           0 :     jwArr_string(&jwc, "rate"); //kHz
     185           0 :     jwArr_string(&jwc, "dropped");
     186           0 :     jwArr_string(&jwc, "largest_size");
     187           0 :     jwArr_string(&jwc, "avg_size");
     188           0 :     jwArr_string(&jwc, "fw_error");
     189           0 :     jwArr_string(&jwc, "sw_error");
     190           0 :     jwArr_string(&jwc, "fw_trunc");
     191           0 :     jwArr_string(&jwc, "sw_trunc");
     192           0 :     jwArr_string(&jwc, "fw_crc");
     193           0 :   jwEnd(&jwc);
     194             : 
     195           0 :   for(unsigned i=0; i<elinktable->num_elinks; i++) {
     196           0 :     local_elink_t elink = elinktable->elinks[i];
     197           0 :     struct elink_entry* entry = &elinktable->entries[elink];
     198             :     // NOTE: stats->fid is the STATS elink. The actual elink is from the elink table. It may have streams
     199             :     // but these are not counted individually so we set streams to 0. Other the info is from elink_entry.
     200           0 :     uint64_t fid = get_fid_from_ids(stats->cfg->did, stats->cfg->cid, elinktable->elinks[i], 0, get_vid(stats->fid), is_to_flx(stats->fid), entry->is_virtual);
     201           0 :     entry->received_chunk_rate =
     202           0 :         (entry->counters.received_chunks - entry->last_counters.received_chunks)/1000./seconds;
     203             : 
     204           0 :     snprintf(sfid, 30, "0x%lx", fid);
     205           0 :     jwObj_array(&jwc, sfid);
     206           0 :       uint64_t chunks = (entry->counters.processed_chunks - entry->last_counters.processed_chunks);
     207           0 :       uint64_t size = (entry->counters.total_chunksize - entry->last_counters.total_chunksize);
     208           0 :       entry->last_counters.total_chunksize = entry->counters.total_chunksize;
     209           0 :       entry->last_counters.processed_chunks = entry->counters.processed_chunks;
     210           0 :       jwArr_ulong(&jwc, entry->counters.processed_chunks);
     211           0 :       jwArr_double(&jwc, chunks/1000./seconds); //kHz
     212           0 :       jwArr_ulong(&jwc, entry->counters.dropped);
     213           0 :       jwArr_ulong(&jwc, entry->counters.largest_chunksize);
     214           0 :       entry->counters.largest_chunksize = 0;
     215             : 
     216           0 :       jwArr_double(&jwc, chunks ? size/chunks : 0);
     217           0 :       jwArr_ulong(&jwc, entry->decoder.counters.fw_error_chunks);
     218           0 :       jwArr_ulong(&jwc, entry->decoder.counters.sw_error_chunks);
     219           0 :       jwArr_ulong(&jwc, entry->decoder.counters.fw_trunc_chunks);
     220           0 :       jwArr_ulong(&jwc, entry->decoder.counters.sw_trunc_chunks);
     221           0 :       if(stats->mode == FLX_MODE_FULL){
     222           0 :         jwArr_ulong(&jwc, entry->decoder.counters.fw_crc_chunks);
     223             :       } else{
     224           0 :         jwArr_ulong(&jwc, 0);
     225             :       }
     226           0 :     jwEnd(&jwc);
     227             :   }
     228             : 
     229           0 :   jwEnd(&jwc);
     230           0 :   int ret = jwClose(&jwc);
     231           0 :   if (ret == 0){
     232           0 :     int count = strlen(stats->buf);
     233             :     //replace null-termination with newline
     234           0 :     if (count >= STATISTICS_MAX_SIZE){
     235           0 :       LOG_ERR("Statistics output error. Trying to write newline at %d out of range.", count);
     236           0 :       return;
     237             :     }
     238           0 :     stats->buf[count] = '\n';
     239             :     //account for newline characted
     240           0 :     ++count;
     241           0 :     write(stats->fd, stats->buf, count);
     242             :   } else {
     243           0 :     LOG_ERR("Statistics output error: %s.", jwErrorToString(ret));
     244             :   }
     245             : 
     246           0 :   data->last_ts = current_ts;
     247             : }
     248             : 
     249             : 
     250           8 : void statistics_write_toflx(struct felix_statistics* stats, struct toflx_stats_data* data, struct timespec* current_ts)
     251             : {
     252             :   // Calculate the duration
     253           8 :   double const seconds = current_ts->tv_sec - data->last_ts.tv_sec + 1e-9 * (current_ts->tv_nsec - data->last_ts.tv_nsec);
     254             : 
     255             :   // Get current time
     256           8 :   struct timespec now;
     257           8 :   clock_gettime(CLOCK_REALTIME, &now);
     258             : 
     259           8 :   char ts[72];
     260           8 :   struct tm *tm = localtime(&now.tv_sec);
     261           8 :   strftime(ts, 72, "%d/%m/%Y %H:%M:%S", tm);
     262             : 
     263           8 :   char timestamp[30];
     264           8 :   char device[30];
     265             : 
     266           8 :   struct jWriteControl jwc;
     267           8 :   snprintf(timestamp, 30, "%lu.%09lu", now.tv_sec, now.tv_nsec);
     268           8 :   snprintf(device, 30, "%d", stats->cfg->device);
     269             : 
     270             :   // STATISTICS_MAX_SIZE-1 to guarantee null-terminmation (jWrite does not account for it)
     271           8 :   jwOpen(&jwc, stats->buf, STATISTICS_MAX_SIZE-1, JW_OBJECT, JW_COMPACT);
     272           8 :   stats->buf[STATISTICS_MAX_SIZE-1] = 0; //clean possible newline from previous round
     273           8 :   jwObj_ulong(&jwc, "fid", stats->fid);
     274           8 :   jwObj_string(&jwc, "hostname", stats->hostname);
     275           8 :   jwObj_string(&jwc, "device", device);
     276           8 :   jwObj_string(&jwc, "app", "toflx");
     277           8 :   jwObj_string(&jwc, "ts", ts);
     278           8 :   jwObj_string(&jwc, "timestamp", timestamp);
     279           8 :   jwObj_double(&jwc, "throughput[Gbps]", data->bytes_received*8./1000./1000./1000./seconds);
     280           8 :   jwObj_double(&jwc, "msg_rate[kHz]", data->messages_received/1000./seconds);
     281           8 :   jwObj_double(&jwc, "dma_rate[kHz]", data->dma_transfers/1000./seconds);
     282           8 :   jwObj_ulong(&jwc, "msgs_received", data->toflx_received);
     283           8 :   int ret = jwClose(&jwc);
     284           8 :   if (ret == 0){
     285           8 :     int count = strlen(stats->buf);
     286             :     //replace null-termination with newline
     287           8 :     if (count >= STATISTICS_MAX_SIZE){
     288           0 :       LOG_ERR("Statistics output error. Trying to write newline at %d out of range.", count);
     289           0 :       return;
     290             :     }
     291           8 :     stats->buf[count] = '\n';
     292             :     //account for newline characted
     293           8 :     ++count;
     294           8 :     write(stats->fd, stats->buf, count);
     295             :   } else {
     296           0 :     LOG_ERR("Statistics output error: %s.", jwErrorToString(ret));
     297             :   }
     298             : }
     299             : 
     300             : 
     301          10 : void reset_toflx_stats_data(struct toflx_stats_data* data, struct timespec* current_ts)
     302             : {
     303          10 :   data->bytes_received = 0;
     304          10 :   data->messages_received = 0;
     305          10 :   data->dma_transfers = 0;
     306          10 :   data->toflx_received = 0;
     307          10 :   if (current_ts) {
     308          10 :     data->last_ts.tv_sec = current_ts->tv_sec;
     309          10 :     data->last_ts.tv_nsec = current_ts->tv_nsec;
     310             :   } else {
     311           0 :     data->last_ts.tv_sec = 0;
     312           0 :     data->last_ts.tv_nsec = 0;
     313             :   }
     314          10 : }
     315             : 
     316             : 
     317          46 : void statistics_free(struct felix_statistics* stats)
     318             : {
     319          46 :   if (stats->fifoname) free(stats->fifoname);
     320          46 : }

Generated by: LCOV version 1.0