LCOV - code coverage report
Current view: top level - vinyl - fd_vinyl_ctl.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 451 0.0 %
Date: 2026-03-19 18:19:27 Functions: 0 2 0.0 %

          Line data    Source code
       1             : /* For O_DIRECT and O_NOATIME */
       2             : #define _GNU_SOURCE
       3             : 
       4             : #include "fd_vinyl.h"
       5             : #include "../util/pod/fd_pod.h"
       6             : 
       7             : #include <stdio.h>
       8             : #include <errno.h>
       9             : #include <unistd.h>
      10             : #include <fcntl.h>
      11             : #include <sys/stat.h>
      12             : 
      13             : FD_IMPORT_CSTR( fd_vinyl_ctl_help, "src/vinyl/fd_vinyl_ctl_help" );
      14             : 
      15             : static int
      16             : fd_vinyl_main( int     argc,
      17           0 :                char ** argv ) {
      18             : 
      19           0 :   ulong seed_default = fd_cstr_hash_append( (ulong)fd_log_wallclock(), fd_log_host() );
      20             : 
      21           0 :   char const * _pod      = fd_env_strip_cmdline_cstr ( &argc, &argv, "--pod",      NULL, NULL            );
      22           0 :   char const * _cfg      = fd_env_strip_cmdline_cstr ( &argc, &argv, "--cfg",      NULL, NULL            );
      23           0 :   ulong        seed      = fd_env_strip_cmdline_ulong( &argc, &argv, "--seed",     NULL, seed_default    );
      24           0 :   char const * type      = fd_env_strip_cmdline_cstr ( &argc, &argv, "--type",     NULL, "mm"            );
      25           0 :   char const * path      = fd_env_strip_cmdline_cstr ( &argc, &argv, "--path",     NULL, NULL            );
      26           0 :   int          dsync     = fd_env_strip_cmdline_int  ( &argc, &argv, "--dsync",    NULL, 0               );
      27           0 :   int          direct    = fd_env_strip_cmdline_int  ( &argc, &argv, "--direct",   NULL, 0               );
      28           0 :   int          noatime   = fd_env_strip_cmdline_int  ( &argc, &argv, "--noatime",  NULL, 0               );
      29           0 :   char const * _page_sz  = fd_env_strip_cmdline_cstr ( &argc, &argv, "--page-sz",  NULL, "gigantic"      );
      30           0 :   ulong        page_cnt  = fd_env_strip_cmdline_ulong( &argc, &argv, "--page-cnt", NULL, 1UL             );
      31           0 :   ulong        near_cpu  = fd_env_strip_cmdline_ulong( &argc, &argv, "--near-cpu", NULL, fd_log_cpu_id() );
      32           0 :   int          reset     = fd_env_strip_cmdline_int  ( &argc, &argv, "--reset",    NULL, 0               );
      33           0 :   char const * info      = fd_env_strip_cmdline_cstr ( &argc, &argv, "--info",     NULL, NULL            );
      34           0 :   ulong        io_seed   = fd_env_strip_cmdline_ulong( &argc, &argv, "--io-seed",  NULL, 0UL             );
      35             : 
      36           0 :   int   open_flags = O_RDWR | (dsync ? O_DSYNC : 0 ) | (direct ? O_DIRECT : 0) | (noatime ? O_NOATIME : 0);
      37           0 :   ulong page_sz    = fd_cstr_to_shmem_page_sz( _page_sz );
      38           0 :   ulong info_sz    = info ? (strlen( info )+1UL) : 0UL;
      39             : 
      40           0 :   if( FD_UNLIKELY( !_pod    ) ) FD_LOG_ERR(( "--pod not specified" ));
      41           0 :   if( FD_UNLIKELY( !page_sz ) ) FD_LOG_ERR(( "bad --page-sz" ));
      42             : 
      43           0 :   FD_LOG_NOTICE(( "Attaching to --pod %s", _pod ));
      44             : 
      45           0 :   uchar const * pod = fd_wksp_pod_attach( _pod ); /* logs details, guaranteed to succeed */
      46           0 :   uchar const * cfg;
      47           0 :   if( FD_UNLIKELY( !_cfg ) ) {
      48           0 :     FD_LOG_NOTICE(( "--cfg not specified (using pod root for config)" ));
      49           0 :     cfg = pod;
      50           0 :   } else {
      51           0 :     FD_LOG_NOTICE(( "Finding config --cfg %s", _cfg ));
      52           0 :     cfg = fd_pod_query_subpod( pod, _cfg );
      53           0 :     if( FD_UNLIKELY( !cfg ) ) FD_LOG_ERR(( "config not found" ));
      54           0 :   }
      55             : 
      56           0 :   FD_LOG_NOTICE(( "Extracting pod configuration" ));
      57             : 
      58             :   /* See below for explanation of defaults */
      59           0 :   ulong spad_max    = fd_pod_query_ulong( cfg, "spad_max",    fd_vinyl_io_spad_est()         );
      60           0 :   ulong async_min   = fd_pod_query_ulong( cfg, "async_min",   2UL                            );
      61           0 :   ulong async_max   = fd_pod_query_ulong( cfg, "async_max",   2UL*async_min                  );
      62           0 :   ulong part_thresh = fd_pod_query_ulong( cfg, "part_thresh", 1UL<<30                        );
      63           0 :   ulong gc_thresh   = fd_pod_query_ulong( cfg, "gc_thresh",   8UL<<30                        );
      64           0 :   int   gc_eager    = fd_pod_query_int  ( cfg, "gc_eager",    2                              );
      65           0 :   int   style       = fd_pod_query_int  ( cfg, "style",       FD_VINYL_BSTREAM_CTL_STYLE_LZ4 );
      66           0 :   int   level       = fd_pod_query_int  ( cfg, "level",       1                              );
      67             : 
      68           0 :   FD_LOG_NOTICE(( "Processing command line configuration overrides" ));
      69             : 
      70           0 :   char const * _style = fd_env_strip_cmdline_cstr( &argc, &argv, "--style", NULL, NULL );
      71           0 :   if( _style ) style = fd_cstr_to_vinyl_bstream_ctl_style( _style );
      72             : 
      73           0 :   spad_max    = fd_env_strip_cmdline_ulong( &argc, &argv, "--spad-max",    NULL, spad_max    );
      74           0 :   async_min   = fd_env_strip_cmdline_ulong( &argc, &argv, "--async-min",   NULL, async_min   );
      75           0 :   async_max   = fd_env_strip_cmdline_ulong( &argc, &argv, "--async-max",   NULL, async_max   );
      76           0 :   part_thresh = fd_env_strip_cmdline_ulong( &argc, &argv, "--part-thresh", NULL, part_thresh );
      77           0 :   gc_thresh   = fd_env_strip_cmdline_ulong( &argc, &argv, "--gc-thresh",   NULL, gc_thresh   );
      78           0 :   gc_eager    = fd_env_strip_cmdline_int  ( &argc, &argv, "--gc-eager",    NULL, gc_eager    );
      79           0 :   level       = fd_env_strip_cmdline_int  ( &argc, &argv, "--level",       NULL, level       );
      80             : 
      81           0 :   FD_LOG_NOTICE(( "Mapping vinyl memory regions" ));
      82             : 
      83           0 :   void * _vinyl = fd_wksp_pod_map( cfg, "vinyl" ); ulong vinyl_footprint = fd_pod_query_ulong( cfg, "vinyl_footprint", 0UL );
      84           0 :   void * _cnc   = fd_wksp_pod_map( cfg, "cnc"   ); ulong cnc_footprint   = fd_pod_query_ulong( cfg, "cnc_footprint",   0UL );
      85           0 :   void * _meta  = fd_wksp_pod_map( cfg, "meta"  ); ulong meta_footprint  = fd_pod_query_ulong( cfg, "meta_footprint",  0UL );
      86           0 :   void * _line  = fd_wksp_pod_map( cfg, "line"  ); ulong line_footprint  = fd_pod_query_ulong( cfg, "line_footprint",  0UL );
      87           0 :   void * _io    = fd_wksp_pod_map( cfg, "io"    ); ulong io_footprint    = fd_pod_query_ulong( cfg, "io_footprint",    0UL );
      88           0 :   void * _ele   = fd_wksp_pod_map( cfg, "ele"   ); ulong ele_footprint   = fd_pod_query_ulong( cfg, "ele_footprint",   0UL );
      89           0 :   void * _obj   = fd_wksp_pod_map( cfg, "obj"   ); ulong obj_footprint   = fd_pod_query_ulong( cfg, "obj_footprint",   0UL );
      90             : 
      91           0 : # define TEST( c, msg ) do {                                              \
      92           0 :     if( FD_UNLIKELY( !(c) ) ) FD_LOG_ERR(( "FAIL: %s (%s)", #c, (msg) )); \
      93           0 :   } while(0)
      94             : 
      95           0 :   fd_wksp_t * wksp = fd_wksp_containing( _obj );
      96           0 :   TEST( wksp, "fd_wksp_containing failed" );
      97             : 
      98           0 :   TEST( fd_ulong_is_aligned( (ulong)_vinyl, fd_vinyl_io_mm_align() ), "bad alloc" );
      99           0 :   TEST( vinyl_footprint >= fd_vinyl_footprint(),                      "bad alloc" );
     100             : 
     101           0 :   int is_mmio = !strcmp( type, "mm" );
     102             : 
     103           0 :   FD_LOG_NOTICE(( "io config"
     104           0 :                   "\n\t--type      \"%s\""
     105           0 :                   "\n\t--spad-max  %lu bytes"
     106           0 :                   "\n\t--path      \"%s\""
     107           0 :                   "\n\t--dsync     %i"
     108           0 :                   "\n\t--direct    %i"
     109           0 :                   "\n\t--noatime   %i"
     110           0 :                   "\n\t--page-sz   \"%s\"%s"
     111           0 :                   "\n\t--page-cnt  %lu pages%s"
     112           0 :                   "\n\t--near-cpu  %lu%s"
     113           0 :                   "\n\t--reset     %i"
     114           0 :                   "\n\t--info      \"%s\" (info_sz %lu bytes%s)"
     115           0 :                   "\n\t--io-seed   0x%016lx%s",
     116           0 :                   type, spad_max, path ? path : "(null)", dsync, direct, noatime,
     117           0 :                   _page_sz, is_mmio && !path ? "" : " (ignored)",
     118           0 :                   page_cnt, is_mmio && !path ? "" : " (ignored)",
     119           0 :                   near_cpu, is_mmio && !path ? "" : " (ignored)",
     120           0 :                   reset, info ? info : "(null)", info_sz, reset ? "" : ", ignored", io_seed, reset ? "" : " (ignored)" ));
     121             : 
     122           0 :   FD_LOG_NOTICE(( "Joining bstream" ));
     123             : 
     124           0 :   int    bstream_type;
     125           0 :   int    fd = -1;
     126           0 :   void * mmio;
     127           0 :   ulong  mmio_sz;
     128             : 
     129           0 :   fd_vinyl_io_t * io;
     130             : 
     131           0 :   if( FD_LIKELY( is_mmio ) ) {
     132             : 
     133           0 :     if( FD_LIKELY( path ) ) {
     134             : 
     135           0 :       fd = open( path, open_flags, (mode_t)0 );
     136             : 
     137           0 :       if( FD_LIKELY( fd!=-1 ) ) { /* --path seems to be file (e.g. testing or basic I/O with weak persistence) */
     138             : 
     139           0 :         TEST( !direct, "--direct 1 not supported with --type mm and file --path" );
     140             :         /* FIXME: is dsync valid for mmio? (unclear) noatime? (probably) */
     141             : 
     142           0 :         FD_LOG_NOTICE(( "Using file at --path as a memory mapped bstream" ));
     143             : 
     144           0 :         bstream_type = 0;
     145             : 
     146           0 :         int err = fd_io_mmio_init( fd, FD_IO_MMIO_MODE_READ_WRITE, &mmio, &mmio_sz );
     147           0 :         if( FD_UNLIKELY( err ) ) FD_LOG_ERR(( "fd_io_mmio_init failed (%i-%s)", err, fd_io_strerror( err ) ));
     148             : 
     149           0 :       } else { /* --path doesn't seem to be a file, use shmem (e.g. testing or ultra HPC with weak persistence) */
     150             : 
     151           0 :         FD_LOG_NOTICE(( "Using shmem region at --path as a memory mapped bstream (ignoring --dsync, --direct and --noatime)" ));
     152             : 
     153           0 :         bstream_type = 1;
     154             : 
     155           0 :         fd_shmem_join_info_t info[1];
     156           0 :         mmio = fd_shmem_join( path, FD_SHMEM_JOIN_MODE_READ_WRITE, 0, NULL, NULL, info );
     157           0 :         TEST( mmio, "fd_shmem_join failed" );
     158           0 :         mmio_sz = info->page_sz * info->page_cnt;
     159             : 
     160           0 :       }
     161             : 
     162           0 :     } else { /* No --path, use an anonymous region (e.g. testing or ultra HPC with no persistence) */
     163             : 
     164           0 :       FD_LOG_NOTICE(( "Using an anonymous shmem region as a memory mapped bstream "
     165           0 :                       "(ignoring --dsync, --direct and --noatime, setting --reset to 1)" ));
     166             : 
     167           0 :       bstream_type = 2;
     168           0 :       reset        = 1;
     169             : 
     170           0 :       mmio = fd_shmem_acquire( page_sz, page_cnt, near_cpu );
     171           0 :       TEST( mmio, "fd_shmem_acquire failed" );
     172           0 :       mmio_sz = page_sz*page_cnt;
     173             : 
     174           0 :     }
     175             : 
     176           0 :     TEST( fd_ulong_is_aligned( (ulong)_io, fd_vinyl_io_mm_align() ), "bad alloc" );
     177           0 :     TEST( io_footprint >= fd_vinyl_io_mm_footprint( spad_max ),      "bad alloc" );
     178             : 
     179           0 :     io = fd_vinyl_io_mm_init( _io, spad_max, mmio, mmio_sz, reset, info, info_sz, io_seed );
     180           0 :     TEST( io, "fd_vinyl_io mm_init failed" );
     181             : 
     182           0 :   } else if( !strcmp( type, "bd" ) ) {
     183             : 
     184           0 :     if( FD_VINYL_BSTREAM_BLOCK_SZ<512UL ) TEST( !direct, "--direct 1 not supported with --type bd and BLOCK_SZ<512" );
     185             : 
     186           0 :     TEST( path, "--path not specified for --type bd" );
     187             : 
     188           0 :     FD_LOG_NOTICE(( "Using --path as a block device bstream" ));
     189             : 
     190           0 :     bstream_type = 3;
     191             : 
     192           0 :     fd = open( path, open_flags, 0 );
     193           0 :     if( FD_UNLIKELY( fd==-1 ) ) FD_LOG_ERR(( "open failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     194             : 
     195           0 :     TEST( fd_ulong_is_aligned( (ulong)_io, fd_vinyl_io_bd_align() ), "bad wksp alloc" );
     196           0 :     TEST( io_footprint >= fd_vinyl_io_bd_footprint( spad_max ),      "bad wksp alloc" );
     197             : 
     198           0 :     io = fd_vinyl_io_bd_init( _io, spad_max, fd, reset, info, info_sz, io_seed );
     199           0 :     TEST( io, "fd_vinyl_io bd_init failed" );
     200             : 
     201           0 :   } else {
     202             : 
     203           0 :     FD_LOG_ERR(( "Unsupported io type" ));
     204             : 
     205           0 :   }
     206             : 
     207           0 :   FD_LOG_NOTICE(( "Creating vinyl" ));
     208             : 
     209           0 :   fd_tpool_t * tpool = NULL;
     210             : 
     211           0 :   ulong thread_cnt = fd_tile_cnt();
     212             : 
     213           0 :   if( thread_cnt>1UL ) {
     214           0 :     FD_LOG_NOTICE(( "Creating temporary tpool from all %lu tiles for thread parallel init", thread_cnt ));
     215             : 
     216           0 :     static uchar _tpool[ FD_TPOOL_FOOTPRINT( FD_TILE_MAX ) ] __attribute__((aligned(FD_TPOOL_ALIGN)));
     217             : 
     218           0 :     tpool = fd_tpool_init( _tpool, thread_cnt, 0UL ); /* logs details */
     219           0 :     if( FD_UNLIKELY( !tpool ) ) FD_LOG_ERR(( "fd_tpool_init failed" ));
     220             : 
     221           0 :     for( ulong thread_idx=1UL; thread_idx<thread_cnt; thread_idx++ )
     222           0 :       if( FD_UNLIKELY( !fd_tpool_worker_push( tpool, thread_idx ) ) ) FD_LOG_ERR(( "fd_tpool_worker_push failed" ));
     223           0 :   }
     224             : 
     225           0 :   fd_vinyl_t * vinyl = fd_vinyl_init( tpool, 0UL, thread_cnt, level,
     226           0 :                                       _vinyl,
     227           0 :                                       _cnc,  cnc_footprint,
     228           0 :                                       _meta, meta_footprint,
     229           0 :                                       _line, line_footprint,
     230           0 :                                       _ele,  ele_footprint,
     231           0 :                                       _obj,  obj_footprint,
     232           0 :                                       io, seed, wksp, async_min, async_max, part_thresh, gc_thresh, gc_eager, style );
     233             : 
     234           0 :   TEST( vinyl, "fd_vinyl_init failed" );
     235             : 
     236           0 :   if( tpool ) {
     237           0 :     FD_LOG_NOTICE(( "Destroying temporary tpool" ));
     238           0 :     fd_tpool_fini( tpool );
     239           0 :   }
     240             : 
     241           0 : # undef TEST
     242             : 
     243           0 :   FD_LOG_NOTICE(( "Running" ));
     244             : 
     245           0 :   fd_vinyl_exec( vinyl );
     246             : 
     247           0 :   FD_LOG_NOTICE(( "Cleaning up" ));
     248             : 
     249           0 :   fd_vinyl_fini( vinyl );
     250           0 :   fd_vinyl_io_fini( io );
     251             : 
     252           0 :   switch( bstream_type ) {
     253           0 :   case 0:  fd_io_mmio_fini ( mmio, mmio_sz           ); /* logs details */ break; /* mmio on a file */
     254           0 :   case 1:  fd_shmem_leave  ( mmio, NULL, 0UL         ); /* logs details */ break; /* mmio on a named shmem region */
     255           0 :   case 2:  fd_shmem_release( mmio, page_sz, page_cnt ); /* logs details */ break; /* mmio on a anon  shmem region */
     256           0 :   default: break;                                                                 /* block device or other */
     257           0 :   }
     258             : 
     259           0 :   if( FD_LIKELY( fd!=-1 ) && FD_UNLIKELY( close( fd ) ) )
     260           0 :     FD_LOG_WARNING(( "close failed (%i-%s); attempting to continue", errno, fd_io_strerror( errno ) ));
     261             : 
     262           0 :   fd_wksp_pod_unmap( _ele   );
     263           0 :   fd_wksp_pod_unmap( _obj   );
     264           0 :   fd_wksp_pod_unmap( _io    );
     265           0 :   fd_wksp_pod_unmap( _line  );
     266           0 :   fd_wksp_pod_unmap( _meta  );
     267           0 :   fd_wksp_pod_unmap( _cnc   );
     268           0 :   fd_wksp_pod_unmap( _vinyl );
     269             : 
     270           0 :   fd_wksp_pod_detach( pod );
     271             : 
     272           0 :   return 0;
     273           0 : }
     274             : 
     275             : int
     276             : main( int     argc,
     277           0 :       char ** argv ) {
     278           0 :   fd_boot( &argc, &argv );
     279             : 
     280           0 : # define SHIFT(n) argv += (n), argc -= (n)
     281             : 
     282           0 :   if( FD_UNLIKELY( argc<1 ) ) FD_LOG_ERR(( "no arguments" ));
     283             : 
     284           0 :   char const * bin = argv[0];
     285           0 :   SHIFT(1);
     286             : 
     287           0 :   umask( (mode_t)0 ); /* So mode setting gets respected */
     288             : 
     289             :   /* We let advanced operators configure these.  The defaults are
     290             :      reasonable safe values.  E.g. a larger pod might be useful if an
     291             :      operator wants to stash their own config in the pod created by a
     292             :      vinyl instance (and then might want the specific vinyl config to be
     293             :      its own subpod of that pod).  Or might want to stash additional
     294             :      info in the vinyl tile cnc_app region.  Or might want a
     295             :      larger/smaller io append scratch pad to speed up performance/reduce
     296             :      memory footprint.  Or to set the meta cache seed manually.  Or use
     297             :      their own tagging conventions.  Or ...
     298             : 
     299             :      For obj_footprint_avg, if we assume most objects are minimum sized,
     300             :      they will take up 2 blocks in the object store (1 for the header
     301             :      and 1 for the raw encoded pair).  Such objects will be stored in a
     302             :      superblock with 64 other objects.  So there is an 8 byte overhead
     303             :      for the superblock header.  And the superblocks are recursively
     304             :      contained in a larger superblocks with a radix of ~12 which adds a
     305             :      little more to the overhead (less than 1 byte practically). */
     306             : 
     307           0 :   ulong        wksp_tag          = 0xfdc12113c597a600UL; /* FD VINYL WKSP TAG 00 */
     308           0 :   ulong        pod_max           = 4096UL;
     309           0 :   char const * cfg_path          = NULL;
     310           0 :   ulong        cnc_app_sz        = FD_VINYL_CNC_APP_SZ;
     311           0 :   ulong        spad_max          = fd_vinyl_io_spad_est();
     312           0 :   ulong        async_min         = 2UL;
     313           0 :   ulong        async_max         = 4UL;
     314           0 :   ulong        part_thresh       = 1UL << 30;                      /* insert parallel recovery partitions every ~1 GiB */
     315           0 :   ulong        gc_thresh         = 8UL << 30;                      /* don't compact unless >~ 8 GiB used */
     316           0 :   int          gc_eager          = 2;                              /* target <~25% garbage items */
     317           0 :   int          style             = FD_VINYL_BSTREAM_CTL_STYLE_LZ4; /* enable data compression */
     318           0 :   int          level             = 1;                              /* do a hard reset by default */
     319           0 :   ulong        obj_footprint_avg = 2UL*FD_VINYL_BSTREAM_BLOCK_SZ + 8UL + 1UL; /* see note above */
     320             : 
     321           0 :   int err = 0;
     322           0 :   int cnt = 0;
     323             : 
     324           0 :   while( argc ) {
     325           0 :     char const * cmd = argv[0];
     326           0 :     SHIFT(1);
     327             : 
     328           0 :     if( !strcmp( cmd, "help" ) ) {
     329             : 
     330           0 :       fflush( stdout ); fflush( stderr );
     331           0 :       fputs( fd_vinyl_ctl_help, stdout );
     332           0 :       fflush( stdout ); fflush( stderr );
     333             : 
     334           0 :       FD_LOG_NOTICE(( "%i: %s: success", cnt, cmd ));
     335             : 
     336           0 :     } else if( !strcmp( cmd, "set" ) ) {
     337             : 
     338           0 :       if( FD_UNLIKELY( argc<2 ) ) FD_LOG_ERR(( "%i: %s: too few arguments\n\tDo %s help for help", cnt, cmd, bin ));
     339             : 
     340           0 :       char const * key = argv[0];
     341           0 :       char const * val = argv[1];
     342             : 
     343           0 :       /**/ if( !strcmp( key, "wksp_tag"          ) ) wksp_tag          = fd_cstr_to_ulong                  ( val );
     344           0 :       else if( !strcmp( key, "pod_max"           ) ) pod_max           = fd_cstr_to_ulong                  ( val );
     345           0 :       else if( !strcmp( key, "cfg_path"          ) ) cfg_path          =                                     val;
     346           0 :       else if( !strcmp( key, "cnc_app_sz"        ) ) cnc_app_sz        = fd_cstr_to_ulong                  ( val );
     347           0 :       else if( !strcmp( key, "spad_max"          ) ) spad_max          = fd_cstr_to_ulong                  ( val );
     348           0 :       else if( !strcmp( key, "async_min"         ) ) async_min         = fd_cstr_to_ulong                  ( val );
     349           0 :       else if( !strcmp( key, "async_max"         ) ) async_max         = fd_cstr_to_ulong                  ( val );
     350           0 :       else if( !strcmp( key, "part_thresh"       ) ) part_thresh       = fd_cstr_to_ulong                  ( val );
     351           0 :       else if( !strcmp( key, "gc_thresh"         ) ) gc_thresh         = fd_cstr_to_ulong                  ( val );
     352           0 :       else if( !strcmp( key, "gc_eager"          ) ) gc_eager          = fd_cstr_to_int                    ( val );
     353           0 :       else if( !strcmp( key, "style"             ) ) style             = fd_cstr_to_vinyl_bstream_ctl_style( val );
     354           0 :       else if( !strcmp( key, "level"             ) ) level             = fd_cstr_to_int                    ( val );
     355           0 :       else if( !strcmp( key, "obj_footprint_avg" ) ) obj_footprint_avg = fd_cstr_to_ulong                  ( val );
     356           0 :       else FD_LOG_ERR(( "%i: %s %s %s: unknown key", cnt, cmd, key, val));
     357             : 
     358           0 :       FD_LOG_NOTICE(( "%i: %s %s %s: success", cnt, cmd, key, val ));
     359           0 :       SHIFT(2);
     360             : 
     361           0 :     } else if( !strcmp( cmd, "alloc-memory" ) ) {
     362             : 
     363           0 :       if( FD_UNLIKELY( argc<5 ) ) FD_LOG_ERR(( "%i: %s: too few arguments\n\tDo %s help for help", cnt, cmd, bin ));
     364             : 
     365           0 :       char const * mem      =                           argv[0];
     366           0 :       ulong        page_cnt = fd_cstr_to_ulong        ( argv[1] );
     367           0 :       ulong        page_sz  = fd_cstr_to_shmem_page_sz( argv[2] );
     368           0 :       char const * seq      =                           argv[3];
     369           0 :       ulong        mode     = fd_cstr_to_ulong_octal  ( argv[4] );
     370             : 
     371           0 :       if( FD_UNLIKELY( !page_cnt ) )
     372           0 :         FD_LOG_ERR(( "%i: %s %s %lu %s %s 0%03lo: bad page count\n\t"
     373           0 :                      "Do %s help for help", cnt, cmd, mem, page_cnt, argv[2], seq, mode, bin ));
     374             : 
     375           0 :       if( FD_UNLIKELY( !page_sz ) )
     376           0 :         FD_LOG_ERR(( "%i: %s %s %lu %s %s 0%03lo: bad page size\n\t"
     377           0 :                      "Do %s help for help", cnt, cmd, mem, page_cnt, argv[2], seq, mode, bin ));
     378             : 
     379             :       /* Partition the pages over the seq */
     380             : 
     381           0 :       ulong sub_page_cnt[ 512UL ];
     382           0 :       ulong sub_cpu_idx [ 512UL ];
     383           0 :       ulong sub_cnt = fd_cstr_to_ulong_seq( seq, sub_cpu_idx, 512UL );
     384             : 
     385           0 :       if( FD_UNLIKELY( !sub_cnt ) )
     386           0 :         FD_LOG_ERR(( "%i: %s %s %lu %s %s 0%03lo: empty or invalid cpu sequence\n\t"
     387           0 :                      "Do %s help for help", cnt, cmd, mem, page_cnt, argv[2], seq, mode, bin ));
     388             : 
     389           0 :       if( FD_UNLIKELY( sub_cnt>512UL ) )
     390           0 :         FD_LOG_ERR(( "%i: %s %s %lu %s %s 0%03lo: sequence too long, increase limit in fd_vinyl_ctl.c\n\t"
     391           0 :                      "Do %s help for help", cnt, cmd, mem, page_cnt, argv[2], seq, mode, bin ));
     392             : 
     393             :       /* TODO: consider striping instead of blocking */
     394             : 
     395           0 :       ulong sub_page_min = page_cnt / sub_cnt;
     396           0 :       ulong sub_page_rem = page_cnt % sub_cnt;
     397           0 :       for( ulong sub_idx=0UL; sub_idx<sub_cnt; sub_idx++ ) sub_page_cnt[ sub_idx ] = sub_page_min + (ulong)(sub_idx<sub_page_rem);
     398             : 
     399             :       /* Create the workspace */
     400             : 
     401             :       /* TODO: allow user to specify seed and/or part_max */
     402           0 :       int err = fd_wksp_new_named( mem, page_sz, sub_cnt, sub_page_cnt, sub_cpu_idx, mode, 0U, 0UL ); /* logs details */
     403           0 :       if( FD_UNLIKELY( err ) )
     404           0 :         FD_LOG_ERR(( "%i: %s %s %lu %s %s 0%03lo: fd_wksp_new_named failed (%i-%s)\n\t"
     405           0 :                      "Do %s help for help", cnt, cmd, mem, page_cnt, argv[2], seq, mode, err, fd_wksp_strerror( err ), bin ));
     406             : 
     407           0 :       FD_LOG_NOTICE(( "%i: %s %s %lu %s %s 0%03lo: success", cnt, cmd, mem, page_cnt, argv[2], seq, mode ));
     408           0 :       SHIFT(5);
     409             : 
     410           0 :    } else if( !strcmp( cmd, "free-memory" ) ) {
     411             : 
     412           0 :       if( FD_UNLIKELY( argc<1 ) ) FD_LOG_ERR(( "%i: %s: too few arguments\n\tDo %s help for help", cnt, cmd, bin ));
     413             : 
     414           0 :       char const * mem = argv[0];
     415             : 
     416           0 :       int err = fd_wksp_delete_named( mem );
     417           0 :       if( FD_UNLIKELY( err ) )
     418           0 :         FD_LOG_ERR(( "%i: %s %s: fd_wksp_delete_named failed (%i-%s)\n\t"
     419           0 :                      "Do %s help for help", cnt, cmd, mem, err, fd_wksp_strerror( err ), bin ));
     420             : 
     421           0 :       FD_LOG_NOTICE(( "%i: %s %s: success", cnt, cmd, mem ));
     422           0 :       SHIFT(1);
     423             : 
     424           0 :     } else if( !strcmp( cmd, "alloc-storage" ) ) {
     425             : 
     426           0 :       if( FD_UNLIKELY( argc<3 ) )
     427           0 :         FD_LOG_ERR(( "%i: %s: too few arguments\n\tDo %s help for help", cnt, cmd, bin ));
     428             : 
     429           0 :       char const * path    =                         argv[0];
     430           0 :       ulong        GiB_cnt = fd_cstr_to_ulong      ( argv[1] );
     431           0 :       ulong        mode    = fd_cstr_to_ulong_octal( argv[2] );
     432             : 
     433           0 :       if( FD_UNLIKELY( (!GiB_cnt) | (GiB_cnt>(1UL<<32)) ) )
     434           0 :         FD_LOG_ERR(( "%i: %s %s %lu 0%03lo: bad number of gigabytes\n\t"
     435           0 :                      "Do %s help for help", cnt, cmd, path, GiB_cnt, mode, bin ));
     436             : 
     437           0 :       ulong sz = GiB_cnt << 30;
     438             : 
     439           0 :       int fd = open( path, O_RDWR | O_CREAT | O_EXCL, (mode_t)mode );
     440           0 :       if( FD_UNLIKELY( fd==-1 ) )
     441           0 :         FD_LOG_ERR(( "%i: %s %s %lu 0%03lo: open failed (%i-%s)\n\tDo %s help for help",
     442           0 :                      cnt, cmd, path, GiB_cnt, mode, errno, fd_io_strerror( errno ), bin ));
     443             : 
     444           0 :       int err = fd_io_truncate( fd, sz );
     445           0 :       if( FD_UNLIKELY( err ) )
     446           0 :         FD_LOG_ERR(( "%i: %s %s %lu 0%03lo: fd_io_truncate failed (%i-%s)\n\tDo %s help for help",
     447           0 :                      cnt, cmd, path, GiB_cnt, mode, err, fd_io_strerror( err ), bin ));
     448             : 
     449           0 :       if( FD_UNLIKELY( close( fd ) ) )
     450           0 :         FD_LOG_WARNING(( "%i: %s %s %lu 0%03lo: close failed (%i-%s); attempting to continue",
     451           0 :                          cnt, cmd, path, GiB_cnt, mode, errno, fd_io_strerror( errno ) ));
     452             : 
     453           0 :       FD_LOG_NOTICE(( "%i: %s %s %lu 0%03lo: success", cnt, cmd, path, GiB_cnt, mode ));
     454           0 :       SHIFT(3);
     455             : 
     456           0 :     } else if( !strcmp( cmd, "free-storage" ) ) {
     457             : 
     458           0 :       if( FD_UNLIKELY( argc<1 ) )
     459           0 :         FD_LOG_ERR(( "%i: %s: too few arguments\n\tDo %s help for help", cnt, cmd, bin ));
     460             : 
     461           0 :       char const * store = argv[0];
     462             : 
     463           0 :       if( FD_UNLIKELY( unlink( store ) ) )
     464           0 :         FD_LOG_ERR(( "%i: %s %s: unlink failed (%i-%s)\n\tDo %s help for help",
     465           0 :                      cnt, cmd, store, errno, fd_io_strerror( errno ), bin ));
     466             : 
     467           0 :       FD_LOG_NOTICE(( "%i: %s %s: success", cnt, cmd, store ));
     468           0 :       SHIFT(1);
     469             : 
     470           0 :     } else if( !strcmp( cmd, "new" ) ) {
     471             : 
     472           0 :       if( FD_UNLIKELY( argc<3 ) )
     473           0 :         FD_LOG_ERR(( "%i: %s: too few arguments\n\tDo %s help for help", cnt, cmd, bin ));
     474             : 
     475           0 :       char const * mem      =                   argv[0];
     476           0 :       ulong        pair_max = fd_cstr_to_ulong( argv[1] );
     477           0 :       ulong        GiB_max  = fd_cstr_to_ulong( argv[2] );
     478             : 
     479           0 : #     define TEST( c, msg ) do {                                                 \
     480           0 :         if( FD_UNLIKELY( !(c) ) )                                                \
     481           0 :           FD_LOG_ERR(( "%i: %s %s %lu %lu: FAIL %s (%s)\n\tDo %s help for help", \
     482           0 :                        cnt, cmd, mem, pair_max, GiB_max, #c, (msg), bin ));      \
     483           0 :       } while(0)
     484             : 
     485           0 :       ulong ele_max   = fd_ulong_pow2_up( pair_max + 1UL );
     486           0 :       ulong lock_cnt  = fd_vinyl_meta_lock_cnt_est( ele_max );
     487           0 :       ulong probe_max = ele_max;
     488             : 
     489           0 :       TEST( (0UL<pair_max) & (pair_max<ele_max) & (ele_max<=(ULONG_MAX/sizeof(fd_vinyl_meta_ele_t))), "bad pair_max" );
     490             : 
     491           0 :       ulong mem_max = GiB_max << 30;
     492             : 
     493           0 :       ulong pod_align       = fd_pod_align();
     494           0 :       ulong pod_footprint   = fd_pod_footprint( pod_max );
     495           0 :       ulong vinyl_align     = fd_vinyl_align();
     496           0 :       ulong vinyl_footprint = fd_vinyl_footprint();
     497           0 :       ulong cnc_align       = fd_cnc_align();
     498           0 :       ulong cnc_footprint   = fd_cnc_footprint( cnc_app_sz );
     499           0 :       ulong meta_align      = fd_vinyl_meta_align();
     500           0 :       ulong meta_footprint  = fd_vinyl_meta_footprint( ele_max, lock_cnt, probe_max );
     501           0 :       ulong io_align        = fd_ulong_max( fd_vinyl_io_bd_align(),               fd_vinyl_io_mm_align()               );
     502           0 :       ulong io_footprint    = fd_ulong_max( fd_vinyl_io_bd_footprint( spad_max ), fd_vinyl_io_mm_footprint( spad_max ) );
     503           0 :       ulong line_align      = alignof(fd_vinyl_line_t);
     504             :       /* line footprint computed below */
     505           0 :       ulong ele_align       = alignof(fd_vinyl_meta_ele_t);
     506           0 :       ulong ele_footprint   =  sizeof(fd_vinyl_meta_ele_t)*ele_max;
     507           0 :       ulong obj_align       = alignof(fd_vinyl_data_obj_t);
     508             :       /* obj_footprint compted below */
     509             : 
     510             :       /* See note re io_align / io_footprint */
     511             : 
     512           0 :       TEST( pod_footprint,  "bad pod_max"    );
     513           0 :       TEST( cnc_footprint,  "bad cnc_app_sz" );
     514           0 :       TEST( meta_footprint, "bad pair_max"   );
     515           0 :       TEST( io_footprint,   "bad spad_max"   );
     516             : 
     517           0 :       ulong mem_req =   pod_footprint +   pod_align - 1UL
     518           0 :                     + vinyl_footprint + vinyl_align - 1UL
     519           0 :                     +   cnc_footprint +   cnc_align - 1UL
     520           0 :                     +  meta_footprint +  meta_align - 1UL
     521           0 :                     +    io_footprint +    io_align - 1UL
     522           0 :                     +     /* below */    line_align - 1UL
     523           0 :                     +   ele_footprint +   ele_align - 1UL
     524           0 :                     +     /* below */     obj_align - 1UL; /* FIXME: USE SATURATING ADDS */
     525             : 
     526           0 :       TEST( mem_req<mem_max, "increase maximum GiB allowed and/or decrease pair_max / spad_max / pod_max / cnc_app_sz" );
     527             : 
     528           0 :       ulong line_max = (mem_max - mem_req) / (sizeof(fd_vinyl_line_t) + obj_footprint_avg);
     529             : 
     530           0 :       TEST( line_max>=3UL, "increase maximum GiB allowed and/or decrease pair_max / spad_max / pod_max / cnc_app_sz" );
     531             : 
     532           0 :       ulong line_footprint = sizeof(fd_vinyl_line_t)*line_max;
     533             : 
     534           0 :       mem_req += line_footprint;
     535             : 
     536           0 :       ulong obj_footprint = fd_ulong_align_dn( mem_max - mem_req, alignof(fd_vinyl_data_obj_t) );
     537             : 
     538           0 :       mem_req += obj_footprint;
     539             : 
     540           0 :       TEST( mem_req<=mem_max, "internal error" );
     541             : 
     542             :       /* Attach to the memory that will contain this vinyl instance */
     543             : 
     544           0 :       fd_wksp_t * wksp = fd_wksp_attach( mem );
     545           0 :       TEST( wksp, "fd_wksp_attach failed" );
     546             : 
     547             :       /* Allocate all the needed regions.  Note that, even though the
     548             :          vinyl io tile state is neither shared nor persistent, we
     549             :          allocate it here so the vinyl tile itself doesn't have to
     550             :          allocate it (it is dynamically sized and rather large).  Since
     551             :          we want the vinyl tile to be able to pick the type of io
     552             :          interface and bstream store at startup without creating a new
     553             :          vinyl instance, we allocated an upper bound for all supported
     554             :          io types above (they are all roughly the same size anyway).
     555             : 
     556             :          Alternatively, we could have the vinyl tile do this allocation
     557             :          at tile startup.  But this would create some additional
     558             :          complexity: the vinyl tile would need an allocator (and then
     559             :          one potentially has allocations left over from previous runs
     560             :          that did not terminate cleanly).
     561             : 
     562             :          Similar considerations apply for the data cache state, vinyl
     563             :          tile state, lines and data objects.
     564             : 
     565             :          Note also that, though meta is shared and persistent,
     566             :          persistence should only be used for post mortem debugging (the
     567             :          meta cache is recreated from scratch on vinyl tile startup). */
     568             : 
     569           0 :       void * _pod   = fd_wksp_alloc_laddr( wksp,   pod_align,   pod_footprint, wksp_tag );
     570           0 :       void * _vinyl = fd_wksp_alloc_laddr( wksp, vinyl_align, vinyl_footprint, wksp_tag );
     571           0 :       void * _cnc   = fd_wksp_alloc_laddr( wksp,   cnc_align,   cnc_footprint, wksp_tag );
     572           0 :       void * _meta  = fd_wksp_alloc_laddr( wksp,  meta_align,  meta_footprint, wksp_tag );
     573           0 :       void * _io    = fd_wksp_alloc_laddr( wksp,    io_align,    io_footprint, wksp_tag );
     574           0 :       void * _line  = fd_wksp_alloc_laddr( wksp,  line_align,  line_footprint, wksp_tag ); /* This is kinda big */
     575           0 :       void * _ele   = fd_wksp_alloc_laddr( wksp,   ele_align,   ele_footprint, wksp_tag ); /* This is really big */
     576           0 :       void * _obj   = fd_wksp_alloc_laddr( wksp,   obj_align,   obj_footprint, wksp_tag );
     577             : 
     578             :       /* Note: the bigger obj gets, the better the performance (until it
     579             :          is large enough pairs always fit in cache but that would dwarf
     580             :          ele).  In typical use cases, this is probably smaller to
     581             :          comparable to ele (resulting in much cheaper hardware at
     582             :          comparable speeds for typical usage patterns but less robust
     583             :          performance for extreme usage patterns). */
     584             : 
     585           0 :       TEST( (!!_pod) & (!!_vinyl) & (!!_cnc) & (!!_io) & (!!_line) & (!!_ele) & (!!_obj),
     586           0 :            "fd_wksp_alloc_laddr failed (free unneeded allocs or increase wksp size or partitions)" );
     587             : 
     588             :       /* Format and the join the pod and create the cfg subpod as
     589             :          necessary. */
     590             : 
     591           0 :       uchar * pod = fd_pod_join( fd_pod_new( _pod, pod_max ) );
     592           0 :       TEST( pod, "internal error" );
     593             : 
     594           0 :       uchar * cfg;
     595           0 :       if( !cfg_path ) cfg = pod;
     596           0 :       else {
     597           0 :         ulong off = fd_pod_alloc_subpod( pod, cfg_path, 1024UL );
     598           0 :         TEST( off, "use shorter cfg_path or increase pod_max?" );
     599           0 :         cfg = pod + off;
     600           0 :       }
     601             : 
     602             :       /* Populate the pod */
     603             : 
     604           0 :       char tmp[ FD_WKSP_CSTR_MAX ];
     605             : 
     606           0 :       TEST( fd_pod_insert_cstr( cfg, "vinyl", fd_wksp_cstr_laddr( _vinyl, tmp ) ), "increase pod_max?" );
     607           0 :       TEST( fd_pod_insert_cstr( cfg, "cnc",   fd_wksp_cstr_laddr( _cnc,   tmp ) ), "increase pod_max?" );
     608           0 :       TEST( fd_pod_insert_cstr( cfg, "meta",  fd_wksp_cstr_laddr( _meta,  tmp ) ), "increase pod_max?" );
     609           0 :       TEST( fd_pod_insert_cstr( cfg, "io",    fd_wksp_cstr_laddr( _io,    tmp ) ), "increase pod_max?" );
     610           0 :       TEST( fd_pod_insert_cstr( cfg, "line",  fd_wksp_cstr_laddr( _line,  tmp ) ), "increase pod_max?" );
     611           0 :       TEST( fd_pod_insert_cstr( cfg, "ele",   fd_wksp_cstr_laddr( _ele,   tmp ) ), "increase pod_max?" );
     612           0 :       TEST( fd_pod_insert_cstr( cfg, "obj",   fd_wksp_cstr_laddr( _obj,   tmp ) ), "increase pod_max?" );
     613             : 
     614           0 :       TEST( fd_pod_insert_ulong( cfg, "vinyl_footprint", vinyl_footprint ), "increase pod_max?" );
     615           0 :       TEST( fd_pod_insert_ulong( cfg,   "cnc_footprint",   cnc_footprint ), "increase pod_max?" );
     616           0 :       TEST( fd_pod_insert_ulong( cfg,  "meta_footprint",  meta_footprint ), "increase pod_max?" );
     617           0 :       TEST( fd_pod_insert_ulong( cfg,    "io_footprint",    io_footprint ), "increase pod_max?" );
     618           0 :       TEST( fd_pod_insert_ulong( cfg,  "line_footprint",  line_footprint ), "increase pod_max?" );
     619           0 :       TEST( fd_pod_insert_ulong( cfg,   "ele_footprint",   ele_footprint ), "increase pod_max?" );
     620           0 :       TEST( fd_pod_insert_ulong( cfg,   "obj_footprint",   obj_footprint ), "increase pod_max?" );
     621             : 
     622           0 :       TEST( fd_pod_insert_ulong( cfg, "spad_max",    spad_max    ), "increase pod_max?" );
     623           0 :       TEST( fd_pod_insert_ulong( cfg, "pair_max",    pair_max    ), "increase pod_max?" );
     624           0 :       TEST( fd_pod_insert_ulong( cfg, "line_max",    line_max    ), "increase pod_max?" );
     625           0 :       TEST( fd_pod_insert_ulong( cfg, "async_min",   async_min   ), "increase pod_max?" );
     626           0 :       TEST( fd_pod_insert_ulong( cfg, "async_max",   async_max   ), "increase pod_max?" );
     627           0 :       TEST( fd_pod_insert_ulong( cfg, "part_thresh", part_thresh ), "increase pod_max?" );
     628           0 :       TEST( fd_pod_insert_ulong( cfg, "gc_thresh",   gc_thresh   ), "increase pod_max?" );
     629           0 :       TEST( fd_pod_insert_int  ( cfg, "gc_eager",    gc_eager    ), "increase pod_max?" );
     630           0 :       TEST( fd_pod_insert_int  ( cfg, "style",       style       ), "increase pod_max?" );
     631           0 :       TEST( fd_pod_insert_int  ( cfg, "level",       level       ), "increase pod_max?" );
     632             : 
     633             :       /* Tell the operator where the pod is */
     634             :       /* FIXME: consider putting the config pod in a normal page named
     635             :          shmem region or a flat file instead?  Probably easier to pass
     636             :          between applications than a wksp gaddr. */
     637             : 
     638           0 :       printf( "%s\n", fd_wksp_cstr_laddr( _pod, tmp ) );
     639             : 
     640             :       /* Clean up */
     641             : 
     642           0 :       if( cfg!=pod ) TEST( fd_pod_compact( cfg, 1 ), "internal error" );
     643             : 
     644           0 :       TEST( fd_pod_leave( pod )==_pod, "internal error" );
     645             : 
     646           0 :       TEST( !fd_wksp_detach( wksp ), "internal error" );
     647             : 
     648           0 : #     undef TEST
     649             : 
     650           0 :       FD_LOG_NOTICE(( "%i: %s %s %lu %lu: success", cnt, cmd, mem, pair_max, GiB_max ));
     651           0 :       SHIFT(3);
     652             : 
     653           0 :     } else if( !strcmp( cmd, "delete" ) ) {
     654             : 
     655           0 :       if( FD_UNLIKELY( argc<1 ) )
     656           0 :         FD_LOG_ERR(( "%i: %s: too few arguments\n\tDo %s help for help", cnt, cmd, bin ));
     657             : 
     658           0 :       char const * cstr = argv[0];
     659             : 
     660           0 : #     define TEST( c, msg ) do {                                         \
     661           0 :         if( FD_UNLIKELY( !(c) ) )                                        \
     662           0 :           FD_LOG_ERR(( "%i: %s %s: FAIL %s (%s)\n\tDo %s help for help", \
     663           0 :                        cnt, cmd, cstr, #c, (msg), bin ));                \
     664           0 :       } while(0)
     665             : 
     666           0 :       uchar const * pod = fd_pod_join( fd_wksp_map( cstr ) ); /* logs details */
     667           0 :       TEST( pod, "unable to join pod" );
     668             : 
     669           0 :       uchar const * cfg;
     670           0 :       if( !cfg_path ) cfg = pod;
     671           0 :       else {
     672           0 :         cfg = fd_pod_query_subpod( pod, cfg_path );
     673           0 :         TEST( cfg, "cfg not found at cfg_path" );
     674           0 :       }
     675             : 
     676           0 :       fd_wksp_cstr_free( fd_pod_query_cstr( cfg, "obj",   NULL ) );
     677           0 :       fd_wksp_cstr_free( fd_pod_query_cstr( cfg, "ele",   NULL ) );
     678           0 :       fd_wksp_cstr_free( fd_pod_query_cstr( cfg, "line",  NULL ) );
     679           0 :       fd_wksp_cstr_free( fd_pod_query_cstr( cfg, "io",    NULL ) );
     680           0 :       fd_wksp_cstr_free( fd_pod_query_cstr( cfg, "meta",  NULL ) );
     681           0 :       fd_wksp_cstr_free( fd_pod_query_cstr( cfg, "cnc",   NULL ) );
     682           0 :       fd_wksp_cstr_free( fd_pod_query_cstr( cfg, "vinyl", NULL ) );
     683             : 
     684           0 :       fd_wksp_unmap( fd_pod_leave( pod ) );
     685             : 
     686           0 :       fd_wksp_cstr_free( cstr );
     687             : 
     688           0 :       FD_LOG_NOTICE(( "%i: %s %s: success", cnt, cmd, cstr ));
     689           0 :       SHIFT(1);
     690             : 
     691           0 :     } else if( !strcmp( cmd, "exec" ) ) {
     692             : 
     693           0 :       err = fd_vinyl_main( argc, argv );
     694           0 :       break;
     695             : 
     696           0 :     } else {
     697             : 
     698           0 :       FD_LOG_ERR(( "%i: %s: unknown command\n\t"
     699           0 :                    "Do %s help for help", cnt, cmd, bin ));
     700             : 
     701           0 :     }
     702           0 :     cnt++;
     703           0 :   }
     704             : 
     705           0 :   if( FD_UNLIKELY( cnt<1 ) ) FD_LOG_NOTICE(( "processed %i commands\n\tDo %s help for help", cnt, bin ));
     706           0 :   else                       FD_LOG_NOTICE(( "processed %i commands", cnt ));
     707             : 
     708           0 : # undef SHIFT
     709             : 
     710           0 :   fd_halt();
     711           0 :   return err;
     712           0 : }

Generated by: LCOV version 1.14