LCOV - code coverage report
Current view: top level - disco/diag - fd_diag_tile.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 1357 0.0 %
Date: 2026-11-11 12:40:43 Functions: 0 46 0.0 %

          Line data    Source code
       1             : #define _GNU_SOURCE
       2             : 
       3             : #include "fd_diag_tile.h"
       4             : 
       5             : #include "../fd_clock_tile.h"
       6             : 
       7             : #include "../bundle/fd_bundle_tile.h"
       8             : #include "../metrics/fd_metrics.h"
       9             : #include "../stem/fd_stem.h"
      10             : #include "../topo/fd_topo.h"
      11             : #include "../topo/fd_cpu_topo.h"
      12             : #include "../../util/tile/fd_tile_private.h"
      13             : #include "../../util/io/fd_io.h"
      14             : 
      15             : #include <dirent.h>
      16             : #include <fcntl.h>
      17             : #include <errno.h>
      18             : #include <stdlib.h>
      19             : #include <sys/types.h> /* SEEK_SET */
      20             : #include <sys/stat.h>
      21             : #include <sys/syscall.h>
      22             : #include <sys/vfs.h>
      23             : #include <linux/futex.h>
      24             : #include <time.h>
      25             : #include <unistd.h>
      26             : 
      27             : #include "fd_proc_interrupts.h"
      28             : #include "generated/fd_diag_tile_seccomp.h"
      29             : 
      30             : /* Raw state values exported by the metrics (states= enums) */
      31             : FD_STATIC_ASSERT( FD_DIAG_BUNDLE_STATUS_DISCONNECTED==FD_METRICS_ENUM_DIAG_BUNDLE_STATE_V_DISCONNECTED_IDX, metrics_state );
      32             : FD_STATIC_ASSERT( FD_DIAG_BUNDLE_STATUS_CONNECTING==FD_METRICS_ENUM_DIAG_BUNDLE_STATE_V_CONNECTING_IDX, metrics_state );
      33             : FD_STATIC_ASSERT( FD_DIAG_BUNDLE_STATUS_CONNECTED==FD_METRICS_ENUM_DIAG_BUNDLE_STATE_V_CONNECTED_IDX, metrics_state );
      34             : FD_STATIC_ASSERT( FD_DIAG_BUNDLE_STATUS_SLEEPING==FD_METRICS_ENUM_DIAG_BUNDLE_STATE_V_SLEEPING_IDX, metrics_state );
      35             : FD_STATIC_ASSERT( FD_DIAG_BUNDLE_STATUS_DISABLED==FD_METRICS_ENUM_DIAG_BUNDLE_STATE_V_DISABLED_IDX, metrics_state );
      36             : 
      37             : /* Each procfs source is read at the rate its consumers need: the CPU
      38             :    regime split every report (100 ms), /proc/<tid>/sched (only feeds
      39             :    cumulative counters, ~10 us a read) once a second, /proc/interrupts
      40             :    (~5 ms on 96 CPUs) every five. */
      41             : 
      42           0 : #define REPORT_INTERVAL_MILLIS (100L)
      43           0 : #define SCHED_REPORT_INTERVAL_NANOS (1000000000L)
      44           0 : #define IRQ_REPORT_INTERVAL_NANOS (5000000000L)
      45           0 : #define SYSTEM_REPORT_INTERVAL_NANOS (30000000000L)
      46             : 
      47             : /* Hardware sensors, read with the IRQ report */
      48             : #define DIAG_SENSOR_MAX (128UL)
      49           0 : #define DIAG_RAPL_MAX   (8UL)
      50           0 : #define DIAG_EDAC_MC_MAX (64UL) /* memory controllers */
      51             : 
      52           0 : #define DIAG_SENSOR_CPU_PACKAGE (0U)
      53           0 : #define DIAG_SENSOR_NIC         (1U)
      54           0 : #define DIAG_SENSOR_NVME        (2U)
      55           0 : #define DIAG_SENSOR_POWER       (3U)
      56           0 : #define DIAG_SENSOR_ECC_CE      (4U)
      57           0 : #define DIAG_SENSOR_ECC_UE      (5U)
      58             : #define DIAG_SENSOR_KIND_CNT    (6U)
      59             : 
      60           0 : #define DIAG_WKSP_TILE_IDX_SHARED (ULONG_MAX)
      61             : 
      62             : 
      63             : struct fd_diag_tile {
      64             :   fd_clock_tile_t clock[1];
      65             :   long next_report_nanos;
      66             :   long next_sched_report_nanos;
      67             :   long next_irq_report_nanos;
      68             : 
      69             :   ulong tile_cnt;
      70             :   int is_voting;
      71             : 
      72             :   struct {
      73             :     ulong bundle_tile_idx[ FD_TILE_MAX ];
      74             :     ulong bundle_cnt;
      75             :     ulong shred_tile_idx[ FD_TILE_MAX ];
      76             :     ulong shred_cnt;
      77             :     ulong votor_idx;
      78             :     ulong replay_idx;
      79             :   } tiles;
      80             : 
      81             :   ulong starttime_nanos[ FD_TILE_MAX ];
      82             :   long  first_seen_died[ FD_TILE_MAX ];
      83             : 
      84             :   int stat_fds[ FD_TILE_MAX ];
      85             :   int sched_fds[ FD_TILE_MAX ];
      86             :   int schedstat_fds[ FD_TILE_MAX ];
      87             : 
      88             :   ulong       irq_cnt[ FD_METRICS_ENUM_SOFTIRQ_TYPE_CNT ][ FD_TILE_MAX ];
      89             :   fd_cpuset_t cpu_live_tile[ fd_cpuset_word_cnt ]; /* CPUs hosting a live pinned tile */
      90             :   ulong       on_tile_softirq[ FD_METRICS_ENUM_SOFTIRQ_TYPE_CNT ]; /* accumulated while the CPU hosts a live tile */
      91             :   ulong       on_tile_device_irq;
      92             :   int         proc_interrupts_fd;
      93             :   int         proc_softirqs_fd;
      94             :   int         proc_stat_fd;
      95             :   int         proc_meminfo_fd;
      96             :   int         proc_loadavg_fd;
      97             :   int         proc_vmstat_fd;
      98             :   int         psi_fd[ 3 ];       /* /proc/pressure/{cpu,memory,io}, -1 if absent */
      99             :   int         disk_fd;           /* /sys/block/<dev>/stat of the accounts disk, -1 if none */
     100             :   ulong       disk_ms[ 3 ];      /* write, busy and queue ms, carried across the 32-bit wrap */
     101             :   ulong       disk_ms_base[ 3 ]; /* the same at diag start */
     102             :   ulong       host_cpu_baseline[ FD_METRICS_ENUM_SYSTEM_CPU_STATE_CNT ]; /* /proc/stat ticks at diag start */
     103             :   ulong       host_cpu_ticks   [ FD_METRICS_ENUM_SYSTEM_CPU_STATE_CNT ]; /* highest ticks since then published */
     104             :   ulong       device_irq_boot    [ FD_TILE_MAX ]; /* /proc/interrupts at diag start */
     105             :   ulong       device_irq_baseline[ FD_TILE_MAX ]; /* at the last sweep */
     106             :   ulong       tlb_baseline[ FD_TILE_MAX ];
     107             :   ulong       loc_baseline[ FD_TILE_MAX ];
     108             :   ulong       irq_ticks_baseline[ FD_TILE_MAX ];
     109             :   ulong       softirq_boot    [ FD_METRICS_ENUM_SOFTIRQ_TYPE_CNT ][ FD_TILE_MAX ];
     110             :   ulong       softirq_baseline[ FD_METRICS_ENUM_SOFTIRQ_TYPE_CNT ][ FD_TILE_MAX ];
     111             : 
     112             :   ulong volatile * metrics    [ FD_TILE_MAX ];
     113             :   ushort           cpu_to_tile[ FD_TILE_MAX ]; /* the one live tile pinned to the CPU, USHORT_MAX if none or shared */
     114             :   ushort           tile_cpu   [ FD_TILE_MAX ]; /* pinned CPU of each tile, USHORT_MAX if floating */
     115             :   fd_cpuset_t      irq_rebase [ fd_cpuset_word_cnt ]; /* owner changed, irq_metrics takes new baselines */
     116             :   fd_cpuset_t      tick_rebase[ fd_cpuset_word_cnt ]; /* owner changed, interrupt_metrics takes new baselines */
     117             :   ulong            owner_irq_baseline [ FD_TILE_MAX ];   /* per-CPU tile baselines, start at the boot baselines */
     118             :   ulong            owner_tlb_baseline [ FD_TILE_MAX ];
     119             :   ulong            owner_loc_baseline [ FD_TILE_MAX ];
     120             :   ulong            owner_tick_baseline[ FD_TILE_MAX ];
     121             : 
     122             :   struct {
     123             :     ulong cnt;
     124             :     int   fd  [ DIAG_SENSOR_MAX ]; /* hwmon inputs and EDAC counts */
     125             :     uint  kind[ DIAG_SENSOR_MAX ]; /* DIAG_SENSOR_* */
     126             : 
     127             :     ulong rapl_cnt;
     128             :     int   rapl_fd   [ DIAG_RAPL_MAX ]; /* powercap package energy_uj */
     129             :     ulong rapl_range[ DIAG_RAPL_MAX ]; /* energy_uj wraps at this */
     130             :     ulong rapl_prev [ DIAG_RAPL_MAX ];
     131             :     long  rapl_prev_nanos;             /* 0 until a baseline is taken */
     132             :   } sensor;
     133             : 
     134             :   int    gui_enabled;
     135             :   long   next_system_report_nanos;
     136             :   ushort numa_entry_idx[ FD_DIAG_SYSTEM_TILE_MAX ][ FD_DIAG_SYSTEM_NUMA_MAX ];
     137             :   struct {
     138             :     ulong cnt;
     139             :     struct {
     140             :       char  name[ FD_SHMEM_NAME_MAX ];
     141             :       ulong tile_idx;
     142             :       ulong numa_idx;
     143             :       ulong bytes;
     144             :       ushort numa_slot;
     145             :     } wksp[ FD_TOPO_MAX_WKSPS ];
     146             :     ushort stack_numa_slot[ FD_DIAG_SYSTEM_TILE_MAX ];
     147             :   } memory;
     148             :   struct {
     149             :     ulong cnt;
     150             :     struct {
     151             :       ushort idx;
     152             :       int    meminfo_fd;
     153             :     } node[ FD_DIAG_SYSTEM_NUMA_MAX ];
     154             :   } numa;
     155             : 
     156             :   struct {
     157             :     void * mem;
     158             :     ulong  idx;
     159             :     ulong  chunk0;
     160             :     ulong  wmark;
     161             :     ulong  chunk;
     162             :   } system_out;
     163             :   fd_diag_system_resources_t system_resources;
     164             : 
     165             :   struct {
     166             :     int   fd;     /* O_PATH reference to an anonymous regular file */
     167             :     ulong mnt_id; /* mount ID from /proc/self/fdinfo */
     168             :     char  path[ PATH_MAX ];
     169             :   } mounts[ FD_DIAG_SYSTEM_FILE_MAX ];
     170             :   ulong mount_cnt;
     171             : 
     172             :   struct {
     173             :     char  path[ PATH_MAX ];
     174             :     uint  category;
     175             :     uint  mount_idx;
     176             :     int   data_fd;
     177             :     ulong volatile * metric;
     178             :   } files[ FD_DIAG_SYSTEM_FILE_MAX ];
     179             :   ulong file_cnt;
     180             : 
     181             :   struct {
     182             :     ulong prev_reset_slot;
     183             :     long  reset_slot_changed_ns;
     184             :     ulong prev_turbine_slot;
     185             :     long  turbine_slot_changed_ns;
     186             : 
     187             :     ulong snapshot_turbine_bytes;
     188             :     ulong snapshot_repair_bytes;
     189             :     long  byte_snapshot_ns;
     190             :     int   repair_outpacing;
     191             :   } check_engine;
     192             : };
     193             : 
     194             : typedef struct fd_diag_tile fd_diag_tile_t;
     195             : 
     196             : FD_FN_CONST static inline ulong
     197           0 : scratch_align( void ) {
     198           0 :   return alignof(fd_diag_tile_t);
     199           0 : }
     200             : 
     201             : FD_FN_PURE static inline ulong
     202           0 : scratch_footprint( fd_topo_tile_t const * tile ) {
     203           0 :   (void)tile;
     204           0 :   return sizeof(fd_diag_tile_t);
     205           0 : }
     206             : 
     207             : static void
     208           0 : during_housekeeping( fd_diag_tile_t * ctx ) {
     209           0 :   if( FD_UNLIKELY( fd_clock_tile_recal_due( ctx->clock ) ) ) fd_clock_tile_recal( ctx->clock );
     210           0 : }
     211             : 
     212             : static inline long
     213           0 : next_deadline( fd_diag_tile_t * ctx ) {
     214           0 :   return fd_clock_tile_wallclock_to_tickcount( ctx->clock, ctx->next_report_nanos );
     215           0 : }
     216             : 
     217             : static int
     218             : read_stat_file( int              fd,
     219             :                 ulong            ns_per_tick,
     220           0 :                 volatile ulong * metrics ) {
     221           0 :   if( FD_UNLIKELY( -1==lseek( fd, 0, SEEK_SET ) ) ) FD_LOG_ERR(( "lseek failed (%i-%s)", errno, strerror( errno ) ));
     222             : 
     223           0 :   char contents[ 4096 ] = {0};
     224           0 :   ulong contents_len = 0UL;
     225             : 
     226           0 :   while( 1 ) {
     227           0 :     if( FD_UNLIKELY( contents_len>=sizeof( contents ) ) ) FD_LOG_ERR(( "stat contents overflow" ));
     228           0 :     long n = read( fd, contents + contents_len, sizeof( contents ) - contents_len );
     229           0 :     if( FD_UNLIKELY( -1==n ) ) {
     230           0 :       if( FD_UNLIKELY( errno==ESRCH ) ) return 1;
     231           0 :       FD_LOG_ERR(( "read failed (%i-%s)", errno, strerror( errno ) ));
     232           0 :     }
     233           0 :     if( FD_LIKELY( 0==n ) ) break;
     234           0 :     contents_len += (ulong)n;
     235           0 :   }
     236             : 
     237             :   /* Parse stat file: fields are space-separated.
     238             :      Field 10 (1-indexed) = minflt, field 12 = majflt,
     239             :      field 14 = utime, field 15 = stime (all in clock ticks). */
     240           0 :   char * saveptr;
     241           0 :   char * token = strtok_r( contents, " ", &saveptr );
     242           0 :   ulong field_idx = 0UL;
     243             : 
     244           0 :   while( token ) {
     245           0 :     if( FD_UNLIKELY( 9UL==field_idx ) ) {
     246           0 :       char * endptr;
     247           0 :       ulong minflt = strtoul( token, &endptr, 10 );
     248           0 :       if( FD_UNLIKELY( *endptr!='\0' || minflt==ULONG_MAX ) ) FD_LOG_ERR(( "strtoul failed for minflt" ));
     249           0 :       metrics[ FD_METRICS_COUNTER_TILE_PAGE_FAULT_MINOR_OFF ] = minflt;
     250           0 :     } else if( FD_UNLIKELY( 11UL==field_idx ) ) {
     251           0 :       char * endptr;
     252           0 :       ulong majflt = strtoul( token, &endptr, 10 );
     253           0 :       if( FD_UNLIKELY( *endptr!='\0' || majflt==ULONG_MAX ) ) FD_LOG_ERR(( "strtoul failed for majflt" ));
     254           0 :       metrics[ FD_METRICS_COUNTER_TILE_PAGE_FAULT_MAJOR_OFF ] = majflt;
     255           0 :     } else if( FD_UNLIKELY( 13UL==field_idx ) ) {
     256           0 :       char * endptr;
     257           0 :       ulong utime_ticks = strtoul( token, &endptr, 10 );
     258           0 :       if( FD_UNLIKELY( *endptr!='\0' || utime_ticks==ULONG_MAX ) ) FD_LOG_ERR(( "strtoul failed for utime" ));
     259           0 :       metrics[ FD_METRICS_COUNTER_TILE_THREAD_DURATION_NANOS_USER_OFF ] = utime_ticks*ns_per_tick;
     260           0 :     } else if( FD_UNLIKELY( 14UL==field_idx ) ) {
     261           0 :       char * endptr;
     262           0 :       ulong stime_ticks = strtoul( token, &endptr, 10 );
     263           0 :       if( FD_UNLIKELY( *endptr!='\0' || stime_ticks==ULONG_MAX ) ) FD_LOG_ERR(( "strtoul failed for stime" ));
     264           0 :       metrics[ FD_METRICS_COUNTER_TILE_THREAD_DURATION_NANOS_SYSTEM_OFF ] = stime_ticks*ns_per_tick;
     265           0 :     } else if( FD_UNLIKELY( 38UL==field_idx ) ) {
     266           0 :       char * endptr;
     267           0 :       ulong last_cpu = strtoul( token, &endptr, 10 );
     268           0 :       if( FD_UNLIKELY( *endptr!='\0' || last_cpu==ULONG_MAX ) ) FD_LOG_ERR(( "strtoul failed for processor" ));
     269           0 :       metrics[ FD_METRICS_GAUGE_TILE_LAST_CPU_INDEX_OFF ] = last_cpu;
     270           0 :       break; /* No need to parse stat further */
     271           0 :     }
     272           0 :     token = strtok_r( NULL, " ", &saveptr );
     273           0 :     field_idx++;
     274           0 :   }
     275             : 
     276           0 :   if( FD_UNLIKELY( field_idx!=38UL ) ) FD_LOG_ERR(( "failed to parse /proc/<pid>/task/<tid>/stat" ));
     277             : 
     278           0 :   return 0;
     279           0 : }
     280             : 
     281             : /* read_schedstat_file reads /proc/<pid>/task/<tid>/schedstat: run_delay
     282             :    is the counter /proc/<tid>/sched prints as wait_sum, in nanoseconds. */
     283             : 
     284             : static int
     285             : read_schedstat_file( int              fd,
     286           0 :                      volatile ulong * metrics ) {
     287           0 :   if( FD_UNLIKELY( -1==lseek( fd, 0, SEEK_SET ) ) ) FD_LOG_ERR(( "lseek failed (%i-%s)", errno, strerror( errno ) ));
     288             : 
     289           0 :   char contents[ 128 ] = {0};
     290           0 :   ulong contents_len = 0UL;
     291             : 
     292           0 :   while( 1 ) {
     293           0 :     if( FD_UNLIKELY( contents_len>=sizeof( contents ) ) ) FD_LOG_ERR(( "schedstat contents overflow" ));
     294           0 :     long n = read( fd, contents + contents_len, sizeof( contents ) - contents_len );
     295           0 :     if( FD_UNLIKELY( -1==n ) ) {
     296           0 :       if( FD_UNLIKELY( errno==EINTR ) ) continue;
     297           0 :       if( FD_UNLIKELY( errno==ESRCH ) ) return 1;
     298           0 :       FD_LOG_ERR(( "read failed (%i-%s)", errno, strerror( errno ) ));
     299           0 :     }
     300           0 :     if( FD_LIKELY( 0==n ) ) break;
     301           0 :     contents_len += (ulong)n;
     302           0 :   }
     303             : 
     304           0 :   char * endptr;
     305           0 :   ulong run_time  = strtoul( contents, &endptr, 10 );
     306           0 :   if( FD_UNLIKELY( ' '!=*endptr || run_time==ULONG_MAX ) ) FD_LOG_ERR(( "failed to parse schedstat sum_exec_runtime" ));
     307           0 :   ulong run_delay = strtoul( endptr+1, &endptr, 10 );
     308           0 :   if( FD_UNLIKELY( ' '!=*endptr || run_delay==ULONG_MAX ) ) FD_LOG_ERR(( "failed to parse schedstat run_delay" ));
     309           0 :   metrics[ FD_METRICS_COUNTER_TILE_THREAD_DURATION_NANOS_WAIT_OFF ] = run_delay;
     310           0 :   return 0;
     311           0 : }
     312             : 
     313             : static int
     314             : read_sched_file( int              fd,
     315           0 :                  volatile ulong * metrics ) {
     316           0 :   if( FD_UNLIKELY( -1==lseek( fd, 0, SEEK_SET ) ) ) FD_LOG_ERR(( "lseek failed (%i-%s)", errno, strerror( errno ) ));
     317             : 
     318           0 :   char contents[ 8192 ] = {0};
     319           0 :   ulong contents_len = 0UL;
     320             : 
     321           0 :   while( 1 ) {
     322           0 :     if( FD_UNLIKELY( contents_len>=sizeof( contents ) ) ) FD_LOG_ERR(( "sched contents overflow" ));
     323           0 :     long n = read( fd, contents + contents_len, sizeof( contents ) - contents_len );
     324           0 :     if( FD_UNLIKELY( -1==n ) ) {
     325           0 :       if( FD_UNLIKELY( errno==ESRCH ) ) return 1;
     326           0 :       FD_LOG_ERR(( "read failed (%i-%s)", errno, strerror( errno ) ));
     327           0 :     }
     328           0 :     if( FD_LIKELY( 0==n ) ) break;
     329           0 :     contents_len += (ulong)n;
     330           0 :   }
     331             : 
     332           0 :   int found_voluntary = 0;
     333           0 :   int found_involuntary = 0;
     334             : 
     335           0 :   char * line = contents;
     336           0 :   while( 1 ) {
     337           0 :     char * next_line = strchr( line, '\n' );
     338           0 :     if( FD_UNLIKELY( NULL==next_line ) ) break;
     339           0 :     *next_line = '\0';
     340             : 
     341           0 :     if( FD_UNLIKELY( !strncmp( line, "nr_voluntary_switches", 21UL ) ) ) {
     342           0 :       char * colon = strchr( line, ':' );
     343           0 :       if( FD_LIKELY( colon ) ) {
     344           0 :         char * value = colon + 1;
     345           0 :         while( ' '==*value || '\t'==*value ) value++;
     346           0 :         char * endptr;
     347           0 :         ulong voluntary_switches = strtoul( value, &endptr, 10 );
     348           0 :         if( FD_UNLIKELY( '\0'!=*endptr ) ) FD_LOG_ERR(( "unexpected char after nr_voluntary_switches" ));
     349           0 :         if( FD_UNLIKELY( voluntary_switches==ULONG_MAX ) ) FD_LOG_ERR(( "strtoul overflow for nr_voluntary_switches" ));
     350           0 :         metrics[ FD_METRICS_COUNTER_TILE_CONTEXT_SWITCH_VOLUNTARY_OFF ] = voluntary_switches;
     351           0 :         found_voluntary = 1;
     352           0 :       }
     353           0 :     } else if( FD_UNLIKELY( !strncmp( line, "nr_involuntary_switches", 23UL ) ) ) {
     354           0 :       char * colon = strchr( line, ':' );
     355           0 :       if( FD_LIKELY( colon ) ) {
     356           0 :         char * value = colon + 1;
     357           0 :         while( ' '==*value || '\t'==*value ) value++;
     358           0 :         char * endptr;
     359           0 :         ulong involuntary_switches = strtoul( value, &endptr, 10 );
     360           0 :         if( FD_UNLIKELY( '\0'!=*endptr ) ) FD_LOG_ERR(( "unexpected char after nr_involuntary_switches" ));
     361           0 :         if( FD_UNLIKELY( involuntary_switches==ULONG_MAX ) ) FD_LOG_ERR(( "strtoul overflow for nr_involuntary_switches" ));
     362           0 :         metrics[ FD_METRICS_COUNTER_TILE_CONTEXT_SWITCH_INVOLUNTARY_OFF ] = involuntary_switches;
     363           0 :         found_involuntary = 1;
     364           0 :       }
     365           0 :     }
     366             : 
     367           0 :     line = next_line + 1;
     368           0 :   }
     369             : 
     370           0 :   if( FD_UNLIKELY( !found_voluntary ) ) FD_LOG_ERR(( "nr_voluntary_switches not found in sched file" ));
     371           0 :   if( FD_UNLIKELY( !found_involuntary ) ) FD_LOG_ERR(( "nr_involuntary_switches not found in sched file" ));
     372             : 
     373           0 :   return 0;
     374           0 : }
     375             : 
     376             : static ulong
     377           0 : check_builder_status( fd_diag_tile_t * ctx ) {
     378           0 :   (void)ctx;
     379           0 :   return FD_DIAG_BUILDER_STATUS_DISABLED;
     380           0 : }
     381             : 
     382             : static void
     383           0 : check_engine_metric( fd_diag_tile_t * ctx, long now ) {
     384           0 :   static ulong const replay_distance_threshold  = 12UL;
     385           0 :   static long  const replay_stall_threshold_ns  = 12L*1000L*1000L*1000L;
     386           0 :   static long  const turbine_stall_threshold_ns = 12L*1000L*1000L*1000L;
     387           0 :   static long  const turbine_byte_cmp_window_ns = 12L*1000L*1000L*1000L;
     388             : 
     389           0 :   ulong bundle_cnt    = ctx->tiles.bundle_cnt;
     390           0 :   ulong bundle_status = FD_DIAG_BUNDLE_STATUS_DISABLED;
     391           0 :   if( FD_LIKELY( bundle_cnt ) ) {
     392             :     /* Find the best state across all bundle tiles.
     393             :        Priority: connected > sleeping > connecting > disconnected */
     394           0 :     int any_connected  = 0;
     395           0 :     int any_sleeping   = 0;
     396           0 :     int any_connecting = 0;
     397           0 :     for( ulong i=0UL; i<bundle_cnt; i++ ) {
     398           0 :       volatile ulong * m = ctx->metrics[ ctx->tiles.bundle_tile_idx[ i ] ];
     399           0 :       ulong state = m[ FD_METRICS_GAUGE_BUNDLE_CONN_STATE_OFF ];
     400           0 :       if( FD_LIKELY( state==FD_BUNDLE_STATE_CONNECTED ) ) any_connected  = 1;
     401           0 :       else if( state==FD_BUNDLE_STATE_SLEEPING )          any_sleeping   = 1;
     402           0 :       else if( state==FD_BUNDLE_STATE_CONNECTING )        any_connecting = 1;
     403           0 :     }
     404           0 :     if(      any_connected  ) bundle_status = FD_DIAG_BUNDLE_STATUS_CONNECTED;
     405           0 :     else if( any_sleeping   ) bundle_status = FD_DIAG_BUNDLE_STATUS_SLEEPING;
     406           0 :     else if( any_connecting ) bundle_status = FD_DIAG_BUNDLE_STATUS_CONNECTING;
     407           0 :     else                      bundle_status = FD_DIAG_BUNDLE_STATUS_DISCONNECTED;
     408           0 :   }
     409             : 
     410           0 :   ulong votor_idx   = ctx->tiles.votor_idx;
     411           0 :   ulong vote_status = FD_DIAG_VOTE_STATUS_DISABLED;
     412             : 
     413           0 :   if( FD_UNLIKELY( ctx->is_voting && votor_idx!=ULONG_MAX ) ) {
     414           0 :     ulong replay_idx_ag = ctx->tiles.replay_idx;
     415           0 :     if( FD_UNLIKELY( ctx->metrics[ votor_idx ][ FD_METRICS_GAUGE_TILE_STATE_OFF ]!=1UL || replay_idx_ag==ULONG_MAX ) ) {
     416           0 :       vote_status = FD_DIAG_VOTE_STATUS_NOT_STARTED;
     417           0 :     } else if( FD_UNLIKELY( ctx->metrics[ votor_idx ][ FD_METRICS_GAUGE_VOTOR_RANK_OFF ]==ULONG_MAX ) ) {
     418             :       /* Not in the rank map, so
     419             :          we cannot be delinquent: report non-voting. */
     420           0 :       vote_status = FD_DIAG_VOTE_STATUS_DISABLED;
     421           0 :     } else {
     422           0 :       volatile ulong * m = ctx->metrics[ replay_idx_ag ];
     423           0 :       ulong vote_slot    = m[ FD_METRICS_GAUGE_REPLAY_LAST_REWARDED_VOTE_SLOT_OFF ];
     424           0 :       ulong replay_slot  = m[ FD_METRICS_GAUGE_REPLAY_RESET_SLOT_OFF ];
     425           0 :       if( FD_UNLIKELY( !vote_slot || !replay_slot ) ) {
     426           0 :         vote_status = FD_DIAG_VOTE_STATUS_NOT_STARTED;
     427           0 :       } else {
     428           0 :         int current = fd_int_if( replay_slot>=128UL, vote_slot+128UL>replay_slot, vote_slot>0UL );
     429           0 :         vote_status = fd_ulong_if( current, FD_DIAG_VOTE_STATUS_VOTING, FD_DIAG_VOTE_STATUS_DELINQUENT );
     430           0 :       }
     431           0 :     }
     432           0 :   }
     433             : 
     434             :   /* Votes land but earn nothing: the stake is not admitted. */
     435           0 :   if( FD_UNLIKELY( vote_status==FD_DIAG_VOTE_STATUS_VOTING &&
     436           0 :                    ctx->tiles.replay_idx!=ULONG_MAX &&
     437           0 :                    ctx->metrics[ ctx->tiles.replay_idx ][ FD_METRICS_GAUGE_REPLAY_IS_VOTE_ACCOUNT_INADMISSIBLE_OFF ] ) ) {
     438           0 :     vote_status = FD_DIAG_VOTE_STATUS_INADMISSIBLE;
     439           0 :   }
     440             : 
     441           0 :   ulong replay_idx     = ctx->tiles.replay_idx;
     442           0 :   int   replay_running = replay_idx!=ULONG_MAX && ctx->metrics[ replay_idx ][ FD_METRICS_GAUGE_TILE_STATE_OFF ]==1UL;
     443           0 :   ulong replay_status  = FD_DIAG_REPLAY_STATUS_DISABLED;
     444           0 :   if( FD_LIKELY( replay_idx!=ULONG_MAX ) ) {
     445           0 :     if( FD_UNLIKELY( !replay_running ) ) {
     446           0 :       replay_status = FD_DIAG_REPLAY_STATUS_NOT_STARTED;
     447           0 :     } else {
     448           0 :       volatile ulong * m = ctx->metrics[ replay_idx ];
     449           0 :       ulong turbine_slot = m[ FD_METRICS_GAUGE_REPLAY_LAST_REASSEMBLY_DEQUEUED_SLOT_OFF ];
     450           0 :       ulong reset_slot   = m[ FD_METRICS_GAUGE_REPLAY_RESET_SLOT_OFF ];
     451           0 :       if( FD_UNLIKELY( reset_slot!=ctx->check_engine.prev_reset_slot ) ) {
     452           0 :         ctx->check_engine.prev_reset_slot       = reset_slot;
     453           0 :         ctx->check_engine.reset_slot_changed_ns = now;
     454           0 :       }
     455           0 :       if( FD_UNLIKELY( (turbine_slot==0UL) || (reset_slot==0UL) ) ) {
     456           0 :         replay_status = FD_DIAG_REPLAY_STATUS_NOT_STARTED;
     457           0 :       } else if( FD_UNLIKELY( ((turbine_slot>reset_slot) && (turbine_slot-reset_slot>replay_distance_threshold)) ||
     458           0 :                                (now-ctx->check_engine.reset_slot_changed_ns>replay_stall_threshold_ns) ) ) {
     459           0 :         replay_status = FD_DIAG_REPLAY_STATUS_BEHIND;
     460           0 :       } else {
     461           0 :         replay_status = FD_DIAG_REPLAY_STATUS_RUNNING;
     462           0 :       }
     463           0 :     }
     464           0 :   }
     465             : 
     466           0 :   ulong shred_cnt      = ctx->tiles.shred_cnt;
     467           0 :   ulong turbine_status = FD_DIAG_TURBINE_STATUS_DISABLED;
     468           0 :   if( FD_LIKELY( replay_idx!=ULONG_MAX && shred_cnt>0UL ) ) {
     469           0 :     if( FD_UNLIKELY( !replay_running ) ) {
     470           0 :       turbine_status = FD_DIAG_TURBINE_STATUS_NOT_STARTED;
     471           0 :     } else {
     472           0 :       int all_shred_running = 1;
     473           0 :       ulong cur_turbine_bytes = 0UL, cur_repair_bytes = 0UL;
     474           0 :       for( ulong i=0UL; i<shred_cnt; i++ ) {
     475           0 :         volatile ulong * sm = ctx->metrics[ ctx->tiles.shred_tile_idx[ i ] ];
     476           0 :         cur_turbine_bytes += sm[ FD_METRICS_COUNTER_SHRED_TURBINE_RX_BYTES_OFF ];
     477           0 :         cur_repair_bytes  += sm[ FD_METRICS_COUNTER_SHRED_REPAIR_RX_BYTES_OFF ];
     478           0 :         if( FD_UNLIKELY( sm[ FD_METRICS_GAUGE_TILE_STATE_OFF ]!=1UL ) ) {
     479           0 :           all_shred_running = 0;
     480           0 :           break;
     481           0 :         }
     482           0 :       }
     483           0 :       if( FD_UNLIKELY( !all_shred_running ) ) {
     484           0 :         turbine_status = FD_DIAG_TURBINE_STATUS_NOT_STARTED;
     485           0 :       } else {
     486           0 :         ulong turbine_slot = ctx->metrics[ replay_idx ][ FD_METRICS_GAUGE_REPLAY_LAST_REASSEMBLY_DEQUEUED_SLOT_OFF ];
     487           0 :         if( FD_UNLIKELY( turbine_slot!=ctx->check_engine.prev_turbine_slot ) ) {
     488           0 :           ctx->check_engine.prev_turbine_slot       = turbine_slot;
     489           0 :           ctx->check_engine.turbine_slot_changed_ns = now;
     490           0 :         }
     491           0 :         if( FD_UNLIKELY( now-ctx->check_engine.byte_snapshot_ns>=turbine_byte_cmp_window_ns ) ) {
     492           0 :           ctx->check_engine.repair_outpacing       = (cur_repair_bytes-ctx->check_engine.snapshot_repair_bytes)>(cur_turbine_bytes-ctx->check_engine.snapshot_turbine_bytes);
     493           0 :           ctx->check_engine.snapshot_turbine_bytes = cur_turbine_bytes;
     494           0 :           ctx->check_engine.snapshot_repair_bytes  = cur_repair_bytes;
     495           0 :           ctx->check_engine.byte_snapshot_ns       = now;
     496           0 :         }
     497             : 
     498           0 :         if( FD_UNLIKELY( turbine_slot==0UL ) ) {
     499           0 :           turbine_status = FD_DIAG_TURBINE_STATUS_NOT_STARTED;
     500           0 :         } else if( FD_UNLIKELY( now-ctx->check_engine.turbine_slot_changed_ns>turbine_stall_threshold_ns ) ) {
     501           0 :           turbine_status = FD_DIAG_TURBINE_STATUS_STALLED;
     502           0 :         } else if( FD_UNLIKELY( ctx->check_engine.repair_outpacing ) ) {
     503           0 :           turbine_status = FD_DIAG_TURBINE_STATUS_REPAIR_OUTPACING;
     504           0 :         } else {
     505           0 :           turbine_status = FD_DIAG_TURBINE_STATUS_RUNNING;
     506           0 :         }
     507           0 :       }
     508           0 :     }
     509           0 :   }
     510             : 
     511           0 :   FD_MGAUGE_SET( DIAG, BUNDLE_STATE,   bundle_status  );
     512           0 :   FD_MGAUGE_SET( DIAG, VOTE_STATE,     vote_status    );
     513           0 :   FD_MGAUGE_SET( DIAG, REPLAY_STATE,   replay_status  );
     514           0 :   FD_MGAUGE_SET( DIAG, TURBINE_STATE,  turbine_status );
     515           0 :   FD_MGAUGE_SET( DIAG, BUILDER_STATE,  check_builder_status( ctx ) );
     516           0 : }
     517             : 
     518             : static void
     519             : cpu_owner_refresh( fd_diag_tile_t *  ctx,
     520           0 :                    fd_topo_t const * topo ) {
     521           0 :   ushort owner[ FD_TILE_MAX ];
     522           0 :   ushort cnt  [ FD_TILE_MAX ] = {0};
     523           0 :   fd_cpuset_new( ctx->cpu_live_tile );
     524           0 :   for( ulong i=0UL; i<ctx->tile_cnt; i++ ) {
     525           0 :     if( topo ) {
     526           0 :       ulong cpu_idx = topo->tiles[ i ].cpu_idx;
     527           0 :       ctx->tile_cpu[ i ] = (ushort)fd_ulong_if( cpu_idx<FD_TILE_MAX && !topo->tiles[ i ].floats, cpu_idx, USHORT_MAX );
     528           0 :     }
     529           0 :     ulong cpu_idx = ctx->tile_cpu[ i ];
     530           0 :     if( cpu_idx==USHORT_MAX || ( !topo && 2UL==ctx->metrics[ i ][ FD_METRICS_GAUGE_TILE_STATE_OFF ] ) ) continue;
     531           0 :     owner[ cpu_idx ] = (ushort)i;
     532           0 :     cnt  [ cpu_idx ]++;
     533           0 :     fd_cpuset_insert( ctx->cpu_live_tile, cpu_idx );
     534           0 :   }
     535           0 :   for( ulong c=0UL; c<FD_TILE_MAX; c++ ) {
     536           0 :     ushort o = cnt[ c ]==1 ? owner[ c ] : USHORT_MAX;
     537           0 :     if( FD_LIKELY( o==ctx->cpu_to_tile[ c ] ) ) continue;
     538           0 :     ctx->cpu_to_tile[ c ] = o;
     539           0 :     if( !topo ) { /* the new owner counts from its next sample */
     540           0 :       fd_cpuset_insert( ctx->irq_rebase,  c );
     541           0 :       fd_cpuset_insert( ctx->tick_rebase, c );
     542           0 :     }
     543           0 :   }
     544           0 : }
     545             : 
     546             : static void
     547           0 : irq_metrics( fd_diag_tile_t * ctx ) {
     548           0 :   if( FD_UNLIKELY( -1==lseek( ctx->proc_softirqs_fd, 0, SEEK_SET ) ) ) FD_LOG_ERR(( "lseek failed (%i-%s)", errno, strerror( errno ) ));
     549           0 :   ulong softirq_cpu_cnt = fd_proc_softirqs_sum( ctx->proc_softirqs_fd, ctx->irq_cnt );
     550           0 :   if( FD_UNLIKELY( !softirq_cpu_cnt ) ) return; /* parse fail */
     551             : 
     552           0 :   ulong volatile * softirq_total     = &fd_metrics_tl[ MIDX( COUNTER, DIAG, SYSTEM_SOFTIRQ      ) ];
     553           0 :   ulong volatile * softirq_undesired = &fd_metrics_tl[ MIDX( COUNTER, DIAG, SOFTIRQ_ON_TILE_CPU ) ];
     554           0 :   for( ulong j=0UL; j<FD_METRICS_ENUM_SOFTIRQ_TYPE_CNT; j++ ) {
     555           0 :     ulong tot_cnt = 0UL;
     556           0 :     for( ulong i=0UL; i<softirq_cpu_cnt; i++ ) {
     557           0 :       tot_cnt += fd_ulong_sat_sub( ctx->irq_cnt[ j ][ i ], ctx->softirq_boot[ j ][ i ] );
     558           0 :       ulong delta = fd_ulong_sat_sub( ctx->irq_cnt[ j ][ i ], ctx->softirq_baseline[ j ][ i ] );
     559           0 :       if( fd_cpuset_test( ctx->cpu_live_tile, i ) ) ctx->on_tile_softirq[ j ] += delta;
     560           0 :       ctx->softirq_baseline[ j ][ i ] = ctx->irq_cnt[ j ][ i ];
     561           0 :     }
     562           0 :     softirq_total    [ j ] = tot_cnt;
     563           0 :     softirq_undesired[ j ] = ctx->on_tile_softirq[ j ];
     564           0 :   }
     565             : 
     566           0 :   ulong * cpu_irq = ctx->irq_cnt[ 0 ]; /* re-use as scratch memory */
     567           0 :   ulong * cpu_tlb = ctx->irq_cnt[ 1 ];
     568           0 :   ulong * cpu_loc = ctx->irq_cnt[ 2 ];
     569           0 :   if( FD_UNLIKELY( -1==lseek( ctx->proc_interrupts_fd, 0, SEEK_SET ) ) ) FD_LOG_ERR(( "lseek failed (%i-%s)", errno, strerror( errno ) ));
     570           0 :   ulong cpu_cnt = fd_proc_interrupts_read( ctx->proc_interrupts_fd, cpu_irq, cpu_tlb, cpu_loc );
     571           0 :   if( FD_UNLIKELY( !cpu_cnt ) ) return; /* parse fail */
     572             : 
     573           0 :   ulong tot_cnt = 0UL;
     574           0 :   for( ulong i=0UL; i<cpu_cnt; i++ ) {
     575           0 :     tot_cnt += fd_ulong_sat_sub( cpu_irq[ i ], ctx->device_irq_boot[ i ] );
     576           0 :     ulong delta = fd_ulong_sat_sub( cpu_irq[ i ], ctx->device_irq_baseline[ i ] );
     577           0 :     if( fd_cpuset_test( ctx->cpu_live_tile, i ) ) ctx->on_tile_device_irq += delta;
     578           0 :     ctx->device_irq_baseline[ i ] = cpu_irq[ i ];
     579           0 :   }
     580           0 :   FD_MCNT_SET( DIAG, SYSTEM_DEVICE_IRQ,      tot_cnt                 );
     581           0 :   FD_MCNT_SET( DIAG, DEVICE_IRQ_ON_TILE_CPU, ctx->on_tile_device_irq );
     582             : 
     583           0 :   for( ulong i=0UL; i<cpu_cnt; i++ ) {
     584           0 :     ulong tile_id = ctx->cpu_to_tile[ i ];
     585           0 :     if( tile_id==USHORT_MAX ) continue;
     586           0 :     if( FD_UNLIKELY( fd_cpuset_test( ctx->irq_rebase, i ) ) ) {
     587           0 :       ctx->owner_irq_baseline[ i ] = cpu_irq[ i ];
     588           0 :       ctx->owner_tlb_baseline[ i ] = cpu_tlb[ i ];
     589           0 :       ctx->owner_loc_baseline[ i ] = cpu_loc[ i ];
     590           0 :       fd_cpuset_remove( ctx->irq_rebase, i );
     591           0 :     }
     592           0 :     ctx->metrics[ tile_id ][ FD_METRICS_COUNTER_TILE_DEVICE_IRQ_OFF    ] = fd_ulong_sat_sub( cpu_irq[ i ], ctx->owner_irq_baseline[ i ] );
     593           0 :     ctx->metrics[ tile_id ][ FD_METRICS_COUNTER_TILE_TLB_SHOOTDOWN_OFF ] = fd_ulong_sat_sub( cpu_tlb[ i ], ctx->owner_tlb_baseline[ i ] );
     594           0 :     ctx->metrics[ tile_id ][ FD_METRICS_COUNTER_TILE_TIMER_IRQ_OFF     ] = fd_ulong_sat_sub( cpu_loc[ i ], ctx->owner_loc_baseline[ i ] );
     595           0 :   }
     596           0 : }
     597             : 
     598             : /* interrupt_metrics reads per-CPU irq+softirq+steal tick counts from
     599             :    /proc/stat and publishes them as the INTERRUPT CPU regime for
     600             :    fixed tiles.  On kernels with CONFIG_IRQ_TIME_ACCOUNTING (near
     601             :    universal), these buckets are disjoint from utime/stime so
     602             :      idle = lifetime - user - system - wait - interrupt
     603             :    is exact up to sampling granularity; without it, the irq columns
     604             :    undercount and interrupt reads near zero (degrades gracefully). */
     605             : 
     606             : static void
     607           0 : interrupt_metrics( fd_diag_tile_t * ctx ) {
     608           0 :   ulong cpu_ticks[ FD_TILE_MAX ];
     609           0 :   if( FD_UNLIKELY( -1==lseek( ctx->proc_stat_fd, 0, SEEK_SET ) ) ) FD_LOG_ERR(( "lseek failed (%i-%s)", errno, strerror( errno ) ));
     610           0 :   ulong cpu_cnt = fd_proc_stat_irq_ticks( ctx->proc_stat_fd, cpu_ticks );
     611           0 :   if( FD_UNLIKELY( !cpu_cnt ) ) return; /* parse fail */
     612             : 
     613           0 :   for( ulong i=0UL; i<cpu_cnt; i++ ) {
     614           0 :     ulong tile_id = ctx->cpu_to_tile[ i ];
     615           0 :     if( tile_id==USHORT_MAX ) continue;
     616           0 :     if( FD_UNLIKELY( fd_cpuset_test( ctx->tick_rebase, i ) ) ) {
     617           0 :       ctx->owner_tick_baseline[ i ] = cpu_ticks[ i ];
     618           0 :       fd_cpuset_remove( ctx->tick_rebase, i );
     619           0 :     }
     620             :     /* CLK_TCK is always 100, so 1 tick = 10ms = 10,000,000 ns */
     621           0 :     ulong since = fd_ulong_sat_sub( cpu_ticks[ i ], ctx->owner_tick_baseline[ i ] );
     622           0 :     ctx->metrics[ tile_id ][ FD_METRICS_COUNTER_TILE_THREAD_DURATION_NANOS_INTERRUPT_OFF ] = since*10000000UL;
     623           0 :   }
     624           0 : }
     625             : 
     626             : static ulong
     627             : read_text( int    fd,
     628             :            char * buf,
     629           0 :            ulong  buf_sz ) {
     630           0 :   if( FD_UNLIKELY( -1==lseek( fd, 0, SEEK_SET ) ) ) FD_LOG_ERR(( "lseek failed (%i-%s)", errno, strerror( errno ) ));
     631           0 :   ulong len;
     632           0 :   int err = fd_io_read( fd, buf, buf_sz-1UL, buf_sz-1UL, &len );
     633           0 :   if( FD_UNLIKELY( err>0 ) ) FD_LOG_ERR(( "fd_io_read failed (%i-%s)", err, fd_io_strerror( err ) ));
     634           0 :   buf[ len ] = '\0';
     635           0 :   return len;
     636           0 : }
     637             : 
     638             : static void
     639             : add_numa_bytes( fd_diag_tile_t * ctx,
     640             :                 ulong            tile_idx,
     641             :                 ulong            numa_slot,
     642           0 :                 ulong            bytes ) {
     643           0 :   if( FD_UNLIKELY( tile_idx==DIAG_WKSP_TILE_IDX_SHARED ) ) {
     644           0 :     ctx->system_resources.numa_mem[ numa_slot ].shared_bytes += bytes;
     645           0 :     return;
     646           0 :   }
     647             : 
     648           0 :   ushort * entry_idx = &ctx->numa_entry_idx[ tile_idx ][ numa_slot ];
     649           0 :   if( FD_UNLIKELY( *entry_idx==USHORT_MAX ) ) {
     650           0 :     FD_TEST( ctx->system_resources.tile_mem_cnt<FD_DIAG_SYSTEM_TILE_MEM_MAX );
     651           0 :     *entry_idx = (ushort)ctx->system_resources.tile_mem_cnt++;
     652           0 :     fd_diag_system_tile_mem_t * entry = &ctx->system_resources.tile_mem[ *entry_idx ];
     653           0 :     entry->tile_idx = (ushort)tile_idx;
     654           0 :     entry->numa_idx = ctx->numa.node[ numa_slot ].idx;
     655           0 :   }
     656           0 :   ctx->system_resources.tile_mem[ *entry_idx ].allocated_bytes += bytes;
     657           0 : }
     658             : 
     659             : static void
     660           0 : add_configured_memory_bytes( fd_diag_tile_t * ctx ) {
     661             :   /* Workspaces and tile stacks are hugetlbfs-backed, so their configured
     662             :      sizes are also their resident physical-memory footprint. */
     663           0 :   for( ulong i=0UL; i<ctx->memory.cnt; i++ ) {
     664           0 :     if( FD_UNLIKELY( ctx->memory.wksp[ i ].numa_slot==USHORT_MAX ) ) continue;
     665           0 :     add_numa_bytes( ctx,
     666           0 :                     ctx->memory.wksp[ i ].tile_idx,
     667           0 :                     ctx->memory.wksp[ i ].numa_slot,
     668           0 :                     ctx->memory.wksp[ i ].bytes );
     669           0 :   }
     670           0 :   for( ulong tile_idx=0UL; tile_idx<ctx->tile_cnt; tile_idx++ ) {
     671           0 :     ushort numa_slot = ctx->memory.stack_numa_slot[ tile_idx ];
     672           0 :     if( FD_UNLIKELY( numa_slot==USHORT_MAX ) ) continue;
     673           0 :     add_numa_bytes( ctx, tile_idx, numa_slot, FD_TILE_PRIVATE_STACK_SZ );
     674           0 :   }
     675           0 : }
     676             : 
     677             : static void
     678           0 : sample_disk( fd_diag_tile_t * ctx ) {
     679           0 :   ctx->system_resources.mount_cnt = (uint)ctx->mount_cnt;
     680           0 :   ctx->system_resources.file_cnt  = (uint)ctx->file_cnt;
     681             : 
     682           0 :   for( ulong i=0UL; i<ctx->mount_cnt; i++ ) {
     683           0 :     struct statfs st;
     684           0 :     if( FD_UNLIKELY( fstatfs( ctx->mounts[ i ].fd, &st ) ) ) FD_LOG_ERR(( "fstatfs failed (%i-%s)", errno, strerror( errno ) ));
     685           0 :     ulong block_sz = (ulong)( st.f_frsize ? st.f_frsize : st.f_bsize );
     686           0 :     fd_diag_system_mount_t * mount = &ctx->system_resources.mount[ i ];
     687           0 :     fd_cstr_ncpy( mount->path, ctx->mounts[ i ].path, sizeof(mount->path) );
     688           0 :     mount->total_bytes     = (ulong)st.f_blocks*block_sz;
     689           0 :     mount->free_bytes      = (ulong)st.f_bfree *block_sz;
     690           0 :     mount->available_bytes = (ulong)st.f_bavail*block_sz;
     691           0 :   }
     692             : 
     693           0 :   for( ulong i=0UL; i<ctx->file_cnt; i++ ) {
     694           0 :     fd_diag_system_file_t * file = &ctx->system_resources.file[ i ];
     695           0 :     fd_cstr_ncpy( file->path, ctx->files[ i ].path, sizeof(file->path) );
     696           0 :     file->category  = ctx->files[ i ].category;
     697           0 :     file->mount_idx = ctx->files[ i ].mount_idx;
     698           0 :     if( ctx->files[ i ].metric ) file->bytes = *ctx->files[ i ].metric;
     699           0 :     else if( ctx->files[ i ].data_fd>=0 ) {
     700           0 :       struct stat st;
     701           0 :       if( FD_UNLIKELY( syscall( SYS_fstat, ctx->files[ i ].data_fd, &st ) ) ) FD_LOG_ERR(( "fstat failed (%i-%s)", errno, strerror( errno ) ));
     702           0 :       file->bytes = (ulong)st.st_size;
     703           0 :     }
     704           0 :   }
     705           0 : }
     706             : 
     707             : static ulong
     708             : read_meminfo_kib( char const * buf,
     709           0 :                   char const * key ) {
     710           0 :   char const * p = strstr( buf, key );
     711           0 :   if( FD_UNLIKELY( !p ) ) return 0UL;
     712           0 :   p += strlen( key );
     713           0 :   while( *p==' ' || *p=='\t' || *p==':' ) p++;
     714           0 :   return strtoul( p, NULL, 10 );
     715           0 : }
     716             : 
     717             : static void
     718             : sample_system( fd_diag_tile_t * ctx,
     719           0 :                long             now ) {
     720           0 :   ctx->next_system_report_nanos = now + SYSTEM_REPORT_INTERVAL_NANOS;
     721             : 
     722           0 :   ctx->system_resources.mem_available_bytes = 0UL;
     723           0 :   ctx->system_resources.mem_free_bytes      = 0UL;
     724           0 :   ctx->system_resources.numa_mem_cnt         = (uint)ctx->numa.cnt;
     725           0 :   ctx->system_resources.tile_mem_cnt         = 0U;
     726           0 :   ctx->system_resources.mount_cnt            = 0U;
     727           0 :   ctx->system_resources.file_cnt             = 0U;
     728           0 :   fd_memset( ctx->system_resources.numa_mem, 0, sizeof(ctx->system_resources.numa_mem) );
     729           0 :   fd_memset( ctx->system_resources.tile_mem, 0, sizeof(ctx->system_resources.tile_mem) );
     730           0 :   fd_memset( ctx->system_resources.mount,    0, sizeof(ctx->system_resources.mount)    );
     731           0 :   fd_memset( ctx->system_resources.file,     0, sizeof(ctx->system_resources.file)     );
     732           0 :   fd_memset( ctx->numa_entry_idx, 0xFF, sizeof(ctx->numa_entry_idx) );
     733           0 :   char meminfo[ 4096 ];
     734           0 :   if( FD_LIKELY( read_text( ctx->proc_meminfo_fd, meminfo, sizeof(meminfo) ) ) ) {
     735           0 :     ctx->system_resources.mem_available_bytes = read_meminfo_kib( meminfo, "MemAvailable" )<<10;
     736           0 :     ctx->system_resources.mem_free_bytes      = read_meminfo_kib( meminfo, "MemFree"      )<<10;
     737           0 :   }
     738           0 :   for( ulong i=0UL; i<ctx->numa.cnt; i++ ) {
     739           0 :     ulong numa_idx = ctx->numa.node[ i ].idx;
     740           0 :     fd_diag_system_numa_mem_t * numa = &ctx->system_resources.numa_mem[ i ];
     741           0 :     numa->numa_idx = (ushort)numa_idx;
     742             : 
     743           0 :     if( FD_UNLIKELY( !read_text( ctx->numa.node[ i ].meminfo_fd, meminfo, sizeof(meminfo) ) ) ) continue;
     744           0 :     char key[ 64 ];
     745           0 :     FD_TEST( fd_cstr_printf_check( key, sizeof(key), NULL, "Node %lu MemTotal", numa_idx ) );
     746           0 :     numa->total_bytes = read_meminfo_kib( meminfo, key )<<10;
     747           0 :     FD_TEST( fd_cstr_printf_check( key, sizeof(key), NULL, "Node %lu MemFree", numa_idx ) );
     748           0 :     numa->free_bytes = read_meminfo_kib( meminfo, key )<<10;
     749           0 :   }
     750           0 :   add_configured_memory_bytes( ctx );
     751           0 :   sample_disk( ctx );
     752           0 :   ctx->system_resources.sample_time_nanos = (ulong)now;
     753           0 : }
     754             : 
     755             : static void
     756             : publish_system( fd_diag_tile_t *    ctx,
     757             :                 fd_stem_context_t * stem,
     758           0 :                 long                now ) {
     759           0 :   if( FD_LIKELY( now<ctx->next_system_report_nanos ) ) return;
     760           0 :   sample_system( ctx, now );
     761             : 
     762           0 :   fd_memcpy( fd_chunk_to_laddr( ctx->system_out.mem, ctx->system_out.chunk ),
     763           0 :              &ctx->system_resources, sizeof(fd_diag_system_resources_t) );
     764           0 :   fd_stem_publish( stem, ctx->system_out.idx, sizeof(fd_diag_system_resources_t),
     765           0 :                    ctx->system_out.chunk, sizeof(fd_diag_system_resources_t), 0UL, 0UL,
     766           0 :                    (ulong)fd_frag_meta_ts_comp( fd_tickcount() ) );
     767           0 :   ctx->system_out.chunk = fd_dcache_compact_next( ctx->system_out.chunk,
     768           0 :     sizeof(fd_diag_system_resources_t), ctx->system_out.chunk0, ctx->system_out.wmark );
     769           0 : }
     770             : 
     771             : static int
     772             : sensor_read( int    fd,
     773           0 :              long * out ) {
     774           0 :   char buf[ 32 ];
     775           0 :   if( FD_UNLIKELY( -1==lseek( fd, 0, SEEK_SET ) ) ) FD_LOG_ERR(( "lseek failed (%i-%s)", errno, strerror( errno ) ));
     776           0 :   long n = read( fd, buf, sizeof(buf)-1UL );
     777           0 :   if( FD_UNLIKELY( n<=0 ) ) return -1;
     778           0 :   buf[ n ] = '\0';
     779           0 :   *out = strtol( buf, NULL, 10 );
     780           0 :   return 0;
     781           0 : }
     782             : 
     783             : static int
     784             : read_optional( int    fd,
     785             :                char * buf,
     786           0 :                ulong  buf_sz ) {
     787           0 :   if( FD_UNLIKELY( -1==lseek( fd, 0, SEEK_SET ) ) ) FD_LOG_ERR(( "lseek failed (%i-%s)", errno, strerror( errno ) ));
     788           0 :   ulong len = 0UL;
     789           0 :   while( len<buf_sz-1UL ) { /* procfs returns at most a page per read */
     790           0 :     long n = read( fd, buf+len, buf_sz-1UL-len );
     791           0 :     if( FD_UNLIKELY( n<0L && errno==EINTR ) ) continue;
     792           0 :     if( FD_UNLIKELY( n<0L ) ) return -1;
     793           0 :     if( !n ) break;
     794           0 :     len += (ulong)n;
     795           0 :   }
     796           0 :   buf[ len ] = '\0';
     797           0 :   return 0;
     798           0 : }
     799             : 
     800             : static ulong
     801             : psi_total_usec( char const * buf,
     802           0 :                 char const * level ) {
     803           0 :   char const * p = strstr( buf, level ); /* cpu has no full line before Linux 5.13 */
     804           0 :   if( FD_UNLIKELY( !p ) ) return 0UL;
     805           0 :   p = strstr( p, "total=" );
     806           0 :   if( FD_UNLIKELY( !p ) ) return 0UL;
     807           0 :   return strtoul( p+6, NULL, 10 );
     808           0 : }
     809             : 
     810             : static int
     811             : host_cpu_ticks( fd_diag_tile_t * ctx,
     812             :                 char *           buf,
     813             :                 ulong            buf_sz,
     814           0 :                 ulong *          cpu ) {
     815           0 :   if( FD_UNLIKELY( !read_text( ctx->proc_stat_fd, buf, buf_sz ) || strncmp( buf, "cpu ", 4UL ) ) ) return -1;
     816           0 :   char * p = buf+4;
     817           0 :   ulong f[ 8 ];
     818           0 :   for( ulong i=0UL; i<8UL; i++ ) f[ i ] = strtoul( p, &p, 10 ); /* user nice system idle iowait irq softirq steal */
     819           0 :   cpu[ FD_METRICS_ENUM_SYSTEM_CPU_STATE_V_USER_IDX      ] = f[ 0 ]+f[ 1 ];
     820           0 :   cpu[ FD_METRICS_ENUM_SYSTEM_CPU_STATE_V_SYSTEM_IDX    ] = f[ 2 ];
     821           0 :   cpu[ FD_METRICS_ENUM_SYSTEM_CPU_STATE_V_IDLE_IDX      ] = f[ 3 ];
     822           0 :   cpu[ FD_METRICS_ENUM_SYSTEM_CPU_STATE_V_IO_WAIT_IDX   ] = f[ 4 ];
     823           0 :   cpu[ FD_METRICS_ENUM_SYSTEM_CPU_STATE_V_INTERRUPT_IDX ] = f[ 5 ]+f[ 6 ];
     824           0 :   cpu[ FD_METRICS_ENUM_SYSTEM_CPU_STATE_V_STEAL_IDX     ] = f[ 7 ];
     825           0 :   return 0;
     826           0 : }
     827             : 
     828             : static void
     829           0 : host_metrics( fd_diag_tile_t * ctx ) {
     830           0 :   char buf[ 16384 ]; /* /proc/vmstat is ~4 KiB and grows with kernel counters */
     831             : 
     832           0 :   if( FD_LIKELY( ctx->proc_loadavg_fd>=0 && !read_optional( ctx->proc_loadavg_fd, buf, sizeof(buf) ) ) ) {
     833           0 :     char * p = buf;
     834           0 :     ulong load[ FD_METRICS_ENUM_LOAD_AVERAGE_TYPE_CNT ];
     835           0 :     for( ulong i=0UL; i<FD_METRICS_ENUM_LOAD_AVERAGE_TYPE_CNT; i++ ) { double l = strtod( p, &p ); load[ i ] = l>0.0 ? (ulong)( l*100.0 + 0.5 ) : 0UL; }
     836           0 :     FD_MGAUGE_ENUM_COPY( DIAG, SYSTEM_LOAD_CENTI, load );
     837           0 :     FD_MGAUGE_SET( DIAG, THREAD_RUNNABLE_COUNT, strtoul( p, NULL, 10 ) ); /* "running/total" */
     838           0 :   }
     839             : 
     840           0 :   ulong cpu[ FD_METRICS_ENUM_SYSTEM_CPU_STATE_CNT ];
     841           0 :   if( FD_LIKELY( !host_cpu_ticks( ctx, buf, sizeof(buf), cpu ) ) ) {
     842             :     /* Since diag start, so nanos cannot overflow.  iowait can go down
     843             :        (proc(5)), clamp in ticks so Prometheus never sees a reset. */
     844           0 :     for( ulong i=0UL; i<FD_METRICS_ENUM_SYSTEM_CPU_STATE_CNT; i++ ) {
     845           0 :       ctx->host_cpu_ticks[ i ] = fd_ulong_max( fd_ulong_sat_sub( cpu[ i ], ctx->host_cpu_baseline[ i ] ), ctx->host_cpu_ticks[ i ] );
     846           0 :       cpu[ i ] = ctx->host_cpu_ticks[ i ]*10000000UL; /* CLK_TCK is 100 */
     847           0 :     }
     848           0 :     FD_MCNT_ENUM_COPY( DIAG, SYSTEM_CPU_DURATION_NANOS, cpu );
     849           0 :   }
     850             : 
     851           0 :   if( FD_LIKELY( read_text( ctx->proc_meminfo_fd, buf, sizeof(buf) ) ) ) {
     852           0 :     FD_MGAUGE_SET( DIAG, MEMORY_AVAILABLE_BYTES, read_meminfo_kib( buf, "MemAvailable" )<<10 );
     853           0 :     FD_MGAUGE_SET( DIAG, MEMORY_FREE_BYTES,      read_meminfo_kib( buf, "MemFree"      )<<10 );
     854           0 :   }
     855             : 
     856           0 :   if( FD_LIKELY( ctx->proc_vmstat_fd>=0 && !read_optional( ctx->proc_vmstat_fd, buf, sizeof(buf) ) ) ) {
     857           0 :     ulong page_sz = (ulong)FD_SHMEM_NORMAL_PAGE_SZ;
     858           0 :     FD_MCNT_SET( DIAG, SWAP_IN_BYTES,           read_meminfo_kib( buf, "\npswpin "     )*page_sz );
     859           0 :     FD_MCNT_SET( DIAG, SWAP_OUT_BYTES,          read_meminfo_kib( buf, "\npswpout "    )*page_sz );
     860           0 :     FD_MCNT_SET( DIAG, SYSTEM_PAGE_FAULT_MAJOR, read_meminfo_kib( buf, "\npgmajfault " ) );
     861           0 :     FD_MCNT_SET( DIAG, OOM_KILLED,              read_meminfo_kib( buf, "\noom_kill "   ) );
     862           0 :   }
     863             : 
     864           0 :   if( ctx->psi_fd[ 0 ]>=0 && !read_optional( ctx->psi_fd[ 0 ], buf, sizeof(buf) ) ) {
     865           0 :     FD_MCNT_SET( DIAG, CPU_PRESSURE_DURATION_NANOS,         psi_total_usec( buf, "some" )*1000UL );
     866           0 :   }
     867           0 :   if( ctx->psi_fd[ 1 ]>=0 && !read_optional( ctx->psi_fd[ 1 ], buf, sizeof(buf) ) ) {
     868           0 :     FD_MCNT_SET( DIAG, MEMORY_PRESSURE_DURATION_NANOS,      psi_total_usec( buf, "some" )*1000UL );
     869           0 :     FD_MCNT_SET( DIAG, MEMORY_PRESSURE_FULL_DURATION_NANOS, psi_total_usec( buf, "full" )*1000UL );
     870           0 :   }
     871           0 :   if( ctx->psi_fd[ 2 ]>=0 && !read_optional( ctx->psi_fd[ 2 ], buf, sizeof(buf) ) ) {
     872           0 :     FD_MCNT_SET( DIAG, IO_PRESSURE_DURATION_NANOS,          psi_total_usec( buf, "some" )*1000UL );
     873           0 :     FD_MCNT_SET( DIAG, IO_PRESSURE_FULL_DURATION_NANOS,     psi_total_usec( buf, "full" )*1000UL );
     874           0 :   }
     875           0 : }
     876             : 
     877             : /* /sys/block/<dev>/stat fields: reads completed, reads merged, sectors
     878             :    read, read ms, writes completed, writes merged, sectors written,
     879             :    write ms, requests in flight, io ms, weighted io ms.  Newer kernels
     880             :    append discard and flush fields.  Sectors are always 512 bytes.  The
     881             :    ms fields are 32-bit in the kernel and wrap.  The low 32 bits of
     882             :    each running sum are the last value read, so adding the difference
     883             :    carries the sum across a wrap. */
     884             : 
     885             : static int
     886             : disk_stat_read( fd_diag_tile_t * ctx,
     887           0 :                 ulong            f[ static 11 ] ) {
     888           0 :   char buf[ 512 ];
     889           0 :   if( ctx->disk_fd<0 || read_optional( ctx->disk_fd, buf, sizeof(buf) ) ) return -1;
     890           0 :   char * p = buf;
     891           0 :   for( ulong i=0UL; i<11UL; i++ ) f[ i ] = strtoul( p, &p, 10 );
     892           0 :   return 0;
     893           0 : }
     894             : 
     895             : static void
     896           0 : disk_metrics( fd_diag_tile_t * ctx ) {
     897           0 :   ulong f[ 11 ];
     898           0 :   if( disk_stat_read( ctx, f ) ) return;
     899             : 
     900           0 :   ulong * ms   = ctx->disk_ms;
     901           0 :   ulong * base = ctx->disk_ms_base;
     902           0 :   ms[ 0 ] += (uint)f[ 7  ]-(uint)ms[ 0 ];
     903           0 :   ms[ 1 ] += (uint)f[ 9  ]-(uint)ms[ 1 ];
     904           0 :   ms[ 2 ] += (uint)f[ 10 ]-(uint)ms[ 2 ];
     905             : 
     906           0 :   FD_MCNT_SET  ( DIAG, ACCOUNTS_DISK_READ_BYTES,                       f[ 2 ]*512UL                 );
     907           0 :   FD_MCNT_SET  ( DIAG, ACCOUNTS_DISK_WRITTEN_BYTES,                    f[ 6 ]*512UL                 );
     908           0 :   FD_MCNT_SET  ( DIAG, ACCOUNTS_DISK_WRITTEN,                          f[ 4 ]                       );
     909           0 :   FD_MCNT_SET  ( DIAG, ACCOUNTS_DISK_WRITE_DURATION_NANOS,             (ms[ 0 ]-base[ 0 ])*1000000UL );
     910           0 :   FD_MCNT_SET  ( DIAG, ACCOUNTS_DISK_BUSY_DURATION_NANOS,              (ms[ 1 ]-base[ 1 ])*1000000UL );
     911           0 :   FD_MCNT_SET  ( DIAG, ACCOUNTS_DISK_REQUEST_IN_FLIGHT_INTEGRAL_NANOS, (ms[ 2 ]-base[ 2 ])*1000000UL );
     912           0 :   FD_MGAUGE_SET( DIAG, ACCOUNTS_DISK_REQUEST_IN_FLIGHT,                f[ 8 ]                       );
     913           0 : }
     914             : 
     915             : static void
     916             : sensor_metrics( fd_diag_tile_t * ctx,
     917           0 :                 long             now ) {
     918           0 :   long val   [ DIAG_SENSOR_KIND_CNT ] = {0}; /* hottest temperature, summed power and errors */
     919           0 :   int  failed[ DIAG_SENSOR_KIND_CNT ] = {0};
     920           0 :   for( ulong i=0UL; i<ctx->sensor.cnt; i++ ) {
     921           0 :     long v;
     922           0 :     uint kind = ctx->sensor.kind[ i ];
     923           0 :     if( FD_UNLIKELY( sensor_read( ctx->sensor.fd[ i ], &v ) ) ) { failed[ kind ] = 1; continue; }
     924           0 :     if( kind>=DIAG_SENSOR_POWER ) val[ kind ] += fd_long_max( v, 0L );
     925           0 :     else                          val[ kind ]  = fd_long_max( val[ kind ], v );
     926           0 :   }
     927           0 :   FD_MGAUGE_SET( DIAG, CPU_PACKAGE_MILLICELSIUS, (ulong)val[ DIAG_SENSOR_CPU_PACKAGE ] );
     928           0 :   FD_MGAUGE_SET( DIAG, NIC_MILLICELSIUS,         (ulong)val[ DIAG_SENSOR_NIC         ] );
     929           0 :   FD_MGAUGE_SET( DIAG, NVME_MILLICELSIUS,        (ulong)val[ DIAG_SENSOR_NVME        ] );
     930           0 :   FD_MGAUGE_SET( DIAG, SYSTEM_POWER_MICROWATTS,  (ulong)val[ DIAG_SENSOR_POWER       ] );
     931           0 :   if( FD_LIKELY( !failed[ DIAG_SENSOR_ECC_CE ] ) ) FD_MCNT_SET( DIAG, MEMORY_ECC_CORRECTED,   (ulong)val[ DIAG_SENSOR_ECC_CE ] );
     932           0 :   if( FD_LIKELY( !failed[ DIAG_SENSOR_ECC_UE ] ) ) FD_MCNT_SET( DIAG, MEMORY_ECC_UNCORRECTED, (ulong)val[ DIAG_SENSOR_ECC_UE ] );
     933             : 
     934             :   /* Package power is the energy counter's rate since the last read */
     935           0 :   if( !ctx->sensor.rapl_cnt ) return;
     936           0 :   ulong energy = 0UL;
     937           0 :   for( ulong i=0UL; i<ctx->sensor.rapl_cnt; i++ ) {
     938           0 :     long e;
     939           0 :     if( FD_UNLIKELY( sensor_read( ctx->sensor.rapl_fd[ i ], &e ) || e<0L ) ) {
     940           0 :       ctx->sensor.rapl_prev_nanos = 0L; /* rebaseline */
     941           0 :       FD_MGAUGE_SET( DIAG, CPU_POWER_MICROWATTS, 0UL );
     942           0 :       return;
     943           0 :     }
     944           0 :     ulong cur  = (ulong)e;
     945           0 :     ulong prev = ctx->sensor.rapl_prev[ i ];
     946           0 :     energy += cur>=prev ? cur-prev : cur+ctx->sensor.rapl_range[ i ]-prev;
     947           0 :     ctx->sensor.rapl_prev[ i ] = cur;
     948           0 :   }
     949           0 :   long dt = now - ctx->sensor.rapl_prev_nanos;
     950           0 :   if( FD_LIKELY( ctx->sensor.rapl_prev_nanos && dt>0L ) ) FD_MGAUGE_SET( DIAG, CPU_POWER_MICROWATTS, (ulong)( (double)energy*1e9/(double)dt ) );
     951           0 :   ctx->sensor.rapl_prev_nanos = now;
     952           0 : }
     953             : 
     954             : static void
     955             : before_credit( fd_diag_tile_t *    ctx,
     956             :                fd_stem_context_t * stem,
     957           0 :                int *               charge_busy ) {
     958           0 :   (void)stem;
     959             : 
     960           0 :   long now = fd_clock_tile_now( ctx->clock );
     961           0 :   if( FD_UNLIKELY( now<ctx->next_report_nanos ) ) return;
     962           0 :   ctx->next_report_nanos += REPORT_INTERVAL_MILLIS*1000L*1000L;
     963             : 
     964           0 :   *charge_busy = 1;
     965           0 :   if( FD_UNLIKELY( ctx->gui_enabled ) ) publish_system( ctx, stem, now );
     966             : 
     967           0 :   struct timespec boottime;
     968           0 :   if( FD_UNLIKELY( -1==clock_gettime( CLOCK_BOOTTIME, &boottime ) ) ) FD_LOG_ERR(( "clock_gettime(CLOCK_BOOTTIME) failed (%i-%s)", errno, strerror( errno ) ));
     969           0 :   ulong now_since_boot_nanos = (ulong)boottime.tv_sec*1000000000UL + (ulong)boottime.tv_nsec;
     970             : 
     971           0 :   cpu_owner_refresh( ctx, NULL );
     972           0 :   interrupt_metrics( ctx ); /* before idle computation below, which subtracts it */
     973             : 
     974           0 :   int sched_due = now>=ctx->next_sched_report_nanos;
     975           0 :   if( FD_UNLIKELY( sched_due ) ) ctx->next_sched_report_nanos = now + SCHED_REPORT_INTERVAL_NANOS;
     976             : 
     977           0 :   for( ulong i=0UL; i<ctx->tile_cnt; i++ ) {
     978           0 :     if( FD_UNLIKELY( -1==ctx->stat_fds[ i ] ) ) continue;
     979             : 
     980             :     /* CLK_TCK is typically 100, so 1 tick = 10ms = 10,000,000 ns */
     981           0 :     int process_died1 = read_stat_file( ctx->stat_fds[ i ], 10000000UL, ctx->metrics[ i ] );
     982           0 :     int process_died2 = -1!=ctx->schedstat_fds[ i ] && read_schedstat_file( ctx->schedstat_fds[ i ], ctx->metrics[ i ] );
     983           0 :     int process_died3 = sched_due && read_sched_file( ctx->sched_fds[ i ], ctx->metrics[ i ] );
     984             : 
     985           0 :     if( FD_UNLIKELY( process_died1 || process_died2 || process_died3 ) ) {
     986           0 :       ctx->stat_fds[ i ] = -1;
     987           0 :       continue;
     988           0 :     }
     989             : 
     990           0 :     ulong task_lifetime_nanos = now_since_boot_nanos - ctx->starttime_nanos[ i ];
     991           0 :     ulong user_nanos      = ctx->metrics[ i ][ FD_METRICS_COUNTER_TILE_THREAD_DURATION_NANOS_USER_OFF ];
     992           0 :     ulong system_nanos    = ctx->metrics[ i ][ FD_METRICS_COUNTER_TILE_THREAD_DURATION_NANOS_SYSTEM_OFF ];
     993           0 :     ulong wait_nanos      = ctx->metrics[ i ][ FD_METRICS_COUNTER_TILE_THREAD_DURATION_NANOS_WAIT_OFF ];
     994           0 :     ulong interrupt_nanos = ctx->metrics[ i ][ FD_METRICS_COUNTER_TILE_THREAD_DURATION_NANOS_INTERRUPT_OFF ];
     995           0 :     ulong busy_nanos      = user_nanos+system_nanos+wait_nanos+interrupt_nanos;
     996           0 :     ulong idle_nanos      = (task_lifetime_nanos>busy_nanos) ? (task_lifetime_nanos-busy_nanos) : 0UL;
     997             : 
     998             :     /* Counter can't go backwards in Prometheus else it thinks the
     999             :        application restarted.  Use max to ensure monotonicity. */
    1000           0 :     ctx->metrics[ i ][ FD_METRICS_COUNTER_TILE_THREAD_DURATION_NANOS_IDLE_OFF ] = fd_ulong_max( idle_nanos, ctx->metrics[ i ][ FD_METRICS_COUNTER_TILE_THREAD_DURATION_NANOS_IDLE_OFF ] );
    1001           0 :   }
    1002             : 
    1003           0 :   for( ulong i=0UL; i<ctx->tile_cnt; i++ ) {
    1004           0 :     if( FD_LIKELY( -1!=ctx->stat_fds[ i ] ) ) continue;
    1005             : 
    1006             :     /* The tile died, but it's a tile which is allowed to shutdown, so
    1007             :        just stop updating metrics for it. */
    1008           0 :     if( FD_LIKELY( 2UL==ctx->metrics[ i ][ FD_METRICS_GAUGE_TILE_STATE_OFF ] ) ) continue;
    1009             : 
    1010             :     /* Supervisor is going to bring the whole process tree down if any
    1011             :        of the target PIDs died, so we can ignore this and wait. */
    1012           0 :     if( FD_UNLIKELY( !ctx->first_seen_died[ i ] ) ) {
    1013           0 :       ctx->first_seen_died[ i ] = now;
    1014           0 :     } else if( FD_LIKELY( ctx->first_seen_died[ i ]==LONG_MAX ) ) {
    1015             :       /* We already reported this, so we can ignore it. */
    1016           0 :     } else if( FD_UNLIKELY( now-ctx->first_seen_died[ i ] < 10L*1000L*1000L*1000L ) ) {
    1017             :       /* Wait 10 seconds for supervisor to kill us before reporting WARNING */
    1018           0 :     } else {
    1019           0 :       FD_LOG_WARNING(( "cannot get metrics for dead tile idx %lu", i ));
    1020           0 :       ctx->first_seen_died[ i ] = LONG_MAX;
    1021           0 :     }
    1022           0 :   }
    1023             : 
    1024           0 :   check_engine_metric( ctx, now );
    1025           0 :   if( FD_UNLIKELY( sched_due ) ) disk_metrics( ctx );
    1026           0 :   if( FD_UNLIKELY( now>=ctx->next_irq_report_nanos ) ) {
    1027           0 :     ctx->next_irq_report_nanos = now + IRQ_REPORT_INTERVAL_NANOS;
    1028           0 :     irq_metrics( ctx );
    1029           0 :     sensor_metrics( ctx, now );
    1030           0 :     host_metrics( ctx );
    1031           0 :   }
    1032           0 : }
    1033             : 
    1034             : /* Disk mount discovery ************************************************/
    1035             : 
    1036             : static int
    1037             : fd_mount_id( int     fd,
    1038           0 :              ulong * mnt_id ) {
    1039           0 :   char fdinfo_path[ 64 ];
    1040           0 :   if( FD_UNLIKELY( !fd_cstr_printf_check( fdinfo_path, sizeof(fdinfo_path), NULL, "/proc/self/fdinfo/%d", fd ) ) ) return EINVAL;
    1041             : 
    1042           0 :   int fdinfo_fd = open( fdinfo_path, O_RDONLY|O_CLOEXEC );
    1043           0 :   if( FD_UNLIKELY( fdinfo_fd<0 ) ) return errno;
    1044             : 
    1045           0 :   char  buf[ 256 ];
    1046           0 :   ulong buf_sz = 0UL;
    1047           0 :   int err = fd_io_read( fdinfo_fd, buf, sizeof(buf)-1UL, sizeof(buf)-1UL, &buf_sz );
    1048           0 :   if( FD_UNLIKELY( err<0 ) ) err = 0; /* EOF before the buffer filled */
    1049           0 :   if( FD_UNLIKELY( close( fdinfo_fd ) && !err ) ) err = errno;
    1050           0 :   if( FD_UNLIKELY( err ) ) return err;
    1051           0 :   buf[ buf_sz ] = '\0';
    1052             : 
    1053           0 :   char const * line = buf;
    1054           0 :   while( line ) {
    1055           0 :     if( FD_UNLIKELY( !strncmp( line, "mnt_id:", 7UL ) ) ) {
    1056           0 :       char * end;
    1057           0 :       ulong id = strtoul( line+7UL, &end, 10 );
    1058           0 :       if( FD_UNLIKELY( end==line+7UL || (*end!='\n' && *end!='\0') || id==ULONG_MAX ) ) return EINVAL;
    1059           0 :       *mnt_id = id;
    1060           0 :       return 0;
    1061           0 :     }
    1062           0 :     line = strchr( line, '\n' );
    1063           0 :     if( line ) line++;
    1064           0 :   }
    1065           0 :   return ENOENT;
    1066           0 : }
    1067             : 
    1068             : static int
    1069             : resolve_path_prefix( char const * path,
    1070           0 :                      char         resolved[ static PATH_MAX ] ) {
    1071           0 :   char candidate[ PATH_MAX ];
    1072           0 :   fd_cstr_ncpy( candidate, path, sizeof(candidate) );
    1073           0 :   while( !realpath( candidate, resolved ) ) {
    1074           0 :     char * slash = strrchr( candidate, '/' );
    1075           0 :     if( FD_UNLIKELY( !slash ) ) return 0;
    1076           0 :     if( slash==candidate ) slash[ 1 ] = '\0';
    1077           0 :     else                  *slash = '\0';
    1078           0 :   }
    1079           0 :   return 1;
    1080           0 : }
    1081             : 
    1082             : static int
    1083             : open_disk_probe_dir( char const * resolved,
    1084             :                      char         probe_dir[ static PATH_MAX ],
    1085           0 :                      ulong *      mnt_id ) {
    1086             :   /* `probe_dir` is on the target mount used only to create the probe
    1087             :      file; the descriptor for `probe_dir` is not retained. */
    1088           0 :   int path_fd = open( resolved, O_PATH|O_CLOEXEC );
    1089           0 :   if( FD_UNLIKELY( path_fd<0 ) ) return -1;
    1090             : 
    1091           0 :   struct stat path_st;
    1092           0 :   if( FD_UNLIKELY( fstat( path_fd, &path_st ) ) ) {
    1093           0 :     close( path_fd );
    1094           0 :     return -1;
    1095           0 :   }
    1096             : 
    1097           0 :   ulong path_mnt_id;
    1098           0 :   int err = fd_mount_id( path_fd, &path_mnt_id );
    1099           0 :   if( FD_UNLIKELY( err ) ) {
    1100           0 :     close( path_fd );
    1101           0 :     FD_LOG_WARNING(( "reading mount ID for `%s` failed (%i-%s); omitting filesystem from system resource reporting",
    1102           0 :                      resolved, err, strerror( err ) ));
    1103           0 :     return -1;
    1104           0 :   }
    1105             : 
    1106           0 :   fd_cstr_ncpy( probe_dir, resolved, PATH_MAX );
    1107           0 :   if( FD_UNLIKELY( !S_ISDIR( path_st.st_mode ) ) ) {
    1108           0 :     char * slash = strrchr( probe_dir, '/' );
    1109           0 :     if( FD_UNLIKELY( !slash ) ) {
    1110           0 :       close( path_fd );
    1111           0 :       return -1;
    1112           0 :     }
    1113           0 :     if( slash==probe_dir ) slash[ 1 ] = '\0';
    1114           0 :     else                  *slash = '\0';
    1115           0 :   }
    1116             : 
    1117           0 :   int dir_fd = open( probe_dir, O_PATH|O_DIRECTORY|O_CLOEXEC );
    1118           0 :   if( FD_UNLIKELY( dir_fd<0 ) ) {
    1119           0 :     int open_err = errno;
    1120           0 :     close( path_fd );
    1121           0 :     FD_LOG_WARNING(( "open `%s` for disk-capacity probe failed (%i-%s); omitting filesystem from system resource reporting",
    1122           0 :                      probe_dir, open_err, strerror( open_err ) ));
    1123           0 :     return -1;
    1124           0 :   }
    1125             : 
    1126           0 :   ulong dir_mnt_id;
    1127           0 :   err = fd_mount_id( dir_fd, &dir_mnt_id );
    1128           0 :   if( FD_UNLIKELY( err ) ) {
    1129           0 :     close( path_fd );
    1130           0 :     close( dir_fd );
    1131           0 :     FD_LOG_WARNING(( "reading mount ID for `%s` failed (%i-%s); omitting filesystem from system resource reporting",
    1132           0 :                      probe_dir, err, strerror( err ) ));
    1133           0 :     return -1;
    1134           0 :   }
    1135           0 :   close( path_fd );
    1136             : 
    1137             :   /* A regular file can itself be a bind mount.  In that case its
    1138             :      containing directory is not on the mount we need to sample,
    1139             :      and there is nowhere on that mount to create an anonymous file. */
    1140           0 :   if( FD_UNLIKELY( path_mnt_id!=dir_mnt_id ) ) {
    1141           0 :     close( dir_fd );
    1142           0 :     FD_LOG_WARNING(( "cannot create anonymous disk-capacity probe for non-directory mount `%s`; omitting filesystem from system resource reporting",
    1143           0 :                      resolved ));
    1144           0 :     return -1;
    1145           0 :   }
    1146             : 
    1147           0 :   *mnt_id = dir_mnt_id;
    1148           0 :   return dir_fd;
    1149           0 : }
    1150             : 
    1151             : static int
    1152             : resolve_mount_path( char const * probe_dir,
    1153             :                     ulong        mnt_id,
    1154           0 :                     char          mount_path[ static PATH_MAX ] ) {
    1155           0 :   fd_cstr_ncpy( mount_path, probe_dir, PATH_MAX );
    1156           0 :   while( strcmp( mount_path, "/" ) ) {
    1157           0 :     char parent[ PATH_MAX ];
    1158           0 :     fd_cstr_ncpy( parent, mount_path, sizeof(parent) );
    1159           0 :     char * slash = strrchr( parent, '/' );
    1160           0 :     if( slash==parent ) slash[ 1 ] = '\0';
    1161           0 :     else               *slash = '\0';
    1162             : 
    1163           0 :     int parent_fd = open( parent, O_PATH|O_DIRECTORY|O_CLOEXEC );
    1164           0 :     if( FD_UNLIKELY( parent_fd<0 ) ) {
    1165           0 :       int open_err = errno;
    1166           0 :       FD_LOG_WARNING(( "open `%s` while resolving mount point failed (%i-%s); omitting filesystem from system resource reporting",
    1167           0 :                        parent, open_err, strerror( open_err ) ));
    1168           0 :       return 0;
    1169           0 :     }
    1170           0 :     ulong parent_mnt_id;
    1171           0 :     int err = fd_mount_id( parent_fd, &parent_mnt_id );
    1172           0 :     close( parent_fd );
    1173           0 :     if( FD_UNLIKELY( err ) ) {
    1174           0 :       FD_LOG_WARNING(( "reading mount ID for `%s` failed (%i-%s); omitting filesystem from system resource reporting",
    1175           0 :                        parent, err, strerror( err ) ));
    1176           0 :       return 0;
    1177           0 :     }
    1178           0 :     if( parent_mnt_id!=mnt_id ) break;
    1179           0 :     fd_cstr_ncpy( mount_path, parent, PATH_MAX );
    1180           0 :   }
    1181           0 :   return 1;
    1182           0 : }
    1183             : 
    1184             : static int
    1185             : create_disk_probe( int          dir_fd,
    1186             :                    char const * probe_dir,
    1187           0 :                    ulong        mnt_id ) {
    1188             :   /* Retain no directory fds after sandboxing.  O_EXCL ensures
    1189             :      that the anonymous inode cannot later be linked into the filesystem;
    1190             :      reopening it O_PATH also removes the temporary read/write authority. */
    1191           0 :   int tmp_fd = openat( dir_fd, ".", O_TMPFILE|O_EXCL|O_RDWR|O_CLOEXEC, S_IRUSR|S_IWUSR );
    1192           0 :   if( FD_UNLIKELY( tmp_fd<0 ) ) {
    1193           0 :     int err = errno;
    1194           0 :     FD_LOG_WARNING(( "anonymous disk-capacity probe in `%s` failed (%i-%s); omitting filesystem from system resource reporting",
    1195           0 :                      probe_dir, err, strerror( err ) ));
    1196           0 :     return -1;
    1197           0 :   }
    1198             : 
    1199           0 :   char tmp_path[ 64 ];
    1200           0 :   FD_TEST( fd_cstr_printf_check( tmp_path, sizeof(tmp_path), NULL, "/proc/self/fd/%d", tmp_fd ) );
    1201           0 :   int mount_fd = open( tmp_path, O_PATH|O_CLOEXEC );
    1202           0 :   if( FD_UNLIKELY( mount_fd<0 ) ) {
    1203           0 :     int err = errno;
    1204           0 :     close( tmp_fd );
    1205           0 :     FD_LOG_WARNING(( "reopening anonymous disk-capacity probe in `%s` as O_PATH failed (%i-%s); omitting filesystem from system resource reporting",
    1206           0 :                      probe_dir, err, strerror( err ) ));
    1207           0 :     return -1;
    1208           0 :   }
    1209             : 
    1210           0 :   struct stat probe_st;
    1211           0 :   ulong probe_mnt_id;
    1212           0 :   int probe_err = 0;
    1213           0 :   if( FD_UNLIKELY( fstat( mount_fd, &probe_st ) ) ) probe_err = errno;
    1214           0 :   else if( FD_UNLIKELY( !S_ISREG( probe_st.st_mode ) || probe_st.st_nlink ) ) probe_err = EINVAL;
    1215           0 :   else probe_err = fd_mount_id( mount_fd, &probe_mnt_id );
    1216           0 :   if( FD_UNLIKELY( !probe_err && probe_mnt_id!=mnt_id ) ) probe_err = EXDEV;
    1217           0 :   if( FD_UNLIKELY( probe_err ) ) {
    1218           0 :     close( mount_fd );
    1219           0 :     close( tmp_fd );
    1220           0 :     FD_LOG_WARNING(( "anonymous disk-capacity probe in `%s` failed validation (%i-%s); omitting filesystem from system resource reporting",
    1221           0 :                      probe_dir, probe_err, strerror( probe_err ) ));
    1222           0 :     return -1;
    1223           0 :   }
    1224           0 :   close( tmp_fd );
    1225           0 :   return mount_fd;
    1226           0 : }
    1227             : 
    1228             : static uint
    1229             : add_disk_mount( fd_diag_tile_t * ctx,
    1230           0 :                 char const *     path ) {
    1231           0 :   if( FD_UNLIKELY( !path[ 0 ] ) ) return UINT_MAX;
    1232             : 
    1233             :   /* Find longest prefix of `path` that is a real path. */
    1234           0 :   char resolved[ PATH_MAX ];
    1235           0 :   if( FD_UNLIKELY( !resolve_path_prefix( path, resolved ) ) ) return UINT_MAX;
    1236             : 
    1237             :   /* Find candidate dir at or above `resolved` where O_TMPFILE can create a probe file. */
    1238           0 :   char  probe_dir[ PATH_MAX ];
    1239           0 :   ulong mnt_id;
    1240           0 :   int dir_fd = open_disk_probe_dir( resolved, probe_dir, &mnt_id );
    1241           0 :   if( FD_UNLIKELY( dir_fd<0 ) ) return UINT_MAX;
    1242             : 
    1243             :   /* Share one probe file among all reported paths on the same mount. */
    1244           0 :   for( ulong i=0UL; i<ctx->mount_cnt; i++ ) {
    1245           0 :     if( ctx->mounts[ i ].mnt_id==mnt_id ) {
    1246           0 :       close( dir_fd );
    1247           0 :       return (uint)i;
    1248           0 :     }
    1249           0 :   }
    1250           0 :   if( FD_UNLIKELY( ctx->mount_cnt>=FD_DIAG_SYSTEM_FILE_MAX ) ) {
    1251           0 :     close( dir_fd );
    1252           0 :     return UINT_MAX;
    1253           0 :   }
    1254             : 
    1255             :   /* Get the path of the mount `path` is on. */
    1256           0 :   char mount_path[ PATH_MAX ];
    1257           0 :   if( FD_UNLIKELY( !resolve_mount_path( probe_dir, mnt_id, mount_path ) ) ) {
    1258           0 :     close( dir_fd );
    1259           0 :     return UINT_MAX;
    1260           0 :   }
    1261             : 
    1262             :   /* Create the probe O_PATH descriptor kept after sandboxing. */
    1263           0 :   int mount_fd = create_disk_probe( dir_fd, probe_dir, mnt_id );
    1264           0 :   close( dir_fd );
    1265           0 :   if( FD_UNLIKELY( mount_fd<0 ) ) return UINT_MAX;
    1266             : 
    1267           0 :   ulong idx = ctx->mount_cnt++;
    1268           0 :   ctx->mounts[ idx ].fd     = mount_fd;
    1269           0 :   ctx->mounts[ idx ].mnt_id = mnt_id;
    1270           0 :   fd_cstr_ncpy( ctx->mounts[ idx ].path, mount_path, sizeof(ctx->mounts[ idx ].path) );
    1271           0 :   return (uint)idx;
    1272           0 : }
    1273             : 
    1274             : static int
    1275             : resolve_file_path( char const * path,
    1276             :                    int          data_fd,
    1277           0 :                    char         resolved[ static PATH_MAX ] ) {
    1278           0 :   char fd_path[ 64 ];
    1279           0 :   if( FD_LIKELY( data_fd>=0 ) ) {
    1280           0 :     struct stat st;
    1281           0 :     if( FD_UNLIKELY( fstat( data_fd, &st ) || !S_ISREG( st.st_mode ) ) ) return 0;
    1282           0 :     FD_TEST( fd_cstr_printf_check( fd_path, sizeof(fd_path), NULL, "/proc/self/fd/%d", data_fd ) );
    1283           0 :     path = fd_path;
    1284           0 :   }
    1285           0 :   if( FD_UNLIKELY( !path || !path[ 0 ] ) ) return 0;
    1286             : 
    1287           0 :   if( FD_LIKELY( realpath( path, resolved ) ) ) return 1;
    1288           0 :   if( FD_LIKELY( path[ 0 ]=='/' ) ) {
    1289           0 :     if( FD_UNLIKELY( strlen( path )>=PATH_MAX ) ) return 0;
    1290           0 :     fd_cstr_ncpy( resolved, path, PATH_MAX );
    1291           0 :     return 1;
    1292           0 :   }
    1293             : 
    1294           0 :   char cwd[ PATH_MAX ];
    1295           0 :   if( FD_UNLIKELY( !getcwd( cwd, sizeof(cwd) ) ) ) return 0;
    1296           0 :   return fd_cstr_printf_check( resolved, PATH_MAX, NULL, "%s/%s", cwd, path );
    1297           0 : }
    1298             : 
    1299             : static void
    1300             : add_file( fd_diag_tile_t * ctx,
    1301             :           uint             category,
    1302             :           char const *     path,
    1303             :           int              data_fd,
    1304           0 :           ulong volatile * metric ) {
    1305           0 :   char resolved[ PATH_MAX ];
    1306           0 :   if( FD_UNLIKELY( !resolve_file_path( path, data_fd, resolved ) ) ) return;
    1307             : 
    1308           0 :   uint mount_idx = add_disk_mount( ctx, resolved );
    1309           0 :   if( FD_UNLIKELY( mount_idx==UINT_MAX || ctx->file_cnt>=FD_DIAG_SYSTEM_FILE_MAX ) ) return;
    1310           0 :   ulong idx = ctx->file_cnt++;
    1311           0 :   fd_cstr_ncpy( ctx->files[ idx ].path, resolved, sizeof(ctx->files[ idx ].path) );
    1312           0 :   ctx->files[ idx ].category              = category;
    1313           0 :   ctx->files[ idx ].mount_idx             = mount_idx;
    1314           0 :   ctx->files[ idx ].data_fd               = data_fd;
    1315           0 :   ctx->files[ idx ].metric                = metric;
    1316           0 : }
    1317             : 
    1318             : static int
    1319             : sysfs_read_cstr( char const * dir,
    1320             :                  char const * file,
    1321             :                  char *       out,
    1322           0 :                  ulong        out_sz ) {
    1323           0 :   char path[ PATH_MAX ];
    1324           0 :   FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "%s/%s", dir, file ) );
    1325           0 :   int fd = open( path, O_RDONLY|O_CLOEXEC );
    1326           0 :   if( fd<0 ) return -1;
    1327           0 :   long n = read( fd, out, out_sz-1UL );
    1328           0 :   close( fd );
    1329           0 :   if( n<=0 ) return -1;
    1330           0 :   out[ n ] = '\0';
    1331           0 :   out[ strcspn( out, "\n" ) ] = '\0';
    1332           0 :   return 0;
    1333           0 : }
    1334             : 
    1335             : static void
    1336             : sensor_add( fd_diag_tile_t * ctx,
    1337             :             char const *     dir,
    1338             :             char const *     file,
    1339           0 :             uint             kind ) {
    1340           0 :   if( FD_UNLIKELY( ctx->sensor.cnt>=DIAG_SENSOR_MAX ) ) {
    1341           0 :     FD_LOG_WARNING(( "more than %lu hardware sensors, ignoring %s/%s", DIAG_SENSOR_MAX, dir, file ));
    1342           0 :     return;
    1343           0 :   }
    1344             : 
    1345           0 :   char path[ PATH_MAX ];
    1346           0 :   FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "%s/%s", dir, file ) );
    1347           0 :   int fd = open( path, O_RDONLY|O_CLOEXEC );
    1348           0 :   if( FD_UNLIKELY( fd<0 ) ) return;
    1349           0 :   ctx->sensor.fd  [ ctx->sensor.cnt ] = fd;
    1350           0 :   ctx->sensor.kind[ ctx->sensor.cnt ] = kind;
    1351           0 :   ctx->sensor.cnt++;
    1352           0 : }
    1353             : 
    1354             : static void
    1355           0 : sensors_init( fd_diag_tile_t * ctx ) {
    1356           0 :   ctx->sensor.cnt      = 0UL;
    1357           0 :   ctx->sensor.rapl_cnt = 0UL;
    1358             : 
    1359           0 :   int dir_fd = open( "/sys/class/hwmon", O_RDONLY|O_DIRECTORY|O_CLOEXEC );
    1360           0 :   for(;;) {
    1361           0 :     if( FD_UNLIKELY( dir_fd<0 ) ) break;
    1362           0 :     uchar buf[ 4096UL ] __attribute__((aligned(alignof(struct dirent))));
    1363           0 :     long dents_sz = syscall( SYS_getdents64, dir_fd, buf, sizeof(buf) );
    1364           0 :     if( FD_UNLIKELY( dents_sz<0L ) ) FD_LOG_WARNING(( "getdents64(/sys/class/hwmon) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
    1365           0 :     if( dents_sz<=0L ) break;
    1366             : 
    1367           0 :     ulong off = 0UL;
    1368           0 :     while( off<(ulong)dents_sz ) {
    1369           0 :       struct dirent const * ent = (struct dirent const *)(buf+off);
    1370           0 :       FD_TEST( ent->d_reclen && off+(ulong)ent->d_reclen<=(ulong)dents_sz );
    1371           0 :       off += ent->d_reclen;
    1372           0 :       if( strncmp( ent->d_name, "hwmon", 5UL ) ) continue;
    1373           0 :       char hw[ PATH_MAX ];
    1374           0 :       char name[ 32 ];
    1375           0 :       FD_TEST( fd_cstr_printf_check( hw, sizeof(hw), NULL, "/sys/class/hwmon/%s", ent->d_name ) );
    1376           0 :       if( sysfs_read_cstr( hw, "name", name, sizeof(name) ) ) continue;
    1377             : 
    1378           0 :       if( !strcmp( name, "k10temp" ) || !strcmp( name, "coretemp" ) ) {
    1379           0 :         for( ulong i=1UL; i<512UL; i++ ) { /* coretemp numbers inputs sparsely by core id */
    1380           0 :           char file [ 32 ];
    1381           0 :           char label[ 32 ];
    1382           0 :           FD_TEST( fd_cstr_printf_check( file, sizeof(file), NULL, "temp%lu_label", i ) );
    1383           0 :           if( sysfs_read_cstr( hw, file, label, sizeof(label) ) ) continue;
    1384           0 :           if( strcmp( label, "Tctl" ) && strncmp( label, "Package id", 10UL ) ) continue;
    1385           0 :           FD_TEST( fd_cstr_printf_check( file, sizeof(file), NULL, "temp%lu_input", i ) );
    1386           0 :           sensor_add( ctx, hw, file, DIAG_SENSOR_CPU_PACKAGE );
    1387           0 :         }
    1388           0 :       }
    1389           0 :       else if( !strncmp( name, "mlx5", 4UL  ) ) sensor_add( ctx, hw, "temp1_input",    DIAG_SENSOR_NIC   ); /* OFED appends the PCI address */
    1390           0 :       else if( !strncmp( name, "ice", 3UL   ) ) sensor_add( ctx, hw, "temp1_input",    DIAG_SENSOR_NIC   ); /* out-of-tree drivers append the PCI address */
    1391           0 :       else if( !strcmp( name, "nvme"        ) ) sensor_add( ctx, hw, "temp1_input",    DIAG_SENSOR_NVME  );
    1392           0 :       else if( !strcmp( name, "power_meter" ) ) sensor_add( ctx, hw, "power1_average", DIAG_SENSOR_POWER );
    1393           0 :     }
    1394           0 :   }
    1395           0 :   if( FD_LIKELY( dir_fd>=0 ) ) close( dir_fd );
    1396             : 
    1397           0 :   for( ulong i=0UL; i<DIAG_EDAC_MC_MAX; i++ ) {
    1398           0 :     char mc[ 64 ];
    1399           0 :     FD_TEST( fd_cstr_printf_check( mc, sizeof(mc), NULL, "/sys/devices/system/edac/mc/mc%lu", i ) );
    1400           0 :     if( access( mc, F_OK ) ) continue; /* ids can have gaps */
    1401           0 :     sensor_add( ctx, mc, "ce_count", DIAG_SENSOR_ECC_CE );
    1402           0 :     sensor_add( ctx, mc, "ue_count", DIAG_SENSOR_ECC_UE );
    1403           0 :   }
    1404             : 
    1405           0 :   for( ulong i=0UL; i<DIAG_RAPL_MAX; i++ ) {
    1406           0 :     char rapl [ 64 ];
    1407           0 :     char name [ 32 ];
    1408           0 :     char range[ 32 ];
    1409           0 :     FD_TEST( fd_cstr_printf_check( rapl, sizeof(rapl), NULL, "/sys/class/powercap/intel-rapl:%lu", i ) );
    1410           0 :     if( sysfs_read_cstr( rapl, "name", name, sizeof(name) ) ) break;
    1411           0 :     if( strncmp( name, "package", 7UL ) ) continue; /* psys on some Intel parts */
    1412           0 :     if( sysfs_read_cstr( rapl, "max_energy_range_uj", range, sizeof(range) ) ) continue;
    1413           0 :     ulong range_uj = strtoul( range, NULL, 10 );
    1414           0 :     if( FD_UNLIKELY( !range_uj ) ) continue;
    1415           0 :     char path[ 96 ];
    1416           0 :     FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "%s/energy_uj", rapl ) );
    1417           0 :     int fd = open( path, O_RDONLY|O_CLOEXEC );
    1418           0 :     if( FD_UNLIKELY( fd<0 ) ) continue;
    1419           0 :     ctx->sensor.rapl_fd   [ ctx->sensor.rapl_cnt ] = fd;
    1420           0 :     ctx->sensor.rapl_range[ ctx->sensor.rapl_cnt ] = range_uj;
    1421           0 :     ctx->sensor.rapl_cnt++;
    1422           0 :   }
    1423           0 :   ctx->sensor.rapl_prev_nanos = 0L;
    1424             : 
    1425           0 :   FD_LOG_INFO(( "diag found %lu hardware sensors and %lu powercap packages", ctx->sensor.cnt, ctx->sensor.rapl_cnt ));
    1426           0 : }
    1427             : 
    1428             : /* mount_device finds the mount that holds path in /proc/self/mountinfo
    1429             :    and copies its device numbers, written "major:minor", into devno.
    1430             :    Returns 0 if the mount is not found. */
    1431             : 
    1432             : static int
    1433             : mount_device( char const * path,
    1434           0 :               char         devno[ static 32 ] ) {
    1435           0 :   int path_fd = open( path, O_PATH|O_CLOEXEC );
    1436           0 :   if( FD_UNLIKELY( path_fd<0 ) ) return 0;
    1437           0 :   ulong mnt_id;
    1438           0 :   int   err = fd_mount_id( path_fd, &mnt_id );
    1439           0 :   close( path_fd );
    1440           0 :   if( FD_UNLIKELY( err ) ) return 0;
    1441             : 
    1442           0 :   int fd = open( "/proc/self/mountinfo", O_RDONLY|O_CLOEXEC );
    1443           0 :   if( FD_UNLIKELY( fd<0 ) ) return 0;
    1444           0 :   static char buf[ 262144 ];
    1445           0 :   ulong       len;
    1446           0 :   err = fd_io_read( fd, buf, sizeof(buf)-1UL, sizeof(buf)-1UL, &len );
    1447           0 :   close( fd );
    1448             :   /* error, or more mounts than fit */
    1449           0 :   if( FD_UNLIKELY( err>=0 ) ) return 0;
    1450           0 :   buf[ len ] = '\0';
    1451             : 
    1452             :   /* fields: id parent major:minor ... */
    1453           0 :   char * lines;
    1454           0 :   for( char * line=strtok_r( buf, "\n", &lines ); line; line=strtok_r( NULL, "\n", &lines ) ) {
    1455           0 :     char * fields;
    1456           0 :     char * id = strtok_r( line, " ", &fields );
    1457           0 :     if( !id || fd_cstr_to_ulong( id )!=mnt_id ) continue;
    1458           0 :     strtok_r( NULL, " ", &fields ); /* parent */
    1459           0 :     char * numbers = strtok_r( NULL, " ", &fields );
    1460           0 :     if( FD_UNLIKELY( !numbers ) ) return 0;
    1461           0 :     fd_cstr_ncpy( devno, numbers, 32UL );
    1462           0 :     return 1;
    1463           0 :   }
    1464           0 :   return 0;
    1465           0 : }
    1466             : 
    1467             : /* Opens the I/O stat file of the block device holding path: the whole
    1468             :    disk for a partition, an md or dm device as is.  Returns -1 if path
    1469             :    is not on a block device. */
    1470             : 
    1471             : static int
    1472           0 : open_disk_stat( char const * path ) {
    1473           0 :   char dev[ PATH_MAX ];
    1474           0 :   char devno[ 32 ];
    1475           0 :   if( !resolve_path_prefix( path, dev ) || !mount_device( dev, devno ) ) return -1;
    1476             : 
    1477           0 :   char file[ PATH_MAX ];
    1478           0 :   FD_TEST( fd_cstr_printf_check( file, sizeof(file), NULL, "/sys/dev/block/%s", devno ) );
    1479           0 :   if( FD_UNLIKELY( !realpath( file, dev ) ) ) return -1;
    1480           0 :   FD_TEST( fd_cstr_printf_check( file, sizeof(file), NULL, "%s/partition", dev ) );
    1481           0 :   if( !access( file, F_OK ) ) *strrchr( dev, '/' ) = '\0';
    1482           0 :   FD_TEST( fd_cstr_printf_check( file, sizeof(file), NULL, "%s/stat", dev ) );
    1483             : 
    1484           0 :   int fd = open( file, O_RDONLY|O_CLOEXEC );
    1485           0 :   if( FD_LIKELY( fd>=0 ) ) FD_LOG_INFO(( "diag reading disk stats of %s for `%s`", strrchr( dev, '/' )+1, path ));
    1486           0 :   return fd;
    1487           0 : }
    1488             : 
    1489             : static void
    1490             : privileged_init( fd_topo_t const *      topo,
    1491           0 :                  fd_topo_tile_t const * tile ) {
    1492           0 :   void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
    1493             : 
    1494           0 :   FD_SCRATCH_ALLOC_INIT( l, scratch );
    1495           0 :   fd_diag_tile_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_diag_tile_t), sizeof(fd_diag_tile_t) );
    1496           0 :   memset( ctx->host_cpu_ticks, 0, sizeof(ctx->host_cpu_ticks) );
    1497             : 
    1498           0 :   FD_TEST( topo->tile_cnt<=FD_DIAG_SYSTEM_TILE_MAX );
    1499             : 
    1500           0 :   FD_TEST( 100L == sysconf( _SC_CLK_TCK ) );
    1501             : 
    1502           0 :   ctx->tile_cnt    = topo->tile_cnt;
    1503           0 :   ctx->gui_enabled = fd_topo_find_tile_out_link( topo, tile, "diag_gui", 0UL )!=ULONG_MAX;
    1504           0 :   ctx->memory.cnt  = topo->wksp_cnt;
    1505           0 :   FD_TEST( ctx->memory.cnt<=FD_TOPO_MAX_WKSPS );
    1506           0 :   for( ulong wksp_idx=0UL; wksp_idx<ctx->memory.cnt; wksp_idx++ ) {
    1507           0 :     fd_topo_wksp_t const * wksp = &topo->workspaces[ wksp_idx ];
    1508           0 :     FD_TEST( fd_cstr_printf_check( ctx->memory.wksp[ wksp_idx ].name,
    1509           0 :                                   sizeof(ctx->memory.wksp[ wksp_idx ].name), NULL,
    1510           0 :                                   "%s_%s.wksp", topo->app_name, wksp->name ) );
    1511           0 :     ctx->memory.wksp[ wksp_idx ].numa_idx  = wksp->numa_idx;
    1512           0 :     ctx->memory.wksp[ wksp_idx ].bytes     = wksp->page_cnt*wksp->page_sz;
    1513           0 :     ctx->memory.wksp[ wksp_idx ].numa_slot = USHORT_MAX;
    1514             : 
    1515           0 :     ushort owners[ FD_DIAG_SYSTEM_TILE_MAX ];
    1516           0 :     ulong  owner_cnt = 0UL;
    1517           0 :     for( ulong tile_idx=0UL; tile_idx<topo->tile_cnt; tile_idx++ ) {
    1518           0 :       fd_topo_tile_t const * owner = &topo->tiles[ tile_idx ];
    1519           0 :       if( topo->objs[ owner->tile_obj_id ].wksp_id!=wksp_idx ) continue;
    1520           0 :       owners[ owner_cnt++ ] = (ushort)tile_idx;
    1521           0 :     }
    1522           0 :     if( FD_LIKELY( owner_cnt==1UL ) ) {
    1523           0 :       ctx->memory.wksp[ wksp_idx ].tile_idx = owners[ 0 ];
    1524           0 :       continue;
    1525           0 :     }
    1526           0 :     if( FD_UNLIKELY( owner_cnt>1UL ) ) {
    1527           0 :       ctx->memory.wksp[ wksp_idx ].tile_idx = DIAG_WKSP_TILE_IDX_SHARED;
    1528           0 :       continue;
    1529           0 :     }
    1530             : 
    1531           0 :     owner_cnt = 0UL;
    1532           0 :     for( ulong link_idx=0UL; link_idx<topo->link_cnt; link_idx++ ) {
    1533           0 :       fd_topo_link_t const * link = &topo->links[ link_idx ];
    1534           0 :       if( topo->objs[ link->mcache_obj_id ].wksp_id!=wksp_idx &&
    1535           0 :           ( !link->mtu || topo->objs[ link->dcache_obj_id ].wksp_id!=wksp_idx ) ) continue;
    1536           0 :       ulong producer_idx = fd_topo_find_link_producer( topo, link );
    1537           0 :       if( producer_idx==ULONG_MAX ) continue;
    1538           0 :       int found = 0;
    1539           0 :       for( ulong owner_idx=0UL; owner_idx<owner_cnt; owner_idx++ )
    1540           0 :         found |= owners[ owner_idx ]==(ushort)producer_idx;
    1541           0 :       if( !found ) owners[ owner_cnt++ ] = (ushort)producer_idx;
    1542           0 :     }
    1543           0 :     if( FD_LIKELY( owner_cnt==1UL ) ) {
    1544           0 :       ctx->memory.wksp[ wksp_idx ].tile_idx = owners[ 0 ];
    1545           0 :       continue;
    1546           0 :     }
    1547           0 :     if( FD_UNLIKELY( owner_cnt>1UL ) ) {
    1548           0 :       ctx->memory.wksp[ wksp_idx ].tile_idx = DIAG_WKSP_TILE_IDX_SHARED;
    1549           0 :       continue;
    1550           0 :     }
    1551             : 
    1552           0 :     for( ulong tile_idx=0UL; tile_idx<topo->tile_cnt; tile_idx++ ) {
    1553           0 :       fd_topo_tile_t const * owner = &topo->tiles[ tile_idx ];
    1554           0 :       int uses_writable = 0;
    1555           0 :       for( ulong obj_idx=0UL; obj_idx<owner->uses_obj_cnt; obj_idx++ ) {
    1556           0 :         ulong obj_id = owner->uses_obj_id[ obj_idx ];
    1557           0 :         uses_writable |= topo->objs[ obj_id ].wksp_id==wksp_idx &&
    1558           0 :                          owner->uses_obj_mode[ obj_idx ]==FD_SHMEM_JOIN_MODE_READ_WRITE;
    1559           0 :       }
    1560           0 :       if( uses_writable ) owners[ owner_cnt++ ] = (ushort)tile_idx;
    1561           0 :     }
    1562           0 :     ctx->memory.wksp[ wksp_idx ].tile_idx = owner_cnt==1UL ? owners[ 0 ] : DIAG_WKSP_TILE_IDX_SHARED;
    1563           0 :   }
    1564           0 :   for( ulong tile_idx=0UL; tile_idx<FD_DIAG_SYSTEM_TILE_MAX; tile_idx++ )
    1565           0 :     ctx->memory.stack_numa_slot[ tile_idx ] = USHORT_MAX;
    1566           0 :   ctx->mount_cnt = 0UL;
    1567           0 :   ctx->file_cnt = 0UL;
    1568           0 :   for( ulong i=0UL; i<FD_TILE_MAX; i++ ) {
    1569           0 :     ctx->stat_fds[ i ]      = -1;
    1570           0 :     ctx->sched_fds[ i ]     = -1;
    1571           0 :     ctx->schedstat_fds[ i ] = -1;
    1572           0 :   }
    1573           0 :   for( ulong i=0UL; i<FD_DIAG_SYSTEM_NUMA_MAX; i++ ) ctx->numa.node[ i ].meminfo_fd = -1;
    1574             : 
    1575           0 :   for( ulong i=0UL; i<topo->tile_cnt; i++ ) {
    1576           0 :     ulong * metrics = fd_metrics_join( fd_topo_obj_laddr( topo, topo->tiles[ i ].metrics_obj_id ) );
    1577             : 
    1578           0 :     for(;;) {
    1579           0 :       ulong pid, tid;
    1580           0 :       if( FD_UNLIKELY( tile->id==i ) ) {
    1581           0 :         pid = fd_sandbox_getpid();
    1582           0 :         tid = fd_sandbox_gettid();
    1583           0 :       } else {
    1584           0 :         pid = fd_metrics_tile( metrics )[ FD_METRICS_GAUGE_TILE_PID_OFF ];
    1585           0 :         tid = fd_metrics_tile( metrics )[ FD_METRICS_GAUGE_TILE_TID_OFF ];
    1586           0 :         if( FD_UNLIKELY( !pid || !tid ) ) {
    1587           0 :           FD_SPIN_PAUSE();
    1588           0 :           continue;
    1589           0 :         }
    1590           0 :       }
    1591             : 
    1592           0 :       ctx->metrics[ i ] = fd_metrics_tile( metrics );
    1593             : 
    1594           0 :       char path[ 64UL ];
    1595           0 :       FD_TEST( fd_cstr_printf_check( path, sizeof( path ), NULL, "/proc/%lu/task/%lu/stat", pid, tid ) );
    1596           0 :       ctx->stat_fds[ i ] = open( path, O_RDONLY );
    1597           0 :       if( FD_UNLIKELY( -1==ctx->stat_fds[ i ] ) ) {
    1598             :         /* Might be a tile that's allowed to shutdown already did so
    1599             :            before we got to here, due to a race condition.  Just
    1600             :            proceed, we will not be able to get metrics for the shut
    1601             :            down process. */
    1602           0 :         if( FD_LIKELY( 2UL!=ctx->metrics[ i ][ FD_METRICS_GAUGE_TILE_STATE_OFF ] ) ) FD_LOG_ERR(( "open stat failed (%i-%s)", errno, strerror( errno ) ));
    1603           0 :         break;
    1604           0 :       }
    1605             : 
    1606           0 :       FD_TEST( fd_cstr_printf_check( path, sizeof( path ), NULL, "/proc/%lu/task/%lu/sched", pid, tid ) );
    1607           0 :       ctx->sched_fds[ i ] = open( path, O_RDONLY );
    1608           0 :       if( FD_UNLIKELY( -1==ctx->sched_fds[ i ] ) ) {
    1609           0 :         if( FD_LIKELY( 2UL!=ctx->metrics[ i ][ FD_METRICS_GAUGE_TILE_STATE_OFF ] ) ) FD_LOG_ERR(( "open sched failed (%i-%s)", errno, strerror( errno ) ));
    1610           0 :         ctx->stat_fds[ i ] = -1;
    1611           0 :         break;
    1612           0 :       }
    1613             : 
    1614             :       /* Absent on kernels without CONFIG_SCHED_INFO: wait then reads 0,
    1615             :          as it did without CONFIG_SCHEDSTATS. */
    1616           0 :       FD_TEST( fd_cstr_printf_check( path, sizeof( path ), NULL, "/proc/%lu/task/%lu/schedstat", pid, tid ) );
    1617           0 :       ctx->schedstat_fds[ i ] = open( path, O_RDONLY );
    1618           0 :       if( FD_UNLIKELY( -1==ctx->schedstat_fds[ i ] && errno!=ENOENT ) ) {
    1619           0 :         if( FD_LIKELY( 2UL!=ctx->metrics[ i ][ FD_METRICS_GAUGE_TILE_STATE_OFF ] ) ) FD_LOG_ERR(( "open schedstat failed (%i-%s)", errno, strerror( errno ) ));
    1620           0 :         ctx->stat_fds[ i ] = -1;
    1621           0 :       }
    1622           0 :       break;
    1623           0 :     }
    1624           0 :   }
    1625             : 
    1626           0 :   ctx->proc_interrupts_fd = open( "/proc/interrupts", O_RDONLY );
    1627           0 :   if( FD_UNLIKELY( -1==ctx->proc_interrupts_fd ) ) FD_LOG_ERR(( "open(/proc/interrupts) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
    1628             : 
    1629           0 :   ctx->proc_softirqs_fd = open( "/proc/softirqs", O_RDONLY );
    1630           0 :   if( FD_UNLIKELY( -1==ctx->proc_softirqs_fd   ) ) FD_LOG_ERR(( "open(/proc/softirqs) failed (%i-%s)",   errno, fd_io_strerror( errno ) ));
    1631             : 
    1632           0 :   ctx->proc_stat_fd = open( "/proc/stat", O_RDONLY );
    1633           0 :   if( FD_UNLIKELY( -1==ctx->proc_stat_fd       ) ) FD_LOG_ERR(( "open(/proc/stat) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
    1634             : 
    1635           0 :   ctx->proc_meminfo_fd = open( "/proc/meminfo", O_RDONLY );
    1636           0 :   if( FD_UNLIKELY( -1==ctx->proc_meminfo_fd    ) ) FD_LOG_ERR(( "open(/proc/meminfo) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
    1637             : 
    1638           0 :   ctx->proc_loadavg_fd = open( "/proc/loadavg", O_RDONLY );
    1639           0 :   ctx->proc_vmstat_fd  = open( "/proc/vmstat",  O_RDONLY );
    1640           0 :   static char const * const psi_path[ 3 ] = { "/proc/pressure/cpu", "/proc/pressure/memory", "/proc/pressure/io" };
    1641           0 :   for( ulong i=0UL; i<3UL; i++ ) ctx->psi_fd[ i ] = open( psi_path[ i ], O_RDONLY );
    1642             : 
    1643           0 :   sensors_init( ctx );
    1644             : 
    1645             :   /* The ms counters count from here, so the first sample is not the
    1646             :      kernel's since-boot value. */
    1647           0 :   ctx->disk_fd = open_disk_stat( tile->diag.accounts_path );
    1648           0 :   memset( ctx->disk_ms, 0, sizeof(ctx->disk_ms) );
    1649           0 :   ulong f[ 11 ];
    1650           0 :   if( !disk_stat_read( ctx, f ) ) {
    1651           0 :     ctx->disk_ms[ 0 ] = f[ 7  ];
    1652           0 :     ctx->disk_ms[ 1 ] = f[ 9  ];
    1653           0 :     ctx->disk_ms[ 2 ] = f[ 10 ];
    1654           0 :   }
    1655           0 :   memcpy( ctx->disk_ms_base, ctx->disk_ms, sizeof(ctx->disk_ms_base) );
    1656             : 
    1657           0 :   ctx->numa.cnt = 0UL;
    1658           0 :   if( FD_UNLIKELY( !ctx->gui_enabled ) ) return;
    1659             : 
    1660           0 :   fd_topo_cpus_t cpus[ 1 ];
    1661           0 :   fd_topo_cpus_init( cpus );
    1662           0 :   ctx->system_resources.cpu_cnt = (uint)fd_ulong_min( cpus->cpu_cnt, FD_DIAG_SYSTEM_CPU_MAX );
    1663           0 :   for( ulong i=0UL; i<ctx->system_resources.cpu_cnt; i++ ) {
    1664           0 :     fd_diag_system_cpu_init( &ctx->system_resources.cpu[ i ], &cpus->cpu[ i ] );
    1665           0 :   }
    1666             : 
    1667           0 :   for( ulong numa_idx=0UL; numa_idx<cpus->numa_node_cnt && ctx->numa.cnt<FD_DIAG_SYSTEM_NUMA_MAX; numa_idx++ ) {
    1668           0 :     char path[ 128 ];
    1669           0 :     FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/devices/system/node/node%lu/meminfo", numa_idx ) );
    1670           0 :     int fd = open( path, O_RDONLY );
    1671           0 :     if( FD_UNLIKELY( fd<0 ) ) {
    1672           0 :       if( FD_UNLIKELY( errno!=ENOENT ) ) FD_LOG_WARNING(( "open `%s` failed (%i-%s)", path, errno, strerror( errno ) ));
    1673           0 :       continue;
    1674           0 :     }
    1675           0 :     ctx->numa.node[ ctx->numa.cnt ].idx        = (ushort)numa_idx;
    1676           0 :     ctx->numa.node[ ctx->numa.cnt ].meminfo_fd = fd;
    1677           0 :     ctx->numa.cnt++;
    1678           0 :   }
    1679             : 
    1680           0 :   for( ulong wksp_idx=0UL; wksp_idx<ctx->memory.cnt; wksp_idx++ ) {
    1681           0 :     ulong numa_slot = 0UL;
    1682           0 :     while( numa_slot<ctx->numa.cnt && ctx->numa.node[ numa_slot ].idx!=ctx->memory.wksp[ wksp_idx ].numa_idx ) numa_slot++;
    1683           0 :     if( FD_UNLIKELY( numa_slot==ctx->numa.cnt ) ) {
    1684           0 :       FD_LOG_WARNING(( "workspace `%s` is assigned to unavailable NUMA node %lu; omitting it from system memory reporting",
    1685           0 :                        ctx->memory.wksp[ wksp_idx ].name, ctx->memory.wksp[ wksp_idx ].numa_idx ));
    1686           0 :       continue;
    1687           0 :     }
    1688           0 :     ctx->memory.wksp[ wksp_idx ].numa_slot = (ushort)numa_slot;
    1689           0 :   }
    1690             : 
    1691           0 :   for( ulong tile_idx=0UL; tile_idx<topo->tile_cnt; tile_idx++ ) {
    1692             :     /* Keep this placement rule in sync with initialize_stacks(). */
    1693           0 :     ulong stack_cpu_idx = topo->tiles[ tile_idx ].cpu_idx<65535UL ? topo->tiles[ tile_idx ].cpu_idx : 0UL;
    1694           0 :     FD_TEST( stack_cpu_idx<cpus->cpu_cnt );
    1695           0 :     ulong stack_numa_idx = cpus->cpu[ stack_cpu_idx ].numa_node;
    1696           0 :     ulong numa_slot = 0UL;
    1697           0 :     while( numa_slot<ctx->numa.cnt && ctx->numa.node[ numa_slot ].idx!=stack_numa_idx ) numa_slot++;
    1698           0 :     if( FD_UNLIKELY( numa_slot==ctx->numa.cnt ) ) {
    1699           0 :       FD_LOG_WARNING(( "stack for tile %s:%lu is assigned to unavailable NUMA node %lu; omitting it from system memory reporting",
    1700           0 :                        topo->tiles[ tile_idx ].name, topo->tiles[ tile_idx ].kind_id, stack_numa_idx ));
    1701           0 :       continue;
    1702           0 :     }
    1703           0 :     ctx->memory.stack_numa_slot[ tile_idx ] = (ushort)numa_slot;
    1704           0 :   }
    1705             : 
    1706           0 :   ulong accdb_idx = fd_topo_find_tile( topo, "accdb", 0UL );
    1707           0 :   if( tile->diag.accounts_path[ 0 ] && accdb_idx!=ULONG_MAX )
    1708           0 :     add_file( ctx, FD_DIAG_SYSTEM_FILE_CATEGORY_ACCOUNTS, tile->diag.accounts_path, -1, ctx->metrics[ accdb_idx ] + FD_METRICS_GAUGE_ACCDB_DISK_ALLOCATED_BYTES_OFF );
    1709           0 :   ulong rserve_idx = fd_topo_find_tile( topo, "rserve", 0UL );
    1710           0 :   if( tile->diag.shreds_path[ 0 ] && rserve_idx!=ULONG_MAX )
    1711           0 :     add_file( ctx, FD_DIAG_SYSTEM_FILE_CATEGORY_SHREDS, tile->diag.shreds_path, -1, ctx->metrics[ rserve_idx ] + FD_METRICS_GAUGE_RSERVE_DISK_PEAK_BYTES_OFF );
    1712           0 :   ulong snapmk_idx = fd_topo_find_tile( topo, "snapmk", 0UL );
    1713           0 :   if( tile->diag.snapshots_path[ 0 ] && snapmk_idx!=ULONG_MAX )
    1714           0 :     add_file( ctx, FD_DIAG_SYSTEM_FILE_CATEGORY_SNAPSHOTS, tile->diag.snapshots_path, -1, ctx->metrics[ snapmk_idx ] + FD_METRICS_GAUGE_SNAPMK_DISK_ALLOCATED_BYTES_OFF );
    1715           0 :   ulong gui_idx = fd_topo_find_tile( topo, "gui", 0UL );
    1716           0 :   if( gui_idx!=ULONG_MAX )
    1717           0 :     add_file( ctx, FD_DIAG_SYSTEM_FILE_CATEGORY_GUI, tile->diag.gui_path, -1, ctx->metrics[ gui_idx ] + FD_METRICS_GAUGE_GUI_DISK_ALLOCATED_BYTES_OFF );
    1718           0 :   int logfile_fd = fd_log_private_logfile_fd();
    1719           0 :   if( logfile_fd>=0 ) add_file( ctx, FD_DIAG_SYSTEM_FILE_CATEGORY_LOGS, tile->diag.log_path, logfile_fd, NULL );
    1720           0 : }
    1721             : 
    1722             : /* Read starttime (field 22) from stat file. Returns 0 on success, 1 if
    1723             :    process died (ESRCH). */
    1724             : 
    1725             : static int
    1726             : read_starttime( int     fd,
    1727             :                 ulong   ns_per_tick,
    1728           0 :                 ulong * out_starttime_nanos ) {
    1729           0 :   char contents[ 4096 ] = {0};
    1730           0 :   ulong contents_len = 0UL;
    1731             : 
    1732           0 :   while( 1 ) {
    1733           0 :     if( FD_UNLIKELY( contents_len>=sizeof( contents ) ) ) FD_LOG_ERR(( "stat contents overflow" ));
    1734           0 :     long n = read( fd, contents + contents_len, sizeof( contents ) - contents_len );
    1735           0 :     if( FD_UNLIKELY( -1==n ) ) {
    1736           0 :       if( FD_UNLIKELY( errno==ESRCH ) ) return 1;
    1737           0 :       FD_LOG_ERR(( "read stat failed (%i-%s)", errno, strerror( errno ) ));
    1738           0 :     }
    1739           0 :     if( FD_LIKELY( 0L==n ) ) break;
    1740           0 :     contents_len += (ulong)n;
    1741           0 :   }
    1742             : 
    1743             :   /* Parse field 22 (starttime) from stat file */
    1744           0 :   char * saveptr;
    1745           0 :   char * token = strtok_r( contents, " ", &saveptr );
    1746           0 :   ulong field_idx = 0UL;
    1747             : 
    1748           0 :   while( token && field_idx<21UL ) {
    1749           0 :     token = strtok_r( NULL, " ", &saveptr );
    1750           0 :     field_idx++;
    1751           0 :   }
    1752             : 
    1753           0 :   if( FD_UNLIKELY( !token || field_idx!=21UL ) ) FD_LOG_ERR(( "starttime (field 22) not found in stat" ));
    1754             : 
    1755           0 :   char * endptr;
    1756           0 :   ulong starttime_ticks = strtoul( token, &endptr, 10 );
    1757           0 :   if( FD_UNLIKELY( *endptr!=' ' && *endptr!='\0' ) ) FD_LOG_ERR(( "strtoul failed for starttime" ));
    1758           0 :   if( FD_UNLIKELY( starttime_ticks==ULONG_MAX ) ) FD_LOG_ERR(( "strtoul overflow for starttime" ));
    1759             : 
    1760           0 :   *out_starttime_nanos = starttime_ticks * ns_per_tick;
    1761           0 :   return 0;
    1762           0 : }
    1763             : 
    1764             : static void
    1765             : unprivileged_init( fd_topo_t const *      topo,
    1766           0 :                    fd_topo_tile_t const * tile ) {
    1767           0 :   fd_diag_tile_t * ctx = fd_topo_obj_laddr( topo, tile->tile_obj_id );
    1768             : 
    1769           0 :   memset( ctx->first_seen_died, 0, sizeof( ctx->first_seen_died ) );
    1770           0 :   fd_clock_tile_init( ctx->clock );
    1771           0 :   ctx->next_report_nanos       = fd_clock_tile_now( ctx->clock );
    1772           0 :   ctx->next_sched_report_nanos = ctx->next_report_nanos;
    1773           0 :   ctx->next_irq_report_nanos   = ctx->next_report_nanos;
    1774           0 :   ctx->next_system_report_nanos = ctx->next_report_nanos;
    1775           0 :   sensor_metrics( ctx, ctx->next_report_nanos ); /* RAPL baseline, so the first report has package power */
    1776           0 :   if( FD_UNLIKELY( ctx->gui_enabled ) ) {
    1777           0 :     ulong out_idx = fd_topo_find_tile_out_link( topo, tile, "diag_gui", 0UL );
    1778           0 :     FD_TEST( out_idx!=ULONG_MAX );
    1779           0 :     fd_topo_link_t const * link = &topo->links[ tile->out_link_id[ out_idx ] ];
    1780           0 :     ctx->system_out.idx    = out_idx;
    1781           0 :     ctx->system_out.mem    = topo->workspaces[ topo->objs[ link->dcache_obj_id ].wksp_id ].wksp;
    1782           0 :     ctx->system_out.chunk0 = fd_dcache_compact_chunk0( ctx->system_out.mem, link->dcache );
    1783           0 :     ctx->system_out.wmark  = fd_dcache_compact_wmark ( ctx->system_out.mem, link->dcache, link->mtu );
    1784           0 :     ctx->system_out.chunk  = ctx->system_out.chunk0;
    1785           0 :   }
    1786             : 
    1787             :   /* Snapshot the cumulative-since-boot /proc interrupt/softirq counters
    1788             :      so the metrics we report are counted since process startup. */
    1789           0 :   memset( ctx->softirq_baseline,    0, sizeof( ctx->softirq_baseline    ) );
    1790           0 :   memset( ctx->device_irq_baseline, 0, sizeof( ctx->device_irq_baseline ) );
    1791           0 :   memset( ctx->tlb_baseline,        0, sizeof( ctx->tlb_baseline        ) );
    1792           0 :   memset( ctx->loc_baseline,        0, sizeof( ctx->loc_baseline        ) );
    1793           0 :   if( FD_UNLIKELY( -1==lseek( ctx->proc_softirqs_fd, 0, SEEK_SET ) ) ) FD_LOG_ERR(( "lseek failed (%i-%s)", errno, strerror( errno ) ));
    1794           0 :   ulong softirq_cpu_cnt = fd_proc_softirqs_sum( ctx->proc_softirqs_fd, ctx->softirq_baseline );
    1795           0 :   if( FD_UNLIKELY( !softirq_cpu_cnt ) ) FD_LOG_WARNING(( "failed to read softirq baseline from /proc/softirqs" ));
    1796             : 
    1797           0 :   if( FD_UNLIKELY( -1==lseek( ctx->proc_interrupts_fd, 0, SEEK_SET ) ) ) FD_LOG_ERR(( "lseek failed (%i-%s)", errno, strerror( errno ) ));
    1798           0 :   ulong interrupt_cpu_cnt = fd_proc_interrupts_read( ctx->proc_interrupts_fd,
    1799           0 :                                                      ctx->device_irq_baseline,
    1800           0 :                                                      ctx->tlb_baseline,
    1801           0 :                                                      ctx->loc_baseline );
    1802           0 :   if( FD_UNLIKELY( !interrupt_cpu_cnt ) ) FD_LOG_WARNING(( "failed to read IRQ baselines from /proc/interrupts" ));
    1803             : 
    1804           0 :   memset( ctx->irq_ticks_baseline, 0, sizeof( ctx->irq_ticks_baseline ) );
    1805           0 :   if( FD_UNLIKELY( -1==lseek( ctx->proc_stat_fd, 0, SEEK_SET ) ) ) FD_LOG_ERR(( "lseek failed (%i-%s)", errno, strerror( errno ) ));
    1806           0 :   ulong stat_cpu_cnt = fd_proc_stat_irq_ticks( ctx->proc_stat_fd, ctx->irq_ticks_baseline );
    1807           0 :   if( FD_UNLIKELY( !stat_cpu_cnt ) ) FD_LOG_WARNING(( "failed to read irq tick baseline from /proc/stat" ));
    1808           0 :   char stat_buf[ 16384 ];
    1809           0 :   if( FD_UNLIKELY( host_cpu_ticks( ctx, stat_buf, sizeof(stat_buf), ctx->host_cpu_baseline ) ) ) memset( ctx->host_cpu_baseline, 0, sizeof(ctx->host_cpu_baseline) );
    1810           0 :   memcpy( ctx->softirq_boot,        ctx->softirq_baseline,    sizeof(ctx->softirq_boot)        );
    1811           0 :   memcpy( ctx->device_irq_boot,     ctx->device_irq_baseline, sizeof(ctx->device_irq_boot)     );
    1812           0 :   memset( ctx->on_tile_softirq,     0,                        sizeof(ctx->on_tile_softirq)     );
    1813           0 :   ctx->on_tile_device_irq = 0UL;
    1814           0 :   memcpy( ctx->owner_irq_baseline,  ctx->device_irq_baseline, sizeof(ctx->owner_irq_baseline)  );
    1815           0 :   memcpy( ctx->owner_tlb_baseline,  ctx->tlb_baseline,        sizeof(ctx->owner_tlb_baseline)  );
    1816           0 :   memcpy( ctx->owner_loc_baseline,  ctx->loc_baseline,        sizeof(ctx->owner_loc_baseline)  );
    1817           0 :   memcpy( ctx->owner_tick_baseline, ctx->irq_ticks_baseline,  sizeof(ctx->owner_tick_baseline) );
    1818           0 :   fd_cpuset_new( ctx->irq_rebase  );
    1819           0 :   fd_cpuset_new( ctx->tick_rebase );
    1820             : 
    1821             :   /* Read starttime (field 22) once at init for idle time calculation.
    1822             :      CLK_TCK is always 100, so 1 tick = 10ms = 10,000,000 ns. */
    1823           0 :   for( ulong i=0UL; i<ctx->tile_cnt; i++ ) {
    1824           0 :     if( FD_LIKELY( -1!=ctx->stat_fds[ i ] ) ) {
    1825           0 :       int died = read_starttime( ctx->stat_fds[ i ], 10000000UL, &ctx->starttime_nanos[ i ] );
    1826           0 :       if( FD_UNLIKELY( died ) ) ctx->stat_fds[ i ] = -1;
    1827           0 :     }
    1828           0 :   }
    1829             : 
    1830           0 :   memset( &ctx->check_engine, 0, sizeof(ctx->check_engine) );
    1831             : 
    1832           0 :   ctx->tiles.bundle_cnt = fd_topo_tile_name_cnt( topo, "bundle" );
    1833           0 :   for( ulong i=0UL; i<ctx->tiles.bundle_cnt; i++ ) ctx->tiles.bundle_tile_idx[ i ] = fd_topo_find_tile( topo, "bundle", i );
    1834           0 :   ctx->tiles.shred_cnt = fd_topo_tile_name_cnt( topo, "shred" );
    1835           0 :   for( ulong i=0UL; i<ctx->tiles.shred_cnt; i++ ) ctx->tiles.shred_tile_idx[ i ] = fd_topo_find_tile( topo, "shred", i );
    1836           0 :   ctx->tiles.votor_idx  = fd_topo_find_tile( topo, "votor",  0UL );
    1837           0 :   ctx->tiles.replay_idx = fd_topo_find_tile( topo, "replay", 0UL );
    1838             : 
    1839           0 :   for( ulong i=0UL; i<FD_TILE_MAX; i++ ) ctx->cpu_to_tile[ i ] = USHORT_MAX;
    1840           0 :   cpu_owner_refresh( ctx, topo );
    1841             : 
    1842           0 :   ctx->is_voting = tile->diag.is_voting;
    1843           0 :   ctx->check_engine.reset_slot_changed_ns   = ctx->next_report_nanos;
    1844           0 :   ctx->check_engine.turbine_slot_changed_ns = ctx->next_report_nanos;
    1845           0 :   ctx->check_engine.byte_snapshot_ns        = ctx->next_report_nanos;
    1846           0 : }
    1847             : 
    1848             : static ulong
    1849             : populate_allowed_seccomp( fd_topo_t const *      topo,
    1850             :                           fd_topo_tile_t const * tile,
    1851             :                           ulong                  out_cnt,
    1852           0 :                           struct sock_filter *   out ) {
    1853           0 :   (void)topo;
    1854           0 :   (void)tile;
    1855             : 
    1856           0 :   populate_sock_filter_policy_fd_diag_tile( out_cnt, out, (uint)fd_log_private_logfile_fd() );
    1857           0 :   return sock_filter_policy_fd_diag_tile_instr_cnt;
    1858           0 : }
    1859             : 
    1860             : static ulong
    1861             : populate_allowed_fds( fd_topo_t const *      topo,
    1862             :                       fd_topo_tile_t const * tile,
    1863             :                       ulong                  out_fds_cnt,
    1864           0 :                       int *                  out_fds ) {
    1865           0 :   fd_diag_tile_t * ctx = fd_topo_obj_laddr( topo, tile->tile_obj_id );
    1866             : 
    1867           0 :   int logfile_fd = fd_log_private_logfile_fd();
    1868           0 :   ulong required_fds = 11UL+3UL*ctx->tile_cnt+ctx->numa.cnt+ctx->mount_cnt+ctx->sensor.cnt+ctx->sensor.rapl_cnt+(ulong)(-1!=logfile_fd);
    1869           0 :   for( ulong i=0UL; i<ctx->file_cnt; i++ )
    1870           0 :     required_fds += (ulong)( ctx->files[ i ].data_fd>=0 && ctx->files[ i ].data_fd!=logfile_fd );
    1871           0 :   if( FD_UNLIKELY( out_fds_cnt<required_fds ) ) FD_LOG_ERR(( "out_fds_cnt %lu", out_fds_cnt ));
    1872             : 
    1873           0 :   ulong out_cnt = 0UL;
    1874           0 :   out_fds[ out_cnt++ ] = 2; /* stderr */
    1875           0 :   if( FD_LIKELY( -1!=logfile_fd ) )
    1876           0 :     out_fds[ out_cnt++ ] = logfile_fd; /* logfile */
    1877           0 :   out_fds[ out_cnt++ ] = ctx->proc_interrupts_fd; /* /proc/interrupts */
    1878           0 :   out_fds[ out_cnt++ ] = ctx->proc_softirqs_fd;   /* /proc/softirqs */
    1879           0 :   out_fds[ out_cnt++ ] = ctx->proc_stat_fd;       /* /proc/stat */
    1880           0 :   out_fds[ out_cnt++ ] = ctx->proc_meminfo_fd;    /* /proc/meminfo */
    1881           0 :   if( ctx->proc_loadavg_fd>=0 ) out_fds[ out_cnt++ ] = ctx->proc_loadavg_fd; /* /proc/loadavg */
    1882           0 :   if( ctx->proc_vmstat_fd >=0 ) out_fds[ out_cnt++ ] = ctx->proc_vmstat_fd;  /* /proc/vmstat */
    1883           0 :   for( ulong i=0UL; i<3UL; i++ ) if( ctx->psi_fd[ i ]>=0 ) out_fds[ out_cnt++ ] = ctx->psi_fd[ i ]; /* /proc/pressure */
    1884           0 :   if( ctx->disk_fd>=0 ) out_fds[ out_cnt++ ] = ctx->disk_fd; /* /sys/block/<dev>/stat */
    1885           0 :   for( ulong i=0UL; i<ctx->tile_cnt; i++ ) {
    1886           0 :     if( -1!=ctx->stat_fds[ i ] )      out_fds[ out_cnt++ ] = ctx->stat_fds[ i ];      /* /proc/<pid>/task/<tid>/stat */
    1887           0 :     if( -1!=ctx->sched_fds[ i ] )     out_fds[ out_cnt++ ] = ctx->sched_fds[ i ];     /* /proc/<pid>/task/<tid>/sched */
    1888           0 :     if( -1!=ctx->schedstat_fds[ i ] ) out_fds[ out_cnt++ ] = ctx->schedstat_fds[ i ]; /* /proc/<pid>/task/<tid>/schedstat */
    1889           0 :   }
    1890           0 :   for( ulong i=0UL; i<ctx->numa.cnt; i++ )
    1891           0 :     if( ctx->numa.node[ i ].meminfo_fd>=0 ) out_fds[ out_cnt++ ] = ctx->numa.node[ i ].meminfo_fd;
    1892           0 :   for( ulong i=0UL; i<ctx->mount_cnt; i++ ) out_fds[ out_cnt++ ] = ctx->mounts[ i ].fd;
    1893           0 :   for( ulong i=0UL; i<ctx->sensor.cnt;      i++ ) out_fds[ out_cnt++ ] = ctx->sensor.fd     [ i ]; /* hwmon */
    1894           0 :   for( ulong i=0UL; i<ctx->sensor.rapl_cnt; i++ ) out_fds[ out_cnt++ ] = ctx->sensor.rapl_fd[ i ]; /* powercap energy_uj */
    1895           0 :   for( ulong i=0UL; i<ctx->file_cnt; i++ )
    1896           0 :     if( ctx->files[ i ].data_fd>=0 && ctx->files[ i ].data_fd!=logfile_fd )
    1897           0 :       out_fds[ out_cnt++ ] = ctx->files[ i ].data_fd;
    1898           0 :   return out_cnt;
    1899           0 : }
    1900             : 
    1901           0 : #define STEM_BURST (1UL)
    1902           0 : #define STEM_LAZY  ((long)10e6) /* 10ms */
    1903             : #define STEM_ALWAYS_PARK 1
    1904             : 
    1905           0 : #define STEM_CALLBACK_CONTEXT_TYPE  fd_diag_tile_t
    1906           0 : #define STEM_CALLBACK_CONTEXT_ALIGN alignof(fd_diag_tile_t)
    1907             : 
    1908           0 : #define STEM_CALLBACK_NEXT_DEADLINE       next_deadline
    1909           0 : #define STEM_CALLBACK_DURING_HOUSEKEEPING during_housekeeping
    1910           0 : #define STEM_CALLBACK_BEFORE_CREDIT       before_credit
    1911             : 
    1912             : #include "../../disco/stem/fd_stem.c"
    1913             : 
    1914             : 
    1915             : fd_topo_run_tile_t fd_tile_diag = {
    1916             :   .name                     = "diag",
    1917             :   .populate_allowed_seccomp = populate_allowed_seccomp,
    1918             :   .populate_allowed_fds     = populate_allowed_fds,
    1919             :   .scratch_align            = scratch_align,
    1920             :   .scratch_footprint        = scratch_footprint,
    1921             :   .privileged_init          = privileged_init,
    1922             :   .unprivileged_init        = unprivileged_init,
    1923             :   .run                      = stem_run,
    1924             : };

Generated by: LCOV version 1.14