diff --git a/src/app/fdctl/config/default.toml b/src/app/fdctl/config/default.toml index f925d29997..3890e3f839 100644 --- a/src/app/fdctl/config/default.toml +++ b/src/app/fdctl/config/default.toml @@ -624,7 +624,7 @@ dynamic_port_range = "8900-9000" # downstream consumers, after which additional packets begin to # replace older ones, which will be dropped. TODO: ... Should # this really be configurable? - send_buffer_size = 8192 + send_buffer_size = 16384 # QUIC tiles are responsible for serving network traffic, including # parsing and responding to packets and managing connection timeouts diff --git a/src/app/fdctl/ready.c b/src/app/fdctl/ready.c index 0ac717d020..4eead6230c 100644 --- a/src/app/fdctl/ready.c +++ b/src/app/fdctl/ready.c @@ -23,7 +23,8 @@ ready_cmd_fn( args_t * args, anyway. */ if( FD_UNLIKELY( fd_topo_tile_kind_is_labs( tile->kind ) ) ) continue; - int first_iter = 1; + long start = fd_log_wallclock(); + int printed = 0; do { ulong signal = fd_cnc_signal_query( tile->cnc ); char buf[ FD_CNC_SIGNAL_CSTR_BUF_MAX ]; @@ -32,8 +33,10 @@ ready_cmd_fn( args_t * args, else if( FD_UNLIKELY( signal!=FD_CNC_SIGNAL_BOOT ) ) FD_LOG_ERR(( "cnc for tile %s(%lu) is in bad state %s", fd_topo_tile_kind_str( tile->kind ), tile->kind_id, fd_cnc_signal_cstr( signal, buf ) )); - if( FD_UNLIKELY( first_iter ) ) FD_LOG_NOTICE(( "waiting for tile %s(%lu) to be ready", fd_topo_tile_kind_str( tile->kind ), tile->kind_id )); - first_iter = 0; + if( FD_UNLIKELY( !printed && (fd_log_wallclock()-start) > 1000000000L*1L ) ) { + FD_LOG_NOTICE(( "waiting for tile %s(%lu) to be ready", fd_topo_tile_kind_str( tile->kind ), tile->kind_id )); + printed = 1; + } } while(1); } diff --git a/src/app/fdctl/run/run.c b/src/app/fdctl/run/run.c index dffdef77d2..3fbb713051 100644 --- a/src/app/fdctl/run/run.c +++ b/src/app/fdctl/run/run.c @@ -44,6 +44,7 @@ run_cmd_perm( args_t * args, struct pidns_clone_args { config_t * config; int * pipefd; + int closefd; }; extern char fd_log_private_path[ 1024 ]; /* empty string on start */ @@ -164,6 +165,9 @@ int main_pid_namespace( void * _args ) { struct pidns_clone_args * args = _args; if( FD_UNLIKELY( close( args->pipefd[ 0 ] ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) )); + if( FD_UNLIKELY( -1!=args->closefd ) ) { + if( FD_UNLIKELY( close( args->closefd ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) )); + } config_t * const config = args->config; @@ -340,6 +344,7 @@ main_pid_namespace( void * _args ) { int clone_firedancer( config_t * const config, + int close_fd, int * out_pipe ) { /* This pipe is here just so that the child process knows when the parent has died (it will get a HUP). */ @@ -348,7 +353,7 @@ clone_firedancer( config_t * const config, /* clone into a pid namespace */ int flags = config->development.sandbox ? CLONE_NEWPID : 0; - struct pidns_clone_args args = { .config = config, .pipefd = pipefd, }; + struct pidns_clone_args args = { .config = config, .closefd = close_fd, .pipefd = pipefd, }; void * stack = fd_tile_private_stack_new( 0, 65535UL ); if( FD_UNLIKELY( !stack ) ) FD_LOG_ERR(( "unable to create a stack for boot process" )); @@ -406,7 +411,7 @@ run_firedancer( config_t * const config ) { if( FD_UNLIKELY( close( 1 ) ) ) FD_LOG_ERR(( "close(1) failed (%i-%s)", errno, fd_io_strerror( errno ) )); int pipefd; - pid_namespace = clone_firedancer( config, &pipefd ); + pid_namespace = clone_firedancer( config, -1, &pipefd ); /* Print the location of the logfile on SIGINT or SIGTERM, and also kill the child. They are connected by a pipe which the child is diff --git a/src/app/fdctl/run/run.h b/src/app/fdctl/run/run.h index b293b490c5..39cc34806d 100644 --- a/src/app/fdctl/run/run.h +++ b/src/app/fdctl/run/run.h @@ -20,6 +20,7 @@ tile_main( void * _args ); int clone_firedancer( config_t * const config, + int close_fd, int * out_pipe ); void diff --git a/src/app/fddev/Local.mk b/src/app/fddev/Local.mk index 72ad7fe9ca..9d5e9459cb 100644 --- a/src/app/fddev/Local.mk +++ b/src/app/fddev/Local.mk @@ -4,7 +4,7 @@ ifdef FD_HAS_X86 ifdef FD_HAS_DOUBLE .PHONY: fddev run monitor -$(call make-bin-rust,fddev,main dev dev1 txn configure/netns configure/keys configure/kill configure/genesis,fd_fdctl fd_disco fd_flamenco fd_reedsol fd_ballet fd_tango fd_util fd_quic solana_validator solana_genesis) +$(call make-bin-rust,fddev,main dev dev1 txn bench configure/netns configure/keys configure/kill configure/genesis,fd_fdctl fd_fddev fd_disco fd_flamenco fd_reedsol fd_ballet fd_tango fd_util fd_quic solana_validator solana_genesis) solana/target/$(RUST_PROFILE)/libsolana_genesis.a: cargo diff --git a/src/app/fddev/bench.c b/src/app/fddev/bench.c new file mode 100644 index 0000000000..3550ff6569 --- /dev/null +++ b/src/app/fddev/bench.c @@ -0,0 +1,392 @@ +#define _GNU_SOURCE +#include "fddev.h" + +#include "../fdctl/configure/configure.h" +#include "../fdctl/run/run.h" +#include "rpc_client/fd_rpc_client.h" + +#include "../../util/net/fd_ip4.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +void +bench_cmd_perm( args_t * args, + fd_caps_ctx_t * caps, + config_t * const config ) { + (void)args; + + args_t configure_args = { + .configure.command = CONFIGURE_CMD_INIT, + }; + for( ulong i=0; ilayout.affinity, tile_to_cpu ); + + ushort i; + for( i=0; i<128; i++ ) { + int found = 0; + for( ulong j=0; j=128 ) ) FD_LOG_ERR(( "no cpus left for bench" )); + return i; +} + +typedef struct __attribute__((packed)) { + uchar sig_cnt; /* = 1 */ + uchar signature[64]; + uchar _sig_cnt; /* also 1 */ + uchar ro_signed_cnt; /* = 0 */ + uchar ro_unsigned_cnt; /* = 1 . System program */ + uchar acct_addr_cnt; /* = 3 */ + uchar fee_payer[32]; + uchar dest_acct[32]; + uchar system_program[32]; /* = {0} */ + uchar recent_blockhash[32]; + uchar instr_cnt; /* = 1 */ + /* Start of instruction */ + uchar prog_id; /* = 2 */ + uchar acct_cnt; /* = 2 */ + uchar acct_idx[2]; /* 0, 1 */ + uchar data_sz; /* = 12 */ + uint transfer_descriminant; /* = 2 */ + ulong lamports; +} transfer_t; + +static void +generate_transfers( transfer_t * transfers, + ulong transfers_cnt, + const uchar * sender_public_key, + const uchar * sender_private_key, + uchar * recent_blockhash ) { + fd_sha512_t _sha[1]; + fd_sha512_t * sha = fd_sha512_join( fd_sha512_new( _sha ) ); + FD_TEST( sha ); + + for( ulong i=0UL; ifee_payer, sender_public_key, 32UL ); + fd_memset( transfer->dest_acct, 0, 32UL ); /* Just send to nowhere */ + fd_memcpy( transfer->recent_blockhash, recent_blockhash, 32UL ); + + fd_ed25519_sign( transfer->signature, + &(transfer->_sig_cnt), + sizeof(*transfer)-65UL, + sender_public_key, + sender_private_key, + sha ); + } +} + +extern int * fd_log_private_shared_lock; + +static int +main_bencher( void * _args ) { + config_t * const config = _args; + + if( FD_UNLIKELY( -1==setpriority( PRIO_PROCESS, 0, -19 ) ) ) FD_LOG_ERR(( "setpriority() failed (%i-%s)", errno, fd_io_strerror( errno ) )); + + cpu_set_t cpu_set[1]; + CPU_ZERO( cpu_set ); + CPU_SET( bench_cpu_idx( config ), cpu_set ); + if( FD_UNLIKELY( -1==sched_setaffinity( 0, sizeof(cpu_set_t), cpu_set ) ) ) FD_LOG_ERR(( "sched_setaffinity() failed (%i-%s)", errno, fd_io_strerror( errno ) )); + + int conn = socket( AF_INET, SOCK_DGRAM, 0 ); + if( FD_UNLIKELY( -1==conn ) ) FD_LOG_ERR(( "socket() failed (%i-%s)", errno, fd_io_strerror( errno ) )); + + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_port = fd_ushort_bswap( config->tiles.quic.regular_transaction_listen_port ), + .sin_addr.s_addr = config->tiles.net.ip_addr, + }; + if( FD_UNLIKELY( -1==connect( conn, fd_type_pun( &addr ), sizeof(addr) ) ) ) FD_LOG_ERR(( "connect() failed (%i-%s)", errno, fd_io_strerror( errno ) )); + + char faucet_key_path[ PATH_MAX ]; + snprintf1( faucet_key_path, PATH_MAX, "%s/faucet.json", config->scratch_directory ); + + const uchar * private_key = load_key_into_protected_memory( faucet_key_path, 0 ); + const uchar * public_key = private_key+32UL; + + fd_rpc_client_t * rpc_client = fd_rpc_client_join( fd_rpc_client_new( aligned_alloc( FD_RPC_CLIENT_ALIGN, FD_RPC_CLIENT_FOOTPRINT ), + FD_IP4_ADDR(127, 0, 0, 1), + config->rpc.port ) ); + + FD_TEST( FD_RPC_CLIENT_SUCCESS==fd_rpc_client_wait_ready( rpc_client, 5000000000L ) ); + + long request = fd_rpc_client_request_latest_block_hash( rpc_client ); + FD_TEST( request>=0L ); + + fd_rpc_client_response_t * response = fd_rpc_client_status( rpc_client, request, 1 ); + if( FD_UNLIKELY( response->status!=FD_RPC_CLIENT_SUCCESS ) ) FD_LOG_ERR(( "fd_rpc_client_status() failed" )); + +#define TRANSFER_CNT (1000000UL) + FD_LOG_NOTICE(( "generating %lu transfers", TRANSFER_CNT )); + + transfer_t * transfers = malloc( TRANSFER_CNT * sizeof(transfer_t ) ); + FD_TEST( transfers ); + generate_transfers( transfers, TRANSFER_CNT, public_key, private_key, response->result.latest_block_hash.block_hash ); + + /* wait until validator is ready to receive txns before sending */ + ready_cmd_fn( NULL, config ); + + /* Wait until sampler is ready. */ + fd_log_private_shared_lock[ 1 ]++; + while( fd_log_private_shared_lock[ 1 ]!=2 ) FD_SPIN_PAUSE(); + + FD_LOG_NOTICE(( "sending %lu transfers", TRANSFER_CNT )); + + for( ulong i=0; irpc.port ) ); + + FD_TEST( FD_RPC_CLIENT_SUCCESS==fd_rpc_client_wait_ready( rpc_client, 5000000000L ) ); + + long request = fd_rpc_client_request_transaction_count( rpc_client ); + FD_TEST( request>=0L ); + + fd_rpc_client_response_t * response = fd_rpc_client_status( rpc_client, request, 1 ); + if( FD_UNLIKELY( response->status!=FD_RPC_CLIENT_SUCCESS ) ) FD_LOG_ERR(( "fd_rpc_client_status() failed" )); + + ulong last_transaction_count = response->result.transaction_count.transaction_count; + + fd_rpc_client_close( rpc_client, request ); + + fd_log_private_shared_lock[ 1 ]++; + + long then = fd_log_wallclock() + 1000000000L; + for( ulong i=0; i<3UL; i++ ) { + long now = fd_log_wallclock(); + + long dt = fd_long_if( now=0L ); + fd_rpc_client_response_t * response = fd_rpc_client_status( rpc_client, request, 1 ); + if( FD_UNLIKELY( response->status!=FD_RPC_CLIENT_SUCCESS ) ) FD_LOG_ERR(( "fd_rpc_client_status() failed" )); + + FD_LOG_NOTICE(( "TPS: %lu", response->result.transaction_count.transaction_count - last_transaction_count )); + last_transaction_count = response->result.transaction_count.transaction_count; + + fd_rpc_client_close( rpc_client, request ); + + then += 1000000000L; + } + + return 0; +} + +static int +clone_child( config_t * const config, + int (*fn)(void *), + int * out_pipefd ) { + int pipefd[2]; + if( FD_UNLIKELY( pipe2( pipefd, O_CLOEXEC | O_NONBLOCK ) ) ) FD_LOG_ERR(( "pipe2() failed (%i-%s)", errno, fd_io_strerror( errno ) )); + + void * stack = fd_tile_private_stack_new( 0, 65535UL ); + if( FD_UNLIKELY( !stack ) ) FD_LOG_ERR(( "unable to create a stack for boot process" )); + + int bencher_pid = clone( fn, (uchar *)stack + FD_TILE_PRIVATE_STACK_SZ, 0, config ); + if( FD_UNLIKELY( bencher_pid<0 ) ) FD_LOG_ERR(( "clone() failed (%i-%s)", errno, fd_io_strerror( errno ) )); + + if( FD_UNLIKELY( close( pipefd[ 1 ] ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) )); + *out_pipefd = pipefd[ 0 ]; + return bencher_pid; +} + +void +bench_cmd_fn( args_t * args, + config_t * const config ) { + (void)args; + + if( FD_LIKELY( !args->dev.no_configure ) ) { + args_t configure_args = { + .configure.command = CONFIGURE_CMD_INIT, + }; + for( ulong i=0; iname, &config->topo, FD_SHMEM_JOIN_MODE_READ_ONLY ); + fd_topo_fill( &config->topo, FD_TOPO_FILL_MODE_JOIN ); + + fd_topo_t * topo = &config->topo; + for( ulong tile_idx=0UL; tile_idxtile_cnt; tile_idx++ ) { + for( ulong in_idx=0UL; in_idxtiles[ tile_idx ].in_cnt; in_idx++ ) { + fd_topo_link_t * link = &topo->links[ topo->tiles[ tile_idx ].in_link_id[ in_idx ] ]; + char * producer; + switch( link->kind ) { + /* Special case Solana produced link names for now since we can't find them + in the topology. */ + case FD_TOPO_LINK_KIND_POH_TO_SHRED: { + producer = "poh"; + break; + } + case FD_TOPO_LINK_KIND_STAKE_TO_OUT: { + producer = "stakes"; + break; + } + case FD_TOPO_LINK_KIND_GOSSIP_TO_PACK: { + producer = "gossip"; + break; + } + case FD_TOPO_LINK_KIND_CRDS_TO_SHRED: { + producer = "crds"; + break; + } + default: { + ulong producer_tile_id = fd_topo_find_link_producer( topo, link ); + FD_TEST( producer_tile_id != ULONG_MAX ); + producer = fd_topo_tile_kind_str( topo->tiles[ producer_tile_id ].kind ); + } + } + + ulong const * in_metrics = (ulong const *)fd_metrics_link_in( topo->tiles[ tile_idx ].metrics, in_idx ); + + ulong producer_id = fd_topo_find_link_producer( topo, link ); + ulong const * out_metrics = NULL; + if( FD_LIKELY( producer_id!=ULONG_MAX && topo->tiles[ tile_idx ].in_link_reliable[ in_idx ] ) ) { + fd_topo_tile_t * producer = &topo->tiles[ producer_id ]; + ulong out_idx; + for( out_idx=0UL; out_idxout_cnt; out_idx++ ) { + if( producer->out_link_id[ out_idx ]==link->id ) break; + } + out_metrics = fd_metrics_link_out( producer->metrics, out_idx ); + } + + printf( " %7s->%-7s", producer, fd_topo_tile_kind_str( topo->tiles[ tile_idx ].kind ) ); + printf( " | %10lu", in_metrics[ FD_METRICS_COUNTER_LINK_OVERRUN_POLLING_COUNT_OFF ] ); + printf( " | %10lu", in_metrics[ FD_METRICS_COUNTER_LINK_OVERRUN_READING_COUNT_OFF ] ); + printf( " | %10lu", out_metrics ? out_metrics[ FD_METRICS_COUNTER_LINK_SLOW_COUNT_OFF ] : 0UL ); + + fd_frag_meta_t const * mcache = topo->links[ topo->tiles[ tile_idx ].in_link_id[ in_idx ] ].mcache; + ulong const * seq = (ulong const *)fd_mcache_seq_laddr_const( mcache ); + printf( " | %10lu", fd_mcache_seq_query( seq ) ); + + ulong const * fseq = topo->tiles[ tile_idx ].in_link_fseq[ in_idx ]; + printf( " | %10lu\n", fd_fseq_query( fseq ) ); + } + } +} diff --git a/src/app/fddev/configure/kill.c b/src/app/fddev/configure/kill.c index c53d606cb0..daa5bdb2b6 100644 --- a/src/app/fddev/configure/kill.c +++ b/src/app/fddev/configure/kill.c @@ -24,11 +24,15 @@ cmdline( char * buf, snprintf1( path, PATH_MAX, "/proc/%lu/cmdline", pid ); FILE * fp = fopen( path, "r" ); - if( FD_UNLIKELY( !fp ) ) FD_LOG_ERR(( "error opening `/proc/self/cmdline` (%i-%s)", errno, fd_io_strerror( errno ) )); + if( FD_UNLIKELY( !fp && errno==ENOENT ) ) { + buf[ 0 ] = '\0'; + return; + } + if( FD_UNLIKELY( !fp ) ) FD_LOG_ERR(( "error opening `/proc/%lu/cmdline` (%i-%s)", pid, errno, fd_io_strerror( errno ) )); ulong read = fread( buf, 1, len - 1, fp ); - if( FD_UNLIKELY( ferror( fp ) ) ) FD_LOG_ERR(( "error reading `/proc/self/cmdline` (%i-%s)", errno, fd_io_strerror( errno ) )); - if( FD_UNLIKELY( fclose( fp ) ) ) FD_LOG_ERR(( "error closing `/proc/self/cmdline` (%i-%s)", errno, fd_io_strerror( errno ) )); + if( FD_UNLIKELY( ferror( fp ) ) ) FD_LOG_ERR(( "error reading `/proc/%lu/cmdline` (%i-%s)", pid, errno, fd_io_strerror( errno ) )); + if( FD_UNLIKELY( fclose( fp ) ) ) FD_LOG_ERR(( "error closing `/proc/%lu/cmdline` (%i-%s)", pid, errno, fd_io_strerror( errno ) )); buf[ read ] = '\0'; } @@ -59,7 +63,8 @@ maybe_kill( config_t * const config, char path[ PATH_MAX ]; snprintf1( path, PATH_MAX, "/proc/%lu/maps", pid ); FILE * fp = fopen( path, "r" ); - if( FD_UNLIKELY( !fp && errno!=ENOENT ) ) FD_LOG_ERR(( "error opening `%s` (%i-%s)", path, errno, fd_io_strerror( errno ) )); + if( FD_UNLIKELY( !fp && errno==ENOENT ) ) return 0; + else if( FD_UNLIKELY( !fp ) ) FD_LOG_ERR(( "error opening `%s` (%i-%s)", path, errno, fd_io_strerror( errno ) )); char line[ 4096 ]; while( FD_LIKELY( fgets( line, 4096, fp ) ) ) { diff --git a/src/app/fddev/fddev.h b/src/app/fddev/fddev.h index cc19e18195..61be5615a2 100644 --- a/src/app/fddev/fddev.h +++ b/src/app/fddev/fddev.h @@ -43,4 +43,18 @@ void txn_cmd_fn( args_t * args, config_t * const config ); +void +bench_cmd_perm( args_t * args, + fd_caps_ctx_t * caps, + config_t * const config ); + +void +bench_cmd_args( int * pargc, + char *** pargv, + args_t * args); + +void +bench_cmd_fn( args_t * args, + config_t * const config ); + #endif /* HEADER_fd_src_app_fddev_fddev_h */ diff --git a/src/app/fddev/main.c b/src/app/fddev/main.c index d28bb32ad8..bb27fd0e7b 100644 --- a/src/app/fddev/main.c +++ b/src/app/fddev/main.c @@ -31,9 +31,10 @@ configure_stage_t * STAGES[ CONFIGURE_STAGE_COUNT ] = { }; static action_t DEV_ACTIONS[] = { - { .name = "dev", .args = dev_cmd_args, .fn = dev_cmd_fn, .perm = dev_cmd_perm }, - { .name = "dev1", .args = dev1_cmd_args, .fn = dev1_cmd_fn, .perm = dev_cmd_perm }, - { .name = "txn", .args = txn_cmd_args, .fn = txn_cmd_fn, .perm = txn_cmd_perm }, + { .name = "dev", .args = dev_cmd_args, .fn = dev_cmd_fn, .perm = dev_cmd_perm }, + { .name = "dev1", .args = dev1_cmd_args, .fn = dev1_cmd_fn, .perm = dev_cmd_perm }, + { .name = "txn", .args = txn_cmd_args, .fn = txn_cmd_fn, .perm = txn_cmd_perm }, + { .name = "bench", .args = bench_cmd_args, .fn = bench_cmd_fn, .perm = bench_cmd_perm }, }; #define MAX_ARGC 32