LCOV - code coverage report
Current view: top level - disco/diag - fd_proc_interrupts.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 272 0.0 %
Date: 2026-09-17 04:28:31 Functions: 0 9 0.0 %

          Line data    Source code
       1             : #include "fd_proc_interrupts.h"
       2             : #include "../../util/tile/fd_tile.h" /* FD_TILE_MAX */
       3             : #include "../../util/io/fd_io.h" /* fd_io_buffered_istream */
       4             : #include <ctype.h> /* isdigit */
       5             : 
       6             : /* The skip_* helpers consume a run of bytes from is.  Each returns 1 if
       7             :    it stopped at a genuine terminator and 0 if it consumed every
       8             :    buffered byte without reaching a terminator. */
       9             : 
      10             : static int
      11           0 : skip_spaces( fd_io_buffered_istream_t * is ) {
      12           0 :   char const * peek    = fd_io_buffered_istream_peek   ( is );
      13           0 :   ulong        peek_sz = fd_io_buffered_istream_peek_sz( is );
      14           0 :   ulong j;
      15           0 :   for( j=0UL; j<peek_sz && peek[j]==' '; j++ ) {}
      16           0 :   fd_io_buffered_istream_skip( is, j );
      17           0 :   return j<peek_sz; /* complete iff we stopped on a non-space */
      18           0 : }
      19             : 
      20             : static int
      21           0 : skip_token( fd_io_buffered_istream_t * is ) {
      22           0 :   char const * peek    = fd_io_buffered_istream_peek   ( is );
      23           0 :   ulong        peek_sz = fd_io_buffered_istream_peek_sz( is );
      24           0 :   ulong j;
      25           0 :   for( j=0UL; j<peek_sz && peek[j]!=' ' && peek[j]!='\n'; j++ ) {}
      26           0 :   fd_io_buffered_istream_skip( is, j );
      27           0 :   return j<peek_sz; /* complete iff we stopped on a delimiter */
      28           0 : }
      29             : 
      30             : static int
      31           0 : skip_line( fd_io_buffered_istream_t * is ) {
      32           0 :   char const * peek    = fd_io_buffered_istream_peek   ( is );
      33           0 :   ulong        peek_sz = fd_io_buffered_istream_peek_sz( is );
      34           0 :   ulong j;
      35           0 :   for( j=0UL; j<peek_sz && peek[j]!='\n'; j++ ) {}
      36           0 :   int complete = j<peek_sz; /* found the '\n' */
      37           0 :   if( complete ) j++;       /* consume the '\n' */
      38           0 :   fd_io_buffered_istream_skip( is, j );
      39           0 :   return complete;
      40           0 : }
      41             : 
      42             : /* read_ulong consumes a decimal ulong from buffered unconsumed chars in
      43             :    is.  Returns ULONG_MAX if parse failed. */
      44             : 
      45             : static ulong
      46           0 : read_ulong( fd_io_buffered_istream_t * is ) {
      47           0 :   char const * peek    = fd_io_buffered_istream_peek   ( is );
      48           0 :   ulong        peek_sz = fd_io_buffered_istream_peek_sz( is );
      49           0 :   char num[ 21 ];
      50           0 :   peek_sz = fd_ulong_min( peek_sz, 20 );
      51           0 :   ulong num_sz;
      52           0 :   for( num_sz=0UL; num_sz<peek_sz && isdigit( peek[num_sz] ); num_sz++ ) {}
      53           0 :   memcpy( num, peek, num_sz );
      54           0 :   num[ num_sz ] = '\0';
      55           0 :   fd_io_buffered_istream_skip( is, num_sz );
      56           0 :   if( FD_UNLIKELY( num_sz==0 ) ) return ULONG_MAX;
      57           0 :   return fd_cstr_to_ulong( num );
      58           0 : }
      59             : 
      60             : static int
      61             : read_until( fd_io_buffered_istream_t * is,
      62             :            ulong                      min_sz,
      63           0 :            int (*skip)( fd_io_buffered_istream_t * is ) ) {
      64             : 
      65           0 :   ulong buf_sz = fd_io_buffered_istream_rbuf_sz( is );
      66           0 :   min_sz = fd_ulong_min( min_sz, buf_sz );
      67             : 
      68             :   /* Read until 'skip' leaves some bytes */
      69             : 
      70           0 :   for(;;) {
      71           0 :     if( skip( is ) ) break; /* unit complete */
      72           0 :     int err = fd_io_buffered_istream_fetch( is );
      73           0 :     if( err==0 ) {
      74           0 :       continue;
      75           0 :     }
      76           0 :     if( err==-1 ) {
      77           0 :       if( fd_io_buffered_istream_peek_sz( is )==0 ) return -1;
      78           0 :       break;
      79           0 :     }
      80           0 :     return err;
      81           0 :   }
      82             : 
      83             :   /* Read ahead */
      84             : 
      85           0 :   while( fd_io_buffered_istream_peek_sz( is )<min_sz ) {
      86           0 :     int err = fd_io_buffered_istream_fetch( is );
      87           0 :     if( err==-1 ) break;
      88           0 :     if( err!=0  ) return err;
      89           0 :   }
      90             : 
      91           0 :   return 0;
      92           0 : }
      93             : 
      94             : /* read_cpu_map reads the first line of /proc/interrupts mapping columns
      95             :    to CPUs.  Usually 0,1,2,3,... but has gaps for offline CPUs.  Also
      96             :    used for /proc/softirqs.  Formatted like this:
      97             :    "   CPU0  CPU1  CPU2  CPU3\n" */
      98             : 
      99             : static int
     100             : read_cpu_map(  fd_io_buffered_istream_t * is,
     101             :                ulong *                    out_col_cnt,
     102             :                ulong *                    out_cpu_cnt,
     103           0 :                ushort                     col_cpu[ FD_TILE_MAX ] ) {
     104           0 :   *out_col_cnt = 0UL;
     105           0 :   *out_cpu_cnt = 0UL;
     106             : 
     107             :   /* Read first line */
     108           0 :   ulong col_cnt = 0UL;
     109           0 :   do {
     110           0 :     int err = read_until( is, 16UL, skip_spaces );
     111           0 :     if( FD_UNLIKELY( err!=0 ) ) return err;
     112           0 :     char const * peek    = fd_io_buffered_istream_peek   ( is );
     113           0 :     ulong        peek_sz = fd_io_buffered_istream_peek_sz( is );
     114           0 :     if( peek[0]=='\n' ) break;
     115           0 :     if( peek_sz<4     ) break;
     116             : 
     117             :     /* Filter for 'CPU<num>' column */
     118           0 :     if( 0!=memcmp( peek, "CPU", 3 ) ) break;
     119           0 :     fd_io_buffered_istream_skip( is, 3 );
     120           0 :     peek    = fd_io_buffered_istream_peek   ( is );
     121           0 :     peek_sz = fd_io_buffered_istream_peek_sz( is );
     122             : 
     123             :     /* Parse number */
     124           0 :     ulong cpu_idx = read_ulong( is );
     125           0 :     if( FD_UNLIKELY( cpu_idx==ULONG_MAX ) ) {
     126           0 :       FD_LOG_WARNING(( "Failed to parse first line of /proc/interrupts" ));
     127           0 :       break;
     128           0 :     }
     129           0 :     if( FD_UNLIKELY( cpu_idx>=FD_TILE_MAX ) ) {
     130           0 :       FD_LOG_WARNING(( "Out of bounds: CPU%lu", cpu_idx ));
     131           0 :       break;
     132           0 :     }
     133             : 
     134           0 :     col_cpu[ col_cnt++ ] = (ushort)cpu_idx;
     135           0 :   } while( col_cnt<FD_TILE_MAX );
     136             : 
     137             :   /* Skip rest of line */
     138           0 :   int err = read_until( is, 0UL, skip_line );
     139           0 :   if( FD_UNLIKELY( err!=0 ) ) return err;
     140             : 
     141             :   /* Verify CPU map */
     142           0 :   if( FD_UNLIKELY( col_cnt==0UL ) ) {
     143           0 :     FD_LOG_WARNING(( "No CPUs found reading /proc/interrupts" ));
     144           0 :     return 0;
     145           0 :   }
     146           0 :   ulong cpu_cnt = 0UL;
     147           0 :   for( ulong col_idx=0UL; col_idx<col_cnt; col_idx++ ) {
     148           0 :     ulong next_cpu_cnt = col_cpu[ col_idx ]+1UL;
     149           0 :     if( FD_UNLIKELY( next_cpu_cnt<=cpu_cnt ) ) {
     150           0 :       FD_LOG_WARNING(( "CPU%u out of order reading /proc/interrupts", col_cpu[ col_idx ] ));
     151           0 :       return 0;
     152           0 :     }
     153           0 :     cpu_cnt = next_cpu_cnt;
     154           0 :   }
     155           0 :   if( FD_UNLIKELY( cpu_cnt>FD_TILE_MAX ) ) {
     156           0 :     FD_LOG_WARNING(( "Too many CPUs found reading /proc/interrupts" ));
     157           0 :     return 0;
     158           0 :   }
     159             : 
     160           0 :   *out_cpu_cnt = cpu_cnt;
     161           0 :   *out_col_cnt = col_cnt;
     162           0 :   return 0;
     163           0 : }
     164             : 
     165             : ulong
     166             : fd_proc_interrupts_read( int     fd,
     167             :                          ulong * opt_device_per_cpu,
     168             :                          ulong * opt_tlb_per_cpu,
     169           0 :                          ulong * opt_loc_per_cpu ) {
     170           0 :   fd_io_buffered_istream_t is[1];
     171           0 :   char buf[ 4096 ];
     172           0 :   fd_io_buffered_istream_init( is, fd, buf, sizeof(buf) );
     173             : 
     174             :   /* Read first line */
     175             : 
     176           0 :   ushort col_cpu[ FD_TILE_MAX ];
     177           0 :   ulong  col_cnt;
     178           0 :   ulong  cpu_cnt;
     179           0 :   int err = read_cpu_map( is, &col_cnt, &cpu_cnt, col_cpu );
     180           0 :   if( FD_UNLIKELY( err!=0 ) ) goto failed;
     181           0 :   if( FD_UNLIKELY( !col_cnt || !cpu_cnt ) ) return 0UL;
     182             : 
     183           0 :   for( ulong cpu=0UL; cpu<cpu_cnt; cpu++ ) {
     184           0 :     if( FD_LIKELY( opt_device_per_cpu ) ) opt_device_per_cpu[ cpu ] = 0UL;
     185           0 :     if( FD_LIKELY( opt_tlb_per_cpu    ) ) opt_tlb_per_cpu   [ cpu ] = 0UL;
     186           0 :     if( FD_LIKELY( opt_loc_per_cpu    ) ) opt_loc_per_cpu   [ cpu ] = 0UL;
     187           0 :   }
     188             : 
     189           0 :   if( FD_UNLIKELY( !opt_device_per_cpu && !opt_tlb_per_cpu && !opt_loc_per_cpu ) ) return cpu_cnt;
     190             : 
     191             :   /* Read interrupt table
     192             :      Device interrupt counters look like this:
     193             :      "  123:  41  42  43  44"
     194             :      Special interrupts look like this:
     195             :      "  NMI:   1   2   3   4" */
     196             : 
     197           0 :   for(;;) { /* each line */
     198             : 
     199             :     /* Read prefix */
     200           0 :     err = read_until( is, 64UL, skip_spaces );
     201           0 :     if( FD_UNLIKELY( err==-1 ) ) return cpu_cnt; /* end of input */
     202           0 :     if( FD_UNLIKELY( err!=0 ) ) goto failed;
     203           0 :     if( fd_io_buffered_istream_peek_sz( is )==0 ) return cpu_cnt;
     204           0 :     char const * prefix     = fd_io_buffered_istream_peek   ( is );
     205           0 :     ulong        prefix_max = fd_io_buffered_istream_peek_sz( is );
     206           0 :     int          row_kind   = 0;
     207           0 :     if( FD_LIKELY( opt_device_per_cpu && isdigit( prefix[0] ) ) ) {
     208           0 :       row_kind = 1;
     209           0 :     } else if( FD_UNLIKELY( opt_tlb_per_cpu && prefix_max>=4UL && fd_memeq( prefix, "TLB:", 4UL ) ) ) {
     210           0 :       row_kind = 2;
     211           0 :     } else if( FD_UNLIKELY( opt_loc_per_cpu && prefix_max>=4UL && fd_memeq( prefix, "LOC:", 4UL ) ) ) {
     212           0 :       row_kind = 3;
     213           0 :     } else {
     214           0 :       goto skip_line;
     215           0 :     }
     216             : 
     217           0 :     err = read_until( is, 0UL, skip_token );
     218           0 :     if( FD_UNLIKELY( err!=0 ) ) goto failed;
     219             : 
     220             :     /* Read interrupt counters */
     221           0 :     for( ulong col_idx=0UL; col_idx<col_cnt; col_idx++ ) {
     222           0 :       err = read_until( is, 21UL, skip_spaces );
     223           0 :       if( FD_UNLIKELY( err!=0 ) ) goto failed;
     224             : 
     225           0 :       ulong irq_cnt = read_ulong( is );
     226           0 :       irq_cnt = fd_ulong_if( irq_cnt!=ULONG_MAX, irq_cnt, 0UL );
     227           0 :       ulong cpu_idx = col_cpu[ col_idx ];
     228           0 :       if(      row_kind==1 ) opt_device_per_cpu[ cpu_idx ] += irq_cnt;
     229           0 :       else if( row_kind==2 ) opt_tlb_per_cpu   [ cpu_idx ]  = irq_cnt;
     230           0 :       else                   opt_loc_per_cpu   [ cpu_idx ]  = irq_cnt;
     231           0 :     }
     232             : 
     233           0 :   skip_line:
     234             :     /* Ignore rest of line */
     235           0 :     err = read_until( is, 0UL, skip_line );
     236           0 :     if( FD_UNLIKELY( err!=0 ) ) {
     237           0 :       if( err==-1 ) break;
     238           0 :       goto failed;
     239           0 :     }
     240             : 
     241           0 :   }
     242           0 :   return cpu_cnt;
     243             : 
     244           0 : failed:
     245           0 :   if( err!=0 ) {
     246           0 :     FD_LOG_WARNING(( "read failed (%i-%s)", err, fd_io_strerror( err ) ));
     247           0 :   }
     248           0 :   return 0UL;
     249           0 : }
     250             : 
     251             : ulong
     252             : fd_proc_softirqs_sum( int   fd,
     253           0 :                       ulong per_cpu[ FD_METRICS_ENUM_SOFTIRQ_CNT ][ FD_TILE_MAX ] ) {
     254           0 :   fd_io_buffered_istream_t is[1];
     255           0 :   char buf[ 4096 ];
     256           0 :   fd_io_buffered_istream_init( is, fd, buf, sizeof(buf) );
     257             : 
     258             :   /* Read first line */
     259             : 
     260           0 :   ushort col_cpu[ FD_TILE_MAX ];
     261           0 :   ulong  col_cnt;
     262           0 :   ulong  cpu_cnt;
     263           0 :   int err = read_cpu_map( is, &col_cnt, &cpu_cnt, col_cpu );
     264           0 :   if( FD_UNLIKELY( err!=0 ) ) goto failed;
     265           0 :   if( FD_UNLIKELY( !col_cnt || !cpu_cnt ) ) return 0UL;
     266           0 :   for( ulong i=0UL; i<FD_METRICS_ENUM_SOFTIRQ_CNT; i++ ) {
     267           0 :     for( ulong c=0UL; c<cpu_cnt; c++ ) per_cpu[ i ][ c ] = 0UL;
     268           0 :   }
     269             : 
     270             :   /* Read softirq table
     271             :      Looks like this:
     272             :      "   NET_TX:   1   2   3   5" */
     273             : 
     274           0 :   for(;;) { /* each line */
     275             : 
     276             :     /* Read prefix */
     277           0 :     err = read_until( is, 64UL, skip_spaces );
     278           0 :     if( FD_UNLIKELY( err==-1 ) ) return cpu_cnt; /* end of input */
     279           0 :     if( FD_UNLIKELY( err!=0 ) ) goto failed;
     280           0 :     if( fd_io_buffered_istream_peek_sz( is )==0 ) return cpu_cnt;
     281             : 
     282             :     /* Match prefix */
     283           0 :     int          kind       = FD_METRICS_ENUM_SOFTIRQ_V_OTHER_IDX;
     284           0 :     char const * prefix     = fd_io_buffered_istream_peek   ( is );
     285           0 :     ulong        prefix_max = fd_io_buffered_istream_peek_sz( is );
     286           0 :     if( prefix_max>=7 && fd_memeq( prefix, "NET_TX:", 7 ) ) {
     287           0 :       kind = FD_METRICS_ENUM_SOFTIRQ_V_NET_IDX;
     288           0 :     } else if( prefix_max>=7 && fd_memeq( prefix, "NET_RX:", 7 ) ) {
     289           0 :       kind = FD_METRICS_ENUM_SOFTIRQ_V_NET_IDX;
     290           0 :     } else if( prefix_max>=6 && fd_memeq( prefix, "BLOCK:", 6 ) ) {
     291           0 :       kind = FD_METRICS_ENUM_SOFTIRQ_V_DISK_IDX;
     292           0 :     }
     293           0 :     err = read_until( is, 0UL, skip_token );
     294           0 :     if( FD_UNLIKELY( err!=0 ) ) goto failed;
     295             : 
     296             :     /* Read interrupt counters */
     297           0 :     for( ulong col_idx=0UL; col_idx<col_cnt; col_idx++ ) {
     298           0 :       err = read_until( is, 21UL, skip_spaces );
     299           0 :       if( FD_UNLIKELY( err!=0 ) ) goto failed;
     300             : 
     301           0 :       ulong irq_cnt = read_ulong( is );
     302           0 :       irq_cnt = fd_ulong_if( irq_cnt!=ULONG_MAX, irq_cnt, 0UL );
     303           0 :       per_cpu[ kind ][ col_cpu[ col_idx ] ] += irq_cnt;
     304           0 :     }
     305             : 
     306             :     /* Ignore rest of line */
     307           0 :     err = read_until( is, 0UL, skip_line );
     308           0 :     if( FD_UNLIKELY( err!=0 ) ) {
     309           0 :       if( err==-1 ) break;
     310           0 :       goto failed;
     311           0 :     }
     312             : 
     313           0 :   }
     314           0 :   return cpu_cnt;
     315             : 
     316           0 : failed:
     317           0 :   if( err!=0 ) {
     318           0 :     FD_LOG_WARNING(( "read failed (%i-%s)", err, fd_io_strerror( err ) ));
     319           0 :   }
     320           0 :   return 0UL;
     321           0 : }
     322             : 
     323             : ulong
     324             : fd_proc_stat_irq_ticks( int   fd,
     325           0 :                         ulong per_cpu[ FD_TILE_MAX ] ) {
     326           0 :   fd_io_buffered_istream_t is[1];
     327           0 :   char buf[ 4096 ];
     328           0 :   fd_io_buffered_istream_init( is, fd, buf, sizeof(buf) );
     329             : 
     330           0 :   for( ulong cpu=0UL; cpu<FD_TILE_MAX; cpu++ ) {
     331           0 :     per_cpu[ cpu ] = 0UL;
     332           0 :   }
     333             : 
     334             :   /* Per-CPU rows look like this ("cpu" summary row has no digit):
     335             :      "cpu7 134688 9390 52162 76129767 4217 3283 2069 0 0 0"
     336             :      Fields after the cpuN token: user nice system idle iowait irq
     337             :      softirq steal guest guest_nice. */
     338             : 
     339           0 :   ulong cpu_cnt = 0UL;
     340           0 :   int   err;
     341           0 :   for(;;) {
     342           0 :     err = read_until( is, 64UL, skip_spaces );
     343           0 :     if( FD_UNLIKELY( err==-1 ) ) return cpu_cnt; /* end of input */
     344           0 :     if( FD_UNLIKELY( err!=0 ) ) goto failed;
     345           0 :     if( fd_io_buffered_istream_peek_sz( is )==0 ) return cpu_cnt;
     346             : 
     347           0 :     char const * prefix     = fd_io_buffered_istream_peek   ( is );
     348           0 :     ulong        prefix_max = fd_io_buffered_istream_peek_sz( is );
     349             : 
     350             :     /* Rows are ordered: "cpu", "cpu0".."cpuN", then non-cpu rows.  Stop
     351             :        at the first non-cpu row. */
     352           0 :     if( FD_UNLIKELY( prefix_max<4UL || !fd_memeq( prefix, "cpu", 3UL ) ) ) return cpu_cnt;
     353           0 :     if( FD_UNLIKELY( !isdigit( prefix[ 3 ] ) ) ) { /* "cpu" summary row */
     354           0 :       err = read_until( is, 0UL, skip_line );
     355           0 :       if( FD_UNLIKELY( err!=0 ) ) { if( err==-1 ) break; goto failed; }
     356           0 :       continue;
     357           0 :     }
     358             : 
     359           0 :     fd_io_buffered_istream_skip( is, 3UL );
     360           0 :     ulong cpu_idx = read_ulong( is );
     361           0 :     if( FD_UNLIKELY( cpu_idx==ULONG_MAX ) ) {
     362           0 :       FD_LOG_WARNING(( "failed to parse cpu index in /proc/stat" ));
     363           0 :       return 0UL;
     364           0 :     }
     365             : 
     366             :     /* Sum fields 6 (irq), 7 (softirq) and 8 (steal), 1-indexed after
     367             :        the cpuN token. */
     368           0 :     ulong ticks = 0UL;
     369           0 :     for( ulong field_idx=1UL; field_idx<=8UL; field_idx++ ) {
     370           0 :       err = read_until( is, 21UL, skip_spaces );
     371           0 :       if( FD_UNLIKELY( err!=0 ) ) goto failed;
     372             : 
     373           0 :       ulong val = read_ulong( is );
     374           0 :       val = fd_ulong_if( val!=ULONG_MAX, val, 0UL );
     375           0 :       if( field_idx>=6UL ) ticks += val;
     376           0 :     }
     377           0 :     if( FD_LIKELY( cpu_idx<FD_TILE_MAX ) ) {
     378           0 :       per_cpu[ cpu_idx ] = ticks;
     379           0 :       cpu_cnt = fd_ulong_max( cpu_cnt, cpu_idx+1UL );
     380           0 :     }
     381             : 
     382           0 :     err = read_until( is, 0UL, skip_line );
     383           0 :     if( FD_UNLIKELY( err!=0 ) ) {
     384           0 :       if( err==-1 ) break;
     385           0 :       goto failed;
     386           0 :     }
     387           0 :   }
     388           0 :   return cpu_cnt;
     389             : 
     390           0 : failed:
     391           0 :   if( err!=0 ) {
     392           0 :     FD_LOG_WARNING(( "read failed (%i-%s)", err, fd_io_strerror( err ) ));
     393           0 :   }
     394           0 :   return 0UL;
     395           0 : }

Generated by: LCOV version 1.14