LCOV - code coverage report
Current view: top level - flamenco/accdb - fd_accdb_ctl.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 412 0.0 %
Date: 2025-12-06 04:45:29 Functions: 0 20 0.0 %

          Line data    Source code
       1             : /* fd_accdb_ctl.c is a command-line debugging tool for interacting with
       2             :    a Firedancer account database. */
       3             : 
       4             : #include "../../vinyl/fd_vinyl.h"
       5             : #include "../../flamenco/fd_flamenco_base.h"
       6             : #include "../../ballet/base58/fd_base58.h"
       7             : #include "../../util/cstr/fd_cstr.h"
       8             : #include "../../util/pod/fd_pod.h"
       9             : #include <ctype.h>
      10             : #include <stddef.h> /* offsetof */
      11             : #include <stdio.h>
      12             : 
      13             : /* req_info contains various request metadata R/W mapped into the vinyl
      14             :    tile. */
      15             : 
      16             : struct req_info {
      17             :   fd_vinyl_key_t  key[1];
      18             :   ulong           val_gaddr[1];
      19             :   schar           err[1];
      20             :   fd_vinyl_comp_t comp[1];
      21             : };
      22             : 
      23             : typedef struct req_info req_info_t;
      24             : 
      25             : /* The client class contains local handles to client-related vinyl
      26             :    objects. */
      27             : 
      28             : struct client {
      29             :   fd_vinyl_rq_t * rq;
      30             :   fd_vinyl_cq_t * cq;
      31             :   ulong           req_id;
      32             :   ulong           link_id;
      33             : 
      34             :   fd_vinyl_meta_t * meta;
      35             : 
      36             :   req_info_t * req_info;
      37             :   ulong        req_info_gaddr;
      38             :   fd_wksp_t *  val_wksp;
      39             :   fd_wksp_t *  client_wksp;
      40             : 
      41             :   /* Vinyl client status */
      42             :   ulong quota_rem;
      43             :   ulong cq_seq;
      44             : };
      45             : 
      46             : typedef struct client client_t;
      47             : 
      48             : static char const bin2hex[ 16 ] = { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' };
      49             : 
      50             : static void
      51             : hexdump( uchar const * data,
      52           0 :          uint          sz ) {
      53           0 :   ulong sz_align = fd_ulong_align_dn( sz, 16UL );
      54           0 :   uint i;
      55           0 :   for( i=0U; i<sz_align; i+=16U ) {
      56           0 :     char line[ 80 ];
      57           0 :     char * p = fd_cstr_init( line );
      58           0 :     p = fd_cstr_append_uint_as_hex( p, '0', i, 7UL );
      59           0 :     p = fd_cstr_append_text( p, ":  ", 3UL );
      60           0 :     for( ulong j=0UL; j<16UL; j++ ) {
      61           0 :       p = fd_cstr_append_char( p, bin2hex[ data[ i+j ]>>4 ] );
      62           0 :       p = fd_cstr_append_char( p, bin2hex[ data[ i+j ]&15 ] );
      63           0 :       p = fd_cstr_append_char( p, ' ' );
      64           0 :     }
      65           0 :     p = fd_cstr_append_char( p, ' ' );
      66           0 :     for( ulong j=0UL; j<16UL; j++ ) {
      67           0 :       int c = data[ i+j ];
      68           0 :       p = fd_cstr_append_char( p, fd_char_if( fd_isalnum( c ) | fd_ispunct( c ) | (c==' '), (char)c, '.' ) );
      69           0 :     }
      70           0 :     p = fd_cstr_append_char( p, '\n' );
      71           0 :     ulong len = (ulong)( p-line );
      72           0 :     fd_cstr_fini( p );
      73           0 :     fwrite( line, 1UL, len, stdout );
      74           0 :   }
      75           0 :   if( sz ) {
      76           0 :     char line[ 80 ];
      77           0 :     char * p = fd_cstr_init( line );
      78           0 :     p = fd_cstr_append_uint_as_hex( p, '0', i, 7UL );
      79           0 :     p = fd_cstr_append_text( p, ":  ", 3UL );
      80           0 :     for( ; i<sz; i++ ) {
      81           0 :       p = fd_cstr_append_char( p, bin2hex[ data[ i ]>>4 ] );
      82           0 :       p = fd_cstr_append_char( p, bin2hex[ data[ i ]&15 ] );
      83           0 :       p = fd_cstr_append_char( p, ' ' );
      84           0 :     }
      85           0 :     p = fd_cstr_append_char( p, '\n' );
      86           0 :     ulong len = (ulong)( p-line );
      87           0 :     fd_cstr_fini( p );
      88           0 :     fwrite( line, 1UL, len, stdout );
      89           0 :   }
      90           0 :   fflush( stdout );
      91           0 : }
      92             : 
      93             : static void
      94             : client_query( client_t * client,
      95             :               char **    arg,
      96           0 :               ulong      arg_cnt ) {
      97           0 :   req_info_t * req_info = client->req_info;
      98           0 :   if( FD_UNLIKELY( arg_cnt!=1UL ) ) {
      99           0 :     puts( "ERR(query): invalid query command, usage is \"query <account address>\"" );
     100           0 :     return;
     101           0 :   }
     102           0 :   char const * acc_addr_b58 = arg[0];
     103           0 :   fd_vinyl_key_t * acc_key = req_info->key;
     104           0 :   if( FD_UNLIKELY( !fd_base58_decode_32( acc_addr_b58, acc_key->uc ) ) ) {
     105           0 :     puts( "ERR(query): invalid account address" );
     106           0 :     return;
     107           0 :   }
     108             : 
     109             :   /* Send an acquire request */
     110             : 
     111           0 :   req_info->comp->seq = 0UL;
     112           0 :   fd_vinyl_rq_send(
     113           0 :       client->rq,
     114           0 :       client->req_id++,
     115           0 :       client->link_id,
     116           0 :       FD_VINYL_REQ_TYPE_ACQUIRE, /* type */
     117           0 :       0UL, /* flags */
     118           0 :       1UL,
     119           0 :       FD_VINYL_VAL_MAX, /* val_max */
     120           0 :       /* key_gaddr       */ client->req_info_gaddr + offsetof( req_info_t, key       ),
     121           0 :       /* val_gaddr_gaddr */ client->req_info_gaddr + offsetof( req_info_t, val_gaddr ),
     122           0 :       /* err_gaddr       */ client->req_info_gaddr + offsetof( req_info_t, err       ),
     123           0 :       /* comp_gaddr      */ client->req_info_gaddr + offsetof( req_info_t, comp      )
     124           0 :   );
     125             : 
     126             :   /* Poll direct completion for acquire (not via CQ) */
     127             : 
     128           0 :   fd_vinyl_comp_t * comp = req_info->comp;
     129           0 :   while( FD_VOLATILE_CONST( comp->seq )!=1UL ) FD_SPIN_PAUSE();
     130           0 :   int acquire_err = req_info->err[0];
     131           0 :   if( acquire_err==FD_VINYL_SUCCESS ) {
     132           0 :     fd_account_meta_t const * val  = fd_wksp_laddr_fast( client->val_wksp, req_info->val_gaddr[0] );
     133           0 :     void const *              data = (void const *)( val+1 );
     134             : 
     135           0 :     FD_BASE58_ENCODE_32_BYTES( val->owner, owner_b58 );
     136           0 :     printf(
     137           0 :         "\n"
     138           0 :         "Public Key: %s\n"
     139           0 :         "Balance: %lu.%lu SOL\n"
     140           0 :         "Owner: %s\n"
     141           0 :         "Executable: %s\n"
     142           0 :         "Length: %u (0x%x) bytes\n",
     143           0 :         acc_addr_b58,
     144           0 :         val->lamports / 1000000000UL,
     145           0 :         val->lamports % 1000000000UL,
     146           0 :         owner_b58,
     147           0 :         val->executable ? "true" : "false",
     148           0 :         val->dlen,
     149           0 :         val->dlen
     150           0 :     );
     151           0 :     hexdump( data, val->dlen );
     152             : 
     153             :     /* Send a release request */
     154             : 
     155           0 :     req_info->comp->seq = 0UL;
     156           0 :     fd_vinyl_rq_send(
     157           0 :         client->rq,
     158           0 :         client->req_id++,
     159           0 :         client->link_id,
     160           0 :         FD_VINYL_REQ_TYPE_RELEASE, /* type */
     161           0 :         0UL, /* flags */
     162           0 :         1UL,
     163           0 :         FD_VINYL_VAL_MAX, /* val_max */
     164           0 :         0UL,
     165           0 :         /* val_gaddr_gaddr */ client->req_info_gaddr + offsetof( req_info_t, val_gaddr ),
     166           0 :         /* err_gaddr       */ client->req_info_gaddr + offsetof( req_info_t, err       ),
     167           0 :         /* comp_gaddr      */ client->req_info_gaddr + offsetof( req_info_t, comp      )
     168           0 :     );
     169             : 
     170             :     /* Poll direct completion for release (not via CQ) */
     171             : 
     172           0 :     while( FD_VOLATILE_CONST( comp->seq )!=1UL ) FD_SPIN_PAUSE();
     173           0 :     FD_TEST( req_info->err[0]==FD_VINYL_SUCCESS );
     174             : 
     175           0 :     puts( "" );
     176           0 :   } else if( acquire_err==FD_VINYL_ERR_KEY ) {
     177           0 :     printf(
     178           0 :         "\n"
     179           0 :         "Public Key: %s\n"
     180           0 :         "Account does not exist\n"
     181           0 :         "\n",
     182           0 :         acc_addr_b58
     183           0 :     );
     184           0 :   } else {
     185           0 :     FD_LOG_ERR(( "Vinyl acquire request failed (err %i-%s)", acquire_err, fd_vinyl_strerror( acquire_err ) ));
     186           0 :   }
     187           0 : }
     188             : 
     189             : typedef struct batch_req batch_req_t;
     190             : struct batch_req {
     191             :   batch_req_t * prev;
     192             :   batch_req_t * next;
     193             : 
     194             :   ulong key_off;
     195             :   ulong err_off;
     196             :   ulong val_gaddr_off;
     197             : 
     198             :   ulong req_id;
     199             : };
     200             : 
     201             : static ulong
     202           0 : batch_req_align( void ) {
     203           0 :   return fd_ulong_max( alignof(batch_req_t), alignof(fd_vinyl_key_t) );
     204           0 : }
     205             : 
     206             : static ulong
     207           0 : batch_req_footprint( ulong depth ) {
     208           0 :   ulong l = FD_LAYOUT_INIT;
     209           0 :   l = FD_LAYOUT_APPEND( l, alignof(batch_req_t),          sizeof(batch_req_t)    );
     210           0 :   l = FD_LAYOUT_APPEND( l, alignof(fd_vinyl_key_t), depth*sizeof(fd_vinyl_key_t) );
     211           0 :   l = FD_LAYOUT_APPEND( l, alignof(schar),          depth*sizeof(schar)          );
     212           0 :   l = FD_LAYOUT_APPEND( l, alignof(ulong),          depth*sizeof(ulong)          );
     213           0 :   return FD_LAYOUT_FINI( l, batch_req_align() );
     214           0 : }
     215             : 
     216             : static batch_req_t *
     217             : batch_req_new( void * mem,
     218           0 :                ulong  depth ) {
     219           0 :   FD_SCRATCH_ALLOC_INIT( l, mem );
     220           0 :   batch_req_t *    req       = FD_SCRATCH_ALLOC_APPEND( l, alignof(batch_req_t),    sizeof(batch_req_t)          );
     221           0 :   fd_vinyl_key_t * key       = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_vinyl_key_t), depth*sizeof(fd_vinyl_key_t) );
     222           0 :   schar *          err       = FD_SCRATCH_ALLOC_APPEND( l, alignof(schar),          depth*sizeof(schar)          );
     223           0 :   ulong *          val_gaddr = FD_SCRATCH_ALLOC_APPEND( l, alignof(ulong),          depth*sizeof(ulong)          );
     224           0 :   FD_SCRATCH_ALLOC_FINI( l, batch_req_align() );
     225             : 
     226           0 :   *req = (batch_req_t) {
     227           0 :     .prev = NULL,
     228           0 :     .next = NULL,
     229             : 
     230           0 :     .key_off       = (ulong)key       - (ulong)mem,
     231           0 :     .err_off       = (ulong)err       - (ulong)mem,
     232           0 :     .val_gaddr_off = (ulong)val_gaddr - (ulong)mem
     233           0 :   };
     234           0 :   return req;
     235           0 : }
     236             : 
     237             : static inline fd_vinyl_key_t *
     238           0 : batch_req_key( batch_req_t * req ) {
     239           0 :   return (fd_vinyl_key_t *)( (ulong)req + req->key_off );
     240           0 : }
     241             : 
     242             : static inline schar *
     243           0 : batch_req_err( batch_req_t * req ) {
     244           0 :   return (schar *)( (ulong)req + req->err_off );
     245           0 : }
     246             : 
     247             : static inline ulong *
     248           0 : batch_req_val_gaddr( batch_req_t * req ) {
     249           0 :   return (ulong *)( (ulong)req + req->val_gaddr_off );
     250           0 : }
     251             : 
     252             : struct bench_query_rand {
     253             :   batch_req_t * req_free;     /* free entries */
     254             :   batch_req_t * req_wait_lo;  /* list of entries awaiting completion */
     255             :   batch_req_t * req_wait_hi;
     256             :   ulong         batch_depth;
     257             : 
     258             :   ulong            iter_rem;
     259             :   fd_vinyl_key_t * sample;
     260             :   ulong            sample_idx;
     261             :   ulong            sample_max;
     262             : 
     263             :   ulong found_cnt;
     264             :   ulong miss_cnt;
     265             : };
     266             : typedef struct bench_query_rand bench_query_rand_t;
     267             : 
     268             : /* bqr_free_push adds a wait queue entry to the free stack. */
     269             : 
     270             : static void
     271             : bqr_free_push( bench_query_rand_t * bqr,
     272           0 :                batch_req_t *        req ) {
     273           0 :   req->prev = NULL;
     274           0 :   req->next = bqr->req_free;
     275           0 :   if( bqr->req_free ) bqr->req_free->prev = req;
     276           0 :   bqr->req_free = req;
     277           0 : }
     278             : 
     279             : /* bqr_free_pop removes a wait queue entry from the free stack (alloc). */
     280             : 
     281             : static batch_req_t *
     282           0 : bqr_free_pop( bench_query_rand_t * bqr ) {
     283           0 :   batch_req_t * req = bqr->req_free;
     284           0 :   bqr->req_free = req->next;
     285           0 :   if( bqr->req_free ) bqr->req_free->prev = NULL;
     286           0 :   req->prev = req->next = NULL;
     287           0 :   return req;
     288           0 : }
     289             : 
     290             : /* bqr_wait_push adds a new wait queue entry. */
     291             : 
     292             : static void
     293             : bqr_wait_push( bench_query_rand_t * bqr,
     294           0 :                batch_req_t *        req ) {
     295           0 :   req->prev        = bqr->req_wait_hi;
     296           0 :   req->next        = NULL;
     297           0 :   if( bqr->req_wait_hi ) bqr->req_wait_hi->next = req;
     298           0 :   bqr->req_wait_hi = req;
     299           0 :   if( !bqr->req_wait_lo ) bqr->req_wait_lo = req;
     300           0 : }
     301             : 
     302             : /* bqr_wait_pop removes the oldest wait queue entry. */
     303             : 
     304             : static batch_req_t *
     305           0 : bqr_wait_pop( bench_query_rand_t * bqr ) {
     306           0 :   batch_req_t * req = bqr->req_wait_lo;
     307           0 :   bqr->req_wait_lo = req->next;
     308           0 :   req->prev = req->next = NULL;
     309           0 :   if( bqr->req_wait_lo ) bqr->req_wait_lo->prev = NULL;
     310           0 :   else                   bqr->req_wait_hi       = NULL;
     311           0 :   return req;
     312           0 : }
     313             : 
     314             : /* bqr_req_release sends a batch RELEASE request for a batch of values.
     315             :    Completions arriving for RELEASE will replenish quota. */
     316             : 
     317             : static void
     318             : bqr_req_release( client_t *           client,
     319             :                  bench_query_rand_t * bqr,
     320             :                  batch_req_t *        req,
     321           0 :                  uint                 cnt ) {
     322           0 :   FD_CRIT( !req->prev && !req->next, "attempt to release a request that is already free or still pending" );
     323             : 
     324           0 :   schar * err = batch_req_err( req );
     325           0 :   for( uint i=0U; i<cnt; i++ ) err[ i ] = FD_VINYL_SUCCESS;
     326             : 
     327           0 :   ulong req_id          = fd_ulong_set_bit( client->req_id++, 63 );
     328           0 :   ulong link_id         = client->link_id;
     329           0 :   int   type            = FD_VINYL_REQ_TYPE_RELEASE;
     330           0 :   ulong flags           = 0UL;
     331           0 :   ulong batch_cnt       = (ulong)cnt;
     332           0 :   ulong val_gaddr_gaddr = fd_wksp_gaddr_fast( client->client_wksp, batch_req_val_gaddr( req ) );
     333           0 :   ulong err_gaddr       = fd_wksp_gaddr_fast( client->client_wksp, err );
     334           0 :   fd_vinyl_rq_send( client->rq, req_id, link_id, type, flags, batch_cnt, 0UL, 0UL, val_gaddr_gaddr, err_gaddr, 0UL );
     335             : 
     336           0 :   req->req_id = req_id;
     337           0 :   bqr_wait_push( bqr, req );
     338           0 : }
     339             : 
     340             : /* bqr_handle_cq handles an ACQUIRE or RELEASE completion. */
     341             : 
     342             : static void
     343             : bqr_handle_cq( client_t *           client,
     344             :                bench_query_rand_t * bqr,
     345           0 :                fd_vinyl_comp_t *    comp ) {
     346           0 :   FD_CRIT( bqr->req_wait_lo, "received completion even though no request is pending" );
     347           0 :   batch_req_t * req = bqr_wait_pop( bqr );
     348           0 :   FD_CRIT( req->req_id==comp->req_id, "received completion for unexpected req_id" );
     349           0 :   FD_CRIT( comp->batch_cnt<=bqr->batch_depth, "corrupt comp->batch_cnt" );
     350             : 
     351             :   /* The high bit of the request ID indicates whether this was an
     352             :      ACQUIRE or RELEASE request. */
     353           0 :   int const is_release = fd_ulong_extract_bit( comp->req_id, 63 );
     354             : 
     355           0 :   fd_vinyl_key_t * key       = batch_req_key( req );
     356           0 :   ulong *          val_gaddr = batch_req_val_gaddr( req );
     357           0 :   schar *          err       = batch_req_err( req );
     358             : 
     359           0 :   if( !is_release ) {
     360             : 
     361           0 :     uint j=0U;
     362           0 :     for( uint i=0U; i<comp->batch_cnt; i++ ) {
     363           0 :       int e = err[ i ];
     364           0 :       if( FD_UNLIKELY( e!=FD_VINYL_SUCCESS && e!=FD_VINYL_ERR_KEY ) ) {
     365           0 :         FD_LOG_CRIT(( "Unexpected vinyl error %i-%s", e, fd_vinyl_strerror( e ) ));
     366           0 :       }
     367           0 :       if( e==FD_VINYL_SUCCESS ) {
     368           0 :         bqr->found_cnt++;
     369           0 :         key      [ j ] = key[ i ];
     370           0 :         val_gaddr[ j ] = val_gaddr[ i ];
     371           0 :         j++;
     372           0 :       } else {
     373           0 :         bqr->miss_cnt++;
     374           0 :         client->quota_rem++;
     375           0 :       }
     376           0 :     }
     377             : 
     378           0 :     if( j ) bqr_req_release( client, bqr, req, j );
     379           0 :     else    bqr_free_push( bqr, req );
     380             : 
     381           0 :   } else {
     382             : 
     383           0 :     schar * err = batch_req_err( req );
     384           0 :     uint cnt = comp->batch_cnt;
     385           0 :     for( uint i=0U; i<cnt; i++ ) {
     386           0 :       int e = err[ i ];
     387           0 :       if( FD_UNLIKELY( e ) ) {
     388           0 :         FD_LOG_CRIT(( "Unexpected vinyl error for req %u %i-%s", i, e, fd_vinyl_strerror( e ) ));
     389           0 :       }
     390           0 :     }
     391           0 :     client->quota_rem += comp->batch_cnt;
     392           0 :     bqr_free_push( bqr, req );
     393             : 
     394           0 :   }
     395             : 
     396           0 : }
     397             : 
     398             : /* bqr_drain_cq drains all completion queue entries. */
     399             : 
     400             : static void
     401             : bqr_drain_cq( client_t *           client,
     402           0 :               bench_query_rand_t * bqr ) {
     403           0 :   for(;;) {
     404           0 :     fd_vinyl_comp_t comp[1];
     405           0 :     long cq_err = fd_vinyl_cq_recv( client->cq, client->cq_seq, comp );
     406           0 :     if( FD_UNLIKELY( cq_err<0 ) ) {
     407           0 :       FD_LOG_CRIT(( "Vinyl completion queue overrun detected" ));
     408           0 :     }
     409           0 :     if( cq_err>0 ) break;
     410           0 :     bqr_handle_cq( client, bqr, comp );
     411           0 :     client->cq_seq++;
     412           0 :   }
     413           0 : }
     414             : 
     415             : /* bqr_req_acquire sends a batch of ACQUIRE requests. */
     416             : 
     417             : static void
     418             : bqr_req_acquire( client_t *           client,
     419           0 :                  bench_query_rand_t * bqr ) {
     420           0 :   FD_CRIT( bqr->req_free, "attempt to acquire a request when none are free" );
     421           0 :   batch_req_t * req = bqr_free_pop( bqr );
     422           0 :   ulong cnt = bqr->batch_depth;
     423           0 :   if( FD_UNLIKELY( cnt>bqr->iter_rem ) ) cnt = bqr->iter_rem;
     424             : 
     425             :   /* Prepare request descriptor */
     426           0 :   fd_vinyl_key_t * key       = batch_req_key      ( req );
     427           0 :   schar *          err       = batch_req_err      ( req );
     428           0 :   ulong *          val_gaddr = batch_req_val_gaddr( req );
     429           0 :   for( ulong i=0UL; i<cnt; i++ ) {
     430           0 :     ulong idx = bqr->sample_idx;
     431           0 :     key      [ i ] = bqr->sample[ idx ];
     432           0 :     err      [ i ] = 0;
     433           0 :     val_gaddr[ i ] = 0UL;
     434           0 :     bqr->sample_idx++;
     435           0 :     if( bqr->sample_idx>=bqr->sample_max ) bqr->sample_idx = 0UL;
     436           0 :   }
     437             : 
     438             :   /* Send request */
     439           0 :   ulong req_id          = fd_ulong_clear_bit( client->req_id++, 63 );
     440           0 :   ulong link_id         = client->link_id;
     441           0 :   int   type            = FD_VINYL_REQ_TYPE_ACQUIRE;
     442           0 :   ulong flags           = 0UL;
     443           0 :   ulong key_gaddr       = fd_wksp_gaddr_fast( client->client_wksp, batch_req_key      ( req ) );
     444           0 :   ulong val_gaddr_gaddr = fd_wksp_gaddr_fast( client->client_wksp, batch_req_val_gaddr( req ) );
     445           0 :   ulong err_gaddr       = fd_wksp_gaddr_fast( client->client_wksp, batch_req_err      ( req ) );
     446           0 :   fd_vinyl_rq_send( client->rq, req_id, link_id, type, flags, cnt, 0UL, key_gaddr, val_gaddr_gaddr, err_gaddr, 0UL );
     447             : 
     448             :   /* Update quotas */
     449           0 :   bqr->iter_rem     -= cnt;
     450           0 :   client->quota_rem -= cnt;
     451             : 
     452           0 :   req->req_id = req_id;
     453           0 :   bqr_wait_push( bqr, req );
     454           0 : }
     455             : 
     456             : /* bench_query_rand_poll sends as many random read requests to vinyl as
     457             :    possible.  Returns 1 if there is more work to do, 0 if the benchmark
     458             :    is done. */
     459             : 
     460             : static int
     461             : bench_query_rand_poll( client_t *           client,
     462           0 :                        bench_query_rand_t * bqr ) {
     463           0 :   if( bqr->req_wait_lo ) {
     464           0 :     bqr_drain_cq( client, bqr );
     465           0 :   }
     466           0 :   while( bqr->req_free && bqr->iter_rem ) {
     467           0 :     bqr_req_acquire( client, bqr );
     468           0 :   }
     469           0 :   return (!!bqr->req_wait_lo) | (!!bqr->iter_rem);
     470           0 : }
     471             : 
     472             : /* client_bench_query_rand runs a random read benchmark against vinyl.
     473             :    Assumes that RQ and CQ are clean and quota_rem==quota_max. */
     474             : 
     475             : static void
     476             : client_bench_query_rand( client_t * client,
     477             :                          int *      pargc,
     478           0 :                          char ***   pargv ) {
     479             : 
     480             :   /* Prepare a random query benchmark
     481             : 
     482             :      1. Randomly sample keys into an array (--keys)
     483             :      2. Inject random keys at a configurable rate (--miss) to exercise
     484             :         index query misses
     485             :      3. Loop through the sampled keys array until (--iter) queries have
     486             :         been submitted, while doing batches of (--batch) keys at a time
     487             : 
     488             :      The benchmark loop is pipelined/asynchronous.  The client will
     489             :      submit request batches until it is blocked by quota, RQ, or CQ. */
     490             : 
     491           0 :   ulong       batch_depth = fd_env_strip_cmdline_ulong( pargc, pargv, "--batch", NULL,       1UL );
     492           0 :   ulong       key_cnt     = fd_env_strip_cmdline_ulong( pargc, pargv, "--keys",  NULL,  262144UL );
     493           0 :   ulong const iter_cnt    = fd_env_strip_cmdline_ulong( pargc, pargv, "--iter",  NULL, 1048576UL );
     494           0 :   ulong const seed        = fd_env_strip_cmdline_ulong( pargc, pargv, "--seed",  NULL, (ulong)fd_tickcount() );
     495           0 :   float const miss_r      = fd_env_strip_cmdline_float( pargc, pargv, "--miss",  NULL,      0.1f );
     496             : 
     497           0 :   batch_depth = fd_ulong_max( batch_depth, 1UL );
     498           0 :   key_cnt     = fd_ulong_min( key_cnt, UINT_MAX );
     499             : 
     500           0 :   fd_rng_t _rng[1]; fd_rng_t * rng = fd_rng_join( fd_rng_new( _rng, (uint)fd_ulong_hash( seed ), 0UL ) );
     501             : 
     502           0 :   fd_vinyl_meta_t * meta      = client->meta;
     503           0 :   ulong const       ele_max   = fd_vinyl_meta_ele_max  ( meta );
     504           0 :   ulong const       probe_max = fd_vinyl_meta_probe_max( meta );
     505             : 
     506             :   /* Allocate a huge page backed scratch memory region to back keys */
     507             : 
     508           0 :   ulong            sample_fp       = fd_ulong_align_up( key_cnt*sizeof(fd_vinyl_key_t), FD_SHMEM_HUGE_PAGE_SZ );
     509           0 :   ulong            sample_page_sz  = FD_SHMEM_NORMAL_PAGE_SZ;
     510           0 :   ulong            sample_page_cnt = sample_fp>>FD_SHMEM_NORMAL_LG_PAGE_SZ;
     511           0 :   fd_vinyl_key_t * sample          = fd_shmem_acquire( sample_page_sz, sample_page_cnt, fd_log_cpu_id()  );
     512           0 :   ulong            sample_cnt      = 0UL;
     513           0 :   if( FD_UNLIKELY( !sample ) ) {
     514           0 :     FD_LOG_WARNING(( "Cannot acquire scratch memory to hold %lu vinyl keys (out of memory).  Aborting benchmark", key_cnt ));
     515           0 :     return;
     516           0 :   }
     517             : 
     518             :   /* Determine pipeline depth */
     519             : 
     520           0 :   ulong const rq_ele_depth = fd_vinyl_rq_req_cnt ( client->rq )*batch_depth;
     521           0 :   ulong const cq_ele_depth = fd_vinyl_cq_comp_cnt( client->cq )*batch_depth;
     522           0 :   ulong const quota_max    = fd_ulong_min( client->quota_rem, fd_ulong_min( rq_ele_depth, cq_ele_depth ) );
     523           0 :   ulong const batch_max    = ( quota_max + batch_depth - 1UL ) / batch_depth;
     524             : 
     525             :   /* Allocate request queue entries */
     526             : 
     527           0 :   ulong req_footprint       = batch_req_footprint( batch_depth );
     528           0 :   ulong req_batch_footprint = batch_max*req_footprint;
     529           0 :   ulong req_laddr           = (ulong)fd_wksp_alloc_laddr( client->client_wksp, batch_req_align(), req_batch_footprint, 1UL );
     530           0 :   if( FD_UNLIKELY( !req_laddr ) ) {
     531           0 :     FD_LOG_WARNING(( "Vinyl client wksp is too small to hold requests.  Aborting benchmark" ));
     532           0 :     fd_shmem_release( sample, sample_page_sz, sample_page_cnt );
     533           0 :     return;
     534           0 :   }
     535           0 :   for( ulong batch_idx=0UL,
     536           0 :              batch_cur=req_laddr;
     537           0 :        batch_idx<quota_max;
     538           0 :        batch_idx++,
     539           0 :        batch_cur+=req_footprint ) {
     540           0 :     batch_req_t * req = batch_req_new( (void *)batch_cur, batch_depth );
     541           0 :     req->prev = batch_idx>0UL           ? (batch_req_t *)( batch_cur - req_footprint ) : NULL;
     542           0 :     req->next = batch_idx+1UL<batch_max ? (batch_req_t *)( batch_cur + req_footprint ) : NULL;
     543           0 :   }
     544           0 :   batch_req_t * req_free = (batch_req_t *)req_laddr; /* free list holding all batch_req */
     545             : 
     546             :   /* Sample keys */
     547             : 
     548           0 :   long dt = -fd_log_wallclock();
     549           0 :   uint const miss_u = (uint)fd_ulong_min( (ulong)( (float)UINT_MAX * miss_r ), UINT_MAX );
     550           0 :   for( ulong i=0UL; i<key_cnt; i++ ) {
     551             : 
     552           0 :     if( fd_rng_uint( rng )<miss_u ) {  /* rand key */
     553           0 :       for( uint j=0U; j<32U; j+=4U ) FD_STORE( uint, sample[ i ].uc+j, fd_rng_uint( rng ) );
     554           0 :       continue;
     555           0 :     }
     556             : 
     557             :     /* sample a key, linear probe until one found */
     558           0 :     ulong meta_idx = fd_rng_ulong_roll( rng, ele_max );
     559           0 :     ulong probe_rem;
     560           0 :     for( probe_rem=probe_max; probe_rem; probe_rem-- ) {
     561           0 :       fd_vinyl_meta_ele_t const * ele = meta->ele + meta_idx;
     562           0 :       if( FD_LIKELY( fd_vinyl_meta_ele_in_use( ele ) ) ) {
     563           0 :         sample[ i ] = ele->phdr.key;
     564           0 :         sample_cnt++;
     565           0 :         break;
     566           0 :       }
     567           0 :       meta_idx = (meta_idx+1UL) % ele_max;
     568           0 :     }
     569           0 :     if( !probe_rem ) {  /* no key found (low hashmap utilization) ... */
     570           0 :       for( uint j=0U; j<32U; j+=4U ) FD_STORE( uint, sample[ i ].uc+j, fd_rng_uint( rng ) );
     571           0 :     }
     572             : 
     573           0 :   }
     574           0 :   dt += fd_log_wallclock();
     575             : 
     576           0 : # if FD_HAS_DOUBLE
     577           0 :   FD_LOG_NOTICE(( "Sampled %lu keys in %gs (miss ratio %g)",
     578           0 :                   key_cnt, (double)dt/1e9, (double)( key_cnt-sample_cnt )/(double)key_cnt ));
     579             : # else
     580             :   FD_LOG_NOTICE(( "Sampled %lu keys in %ldns (%lu missed)",
     581             :                   key_cnt, dt, key_cnt-sample_cnt ));
     582             : # endif
     583             : 
     584             :   /* Run benchmark */
     585             : 
     586           0 :   bench_query_rand_t bqr = {
     587           0 :     .req_free    = req_free,
     588           0 :     .req_wait_lo = NULL,
     589           0 :     .req_wait_hi = NULL,
     590           0 :     .batch_depth = batch_depth,
     591           0 :     .iter_rem    = iter_cnt,
     592           0 :     .sample      = sample,
     593           0 :     .sample_idx  = 0UL,
     594           0 :     .sample_max  = key_cnt
     595           0 :   };
     596           0 :   dt = -fd_log_wallclock();
     597           0 :   while( bench_query_rand_poll( client, &bqr ) );
     598           0 :   dt += fd_log_wallclock();
     599             : 
     600           0 : # if FD_HAS_DOUBLE
     601           0 :   FD_LOG_NOTICE(( "Completed %lu queries (%lu found, %lu missed) in %gs (%g q/s)",
     602           0 :                   iter_cnt, bqr.found_cnt, bqr.miss_cnt,
     603           0 :                   (double)dt/1e9,
     604           0 :                   (double)iter_cnt / ( (double)dt/1e9 ) ));
     605             : # else
     606             :   FD_LOG_NOTICE(( "Completed %lu queries (%lu found, %lu missed) in %ldns",
     607             :                   iter_cnt, bqr.found_cnt, bqr.miss_cnt, dt ));
     608             : # endif
     609             : 
     610             :   /* Clean up */
     611             : 
     612           0 :   fd_rng_delete( fd_rng_leave( rng ) );
     613             : 
     614           0 :   fd_wksp_free_laddr( (void *)req_laddr );
     615             : 
     616           0 :   fd_shmem_release( sample, sample_page_sz, sample_page_cnt );
     617           0 : }
     618             : 
     619             : static int
     620             : client_cmd( client_t * client,
     621             :             char **    tok,
     622           0 :             ulong      tok_cnt ) {
     623           0 :   if( FD_UNLIKELY( !tok_cnt ) ) return 1;
     624           0 :   char const * cmd = tok[0];
     625           0 :   if( !strcmp( cmd, "query" ) ) {
     626           0 :     client_query( client, tok+1, tok_cnt-1 );
     627           0 :   } else if( !strcmp( cmd, "bench-query-rand" ) ) {
     628           0 :     int argc = (int)tok_cnt;
     629           0 :     client_bench_query_rand( client, &argc, &tok );
     630           0 :   } else if( !strcmp( cmd, "quit" ) || !strcmp( cmd, "exit" ) ) {
     631           0 :     return 0;
     632           0 :   } else {
     633           0 :     printf( "ERR: unknown command `%s`\n", cmd );
     634           0 :   }
     635           0 :   return 1;
     636           0 : }
     637             : 
     638             : static void
     639           0 : repl( client_t * client ) {
     640           0 :   char   line[ 4096 ] = {0};
     641           0 : # define TOK_MAX 16
     642           0 :   char * tokens[ 16 ] = {0};
     643           0 :   for(;;) {
     644           0 :     fputs( "accdb> ", stdout );
     645           0 :     fflush( stdout );
     646             : 
     647             :     /* Read command */
     648           0 :     if( fgets( line, sizeof(line), stdin )==NULL ) {
     649           0 :       putc( '\n', stdout );
     650           0 :       break;
     651           0 :     }
     652           0 :     line[ strcspn( line, "\n" ) ] = '\0';
     653           0 :     line[ sizeof(line)-1        ] = '\0';
     654             : 
     655             :     /* Interpret command */
     656           0 :     ulong tok_cnt = fd_cstr_tokenize( tokens, TOK_MAX, line, ' ' );
     657           0 :     if( !client_cmd( client, tokens, tok_cnt ) ) break;
     658           0 :   }
     659           0 : # undef TOK_MAX
     660           0 : }
     661             : 
     662             : int
     663             : main( int     argc,
     664             :       char ** argv ) {
     665             :   fd_boot( &argc, &argv );
     666             : 
     667             :   char const * cfg_gaddr = fd_env_strip_cmdline_cstr ( &argc, &argv, "--cfg",       NULL, NULL );
     668             :   char const * wksp_name = fd_env_strip_cmdline_cstr ( &argc, &argv, "--wksp",      NULL, NULL );
     669             :   ulong const  burst_max = fd_env_strip_cmdline_ulong( &argc, &argv, "--burst-max", NULL, 1UL  );
     670             :   ulong const  quota_max = fd_env_strip_cmdline_ulong( &argc, &argv, "--quota-max", NULL, 2UL  );
     671             :   if( FD_UNLIKELY( !cfg_gaddr ) ) FD_LOG_ERR(( "Missing required argument --cfg" ));
     672             :   if( FD_UNLIKELY( !wksp_name ) ) FD_LOG_ERR(( "Missing required argument --wksp" ));
     673             : 
     674             :   argc--; argv++;
     675             : 
     676             :   /* Join server shared memory structures */
     677             : 
     678             :   uchar * pod = fd_pod_join( fd_wksp_map( cfg_gaddr ) );
     679             :   if( FD_UNLIKELY( !pod ) ) FD_LOG_ERR(( "Invalid --cfg pod" ));
     680             : 
     681             :   void * _cnc  = fd_wksp_pod_map( pod, "cnc"  );
     682             :   void * _meta = fd_wksp_pod_map( pod, "meta" );
     683             :   void * _ele  = fd_wksp_pod_map( pod, "ele"  );
     684             :   void * _obj  = fd_wksp_pod_map( pod, "obj"  );
     685             : 
     686             :   fd_cnc_t * cnc = fd_cnc_join( _cnc ); FD_TEST( cnc );
     687             :   fd_vinyl_meta_t meta[1];
     688             :   FD_TEST( fd_vinyl_meta_join( meta, _meta, _ele ) );
     689             : 
     690             :   ulong vinyl_status = fd_cnc_signal_query( cnc );
     691             :   if( FD_UNLIKELY( vinyl_status!=FD_CNC_SIGNAL_RUN ) ) {
     692             :     char status_cstr[ FD_CNC_SIGNAL_CSTR_BUF_MAX ];
     693             :     FD_LOG_ERR(( "Vinyl tile not running (status %lu-%s)", vinyl_status, fd_cnc_signal_cstr( vinyl_status, status_cstr ) ));
     694             :   }
     695             : 
     696             :   /* Allocate client structures */
     697             : 
     698             :   fd_wksp_t * wksp = fd_wksp_attach( wksp_name );
     699             :   FD_TEST( wksp );
     700             : 
     701             :   ulong const rq_max = 32UL;
     702             :   ulong const cq_max = 32UL;
     703             :   void * _rq = fd_wksp_alloc_laddr( wksp, fd_vinyl_rq_align(), fd_vinyl_rq_footprint( rq_max ), 1UL );
     704             :   void * _cq = fd_wksp_alloc_laddr( wksp, fd_vinyl_cq_align(), fd_vinyl_cq_footprint( cq_max ), 1UL );
     705             :   fd_vinyl_rq_t * rq = fd_vinyl_rq_join( fd_vinyl_rq_new( _rq, rq_max ) );
     706             :   fd_vinyl_cq_t * cq = fd_vinyl_cq_join( fd_vinyl_cq_new( _cq, cq_max ) );
     707             :   if( FD_UNLIKELY( !rq || !cq ) ) {
     708             :     FD_LOG_WARNING(( "Failed to allocate request/completion queues" ));
     709             :     goto dealloc2;
     710             :   }
     711             : 
     712             :   ulong req_info_gaddr = fd_wksp_alloc( wksp, alignof(req_info_t), sizeof(req_info_t), 1UL );
     713             :   if( FD_UNLIKELY( !req_info_gaddr ) ) {
     714             :     FD_LOG_WARNING(( "Failed to pre-allocate request metadata" ));
     715             :     goto dealloc1;
     716             :   }
     717             :   req_info_t * req_info = fd_wksp_laddr_fast( wksp, req_info_gaddr );
     718             : 
     719             :   /* Run client */
     720             : 
     721             :   ulong const link_id = 0UL;
     722             :   int join_err = fd_vinyl_client_join( cnc, rq, cq, wksp, link_id, burst_max, quota_max );
     723             :   if( FD_UNLIKELY( join_err ) ) FD_LOG_ERR(( "Failed to join vinyl client to server (err %i-%s)", join_err, fd_vinyl_strerror( join_err ) ));
     724             : 
     725             :   FD_LOG_NOTICE(( "Attached client" ));
     726             : 
     727             :   client_t client = {
     728             :     .rq      = rq,
     729             :     .cq      = cq,
     730             :     .req_id  = 0UL,
     731             :     .link_id = link_id,
     732             : 
     733             :     .meta = meta,
     734             : 
     735             :     .req_info       = req_info,
     736             :     .req_info_gaddr = req_info_gaddr,
     737             :     .val_wksp       = fd_wksp_containing( _obj ),
     738             :     .client_wksp    = wksp,
     739             : 
     740             :     .quota_rem = quota_max,
     741             :     .cq_seq    = fd_vinyl_cq_seq( cq )
     742             :   };
     743             : 
     744             :   if( argc>0 ) {
     745             :     client_cmd( &client, argv, (ulong)argc );
     746             :   } else {
     747             :     repl( &client );
     748             :   }
     749             : 
     750             :   FD_LOG_NOTICE(( "Detaching client" ));
     751             : 
     752             :   int leave_err = fd_vinyl_client_leave( cnc, link_id );
     753             :   if( FD_UNLIKELY( leave_err ) ) FD_LOG_ERR(( "Failed to leave vinyl client from server (err %i-%s)", leave_err, fd_vinyl_strerror( leave_err ) ));
     754             : 
     755             : dealloc1:
     756             :   fd_wksp_free( wksp, req_info_gaddr );
     757             : 
     758             : dealloc2:
     759             :   fd_wksp_free_laddr( fd_vinyl_rq_delete( fd_vinyl_rq_leave( rq ) ) );
     760             :   fd_wksp_free_laddr( fd_vinyl_cq_delete( fd_vinyl_cq_leave( cq ) ) );
     761             : 
     762             :   fd_wksp_unmap( fd_cnc_leave( cnc ) );
     763             :   fd_vinyl_meta_leave( meta );
     764             :   fd_wksp_unmap( _meta );
     765             :   fd_wksp_unmap( _ele );
     766             :   fd_wksp_unmap( _obj );
     767             :   fd_wksp_detach( wksp );
     768             : 
     769             :   fd_halt();
     770             :   return 0;
     771             : }

Generated by: LCOV version 1.14