LCOV - code coverage report
Current view: top level - app/firedancer-dev/commands - backtest.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 348 0.0 %
Date: 2026-10-22 18:10:43 Functions: 0 7 0.0 %

          Line data    Source code
       1             : /* The backtest topology exercises offline replay with mocked consensus.
       2             : 
       3             :    The smaller topology is:
       4             :            repair_out            replay_execrp
       5             :    backtest-------------->replay------------->execrp
       6             :      ^                    |^ | ^                |
       7             :      |____________________|| | |________________|
       8             :           replay_slot      | |   execrp_replay
       9             :                            | |------------------------------>no consumer
      10             :     genesi------------------  replay_epoch
      11             :                 genesi_out
      12             : */
      13             : #define _GNU_SOURCE
      14             : #include "../../firedancer/topology.h"
      15             : #include "../../shared/commands/configure/configure.h"
      16             : #include "../../shared/commands/run/run.h" /* initialize_workspaces */
      17             : #include "../../shared/commands/watch/watch.h"
      18             : #include "../../shared/fd_config.h" /* config_t */
      19             : #include "../../shared/fd_config_json.h"
      20             : #include "../../../disco/topo/fd_topob.h"
      21             : #include "../../../disco/topo/fd_cpu_topo.h"
      22             : #include "../../../util/pod/fd_pod_format.h"
      23             : #include "../../../disco/gui/fd_gui_config_parse.h"
      24             : #include "../../../disco/diag/fd_diag_tile.h"
      25             : #include "../../../discof/genesis/fd_genesi_tile.h"
      26             : #include "../../../discof/replay/fd_replay_tile.h"
      27             : #include "../../../discof/replay/fd_sched.h"
      28             : #include "../../../discof/restore/fd_snapct_tile.h"
      29             : #include "../../../discof/restore/utils/fd_ssctrl.h"
      30             : #include "../../../discof/restore/utils/fd_ssmsg.h"
      31             : #include "../../../discof/rotor/fd_rotor_tile.h"
      32             : #include "../../../discof/votor/fd_votor_tile.h"
      33             : #include "../../../discof/replay/fd_execrp.h"
      34             : #include "../../../disco/shred/fd_shred_tile.h"
      35             : #include "../../../flamenco/capture/fd_capture_ctx.h"
      36             : #include "../../../disco/pack/fd_pack_cost.h"
      37             : #include "../../../flamenco/runtime/fd_cost_tracker.h"
      38             : 
      39             : #include <errno.h>
      40             : #include <stdlib.h> /* getenv */
      41             : #include <unistd.h>
      42             : #include <fcntl.h>
      43             : 
      44             : extern fd_topo_obj_callbacks_t * CALLBACKS[];
      45             : fd_topo_run_tile_t fdctl_tile_run( fd_topo_tile_t const * tile );
      46             : 
      47             : void
      48             : backtest_cmd_args( int *    pargc,
      49             :                    char *** pargv,
      50           0 :                    args_t * args ) {
      51           0 :   args->backtest.no_watch = fd_env_strip_cmdline_contains( pargc, pargv, "--no-watch" );
      52           0 : }
      53             : 
      54             : static void
      55           0 : backtest_topo( config_t * config ) {
      56             : 
      57           0 :   config->development.sandbox  = 0;
      58           0 :   config->development.no_clone = 1;
      59             : 
      60           0 :   config->firedancer.layout.resolv_tile_count = 0;
      61             : 
      62           0 :   ulong execrp_tile_cnt = config->firedancer.layout.execrp_tile_count;
      63           0 :   ulong snapdc_tile_cnt = config->firedancer.layout.snapdc_tile_count;
      64           0 :   ulong snapin_tile_cnt = config->firedancer.layout.snapin_tile_count;
      65             : 
      66           0 :   int disable_snap_loader      = !config->gossip.entrypoints_cnt;
      67           0 :   int solcap_enabled           = strlen( config->capture.solcap_capture )>0;
      68           0 :   int telemetry_enabled        = config->telemetry && strcmp( config->tiles.event.url, "" );
      69             : 
      70           0 :   fd_topo_t * topo = { fd_topob_new( &config->topo, config->name ) };
      71           0 :   topo->max_page_size = fd_cstr_to_shmem_page_sz( config->hugetlbfs.max_page_size );
      72           0 :   topo->gigantic_page_threshold = config->hugetlbfs.gigantic_page_threshold_mib << 20;
      73             : 
      74           0 :   ushort parsed_tile_to_cpu[ FD_TILE_MAX ];
      75             :   /* Unassigned tiles will be floating, unless auto topology is enabled. */
      76           0 :   for( ulong i=0UL; i<FD_TILE_MAX; i++ ) parsed_tile_to_cpu[ i ] = USHORT_MAX;
      77             : 
      78           0 :   int is_auto_affinity = !strcmp( config->layout.affinity, "auto" );
      79             : 
      80           0 :   fd_topo_cpus_t cpus[1];
      81           0 :   fd_topo_cpus_init( cpus );
      82             : 
      83           0 :   ulong affinity_tile_cnt = 0UL;
      84           0 :   if( FD_LIKELY( !is_auto_affinity ) ) affinity_tile_cnt = fd_topob_parse_affinity_cstr( config->layout.affinity, parsed_tile_to_cpu, 1, 1 );
      85             : 
      86           0 :   ulong tile_to_cpu[ FD_TILE_MAX ] = {0};
      87           0 :   for( ulong i=0UL; i<affinity_tile_cnt; i++ ) {
      88           0 :     ushort cpu = (ushort)( parsed_tile_to_cpu[ i ] & ~FD_TOPOB_CPU_SHARED );
      89           0 :     if( FD_UNLIKELY( parsed_tile_to_cpu[ i ]!=USHORT_MAX && cpu>=cpus->cpu_cnt ) )
      90           0 :       FD_LOG_ERR(( "[layout.affinity] specifies CPU %hu but the system only has %lu CPUs", cpu, cpus->cpu_cnt ));
      91           0 :     tile_to_cpu[ i ] = fd_ulong_if( parsed_tile_to_cpu[ i ]==USHORT_MAX, ULONG_MAX, (ulong)parsed_tile_to_cpu[ i ] );
      92           0 :   }
      93             : 
      94           0 :   ulong cpu_idx = 1;
      95           0 : # define NEXT_CPU  ( is_auto_affinity ? cpu_idx++  : tile_to_cpu[ topo->tile_cnt ] )
      96           0 : # define FLOAT_CPU ( is_auto_affinity ? ULONG_MAX  : tile_to_cpu[ topo->tile_cnt ] )
      97             : 
      98           0 :   fd_topob_wksp( topo, "metric" );
      99           0 :   fd_topob_wksp( topo, "metric_in" );
     100           0 :   fd_topob_tile( topo, "metric", "metric", "metric_in", FLOAT_CPU, 0, 0, 1 );
     101             : 
     102           0 :   if( FD_UNLIKELY( !strcmp( config->firedancer.layout.mode, "efficient" ) ) ) fd_topob_sleep( topo, "metric_in", NEXT_CPU );
     103             : 
     104           0 :   fd_topob_wksp( topo, "backt" );
     105           0 :   fd_topo_tile_t * backt_tile = fd_topob_tile( topo, "backt", "backt", "metric_in", NEXT_CPU, 0, 0, 0 );
     106             : 
     107           0 :   fd_topob_wksp( topo, "replay" );
     108           0 :   fd_topo_tile_t * replay_tile = fd_topob_tile( topo, "replay", "replay", "metric_in", NEXT_CPU, 1, 0, 0 );
     109             : 
     110           0 :   fd_topo_obj_t * node_info_obj = fd_topob_obj( topo, "node_info", "replay" );
     111           0 :   fd_topob_tile_uses( topo, replay_tile, node_info_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
     112           0 :   FD_TEST( fd_pod_insertf_ulong( topo->props, node_info_obj->id, "node_info" ) );
     113             : 
     114           0 :   fd_topob_wksp( topo, "accdb" )->core_dump_level = FD_TOPO_CORE_DUMP_LEVEL_FULL;
     115           0 :   fd_topo_tile_t * accdb_tile = fd_topob_tile( topo, "accdb", "accdb", "metric_in", NEXT_CPU, 0, 0, 0 );
     116             : 
     117           0 :   fd_topob_wksp( topo, "progcache" );
     118           0 :   setup_topo_progcache( topo, "progcache",
     119           0 :       config->firedancer.runtime.max_live_slots,
     120           0 :       config->firedancer.runtime.program_cache_size_mib<<20 );
     121           0 :   ulong progcache_obj_id; FD_TEST( (progcache_obj_id = fd_pod_query_ulong( topo->props, "progcache", ULONG_MAX ) )!=ULONG_MAX );
     122           0 :   fd_topob_tile_uses( topo, replay_tile, &topo->objs[ progcache_obj_id ], FD_SHMEM_JOIN_MODE_READ_WRITE );
     123           0 :   fd_topob_tile_uses( topo, accdb_tile,  &topo->objs[ progcache_obj_id ], FD_SHMEM_JOIN_MODE_READ_WRITE );
     124             : 
     125           0 :   fd_topob_wksp( topo, "accdb_data" );
     126           0 :   fd_topo_obj_t * accdb_obj = setup_topo_accdb( topo, "accdb_data",
     127           0 :       config->firedancer.accounts.max_accounts,
     128           0 :       config->firedancer.runtime.max_live_slots,
     129           0 :       FD_RUNTIME_MAX_ACC_WRITES_PER_SLOT,
     130           0 :       8192UL,
     131           0 :       config->development.accdb.partition_size_gib*(1UL<<30UL),
     132           0 :       config->firedancer.accounts.cache_size_gib*(1UL<<30UL),
     133           0 :       config->tiles.bundle.enabled,
     134           0 :       execrp_tile_cnt+2UL+fd_ulong_if( disable_snap_loader, 1UL, snapin_tile_cnt ),
     135           0 :       0UL );
     136           0 :   FD_TEST( fd_pod_insertf_ulong( topo->props, accdb_obj->id, "accdb" ) );
     137             : 
     138             :   /**********************************************************************/
     139             :   /* Add the executor tiles to topo                                     */
     140             :   /**********************************************************************/
     141           0 :   fd_topob_wksp( topo, "execrp" );
     142           0 :   #define FOR(cnt) for( ulong i=0UL; i<cnt; i++ )
     143           0 :   FOR(execrp_tile_cnt) fd_topob_tile( topo, "execrp", "execrp", "metric_in", NEXT_CPU, 0, 0, 0 );
     144             : 
     145             :   /**********************************************************************/
     146             :   /* Add the capture tile to topo                                       */
     147             :   /**********************************************************************/
     148           0 :   if( solcap_enabled ) {
     149           0 :     fd_topob_wksp( topo, "solcap" );
     150           0 :     fd_topob_tile( topo, "solcap", "solcap", "metric_in", NEXT_CPU, 0, 0, 0 );
     151           0 :   }
     152             : 
     153           0 :   fd_topob_wksp( topo, "diag" );
     154           0 :   fd_topob_tile( topo, "diag", "diag", "metric_in", FLOAT_CPU, 0, 0, 0 );
     155             : 
     156             :   /**********************************************************************/
     157             :   /* Add the snapshot tiles to topo                                       */
     158             :   /**********************************************************************/
     159           0 :   if( FD_UNLIKELY( !disable_snap_loader ) ) {
     160           0 :     fd_topob_wksp( topo, "snapct" );
     161           0 :     fd_topob_wksp( topo, "snapld" );
     162           0 :     fd_topob_wksp( topo, "snapdc" );
     163           0 :     fd_topob_wksp( topo, "snapin" );
     164             : 
     165           0 :     fd_topo_tile_t * snapct_tile = fd_topob_tile( topo, "snapct",  "snapct",  "metric_in",  NEXT_CPU, 0, 0, 1 );
     166           0 :     fd_topo_tile_t * snapld_tile = fd_topob_tile( topo, "snapld",  "snapld",  "metric_in",  NEXT_CPU, 0, 0, 1 );
     167           0 :     FOR(snapdc_tile_cnt)           fd_topob_tile( topo, "snapdc",  "snapdc",  "metric_in",  NEXT_CPU, 0, 0, 0 )->allow_shutdown = 1;
     168           0 :     FOR(snapin_tile_cnt)           fd_topob_tile( topo, "snapin",  "snapin",  "metric_in",  NEXT_CPU, 0, 0, 0 )->allow_shutdown = 1;
     169             : 
     170           0 :     snapct_tile->allow_shutdown = 1;
     171           0 :     snapld_tile->allow_shutdown = 1;
     172           0 :   }
     173             : 
     174           0 :   fd_topob_wksp( topo, "genesi" );
     175           0 :   fd_topo_tile_t * genesi_tile = fd_topob_tile( topo, "genesi",  "genesi",  "metric_in",  NEXT_CPU, 0, 0, 1 );
     176           0 :   genesi_tile->allow_shutdown = 1;
     177             : 
     178           0 :   fd_topob_wksp( topo, "genesi_out" );
     179           0 :   fd_topob_link( topo, "genesi_out", "genesi_out", 1UL,
     180           0 :                  fd_genesi_tile_mtu( config->firedancer.development.genesis.max_file_size_mib<<20 ), 1UL );
     181           0 :   fd_topob_tile_out( topo, "genesi", 0UL, "genesi_out", 0UL );
     182           0 :   fd_topob_tile_in ( topo, "replay", 0UL, "metric_in", "genesi_out", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
     183             : 
     184             :   /**********************************************************************/
     185             :   /* Add the event + sign tiles to topo                                 */
     186             :   /**********************************************************************/
     187           0 :   if( FD_LIKELY( telemetry_enabled ) ) {
     188           0 :     fd_topob_wksp( topo, "sign"       );
     189           0 :     fd_topob_wksp( topo, "event"      );
     190           0 :     fd_topob_wksp( topo, "event_sign" );
     191           0 :     fd_topob_wksp( topo, "sign_event" );
     192             : 
     193           0 :     fd_topob_tile( topo, "sign",  "sign",  "metric_in", NEXT_CPU, 1, 1, 0 );
     194           0 :     fd_topob_tile( topo, "event", "event", "metric_in", NEXT_CPU, 1, 0, 1 );
     195             : 
     196           0 :     topo->resolved_config_json_len = fd_config_to_json( config, topo->resolved_config_json, sizeof(topo->resolved_config_json) );
     197           0 :     topo->user_config_json_len     = fd_config_user_toml_to_json( config, topo->user_config_json, sizeof(topo->user_config_json) );
     198             : 
     199           0 :     fd_topob_link( topo, "event_sign", "event_sign", 128UL, 317UL, 1UL );
     200           0 :     fd_topob_link( topo, "sign_event", "sign_event", 128UL, 64UL,  1UL );
     201             : 
     202           0 :     fd_topob_tile_in ( topo, "sign",  0UL, "metric_in", "event_sign", 0UL, FD_TOPOB_UNRELIABLE, FD_TOPOB_POLLED   );
     203           0 :     fd_topob_tile_out( topo, "event", 0UL,              "event_sign", 0UL                                         );
     204           0 :     fd_topob_tile_in ( topo, "event", 0UL, "metric_in", "sign_event", 0UL, FD_TOPOB_UNRELIABLE, FD_TOPOB_UNPOLLED );
     205           0 :     fd_topob_tile_out( topo, "sign",  0UL,              "sign_event", 0UL                                         );
     206           0 :     fd_topob_tile_in ( topo, "event", 0UL, "metric_in", "genesi_out", 0UL, FD_TOPOB_RELIABLE,   FD_TOPOB_POLLED   );
     207             : 
     208             :     /* No ipecho tile in backtest, so the backtest tile stands in and
     209             :        publishes the shred version to the event tile. */
     210           0 :     fd_topob_wksp( topo, "ipecho_out" );
     211           0 :     fd_topob_link( topo, "ipecho_out", "ipecho_out", 1024UL, 0UL, 1UL );
     212           0 :     fd_topob_tile_out( topo, "backt", 0UL,              "ipecho_out", 0UL                                         );
     213           0 :     fd_topob_tile_in ( topo, "event", 0UL, "metric_in", "ipecho_out", 0UL, FD_TOPOB_RELIABLE,   FD_TOPOB_POLLED   );
     214           0 :   }
     215             : 
     216             :   /**********************************************************************/
     217             :   /* Setup backtest->replay link (repair_out) in topo                 */
     218             :   /**********************************************************************/
     219             : 
     220             :   /* The repair tile is replaced by the backtest tile for the repair to
     221             :      replay link.  The frag interface is a "slice", ie. entry batch,
     222             :      which is provided by the backtest tile, which reads in the entry
     223             :      batches from the CLI-specified source (eg. pcap/pcapng file). */
     224             : 
     225           0 :   fd_topob_wksp( topo, "repair_out" );
     226           0 :   fd_topob_link( topo, "repair_out", "repair_out", 65536UL, sizeof(fd_rotor_replay_fec_t), 1UL );
     227           0 :   fd_topob_tile_in( topo, "replay", 0UL, "metric_in", "repair_out", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
     228           0 :   fd_topob_tile_out( topo, "backt", 0UL, "repair_out", 0UL );
     229             : 
     230             :   /**********************************************************************/
     231             :   /* Setup snapshot links in topo                                       */
     232             :   /**********************************************************************/
     233           0 :   if( FD_LIKELY( !disable_snap_loader ) ) {
     234           0 :     fd_topob_wksp( topo, "snapct_ld"    );
     235           0 :     fd_topob_wksp( topo, "snapld_dc"    );
     236           0 :     fd_topob_wksp( topo, "snapdc_in"    );
     237             : 
     238           0 :     fd_topob_wksp( topo, "snapin_manif" );
     239           0 :     fd_topob_wksp( topo, "snapct_repr"  );
     240             : 
     241           0 :     fd_topob_wksp( topo, "snapin_ct" );
     242             : 
     243           0 :     fd_topob_link( topo, "snapct_ld",    "snapct_ld",    128UL,   sizeof(fd_ssctrl_msg_t),        1UL );
     244           0 :     fd_topob_link( topo, "snapld_dc",    "snapld_dc",    FD_SNAPSHOT_DATA_DEPTH, FD_SNAPSHOT_DATA_MTU,           1UL );
     245           0 :     FOR(snapdc_tile_cnt) fd_topob_link( topo, "snapdc_in", "snapdc_in", FD_SNAPSHOT_DC_IN_DEPTH, FD_SNAPSHOT_DATA_MTU, 1UL );
     246             : 
     247           0 :     fd_topob_link( topo, "snapin_manif", "snapin_manif", 4UL,     sizeof(fd_snapshot_manifest_t), 1UL ); /* TODO: Should be depth 1 or 2 but replay backpressures */
     248           0 :     fd_topob_link( topo, "snapct_repr",  "snapct_repr",  128UL,   0UL,                            1UL )->permit_no_consumers = 1;
     249             : 
     250           0 :     FOR(snapin_tile_cnt) fd_topob_link( topo, "snapin_ct", "snapin_ct", 128UL, 0UL, 1UL );
     251             : 
     252           0 :     FOR(snapin_tile_cnt) fd_topob_tile_in( topo, "snapct", 0UL, "metric_in", "snapin_ct", i, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
     253           0 :     fd_topob_tile_in ( topo, "snapct",  0UL, "metric_in", "snapld_dc",    0UL, FD_TOPOB_RELIABLE,   FD_TOPOB_POLLED );
     254           0 :     fd_topob_tile_out( topo, "snapct",  0UL,              "snapct_ld",    0UL                                       );
     255           0 :     fd_topob_tile_out( topo, "snapct",  0UL,              "snapct_repr",  0UL                                       );
     256           0 :     fd_topob_tile_in ( topo, "snapld",  0UL, "metric_in", "snapct_ld",    0UL, FD_TOPOB_RELIABLE,   FD_TOPOB_POLLED );
     257           0 :     fd_topob_tile_out( topo, "snapld",  0UL,              "snapld_dc",    0UL                                       );
     258           0 :     FOR(snapdc_tile_cnt) fd_topob_tile_in ( topo, "snapdc", i,   "metric_in", "snapld_dc", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
     259           0 :     FOR(snapdc_tile_cnt) fd_topob_tile_out( topo, "snapdc", i,               "snapdc_in", i                                         );
     260           0 :     for( ulong t=0UL; t<snapin_tile_cnt; t++ ) {
     261           0 :       FOR(snapdc_tile_cnt) fd_topob_tile_in( topo, "snapin", t, "metric_in", "snapdc_in", i, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
     262           0 :       fd_topob_tile_out( topo, "snapin", t, "snapin_ct", t );
     263           0 :     }
     264             : 
     265           0 :     fd_topob_tile_out( topo, "snapin",  0UL,              "snapin_manif", 0UL                                       );
     266           0 :     fd_topob_tile_in ( topo, "replay",  0UL, "metric_in", "snapin_manif", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED   );
     267           0 :   }
     268             : 
     269             :   /**********************************************************************/
     270             :   /* More backtest->replay links in topo                                */
     271             :   /**********************************************************************/
     272             : 
     273             :   /* Under Alpenglow the votor tile is likewise replaced by the backtest
     274             :      tile, which certifies each rooted slot as fast finalized so replay
     275             :      roots it.  Replay keys blocks by the synthetic ids the backtest tile
     276             :      feeds it, so the finalization certs in the ledger's footers can not
     277             :      root anything here. */
     278           0 :   fd_topob_wksp( topo, "votor_out" );
     279           0 :   fd_topob_link( topo, "votor_out", "votor_out", 1024UL, sizeof(fd_votor_msg_t), 1UL );
     280           0 :   fd_topob_tile_in( topo, "replay", 0UL, "metric_in", "votor_out", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
     281           0 :   fd_topob_tile_out( topo, "backt", 0UL, "votor_out", 0UL );
     282             : 
     283             :   /**********************************************************************/
     284             :   /* Setup replay->stake/send/poh links in topo w/o consumers         */
     285             :   /**********************************************************************/
     286           0 :   fd_topob_wksp( topo, "replay_epoch"    );
     287           0 :   fd_topob_link( topo, "replay_epoch", "replay_epoch", 32UL, FD_EPOCH_OUT_MTU, 1UL );
     288           0 :   fd_topob_tile_out( topo, "replay", 0UL, "replay_epoch",   0UL );
     289           0 :   topo->links[ replay_tile->out_link_id[ fd_topo_find_tile_out_link( topo, replay_tile, "replay_epoch",   0 ) ] ].permit_no_consumers = 1;
     290             : 
     291             :   /**********************************************************************/
     292             :   /* Setup replay->backtest link (replay_notif) in topo                 */
     293             :   /**********************************************************************/
     294             : 
     295           0 :   fd_topob_wksp( topo, "replay_out" );
     296           0 :   fd_topob_link( topo, "replay_out", "replay_out", 8192UL, sizeof( fd_replay_message_t ), 1UL )->permit_no_consumers = 1;
     297           0 :   fd_topob_tile_out( topo, "replay", 0UL, "replay_out", 0UL );
     298           0 :   fd_topob_wksp( topo, "replay_slot" );
     299           0 :   fd_topob_link( topo, "replay_slot", "replay_slot", 4096UL, sizeof( fd_replay_message_t ), 1UL );
     300           0 :   fd_topob_tile_out( topo, "replay", 0UL, "replay_slot", 0UL );
     301           0 :   fd_topob_tile_in ( topo, "backt", 0UL, "metric_in", "replay_slot", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
     302           0 :   fd_topob_tile_in ( topo, "backt", 0UL, "metric_in", "genesi_out", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
     303           0 :   if( FD_LIKELY( !disable_snap_loader ) ) {
     304           0 :     fd_topob_tile_in ( topo, "backt", 0UL, "metric_in", "snapin_manif", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
     305           0 :   }
     306             : 
     307             :   /**********************************************************************/
     308             :   /* Setup replay->exec link in topo                                    */
     309             :   /**********************************************************************/
     310           0 :   fd_topob_wksp( topo, "replay_execrp" );
     311           0 :   for( ulong i=0UL; i<execrp_tile_cnt; i++ ) {
     312           0 :     fd_topob_link( topo, "replay_execrp", "replay_execrp", 256UL, sizeof(fd_execrp_task_msg_t), 1UL );
     313           0 :     fd_topob_tile_out( topo, "replay", 0UL, "replay_execrp", i );
     314           0 :     fd_topob_tile_in( topo, "execrp", i, "metric_in", "replay_execrp", i, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
     315           0 :   }
     316             : 
     317             :   /**********************************************************************/
     318             :   /* Setup exec->replay links in topo, to send solcap account updates
     319             :      so that they are serialized, and to notify replay tile that a txn
     320             :      has been finalized by the exec tile. */
     321             :   /**********************************************************************/
     322           0 :   fd_topob_wksp( topo, "execrp_replay" );
     323             : 
     324           0 :   FOR(execrp_tile_cnt) fd_topob_link( topo, "execrp_replay", "execrp_replay", 16384UL, sizeof(fd_execrp_task_done_msg_t), 1UL );
     325             : 
     326           0 :   FOR(execrp_tile_cnt) fd_topob_tile_out( topo, "execrp", i, "execrp_replay", i );
     327           0 :   FOR(execrp_tile_cnt) fd_topob_tile_in( topo, "replay", 0UL, "metric_in", "execrp_replay", i, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
     328             : 
     329             :   /**********************************************************************/
     330             :   /* Add the gui tile to topo                                           */
     331             :   /**********************************************************************/
     332             : 
     333             :   /* The gui tile consumes the subset of its usual inputs that exist in
     334             :      the backtest topology (there is no networking, gossip, shred or
     335             :      leader pipeline here).  This is enough for the frontend to show
     336             :      snapshot load and replay progress.  Genesis-mode backtest (no
     337             :      snapshot loader) is not supported: the gui boot-progress sampler
     338             :      requires the snapshot tiles. */
     339           0 :   if( FD_UNLIKELY( config->tiles.gui.enabled && !disable_snap_loader ) ) {
     340           0 :     fd_topob_wksp( topo, "gui" );
     341           0 :     fd_topo_tile_t * gui_tile = fd_topob_tile( topo, "gui", "gui", "metric_in", NEXT_CPU, 1, 0, 1 );
     342             : 
     343           0 :     fd_topob_wksp( topo, "diag_gui" );
     344           0 :     fd_topob_link( topo, "diag_gui", "diag_gui", 4UL, sizeof(fd_diag_system_resources_t), 1UL );
     345           0 :     fd_topob_tile_out( topo, "diag", 0UL, "diag_gui", 0UL );
     346           0 :     fd_topob_tile_in( topo, "gui", 0UL, "metric_in", "diag_gui", 0UL, FD_TOPOB_UNRELIABLE, FD_TOPOB_POLLED );
     347             : 
     348           0 :     fd_topob_wksp( topo, "snapct_gui" );
     349           0 :     fd_topob_wksp( topo, "snapin_gui" );
     350           0 :     fd_topob_link( topo, "snapct_gui", "snapct_gui", 128UL, sizeof(fd_snapct_update_t),            1UL );
     351           0 :     FOR(snapin_tile_cnt) fd_topob_link( topo, "snapin_gui", "snapin_gui", 128UL, FD_GUI_CONFIG_PARSE_MAX_VALID_ACCT_SZ, 1UL );
     352           0 :     fd_topob_tile_out( topo, "snapct", 0UL, "snapct_gui", 0UL );
     353           0 :     FOR(snapin_tile_cnt) fd_topob_tile_out( topo, "snapin", i, "snapin_gui", i );
     354             : 
     355           0 :     /**/                 fd_topob_tile_in( topo, "gui", 0UL, "metric_in", "replay_out",    0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
     356           0 :     /**/                 fd_topob_tile_in( topo, "gui", 0UL, "metric_in", "replay_epoch",  0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
     357           0 :     /**/                 fd_topob_tile_in( topo, "gui", 0UL, "metric_in", "genesi_out",    0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
     358           0 :     FOR(execrp_tile_cnt) fd_topob_tile_in( topo, "gui", 0UL, "metric_in", "execrp_replay", i,   FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
     359           0 :     /**/                 fd_topob_tile_in( topo, "gui", 0UL, "metric_in", "snapct_gui",    0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
     360           0 :     FOR(snapin_tile_cnt) fd_topob_tile_in( topo, "gui", 0UL, "metric_in", "snapin_gui",    i,   FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
     361           0 :     /**/                 fd_topob_tile_in( topo, "gui", 0UL, "metric_in", "snapin_manif",  0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
     362             : 
     363           0 :     fd_topob_tile_uses( topo, gui_tile, accdb_obj, FD_SHMEM_JOIN_MODE_READ_ONLY );
     364           0 :   }
     365             : 
     366             :   /**********************************************************************/
     367             :   /* Setup the shared objs used by replay and exec tiles                */
     368             :   /**********************************************************************/
     369             : 
     370           0 :   if( FD_UNLIKELY( solcap_enabled ) ) {
     371             :     /* 32 sections of SOLCAP_WRITE_ACCOUNT_DATA_MTU bytes each ≈ 4MB.
     372             :        This is done to ideally avoid cache thrashing and allow for all
     373             :        the links to sit on L3 cache. */
     374           0 :     fd_topob_link( topo, "cap_repl", "solcap", 32UL, SOLCAP_WRITE_ACCOUNT_DATA_MTU, 1UL );
     375           0 :     fd_topob_tile_out( topo, "replay", 0UL, "cap_repl", 0UL );
     376           0 :     fd_topob_tile_in( topo, "solcap", 0UL, "metric_in", "cap_repl", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
     377           0 :     FOR(execrp_tile_cnt) fd_topob_link( topo, "cap_execrp", "solcap", 32UL, SOLCAP_WRITE_ACCOUNT_DATA_MTU, 1UL );
     378           0 :     FOR(execrp_tile_cnt) fd_topob_tile_out( topo, "execrp", i, "cap_execrp", i );
     379           0 :     FOR(execrp_tile_cnt) fd_topob_tile_in( topo, "solcap", 0UL, "metric_in", "cap_execrp", i, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
     380           0 :   }
     381             : 
     382           0 :   fd_topob_wksp( topo, "store" );
     383           0 :   fd_topo_link_t const * repair_out_link = &topo->links[ fd_topo_find_link( topo, "repair_out", 0UL ) ];
     384           0 :   ulong fec_sets_per_slot = fd_ulong_if( config->firedancer.development.fixed_fec_sets,
     385           0 :                                          config->limits.max_shreds_per_block/FD_FEC_SHRED_CNT, config->limits.max_shreds_per_block );
     386           0 :   ulong store_fec_max = config->firedancer.runtime.max_live_slots * fec_sets_per_slot + repair_out_link->depth + 1UL;
     387           0 :   ulong store_fec_data_max = fd_ulong_if( config->firedancer.development.fixed_fec_sets, 31840UL, 63985UL );
     388           0 :   fd_topo_obj_t * store_obj = setup_topo_store( topo, "store", store_fec_max, store_fec_data_max, 0UL, config->tiles.shred.shred_cache_size_mib, 0UL, config->limits.max_shreds_per_block, config->paths.shredb, 1UL );
     389           0 :   fd_topob_tile_uses( topo, backt_tile, store_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
     390           0 :   fd_topob_tile_uses( topo, replay_tile, store_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
     391           0 :   FD_TEST( fd_pod_insertf_ulong( topo->props, store_obj->id, "store" ) );
     392             : 
     393           0 :   fd_topob_wksp( topo, "banks" );
     394           0 :   ulong min_bank_cache_cnt = fd_ulong_min( execrp_tile_cnt, FD_SCHED_MAX_STAGING_LANES )+2UL;
     395           0 :   if( FD_UNLIKELY( config->firedancer.runtime.bank_cache_cnt<min_bank_cache_cnt ) ) {
     396           0 :     FD_LOG_ERR(( "Invalid [runtime]: bank_cache_cnt is %lu, but it must be at least %lu (2 more than the smaller of [layout] execrp_tile_count %lu "
     397           0 :                  "and %lu replay staging lanes).  Increase [runtime] bank_cache_cnt.",
     398           0 :                  config->firedancer.runtime.bank_cache_cnt, min_bank_cache_cnt, execrp_tile_cnt, FD_SCHED_MAX_STAGING_LANES ));
     399           0 :   }
     400           0 :   fd_topo_obj_t * banks_obj = setup_topo_banks( topo, "banks", config->firedancer.runtime.max_live_slots, config->firedancer.runtime.bank_cache_cnt, config->firedancer.runtime.stake_delegation_cache_cnt, config->development.bench.max_cost_per_block );
     401           0 :   fd_topob_tile_uses( topo, replay_tile, banks_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
     402           0 :   FOR(execrp_tile_cnt) fd_topob_tile_uses( topo, &topo->tiles[ fd_topo_find_tile( topo, "execrp", i ) ], banks_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
     403           0 :   setup_topo_footer_cache( topo, "banks" );
     404           0 :   if( FD_LIKELY( !disable_snap_loader ) ) {
     405           0 :     FOR(snapin_tile_cnt) fd_topob_tile_uses( topo, &topo->tiles[ fd_topo_find_tile( topo, "snapin", i ) ], banks_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
     406           0 :   }
     407           0 :   FD_TEST( fd_pod_insertf_ulong( topo->props, banks_obj->id, "banks" ) );
     408             : 
     409           0 :   fd_topob_wksp( topo, "txncache"    );
     410           0 :   fd_topo_obj_t * txncache_obj = setup_topo_txncache( topo, "txncache", config->firedancer.runtime.max_live_slots, 2UL*config->limits.max_txn_per_slot );
     411           0 :   fd_topob_tile_uses( topo, replay_tile, txncache_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
     412           0 :   if( FD_LIKELY( !disable_snap_loader ) ) {
     413           0 :     fd_topob_tile_uses( topo, &topo->tiles[ fd_topo_find_tile( topo, "snapin", 0UL ) ], txncache_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
     414           0 :   }
     415           0 :   for( ulong i=0UL; i<execrp_tile_cnt; i++ ) {
     416           0 :     fd_topob_tile_uses( topo, &topo->tiles[ fd_topo_find_tile( topo, "execrp", i ) ], txncache_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
     417           0 :   }
     418           0 :   FD_TEST( fd_pod_insertf_ulong( topo->props, txncache_obj->id, "txncache" ) );
     419             : 
     420             :   /* snapin (snapshot mode) and genesi (genesis mode) are mutually
     421             :      exclusive accdb writers */
     422           0 :   if( FD_LIKELY( !disable_snap_loader ) ) {
     423           0 :     FOR(snapin_tile_cnt) fd_topob_tile_uses( topo, &topo->tiles[ fd_topo_find_tile( topo, "snapin", i ) ], accdb_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
     424           0 :   } else fd_topob_tile_uses( topo, genesi_tile, accdb_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
     425           0 :   fd_topob_tile_uses( topo, accdb_tile,  accdb_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
     426           0 :   fd_topob_tile_uses( topo, replay_tile, accdb_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
     427           0 :   FOR( execrp_tile_cnt ) fd_topob_tile_uses( topo, &topo->tiles[ fd_topo_find_tile( topo, "execrp", i ) ], accdb_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
     428             : 
     429           0 :   if( FD_LIKELY( !disable_snap_loader ) ) {
     430           0 :     fd_topob_wksp( topo, "snapin_shmem" );
     431           0 :     fd_topo_obj_t * shmem_obj = fd_topob_obj( topo, "snapin_shmem", "snapin_shmem" );
     432           0 :     FD_TEST( fd_pod_insertf_ulong( topo->props, shmem_obj->id, "snapin_shmem" ) );
     433           0 :     FOR(snapin_tile_cnt) fd_topob_tile_uses( topo, &topo->tiles[ fd_topo_find_tile( topo, "snapin", i ) ], shmem_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
     434             : 
     435           0 :     fd_topo_obj_t * dc_ticket_obj = fd_topob_obj_named( topo, "fseq", "snapdc", "frame_ticket" );
     436           0 :     FOR(snapdc_tile_cnt) fd_topob_tile_uses( topo, &topo->tiles[ fd_topo_find_tile( topo, "snapdc", i ) ], dc_ticket_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
     437           0 :   }
     438             : 
     439           0 :   for( ulong i=0UL; i<topo->tile_cnt; i++ ) {
     440           0 :     fd_topo_tile_t * tile = &topo->tiles[ i ];
     441           0 :     fd_topo_configure_tile( tile, config );
     442             : 
     443           0 :     if( FD_UNLIKELY( !strcmp( tile->name, "gui" ) ) ) {
     444           0 :       tile->gui.tile_cnt = topo->tile_cnt;
     445           0 :     }
     446             : 
     447           0 :     if( !strcmp( tile->name, "replay" ) ) {
     448           0 :       tile->replay.fec_max = config->firedancer.runtime.max_live_slots * fec_sets_per_slot;
     449           0 :       tile->replay.enable_features_cnt = config->tiles.replay.enable_features_cnt;
     450           0 :       for( ulong i = 0; i < tile->replay.enable_features_cnt; i++ ) {
     451           0 :         fd_cstr_ncpy( tile->replay.enable_features[i], config->tiles.replay.enable_features[i], sizeof(tile->replay.enable_features[i]) );
     452           0 :       }
     453           0 :     }
     454           0 :   }
     455             : 
     456           0 :   if( FD_LIKELY( telemetry_enabled ) ) wire_event_links( topo );
     457             : 
     458           0 :   if( FD_UNLIKELY( !is_auto_affinity ) ) {
     459           0 :     if( FD_UNLIKELY( affinity_tile_cnt<topo->tile_cnt ) )
     460           0 :       FD_LOG_ERR(( "topology has %lu tiles but [layout.affinity] only provides %lu entries", topo->tile_cnt, affinity_tile_cnt ));
     461           0 :     if( FD_UNLIKELY( affinity_tile_cnt>topo->tile_cnt ) )
     462           0 :       FD_LOG_WARNING(( "topology has %lu tiles but [layout.affinity] provides %lu entries, extra entries will be unused", topo->tile_cnt, affinity_tile_cnt ));
     463           0 :   } else {
     464           0 :     ushort blocklist_cores[ FD_TILE_MAX ];
     465           0 :     topo->blocklist_cores_cnt = fd_topob_parse_affinity_cstr( config->layout.blocklist_cores, blocklist_cores, 0, 0 );
     466           0 :     if( FD_UNLIKELY( topo->blocklist_cores_cnt>FD_TILE_MAX ) ) FD_LOG_ERR(( "[layout.blocklist_cores] specifies more than %lu CPUs", FD_TILE_MAX ));
     467           0 :     for( ulong i=0UL; i<topo->blocklist_cores_cnt; i++ ) {
     468           0 :       if( FD_UNLIKELY( blocklist_cores[ i ]==USHORT_MAX ) ) FD_LOG_ERR(( "[layout.blocklist_cores] must not contain `f`" ));
     469           0 :       topo->blocklist_cores_cpu_idx[ i ] = blocklist_cores[ i ];
     470           0 :     }
     471           0 :     fd_topob_auto_layout( topo );
     472           0 :   }
     473             : 
     474           0 :   fd_topob_waker( topo );
     475           0 :   fd_topob_sleep_finish( topo );
     476             : 
     477           0 :   fd_topob_finish( topo, CALLBACKS );
     478           0 : }
     479             : 
     480             : static void
     481           0 : backtest_cmd_topo( config_t * config ) {
     482           0 :   backtest_topo( config );
     483           0 : }
     484             : 
     485             : extern configure_stage_t fd_cfg_stage_keys;
     486             : 
     487             : static args_t
     488           0 : configure_args( void ) {
     489           0 :   args_t args = {
     490           0 :     .configure.command = CONFIGURE_CMD_INIT,
     491           0 :   };
     492             : 
     493             :   /* FD_BACKTEST_SHARED_HOST skips the host-global stages (irq-affinity,
     494             :      kworkers) so multiple backtest instances can share a host. */
     495           0 :   int shared_host = !!getenv( "FD_BACKTEST_SHARED_HOST" );
     496             : 
     497           0 :   ulong stage_idx = 0UL;
     498           0 :   args.configure.stages[ stage_idx++ ] = &fd_cfg_stage_hugetlbfs;
     499           0 :   if( FD_LIKELY( !shared_host ) ) {
     500           0 :     args.configure.stages[ stage_idx++ ] = &fd_cfg_stage_irq_affinity;
     501           0 :     args.configure.stages[ stage_idx++ ] = &fd_cfg_stage_kworkers;
     502           0 :   }
     503           0 :   args.configure.stages[ stage_idx++ ] = &fd_cfg_stage_cpuset;
     504           0 :   args.configure.stages[ stage_idx++ ] = &fd_cfg_stage_snapshots;
     505           0 :   args.configure.stages[ stage_idx++ ] = &fd_cfg_stage_keys;
     506           0 :   args.configure.stages[ stage_idx++ ] = NULL;
     507             : 
     508           0 :   return args;
     509           0 : }
     510             : 
     511             : void
     512             : backtest_cmd_perm( args_t *         args FD_PARAM_UNUSED,
     513             :                    fd_cap_chk_t *   chk,
     514           0 :                    config_t const * config ) {
     515           0 :   args_t c_args = configure_args();
     516           0 :   configure_cmd_perm( &c_args, chk, config );
     517           0 :   run_cmd_perm( NULL, chk, config );
     518           0 : }
     519             : 
     520             : static void
     521           0 : fixup_config( config_t * config ) {
     522             : 
     523             :   /* FIXME Unfortunately, the fdctl boot procedure constructs the
     524             :            topology before parsing command-line arguments.  So, here,
     525             :            we construct the topology again (a third time ... sigh). */
     526           0 :   backtest_topo( config );
     527           0 : }
     528             : 
     529             : static void
     530             : backtest_cmd_fn( args_t *   args,
     531           0 :                  config_t * config ) {
     532           0 :   fixup_config( config );
     533           0 :   args_t c_args = configure_args();
     534           0 :   configure_cmd_fn( &c_args, config );
     535             : 
     536           0 :   initialize_workspaces( config );
     537           0 :   initialize_stacks( config );
     538           0 :   initialize_accdb_fd( config );
     539           0 :   initialize_epoch_credits_fd( config );
     540           0 :   initialize_cost_tracker_fd( config );
     541           0 :   initialize_vote_stakes_fd( config );
     542           0 :   initialize_stake_delegations_fd( config );
     543           0 :   initialize_store_fds( config );
     544           0 :   initialize_snapshot_fds( config );
     545             : 
     546           0 :   fd_topo_join_workspaces( &config->topo, FD_SHMEM_JOIN_MODE_READ_WRITE, FD_TOPO_CORE_DUMP_LEVEL_DISABLED );
     547           0 :   fd_topo_fill( &config->topo );
     548             : 
     549           0 :   args_t watch_args;
     550           0 :   int pipefd[2];
     551           0 :   if( !args->backtest.no_watch ) {
     552           0 :     if( FD_UNLIKELY( pipe2( pipefd, O_NONBLOCK ) ) ) FD_LOG_ERR(( "pipe2() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     553             : 
     554           0 :     watch_args.watch.drain_output_fd = pipefd[0];
     555           0 :     watch_args.watch.full = 1;
     556           0 :     if( FD_UNLIKELY( -1==dup2( pipefd[ 1 ], STDERR_FILENO ) ) ) FD_LOG_ERR(( "dup2() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     557           0 :     if( FD_UNLIKELY( -1==close( pipefd[1] ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     558           0 :   }
     559             : 
     560           0 :   fd_topo_run_single_process( &config->topo, config->uid, config->gid, fdctl_tile_run );
     561           0 :   if( args->backtest.no_watch ) {
     562           0 :     for(;;) pause();
     563           0 :   } else {
     564           0 :     watch_cmd_fn( &watch_args, config );
     565           0 :   }
     566           0 : }
     567             : 
     568             : action_t fd_action_backtest = {
     569             :   .name        = "backtest",
     570             :   .args        = backtest_cmd_args,
     571             :   .fn          = backtest_cmd_fn,
     572             :   .perm        = backtest_cmd_perm,
     573             :   .topo        = backtest_cmd_topo,
     574             :   .description = "Replay a ledger offline through the replay tile with mocked consensus",
     575             : };

Generated by: LCOV version 1.14