LCOV - code coverage report
Current view: top level - app/shared/commands/watch - watch.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 1166 0.0 %
Date: 2026-06-29 05:51:35 Functions: 0 35 0.0 %

          Line data    Source code
       1             : #include "watch.h"
       2             : #include "generated/watch_seccomp.h"
       3             : 
       4             : #include "../../../../discof/restore/fd_snapct_tile.h"
       5             : #include "../../../../discof/gossip/fd_gossip_tile.h"
       6             : #include "../../../../disco/metrics/fd_metrics.h"
       7             : #include "../../../../disco/node_info/fd_node_info.h"
       8             : #include "../../../../disco/genesis/fd_genesis_cluster.h"
       9             : #include "../../../../util/pod/fd_pod.h"
      10             : #include "../../../../util/tile/fd_tile.h"
      11             : 
      12             : #include <errno.h>
      13             : #include <unistd.h>
      14             : #include <sys/resource.h>
      15             : #include <linux/capability.h>
      16             : 
      17             : void
      18             : watch_cmd_perm( args_t *         args FD_PARAM_UNUSED,
      19             :                 fd_cap_chk_t *   chk,
      20           0 :                 config_t const * config ) {
      21           0 :   ulong mlock_limit = fd_topo_mlock( &config->topo );
      22             : 
      23           0 :   fd_cap_chk_raise_rlimit( chk, "watch", RLIMIT_MEMLOCK, mlock_limit, "call `rlimit(2)` to increase `RLIMIT_MEMLOCK` so all memory can be locked with `mlock(2)`" );
      24             : 
      25           0 :   if( fd_sandbox_requires_cap_sys_admin( config->uid, config->gid ) )
      26           0 :     fd_cap_chk_cap( chk, "watch", CAP_SYS_ADMIN,               "call `unshare(2)` with `CLONE_NEWUSER` to sandbox the process in a user namespace" );
      27           0 :   if( FD_LIKELY( getuid() != config->uid ) )
      28           0 :     fd_cap_chk_cap( chk, "watch", CAP_SETUID,                  "call `setresuid(2)` to switch uid to the sanbox user" );
      29           0 :   if( FD_LIKELY( getgid() != config->gid ) )
      30           0 :     fd_cap_chk_cap( chk, "watch", CAP_SETGID,                  "call `setresgid(2)` to switch gid to the sandbox user" );
      31           0 : }
      32             : 
      33             : 
      34             : static ulong lines_printed;
      35             : static int ended_on_newline = 1;
      36             : 
      37             : static char  frame_buf[ 65536UL ];
      38             : static ulong frame_len;
      39             : 
      40             : static void
      41           0 : flush_frame( void ) {
      42           0 :   ulong written = 0UL;
      43           0 :   while( written<frame_len ) {
      44           0 :     long w = write( STDOUT_FILENO, frame_buf+written, frame_len-written );
      45           0 :     if( FD_UNLIKELY( -1==w && errno==EAGAIN ) ) continue;
      46           0 :     else if( FD_UNLIKELY( -1==w ) ) FD_LOG_ERR(( "write() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
      47           0 :     else if( FD_UNLIKELY( 0==w ) ) break;
      48           0 :     written += (ulong)w;
      49           0 :   }
      50           0 :   frame_len = 0UL;
      51           0 : }
      52             : 
      53             : static int
      54           0 : drain( int fd ) {
      55           0 :   int needs_reprint = 0;
      56             : 
      57           0 :   while( 1 ) {
      58           0 :     uchar buf[ 16384UL ];
      59           0 :     long result = read( fd, buf, sizeof(buf) );
      60           0 :     if( FD_UNLIKELY( -1==result && errno==EAGAIN ) ) break;
      61           0 :     else if( FD_UNLIKELY( -1==result ) ) FD_LOG_ERR(( "read() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
      62             : 
      63           0 :     if( FD_LIKELY( !needs_reprint ) ) {
      64             :       /* Buffer the erase sequence and first log chunk together so the
      65             :          terminal never renders a blank frame between erase and content. */
      66           0 :       frame_len = 0UL;
      67           0 :       if( FD_UNLIKELY( !ended_on_newline ) ) {
      68           0 :         FD_TEST( fd_cstr_printf_check( frame_buf, sizeof(frame_buf), &frame_len, "\033[%luA\033[%luM\033[1A\033[0J", lines_printed, lines_printed ) );
      69           0 :       } else {
      70           0 :         FD_TEST( fd_cstr_printf_check( frame_buf, sizeof(frame_buf), &frame_len, "\033[%luA\033[%luM\033[0J", lines_printed, lines_printed ) );
      71           0 :       }
      72           0 :     }
      73           0 :     FD_TEST( frame_len+(ulong)result<=sizeof(frame_buf) );
      74           0 :     fd_memcpy( frame_buf+frame_len, buf, (ulong)result );
      75           0 :     frame_len += (ulong)result;
      76           0 :     flush_frame();
      77           0 :     needs_reprint = 1;
      78             : 
      79           0 :     ended_on_newline = buf[ (ulong)result-1UL ]=='\n';
      80           0 :   }
      81             : 
      82           0 :   return needs_reprint;
      83           0 : }
      84             : 
      85             : static char *
      86             : fmt_bytes( char * buf,
      87             :            ulong  buf_sz,
      88           0 :            long   bytes ) {
      89           0 :   char * tmp = fd_alloca_check( 1UL, buf_sz );
      90           0 :   if( FD_LIKELY( 8L*bytes<1000L ) ) FD_TEST( fd_cstr_printf_check( tmp, buf_sz, NULL, "%ld bits", 8L*bytes ) );
      91           0 :   else if( FD_LIKELY( 8L*bytes<1000000L ) ) FD_TEST( fd_cstr_printf_check( tmp, buf_sz, NULL, "%.1f Kbit", (double)(8L*bytes)/1000.0 ) );
      92           0 :   else if( FD_LIKELY( 8L*bytes<1000000000L ) ) FD_TEST( fd_cstr_printf_check( tmp, buf_sz, NULL, "%.1f Mbit", (double)(8L*bytes)/1000000.0 ) );
      93           0 :   else FD_TEST( fd_cstr_printf_check( tmp, buf_sz, NULL, "%.1f Gbit", (double)(8L*bytes)/1000000000.0 ) );
      94             : 
      95           0 :   FD_TEST( fd_cstr_printf_check( buf, buf_sz, NULL, "%10s", tmp ) );
      96           0 :   return buf;
      97           0 : }
      98             : 
      99             : static char *
     100             : fmt_count( char * buf,
     101             :            ulong  buf_sz,
     102           0 :            ulong  count ) {
     103           0 :   char * tmp = fd_alloca_check( 1UL, buf_sz );
     104           0 :   if( FD_LIKELY( count<1000UL ) ) FD_TEST( fd_cstr_printf_check( tmp, buf_sz, NULL, "%lu", count ) );
     105           0 :   else if( FD_LIKELY( count<1000000UL ) ) FD_TEST( fd_cstr_printf_check( tmp, buf_sz, NULL, "%.1f K", (double)count/1000.0 ) );
     106           0 :   else if( FD_LIKELY( count<1000000000UL ) ) FD_TEST( fd_cstr_printf_check( tmp, buf_sz, NULL, "%.1f M", (double)count/1000000.0 ) );
     107             : 
     108           0 :   FD_TEST( fd_cstr_printf_check( buf, buf_sz, NULL, "%10s", tmp ) );
     109           0 :   return buf;
     110           0 : }
     111             : 
     112             : static char *
     113             : fmt_countf( char * buf,
     114             :             ulong  buf_sz,
     115           0 :             double count ) {
     116           0 :   char * tmp = fd_alloca_check( 1UL, buf_sz );
     117           0 :   if( FD_LIKELY( count<1000UL ) ) FD_TEST( fd_cstr_printf_check( tmp, buf_sz, NULL, "%.1f", count ) );
     118           0 :   else if( FD_LIKELY( count<1000000UL ) ) FD_TEST( fd_cstr_printf_check( tmp, buf_sz, NULL, "%.1f K", (double)count/1000.0 ) );
     119           0 :   else if( FD_LIKELY( count<1000000000UL ) ) FD_TEST( fd_cstr_printf_check( tmp, buf_sz, NULL, "%.1f M", (double)count/1000000.0 ) );
     120           0 :   else memcpy( tmp, "-", 2UL );
     121             : 
     122           0 :   FD_TEST( fd_cstr_printf_check( buf, buf_sz, NULL, "%10s", tmp ) );
     123           0 :   return buf;
     124           0 : }
     125             : 
     126             : static char *
     127             : fmt_count_tight( char * buf,
     128             :                  ulong  buf_sz,
     129           0 :                  ulong  count ) {
     130           0 :   char * tmp = fd_alloca_check( 1UL, buf_sz );
     131           0 :   if(      count<1000UL )       FD_TEST( fd_cstr_printf_check( tmp, buf_sz, NULL, "%lu",    count            ) );
     132           0 :   else if( count<1000000UL )    FD_TEST( fd_cstr_printf_check( tmp, buf_sz, NULL, "%.1fK", (double)count/1e3 ) );
     133           0 :   else if( count<1000000000UL ) FD_TEST( fd_cstr_printf_check( tmp, buf_sz, NULL, "%.1fM", (double)count/1e6 ) );
     134           0 :   else                          FD_TEST( fd_cstr_printf_check( tmp, buf_sz, NULL, "%.1fG", (double)count/1e9 ) );
     135           0 :   FD_TEST( fd_cstr_printf_check( buf, buf_sz, NULL, "%6s", tmp ) );
     136           0 :   return buf;
     137           0 : }
     138             : 
     139             : static char *
     140             : fmt_countf_tight( char * buf,
     141             :                   ulong  buf_sz,
     142           0 :                   double count ) {
     143           0 :   char * tmp = fd_alloca_check( 1UL, buf_sz );
     144           0 :   if(      count<1000.0 )       FD_TEST( fd_cstr_printf_check( tmp, buf_sz, NULL, "%.1f",  count     ) );
     145           0 :   else if( count<1000000.0 )    FD_TEST( fd_cstr_printf_check( tmp, buf_sz, NULL, "%.1fK", count/1e3 ) );
     146           0 :   else if( count<1000000000.0 ) FD_TEST( fd_cstr_printf_check( tmp, buf_sz, NULL, "%.1fM", count/1e6 ) );
     147           0 :   else                          FD_TEST( fd_cstr_printf_check( tmp, buf_sz, NULL, "%.1fG", count/1e9 ) );
     148           0 :   FD_TEST( fd_cstr_printf_check( buf, buf_sz, NULL, "%6s", tmp ) );
     149           0 :   return buf;
     150           0 : }
     151             : 
     152             : static long
     153             : diff_link( config_t const * config,
     154             :                  char const *     link_name,
     155             :                  ulong const *    prev_link,
     156             :                  ulong const *    cur_link,
     157           0 :                  ulong            idx ) {
     158           0 :   long result = 0L;
     159             : 
     160           0 :   ulong overall_polled_idx = 0UL;
     161           0 :   for( ulong i=0UL; i<config->topo.tile_cnt; i++ ) {
     162           0 :     fd_topo_tile_t const * tile = &config->topo.tiles[ i ];
     163           0 :     for( ulong j=0UL; j<config->topo.tiles[ i ].in_cnt; j++ ) {
     164           0 :       fd_topo_link_t const * link = &config->topo.links[ tile->in_link_id[ j ] ];
     165           0 :       if( FD_UNLIKELY( !tile->in_link_poll[ j ] ) ) continue;
     166             : 
     167           0 :       if( FD_LIKELY( !strcmp( link->name, link_name ) ) ) {
     168           0 :         result += (long)cur_link[ overall_polled_idx*8UL+idx ]-(long)prev_link[ overall_polled_idx*8UL+idx ];
     169           0 :       }
     170             : 
     171           0 :       overall_polled_idx++;
     172           0 :     }
     173           0 :   }
     174           0 :   return result;
     175           0 : }
     176             : 
     177             : static long
     178             : diff_tile( config_t const * config,
     179             :            char const *     tile_name,
     180             :            ulong const *    prev_tile,
     181             :            ulong const *    cur_tile,
     182           0 :            ulong            idx ) {
     183           0 :   long result = 0L;
     184             : 
     185           0 :   for( ulong i=0UL; i<config->topo.tile_cnt; i++ ) {
     186           0 :     fd_topo_tile_t const * tile = &config->topo.tiles[ i ];
     187           0 :     if( FD_UNLIKELY( strcmp( tile->name, tile_name ) ) ) continue;
     188           0 :     result += (long)cur_tile[ i*FD_METRICS_TOTAL_SZ+idx ]-(long)prev_tile[ i*FD_METRICS_TOTAL_SZ+idx ];
     189           0 :   }
     190           0 :   return result;
     191           0 : }
     192             : 
     193             : static ulong
     194           0 : total_crds( ulong const * metrics ) {
     195           0 :   ulong sum = 0UL;
     196           0 :   for( ulong i=0UL; i<FD_METRICS_ENUM_CRDS_VALUE_CNT; i++ ) {
     197           0 :     sum += metrics[ MIDX( GAUGE, GOSSIP, CRDS_OCCUPIED_CONTACT_INFO_V1 )+i ];
     198           0 :   }
     199           0 :   return sum;
     200           0 : }
     201             : 
     202             : static ulong
     203           0 : total_regime( ulong const * metrics ) {
     204           0 :   ulong sum = 0UL;
     205           0 :   for( ulong i=0UL; i<FD_METRICS_ENUM_TILE_REGIME_CNT; i++ ) {
     206           0 :     sum += metrics[ MIDX( COUNTER, TILE, REGIME_DURATION_NANOS )+i ];
     207           0 :   }
     208           0 :   return sum;
     209           0 : }
     210             : 
     211             : /* Bench */
     212             : static ulong tps_sent_samples_idx = 0UL;
     213             : static ulong tps_sent_samples[ 200UL ];
     214             : /* Replay */
     215             : static ulong cups_samples_idx = 0UL;
     216             : static ulong cups_samples[ 100UL ];
     217             : static ulong sps_samples_idx = 0UL;
     218             : static ulong sps_samples[ 200UL ];
     219             : static ulong tps_samples_idx = 0UL;
     220             : static ulong tps_samples[ 200UL ];
     221             : /* Snapshot */
     222             : static ulong snapshot_rx_idx = 0UL;
     223             : static ulong snapshot_rx_samples[ 100UL ];
     224             : static ulong snapshot_acc_idx = 0UL;
     225             : static ulong snapshot_acc_samples[ 100UL ];
     226             : static ulong snapshot_wr_idx = 0UL;
     227             : static ulong snapshot_wr_samples[ 100UL ];
     228             : /* Event */
     229             : static ulong events_sent_samples_idx = 0UL;
     230             : static ulong events_sent_samples[ 100UL ];
     231             : static ulong events_acked_samples_idx = 0UL;
     232             : static ulong events_acked_samples[ 100UL ];
     233             : static ulong event_bytes_written_samples_idx = 0UL;
     234             : static ulong event_bytes_written_samples[ 100UL ];
     235             : static ulong event_bytes_read_samples_idx = 0UL;
     236             : static ulong event_bytes_read_samples[ 100UL ];
     237             : /* Accounts */
     238             : static ulong accdb_samples_idx = 0UL;
     239             : static ulong accdb_acquired_samples[ 200UL ];
     240             : static ulong accdb_writable_samples[ 200UL ];
     241             : static ulong accdb_missed_samples  [ 200UL ];
     242             : static ulong accdb_evicted_samples [ 200UL ];
     243             : static ulong accdb_waited_samples  [ 200UL ];
     244             : static ulong accdb_bytes_rd_samples[ 200UL ];
     245             : static ulong accdb_bytes_wr_samples[ 200UL ];
     246             : static ulong accdb_bytes_cp_samples[ 200UL ];
     247             : static ulong accdb_bytes_pe_samples[ 200UL ];
     248             : static ulong accdb_evicted_class_samples[ 8UL ][ 200UL ];
     249             : static ulong accdb_preevicted_samples[ 200UL ];
     250             : static ulong accdb_preevicted_class_samples[ 8UL ][ 200UL ];
     251             : static ulong accdb_committed_new_class_samples[ 8UL ][ 200UL ];
     252             : static ulong accdb_committed_overwrite_class_samples[ 8UL ][ 200UL ];
     253             : /* Repair server */
     254             : static ulong shreds_stored_samples_idx = 0UL;
     255             : static ulong shreds_stored_sample[ 200UL ]  ;
     256             : static ulong rserve_rps_valid_samples_idx = 0UL;
     257             : static ulong rserve_rps_valid_samples[ 100UL ];
     258             : static ulong rserve_rps_invalid_samples_idx = 0UL;
     259             : static ulong rserve_rps_invalid_samples[ 100UL ];
     260             : 
     261             : #define RESET   "\033[0m"
     262             : #define BOLD    "\033[1m"
     263             : #define UNBOLD  "\033[22m"
     264             : 
     265             : #define RED     "\033[31m"
     266             : #define GREEN   "\033[32m"
     267             : #define ORANGE  "\033[38;5;208m"
     268             : #define YELLOW  "\033[33m"
     269             : #define BLUE    "\033[34m"
     270             : #define MAGENTA "\033[35m"
     271             : #define CYAN    "\033[36m"
     272             : 
     273             : #define BGREEN  "\033[92m"
     274             : #define BYELLOW "\033[93m"
     275             : 
     276             : #define CLEARLN "\033[K"
     277             : 
     278           0 : #define PRINT(...) do {                          \
     279           0 :   ulong _len;                                    \
     280           0 :   FD_TEST( fd_cstr_printf_check( frame_buf+frame_len, sizeof(frame_buf)-frame_len, &_len, __VA_ARGS__ ) ); \
     281           0 :   frame_len += _len;                             \
     282           0 : } while(0)
     283             : 
     284             : #define DIFF_LINK_BYTES( link_name, metric_type, metric_subtype, metric ) (__extension__({ \
     285             :     long bytes = diff_link( config, link_name, prev_link, cur_link, MIDX( metric_type, metric_subtype, metric ) ); \
     286             :      fmt_bytes( fd_alloca_check( 1UL, 64UL ), 64UL, bytes );                               \
     287             :   }))
     288             : 
     289           0 : #define DIFF_BYTES( tile_name, metric_type, metric_subtype, metric ) (__extension__({ \
     290           0 :     long bytes = diff_tile( config, tile_name, prev_tile, cur_tile, MIDX( metric_type, metric_subtype, metric ) ); \
     291           0 :      fmt_bytes( fd_alloca_check( 1UL, 64UL ), 64UL, bytes );                               \
     292           0 :   }))
     293             : 
     294           0 : #define COUNT( count ) (__extension__({                     \
     295           0 :     fmt_count( fd_alloca_check( 1UL, 64UL ), 64UL, count ); \
     296           0 :   }))
     297             : 
     298           0 : #define COUNTF( count ) (__extension__({                     \
     299           0 :     fmt_countf( fd_alloca_check( 1UL, 64UL ), 64UL, count ); \
     300           0 :   }))
     301             : 
     302           0 : #define COUNT_T( count ) (__extension__({                          \
     303           0 :     fmt_count_tight( fd_alloca_check( 1UL, 32UL ), 32UL, count );  \
     304           0 :   }))
     305             : 
     306           0 : #define COUNTF_T( count ) (__extension__({                         \
     307           0 :     fmt_countf_tight( fd_alloca_check( 1UL, 32UL ), 32UL, count ); \
     308           0 :   }))
     309             : 
     310             : static int
     311             : write_bench( config_t const * config,
     312             :              ulong const *    cur_tile,
     313           0 :              ulong const *    prev_tile ) {
     314           0 :   if( FD_UNLIKELY( fd_topo_find_tile( &config->topo, "benchs", 0UL )==ULONG_MAX ) ) return 0;
     315             : 
     316           0 :   ulong tps_sum = 0UL;
     317           0 :   ulong num_tps_samples = fd_ulong_min( tps_sent_samples_idx, sizeof(tps_sent_samples)/sizeof(tps_sent_samples[0]));
     318           0 :   for( ulong i=0UL; i<num_tps_samples; i++ ) tps_sum += tps_sent_samples[ i ];
     319           0 :   char * tps_str = COUNTF( 100.0*(double)tps_sum/(double)num_tps_samples );
     320             : 
     321           0 :   PRINT( "๐ŸŒถ  " BOLD BGREEN "BENCH......." RESET UNBOLD
     322           0 :          " " BOLD "GENERATED TPS" UNBOLD " %s"
     323           0 :          " " BOLD "BENCHG BUSY"   UNBOLD, tps_str );
     324           0 :   for( ulong i=0UL; i<config->topo.tile_cnt; i++ ) {
     325           0 :     if( FD_LIKELY( strcmp( config->topo.tiles[ i ].name, "benchg" ) ) ) continue;
     326             : 
     327           0 :     ulong total_ticks = total_regime( &cur_tile[ i*FD_METRICS_TOTAL_SZ ] )-total_regime( &prev_tile[ i*FD_METRICS_TOTAL_SZ ] );
     328           0 :     double backp_pct = 100.0*(double)( diff_tile( config, "benchg", prev_tile, cur_tile, MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_BACKPRESSURE_PREFRAG ) ) )/(double)total_ticks;
     329           0 :     double idle_pct = 100.0*(double)( diff_tile( config, "benchg", prev_tile, cur_tile, MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_CAUGHT_UP_POSTFRAG ) ) )/(double)total_ticks;
     330           0 :     double busy_pct = 100.0 - idle_pct - backp_pct;
     331             : 
     332           0 :     PRINT( " %.1f %%", busy_pct );
     333           0 :   }
     334             : 
     335           0 :   PRINT( " " BOLD "BENCHS BUSY" UNBOLD );
     336           0 :   for( ulong i=0UL; i<config->topo.tile_cnt; i++ ) {
     337           0 :     if( FD_LIKELY( strcmp( config->topo.tiles[ i ].name, "benchs" ) ) ) continue;
     338             : 
     339           0 :     ulong total_ticks = total_regime( &cur_tile[ i*FD_METRICS_TOTAL_SZ ] )-total_regime( &prev_tile[ i*FD_METRICS_TOTAL_SZ ] );
     340           0 :     double backp_pct = 100.0*(double)( diff_tile( config, "benchs", prev_tile, cur_tile, MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_BACKPRESSURE_PREFRAG ) ) )/(double)total_ticks;
     341           0 :     double idle_pct = 100.0*(double)( diff_tile( config, "benchs", prev_tile, cur_tile, MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_CAUGHT_UP_POSTFRAG ) ) )/(double)total_ticks;
     342           0 :     double busy_pct = 100.0 - idle_pct - backp_pct;
     343             : 
     344           0 :     PRINT( " %.1f %%", busy_pct );
     345           0 :   }
     346             : 
     347           0 :   PRINT( CLEARLN "\n" );
     348           0 :   return 1;
     349           0 : }
     350             : 
     351             : static void
     352             : write_backtest( config_t const * config,
     353           0 :                 ulong const *    cur_tile ) {
     354           0 :   ulong backt_idx = fd_topo_find_tile( &config->topo, "backt", 0UL );
     355           0 :   ulong start_slot = cur_tile[ backt_idx*FD_METRICS_TOTAL_SZ+MIDX( GAUGE, BACKT, START_SLOT ) ];
     356           0 :   ulong final_slot = cur_tile[ backt_idx*FD_METRICS_TOTAL_SZ+MIDX( GAUGE, BACKT, FINAL_SLOT ) ];
     357             : 
     358           0 :   ulong replay_idx = fd_topo_find_tile( &config->topo, "replay", 0UL );
     359           0 :   ulong current_slot = cur_tile[ replay_idx*FD_METRICS_TOTAL_SZ+MIDX( GAUGE, REPLAY, ROOT_SLOT ) ];
     360           0 :   current_slot = current_slot ? current_slot : start_slot;
     361             : 
     362           0 :   ulong completed_slots = current_slot-start_slot;
     363             : 
     364           0 :   if( FD_UNLIKELY( final_slot==ULONG_MAX ) ) {
     365           0 :     PRINT( "๐Ÿงช " BOLD BGREEN "BACKTEST...." RESET UNBOLD
     366           0 :             " " BOLD "PCT" UNBOLD "     ? (%lu/?)" CLEARLN "\n", completed_slots );
     367           0 :     return;
     368           0 :   }
     369             : 
     370           0 :   ulong  total_slots = final_slot-start_slot;
     371           0 :   double progress    = total_slots ? 100.0 * (double)completed_slots / (double)total_slots : 100.0;
     372           0 :   PRINT( "๐Ÿงช " BOLD BGREEN "BACKTEST...." RESET UNBOLD
     373           0 :          " " BOLD "PCT" UNBOLD " %.1f %% (%lu/%lu)" CLEARLN "\n",
     374           0 :     progress, completed_slots, total_slots );
     375           0 : }
     376             : 
     377             : static void
     378             : write_snapshots( config_t const * config,
     379             :                  ulong const *    cur_tile,
     380           0 :                  ulong const *    prev_tile ) {
     381           0 :   ulong snapct_idx = fd_topo_find_tile( &config->topo, "snapct", 0UL );
     382           0 :   ulong state = cur_tile[ snapct_idx*FD_METRICS_TOTAL_SZ+MIDX( GAUGE, SNAPCT, STATE ) ];
     383             : 
     384           0 :   ulong bytes_read = cur_tile[ snapct_idx*FD_METRICS_TOTAL_SZ+MIDX( GAUGE, SNAPCT, FULL_BYTES_READ ) ];
     385           0 :   ulong bytes_total = cur_tile[ snapct_idx*FD_METRICS_TOTAL_SZ+MIDX( GAUGE, SNAPCT, FULL_SIZE_BYTES ) ];
     386             : 
     387           0 :   double progress = 0.0;
     388           0 :   switch( state ) {
     389           0 :     case FD_SNAPCT_STATE_WAITING_FOR_PEERS:
     390           0 :     case FD_SNAPCT_STATE_WAITING_FOR_PEERS_INCREMENTAL:
     391           0 :     case FD_SNAPCT_STATE_COLLECTING_PEERS:
     392           0 :     case FD_SNAPCT_STATE_COLLECTING_PEERS_INCREMENTAL:
     393           0 :       break;
     394           0 :     case FD_SNAPCT_STATE_READING_FULL_FILE:
     395           0 :     case FD_SNAPCT_STATE_FLUSHING_FULL_FILE_FINI:
     396           0 :     case FD_SNAPCT_STATE_FLUSHING_FULL_FILE_DONE:
     397           0 :     case FD_SNAPCT_STATE_READING_INCREMENTAL_FILE:
     398           0 :     case FD_SNAPCT_STATE_FLUSHING_INCREMENTAL_FILE_FINI:
     399           0 :     case FD_SNAPCT_STATE_FLUSHING_INCREMENTAL_FILE_DONE:
     400           0 :     case FD_SNAPCT_STATE_READING_FULL_HTTP:
     401           0 :     case FD_SNAPCT_STATE_FLUSHING_FULL_HTTP_FINI:
     402           0 :     case FD_SNAPCT_STATE_FLUSHING_FULL_HTTP_DONE:
     403           0 :     case FD_SNAPCT_STATE_READING_INCREMENTAL_HTTP:
     404           0 :     case FD_SNAPCT_STATE_FLUSHING_INCREMENTAL_HTTP_FINI:
     405           0 :     case FD_SNAPCT_STATE_FLUSHING_INCREMENTAL_HTTP_DONE:
     406           0 :       if( FD_LIKELY( bytes_total>0UL ) ) progress = 100.0 * (double)bytes_read / (double)bytes_total;
     407           0 :       break;
     408           0 :     case FD_SNAPCT_STATE_SHUTDOWN:
     409           0 :       progress = 100.0;
     410           0 :       break;
     411           0 :   }
     412             : 
     413           0 :   ulong snap_rx_sum = 0UL;
     414           0 :   ulong num_snap_rx_samples = fd_ulong_min( snapshot_rx_idx, sizeof(snapshot_rx_samples)/sizeof(snapshot_rx_samples[0]) );
     415           0 :   for( ulong i=0UL; i<num_snap_rx_samples; i++ ) snap_rx_sum += snapshot_rx_samples[ i ];
     416           0 :   double megabytes_per_second = 0.0;
     417           0 :   if( FD_LIKELY( num_snap_rx_samples ) ) megabytes_per_second = 100.0*(double)snap_rx_sum/(double)num_snap_rx_samples/1e6;
     418             : 
     419           0 :   ulong accounts_sum = 0UL;
     420           0 :   ulong num_accounts_samples = fd_ulong_min( snapshot_acc_idx, sizeof(snapshot_acc_samples)/sizeof(snapshot_acc_samples[0]) );
     421           0 :   for( ulong i=0UL; i<num_accounts_samples; i++ ) accounts_sum += snapshot_acc_samples[ i ];
     422           0 :   double million_accounts_per_second = 0.0;
     423           0 :   if( FD_LIKELY( num_accounts_samples ) ) million_accounts_per_second = 100.0*(double)accounts_sum/(double)num_accounts_samples/1e6;
     424             : 
     425           0 :   ulong snap_wr_sum = 0UL;
     426           0 :   ulong num_snap_wr_samples = fd_ulong_min( snapshot_wr_idx, sizeof(snapshot_wr_samples)/sizeof(snapshot_wr_samples[0]) );
     427           0 :   for( ulong i=0UL; i<num_snap_wr_samples; i++ ) snap_wr_sum += snapshot_wr_samples[ i ];
     428           0 :   double wr_megabytes_per_second = 0.0;
     429           0 :   if( FD_LIKELY( num_snap_wr_samples ) ) wr_megabytes_per_second = 100.0*(double)snap_wr_sum/(double)num_snap_wr_samples/1e6;
     430             : 
     431           0 :   ulong snapct_total_ticks = total_regime( &cur_tile[ snapct_idx*FD_METRICS_TOTAL_SZ ] )-total_regime( &prev_tile[ snapct_idx*FD_METRICS_TOTAL_SZ ] );
     432           0 :   ulong snapld_total_ticks = total_regime( &cur_tile[ fd_topo_find_tile( &config->topo, "snapld", 0UL )*FD_METRICS_TOTAL_SZ ] )-total_regime( &prev_tile[ fd_topo_find_tile( &config->topo, "snapld", 0UL )*FD_METRICS_TOTAL_SZ ] );
     433           0 :   ulong snapdc_total_ticks = total_regime( &cur_tile[ fd_topo_find_tile( &config->topo, "snapdc", 0UL )*FD_METRICS_TOTAL_SZ ] )-total_regime( &prev_tile[ fd_topo_find_tile( &config->topo, "snapdc", 0UL )*FD_METRICS_TOTAL_SZ ] );
     434           0 :   ulong snapin_total_ticks = total_regime( &cur_tile[ fd_topo_find_tile( &config->topo, "snapin", 0UL )*FD_METRICS_TOTAL_SZ ] )-total_regime( &prev_tile[ fd_topo_find_tile( &config->topo, "snapin", 0UL )*FD_METRICS_TOTAL_SZ ] );
     435           0 :   ulong snapwr_total_ticks = total_regime( &cur_tile[ fd_topo_find_tile( &config->topo, "snapwr", 0UL )*FD_METRICS_TOTAL_SZ ] )-total_regime( &prev_tile[ fd_topo_find_tile( &config->topo, "snapwr", 0UL )*FD_METRICS_TOTAL_SZ ] );
     436           0 :   snapct_total_ticks = fd_ulong_max( snapct_total_ticks, 1UL );
     437           0 :   snapld_total_ticks = fd_ulong_max( snapld_total_ticks, 1UL );
     438           0 :   snapdc_total_ticks = fd_ulong_max( snapdc_total_ticks, 1UL );
     439           0 :   snapin_total_ticks = fd_ulong_max( snapin_total_ticks, 1UL );
     440           0 :   snapwr_total_ticks = fd_ulong_max( snapwr_total_ticks, 1UL );
     441             : 
     442           0 :   double snapct_backp_pct = 100.0*(double)diff_tile( config, "snapct", prev_tile, cur_tile, MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_BACKPRESSURE_PREFRAG ) )/(double)snapct_total_ticks;
     443           0 :   double snapld_backp_pct = 100.0*(double)diff_tile( config, "snapld", prev_tile, cur_tile, MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_BACKPRESSURE_PREFRAG ) )/(double)snapld_total_ticks;
     444           0 :   double snapdc_backp_pct = 100.0*(double)diff_tile( config, "snapdc", prev_tile, cur_tile, MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_BACKPRESSURE_PREFRAG ) )/(double)snapdc_total_ticks;
     445           0 :   double snapin_backp_pct = 100.0*(double)diff_tile( config, "snapin", prev_tile, cur_tile, MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_BACKPRESSURE_PREFRAG ) )/(double)snapin_total_ticks;
     446           0 :   double snapwr_backp_pct = 100.0*(double)diff_tile( config, "snapwr", prev_tile, cur_tile, MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_BACKPRESSURE_PREFRAG ) )/(double)snapwr_total_ticks;
     447             : 
     448           0 :   double snapct_idle_pct = 100.0*(double)diff_tile( config, "snapct", prev_tile, cur_tile, MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_CAUGHT_UP_POSTFRAG ) )/(double)snapct_total_ticks;
     449           0 :   double snapld_idle_pct = 100.0*(double)diff_tile( config, "snapld", prev_tile, cur_tile, MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_CAUGHT_UP_POSTFRAG ) )/(double)snapld_total_ticks;
     450           0 :   double snapdc_idle_pct = 100.0*(double)diff_tile( config, "snapdc", prev_tile, cur_tile, MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_CAUGHT_UP_POSTFRAG ) )/(double)snapdc_total_ticks;
     451           0 :   double snapin_idle_pct = 100.0*(double)diff_tile( config, "snapin", prev_tile, cur_tile, MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_CAUGHT_UP_POSTFRAG ) )/(double)snapin_total_ticks;
     452           0 :   double snapwr_idle_pct = 100.0*(double)diff_tile( config, "snapwr", prev_tile, cur_tile, MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_CAUGHT_UP_POSTFRAG ) )/(double)snapwr_total_ticks;
     453             : 
     454           0 :   PRINT( "โšก " BOLD BYELLOW "SNAPSHOTS..." RESET UNBOLD
     455           0 :           " " BOLD "STATE" UNBOLD " %s"
     456           0 :           " " BOLD "PCT"   UNBOLD " %.1f %%"
     457           0 :           " " BOLD "RX"    UNBOLD " %3.f MB/s"
     458           0 :           " " BOLD "WR"    UNBOLD " %3.f MB/s"
     459           0 :           " " BOLD "ACC"   UNBOLD " %3.1f M/s"
     460           0 :           " " BOLD "BACKP" UNBOLD " %3.0f%%,%3.0f%%,%3.0f%%,%3.0f%%,%3.0f%%"
     461           0 :           " " BOLD "BUSY"  UNBOLD " %3.0f%%,%3.0f%%,%3.0f%%,%3.0f%%,%3.0f%%" CLEARLN "\n",
     462           0 :     fd_snapct_state_str( (int)state ),
     463           0 :     progress,
     464           0 :     megabytes_per_second,
     465           0 :     wr_megabytes_per_second,
     466           0 :     million_accounts_per_second,
     467           0 :     snapct_backp_pct,
     468           0 :     snapld_backp_pct,
     469           0 :     snapdc_backp_pct,
     470           0 :     snapin_backp_pct,
     471           0 :     snapwr_backp_pct,
     472           0 :     100.0-snapct_idle_pct-snapct_backp_pct,
     473           0 :     100.0-snapld_idle_pct-snapld_backp_pct,
     474           0 :     100.0-snapdc_idle_pct-snapdc_backp_pct,
     475           0 :     100.0-snapin_idle_pct-snapin_backp_pct,
     476           0 :     100.0-snapwr_idle_pct-snapwr_backp_pct );
     477           0 : }
     478             : 
     479             : static long
     480             : diff_tile_idx( ulong const * prev_tile,
     481             :                ulong const * cur_tile,
     482             :                ulong         tile_idx,
     483           0 :                ulong         metric_off ) {
     484           0 :   return (long)cur_tile [ tile_idx*FD_METRICS_TOTAL_SZ+metric_off ] -
     485           0 :          (long)prev_tile[ tile_idx*FD_METRICS_TOTAL_SZ+metric_off ];
     486           0 : }
     487             : 
     488             : static void
     489             : accdb_per_tile_offsets( char const * name,
     490           0 :                         ulong *      offs /* 11 entries: acquired, writable, missed, waited, rd, wr, cp, evicted_class_base, preevicted_class_base, committed_new_class_base, committed_overwrite_class_base */ ) {
     491           0 :   if(      !strcmp( name, "execle" ) ) {
     492           0 :     offs[0] =MIDX(COUNTER,EXECLE,ACCDB_ACCOUNT_ACQUIRED         ); offs[1]=MIDX(COUNTER,EXECLE,ACCDB_ACCOUNT_WRITABLE_ACQUIRED);
     493           0 :     offs[2] =MIDX(COUNTER,EXECLE,ACCDB_ACCOUNT_NOT_FOUND        ); offs[3]=MIDX(COUNTER,EXECLE,ACCDB_ACCOUNT_WAITED          );
     494           0 :     offs[4] =MIDX(COUNTER,EXECLE,ACCDB_BYTES_READ                ); offs[5]=MIDX(COUNTER,EXECLE,ACCDB_BYTES_WRITTEN            );
     495           0 :     offs[6] =MIDX(COUNTER,EXECLE,ACCDB_BYTES_COPIED              ); offs[7]=MIDX(COUNTER,EXECLE,ACCDB_ACCOUNT_EVICTED         );
     496           0 :     offs[8] =ULONG_MAX                                            ; offs[9]=MIDX(COUNTER,EXECLE,ACCDB_ACCOUNT_COMMITTED_NEW   );
     497           0 :     offs[10]=MIDX(COUNTER,EXECLE,ACCDB_ACCOUNT_COMMITTED_OVERWRITE);
     498           0 :   } else if( !strcmp( name, "execrp" ) ) {
     499           0 :     offs[0] =MIDX(COUNTER,EXECRP,ACCDB_ACCOUNT_ACQUIRED         ); offs[1]=MIDX(COUNTER,EXECRP,ACCDB_ACCOUNT_WRITABLE_ACQUIRED);
     500           0 :     offs[2] =MIDX(COUNTER,EXECRP,ACCDB_ACCOUNT_NOT_FOUND        ); offs[3]=MIDX(COUNTER,EXECRP,ACCDB_ACCOUNT_WAITED          );
     501           0 :     offs[4] =MIDX(COUNTER,EXECRP,ACCDB_BYTES_READ                ); offs[5]=MIDX(COUNTER,EXECRP,ACCDB_BYTES_WRITTEN            );
     502           0 :     offs[6] =MIDX(COUNTER,EXECRP,ACCDB_BYTES_COPIED              ); offs[7]=MIDX(COUNTER,EXECRP,ACCDB_ACCOUNT_EVICTED         );
     503           0 :     offs[8] =ULONG_MAX                                            ; offs[9]=MIDX(COUNTER,EXECRP,ACCDB_ACCOUNT_COMMITTED_NEW   );
     504           0 :     offs[10]=MIDX(COUNTER,EXECRP,ACCDB_ACCOUNT_COMMITTED_OVERWRITE);
     505           0 :   } else if( !strcmp( name, "replay" ) ) {
     506           0 :     offs[0] =MIDX(COUNTER,REPLAY,ACCDB_ACCOUNT_ACQUIRED         ); offs[1]=MIDX(COUNTER,REPLAY,ACCDB_ACCOUNT_WRITABLE_ACQUIRED);
     507           0 :     offs[2] =MIDX(COUNTER,REPLAY,ACCDB_ACCOUNT_NOT_FOUND        ); offs[3]=MIDX(COUNTER,REPLAY,ACCDB_ACCOUNT_WAITED          );
     508           0 :     offs[4] =MIDX(COUNTER,REPLAY,ACCDB_BYTES_READ                ); offs[5]=MIDX(COUNTER,REPLAY,ACCDB_BYTES_WRITTEN            );
     509           0 :     offs[6] =MIDX(COUNTER,REPLAY,ACCDB_BYTES_COPIED              ); offs[7]=MIDX(COUNTER,REPLAY,ACCDB_ACCOUNT_EVICTED         );
     510           0 :     offs[8] =ULONG_MAX                                            ; offs[9]=MIDX(COUNTER,REPLAY,ACCDB_ACCOUNT_COMMITTED_NEW   );
     511           0 :     offs[10]=MIDX(COUNTER,REPLAY,ACCDB_ACCOUNT_COMMITTED_OVERWRITE);
     512           0 :   } else if( !strcmp( name, "tower" ) ) {
     513           0 :     offs[0] =MIDX(COUNTER,TOWER,ACCDB_ACCOUNT_ACQUIRED          ); offs[1]=MIDX(COUNTER,TOWER,ACCDB_ACCOUNT_WRITABLE_ACQUIRED );
     514           0 :     offs[2] =MIDX(COUNTER,TOWER,ACCDB_ACCOUNT_NOT_FOUND         ); offs[3]=MIDX(COUNTER,TOWER,ACCDB_ACCOUNT_WAITED            );
     515           0 :     offs[4] =MIDX(COUNTER,TOWER,ACCDB_BYTES_READ                 ); offs[5]=MIDX(COUNTER,TOWER,ACCDB_BYTES_WRITTEN              );
     516           0 :     offs[6] =MIDX(COUNTER,TOWER,ACCDB_BYTES_COPIED               ); offs[7]=MIDX(COUNTER,TOWER,ACCDB_ACCOUNT_EVICTED           );
     517           0 :     offs[8] =ULONG_MAX                                            ; offs[9]=MIDX(COUNTER,TOWER,ACCDB_ACCOUNT_COMMITTED_NEW     );
     518           0 :     offs[10]=MIDX(COUNTER,TOWER,ACCDB_ACCOUNT_COMMITTED_OVERWRITE);
     519           0 :   } else if( !strcmp( name, "accdb" ) ) {
     520             :     /* The accdb tile only runs background work (compact, preevict,
     521             :        advance_root, purge); it never acquires/releases.  Sentinel
     522             :        everything that comes from the acquire/release path. */
     523           0 :     offs[0] =ULONG_MAX                                            ; offs[1]=ULONG_MAX                                            ;
     524           0 :     offs[2] =ULONG_MAX                                            ; offs[3]=ULONG_MAX                                            ;
     525           0 :     offs[4] =MIDX(COUNTER,ACCDB,BYTES_READ                       ); offs[5]=MIDX(COUNTER,ACCDB,BYTES_WRITTEN                    );
     526           0 :     offs[6] =ULONG_MAX                                            ; offs[7]=ULONG_MAX                                            ;
     527           0 :     offs[8] =MIDX(COUNTER,ACCDB,ACCOUNT_PREEVICTED               ); offs[9]=ULONG_MAX                                            ;
     528           0 :     offs[10]=ULONG_MAX;
     529           0 :   } else if( !strcmp( name, "rpc" ) ) {
     530             :     /* RPC is a read-only accdb consumer.  It only emits the subset
     531             :        of counters that fd_accdb_read_one_nocache touches; everything
     532             :        else is sentinel and skipped by sample_accdb. */
     533           0 :     offs[0] =MIDX(COUNTER,RPC,ACCDB_ACCOUNT_ACQUIRED); offs[1]=ULONG_MAX;
     534           0 :     offs[2] =MIDX(COUNTER,RPC,ACCDB_ACCOUNT_NOT_FOUND); offs[3]=MIDX(COUNTER,RPC,ACCDB_ACCOUNT_WAITED);
     535           0 :     offs[4] =MIDX(COUNTER,RPC,ACCDB_BYTES_READ       ); offs[5]=ULONG_MAX;
     536           0 :     offs[6] =MIDX(COUNTER,RPC,ACCDB_BYTES_COPIED     ); offs[7]=ULONG_MAX;
     537           0 :     offs[8] =ULONG_MAX;                                 offs[9]=ULONG_MAX;
     538           0 :     offs[10]=ULONG_MAX;
     539           0 :   } else if( !strcmp( name, "resolv" ) ) {
     540             :     /* Resolv is a read-only accdb consumer (address lookup table
     541             :        reads on the receive path).  Same RO subset as RPC. */
     542           0 :     offs[0] =MIDX(COUNTER,RESOLV,ACCDB_ACCOUNT_ACQUIRED ); offs[1]=ULONG_MAX;
     543           0 :     offs[2] =MIDX(COUNTER,RESOLV,ACCDB_ACCOUNT_NOT_FOUND); offs[3]=MIDX(COUNTER,RESOLV,ACCDB_ACCOUNT_WAITED);
     544           0 :     offs[4] =MIDX(COUNTER,RESOLV,ACCDB_BYTES_READ        ); offs[5]=ULONG_MAX;
     545           0 :     offs[6] =MIDX(COUNTER,RESOLV,ACCDB_BYTES_COPIED      ); offs[7]=ULONG_MAX;
     546           0 :     offs[8] =ULONG_MAX;                                     offs[9]=ULONG_MAX;
     547           0 :     offs[10]=ULONG_MAX;
     548           0 :   } else {
     549           0 :     for( ulong i=0UL; i<11UL; i++ ) offs[i] = ULONG_MAX;
     550           0 :   }
     551           0 : }
     552             : 
     553             : static void
     554             : sample_accdb( config_t const * config,
     555             :               ulong const *    prev_tile,
     556           0 :               ulong const *    cur_tile ) {
     557           0 :   long acquired = 0L, writable = 0L, missed = 0L, evicted = 0L, waited = 0L;
     558           0 :   long bytes_rd = 0L, bytes_wr = 0L, bytes_cp = 0L, bytes_pe = 0L;
     559           0 :   long preevicted = 0L;
     560           0 :   long evicted_class[ 8 ] = {0};
     561           0 :   long preevicted_class[ 8 ] = {0};
     562           0 :   long committed_new_class[ 8 ] = {0};
     563           0 :   long committed_overwrite_class[ 8 ] = {0};
     564             : 
     565           0 :   for( ulong i=0UL; i<config->topo.tile_cnt; i++ ) {
     566           0 :     ulong offs[11];
     567           0 :     accdb_per_tile_offsets( config->topo.tiles[ i ].name, offs );
     568           0 :     if( offs[0]!=ULONG_MAX ) {
     569           0 :       for( ulong c=0UL; c<8UL; c++ ) acquired += diff_tile_idx( prev_tile, cur_tile, i, offs[0] + c );
     570           0 :     }
     571           0 :     if( offs[1]!=ULONG_MAX ) {
     572           0 :       for( ulong c=0UL; c<8UL; c++ ) writable += diff_tile_idx( prev_tile, cur_tile, i, offs[1] + c );
     573           0 :     }
     574           0 :     if( offs[3]!=ULONG_MAX ) waited   += diff_tile_idx( prev_tile, cur_tile, i, offs[3] );
     575           0 :     if( offs[4]!=ULONG_MAX ) bytes_rd += diff_tile_idx( prev_tile, cur_tile, i, offs[4] );
     576           0 :     if( offs[6]!=ULONG_MAX ) bytes_cp += diff_tile_idx( prev_tile, cur_tile, i, offs[6] );
     577           0 :     if( offs[5]!=ULONG_MAX ) {
     578           0 :       long this_wr = diff_tile_idx( prev_tile, cur_tile, i, offs[5] );
     579           0 :       if( !strcmp( config->topo.tiles[ i ].name, "accdb" ) ) bytes_pe += this_wr;
     580           0 :       else                                                   bytes_wr += this_wr;
     581           0 :     }
     582           0 :     for( ulong c=0UL; c<8UL; c++ ) {
     583           0 :       if( offs[2]!=ULONG_MAX ) missed += diff_tile_idx( prev_tile, cur_tile, i, offs[2] + c );
     584           0 :       if( offs[7]!=ULONG_MAX ) {
     585           0 :         long d = diff_tile_idx( prev_tile, cur_tile, i, offs[7] + c );
     586           0 :         evicted_class[ c ] += d;
     587           0 :         evicted            += d;
     588           0 :       }
     589           0 :       if( offs[8]!=ULONG_MAX ) {
     590           0 :         long d = diff_tile_idx( prev_tile, cur_tile, i, offs[8] + c );
     591           0 :         preevicted_class[ c ] += d;
     592           0 :         preevicted            += d;
     593           0 :       }
     594           0 :       if( offs[9] !=ULONG_MAX ) committed_new_class      [ c ] += diff_tile_idx( prev_tile, cur_tile, i, offs[9]  + c );
     595           0 :       if( offs[10]!=ULONG_MAX ) committed_overwrite_class[ c ] += diff_tile_idx( prev_tile, cur_tile, i, offs[10] + c );
     596           0 :     }
     597           0 :   }
     598             : 
     599           0 :   ulong slot = accdb_samples_idx % (sizeof(accdb_acquired_samples)/sizeof(accdb_acquired_samples[0]));
     600           0 :   accdb_acquired_samples[ slot ] = (ulong)acquired;
     601           0 :   accdb_writable_samples[ slot ] = (ulong)writable;
     602           0 :   accdb_missed_samples  [ slot ] = (ulong)missed;
     603           0 :   accdb_evicted_samples [ slot ] = (ulong)evicted;
     604           0 :   accdb_waited_samples  [ slot ] = (ulong)waited;
     605           0 :   accdb_bytes_rd_samples[ slot ] = (ulong)bytes_rd;
     606           0 :   accdb_bytes_wr_samples[ slot ] = (ulong)bytes_wr;
     607           0 :   accdb_bytes_cp_samples[ slot ] = (ulong)bytes_cp;
     608           0 :   accdb_bytes_pe_samples[ slot ] = (ulong)bytes_pe;
     609           0 :   accdb_preevicted_samples[ slot ] = (ulong)preevicted;
     610           0 :   for( ulong c=0UL; c<8UL; c++ ) {
     611           0 :     accdb_evicted_class_samples            [ c ][ slot ] = (ulong)evicted_class            [ c ];
     612           0 :     accdb_preevicted_class_samples         [ c ][ slot ] = (ulong)preevicted_class         [ c ];
     613           0 :     accdb_committed_new_class_samples      [ c ][ slot ] = (ulong)committed_new_class      [ c ];
     614           0 :     accdb_committed_overwrite_class_samples[ c ][ slot ] = (ulong)committed_overwrite_class[ c ];
     615           0 :   }
     616           0 :   accdb_samples_idx++;
     617           0 : }
     618             : 
     619             : static uint
     620             : write_accdb( config_t const * config,
     621             :              ulong const *    cur_tile,
     622           0 :              ulong const *    prev_tile ) {
     623           0 :   ulong accdb_tile_idx = fd_topo_find_tile( &config->topo, "accdb", 0UL );
     624           0 :   if( accdb_tile_idx==ULONG_MAX ) return 0U;
     625             : 
     626           0 :   ulong const * t = cur_tile + accdb_tile_idx*FD_METRICS_TOTAL_SZ;
     627             : 
     628           0 :   ulong accdb_total_ticks = total_regime( &cur_tile[ accdb_tile_idx*FD_METRICS_TOTAL_SZ ] )-total_regime( &prev_tile[ accdb_tile_idx*FD_METRICS_TOTAL_SZ ] );
     629           0 :   accdb_total_ticks = fd_ulong_max( accdb_total_ticks, 1UL );
     630           0 :   double accdb_backp_pct = 100.0*(double)diff_tile( config, "accdb", prev_tile, cur_tile, MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_BACKPRESSURE_PREFRAG ) )/(double)accdb_total_ticks;
     631           0 :   double accdb_idle_pct  = 100.0*(double)diff_tile( config, "accdb", prev_tile, cur_tile, MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_CAUGHT_UP_POSTFRAG  ) )/(double)accdb_total_ticks;
     632           0 :   double accdb_busy_pct  = 100.0 - accdb_backp_pct - accdb_idle_pct;
     633             : 
     634           0 :   ulong acct_cnt        = t[ MIDX( GAUGE,   ACCDB, ACCOUNT_COUNT         ) ];
     635           0 :   ulong acct_cap        = t[ MIDX( GAUGE,   ACCDB, ACCOUNT_CAPACITY      ) ];
     636           0 :   ulong used_bytes      = t[ MIDX( GAUGE,   ACCDB, DISK_USED_BYTES       ) ];
     637           0 :   ulong current_bytes   = t[ MIDX( GAUGE,   ACCDB, DISK_CURRENT_BYTES    ) ];
     638           0 :   ulong alloc_bytes     = t[ MIDX( GAUGE,   ACCDB, DISK_ALLOCATED_BYTES  ) ];
     639           0 :   ulong in_compaction   = t[ MIDX( GAUGE,   ACCDB, IN_COMPACTION         ) ];
     640           0 :   ulong compact_req     = t[ MIDX( COUNTER, ACCDB, COMPACTION_REQUESTED  ) ];
     641           0 :   ulong compact_done    = t[ MIDX( COUNTER, ACCDB, COMPACTION_COMPLETED  ) ];
     642             : 
     643           0 :   ulong  frag_bytes  = current_bytes>used_bytes ? current_bytes-used_bytes : 0UL;
     644           0 :   double data_gb     = (double)alloc_bytes/1e9;
     645           0 :   double live_gb     = (double)used_bytes/1e9;
     646           0 :   double frag_gb     = (double)frag_bytes/1e9;
     647           0 :   double frag_pct    = current_bytes ? 100.0*(double)frag_bytes/(double)current_bytes : 0.0;
     648           0 :   double index_pct   = acct_cap      ? 100.0*(double)acct_cnt/(double)acct_cap        : 0.0;
     649             : 
     650           0 :   PRINT( "๐Ÿ’พ " BOLD GREEN "ACCOUNTS...." RESET UNBOLD
     651           0 :          " " BOLD "CACHE SIZE"    UNBOLD " %lu GiB"
     652           0 :          " " BOLD "DISK"          UNBOLD " %.1f GB"
     653           0 :          " " BOLD "LIVE DATA"     UNBOLD " %.1f GB"
     654           0 :          " " BOLD "FRAGMENTATION" UNBOLD " %.1f GB (%4.1f%%)"
     655           0 :          " " BOLD "INDEX"         UNBOLD " %4.1f%% (%.1fM / %.1fM)"
     656           0 :          " " BOLD "COMPACTION"    UNBOLD " %s (%lu / %lu)"
     657           0 :          " " BOLD "BUSY"          UNBOLD " %3.0f%%" CLEARLN "\n",
     658           0 :     config->firedancer.accounts.cache_size_gib,
     659           0 :     data_gb, live_gb, frag_gb, frag_pct,
     660           0 :     index_pct, (double)acct_cnt/1e6, (double)acct_cap/1e6,
     661           0 :     in_compaction ? "running" : "idle", compact_done, compact_req,
     662           0 :     accdb_busy_pct );
     663             : 
     664           0 :   ulong const cap = sizeof(accdb_acquired_samples)/sizeof(accdb_acquired_samples[0]);
     665           0 :   ulong n = fd_ulong_min( accdb_samples_idx, cap );
     666           0 :   if( !n ) n = 1UL;
     667             : 
     668           0 :   ulong sum_acq = 0UL, sum_wr = 0UL, sum_miss = 0UL, sum_evict = 0UL, sum_wait = 0UL;
     669           0 :   ulong sum_brd = 0UL, sum_bwr = 0UL, sum_bcp = 0UL, sum_bpe = 0UL;
     670           0 :   ulong sum_pre = 0UL;
     671           0 :   for( ulong i=0UL; i<n; i++ ) {
     672           0 :     sum_acq   += accdb_acquired_samples[ i ];
     673           0 :     sum_wr    += accdb_writable_samples[ i ];
     674           0 :     sum_miss  += accdb_missed_samples  [ i ];
     675           0 :     sum_evict += accdb_evicted_samples [ i ];
     676           0 :     sum_wait  += accdb_waited_samples  [ i ];
     677           0 :     sum_brd   += accdb_bytes_rd_samples[ i ];
     678           0 :     sum_bwr   += accdb_bytes_wr_samples[ i ];
     679           0 :     sum_bcp   += accdb_bytes_cp_samples[ i ];
     680           0 :     sum_bpe   += accdb_bytes_pe_samples[ i ];
     681           0 :     sum_pre   += accdb_preevicted_samples[ i ];
     682           0 :   }
     683             : 
     684             :   /* Snap interval is 10ms, so per-second rate = mean diff * 100. */
     685           0 :   double acquired = 100.0*(double)sum_acq  /(double)n;
     686           0 :   double writable = 100.0*(double)sum_wr   /(double)n;
     687           0 :   double missed   = 100.0*(double)sum_miss /(double)n;
     688           0 :   double evicted  = 100.0*(double)sum_evict/(double)n;
     689           0 :   double waited   = 100.0*(double)sum_wait /(double)n;
     690           0 :   double bytes_rd = 100.0*(double)sum_brd  /(double)n;
     691           0 :   double bytes_wr = 100.0*(double)sum_bwr  /(double)n;
     692           0 :   double bytes_cp = 100.0*(double)sum_bcp  /(double)n;
     693           0 :   double bytes_pe = 100.0*(double)sum_bpe  /(double)n;
     694           0 :   double preevicted = 100.0*(double)sum_pre/(double)n;
     695             : 
     696           0 :   double hit_pct = acquired>0.0 ? 100.0*(acquired-missed)/acquired : 0.0;
     697             : 
     698           0 :   char * read_str    = fmt_bytes( fd_alloca_check( 1UL, 64UL ), 64UL, (long)bytes_rd );
     699           0 :   char * write_str   = fmt_bytes( fd_alloca_check( 1UL, 64UL ), 64UL, (long)bytes_wr );
     700           0 :   char * copy_str    = fmt_bytes( fd_alloca_check( 1UL, 64UL ), 64UL, (long)bytes_cp );
     701           0 :   char * preevict_str= fmt_bytes( fd_alloca_check( 1UL, 64UL ), 64UL, (long)bytes_pe );
     702           0 :   char * acq_str   = COUNTF( acquired );
     703           0 :   char * wr_str    = COUNTF( writable );
     704           0 :   char * miss_str  = COUNTF( missed   );
     705           0 :   char * evict_str = COUNTF( evicted  );
     706           0 :   char * pre_str   = COUNTF( preevicted );
     707           0 :   char * wait_str  = COUNTF( waited   );
     708             : 
     709           0 :   PRINT( "               "
     710           0 :          " " BOLD "ACQUIRE" UNBOLD " %s /s (%s wr /s)"
     711           0 :          " " BOLD "HIT"     UNBOLD " %5.1f%%"
     712           0 :          " " BOLD "MISS"    UNBOLD " %s /s"
     713           0 :          " " BOLD "EVICT"   UNBOLD " %s /s (+%s /s)"
     714           0 :          " " BOLD "WAIT"    UNBOLD " %s /s"
     715           0 :          " " BOLD "IO"      UNBOLD " %s rd %s wr-acq %s wr-pe %s cp" CLEARLN "\n",
     716           0 :     acq_str, wr_str, hit_pct, miss_str, evict_str, pre_str, wait_str,
     717           0 :     read_str, write_str, preevict_str, copy_str );
     718             : 
     719           0 :   char * evict_class_str[ 8 ];
     720           0 :   char * preevict_class_str[ 8 ];
     721           0 :   char * commit_new_class_str[ 8 ];
     722           0 :   char * commit_overwrite_class_str[ 8 ];
     723           0 :   for( ulong c=0UL; c<8UL; c++ ) {
     724           0 :     ulong sum_c = 0UL, sum_pc = 0UL, sum_cn = 0UL, sum_co = 0UL;
     725           0 :     for( ulong i=0UL; i<n; i++ ) {
     726           0 :       sum_c  += accdb_evicted_class_samples            [ c ][ i ];
     727           0 :       sum_pc += accdb_preevicted_class_samples         [ c ][ i ];
     728           0 :       sum_cn += accdb_committed_new_class_samples      [ c ][ i ];
     729           0 :       sum_co += accdb_committed_overwrite_class_samples[ c ][ i ];
     730           0 :     }
     731           0 :     evict_class_str           [ c ] = COUNTF_T( 100.0*(double)sum_c /(double)n );
     732           0 :     preevict_class_str        [ c ] = COUNTF_T( 100.0*(double)sum_pc/(double)n );
     733           0 :     commit_new_class_str      [ c ] = COUNTF_T( 100.0*(double)sum_cn/(double)n );
     734           0 :     commit_overwrite_class_str[ c ] = COUNTF_T( 100.0*(double)sum_co/(double)n );
     735           0 :   }
     736             : 
     737           0 :   PRINT( "               "
     738           0 :          " " BOLD "EVICT/s BY CLASS" UNBOLD
     739           0 :          " " BOLD "128B" UNBOLD " %s (+%s)"
     740           0 :          " " BOLD "512B" UNBOLD " %s (+%s)"
     741           0 :          " " BOLD "2K"   UNBOLD " %s (+%s)"
     742           0 :          " " BOLD "8K"   UNBOLD " %s (+%s)"
     743           0 :          " " BOLD "32K"  UNBOLD " %s (+%s)"
     744           0 :          " " BOLD "128K" UNBOLD " %s (+%s)"
     745           0 :          " " BOLD "1M"   UNBOLD " %s (+%s)"
     746           0 :          " " BOLD "10M"  UNBOLD " %s (+%s)" CLEARLN "\n",
     747           0 :     evict_class_str[0], preevict_class_str[0],
     748           0 :     evict_class_str[1], preevict_class_str[1],
     749           0 :     evict_class_str[2], preevict_class_str[2],
     750           0 :     evict_class_str[3], preevict_class_str[3],
     751           0 :     evict_class_str[4], preevict_class_str[4],
     752           0 :     evict_class_str[5], preevict_class_str[5],
     753           0 :     evict_class_str[6], preevict_class_str[6],
     754           0 :     evict_class_str[7], preevict_class_str[7] );
     755             : 
     756           0 :   PRINT( "               "
     757           0 :          " " BOLD "COMMIT/s        " UNBOLD
     758           0 :          " " BOLD "128B" UNBOLD " %s (=%s)"
     759           0 :          " " BOLD "512B" UNBOLD " %s (=%s)"
     760           0 :          " " BOLD "2K"   UNBOLD " %s (=%s)"
     761           0 :          " " BOLD "8K"   UNBOLD " %s (=%s)"
     762           0 :          " " BOLD "32K"  UNBOLD " %s (=%s)"
     763           0 :          " " BOLD "128K" UNBOLD " %s (=%s)"
     764           0 :          " " BOLD "1M"   UNBOLD " %s (=%s)"
     765           0 :          " " BOLD "10M"  UNBOLD " %s (=%s)" CLEARLN "\n",
     766           0 :     commit_new_class_str[0], commit_overwrite_class_str[0],
     767           0 :     commit_new_class_str[1], commit_overwrite_class_str[1],
     768           0 :     commit_new_class_str[2], commit_overwrite_class_str[2],
     769           0 :     commit_new_class_str[3], commit_overwrite_class_str[3],
     770           0 :     commit_new_class_str[4], commit_overwrite_class_str[4],
     771           0 :     commit_new_class_str[5], commit_overwrite_class_str[5],
     772           0 :     commit_new_class_str[6], commit_overwrite_class_str[6],
     773           0 :     commit_new_class_str[7], commit_overwrite_class_str[7] );
     774             : 
     775           0 :   ulong cache_used_off = MIDX( GAUGE, ACCDB, CACHE_CLASS_USED );
     776           0 :   ulong cache_max_off  = MIDX( GAUGE, ACCDB, CACHE_CLASS_MAX  );
     777           0 :   char * cache_used_str[ 8 ];
     778           0 :   char * cache_max_str [ 8 ];
     779           0 :   double cache_pct     [ 8 ];
     780           0 :   for( ulong c=0UL; c<8UL; c++ ) {
     781           0 :     ulong used = t[ cache_used_off + c ];
     782           0 :     ulong max  = t[ cache_max_off  + c ];
     783           0 :     cache_used_str[ c ] = COUNT_T( used );
     784           0 :     cache_max_str [ c ] = COUNT_T( max  );
     785           0 :     cache_pct     [ c ] = max ? 100.0*(double)used/(double)max : 0.0;
     786           0 :   }
     787             : 
     788           0 :   PRINT( "               "
     789           0 :          " " BOLD "CACHE FULL" UNBOLD
     790           0 :          " " BOLD "128B" UNBOLD " %s/%s (%5.1f%%)"
     791           0 :          " " BOLD "512B" UNBOLD " %s/%s (%5.1f%%)"
     792           0 :          " " BOLD "2K"   UNBOLD " %s/%s (%5.1f%%)"
     793           0 :          " " BOLD "8K"   UNBOLD " %s/%s (%5.1f%%)"
     794           0 :          " " BOLD "32K"  UNBOLD " %s/%s (%5.1f%%)"
     795           0 :          " " BOLD "128K" UNBOLD " %s/%s (%5.1f%%)"
     796           0 :          " " BOLD "1M"   UNBOLD " %s/%s (%5.1f%%)"
     797           0 :          " " BOLD "10M"  UNBOLD " %s/%s (%5.1f%%)" CLEARLN "\n",
     798           0 :     cache_used_str[0], cache_max_str[0], cache_pct[0],
     799           0 :     cache_used_str[1], cache_max_str[1], cache_pct[1],
     800           0 :     cache_used_str[2], cache_max_str[2], cache_pct[2],
     801           0 :     cache_used_str[3], cache_max_str[3], cache_pct[3],
     802           0 :     cache_used_str[4], cache_max_str[4], cache_pct[4],
     803           0 :     cache_used_str[5], cache_max_str[5], cache_pct[5],
     804           0 :     cache_used_str[6], cache_max_str[6], cache_pct[6],
     805           0 :     cache_used_str[7], cache_max_str[7], cache_pct[7] );
     806             : 
     807           0 :   ulong cache_resv_off = MIDX( GAUGE, ACCDB, CACHE_CLASS_RESERVED );
     808           0 :   char * cache_resv_str[ 8 ];
     809           0 :   for( ulong c=0UL; c<8UL; c++ ) {
     810           0 :     ulong resv = t[ cache_resv_off + c ];
     811           0 :     if( resv==ULONG_MAX ) cache_resv_str[ c ] = "  off ";
     812           0 :     else                  cache_resv_str[ c ] = COUNT_T( resv );
     813           0 :   }
     814             : 
     815           0 :   PRINT( "               "
     816           0 :          " " BOLD "RESERVED  " UNBOLD
     817           0 :          " " BOLD "128B" UNBOLD " %s         "
     818           0 :          " " BOLD "512B" UNBOLD " %s         "
     819           0 :          " " BOLD "2K"   UNBOLD " %s         "
     820           0 :          " " BOLD "8K"   UNBOLD " %s         "
     821           0 :          " " BOLD "32K"  UNBOLD " %s         "
     822           0 :          " " BOLD "128K" UNBOLD " %s         "
     823           0 :          " " BOLD "1M"   UNBOLD " %s         "
     824           0 :          " " BOLD "10M"  UNBOLD " %s         " CLEARLN "\n",
     825           0 :     cache_resv_str[0], cache_resv_str[1], cache_resv_str[2], cache_resv_str[3],
     826           0 :     cache_resv_str[4], cache_resv_str[5], cache_resv_str[6], cache_resv_str[7] );
     827           0 :   return 6;
     828           0 : }
     829             : 
     830             : static uint
     831             : write_wfs( config_t const * config,
     832           0 :            ulong const *    cur_tile ) {
     833           0 :   ulong gossip_tile_idx = fd_topo_find_tile( &config->topo, "gossip", 0UL );
     834           0 :   if( FD_UNLIKELY( gossip_tile_idx==ULONG_MAX ) ) return 0U;
     835             : 
     836           0 :   int wfs_state = (int)cur_tile[ gossip_tile_idx*FD_METRICS_TOTAL_SZ+MIDX( GAUGE, GOSSIP, WAIT_FOR_SUPERMAJORITY_STATE ) ];
     837           0 :   if( FD_LIKELY( wfs_state==FD_GOSSIP_WFS_STATE_DONE ) ) return 0U;
     838             : 
     839           0 :   char const * state_str;
     840           0 :   switch( wfs_state ) {
     841           0 :     case FD_GOSSIP_WFS_STATE_INIT:    state_str = "loading snapshot";      break;
     842           0 :     case FD_GOSSIP_WFS_STATE_WAIT:    state_str = "waiting";               break;
     843           0 :     case FD_GOSSIP_WFS_STATE_PUBLISH: state_str = "starting";              break;
     844           0 :     default:                          return 0U;
     845           0 :   }
     846             : 
     847           0 :   ulong _stake_online = cur_tile[ gossip_tile_idx*FD_METRICS_TOTAL_SZ+MIDX( GAUGE, GOSSIP, WAIT_FOR_SUPERMAJORITY_STAKE_ONLINE ) ];
     848           0 :   ulong _stake_total  = cur_tile[ gossip_tile_idx*FD_METRICS_TOTAL_SZ+MIDX( GAUGE, GOSSIP, WAIT_FOR_SUPERMAJORITY_STAKE_TOTAL  ) ];
     849           0 :   ulong peers_online  = cur_tile[ gossip_tile_idx*FD_METRICS_TOTAL_SZ+MIDX( GAUGE, GOSSIP, WAIT_FOR_SUPERMAJORITY_STAKED_PEER_ONLINE ) ];
     850           0 :   ulong peers_total   = cur_tile[ gossip_tile_idx*FD_METRICS_TOTAL_SZ+MIDX( GAUGE, GOSSIP, WAIT_FOR_SUPERMAJORITY_STAKED_PEER_TOTAL  ) ];
     851             : 
     852           0 :   ulong ipecho_tile_idx = fd_topo_find_tile( &config->topo, "ipecho", 0UL );
     853           0 :   ulong shred_ver       = 0UL;
     854           0 :   if( FD_LIKELY( ipecho_tile_idx!=ULONG_MAX ) ) shred_ver = cur_tile[ ipecho_tile_idx*FD_METRICS_TOTAL_SZ+MIDX( GAUGE, IPECHO, CURRENT_SHRED_VERSION ) ];
     855             : 
     856           0 :   double stake_pct = _stake_total>0UL ? 100.0*(double)_stake_online/(double)_stake_total : 0.0;
     857           0 :   double         stake_div     = (_stake_total<(ulong)1e14) ? 1e9 : 1e15;
     858           0 :   char const *   stake_unit    = (_stake_total<(ulong)1e14) ? " SOL" : "M";
     859           0 :   double         stake_online  = (double)_stake_online / stake_div;
     860           0 :   double         stake_total   = (double)_stake_total  / stake_div;
     861             : 
     862           0 :   PRINT( "โณ " BOLD YELLOW "CLUSTER BOOT" RESET UNBOLD
     863           0 :          " " BOLD "STATE" UNBOLD " %s"
     864           0 :          " " BOLD "STAKE" UNBOLD " %3.0f%% (%.1f%s / %.1f%s)"
     865           0 :          " " BOLD "SHRED VERSION" UNBOLD " %lu"
     866           0 :          " " BOLD "PEERS" UNBOLD " %lu online %lu offline"
     867           0 :          " " BOLD "BANK HASH"  UNBOLD " %s" CLEARLN "\n",
     868           0 :     state_str,
     869           0 :     stake_pct,
     870           0 :     stake_online,
     871           0 :     stake_unit,
     872           0 :     stake_total,
     873           0 :     stake_unit,
     874           0 :     shred_ver,
     875           0 :     peers_online,
     876           0 :     peers_total>peers_online ? peers_total-peers_online : 0UL,
     877           0 :     config->firedancer.consensus.wait_for_supermajority_with_bank_hash );
     878           0 :   return 1U;
     879           0 : }
     880             : 
     881             : static uint
     882             : write_gossip( config_t const * config,
     883             :               ulong const *    cur_tile,
     884             :               ulong const *    prev_tile,
     885             :               ulong const *    cur_link,
     886           0 :               ulong const *    prev_link ) {
     887           0 :   ulong gossip_tile_idx = fd_topo_find_tile( &config->topo, "gossip", 0UL );
     888           0 :   if( gossip_tile_idx==ULONG_MAX ) return 0U;
     889           0 :   char * contact_info = COUNT( cur_tile[ gossip_tile_idx*FD_METRICS_TOTAL_SZ+MIDX( GAUGE, GOSSIP, CRDS_OCCUPIED_CONTACT_INFO_V2 ) ] );
     890             : 
     891           0 :   ulong gossip_total_ticks = total_regime( &cur_tile[ gossip_tile_idx*FD_METRICS_TOTAL_SZ ] )-total_regime( &prev_tile[ gossip_tile_idx*FD_METRICS_TOTAL_SZ ] );
     892           0 :   gossip_total_ticks = fd_ulong_max( gossip_total_ticks, 1UL );
     893           0 :   double gossip_backp_pct = 100.0*(double)diff_tile( config, "gossip", prev_tile, cur_tile, MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_BACKPRESSURE_PREFRAG ) )/(double)gossip_total_ticks;
     894           0 :   double gossip_idle_pct = 100.0*(double)diff_tile( config, "gossip", prev_tile, cur_tile, MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_CAUGHT_UP_POSTFRAG ) )/(double)gossip_total_ticks;
     895           0 :   double gossip_busy_pct = 100.0 - gossip_backp_pct - gossip_idle_pct;
     896             : 
     897           0 :   PRINT( "๐Ÿ’ฌ " BOLD BLUE "GOSSIP......" RESET UNBOLD
     898           0 :          " " BOLD "RX"    UNBOLD " %s"
     899           0 :          " " BOLD "TX"    UNBOLD " %s"
     900           0 :          " " BOLD "CRDS"  UNBOLD " %s"
     901           0 :          " " BOLD "PEERS" UNBOLD " %s"
     902           0 :          " " BOLD "BUSY"  UNBOLD " %3.0f%%"
     903           0 :          " " BOLD "BACKP" UNBOLD " %3.0f%%" CLEARLN "\n",
     904           0 :     DIFF_LINK_BYTES( "net_gossvf", COUNTER, LINK, FRAG_CONSUMED_BYTES ),
     905           0 :     DIFF_LINK_BYTES( "gossip_net", COUNTER, LINK, FRAG_CONSUMED_BYTES ),
     906           0 :     COUNT( total_crds( &cur_tile[ fd_topo_find_tile( &config->topo, "gossip", 0UL )*FD_METRICS_TOTAL_SZ ] ) ),
     907           0 :     contact_info,
     908           0 :     gossip_busy_pct,
     909           0 :     gossip_backp_pct );
     910           0 :   return 1U;
     911           0 : }
     912             : 
     913             : static uint
     914             : write_repair( config_t const * config,
     915             :               ulong const *    cur_tile,
     916             :               ulong const *    cur_link,
     917           0 :               ulong const *    prev_link ) {
     918           0 :   ulong repair_tile_idx = fd_topo_find_tile( &config->topo, "repair", 0UL );
     919           0 :   if( repair_tile_idx==ULONG_MAX ) return 0U;
     920           0 :   ulong repair_slot = cur_tile[ repair_tile_idx*FD_METRICS_TOTAL_SZ+MIDX( GAUGE, REPAIR, SLOT_HIGHEST_REPAIRED ) ];
     921           0 :   ulong turbine_slot = cur_tile[ repair_tile_idx*FD_METRICS_TOTAL_SZ+MIDX( GAUGE, REPAIR, SLOT_CURRENT ) ];
     922           0 :   PRINT( "๐Ÿงฑ " BOLD RED "REPAIR......" RESET UNBOLD
     923           0 :          " " BOLD "RX"            UNBOLD " %s"
     924           0 :          " " BOLD "TX"            UNBOLD " %s"
     925           0 :          " " BOLD "REPAIR SLOT"   UNBOLD " %lu (%02ld)"
     926           0 :          " " BOLD "TURBINE SLOT"  UNBOLD " %lu" CLEARLN "\n",
     927           0 :     DIFF_LINK_BYTES( "net_repair", COUNTER, LINK, FRAG_CONSUMED_BYTES ),
     928           0 :     DIFF_LINK_BYTES( "repair_net", COUNTER, LINK, FRAG_CONSUMED_BYTES ),
     929           0 :     repair_slot,
     930           0 :     (long)repair_slot-(long)turbine_slot,
     931           0 :     turbine_slot );
     932           0 :   return 1U;
     933           0 : }
     934             : 
     935             : static uint
     936             : write_rserve( config_t const * config,
     937             :               ulong const * cur_tile,
     938             :               ulong const * cur_link,
     939           0 :               ulong const * prev_link ) {
     940           0 :   ulong rserve_tile_idx = fd_topo_find_tile( &config->topo, "rserve", 0UL );
     941           0 :   if( rserve_tile_idx==ULONG_MAX ) return 0UL;
     942             : 
     943           0 :   (void)cur_tile;
     944             : 
     945           0 :   ulong shreds_stored_sum = 0UL;
     946           0 :   ulong num_stored_shreds = fd_ulong_min( shreds_stored_samples_idx, sizeof(shreds_stored_sample)/sizeof(shreds_stored_sample[0]));
     947           0 :   for( ulong i=0UL; i<num_stored_shreds; i++ ) shreds_stored_sum += shreds_stored_sample[ i ];
     948           0 :   char * shreds_stored = COUNTF( 100.0*(double)shreds_stored_sum/(double)num_stored_shreds );
     949             : 
     950           0 :   ulong valid_sum = 0UL;
     951           0 :   ulong num_valid_samples = fd_ulong_min( rserve_rps_valid_samples_idx, sizeof(rserve_rps_valid_samples)/sizeof(rserve_rps_valid_samples[0]) );
     952           0 :   for( ulong i=0UL; i<num_valid_samples; i++ ) valid_sum += rserve_rps_valid_samples[ i ];
     953           0 :   char * valid_str = COUNTF( 100.0*(double)valid_sum/(double)num_valid_samples );
     954             : 
     955           0 :   ulong invalid_sum = 0UL;
     956           0 :   ulong num_invalid_samples = fd_ulong_min( rserve_rps_invalid_samples_idx, sizeof(rserve_rps_invalid_samples)/sizeof(rserve_rps_invalid_samples[0]) );
     957           0 :   for( ulong i=0UL; i<num_invalid_samples; i++ ) invalid_sum += rserve_rps_invalid_samples[ i ];
     958           0 :   char * invalid_str = COUNTF( 100.0*(double)invalid_sum/(double)num_invalid_samples );
     959             : 
     960           0 :   ulong num_total_samples = fd_ulong_max( num_valid_samples, 1UL );
     961           0 :   char * total_str = COUNTF( 100.0*(double)(valid_sum+invalid_sum)/(double)num_total_samples );
     962             : 
     963           0 :   PRINT( "๐Ÿ”ง " BOLD GREEN "RSERVE......" RESET UNBOLD
     964           0 :          " " BOLD "RX" UNBOLD " %s"
     965           0 :          " " BOLD "TX" UNBOLD " %s"
     966           0 :          " " BOLD "STORED SHREDS" UNBOLD " %s /s"
     967           0 :          " " BOLD "RPS" UNBOLD " %s (%s valid, %s invalid) /s" CLEARLN "\n",
     968           0 :       DIFF_LINK_BYTES( "net_rserve", COUNTER, LINK, FRAG_CONSUMED_BYTES ),
     969           0 :       DIFF_LINK_BYTES( "rserve_net", COUNTER, LINK, FRAG_CONSUMED_BYTES ),
     970           0 :       shreds_stored,
     971           0 :       total_str, valid_str, invalid_str );
     972           0 :   return 1U;
     973           0 : }
     974             : 
     975             : static uint
     976             : write_replay( config_t const * config,
     977           0 :               ulong const *    cur_tile ) {
     978           0 :   ulong repair_tile_idx = fd_topo_find_tile( &config->topo, "repair", 0UL );
     979           0 :   ulong replay_tile_idx = fd_topo_find_tile( &config->topo, "replay", 0UL );
     980           0 :   if( replay_tile_idx==ULONG_MAX ) return 0U;
     981             : 
     982           0 :   ulong reset_slot       = cur_tile[ replay_tile_idx*FD_METRICS_TOTAL_SZ+MIDX( GAUGE, REPLAY, RESET_SLOT       ) ];
     983           0 :   ulong next_leader_slot = cur_tile[ replay_tile_idx*FD_METRICS_TOTAL_SZ+MIDX( GAUGE, REPLAY, NEXT_LEADER_SLOT ) ];
     984           0 :   ulong leader_slot      = cur_tile[ replay_tile_idx*FD_METRICS_TOTAL_SZ+MIDX( GAUGE, REPLAY, LEADER_SLOT      ) ];
     985           0 :   char * next_leader_slot_str = fd_alloca_check( 1UL, 64UL );
     986             : 
     987           0 :   ulong turbine_slot;
     988           0 :   if( repair_tile_idx!=ULONG_MAX ) {
     989           0 :     turbine_slot = cur_tile[ repair_tile_idx*FD_METRICS_TOTAL_SZ+MIDX( GAUGE, REPAIR, SLOT_CURRENT ) ];
     990           0 :   } else {
     991           0 :     turbine_slot = reset_slot;
     992           0 :   }
     993             : 
     994           0 :   ulong slot_in_seconds = (ulong)((double)(next_leader_slot-reset_slot)*0.4);
     995           0 :   if( FD_UNLIKELY( leader_slot ) ) FD_TEST( fd_cstr_printf_check( next_leader_slot_str, 64UL, NULL, "now" ) );
     996           0 :   else if( FD_LIKELY( next_leader_slot>0UL ) ) FD_TEST( fd_cstr_printf_check( next_leader_slot_str, 64UL, NULL, "%lum %lus", slot_in_seconds/60UL, slot_in_seconds%60UL ) );
     997           0 :   else FD_TEST( fd_cstr_printf_check( next_leader_slot_str, 64UL, NULL, "never" ) );
     998             : 
     999           0 :   ulong root_distance = cur_tile[ replay_tile_idx*FD_METRICS_TOTAL_SZ+MIDX( GAUGE, REPLAY, ROOT_DISTANCE ) ];
    1000           0 :   ulong live_banks    = cur_tile[ replay_tile_idx*FD_METRICS_TOTAL_SZ+MIDX( GAUGE, REPLAY, BANK_LIVE     ) ];
    1001             : 
    1002           0 :   ulong sps_sum = 0UL;
    1003           0 :   ulong num_sps_samples = fd_ulong_min( sps_samples_idx, sizeof(sps_samples)/sizeof(sps_samples[0]));
    1004           0 :   for( ulong i=0UL; i<num_sps_samples; i++ ) sps_sum += sps_samples[ i ];
    1005           0 :   char * sps_str = COUNTF( 100.0*(double)sps_sum/(double)num_sps_samples );
    1006             : 
    1007           0 :   ulong tps_sum = 0UL;
    1008           0 :   ulong num_tps_samples = fd_ulong_min( tps_samples_idx, sizeof(tps_samples)/sizeof(tps_samples[0]));
    1009           0 :   for( ulong i=0UL; i<num_tps_samples; i++ ) tps_sum += tps_samples[ i ];
    1010           0 :   char * tps_str = COUNTF( 100.0*(double)tps_sum/(double)num_tps_samples );
    1011             : 
    1012           0 :   ulong cups_sum = 0UL;
    1013           0 :   ulong num_cups_samples = fd_ulong_min( cups_samples_idx, sizeof(cups_samples)/sizeof(cups_samples[0]));
    1014           0 :   for( ulong i=0UL; i<num_cups_samples; i++ ) cups_sum += cups_samples[ i ];
    1015           0 :   char * mcups_str = COUNTF( 100.0*(double)cups_sum/(double)num_cups_samples );
    1016             : 
    1017           0 :   PRINT( "๐Ÿ’ฅ " BOLD MAGENTA "REPLAY......" RESET UNBOLD
    1018           0 :          " " BOLD "SLOT"      UNBOLD " %lu (%02ld)"
    1019           0 :          " " BOLD "CU/s"      UNBOLD " %s"
    1020           0 :          " " BOLD "TPS"       UNBOLD " %s"
    1021           0 :          " " BOLD "SPS"       UNBOLD " %s"
    1022           0 :          " " BOLD "LEADER IN" UNBOLD " %s"
    1023           0 :          " " BOLD "ROOT DIST" UNBOLD " %lu"
    1024           0 :          " " BOLD "BANKS"     UNBOLD " %2lu" CLEARLN "\n",
    1025           0 :     reset_slot,
    1026           0 :     (long)reset_slot-(long)turbine_slot,
    1027           0 :     mcups_str,
    1028           0 :     tps_str,
    1029           0 :     sps_str,
    1030           0 :     next_leader_slot_str,
    1031           0 :     root_distance,
    1032           0 :     live_banks );
    1033           0 :   return 1U;
    1034           0 : }
    1035             : 
    1036             : static uint
    1037             : write_gui( config_t const * config,
    1038             :            ulong const *    cur_tile,
    1039           0 :            ulong const *    prev_tile ) {
    1040           0 :   char const * gui_name = "gui";
    1041           0 :   ulong gui_tile_idx = fd_topo_find_tile( &config->topo, gui_name, 0UL );
    1042             : 
    1043           0 :   ulong off_conn_active, off_websocket_conn_active, off_websocket_frame_tx, off_websocket_frame_rx;
    1044           0 :   char * bytes_read_s;
    1045           0 :   char * bytes_written_s;
    1046           0 :   if( FD_LIKELY( gui_tile_idx!=ULONG_MAX ) ) {
    1047           0 :     off_conn_active           = MIDX( GAUGE,   GUI, CONN_ACTIVE           );
    1048           0 :     off_websocket_conn_active = MIDX( GAUGE,   GUI, WEBSOCKET_CONN_ACTIVE );
    1049           0 :     off_websocket_frame_tx    = MIDX( COUNTER, GUI, WEBSOCKET_FRAME_TX    );
    1050           0 :     off_websocket_frame_rx    = MIDX( COUNTER, GUI, WEBSOCKET_FRAME_RX    );
    1051             : 
    1052           0 :     bytes_read_s              = DIFF_BYTES( gui_name, COUNTER, GUI, BYTES_READ    );
    1053           0 :     bytes_written_s           = DIFF_BYTES( gui_name, COUNTER, GUI, BYTES_WRITTEN );
    1054           0 :   } else {
    1055           0 :     gui_name = "guih";
    1056           0 :     gui_tile_idx = fd_topo_find_tile( &config->topo, gui_name, 0UL );
    1057           0 :     if( FD_UNLIKELY( gui_tile_idx==ULONG_MAX ) ) return 0U;
    1058           0 :     off_conn_active           = MIDX( GAUGE,   GUIH, CONN_ACTIVE           );
    1059           0 :     off_websocket_conn_active = MIDX( GAUGE,   GUIH, WEBSOCKET_CONN_ACTIVE );
    1060           0 :     off_websocket_frame_tx    = MIDX( COUNTER, GUIH, WEBSOCKET_FRAME_TX    );
    1061           0 :     off_websocket_frame_rx    = MIDX( COUNTER, GUIH, WEBSOCKET_FRAME_RX    );
    1062             : 
    1063           0 :     bytes_read_s              = DIFF_BYTES( gui_name, COUNTER, GUIH, BYTES_READ    );
    1064           0 :     bytes_written_s           = DIFF_BYTES( gui_name, COUNTER, GUIH, BYTES_WRITTEN );
    1065           0 :   }
    1066             : 
    1067           0 :   ulong connection_count = cur_tile[ gui_tile_idx*FD_METRICS_TOTAL_SZ+off_conn_active ]+cur_tile[ gui_tile_idx*FD_METRICS_TOTAL_SZ+off_websocket_conn_active ];
    1068           0 :   ulong gui_total_ticks = total_regime( &cur_tile[ gui_tile_idx*FD_METRICS_TOTAL_SZ ] )-total_regime( &prev_tile[ gui_tile_idx*FD_METRICS_TOTAL_SZ ] );
    1069           0 :   gui_total_ticks = fd_ulong_max( gui_total_ticks, 1UL );
    1070           0 :   double gui_backp_pct = 100.0*(double)diff_tile( config, gui_name, prev_tile, cur_tile, MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_BACKPRESSURE_PREFRAG ) )/(double)gui_total_ticks;
    1071           0 :   double gui_idle_pct  = 100.0*(double)diff_tile( config, gui_name, prev_tile, cur_tile, MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_CAUGHT_UP_POSTFRAG ) )/(double)gui_total_ticks;
    1072           0 :   double gui_busy_pct  = 100.0 - gui_backp_pct - gui_idle_pct;
    1073             : 
    1074           0 :   long sent_frame_count = diff_tile( config, gui_name, prev_tile, cur_tile, off_websocket_frame_tx );
    1075           0 :   char * sent_frame_count_s = COUNT( (ulong)sent_frame_count );
    1076           0 :   long received_frame_count = diff_tile( config, gui_name, prev_tile, cur_tile, off_websocket_frame_rx );
    1077             : 
    1078           0 :   PRINT( "๐Ÿ‘  " BOLD CYAN "GUI........." RESET UNBOLD
    1079           0 :          " " BOLD "CONNS"  UNBOLD " %lu"
    1080           0 :          " " BOLD "FRAMES" UNBOLD " %s in %s out"
    1081           0 :          " " BOLD "BW"     UNBOLD " %s in %s out"
    1082           0 :          " " BOLD "BUSY"   UNBOLD " %3.0f%% " CLEARLN "\n",
    1083           0 :     connection_count,
    1084           0 :     COUNT( (ulong)received_frame_count ),
    1085           0 :     sent_frame_count_s,
    1086           0 :     bytes_read_s,
    1087           0 :     bytes_written_s,
    1088           0 :     gui_busy_pct );
    1089           0 :   return 1U;
    1090           0 : }
    1091             : 
    1092             : static uint
    1093             : write_event( config_t const * config,
    1094           0 :              ulong const *    cur_tile ) {
    1095           0 :   ulong event_tile_idx = fd_topo_find_tile( &config->topo, "event", 0UL );
    1096           0 :   if( event_tile_idx==ULONG_MAX ) return 0U;
    1097             : 
    1098           0 :   ulong connection_state = cur_tile[ event_tile_idx*FD_METRICS_TOTAL_SZ+MIDX( GAUGE, EVENT, CONN_STATE ) ];
    1099           0 :   char const * connection_state_str;
    1100           0 :   switch( connection_state ) {
    1101           0 :     case 0UL: connection_state_str = "disconnected";    break;
    1102           0 :     case 1UL: connection_state_str = "connecting";      break;
    1103           0 :     case 2UL: connection_state_str = "authenticating";  break;
    1104           0 :     case 3UL: connection_state_str = "confirming_auth"; break;
    1105           0 :     case 4UL: connection_state_str = "connected";       break;
    1106           0 :     default:  connection_state_str = "unknown";         break;
    1107           0 :   }
    1108             : 
    1109           0 :   ulong event_queue_count = cur_tile[ event_tile_idx*FD_METRICS_TOTAL_SZ+MIDX( GAUGE, EVENT, QUEUE_DEPTH ) ];
    1110           0 :   ulong event_queue_unsent = cur_tile[ event_tile_idx*FD_METRICS_TOTAL_SZ+MIDX( GAUGE, EVENT, QUEUE_UNSENT ) ];
    1111           0 :   ulong event_queue_unacked = event_queue_count>event_queue_unsent ? event_queue_count-event_queue_unsent : 0UL;
    1112           0 :   ulong event_last_acked_id = cur_tile[ event_tile_idx*FD_METRICS_TOTAL_SZ+MIDX( GAUGE, EVENT, LAST_ACKED_ID ) ];
    1113           0 :   ulong event_queue_drops = cur_tile[ event_tile_idx*FD_METRICS_TOTAL_SZ+MIDX( COUNTER, EVENT, QUEUE_DROPPED ) ];
    1114           0 :   ulong event_queue_bytes_used = cur_tile[ event_tile_idx*FD_METRICS_TOTAL_SZ+MIDX( GAUGE, EVENT, QUEUE_BYTES_USED ) ];
    1115           0 :   ulong event_queue_bytes_capacity = cur_tile[ event_tile_idx*FD_METRICS_TOTAL_SZ+MIDX( GAUGE, EVENT, QUEUE_BYTES_CAPACITY ) ];
    1116             : 
    1117           0 :   double event_queue_pct_full = event_queue_bytes_capacity>0UL ? 100.0*(double)event_queue_bytes_used/(double)event_queue_bytes_capacity : 0.0;
    1118             : 
    1119           0 :   ulong events_sent_sum = 0UL;
    1120           0 :   ulong num_events_sent_samples = fd_ulong_min( events_sent_samples_idx, sizeof(events_sent_samples)/sizeof(events_sent_samples[0]));
    1121           0 :   for( ulong i=0UL; i<num_events_sent_samples; i++ ) events_sent_sum += events_sent_samples[ i ];
    1122           0 :   char * events_sent_str = COUNTF( 100.0*(double)events_sent_sum/(double)num_events_sent_samples );
    1123             : 
    1124           0 :   ulong events_acked_sum = 0UL;
    1125           0 :   ulong num_events_acked_samples = fd_ulong_min( events_acked_samples_idx, sizeof(events_acked_samples)/sizeof(events_acked_samples[0]));
    1126           0 :   for( ulong i=0UL; i<num_events_acked_samples; i++ ) events_acked_sum += events_acked_samples[ i ];
    1127           0 :   char * events_acked_str = COUNTF( 100.0*(double)events_acked_sum/(double)num_events_acked_samples );
    1128             : 
    1129           0 :   ulong bytes_written_sum = 0UL;
    1130           0 :   ulong num_bytes_written_samples = fd_ulong_min( event_bytes_written_samples_idx, sizeof(event_bytes_written_samples)/sizeof(event_bytes_written_samples[0]));
    1131           0 :   for( ulong i=0UL; i<num_bytes_written_samples; i++ ) bytes_written_sum += event_bytes_written_samples[ i ];
    1132           0 :   long bytes_written_per_sec = (long)(100.0*(double)bytes_written_sum/(double)num_bytes_written_samples);
    1133           0 :   char * bytes_written_str = fmt_bytes( fd_alloca_check( 1UL, 64UL ), 64UL, bytes_written_per_sec );
    1134             : 
    1135           0 :   ulong bytes_read_sum = 0UL;
    1136           0 :   ulong num_bytes_read_samples = fd_ulong_min( event_bytes_read_samples_idx, sizeof(event_bytes_read_samples)/sizeof(event_bytes_read_samples[0]));
    1137           0 :   for( ulong i=0UL; i<num_bytes_read_samples; i++ ) bytes_read_sum += event_bytes_read_samples[ i ];
    1138           0 :   long bytes_read_per_sec = (long)(100.0*(double)bytes_read_sum/(double)num_bytes_read_samples);
    1139           0 :   char * bytes_read_str = fmt_bytes( fd_alloca_check( 1UL, 64UL ), 64UL, bytes_read_per_sec );
    1140             : 
    1141           0 :   char * event_queue_unacked_s = COUNT( event_queue_unacked );
    1142           0 :   char * event_queue_unsent_s  = COUNT( event_queue_unsent );
    1143           0 :   char * event_last_acked_id_s = COUNT( event_last_acked_id );
    1144             : 
    1145           0 :   PRINT( "๐Ÿ“ก " BOLD YELLOW "EVENT......." RESET UNBOLD
    1146           0 :          " " BOLD "STATE"    UNBOLD " %12s"
    1147           0 :          " " BOLD "UNACKED"  UNBOLD " %s"
    1148           0 :          " " BOLD "QUEUE"    UNBOLD " %s"
    1149           0 :          " " BOLD "SENT"     UNBOLD " %s /s"
    1150           0 :          " " BOLD "ACKED"    UNBOLD " %s /s"
    1151           0 :          " " BOLD "LAST ACK" UNBOLD " %s"
    1152           0 :          " " BOLD "BW"       UNBOLD " %s in %s out"
    1153           0 :          " " BOLD "DROPS"    UNBOLD " %s"
    1154           0 :          " " BOLD "FULL"     UNBOLD " %3.0f%%" CLEARLN "\n",
    1155           0 :     connection_state_str,
    1156           0 :     event_queue_unacked_s,
    1157           0 :     event_queue_unsent_s,
    1158           0 :     events_sent_str,
    1159           0 :     events_acked_str,
    1160           0 :     event_last_acked_id_s,
    1161           0 :     bytes_read_str,
    1162           0 :     bytes_written_str,
    1163           0 :     COUNT( event_queue_drops ),
    1164           0 :     event_queue_pct_full );
    1165           0 :   return 1U;
    1166           0 : }
    1167             : 
    1168             : static char *
    1169             : base58_short( char        out[ static 16 ],
    1170           0 :               uchar const key[ static 32 ] ) {
    1171           0 :   char enc[ FD_BASE58_ENCODED_32_SZ ];
    1172           0 :   ulong len;
    1173           0 :   fd_base58_encode_32( key, &len, enc );
    1174           0 :   char * p = fd_cstr_init( out );
    1175           0 :   p = fd_cstr_append_text( p, enc, 6 ),
    1176           0 :   p = fd_cstr_append_text( p, "โ€ฆ", sizeof("โ€ฆ")-1 ),
    1177           0 :   p = fd_cstr_append_text( p, enc+len-6, 6 );
    1178           0 :   fd_cstr_fini( p );
    1179           0 :   return out;
    1180           0 : }
    1181             : 
    1182             : static void
    1183             : fmt_balance( char  out[ static 64 ],
    1184           0 :              ulong lamports ) {
    1185           0 :   ulong maj = lamports / 1000000000UL;
    1186           0 :   ulong min = lamports % 1000000000UL;
    1187           0 :   fd_cstr_printf_check( out, 64UL, NULL, "%lu.%09lu", maj, min );
    1188           0 : }
    1189             : 
    1190             : static uint
    1191             : write_node_info( config_t const *       config,
    1192             :                  ulong const *          cur_tile,
    1193           0 :                  fd_node_info_t const * node_info ) {
    1194             : 
    1195           0 :   char identity_str[ FD_BASE58_ENCODED_32_SZ ];
    1196           0 :   if( FD_LIKELY( node_info && !fd_pubkey_check_zero( &node_info->identity ) ) ) {
    1197           0 :     fd_base58_encode_32( node_info->identity.key, NULL, identity_str );
    1198           0 :   } else {
    1199           0 :     strcpy( identity_str, "???" );
    1200           0 :   }
    1201             : 
    1202           0 :   char vote_acc_str[ FD_BASE58_ENCODED_32_SZ ];
    1203           0 :   if( FD_LIKELY( node_info && !fd_pubkey_check_zero( &node_info->vote_account ) ) ) {
    1204           0 :     fd_base58_encode_32( node_info->vote_account.key, NULL, vote_acc_str );
    1205           0 :   } else {
    1206           0 :     strcpy( vote_acc_str, "???" );
    1207           0 :   }
    1208             : 
    1209           0 :   char shred_ver_str[ 16 ];
    1210           0 :   ulong ipecho_idx = fd_topo_find_tile( &config->topo, "ipecho", 0UL );
    1211           0 :   ushort shred_version = 0;
    1212           0 :   if( FD_LIKELY( ipecho_idx!=ULONG_MAX ) ) shred_version = (ushort)cur_tile[ ipecho_idx*FD_METRICS_TOTAL_SZ+MIDX( GAUGE, IPECHO, CURRENT_SHRED_VERSION ) ];
    1213           0 :   if( shred_version ) {
    1214           0 :     fd_cstr_printf_check( shred_ver_str, sizeof(shred_ver_str), NULL, "%hu", shred_version );
    1215           0 :   } else if( config->consensus.expected_shred_version ) {
    1216           0 :     fd_cstr_printf_check( shred_ver_str, sizeof(shred_ver_str), NULL, "(%hu)", config->consensus.expected_shred_version );
    1217           0 :   } else {
    1218           0 :     fd_cstr_printf_check( shred_ver_str, sizeof(shred_ver_str), NULL, "???" );
    1219           0 :   }
    1220             : 
    1221           0 :   char genesis_hash_b58[ FD_BASE58_ENCODED_32_SZ ] = {0};
    1222           0 :   char genesis_short[ 16 ] = {0};
    1223           0 :   int  has_genesis_b58 = 0;
    1224           0 :   if( FD_LIKELY( node_info && !fd_pubkey_check_zero( &node_info->genesis_hash ) ) ) {
    1225           0 :     fd_base58_encode_32( node_info->genesis_hash.key, NULL, genesis_hash_b58 );
    1226           0 :     base58_short( genesis_short, node_info->genesis_hash.key );
    1227           0 :     has_genesis_b58 = 1;
    1228           0 :   } else {
    1229           0 :     fd_cstr_ncpy( genesis_short, "???", sizeof(genesis_short) );
    1230           0 :   }
    1231             : 
    1232           0 :   char const * cluster_str = "unknown";
    1233           0 :   if( has_genesis_b58 ) {
    1234           0 :     ulong cluster_id = fd_genesis_cluster_identify( genesis_hash_b58 );
    1235           0 :     cluster_str = fd_genesis_cluster_name( cluster_id );
    1236           0 :   }
    1237             : 
    1238           0 :   char uptime_str[ 32UL ];
    1239           0 :   long now = fd_log_wallclock();
    1240           0 :   if( FD_LIKELY( config->boot_timestamp_nanos>0L && now>config->boot_timestamp_nanos ) ) {
    1241           0 :     ulong elapsed_s = (ulong)( (now - config->boot_timestamp_nanos) / (long)1e9 );
    1242           0 :     ulong days  = elapsed_s / 86400UL;
    1243           0 :     ulong hours = (elapsed_s % 86400UL) / 3600UL;
    1244           0 :     ulong mins  = (elapsed_s % 3600UL) / 60UL;
    1245           0 :     ulong secs  = elapsed_s % 60UL;
    1246           0 :     if( days ) fd_cstr_printf_check( uptime_str, sizeof(uptime_str), NULL, "%lud %luh %lum", days, hours, mins );
    1247           0 :     else if( hours ) fd_cstr_printf_check( uptime_str, sizeof(uptime_str), NULL, "%luh %lum %lus", hours, mins, secs );
    1248           0 :     else fd_cstr_printf_check( uptime_str, sizeof(uptime_str), NULL, "%lum %lus", mins, secs );
    1249           0 :   } else {
    1250           0 :     fd_cstr_printf_check( uptime_str, sizeof(uptime_str), NULL, "???" );
    1251           0 :   }
    1252             : 
    1253           0 :   ulong replay_idx = fd_topo_find_tile( &config->topo, "replay", 0UL );
    1254           0 :   ulong const * replay_metrics = &cur_tile[ replay_idx*FD_METRICS_TOTAL_SZ ];
    1255           0 :   ulong identity_balance = replay_metrics[ MIDX( GAUGE, REPLAY, IDENTITY_BALANCE_LAMPORTS     ) ];
    1256           0 :   ulong stake_amount     = replay_metrics[ MIDX( GAUGE, REPLAY, ACTIVE_STAKE_LAMPORTS         ) ];
    1257           0 :   ulong epoch_credits    = replay_metrics[ MIDX( GAUGE, REPLAY, EPOCH_CREDITS                 ) ];
    1258           0 :   ulong tot_stake        = replay_metrics[ MIDX( GAUGE, REPLAY, CLUSTER_ACTIVE_STAKE_LAMPORTS ) ];
    1259           0 :   char identity_balance_str[ 64 ]; fmt_balance( identity_balance_str, identity_balance );
    1260           0 :   char stake_amount_str    [ 64 ]; fmt_balance( stake_amount_str,     stake_amount     );
    1261           0 :   double stake_percent = 0.0;
    1262           0 :   if( tot_stake>0UL ) stake_percent = 100.0*(double)stake_amount/(double)tot_stake;
    1263             : 
    1264           0 :   PRINT( "๐Ÿ”‘" BOLD ORANGE " NODE........" RESET UNBOLD
    1265           0 :          " " BOLD "ID"      UNBOLD " %44s"
    1266           0 :          " " BOLD "VOTE"    UNBOLD " %44s"
    1267           0 :          " " BOLD "CLUSTER" UNBOLD " %s"
    1268           0 :          " " BOLD "UPTIME"  UNBOLD " %s"
    1269           0 :          " " BOLD "SHRED"   UNBOLD " %s"
    1270           0 :          " " BOLD "GENESIS" UNBOLD " %s",
    1271           0 :     identity_str,
    1272           0 :     vote_acc_str,
    1273           0 :     cluster_str,
    1274           0 :     uptime_str,
    1275           0 :     shred_ver_str,
    1276           0 :     genesis_short );
    1277           0 :   PRINT( CLEARLN "\n" );
    1278             : 
    1279           0 :   PRINT( "                " BOLD "BALANCE" UNBOLD " %s"
    1280           0 :          " " BOLD "STAKE"   UNBOLD " %s (%.2f %%)"
    1281           0 :          " " BOLD "CREDITS" UNBOLD " %lu" CLEARLN "\n",
    1282           0 :          identity_balance_str, stake_amount_str, stake_percent, epoch_credits );
    1283             : 
    1284           0 :   return 2U;
    1285           0 : }
    1286             : 
    1287             : static void
    1288             : write_summary( config_t const *           config,
    1289             :                fd_node_info_box_t const * shinfo,
    1290             :                ulong const *              cur_tile,
    1291             :                ulong const *              prev_tile,
    1292             :                ulong const *              cur_link,
    1293             :                ulong const *              prev_link,
    1294           0 :                int                        interposing ) {
    1295           0 :   (void)config;
    1296           0 :   (void)prev_tile;
    1297           0 :   (void)cur_tile;
    1298             : 
    1299           0 :   if( FD_UNLIKELY( !ended_on_newline ) ) PRINT( "\n" );
    1300           0 :   PRINT( "\033[?7l" ); /* disable autowrap mode */
    1301           0 :   lines_printed = 0UL;
    1302           0 :   if( FD_UNLIKELY( interposing ) ) {
    1303           0 :     PRINT( "โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€" CLEARLN "\n" );
    1304           0 :     lines_printed = 1UL;
    1305           0 :   }
    1306             : 
    1307           0 :   ulong snapct_idx = fd_topo_find_tile( &config->topo, "snapct", 0UL );
    1308           0 :   int shutdown = 1;
    1309           0 :   if( FD_LIKELY( snapct_idx!=ULONG_MAX ) ) shutdown = cur_tile[ snapct_idx*FD_METRICS_TOTAL_SZ+MIDX( GAUGE, SNAPCT, STATE ) ]==FD_SNAPCT_STATE_SHUTDOWN;
    1310             : 
    1311           0 :   static long snap_shutdown_time = 0L;
    1312           0 :   if( FD_UNLIKELY( !snap_shutdown_time && !shutdown ) ) snap_shutdown_time = 1L; /* Was not shutdown on boot */
    1313           0 :   if( FD_UNLIKELY( !snap_shutdown_time && shutdown  ) ) snap_shutdown_time = 2L; /* Was shutdown on boot */
    1314           0 :   if( FD_UNLIKELY( snap_shutdown_time==1L && shutdown  ) ) snap_shutdown_time = fd_log_wallclock();
    1315             : 
    1316           0 :   fd_node_info_t node_info[1]; fd_node_info_read( node_info, shinfo );
    1317           0 :   lines_printed += write_node_info( config, cur_tile, node_info );
    1318             : 
    1319           0 :   if( FD_UNLIKELY( write_bench( config, cur_tile, prev_tile ) ) ) lines_printed++;
    1320             : 
    1321           0 :   ulong backt_idx = fd_topo_find_tile( &config->topo, "backt", 0UL );
    1322           0 :   if( FD_UNLIKELY( backt_idx!=ULONG_MAX ) ) {
    1323           0 :     lines_printed++;
    1324           0 :     write_backtest( config, cur_tile );
    1325           0 :   }
    1326             : 
    1327           0 :   long now = fd_log_wallclock();
    1328           0 :   if( FD_UNLIKELY( snap_shutdown_time==1L || now<snap_shutdown_time+(long)2e9 ) ) {
    1329           0 :     lines_printed++;
    1330           0 :     write_snapshots( config, cur_tile, prev_tile );
    1331           0 :   }
    1332             : 
    1333           0 :   lines_printed += write_accdb( config, cur_tile, prev_tile );
    1334           0 :   lines_printed += write_wfs( config, cur_tile );
    1335           0 :   lines_printed += write_gossip( config, cur_tile, prev_tile, cur_link, prev_link );
    1336           0 :   lines_printed += write_repair( config, cur_tile, cur_link, prev_link );
    1337           0 :   lines_printed += write_rserve( config, cur_tile, cur_link, prev_link );
    1338           0 :   lines_printed += write_replay( config, cur_tile );
    1339           0 :   lines_printed += write_gui( config, cur_tile, prev_tile );
    1340           0 :   lines_printed += write_event( config, cur_tile );
    1341             : 
    1342           0 :   PRINT( "\033[?7h" ); /* enable autowrap mode */
    1343           0 : }
    1344             : 
    1345             : static void
    1346             : snap_tiles( fd_topo_t const * topo,
    1347           0 :             ulong *           tiles ) {
    1348           0 :   for( ulong i=0UL; i<topo->tile_cnt; i++ ) {
    1349           0 :     fd_topo_tile_t const * tile = &topo->tiles[ i ];
    1350           0 :     volatile ulong const * metrics = fd_metrics_tile( tile->metrics );
    1351           0 :     FD_TEST( metrics );
    1352           0 :     for( ulong j=0UL; j<FD_METRICS_TOTAL_SZ/8UL; j++ ) tiles[ i*FD_METRICS_TOTAL_SZ+j ] = metrics[ j ];
    1353           0 :   }
    1354           0 : }
    1355             : 
    1356             : static void
    1357             : snap_links( fd_topo_t const * topo,
    1358           0 :             ulong *           links ) {
    1359           0 :   ulong overall_polled_idx = 0UL;
    1360             : 
    1361           0 :   for( ulong i=0UL; i<topo->tile_cnt; i++ ) {
    1362           0 :     fd_topo_tile_t const * tile = &topo->tiles[ i ];
    1363             : 
    1364           0 :     ulong polled_in_idx = 0UL;
    1365           0 :     for( ulong j=0UL; j<topo->tiles[ i ].in_cnt; j++ ) {
    1366           0 :       if( FD_UNLIKELY( !tile->in_link_poll[ j ] ) ) continue;
    1367             : 
    1368           0 :       volatile ulong const * metrics = fd_metrics_link_in( tile->metrics, polled_in_idx );
    1369           0 :       FD_TEST( metrics );
    1370           0 :       for( ulong k=0UL; k<FD_METRICS_ALL_LINK_IN_TOTAL; k++ ) links[ overall_polled_idx*8UL+k ] = metrics[ k ];
    1371           0 :       polled_in_idx++;
    1372           0 :       overall_polled_idx++;
    1373           0 :     }
    1374           0 :   }
    1375           0 : }
    1376             : 
    1377             : static ulong tiles[ 2UL*FD_TILE_MAX*FD_METRICS_TOTAL_SZ ];
    1378             : static ulong links[ 2UL*4096UL*8UL*FD_METRICS_ALL_LINK_IN_TOTAL ];
    1379             : 
    1380             : static void
    1381             : run( config_t const * config,
    1382           0 :      int              drain_output_fd ) {
    1383           0 :   (void)config;
    1384           0 :   (void)drain_output_fd;
    1385             : 
    1386           0 :   ulong node_info_obj_id = fd_pod_query_ulong( config->topo.props, "node_info", ULONG_MAX );
    1387           0 :   FD_TEST( node_info_obj_id!=ULONG_MAX );
    1388           0 :   fd_node_info_box_t * node_info = fd_node_info_box_join( fd_topo_obj_laddr( &config->topo, node_info_obj_id ) );
    1389           0 :   FD_TEST( node_info );
    1390             : 
    1391           0 :   ulong tile_cnt = config->topo.tile_cnt;
    1392             : 
    1393           0 :   ulong cons_cnt = 0UL;
    1394           0 :   for( ulong i=0UL; i<config->topo.tile_cnt; i++ ) {
    1395           0 :     for( ulong j=0UL; j<config->topo.tiles[ i ].in_cnt; j++ ) {
    1396           0 :       if( FD_UNLIKELY( config->topo.tiles[ i ].in_link_poll[ j ] ) ) cons_cnt++;
    1397           0 :     }
    1398           0 :   }
    1399             : 
    1400           0 :   FD_TEST( tile_cnt<=FD_TILE_MAX );
    1401           0 :   FD_TEST( cons_cnt<=4096UL );
    1402             : 
    1403           0 :   snap_tiles( &config->topo, tiles );
    1404           0 :   fd_memcpy( tiles+tile_cnt*FD_METRICS_TOTAL_SZ, tiles, tile_cnt*FD_METRICS_TOTAL_SZ*sizeof(ulong) );
    1405             : 
    1406           0 :   snap_links( &config->topo, links );
    1407           0 :   fd_memcpy( links+(cons_cnt*8UL*FD_METRICS_ALL_LINK_IN_TOTAL), links, cons_cnt*8UL*FD_METRICS_ALL_LINK_IN_TOTAL*sizeof(ulong) );
    1408             : 
    1409           0 :   ulong last_snap = 1UL;
    1410             : 
    1411           0 :   int interposing = drain_output_fd>=0;
    1412             : 
    1413           0 :   frame_len = 0UL;
    1414           0 :   write_summary( config, node_info, tiles+last_snap*tile_cnt*FD_METRICS_TOTAL_SZ, tiles+(1UL-last_snap)*tile_cnt*FD_METRICS_TOTAL_SZ, links+last_snap*(cons_cnt*8UL*FD_METRICS_ALL_LINK_IN_TOTAL), links+(1UL-last_snap)*(cons_cnt*8UL*FD_METRICS_ALL_LINK_IN_TOTAL), interposing );
    1415           0 :   flush_frame();
    1416             : 
    1417           0 :   long next = fd_log_wallclock()+(long)1e9;
    1418           0 :   for(;;) {
    1419           0 :     if( FD_UNLIKELY( drain_output_fd>=0 ) ) {
    1420           0 :       if( FD_UNLIKELY( drain( drain_output_fd ) ) ) {
    1421           0 :         frame_len = 0UL;
    1422           0 :         write_summary( config, node_info, tiles+last_snap*tile_cnt*FD_METRICS_TOTAL_SZ, tiles+(1UL-last_snap)*tile_cnt*FD_METRICS_TOTAL_SZ, links+last_snap*(cons_cnt*8UL*FD_METRICS_ALL_LINK_IN_TOTAL), links+(1UL-last_snap)*(cons_cnt*8UL*FD_METRICS_ALL_LINK_IN_TOTAL), interposing );
    1423           0 :         flush_frame();
    1424           0 :       }
    1425           0 :     }
    1426             : 
    1427           0 :     long now = fd_log_wallclock();
    1428           0 :     if( FD_UNLIKELY( now>=next ) ) {
    1429           0 :       last_snap = 1UL-last_snap;
    1430           0 :       snap_tiles( &config->topo, tiles+last_snap*tile_cnt*FD_METRICS_TOTAL_SZ );
    1431           0 :       snap_links( &config->topo, links+last_snap*(cons_cnt*8UL*FD_METRICS_ALL_LINK_IN_TOTAL) );
    1432             : 
    1433             :       /* Bench */
    1434           0 :       tps_sent_samples[ tps_sent_samples_idx%(sizeof(tps_sent_samples)/sizeof(tps_sent_samples[0])) ] = (ulong)diff_tile( config, "benchs", tiles+(1UL-last_snap)*tile_cnt*FD_METRICS_TOTAL_SZ, tiles+last_snap*tile_cnt*FD_METRICS_TOTAL_SZ, MIDX( COUNTER, BENCHS, TXN_TX ) );
    1435           0 :       tps_sent_samples_idx++;
    1436             : 
    1437             :       /* Replay */
    1438           0 :       sps_samples[ sps_samples_idx%(sizeof(sps_samples)/sizeof(sps_samples[0])) ] = (ulong)diff_tile( config, "replay", tiles+(1UL-last_snap)*tile_cnt*FD_METRICS_TOTAL_SZ, tiles+last_snap*tile_cnt*FD_METRICS_TOTAL_SZ, MIDX( COUNTER, REPLAY, SLOT_REPLAYED ) );
    1439           0 :       sps_samples_idx++;
    1440           0 :       tps_samples[ tps_samples_idx%(sizeof(tps_samples)/sizeof(tps_samples[0])) ] = (ulong)diff_tile( config, "replay", tiles+(1UL-last_snap)*tile_cnt*FD_METRICS_TOTAL_SZ, tiles+last_snap*tile_cnt*FD_METRICS_TOTAL_SZ, MIDX( COUNTER, REPLAY, TXN_PROCESSED ) );
    1441           0 :       tps_samples_idx++;
    1442           0 :       cups_samples[ cups_samples_idx%(sizeof(cups_samples)/sizeof(cups_samples[0])) ] =
    1443           0 :           (ulong)diff_tile( config, "execrp", tiles+(1UL-last_snap)*tile_cnt*FD_METRICS_TOTAL_SZ, tiles+last_snap*tile_cnt*FD_METRICS_TOTAL_SZ, MIDX( COUNTER, EXECRP, CU_EXECUTED ) ) +
    1444           0 :           (ulong)diff_tile( config, "execle", tiles+(1UL-last_snap)*tile_cnt*FD_METRICS_TOTAL_SZ, tiles+last_snap*tile_cnt*FD_METRICS_TOTAL_SZ, MIDX( COUNTER, EXECLE, CU_EXECUTED ) );
    1445           0 :       cups_samples_idx++;
    1446             : 
    1447             :       /* Snapshot */
    1448           0 :       snapshot_rx_samples[ snapshot_rx_idx%(sizeof(snapshot_rx_samples)/sizeof(snapshot_rx_samples[0])) ] = (ulong)diff_tile( config, "snapct", tiles+(1UL-last_snap)*tile_cnt*FD_METRICS_TOTAL_SZ, tiles+last_snap*tile_cnt*FD_METRICS_TOTAL_SZ, MIDX( GAUGE, SNAPCT, FULL_BYTES_READ ) ) +
    1449           0 :                                                                                                             (ulong)diff_tile( config, "snapct", tiles+(1UL-last_snap)*tile_cnt*FD_METRICS_TOTAL_SZ, tiles+last_snap*tile_cnt*FD_METRICS_TOTAL_SZ, MIDX( GAUGE, SNAPCT, INCREMENTAL_BYTES_READ ) );
    1450           0 :       snapshot_rx_idx++;
    1451           0 :       snapshot_acc_samples[ snapshot_acc_idx%(sizeof(snapshot_acc_samples)/sizeof(snapshot_acc_samples[0])) ] = (ulong)diff_tile( config, "snapin", tiles+(1UL-last_snap)*tile_cnt*FD_METRICS_TOTAL_SZ, tiles+last_snap*tile_cnt*FD_METRICS_TOTAL_SZ, MIDX( GAUGE, SNAPIN, ACCOUNT_LOADED ) );
    1452           0 :       snapshot_acc_idx++;
    1453           0 :       snapshot_wr_samples[ snapshot_wr_idx%(sizeof(snapshot_wr_samples)/sizeof(snapshot_wr_samples[0])) ] = (ulong)diff_tile( config, "snapwr", tiles+(1UL-last_snap)*tile_cnt*FD_METRICS_TOTAL_SZ, tiles+last_snap*tile_cnt*FD_METRICS_TOTAL_SZ, MIDX( GAUGE, SNAPWR, BYTES_WRITTEN ) );
    1454           0 :       snapshot_wr_idx++;
    1455             : 
    1456             :       /* Events */
    1457           0 :       events_sent_samples[ events_sent_samples_idx%(sizeof(events_sent_samples)/sizeof(events_sent_samples[0])) ] = (ulong)diff_tile( config, "event", tiles+(1UL-last_snap)*tile_cnt*FD_METRICS_TOTAL_SZ, tiles+last_snap*tile_cnt*FD_METRICS_TOTAL_SZ, MIDX( COUNTER, EVENT, SENT ) );
    1458           0 :       events_sent_samples_idx++;
    1459           0 :       events_acked_samples[ events_acked_samples_idx%(sizeof(events_acked_samples)/sizeof(events_acked_samples[0])) ] = (ulong)diff_tile( config, "event", tiles+(1UL-last_snap)*tile_cnt*FD_METRICS_TOTAL_SZ, tiles+last_snap*tile_cnt*FD_METRICS_TOTAL_SZ, MIDX( COUNTER, EVENT, ACKED ) );
    1460           0 :       events_acked_samples_idx++;
    1461           0 :       event_bytes_written_samples[ event_bytes_written_samples_idx%(sizeof(event_bytes_written_samples)/sizeof(event_bytes_written_samples[0])) ] = (ulong)diff_tile( config, "event", tiles+(1UL-last_snap)*tile_cnt*FD_METRICS_TOTAL_SZ, tiles+last_snap*tile_cnt*FD_METRICS_TOTAL_SZ, MIDX( COUNTER, EVENT, BYTES_WRITTEN ) );
    1462           0 :       event_bytes_written_samples_idx++;
    1463           0 :       event_bytes_read_samples[ event_bytes_read_samples_idx%(sizeof(event_bytes_read_samples)/sizeof(event_bytes_read_samples[0])) ] = (ulong)diff_tile( config, "event", tiles+(1UL-last_snap)*tile_cnt*FD_METRICS_TOTAL_SZ, tiles+last_snap*tile_cnt*FD_METRICS_TOTAL_SZ, MIDX( COUNTER, EVENT, BYTES_READ ) );
    1464           0 :       event_bytes_read_samples_idx++;
    1465             : 
    1466             :       /* Accounts */
    1467           0 :       sample_accdb( config, tiles+(1UL-last_snap)*tile_cnt*FD_METRICS_TOTAL_SZ, tiles+last_snap*tile_cnt*FD_METRICS_TOTAL_SZ );
    1468             : 
    1469             :       /* Repair server */
    1470           0 :       shreds_stored_sample[ shreds_stored_samples_idx%(sizeof(shreds_stored_sample)/sizeof(shreds_stored_sample[0])) ] = (ulong)diff_tile( config, "rserve", tiles+(1UL-last_snap)*tile_cnt*FD_METRICS_TOTAL_SZ, tiles+last_snap*tile_cnt*FD_METRICS_TOTAL_SZ, MIDX( GAUGE, RSERVE, SHREDS_CURRENT ) );
    1471           0 :       shreds_stored_samples_idx++;
    1472             : 
    1473           0 :       rserve_rps_valid_samples[ rserve_rps_valid_samples_idx%(sizeof(rserve_rps_valid_samples)/sizeof(rserve_rps_valid_samples[0])) ] = (ulong)(
    1474           0 :           diff_tile( config, "rserve", tiles+(1UL-last_snap)*tile_cnt*FD_METRICS_TOTAL_SZ, tiles+last_snap*tile_cnt*FD_METRICS_TOTAL_SZ, MIDX( COUNTER, RSERVE, SENT_RESPONSE_TYPES_PING ) ) +
    1475           0 :           diff_tile( config, "rserve", tiles+(1UL-last_snap)*tile_cnt*FD_METRICS_TOTAL_SZ, tiles+last_snap*tile_cnt*FD_METRICS_TOTAL_SZ, MIDX( COUNTER, RSERVE, SENT_RESPONSE_TYPES_WINDOW ) ) +
    1476           0 :           diff_tile( config, "rserve", tiles+(1UL-last_snap)*tile_cnt*FD_METRICS_TOTAL_SZ, tiles+last_snap*tile_cnt*FD_METRICS_TOTAL_SZ, MIDX( COUNTER, RSERVE, SENT_RESPONSE_TYPES_HIGHEST_WINDOW ) ) +
    1477           0 :           diff_tile( config, "rserve", tiles+(1UL-last_snap)*tile_cnt*FD_METRICS_TOTAL_SZ, tiles+last_snap*tile_cnt*FD_METRICS_TOTAL_SZ, MIDX( COUNTER, RSERVE, SENT_RESPONSE_TYPES_ORPHAN ) ) );
    1478           0 :       rserve_rps_valid_samples_idx++;
    1479             : 
    1480           0 :       rserve_rps_invalid_samples[ rserve_rps_invalid_samples_idx%(sizeof(rserve_rps_invalid_samples)/sizeof(rserve_rps_invalid_samples[0])) ] = (ulong)(
    1481           0 :           diff_tile( config, "rserve", tiles+(1UL-last_snap)*tile_cnt*FD_METRICS_TOTAL_SZ, tiles+last_snap*tile_cnt*FD_METRICS_TOTAL_SZ, MIDX( COUNTER, RSERVE, MISSED_RESPONSE_TYPES_PING ) ) +
    1482           0 :           diff_tile( config, "rserve", tiles+(1UL-last_snap)*tile_cnt*FD_METRICS_TOTAL_SZ, tiles+last_snap*tile_cnt*FD_METRICS_TOTAL_SZ, MIDX( COUNTER, RSERVE, MISSED_RESPONSE_TYPES_WINDOW ) ) +
    1483           0 :           diff_tile( config, "rserve", tiles+(1UL-last_snap)*tile_cnt*FD_METRICS_TOTAL_SZ, tiles+last_snap*tile_cnt*FD_METRICS_TOTAL_SZ, MIDX( COUNTER, RSERVE, MISSED_RESPONSE_TYPES_HIGHEST_WINDOW ) ) +
    1484           0 :           diff_tile( config, "rserve", tiles+(1UL-last_snap)*tile_cnt*FD_METRICS_TOTAL_SZ, tiles+last_snap*tile_cnt*FD_METRICS_TOTAL_SZ, MIDX( COUNTER, RSERVE, MISSED_RESPONSE_TYPES_ORPHAN ) ) +
    1485           0 :           diff_tile( config, "rserve", tiles+(1UL-last_snap)*tile_cnt*FD_METRICS_TOTAL_SZ, tiles+last_snap*tile_cnt*FD_METRICS_TOTAL_SZ, MIDX( COUNTER, RSERVE, FAILED_SIGVERIFY ) ) +
    1486           0 :           diff_tile( config, "rserve", tiles+(1UL-last_snap)*tile_cnt*FD_METRICS_TOTAL_SZ, tiles+last_snap*tile_cnt*FD_METRICS_TOTAL_SZ, MIDX( COUNTER, RSERVE, FAILED_OWN_KEY ) ) +
    1487           0 :           diff_tile( config, "rserve", tiles+(1UL-last_snap)*tile_cnt*FD_METRICS_TOTAL_SZ, tiles+last_snap*tile_cnt*FD_METRICS_TOTAL_SZ, MIDX( COUNTER, RSERVE, FAILED_INVALID_TOKEN ) ) +
    1488           0 :           diff_tile( config, "rserve", tiles+(1UL-last_snap)*tile_cnt*FD_METRICS_TOTAL_SZ, tiles+last_snap*tile_cnt*FD_METRICS_TOTAL_SZ, MIDX( COUNTER, RSERVE, FAILED_NOT_FOR_US ) ) +
    1489           0 :           diff_tile( config, "rserve", tiles+(1UL-last_snap)*tile_cnt*FD_METRICS_TOTAL_SZ, tiles+last_snap*tile_cnt*FD_METRICS_TOTAL_SZ, MIDX( COUNTER, RSERVE, FAILED_OUTDATED ) ) +
    1490           0 :           diff_tile( config, "rserve", tiles+(1UL-last_snap)*tile_cnt*FD_METRICS_TOTAL_SZ, tiles+last_snap*tile_cnt*FD_METRICS_TOTAL_SZ, MIDX( COUNTER, RSERVE, FAILED_INVALID_SHRED_INDEX ) ) );
    1491           0 :       rserve_rps_invalid_samples_idx++;
    1492             : 
    1493             :       /* Move cursor to top of dashboard and overwrite in place.
    1494             :          All output is buffered and flushed in a single write() so
    1495             :          the terminal never renders a partially drawn frame. */
    1496           0 :       frame_len = 0UL;
    1497           0 :       PRINT( "\033[?25l" ); /* hide cursor during redraw */
    1498           0 :       if( FD_UNLIKELY( !ended_on_newline ) ) {
    1499           0 :         PRINT( "\033[%luA\r", lines_printed+1UL );
    1500           0 :       } else {
    1501           0 :         PRINT( "\033[%luA\r", lines_printed );
    1502           0 :       }
    1503           0 :       write_summary( config, node_info, tiles+last_snap*tile_cnt*FD_METRICS_TOTAL_SZ, tiles+(1UL-last_snap)*tile_cnt*FD_METRICS_TOTAL_SZ, links+last_snap*(cons_cnt*8UL*FD_METRICS_ALL_LINK_IN_TOTAL), links+(1UL-last_snap)*(cons_cnt*8UL*FD_METRICS_ALL_LINK_IN_TOTAL), interposing );
    1504           0 :       PRINT( "\033[0J" );    /* clear any leftover lines below */
    1505           0 :       PRINT( "\033[?25h" ); /* show cursor */
    1506           0 :       flush_frame();
    1507           0 :       next += (long)1e7;
    1508           0 :     }
    1509           0 :   }
    1510           0 : }
    1511             : 
    1512             : void
    1513             : watch_cmd_args( int *    pargc FD_PARAM_UNUSED,
    1514             :                 char *** pargv FD_PARAM_UNUSED,
    1515           0 :                 args_t * args ) {
    1516           0 :   args->watch.drain_output_fd = -1;
    1517           0 : }
    1518             : 
    1519             : void
    1520             : watch_cmd_fn( args_t *   args,
    1521           0 :               config_t * config ) {
    1522           0 :   int allow_fds[ 5 ];
    1523           0 :   ulong allow_fds_cnt = 0;
    1524           0 :   allow_fds[ allow_fds_cnt++ ] = 0; /* stdin */
    1525           0 :   allow_fds[ allow_fds_cnt++ ] = 1; /* stdout */
    1526           0 :   allow_fds[ allow_fds_cnt++ ] = 2; /* stderr */
    1527           0 :   if( FD_LIKELY( fd_log_private_logfile_fd()!=-1 ) )
    1528           0 :     allow_fds[ allow_fds_cnt++ ] = fd_log_private_logfile_fd(); /* logfile */
    1529           0 :   if( FD_UNLIKELY( args->watch.drain_output_fd!=-1 ) )
    1530           0 :     allow_fds[ allow_fds_cnt++ ] = args->watch.drain_output_fd; /* maybe we are interposing firedancer log output with the monitor */
    1531             : 
    1532           0 :   fd_topo_join_workspaces( &config->topo, FD_SHMEM_JOIN_MODE_READ_ONLY, FD_TOPO_CORE_DUMP_LEVEL_DISABLED );
    1533             : 
    1534           0 :   struct sock_filter seccomp_filter[ 128UL ];
    1535           0 :   uint drain_output_fd = args->watch.drain_output_fd >= 0 ? (uint)args->watch.drain_output_fd : (uint)-1;
    1536           0 :   populate_sock_filter_policy_watch( 128UL, seccomp_filter, (uint)fd_log_private_logfile_fd(), drain_output_fd );
    1537             : 
    1538           0 :   if( FD_LIKELY( config->development.sandbox ) ) {
    1539           0 :     fd_sandbox_enter( config->uid,
    1540           0 :                       config->gid,
    1541           0 :                       0,
    1542           0 :                       0,
    1543           0 :                       0,
    1544           0 :                       1, /* Keep controlling terminal for main so it can receive Ctrl+C */
    1545           0 :                       0,
    1546           0 :                       0UL,
    1547           0 :                       0UL,
    1548           0 :                       0UL,
    1549           0 :                       0UL,
    1550           0 :                       allow_fds_cnt,
    1551           0 :                       allow_fds,
    1552           0 :                       sock_filter_policy_watch_instr_cnt,
    1553           0 :                       seccomp_filter );
    1554           0 :   } else {
    1555           0 :     fd_sandbox_switch_uid_gid( config->uid, config->gid );
    1556           0 :   }
    1557             : 
    1558           0 :   fd_topo_fill( &config->topo );
    1559             : 
    1560           0 :   run( config, args->watch.drain_output_fd );
    1561           0 : }
    1562             : 
    1563             : action_t fd_action_watch = {
    1564             :   .name           = "watch",
    1565             :   .args           = watch_cmd_args,
    1566             :   .fn             = watch_cmd_fn,
    1567             :   .require_config = 1,
    1568             :   .perm           = watch_cmd_perm,
    1569             :   .description    = "Watch a locally running Firedancer instance with a terminal GUI",
    1570             :   .detail         = "Connects to a running validator and renders a terminal dashboard of the\n"
    1571             :                     "most important monitoring and operational metrics.",
    1572             : };

Generated by: LCOV version 1.14