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 441 0.0 %
Date: 2026-08-13 04:56:22 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 :       FD_PACK_MAX_TXNCACHE_TXN_PER_SLOT,
      85           0 :       config->development.bench.larger_max_cost_per_block );
      86           0 :   FD_TEST( fd_pod_insertf_ulong( topo->props, txncache_obj->id, "txncache" ) );
      87             : 
      88           0 :   fd_topob_wksp( topo, "accdb" );
      89           0 :   fd_topo_obj_t * accdb_obj = setup_topo_accdb( topo, "accdb",
      90           0 :       config->firedancer.accounts.max_accounts,
      91           0 :       config->firedancer.runtime.max_live_slots,
      92           0 :       FD_RUNTIME_MAX_WRITABLE_ACCOUNTS_PER_SLOT,
      93           0 :       8192UL,
      94           0 :       1UL<<35UL,
      95           0 :       config->firedancer.accounts.cache_size_gib*(1UL<<30UL),
      96           0 :       config->tiles.bundle.enabled,
      97           0 :       2UL,
      98           0 :       0UL );
      99           0 :   FD_TEST( fd_pod_insertf_ulong( topo->props, accdb_obj->id, "accdb" ) );
     100             : 
     101           0 :   fd_topob_wksp( topo, "banks" );
     102           0 :   fd_topo_obj_t * banks_obj = setup_topo_banks( topo, "banks",
     103           0 :       config->firedancer.runtime.max_live_slots,
     104           0 :       config->firedancer.runtime.max_fork_width,
     105           0 :       config->development.bench.larger_max_cost_per_block );
     106           0 :   FD_TEST( fd_pod_insertf_ulong( topo->props, banks_obj->id, "banks" ) );
     107             : 
     108           0 : #define FOR(cnt) for( ulong i=0UL; i<cnt; i++ )
     109             : 
     110             :   /* metrics tile *****************************************************/
     111           0 :   fd_topob_wksp( topo, "metric_in" );
     112           0 :   fd_topob_wksp( topo, "metric" );
     113           0 :   fd_topob_tile( topo, "metric",  "metric", "metric_in", ULONG_MAX, 0, 0, 0 );
     114             : 
     115             :   /* read() tile */
     116           0 :   fd_topob_wksp( topo, "snapct" );
     117           0 :   fd_topo_tile_t * snapct_tile = fd_topob_tile( topo, "snapct", "snapct", "metric_in", ULONG_MAX, 0, 0, 0 );
     118           0 :   snapct_tile->allow_shutdown = 1;
     119             : 
     120             :   /* load tile */
     121           0 :   fd_topob_wksp( topo, "snapld" );
     122           0 :   fd_topo_tile_t * snapld_tile = fd_topob_tile( topo, "snapld", "snapld", "metric_in", ULONG_MAX, 0, 0, 0 );
     123           0 :   snapld_tile->allow_shutdown = 1;
     124             : 
     125             :   /* "snapdc": Zstandard decompress tile */
     126           0 :   fd_topob_wksp( topo, "snapdc" );
     127           0 :   fd_topo_tile_t * snapdc_tile = fd_topob_tile( topo, "snapdc", "snapdc", "metric_in", ULONG_MAX, 0, 0, 0 );
     128           0 :   snapdc_tile->allow_shutdown = 1;
     129             : 
     130             :   /* "snapin": Snapshot parser tile */
     131           0 :   fd_topob_wksp( topo, "snapin" );
     132           0 :   fd_topo_tile_t * snapin_tile = fd_topob_tile( topo, "snapin", "snapin", "metric_in", ULONG_MAX, 0, 0, 0 );
     133           0 :   snapin_tile->allow_shutdown = 1;
     134             : 
     135           0 :   fd_topob_wksp( topo, "snapwr" );
     136           0 :   fd_topo_tile_t * snapwr_tile = fd_topob_tile( topo, "snapwr", "snapwr", "metric_in", ULONG_MAX, 0, 0, 0 );
     137           0 :   snapwr_tile->allow_shutdown = 1;
     138             : 
     139           0 :   fd_topob_wksp( topo, "diag" );
     140           0 :   fd_topob_tile( topo, "diag", "diag", "metric_in", ULONG_MAX, 0, 0, 0 );
     141           0 :   fd_topo_tile_t * accdb_tile = fd_topob_tile( topo, "accdb", "accdb", "metric_in", ULONG_MAX, 0, 0, 0 );
     142             : 
     143           0 :   fd_topob_wksp( topo, "snapct_ld"    );
     144           0 :   fd_topob_wksp( topo, "snapld_dc"    );
     145           0 :   fd_topob_wksp( topo, "snapdc_in"    );
     146             : 
     147           0 :   fd_topob_wksp( topo, "snapin_manif" );
     148           0 :   fd_topob_wksp( topo, "snapct_repr"  );
     149             : 
     150           0 :   fd_topob_wksp( topo, "snapin_ct"    );
     151           0 :   fd_topob_wksp( topo, "snapwr_ct"    );
     152             : 
     153           0 :   fd_topob_link( topo, "snapct_ld",    "snapct_ld",    128UL,   sizeof(fd_ssctrl_init_t),       1UL );
     154           0 :   fd_topob_link( topo, "snapld_dc",    "snapld_dc",    16384UL, FD_SNAPSHOT_DATA_MTU,           1UL );
     155           0 :   fd_topob_link( topo, "snapdc_in",    "snapdc_in",    16384UL, FD_SNAPSHOT_DATA_MTU,           1UL );
     156           0 :   fd_topob_link( topo, "snapin_manif", "snapin_manif", 4UL,     sizeof(fd_snapshot_manifest_t), 1UL )->permit_no_consumers = 1;
     157           0 :   fd_topob_link( topo, "snapct_repr",  "snapct_repr",  128UL,   0UL,                            1UL )->permit_no_consumers = 1;
     158             : 
     159           0 :   fd_topob_link( topo, "snapin_ct", "snapin_ct",   128UL,  0UL,                             1UL );
     160           0 :   fd_topob_link( topo, "snapwr_ct", "snapwr_ct",   128UL,  0UL,                             1UL );
     161           0 :   fd_topob_tile_in( topo, "snapct",  0UL, "metric_in", "snapin_ct",  0UL, FD_TOPOB_RELIABLE,   FD_TOPOB_POLLED );
     162           0 :   fd_topob_tile_in( topo, "snapct",  0UL, "metric_in", "snapwr_ct",  0UL, FD_TOPOB_RELIABLE,   FD_TOPOB_POLLED );
     163             : 
     164           0 :   fd_topob_tile_in ( topo, "snapct",  0UL, "metric_in", "snapld_dc",    0UL, FD_TOPOB_RELIABLE,   FD_TOPOB_POLLED );
     165           0 :   fd_topob_tile_out( topo, "snapct",  0UL,              "snapct_ld",    0UL                                       );
     166           0 :   fd_topob_tile_out( topo, "snapct",  0UL,              "snapct_repr",  0UL                                       );
     167           0 :   fd_topob_tile_in ( topo, "snapld",  0UL, "metric_in", "snapct_ld",    0UL, FD_TOPOB_RELIABLE,   FD_TOPOB_POLLED );
     168           0 :   fd_topob_tile_out( topo, "snapld",  0UL,              "snapld_dc",    0UL                                       );
     169           0 :   fd_topob_tile_in ( topo, "snapdc",  0UL, "metric_in", "snapld_dc",    0UL, FD_TOPOB_RELIABLE,   FD_TOPOB_POLLED );
     170           0 :   fd_topob_tile_out( topo, "snapdc",  0UL,              "snapdc_in",    0UL                                       );
     171           0 :   fd_topob_tile_in ( topo, "snapin",  0UL, "metric_in", "snapdc_in",    0UL, FD_TOPOB_RELIABLE,   FD_TOPOB_POLLED );
     172           0 :   fd_topob_tile_out( topo, "snapin",  0UL,              "snapin_manif", 0UL                                       );
     173           0 :   fd_topob_tile_out( topo, "snapin",  0UL,              "snapin_ct",    0UL                                       );
     174           0 :   fd_topob_tile_in ( topo, "snapwr",  0UL, "metric_in", "snapdc_in",    0UL, FD_TOPOB_RELIABLE,   FD_TOPOB_POLLED );
     175           0 :   fd_topob_tile_out( topo, "snapwr",  0UL,              "snapwr_ct",    0UL                                       );
     176             : 
     177           0 :   fd_topob_tile_uses( topo, snapin_tile, txncache_obj,   FD_SHMEM_JOIN_MODE_READ_WRITE );
     178           0 :   fd_topob_tile_uses( topo, snapin_tile, accdb_obj,      FD_SHMEM_JOIN_MODE_READ_WRITE );
     179           0 :   fd_topob_tile_uses( topo, snapin_tile, banks_obj,      FD_SHMEM_JOIN_MODE_READ_WRITE );
     180           0 :   fd_topob_tile_uses( topo, accdb_tile,  accdb_obj,      FD_SHMEM_JOIN_MODE_READ_WRITE );
     181           0 :   snapin_tile->snapin.accdb_obj_id    = accdb_obj->id;
     182           0 :   snapin_tile->snapin.txncache_obj_id = txncache_obj->id;
     183           0 :   snapin_tile->snapin.banks_obj_id    = banks_obj->id;
     184           0 :   snapin_tile->snapin.max_live_slots  = config->firedancer.runtime.max_live_slots;
     185             : 
     186           0 :   for( ulong i=0UL; i<topo->tile_cnt; i++ ) {
     187           0 :     fd_topo_tile_t * tile = &topo->tiles[ i ];
     188           0 :     fd_topo_configure_tile( tile, config );
     189           0 :   }
     190             : 
     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             : 
     232           0 :   fd_cstr_ncpy( args->snapshot_load.snapshot_dir, snapshot_dir, sizeof(args->snapshot_load.snapshot_dir) );
     233           0 :   args->snapshot_load.accounts_hist  = accounts_hist;
     234           0 :   args->snapshot_load.offline        = offline;
     235           0 :   args->snapshot_load.no_incremental = no_incremental;
     236           0 :   args->snapshot_load.no_watch       = no_watch;
     237           0 : }
     238             : 
     239             : /* ACCOUNTS_HIST_N (32) is chosen to make the histogram lightweight.
     240             :    And because accounts can have a data size in the range [0, 10MiB],
     241             :    the width of the bins increments in powers of 2.  In the future, it
     242             :    should be possible to pass this as a configuration parameter. */
     243           0 : #define ACCOUNTS_HIST_N (32)
     244             : 
     245             : struct accounts_hist {
     246             :   ulong total_cnt;
     247             :   ulong total_acc;
     248             :   ulong bin_thi[ ACCOUNTS_HIST_N ];
     249             :   ulong bin_cnt[ ACCOUNTS_HIST_N ];
     250             :   ulong bin_acc[ ACCOUNTS_HIST_N ];
     251             :   ulong bin_min[ ACCOUNTS_HIST_N ];
     252             :   ulong bin_max[ ACCOUNTS_HIST_N ];
     253             :   ulong token_cnt;
     254             : };
     255             : typedef struct accounts_hist accounts_hist_t;
     256             : 
     257             : static inline void
     258           0 : accounts_hist_reset( accounts_hist_t * hist ) {
     259           0 :   hist->total_cnt = 0UL;
     260           0 :   hist->total_acc = 0UL;
     261           0 :   for( int i=0; i < ACCOUNTS_HIST_N; i++ ) {
     262           0 :     hist->bin_thi[ i ] = fd_ulong_if( i > 0, fd_pow2( ulong, i-1 ), 0UL );
     263           0 :     hist->bin_cnt[ i ] = 0UL;
     264           0 :     hist->bin_acc[ i ] = 0UL;
     265           0 :     hist->bin_min[ i ] = ULONG_MAX;
     266           0 :     hist->bin_max[ i ] = 0UL;
     267           0 :   }
     268           0 :   hist->token_cnt = 0UL;
     269           0 : }
     270             : 
     271             : static inline void FD_FN_UNUSED
     272             : accounts_hist_update( accounts_hist_t * hist,
     273           0 :                       ulong             account_sz ) {
     274           0 :   hist->total_cnt += 1UL;
     275           0 :   hist->total_acc += account_sz;
     276           0 :   int i=0;
     277             :   /* This allows for arbitrary thresholds - not optimized for pow2
     278             :      bins. */
     279           0 :   for( ; i < ACCOUNTS_HIST_N; i++ ) {
     280           0 :     if( FD_UNLIKELY( account_sz <= hist->bin_thi[ i ] )) {
     281           0 :       hist->bin_cnt[ i ] += 1;
     282           0 :       hist->bin_acc[ i ] += account_sz;
     283           0 :       hist->bin_min[ i ] = fd_ulong_min( hist->bin_min[ i ], account_sz );
     284           0 :       hist->bin_max[ i ] = fd_ulong_max( hist->bin_max[ i ], account_sz );
     285           0 :       break;
     286           0 :     }
     287           0 :   }
     288           0 :   FD_TEST( i < ACCOUNTS_HIST_N );
     289           0 : }
     290             : 
     291             : static inline int
     292           0 : accounts_hist_check( accounts_hist_t const * hist ) {
     293           0 :   ulong cnt = 0UL;
     294           0 :   ulong acc = 0UL;
     295           0 :   for( int i=0; i < ACCOUNTS_HIST_N; i++ ) {
     296           0 :     cnt += hist->bin_cnt[ i ];
     297           0 :     acc += hist->bin_acc[ i ];
     298           0 :   }
     299           0 :   if( cnt != hist->total_cnt ) return -1;
     300           0 :   if( acc != hist->total_acc ) return -2;
     301           0 :   return 0;
     302           0 : }
     303             : 
     304             : static void
     305           0 : accounts_hist_print( accounts_hist_t const * hist ) {
     306           0 :   double hist_total_cnt_M   = (double)hist->total_cnt / (double)1.0e6;
     307           0 :   double hist_total_cnt_GiB = (double)hist->total_acc / (double)1073741824;
     308           0 :   printf( "\n" );
     309           0 :   printf( "hist_total_cnt %16lu ( %6.1f M   )\n", hist->total_cnt, hist_total_cnt_M   );
     310           0 :   printf( "hist_total_acc %16lu ( %6.1f GiB )\n", hist->total_acc, hist_total_cnt_GiB );
     311           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" );
     312           0 :   ulong sum_cnt = 0UL;
     313           0 :   ulong sum_acc = 0UL;
     314           0 :   for( int i=0; i < ACCOUNTS_HIST_N; i++ ) {
     315             :     /* bin thresholds */
     316           0 :     ulong hist_bin_tlo      = hist->bin_thi[ fd_int_if( i>0, i-1, i ) ];
     317           0 :     ulong hist_bin_thi      = hist->bin_thi[ i ];
     318             :     /* bin cnt */
     319           0 :     ulong hist_bin_cnt      = hist->bin_cnt[ i ];
     320           0 :     sum_cnt                += hist->bin_cnt[ i ];
     321           0 :     double sum_cnt_p        = (double)(sum_cnt * 100) / (double)hist->total_cnt;
     322           0 :     double hist_bin_cnt_K   = (double)(hist_bin_cnt) / (double)1.0e3;
     323             :     /* bin acc */
     324           0 :     ulong hist_bin_acc      = hist->bin_acc[ i ];
     325           0 :     sum_acc                += hist->bin_acc[ i ];
     326           0 :     double sum_acc_p        = (double)(sum_acc * 100) / (double)hist->total_acc;
     327           0 :     double hist_bin_acc_MiB = (double)(hist_bin_acc) / (double)1048576.0f;
     328             :     /* bin min, max, avg */
     329           0 :     ulong hist_bin_min      = fd_ulong_if( hist->bin_cnt[ i ] > 0, hist->bin_min[ i ], 0UL );
     330           0 :     ulong hist_bin_max      = hist->bin_max[ i ];
     331           0 :     ulong hist_bin_avg      = hist->bin_cnt[ i ] > 0 ? hist->bin_acc[ i ] / hist->bin_cnt[ i ] : 0UL;
     332             :     /* log */
     333           0 :     char buf[256];
     334           0 :     FD_TEST( fd_cstr_printf_check( buf, sizeof(buf), NULL,
     335           0 :                                   "%12lu %s sz <= %12lu | %8.1f K (%6.1f %%) | %8.1f MiB (%6.1f %%) | %12lu | %12lu | %12lu |\n",
     336           0 :                                   hist_bin_tlo, i==0? "<=" : "< ", hist_bin_thi,
     337           0 :                                   hist_bin_cnt_K, sum_cnt_p,
     338           0 :                                   hist_bin_acc_MiB, sum_acc_p,
     339           0 :                                   hist_bin_min, hist_bin_max, hist_bin_avg ) );
     340           0 :     printf( "%s", buf );
     341           0 :   }
     342           0 :   printf( "\n" );
     343           0 : }
     344             : 
     345             : static void
     346             : accounts_hist( accounts_hist_t * hist,
     347           0 :                config_t *        config ) {
     348           0 :   fd_topo_t * topo = &config->topo;
     349           0 :   ulong accdb_obj_id = fd_pod_query_ulong( topo->props, "accdb", ULONG_MAX );
     350           0 :   FD_TEST( accdb_obj_id!=ULONG_MAX );
     351           0 :   void * _accdb_shmem = fd_topo_obj_laddr( topo, accdb_obj_id );
     352           0 :   fd_accdb_shmem_t * shmem = fd_accdb_shmem_join( _accdb_shmem );
     353           0 :   FD_TEST( shmem );
     354             : 
     355             :   /* Recompute the shmem layout to locate acc_map and acc_pool element
     356             :      storage without taking a writer joiner slot.  This mirrors the
     357             :      layout in fd_accdb_shmem_new and fd_accdb_join_readonly. */
     358             : 
     359           0 :   ulong max_live_slots              = shmem->max_live_slots;
     360           0 :   ulong max_accounts                = shmem->max_accounts;
     361           0 :   ulong max_account_writes_per_slot = shmem->max_account_writes_per_slot;
     362           0 :   ulong partition_cnt               = shmem->partition_cnt;
     363           0 :   ulong chain_cnt                   = shmem->chain_cnt;
     364           0 :   ulong txn_max                     = max_live_slots * max_account_writes_per_slot;
     365             : 
     366           0 :   FD_SCRATCH_ALLOC_INIT( l, shmem );
     367           0 :                                   FD_SCRATCH_ALLOC_APPEND( l, FD_ACCDB_SHMEM_ALIGN,           sizeof(fd_accdb_shmem_t)                                );
     368           0 :                                   FD_SCRATCH_ALLOC_APPEND( l, fork_pool_align(),              fork_pool_footprint()                                   );
     369           0 :                                   FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_accdb_fork_shmem_t), max_live_slots*sizeof(fd_accdb_fork_shmem_t)            );
     370           0 :                                   FD_SCRATCH_ALLOC_APPEND( l, descends_set_align(),           max_live_slots*descends_set_footprint( max_live_slots ) );
     371           0 :   uint *               acc_map  = FD_SCRATCH_ALLOC_APPEND( l, alignof(uint),                  chain_cnt*sizeof(uint)                                  );
     372           0 :                                   FD_SCRATCH_ALLOC_APPEND( l, acc_pool_align(),               acc_pool_footprint()                                    );
     373           0 :   fd_accdb_accmeta_t * acc_pool = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_accdb_accmeta_t),    max_accounts*sizeof(fd_accdb_accmeta_t)                     );
     374           0 :   (void)txn_max; (void)partition_cnt;
     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_snapshot_fds( config );
     455             : 
     456           0 :   fd_topo_join_workspaces( topo, FD_SHMEM_JOIN_MODE_READ_WRITE, FD_TOPO_CORE_DUMP_LEVEL_DISABLED );
     457           0 :   fd_topo_fill( topo );
     458             : 
     459           0 :   fd_topo_tile_t * snapct_tile = &topo->tiles[ fd_topo_find_tile( topo, "snapct", 0UL ) ];
     460           0 :   fd_topo_tile_t * snapld_tile = &topo->tiles[ fd_topo_find_tile( topo, "snapld", 0UL ) ];
     461           0 :   fd_topo_tile_t * snapdc_tile = &topo->tiles[ fd_topo_find_tile( topo, "snapdc", 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             : 
     465           0 :   double tick_per_ns = fd_tempo_tick_per_ns( NULL );
     466           0 :   double ns_per_tick = 1.0/tick_per_ns;
     467             : 
     468           0 :   long start = fd_log_wallclock();
     469           0 :   fd_topo_run_single_process( topo, 2, config->uid, config->gid, fdctl_tile_run );
     470             : 
     471           0 :   ulong volatile * const snapct_metrics = fd_metrics_tile( snapct_tile->metrics );
     472           0 :   ulong volatile * const snapld_metrics = fd_metrics_tile( snapld_tile->metrics );
     473           0 :   ulong volatile * const snapdc_metrics = fd_metrics_tile( snapdc_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             : 
     477           0 :   ulong total_off_old    = 0UL;
     478           0 :   ulong decomp_off_old   = 0UL;
     479           0 :   ulong snapld_wait_old  = 0UL;
     480           0 :   ulong snapdc_wait_old  = 0UL;
     481           0 :   ulong snapin_wait_old  = 0UL;
     482           0 :   ulong snapwr_wait_old  = 0UL;
     483           0 :   ulong acc_cnt_old      = 0UL;
     484             : 
     485           0 :   int color = fd_log_colorize() && isatty( STDOUT_FILENO );
     486           0 :   char const * c_bold = color ? SL_BOLD : "";
     487           0 :   char const * c_dim  = color ? SL_DIM  : "";
     488           0 :   char const * c_norm = color ? SL_RESET : "";
     489             : 
     490           0 :   sleep( 1 );
     491           0 :   if( watch ) {
     492           0 :     printf( "%scomp%s compressed %s·%s %sraw%s uncompressed %s·%s %sacc%s accounts %s·%s "
     493           0 :             "%sbusy%s %%%s of load·decompress·insert·write, yellow when the bottleneck%s\n",
     494           0 :             c_dim, c_norm, c_dim, c_norm,
     495           0 :             c_dim, c_norm, c_dim, c_norm,
     496           0 :             c_dim, c_norm, c_dim, c_norm,
     497           0 :             c_dim, c_norm, c_dim, c_norm );
     498           0 :     fflush( stdout );
     499           0 :   }
     500             : 
     501           0 :   long next = start+1000L*1000L*1000L;
     502           0 :   for(;;) {
     503           0 :     ulong snapct_status = FD_VOLATILE_CONST( snapct_metrics[ MIDX( GAUGE, TILE, STATUS ) ] );
     504           0 :     ulong snapld_status = FD_VOLATILE_CONST( snapld_metrics[ MIDX( GAUGE, TILE, STATUS ) ] );
     505           0 :     ulong snapdc_status = FD_VOLATILE_CONST( snapdc_metrics[ MIDX( GAUGE, TILE, STATUS ) ] );
     506           0 :     ulong snapin_status = FD_VOLATILE_CONST( snapin_metrics[ MIDX( GAUGE, TILE, STATUS ) ] );
     507           0 :     ulong snapwr_status = FD_VOLATILE_CONST( snapwr_metrics[ MIDX( GAUGE, TILE, STATUS ) ] );
     508             : 
     509           0 :     if( FD_UNLIKELY( snapct_status==2UL && snapld_status==2UL && snapdc_status==2UL && snapin_status==2UL && snapwr_status==2UL ) ) break;
     510             : 
     511           0 :     long cur = fd_log_wallclock();
     512           0 :     if( FD_UNLIKELY( cur<next ) ) {
     513           0 :       long sleep_nanos = fd_long_min( 1000L*1000L, next-cur );
     514           0 :       FD_TEST( !fd_sys_util_nanosleep(  (uint)(sleep_nanos/(1000L*1000L*1000L)), (uint)(sleep_nanos%(1000L*1000L*1000L)) ) );
     515           0 :       continue;
     516           0 :     }
     517             : 
     518           0 :     ulong total_off    = snapct_metrics[ MIDX( GAUGE, SNAPCT, FULL_BYTES_READ ) ] +
     519           0 :                          snapct_metrics[ MIDX( GAUGE, SNAPCT, INCREMENTAL_BYTES_READ ) ];
     520           0 :     ulong decomp_off   = snapdc_metrics[ MIDX( GAUGE, SNAPDC, FULL_DECOMPRESSED_BYTES_WRITTEN ) ] +
     521           0 :                          snapdc_metrics[ MIDX( GAUGE, SNAPDC, INCREMENTAL_DECOMPRESSED_BYTES_WRITTEN ) ];
     522             :     /* Waiting on either neighbor counts as not busy */
     523           0 :     ulong snapld_wait  = snapld_metrics[ MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_CAUGHT_UP_POSTFRAG ) ]
     524           0 :                        + snapld_metrics[ MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_BACKPRESSURE_PREFRAG ) ];
     525           0 :     ulong snapdc_wait  = snapdc_metrics[ MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_CAUGHT_UP_POSTFRAG ) ]
     526           0 :                        + snapdc_metrics[ MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_BACKPRESSURE_PREFRAG ) ];
     527           0 :     ulong snapin_wait  = snapin_metrics[ MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_CAUGHT_UP_POSTFRAG ) ]
     528           0 :                        + snapin_metrics[ MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_BACKPRESSURE_PREFRAG ) ];
     529           0 :     ulong snapwr_wait  = snapwr_metrics[ MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_CAUGHT_UP_POSTFRAG ) ]
     530           0 :                        + snapwr_metrics[ MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_BACKPRESSURE_PREFRAG ) ];
     531             : 
     532           0 :     char const * phase = phase_cstr( snapct_metrics[ MIDX( GAUGE, SNAPCT, STATE ) ] );
     533           0 :     ulong consumed, dc_in, dc_out, size_bytes;
     534           0 :     if( FD_UNLIKELY( !strcmp( phase, "incr" ) ) ) {
     535           0 :       consumed   = fd_ulong_min( snapin_metrics[ MIDX( GAUGE, SNAPIN, INCREMENTAL_BYTES_READ ) ],
     536           0 :                                  snapwr_metrics[ MIDX( GAUGE, SNAPWR, INCREMENTAL_BYTES_READ ) ] );
     537           0 :       dc_in      = snapdc_metrics[ MIDX( GAUGE, SNAPDC, INCREMENTAL_COMPRESSED_BYTES_READ ) ];
     538           0 :       dc_out     = snapdc_metrics[ MIDX( GAUGE, SNAPDC, INCREMENTAL_DECOMPRESSED_BYTES_WRITTEN ) ];
     539           0 :       size_bytes = snapct_metrics[ MIDX( GAUGE, SNAPCT, INCREMENTAL_SIZE_BYTES ) ];
     540           0 :     } else {
     541           0 :       consumed   = fd_ulong_min( snapin_metrics[ MIDX( GAUGE, SNAPIN, FULL_BYTES_READ ) ],
     542           0 :                                  snapwr_metrics[ MIDX( GAUGE, SNAPWR, FULL_BYTES_READ ) ] );
     543           0 :       dc_in      = snapdc_metrics[ MIDX( GAUGE, SNAPDC, FULL_COMPRESSED_BYTES_READ ) ];
     544           0 :       dc_out     = snapdc_metrics[ MIDX( GAUGE, SNAPDC, FULL_DECOMPRESSED_BYTES_WRITTEN ) ];
     545           0 :       size_bytes = snapct_metrics[ MIDX( GAUGE, SNAPCT, FULL_SIZE_BYTES ) ];
     546           0 :     }
     547           0 :     double done_comp = dc_out ? (double)dc_in*( (double)consumed/(double)dc_out ) : 0.0;
     548           0 :     double progress  = size_bytes ? clamp_pct( 100.0*done_comp/(double)size_bytes ) : 0.0;
     549             : 
     550           0 :     ulong acc_cnt      = snapin_metrics[ MIDX( GAUGE, SNAPIN, ACCOUNT_LOADED    ) ];
     551             : 
     552           0 :     if( watch ) {
     553           0 :       double busy[ 4 ] = {
     554           0 :         clamp_pct( 100.0-( ( (double)( snapld_wait-snapld_wait_old )*ns_per_tick )/1e7 ) ),
     555           0 :         clamp_pct( 100.0-( ( (double)( snapdc_wait-snapdc_wait_old )*ns_per_tick )/1e7 ) ),
     556           0 :         clamp_pct( 100.0-( ( (double)( snapin_wait-snapin_wait_old )*ns_per_tick )/1e7 ) ),
     557           0 :         clamp_pct( 100.0-( ( (double)( snapwr_wait-snapwr_wait_old )*ns_per_tick )/1e7 ) ),
     558           0 :       };
     559             : 
     560           0 :       char bar[ 256 ];
     561           0 :       printf( " %s%4s%s %s %s%5.1f%%%s"
     562           0 :               "  %scomp%s %5.2f %sGB/s%s"
     563           0 :               "  %sraw%s %5.2f %sGB/s%s"
     564           0 :               "  %sacc%s %4.1f %sM/s%s",
     565           0 :               c_dim, phase, c_norm,
     566           0 :               fmt_bar( bar, sizeof(bar), color, progress, 20UL ),
     567           0 :               c_bold, progress, c_norm,
     568           0 :               c_dim, c_norm, (double)( total_off -total_off_old  )/1e9, c_dim, c_norm,
     569           0 :               c_dim, c_norm, (double)( decomp_off-decomp_off_old )/1e9, c_dim, c_norm,
     570           0 :               c_dim, c_norm, (double)( acc_cnt   -acc_cnt_old    )/1e6, c_dim, c_norm );
     571             : 
     572           0 :       static char const * tile_key[ 4 ] = { "ld", "dc", "in", "wr" };
     573           0 :       printf( "  %sbusy%s", c_dim, c_norm );
     574           0 :       for( ulong i=0UL; i<4UL; i++ )
     575           0 :         printf( " %s%s%s %s%3.0f%s%%%s",
     576           0 :                 c_dim, tile_key[ i ], c_norm,
     577           0 :                 sev_color( color, busy[ i ] ), busy[ i ], c_dim, c_norm );
     578           0 :       printf( "\n" );
     579           0 :       fflush( stdout );
     580           0 :     }
     581           0 :     total_off_old    = total_off;
     582           0 :     decomp_off_old   = decomp_off;
     583           0 :     snapld_wait_old  = snapld_wait;
     584           0 :     snapdc_wait_old  = snapdc_wait;
     585           0 :     snapin_wait_old  = snapin_wait;
     586           0 :     snapwr_wait_old  = snapwr_wait;
     587           0 :     acc_cnt_old      = acc_cnt;
     588             : 
     589           0 :     next+=1000L*1000L*1000L;
     590           0 :   }
     591             : 
     592           0 :   if( args->snapshot_load.accounts_hist ) {
     593           0 :     accounts_hist_t hist[1];
     594           0 :     accounts_hist_reset( hist );
     595           0 :     FD_LOG_NOTICE(( "Accounts histogram: starting" ));
     596           0 :     accounts_hist( hist, config );
     597           0 :     FD_TEST( !accounts_hist_check( hist ) );
     598           0 :     accounts_hist_print( hist );
     599           0 :   }
     600           0 : }
     601             : 
     602             : static void
     603           0 : snapshot_load_args_help( fd_action_help_t * help ) {
     604           0 :   fd_action_help_arg( help, "--snapshot-dir",   "<path>",  "Load/save snapshots from this directory" );
     605           0 :   fd_action_help_arg( help, "--offline",        NULL,      "Do not attempt to download snapshots" );
     606           0 :   fd_action_help_arg( help, "--no-incremental", NULL,      "Disable incremental snapshot loading" );
     607           0 :   fd_action_help_arg( help, "--no-watch",       NULL,      "Do not print periodic progress updates" );
     608           0 :   fd_action_help_arg( help, "--db-sz",          "<bytes>", "Database size in bytes (e.g. 10e9 -> 10 GB)" );
     609           0 :   fd_action_help_arg( help, "--db-rec-max",     "<num>",   "Database max record/account count (e.g. 10e6 -> 10M accounts)" );
     610           0 :   fd_action_help_arg( help, "--fsck",           NULL,      "After loading, run database integrity checks" );
     611             :   fd_action_help_arg( help, "--accounts-hist",  NULL,      "After loading, analyze account size distribution" );
     612           0 : }
     613             : 
     614             : action_t fd_action_snapshot_load = {
     615             :   .name        = NAME,
     616             :   .topo        = snapshot_load_topo1,
     617             :   .perm        = dev_cmd_perm,
     618             :   .args        = snapshot_load_args,
     619             :   .fn          = snapshot_load_cmd_fn,
     620             :   .description = "Load a snapshot into a database and optionally inspect it",
     621             :   .detail      = "Boots a reduced topology that downloads (or reads from disk) a full and\n"
     622             :                  "optional incremental snapshot, loads the accounts into a database, and\n"
     623             :                  "can then run integrity checks or analyze the account size distribution.",
     624             :   .usage       = NAME " [OPTIONS]",
     625             :   .args_help   = snapshot_load_args_help,
     626             : };

Generated by: LCOV version 1.14