LCOV - code coverage report
Current view: top level - app/firedancer-dev/commands - snapshot_load.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 476 0.0 %
Date: 2026-09-17 04:28:31 Functions: 0 15 0.0 %

          Line data    Source code
       1             : #include "../../firedancer/topology.h"
       2             : #include "../../platform/fd_sys_util.h"
       3             : #include "../../shared/commands/configure/configure.h"
       4             : #include "../../shared/commands/run/run.h"
       5             : #include "../../shared_dev/commands/dev.h"
       6             : #include "../../../disco/metrics/fd_metrics.h"
       7             : #include "../../../disco/topo/fd_topob.h"
       8             : #include "../../../disco/pack/fd_pack_cost.h"
       9             : #include "../../../util/pod/fd_pod_format.h"
      10             : #include "../../../discof/restore/utils/fd_ssctrl.h"
      11             : #include "../../../discof/restore/utils/fd_ssmsg.h"
      12             : #include "../../../flamenco/runtime/fd_cost_tracker.h"
      13             : #include "../../../flamenco/accdb/fd_accdb_private.h"
      14             : 
      15             : #include <fcntl.h> /* open */
      16             : #include <sys/resource.h>
      17             : #include <linux/capability.h>
      18             : #include <unistd.h> /* close, sleep */
      19             : #include <stdlib.h>
      20             : #include <stdio.h>
      21             : 
      22             : #define NAME "snapshot-load"
      23             : 
      24           0 : #define SL_RESET  "\033[0m"
      25           0 : #define SL_BOLD   "\033[1m"
      26           0 : #define SL_DIM    "\033[2m"
      27           0 : #define SL_RED    "\033[31m"
      28             : #define SL_GREEN  "\033[32m"
      29           0 : #define SL_YELLOW "\033[33m"
      30             : 
      31             : static char const *
      32             : sev_color( int    color,
      33           0 :            double pct ) {
      34           0 :   if( FD_UNLIKELY( !color ) ) return "";
      35           0 :   if( FD_UNLIKELY( pct>=85.0 ) ) return SL_RED;
      36           0 :   if( FD_UNLIKELY( pct>=50.0 ) ) return SL_YELLOW;
      37           0 :   return "";
      38           0 : }
      39             : 
      40             : static double
      41           0 : clamp_pct( double pct ) {
      42           0 :   return fd_double_if( pct<0.0, 0.0, fd_double_if( pct>100.0, 100.0, pct ) );
      43           0 : }
      44             : 
      45             : static char *
      46             : fmt_bar( char * buf,
      47             :          ulong  buf_sz,
      48             :          int    color,
      49             :          double pct,
      50           0 :          ulong  width ) {
      51           0 :   ulong filled = (ulong)( clamp_pct( pct )*(double)width/100.0+0.5 );
      52           0 :   ulong len = 0UL, l;
      53           0 :   FD_TEST( fd_cstr_printf_check( buf, buf_sz, &len, "%s", color ? SL_GREEN : "" ) );
      54           0 :   for( ulong i=0UL;    i<filled; i++ ) { FD_TEST( fd_cstr_printf_check( buf+len, buf_sz-len, &l, "█" ) ); len += l; }
      55           0 :   FD_TEST( fd_cstr_printf_check( buf+len, buf_sz-len, &l, "%s", color ? SL_DIM : "" ) ); len += l;
      56           0 :   for( ulong i=filled; i<width;  i++ ) { FD_TEST( fd_cstr_printf_check( buf+len, buf_sz-len, &l, "░" ) ); len += l; }
      57           0 :   FD_TEST( fd_cstr_printf_check( buf+len, buf_sz-len, &l, "%s", color ? SL_RESET : "" ) );
      58           0 :   return buf;
      59           0 : }
      60             : 
      61             : static char const *
      62           0 : phase_cstr( ulong state ) {
      63           0 :   if( FD_LIKELY( ( state>= 5UL && state<= 8UL ) || ( state>=13UL && state<=16UL ) ) ) return "full";
      64           0 :   if( FD_LIKELY( ( state>= 9UL && state<=12UL ) || ( state>=17UL && state<=20UL ) ) ) return "incr";
      65           0 :   if( FD_UNLIKELY( state==21UL ) ) return "done";
      66           0 :   return "wait";
      67           0 : }
      68             : 
      69             : extern fd_topo_obj_callbacks_t * CALLBACKS[];
      70             : 
      71             : fd_topo_run_tile_t
      72             : fdctl_tile_run( fd_topo_tile_t const * tile );
      73             : 
      74             : static void
      75           0 : snapshot_load_topo( config_t * config ) {
      76           0 :   config->firedancer.layout.resolv_tile_count = 0;
      77           0 :   fd_topo_t * topo = &config->topo;
      78           0 :   fd_topob_new( &config->topo, config->name );
      79           0 :   topo->max_page_size = fd_cstr_to_shmem_page_sz( config->hugetlbfs.max_page_size );
      80             : 
      81           0 :   fd_topob_wksp( topo, "txncache" );
      82           0 :   fd_topo_obj_t * txncache_obj = setup_topo_txncache( topo, "txncache",
      83           0 :       config->firedancer.runtime.max_live_slots,
      84           0 :       2UL*config->limits.max_txn_per_slot );
      85           0 :   FD_TEST( fd_pod_insertf_ulong( topo->props, txncache_obj->id, "txncache" ) );
      86             : 
      87           0 :   fd_topob_wksp( topo, "accdb" );
      88           0 :   fd_topo_obj_t * accdb_obj = setup_topo_accdb( topo, "accdb",
      89           0 :       config->firedancer.accounts.max_accounts,
      90           0 :       config->firedancer.runtime.max_live_slots,
      91           0 :       FD_RUNTIME_MAX_ACC_WRITES_PER_SLOT,
      92           0 :       8192UL,
      93           0 :       1UL<<35UL,
      94           0 :       config->firedancer.accounts.cache_size_gib*(1UL<<30UL),
      95           0 :       config->tiles.bundle.enabled,
      96           0 :       2UL,
      97           0 :       0UL );
      98           0 :   FD_TEST( fd_pod_insertf_ulong( topo->props, accdb_obj->id, "accdb" ) );
      99             : 
     100           0 :   fd_topob_wksp( topo, "banks" );
     101           0 :   fd_topo_obj_t * banks_obj = setup_topo_banks( topo, "banks",
     102           0 :       config->firedancer.runtime.max_live_slots,
     103           0 :       config->firedancer.runtime.max_fork_width,
     104           0 :       config->development.bench.max_cost_per_block );
     105           0 :   FD_TEST( fd_pod_insertf_ulong( topo->props, banks_obj->id, "banks" ) );
     106             : 
     107           0 : #define FOR(cnt) for( ulong i=0UL; i<cnt; i++ )
     108             : 
     109             :   /* metrics tile *****************************************************/
     110           0 :   fd_topob_wksp( topo, "metric_in" );
     111           0 :   fd_topob_wksp( topo, "metric" );
     112           0 :   fd_topob_tile( topo, "metric",  "metric", "metric_in", ULONG_MAX, 0, 0, 0, 1 );
     113             : 
     114             :   /* read() tile */
     115           0 :   fd_topob_wksp( topo, "snapct" );
     116           0 :   fd_topo_tile_t * snapct_tile = fd_topob_tile( topo, "snapct", "snapct", "metric_in", ULONG_MAX, 0, 0, 0, 0 );
     117           0 :   snapct_tile->allow_shutdown = 1;
     118             : 
     119             :   /* load tile */
     120           0 :   fd_topob_wksp( topo, "snapld" );
     121           0 :   fd_topo_tile_t * snapld_tile = fd_topob_tile( topo, "snapld", "snapld", "metric_in", ULONG_MAX, 0, 0, 0, 0 );
     122           0 :   snapld_tile->allow_shutdown = 1;
     123             : 
     124             :   /* "snapdc": Zstandard decompress tile */
     125           0 :   fd_topob_wksp( topo, "snapdc" );
     126           0 :   ulong snapdc_tile_cnt = config->firedancer.layout.snapdc_tile_count;
     127           0 :   FOR(snapdc_tile_cnt) fd_topob_tile( topo, "snapdc", "snapdc", "metric_in", ULONG_MAX, 0, 0, 0, 0 )->allow_shutdown = 1;
     128             : 
     129             :   /* "snapin": Snapshot parser tile */
     130           0 :   fd_topob_wksp( topo, "snapin" );
     131           0 :   fd_topo_tile_t * snapin_tile = fd_topob_tile( topo, "snapin", "snapin", "metric_in", ULONG_MAX, 0, 0, 0, 0 );
     132           0 :   snapin_tile->allow_shutdown = 1;
     133             : 
     134           0 :   fd_topob_wksp( topo, "snapwr" );
     135           0 :   fd_topo_tile_t * snapwr_tile = fd_topob_tile( topo, "snapwr", "snapwr", "metric_in", ULONG_MAX, 0, 0, 0, 0 );
     136           0 :   snapwr_tile->allow_shutdown = 1;
     137             : 
     138           0 :   fd_topob_wksp( topo, "diag" );
     139           0 :   fd_topob_tile( topo, "diag", "diag", "metric_in", ULONG_MAX, 0, 0, 0, 0 );
     140           0 :   fd_topo_tile_t * accdb_tile = fd_topob_tile( topo, "accdb", "accdb", "metric_in", ULONG_MAX, 0, 0, 0, 0 );
     141             : 
     142           0 :   fd_topob_wksp( topo, "snapct_ld"    );
     143           0 :   fd_topob_wksp( topo, "snapld_dc"    );
     144           0 :   fd_topob_wksp( topo, "snapdc_in"    );
     145             : 
     146           0 :   fd_topob_wksp( topo, "snapin_manif" );
     147           0 :   fd_topob_wksp( topo, "snapct_repr"  );
     148             : 
     149           0 :   fd_topob_wksp( topo, "snapin_ct"    );
     150           0 :   fd_topob_wksp( topo, "snapwr_ct"    );
     151             : 
     152           0 :   fd_topob_link( topo, "snapct_ld",    "snapct_ld",    128UL,   sizeof(fd_ssctrl_init_t),       1UL );
     153           0 :   fd_topob_link( topo, "snapld_dc",    "snapld_dc",    FD_SNAPSHOT_DATA_DEPTH, FD_SNAPSHOT_DATA_MTU,           1UL );
     154           0 :   FOR(snapdc_tile_cnt) fd_topob_link( topo, "snapdc_in", "snapdc_in", FD_SNAPSHOT_DATA_DEPTH, FD_SNAPSHOT_DATA_MTU, 1UL );
     155           0 :   fd_topob_link( topo, "snapin_manif", "snapin_manif", 4UL,     sizeof(fd_snapshot_manifest_t), 1UL )->permit_no_consumers = 1;
     156           0 :   fd_topob_link( topo, "snapct_repr",  "snapct_repr",  128UL,   0UL,                            1UL )->permit_no_consumers = 1;
     157             : 
     158           0 :   fd_topob_link( topo, "snapin_ct", "snapin_ct",   128UL,  0UL,                             1UL );
     159           0 :   fd_topob_link( topo, "snapwr_ct", "snapwr_ct",   128UL,  0UL,                             1UL );
     160           0 :   fd_topob_tile_in( topo, "snapct",  0UL, "metric_in", "snapin_ct",  0UL, FD_TOPOB_RELIABLE,   FD_TOPOB_POLLED );
     161           0 :   fd_topob_tile_in( topo, "snapct",  0UL, "metric_in", "snapwr_ct",  0UL, FD_TOPOB_RELIABLE,   FD_TOPOB_POLLED );
     162             : 
     163           0 :   fd_topob_tile_in ( topo, "snapct",  0UL, "metric_in", "snapld_dc",    0UL, FD_TOPOB_RELIABLE,   FD_TOPOB_POLLED );
     164           0 :   fd_topob_tile_out( topo, "snapct",  0UL,              "snapct_ld",    0UL                                       );
     165           0 :   fd_topob_tile_out( topo, "snapct",  0UL,              "snapct_repr",  0UL                                       );
     166           0 :   fd_topob_tile_in ( topo, "snapld",  0UL, "metric_in", "snapct_ld",    0UL, FD_TOPOB_RELIABLE,   FD_TOPOB_POLLED );
     167           0 :   fd_topob_tile_out( topo, "snapld",  0UL,              "snapld_dc",    0UL                                       );
     168           0 :   FOR(snapdc_tile_cnt) fd_topob_tile_in ( topo, "snapdc", i,   "metric_in", "snapld_dc", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
     169           0 :   FOR(snapdc_tile_cnt) fd_topob_tile_out( topo, "snapdc", i,               "snapdc_in", i                                         );
     170           0 :   FOR(snapdc_tile_cnt) fd_topob_tile_in ( topo, "snapin", 0UL, "metric_in", "snapdc_in", i,   FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
     171           0 :   fd_topob_tile_out( topo, "snapin",  0UL,              "snapin_manif", 0UL                                       );
     172           0 :   fd_topob_tile_out( topo, "snapin",  0UL,              "snapin_ct",    0UL                                       );
     173           0 :   FOR(snapdc_tile_cnt) fd_topob_tile_in ( topo, "snapwr", 0UL, "metric_in", "snapdc_in", i,   FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
     174           0 :   fd_topob_tile_out( topo, "snapwr",  0UL,              "snapwr_ct",    0UL                                       );
     175             : 
     176           0 :   fd_topob_tile_uses( topo, snapin_tile, txncache_obj,   FD_SHMEM_JOIN_MODE_READ_WRITE );
     177           0 :   fd_topob_tile_uses( topo, snapin_tile, accdb_obj,      FD_SHMEM_JOIN_MODE_READ_WRITE );
     178           0 :   fd_topob_tile_uses( topo, snapin_tile, banks_obj,      FD_SHMEM_JOIN_MODE_READ_WRITE );
     179           0 :   fd_topob_tile_uses( topo, accdb_tile,  accdb_obj,      FD_SHMEM_JOIN_MODE_READ_WRITE );
     180           0 :   snapin_tile->snapin.accdb_obj_id    = accdb_obj->id;
     181           0 :   snapin_tile->snapin.txncache_obj_id = txncache_obj->id;
     182           0 :   snapin_tile->snapin.banks_obj_id    = banks_obj->id;
     183           0 :   snapin_tile->snapin.max_live_slots  = config->firedancer.runtime.max_live_slots;
     184             : 
     185           0 :   for( ulong i=0UL; i<topo->tile_cnt; i++ ) {
     186           0 :     fd_topo_tile_t * tile = &topo->tiles[ i ];
     187           0 :     fd_topo_configure_tile( tile, config );
     188           0 :   }
     189             : 
     190           0 :   fd_topob_waker( topo );
     191           0 :   fd_topob_auto_layout( topo, 0 );
     192           0 :   fd_topob_finish( topo, CALLBACKS );
     193           0 : }
     194             : 
     195             : static void
     196           0 : snapshot_load_topo1( config_t * config ) {
     197           0 :   snapshot_load_topo( config );
     198           0 : }
     199             : 
     200             : static void
     201             : snapshot_load_args( int *    pargc,
     202             :                     char *** pargv,
     203           0 :                     args_t * args ) {
     204           0 :   if( FD_UNLIKELY( fd_env_strip_cmdline_contains( pargc, pargv, "--help" ) ) ) {
     205           0 :     fputs(
     206           0 :       "\nUsage: firedancer-dev snapshot-load [GLOBAL FLAGS] [FLAGS]\n"
     207           0 :       "\n"
     208           0 :       "Global Flags:\n"
     209           0 :       "  --mainnet            Use Solana mainnet-beta defaults\n"
     210           0 :       "  --testnet            Use Solana testnet defaults\n"
     211           0 :       "  --devnet             Use Solana devnet defaults\n"
     212           0 :       "\n"
     213           0 :       "Flags:\n"
     214           0 :       "  --snapshot-dir PATH  Load/save snapshots from this directory\n"
     215           0 :       "  --offline            Do not attempt to download snapshots\n"
     216           0 :       "  --no-incremental     Disable incremental snapshot loading\n"
     217           0 :       "  --no-watch           Do not print periodic progress updates\n"
     218           0 :       "  --db-rec-max <num>   Database max record/account count (e.g. 10e6 -> 10M accounts)\n"
     219           0 :       "  --accounts-hist      After loading, analyze account size distribution\n"
     220           0 :       "\n",
     221           0 :       stderr );
     222           0 :     exit( 0 );
     223           0 :   }
     224           0 :   memset( &args->snapshot_load, 0, sizeof(args->snapshot_load) );
     225             : 
     226           0 :   char const * snapshot_dir  = fd_env_strip_cmdline_cstr    ( pargc, pargv, "--snapshot-dir", NULL, NULL   );
     227           0 :   int          offline       = fd_env_strip_cmdline_contains( pargc, pargv, "--offline"                    )!=0;
     228           0 :   int          no_incremental= fd_env_strip_cmdline_contains( pargc, pargv, "--no-incremental"             )!=0;
     229           0 :   int          no_watch      = fd_env_strip_cmdline_contains( pargc, pargv, "--no-watch"                   )!=0;
     230           0 :   int          accounts_hist = fd_env_strip_cmdline_contains( pargc, pargv, "--accounts-hist"              )!=0;
     231           0 :   double       db_sz         = fd_env_strip_cmdline_double  ( pargc, pargv, "--db-sz",        NULL, 0.0    );
     232           0 :   double       db_rec_max    = fd_env_strip_cmdline_double  ( pargc, pargv, "--db-rec-max",   NULL, 0.0    );
     233           0 :   if( FD_UNLIKELY( !(db_sz>=0.0 && db_sz<1.8e19) ) )           FD_LOG_ERR(( "--db-sz out of range" ));      /* also rejects NaN */
     234           0 :   if( FD_UNLIKELY( !(db_rec_max>=0.0 && db_rec_max<1.8e19) ) ) FD_LOG_ERR(( "--db-rec-max out of range" ));
     235             : 
     236           0 :   fd_cstr_ncpy( args->snapshot_load.snapshot_dir, snapshot_dir, sizeof(args->snapshot_load.snapshot_dir) );
     237           0 :   args->snapshot_load.accounts_hist  = accounts_hist;
     238           0 :   args->snapshot_load.offline        = offline;
     239           0 :   args->snapshot_load.no_incremental = no_incremental;
     240           0 :   args->snapshot_load.no_watch       = no_watch;
     241           0 :   args->snapshot_load.db_rec_max     = (ulong)db_rec_max;
     242           0 :   args->snapshot_load.cache_sz       = (ulong)db_sz;
     243           0 : }
     244             : 
     245             : /* ACCOUNTS_HIST_N (32) is chosen to make the histogram lightweight.
     246             :    And because accounts can have a data size in the range [0, 10MiB],
     247             :    the width of the bins increments in powers of 2.  In the future, it
     248             :    should be possible to pass this as a configuration parameter. */
     249           0 : #define ACCOUNTS_HIST_N (32)
     250             : 
     251             : struct accounts_hist {
     252             :   ulong total_cnt;
     253             :   ulong total_acc;
     254             :   ulong bin_thi[ ACCOUNTS_HIST_N ];
     255             :   ulong bin_cnt[ ACCOUNTS_HIST_N ];
     256             :   ulong bin_acc[ ACCOUNTS_HIST_N ];
     257             :   ulong bin_min[ ACCOUNTS_HIST_N ];
     258             :   ulong bin_max[ ACCOUNTS_HIST_N ];
     259             :   ulong token_cnt;
     260             : };
     261             : typedef struct accounts_hist accounts_hist_t;
     262             : 
     263             : static inline void
     264           0 : accounts_hist_reset( accounts_hist_t * hist ) {
     265           0 :   hist->total_cnt = 0UL;
     266           0 :   hist->total_acc = 0UL;
     267           0 :   for( int i=0; i < ACCOUNTS_HIST_N; i++ ) {
     268           0 :     hist->bin_thi[ i ] = fd_ulong_if( i > 0, fd_pow2( ulong, i-1 ), 0UL );
     269           0 :     hist->bin_cnt[ i ] = 0UL;
     270           0 :     hist->bin_acc[ i ] = 0UL;
     271           0 :     hist->bin_min[ i ] = ULONG_MAX;
     272           0 :     hist->bin_max[ i ] = 0UL;
     273           0 :   }
     274           0 :   hist->token_cnt = 0UL;
     275           0 : }
     276             : 
     277             : static inline void FD_FN_UNUSED
     278             : accounts_hist_update( accounts_hist_t * hist,
     279           0 :                       ulong             account_sz ) {
     280           0 :   hist->total_cnt += 1UL;
     281           0 :   hist->total_acc += account_sz;
     282           0 :   int i=0;
     283             :   /* This allows for arbitrary thresholds - not optimized for pow2
     284             :      bins. */
     285           0 :   for( ; i < ACCOUNTS_HIST_N; i++ ) {
     286           0 :     if( FD_UNLIKELY( account_sz <= hist->bin_thi[ i ] )) {
     287           0 :       hist->bin_cnt[ i ] += 1;
     288           0 :       hist->bin_acc[ i ] += account_sz;
     289           0 :       hist->bin_min[ i ] = fd_ulong_min( hist->bin_min[ i ], account_sz );
     290           0 :       hist->bin_max[ i ] = fd_ulong_max( hist->bin_max[ i ], account_sz );
     291           0 :       break;
     292           0 :     }
     293           0 :   }
     294           0 :   FD_TEST( i < ACCOUNTS_HIST_N );
     295           0 : }
     296             : 
     297             : static inline int
     298           0 : accounts_hist_check( accounts_hist_t const * hist ) {
     299           0 :   ulong cnt = 0UL;
     300           0 :   ulong acc = 0UL;
     301           0 :   for( int i=0; i < ACCOUNTS_HIST_N; i++ ) {
     302           0 :     cnt += hist->bin_cnt[ i ];
     303           0 :     acc += hist->bin_acc[ i ];
     304           0 :   }
     305           0 :   if( cnt != hist->total_cnt ) return -1;
     306           0 :   if( acc != hist->total_acc ) return -2;
     307           0 :   return 0;
     308           0 : }
     309             : 
     310             : static void
     311           0 : accounts_hist_print( accounts_hist_t const * hist ) {
     312           0 :   double hist_total_cnt_M   = (double)hist->total_cnt / (double)1.0e6;
     313           0 :   double hist_total_cnt_GiB = (double)hist->total_acc / (double)1073741824;
     314           0 :   printf( "\n" );
     315           0 :   printf( "hist_total_cnt %16lu ( %6.1f M   )\n", hist->total_cnt, hist_total_cnt_M   );
     316           0 :   printf( "hist_total_acc %16lu ( %6.1f GiB )\n", hist->total_acc, hist_total_cnt_GiB );
     317           0 :   printf( "   bin_th_lo <  sz <=    bin_th_hi |    bin_cnt (run_sum%%) |      bin_acc (run_sum%%) |    bin_min B |    bin_max B |    bin_avg B |\n" );
     318           0 :   ulong sum_cnt = 0UL;
     319           0 :   ulong sum_acc = 0UL;
     320           0 :   for( int i=0; i < ACCOUNTS_HIST_N; i++ ) {
     321             :     /* bin thresholds */
     322           0 :     ulong hist_bin_tlo      = hist->bin_thi[ fd_int_if( i>0, i-1, i ) ];
     323           0 :     ulong hist_bin_thi      = hist->bin_thi[ i ];
     324             :     /* bin cnt */
     325           0 :     ulong hist_bin_cnt      = hist->bin_cnt[ i ];
     326           0 :     sum_cnt                += hist->bin_cnt[ i ];
     327           0 :     double sum_cnt_p        = (double)(sum_cnt * 100) / (double)hist->total_cnt;
     328           0 :     double hist_bin_cnt_K   = (double)(hist_bin_cnt) / (double)1.0e3;
     329             :     /* bin acc */
     330           0 :     ulong hist_bin_acc      = hist->bin_acc[ i ];
     331           0 :     sum_acc                += hist->bin_acc[ i ];
     332           0 :     double sum_acc_p        = (double)(sum_acc * 100) / (double)hist->total_acc;
     333           0 :     double hist_bin_acc_MiB = (double)(hist_bin_acc) / (double)1048576.0f;
     334             :     /* bin min, max, avg */
     335           0 :     ulong hist_bin_min      = fd_ulong_if( hist->bin_cnt[ i ] > 0, hist->bin_min[ i ], 0UL );
     336           0 :     ulong hist_bin_max      = hist->bin_max[ i ];
     337           0 :     ulong hist_bin_avg      = hist->bin_cnt[ i ] > 0 ? hist->bin_acc[ i ] / hist->bin_cnt[ i ] : 0UL;
     338             :     /* log */
     339           0 :     char buf[256];
     340           0 :     FD_TEST( fd_cstr_printf_check( buf, sizeof(buf), NULL,
     341           0 :                                   "%12lu %s sz <= %12lu | %8.1f K (%6.1f %%) | %8.1f MiB (%6.1f %%) | %12lu | %12lu | %12lu |\n",
     342           0 :                                   hist_bin_tlo, i==0? "<=" : "< ", hist_bin_thi,
     343           0 :                                   hist_bin_cnt_K, sum_cnt_p,
     344           0 :                                   hist_bin_acc_MiB, sum_acc_p,
     345           0 :                                   hist_bin_min, hist_bin_max, hist_bin_avg ) );
     346           0 :     printf( "%s", buf );
     347           0 :   }
     348           0 :   printf( "\n" );
     349           0 : }
     350             : 
     351             : static void
     352             : accounts_hist( accounts_hist_t * hist,
     353           0 :                config_t *        config ) {
     354           0 :   fd_topo_t * topo = &config->topo;
     355           0 :   ulong accdb_obj_id = fd_pod_query_ulong( topo->props, "accdb", ULONG_MAX );
     356           0 :   FD_TEST( accdb_obj_id!=ULONG_MAX );
     357           0 :   void * _accdb_shmem = fd_topo_obj_laddr( topo, accdb_obj_id );
     358           0 :   fd_accdb_shmem_t * shmem = fd_accdb_shmem_join( _accdb_shmem );
     359           0 :   FD_TEST( shmem );
     360             : 
     361             :   /* Recompute the shmem layout to locate acc_map and acc_pool element
     362             :      storage without taking a writer joiner slot.  This mirrors the
     363             :      layout in fd_accdb_shmem_new and fd_accdb_join_readonly. */
     364             : 
     365           0 :   ulong max_live_slots              = shmem->max_live_slots;
     366           0 :   ulong max_accounts                = shmem->max_accounts;
     367           0 :   ulong chain_cnt                   = shmem->chain_cnt;
     368             : 
     369           0 :   FD_SCRATCH_ALLOC_INIT( l, shmem );
     370           0 :                                   FD_SCRATCH_ALLOC_APPEND( l, FD_ACCDB_SHMEM_ALIGN,           sizeof(fd_accdb_shmem_t)                                );
     371           0 :                                   FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_accdb_fork_shmem_t), max_live_slots*sizeof(fd_accdb_fork_shmem_t)            );
     372           0 :                                   FD_SCRATCH_ALLOC_APPEND( l, descends_set_align(),           max_live_slots*descends_set_footprint( max_live_slots ) );
     373           0 :   uint *               acc_map  = FD_SCRATCH_ALLOC_APPEND( l, alignof(uint),                  chain_cnt*sizeof(uint)                                  );
     374           0 :   fd_accdb_accmeta_t * acc_pool = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_accdb_accmeta_t),    max_accounts*sizeof(fd_accdb_accmeta_t)                 );
     375             : 
     376             :   /* Walk every hash chain.  Each non-UINT_MAX head index yields a
     377             :      linked list of live acc_pool elements via map.next. */
     378             : 
     379           0 :   for( ulong chain_i=0UL; chain_i<chain_cnt; chain_i++ ) {
     380           0 :     uint acc_idx = acc_map[ chain_i ];
     381           0 :     while( acc_idx!=UINT_MAX ) {
     382           0 :       fd_accdb_accmeta_t const * accmeta = &acc_pool[ acc_idx ];
     383           0 :       ulong data_sz = (ulong)FD_ACCDB_SIZE_DATA( accmeta->executable_size );
     384           0 :       accounts_hist_update( hist, sizeof(fd_accdb_disk_meta_t) + data_sz );
     385           0 :       acc_idx = accmeta->map.next;
     386           0 :     }
     387           0 :   }
     388           0 : }
     389             : 
     390             : /* fixup_config applies command-line arguments to config, overriding
     391             :    defaults / config file */
     392             : 
     393             : static void
     394             : fixup_config( config_t *     config,
     395           0 :               args_t const * args ) {
     396           0 :   fd_topo_t * topo = &config->topo;
     397           0 :   if( args->snapshot_load.snapshot_dir[0] ) {
     398           0 :     fd_cstr_ncpy( config->paths.snapshots, args->snapshot_load.snapshot_dir, sizeof(config->paths.snapshots) );
     399           0 :   }
     400             : 
     401           0 :   if( args->snapshot_load.db_rec_max ) {
     402           0 :     config->firedancer.accounts.max_accounts = args->snapshot_load.db_rec_max;
     403           0 :   }
     404             : 
     405           0 :   if( args->snapshot_load.cache_sz ) {
     406           0 :     config->firedancer.accounts.cache_size_gib = fd_ulong_align_up( args->snapshot_load.cache_sz, (1UL<<30) )>>30;
     407           0 :   }
     408             : 
     409           0 :   if( args->snapshot_load.offline ) {
     410           0 :     config->firedancer.snapshots.sources.gossip.allow_any      = 0;
     411           0 :     config->firedancer.snapshots.sources.gossip.allow_list_cnt = 0;
     412           0 :     config->firedancer.snapshots.sources.servers_cnt           = 0;
     413           0 :   }
     414             : 
     415           0 :   if( args->snapshot_load.no_incremental ) {
     416           0 :     config->firedancer.snapshots.incremental_snapshots = 0;
     417           0 :   }
     418             : 
     419           0 :   if( FD_UNLIKELY( config->firedancer.snapshots.sources.gossip.allow_any || config->firedancer.snapshots.sources.gossip.allow_list_cnt ) ) {
     420           0 :     FD_LOG_WARNING(( "snapshot-load command is incompatible with gossip snapshot sources; disabling gossip snapshot sources" ));
     421           0 :     config->firedancer.snapshots.sources.gossip.allow_any      = 0;
     422           0 :     config->firedancer.snapshots.sources.gossip.allow_list_cnt = 0;
     423           0 :   }
     424             : 
     425             :   /* FIXME Unfortunately, the fdctl boot procedure constructs the
     426             :            topology before parsing command-line arguments.  So, here,
     427             :            we construct the topology again (a third time ... sigh). */
     428           0 :   snapshot_load_topo( config );
     429             : 
     430           0 :   fd_topob_auto_layout( topo, 0 );
     431           0 :   fd_topob_finish( topo, CALLBACKS );
     432           0 : }
     433             : 
     434             : static void
     435             : snapshot_load_cmd_fn( args_t *   args,
     436           0 :                       config_t * config ) {
     437           0 :   fixup_config( config, args );
     438             : 
     439           0 :   int watch = !args->snapshot_load.no_watch;
     440             : 
     441           0 :   fd_topo_t * topo = &config->topo;
     442             : 
     443           0 :   args_t configure_args = {
     444           0 :     .configure.command = CONFIGURE_CMD_INIT,
     445           0 :   };
     446             : 
     447           0 :   for( ulong i=0UL; STAGES[ i ]; i++ )
     448           0 :     configure_args.configure.stages[ i ] = STAGES[ i ];
     449           0 :   configure_cmd_fn( &configure_args, config );
     450             : 
     451           0 :   run_firedancer_init( config, 1, 0 );
     452             : 
     453           0 :   initialize_accdb_fd( config );
     454           0 :   initialize_store_fds( config );
     455           0 :   initialize_snapshot_fds( config );
     456             : 
     457           0 :   fd_topo_join_workspaces( topo, FD_SHMEM_JOIN_MODE_READ_WRITE, FD_TOPO_CORE_DUMP_LEVEL_DISABLED );
     458           0 :   fd_topo_fill( topo );
     459             : 
     460           0 :   fd_topo_tile_t * snapct_tile = &topo->tiles[ fd_topo_find_tile( topo, "snapct", 0UL ) ];
     461           0 :   fd_topo_tile_t * snapld_tile = &topo->tiles[ fd_topo_find_tile( topo, "snapld", 0UL ) ];
     462           0 :   fd_topo_tile_t * snapin_tile = &topo->tiles[ fd_topo_find_tile( topo, "snapin", 0UL ) ];
     463           0 :   fd_topo_tile_t * snapwr_tile = &topo->tiles[ fd_topo_find_tile( topo, "snapwr", 0UL ) ];
     464           0 :   ulong snapdc_tile_cnt = config->firedancer.layout.snapdc_tile_count;
     465             : 
     466           0 :   double tick_per_ns = fd_tempo_tick_per_ns( NULL );
     467           0 :   double ns_per_tick = 1.0/tick_per_ns;
     468             : 
     469           0 :   long start = fd_log_wallclock();
     470           0 :   fd_topo_run_single_process( topo, 2, config->uid, config->gid, fdctl_tile_run );
     471             : 
     472           0 :   ulong volatile * const snapct_metrics = fd_metrics_tile( snapct_tile->metrics );
     473           0 :   ulong volatile * const snapld_metrics = fd_metrics_tile( snapld_tile->metrics );
     474           0 :   ulong volatile * const snapin_metrics = fd_metrics_tile( snapin_tile->metrics );
     475           0 :   ulong volatile * const snapwr_metrics = fd_metrics_tile( snapwr_tile->metrics );
     476           0 :   ulong volatile *       snapdc_metrics[ FD_TOPO_MAX_TILE_IN_LINKS ];
     477           0 :   for( ulong i=0UL; i<snapdc_tile_cnt; i++ ) {
     478           0 :     fd_topo_tile_t * snapdc_tile = &topo->tiles[ fd_topo_find_tile( topo, "snapdc", i ) ];
     479           0 :     snapdc_metrics[ i ] = fd_metrics_tile( snapdc_tile->metrics );
     480           0 :   }
     481             : 
     482           0 :   ulong total_off_old    = 0UL;
     483           0 :   ulong decomp_off_old   = 0UL;
     484           0 :   ulong snapld_wait_old  = 0UL;
     485           0 :   ulong snapdc_wait_old[ FD_TOPO_MAX_TILE_IN_LINKS ] = {0};
     486           0 :   ulong snapin_wait_old  = 0UL;
     487           0 :   ulong snapwr_wait_old  = 0UL;
     488           0 :   ulong acc_cnt_old      = 0UL;
     489             : 
     490           0 :   int color = fd_log_colorize() && isatty( STDOUT_FILENO );
     491           0 :   char const * c_bold = color ? SL_BOLD : "";
     492           0 :   char const * c_dim  = color ? SL_DIM  : "";
     493           0 :   char const * c_norm = color ? SL_RESET : "";
     494             : 
     495           0 :   sleep( 1 );
     496           0 :   if( watch ) {
     497           0 :     printf( "%scomp%s compressed %s·%s %sraw%s uncompressed %s·%s %sacc%s accounts %s·%s "
     498           0 :             "%sbusy%s %%%s of load·decompress·insert·write, yellow when the bottleneck%s\n",
     499           0 :             c_dim, c_norm, c_dim, c_norm,
     500           0 :             c_dim, c_norm, c_dim, c_norm,
     501           0 :             c_dim, c_norm, c_dim, c_norm,
     502           0 :             c_dim, c_norm, c_dim, c_norm );
     503           0 :     fflush( stdout );
     504           0 :   }
     505             : 
     506           0 :   long next = start+1000L*1000L*1000L;
     507           0 :   for(;;) {
     508           0 :     ulong snapct_status = FD_VOLATILE_CONST( snapct_metrics[ MIDX( GAUGE, TILE, STATUS ) ] );
     509           0 :     ulong snapld_status = FD_VOLATILE_CONST( snapld_metrics[ MIDX( GAUGE, TILE, STATUS ) ] );
     510           0 :     ulong snapin_status = FD_VOLATILE_CONST( snapin_metrics[ MIDX( GAUGE, TILE, STATUS ) ] );
     511           0 :     ulong snapwr_status = FD_VOLATILE_CONST( snapwr_metrics[ MIDX( GAUGE, TILE, STATUS ) ] );
     512           0 :     int snapdc_shutdown = 1;
     513           0 :     for( ulong i=0UL; i<snapdc_tile_cnt; i++ ) {
     514           0 :       snapdc_shutdown &= FD_VOLATILE_CONST( snapdc_metrics[ i ][ MIDX( GAUGE, TILE, STATUS ) ] )==2UL;
     515           0 :     }
     516             : 
     517           0 :     if( FD_UNLIKELY( snapct_status==2UL && snapld_status==2UL && snapdc_shutdown && snapin_status==2UL && snapwr_status==2UL ) ) break;
     518             : 
     519           0 :     long cur = fd_log_wallclock();
     520           0 :     if( FD_UNLIKELY( cur<next ) ) {
     521           0 :       long sleep_nanos = fd_long_min( 1000L*1000L, next-cur );
     522           0 :       FD_TEST( !fd_sys_util_nanosleep(  (uint)(sleep_nanos/(1000L*1000L*1000L)), (uint)(sleep_nanos%(1000L*1000L*1000L)) ) );
     523           0 :       continue;
     524           0 :     }
     525             : 
     526           0 :     ulong total_off    = snapct_metrics[ MIDX( GAUGE, SNAPCT, FULL_BYTES_READ ) ] +
     527           0 :                          snapct_metrics[ MIDX( GAUGE, SNAPCT, INCREMENTAL_BYTES_READ ) ];
     528           0 :     ulong decomp_off   = 0UL;
     529           0 :     ulong snapdc_wait[ FD_TOPO_MAX_TILE_IN_LINKS ];
     530           0 :     for( ulong i=0UL; i<snapdc_tile_cnt; i++ ) {
     531           0 :       decomp_off += snapdc_metrics[ i ][ MIDX( GAUGE, SNAPDC, FULL_DECOMPRESSED_BYTES_WRITTEN ) ] +
     532           0 :                     snapdc_metrics[ i ][ MIDX( GAUGE, SNAPDC, INCREMENTAL_DECOMPRESSED_BYTES_WRITTEN ) ];
     533           0 :       snapdc_wait[ i ] = snapdc_metrics[ i ][ MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_CAUGHT_UP_POSTFRAG ) ]
     534           0 :                        + snapdc_metrics[ i ][ MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_BACKPRESSURE_PREFRAG ) ];
     535           0 :     }
     536             :     /* Waiting on either neighbor counts as not busy */
     537           0 :     ulong snapld_wait  = snapld_metrics[ MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_CAUGHT_UP_POSTFRAG ) ]
     538           0 :                        + snapld_metrics[ MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_BACKPRESSURE_PREFRAG ) ];
     539           0 :     ulong snapin_wait  = snapin_metrics[ MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_CAUGHT_UP_POSTFRAG ) ]
     540           0 :                        + snapin_metrics[ MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_BACKPRESSURE_PREFRAG ) ];
     541           0 :     ulong snapwr_wait  = snapwr_metrics[ MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_CAUGHT_UP_POSTFRAG ) ]
     542           0 :                        + snapwr_metrics[ MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_BACKPRESSURE_PREFRAG ) ];
     543             : 
     544           0 :     char const * phase = phase_cstr( snapct_metrics[ MIDX( GAUGE, SNAPCT, STATE ) ] );
     545           0 :     ulong consumed, dc_in, dc_out, size_bytes;
     546           0 :     if( FD_UNLIKELY( !strcmp( phase, "incr" ) ) ) {
     547           0 :       consumed   = fd_ulong_min( snapin_metrics[ MIDX( GAUGE, SNAPIN, INCREMENTAL_BYTES_READ ) ],
     548           0 :                                  snapwr_metrics[ MIDX( GAUGE, SNAPWR, INCREMENTAL_BYTES_READ ) ] );
     549           0 :       dc_in      = 0UL;
     550           0 :       dc_out     = 0UL;
     551           0 :       for( ulong i=0UL; i<snapdc_tile_cnt; i++ ) {
     552           0 :         dc_in  += snapdc_metrics[ i ][ MIDX( GAUGE, SNAPDC, INCREMENTAL_COMPRESSED_BYTES_READ ) ];
     553           0 :         dc_out += snapdc_metrics[ i ][ MIDX( GAUGE, SNAPDC, INCREMENTAL_DECOMPRESSED_BYTES_WRITTEN ) ];
     554           0 :       }
     555           0 :       size_bytes = snapct_metrics[ MIDX( GAUGE, SNAPCT, INCREMENTAL_SIZE_BYTES ) ];
     556           0 :     } else {
     557           0 :       consumed   = fd_ulong_min( snapin_metrics[ MIDX( GAUGE, SNAPIN, FULL_BYTES_READ ) ],
     558           0 :                                  snapwr_metrics[ MIDX( GAUGE, SNAPWR, FULL_BYTES_READ ) ] );
     559           0 :       dc_in      = 0UL;
     560           0 :       dc_out     = 0UL;
     561           0 :       for( ulong i=0UL; i<snapdc_tile_cnt; i++ ) {
     562           0 :         dc_in  += snapdc_metrics[ i ][ MIDX( GAUGE, SNAPDC, FULL_COMPRESSED_BYTES_READ ) ];
     563           0 :         dc_out += snapdc_metrics[ i ][ MIDX( GAUGE, SNAPDC, FULL_DECOMPRESSED_BYTES_WRITTEN ) ];
     564           0 :       }
     565           0 :       size_bytes = snapct_metrics[ MIDX( GAUGE, SNAPCT, FULL_SIZE_BYTES ) ];
     566           0 :     }
     567           0 :     double done_comp = dc_out ? (double)dc_in*( (double)consumed/(double)dc_out ) : 0.0;
     568           0 :     double progress  = size_bytes ? clamp_pct( 100.0*done_comp/(double)size_bytes ) : 0.0;
     569             : 
     570           0 :     ulong acc_cnt      = snapin_metrics[ MIDX( GAUGE, SNAPIN, ACCOUNT_LOADED    ) ];
     571             : 
     572           0 :     if( watch ) {
     573           0 :       double snapdc_busy[ FD_TOPO_MAX_TILE_IN_LINKS ];
     574           0 :       double snapdc_busy_avg = 0.0;
     575           0 :       for( ulong i=0UL; i<snapdc_tile_cnt; i++ ) {
     576           0 :         snapdc_busy[ i ] = clamp_pct( 100.0-( ( (double)( snapdc_wait[ i ]-snapdc_wait_old[ i ] )*ns_per_tick )/1e7 ) );
     577           0 :         snapdc_busy_avg += snapdc_busy[ i ];
     578           0 :       }
     579           0 :       snapdc_busy_avg /= (double)snapdc_tile_cnt;
     580             : 
     581           0 :       double busy[ 4 ] = {
     582           0 :         clamp_pct( 100.0-( ( (double)( snapld_wait-snapld_wait_old )*ns_per_tick )/1e7 ) ),
     583           0 :         snapdc_busy_avg,
     584           0 :         clamp_pct( 100.0-( ( (double)( snapin_wait-snapin_wait_old )*ns_per_tick )/1e7 ) ),
     585           0 :         clamp_pct( 100.0-( ( (double)( snapwr_wait-snapwr_wait_old )*ns_per_tick )/1e7 ) ),
     586           0 :       };
     587             : 
     588           0 :       char bar[ 256 ];
     589           0 :       printf( " %s%4s%s %s %s%5.1f%%%s"
     590           0 :               "  %scomp%s %5.2f %sGB/s%s"
     591           0 :               "  %sraw%s %5.2f %sGB/s%s"
     592           0 :               "  %sacc%s %4.1f %sM/s%s",
     593           0 :               c_dim, phase, c_norm,
     594           0 :               fmt_bar( bar, sizeof(bar), color, progress, 20UL ),
     595           0 :               c_bold, progress, c_norm,
     596           0 :               c_dim, c_norm, (double)( total_off -total_off_old  )/1e9, c_dim, c_norm,
     597           0 :               c_dim, c_norm, (double)( decomp_off-decomp_off_old )/1e9, c_dim, c_norm,
     598           0 :               c_dim, c_norm, (double)( acc_cnt   -acc_cnt_old    )/1e6, c_dim, c_norm );
     599             : 
     600           0 :       static char const * tile_key[ 4 ] = { "ld", "dc(avg)", "in", "wr" };
     601           0 :       printf( "  %sbusy%s", c_dim, c_norm );
     602           0 :       for( ulong i=0UL; i<4UL; i++ ) {
     603           0 :         printf( " %s%s%s %s%3.0f%s%%%s",
     604           0 :                 c_dim, tile_key[ i ], c_norm,
     605           0 :                 sev_color( color, busy[ i ] ), busy[ i ], c_dim, c_norm );
     606           0 :       }
     607           0 :       for( ulong i=0UL; i<snapdc_tile_cnt; i++ ) {
     608           0 :         printf( " %sdc%lu%s %s%3.0f%s%%%s",
     609           0 :                 c_dim, i, c_norm,
     610           0 :                 sev_color( color, snapdc_busy[ i ] ), snapdc_busy[ i ], c_dim, c_norm );
     611           0 :       }
     612           0 :       printf( "\n" );
     613           0 :       fflush( stdout );
     614           0 :     }
     615           0 :     total_off_old    = total_off;
     616           0 :     decomp_off_old   = decomp_off;
     617           0 :     snapld_wait_old  = snapld_wait;
     618           0 :     for( ulong i=0UL; i<snapdc_tile_cnt; i++ ) {
     619           0 :       snapdc_wait_old[ i ] = snapdc_wait[ i ];
     620           0 :     }
     621           0 :     snapin_wait_old  = snapin_wait;
     622           0 :     snapwr_wait_old  = snapwr_wait;
     623           0 :     acc_cnt_old      = acc_cnt;
     624             : 
     625           0 :     next+=1000L*1000L*1000L;
     626           0 :   }
     627             : 
     628           0 :   if( args->snapshot_load.accounts_hist ) {
     629           0 :     accounts_hist_t hist[1];
     630           0 :     accounts_hist_reset( hist );
     631           0 :     FD_LOG_NOTICE(( "Accounts histogram: starting" ));
     632           0 :     accounts_hist( hist, config );
     633           0 :     FD_TEST( !accounts_hist_check( hist ) );
     634           0 :     accounts_hist_print( hist );
     635           0 :   }
     636           0 : }
     637             : 
     638             : static void
     639           0 : snapshot_load_args_help( fd_action_help_t * help ) {
     640           0 :   fd_action_help_arg( help, "--snapshot-dir",   "<path>",  "Load/save snapshots from this directory" );
     641           0 :   fd_action_help_arg( help, "--offline",        NULL,      "Do not attempt to download snapshots" );
     642           0 :   fd_action_help_arg( help, "--no-incremental", NULL,      "Disable incremental snapshot loading" );
     643           0 :   fd_action_help_arg( help, "--no-watch",       NULL,      "Do not print periodic progress updates" );
     644           0 :   fd_action_help_arg( help, "--db-sz",          "<bytes>", "Accounts cache size in bytes (e.g. 10e9 -> 10 GB)" );
     645           0 :   fd_action_help_arg( help, "--db-rec-max",     "<num>",   "Database max record/account count (e.g. 10e6 -> 10M accounts)" );
     646           0 :   fd_action_help_arg( help, "--fsck",           NULL,      "After loading, run database integrity checks" );
     647             :   fd_action_help_arg( help, "--accounts-hist",  NULL,      "After loading, analyze account size distribution" );
     648           0 : }
     649             : 
     650             : action_t fd_action_snapshot_load = {
     651             :   .name        = NAME,
     652             :   .topo        = snapshot_load_topo1,
     653             :   .perm        = dev_cmd_perm,
     654             :   .args        = snapshot_load_args,
     655             :   .fn          = snapshot_load_cmd_fn,
     656             :   .description = "Load a snapshot into a database and optionally inspect it",
     657             :   .detail      = "Boots a reduced topology that downloads (or reads from disk) a full and\n"
     658             :                  "optional incremental snapshot, loads the accounts into a database, and\n"
     659             :                  "can then run integrity checks or analyze the account size distribution.",
     660             :   .usage       = NAME " [OPTIONS]",
     661             :   .args_help   = snapshot_load_args_help,
     662             : };

Generated by: LCOV version 1.14