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 "../../../disco/repair/fd_repair_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 0 : fd_topob_wksp( topo, "bpf_frames" );
146 0 : fd_topo_obj_t * bpf_frames_obj = setup_topo_bpf_frames( topo, "bpf_frames", config->firedancer.runtime.bpf_shared_frames, config->firedancer.runtime.bpf_local_frames, execrp_tile_cnt );
147 0 : FOR(execrp_tile_cnt) fd_topob_tile_uses( topo, &topo->tiles[ fd_topo_find_tile( topo, "execrp", i ) ], bpf_frames_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
148 :
149 : /**********************************************************************/
150 : /* Add the capture tile to topo */
151 : /**********************************************************************/
152 0 : if( solcap_enabled ) {
153 0 : fd_topob_wksp( topo, "solcap" );
154 0 : fd_topob_tile( topo, "solcap", "solcap", "metric_in", NEXT_CPU, 0, 0, 0 );
155 0 : }
156 :
157 0 : fd_topob_wksp( topo, "diag" );
158 0 : fd_topob_tile( topo, "diag", "diag", "metric_in", FLOAT_CPU, 0, 0, 0 );
159 :
160 : /**********************************************************************/
161 : /* Add the snapshot tiles to topo */
162 : /**********************************************************************/
163 0 : if( FD_UNLIKELY( !disable_snap_loader ) ) {
164 0 : fd_topob_wksp( topo, "snapct" );
165 0 : fd_topob_wksp( topo, "snapld" );
166 0 : fd_topob_wksp( topo, "snapdc" );
167 0 : fd_topob_wksp( topo, "snapin" );
168 :
169 0 : fd_topo_tile_t * snapct_tile = fd_topob_tile( topo, "snapct", "snapct", "metric_in", NEXT_CPU, 0, 0, 1 );
170 0 : fd_topo_tile_t * snapld_tile = fd_topob_tile( topo, "snapld", "snapld", "metric_in", NEXT_CPU, 0, 0, 1 );
171 0 : FOR(snapdc_tile_cnt) fd_topob_tile( topo, "snapdc", "snapdc", "metric_in", NEXT_CPU, 0, 0, 0 )->allow_shutdown = 1;
172 0 : FOR(snapin_tile_cnt) fd_topob_tile( topo, "snapin", "snapin", "metric_in", NEXT_CPU, 0, 0, 0 )->allow_shutdown = 1;
173 :
174 0 : snapct_tile->allow_shutdown = 1;
175 0 : snapld_tile->allow_shutdown = 1;
176 0 : }
177 :
178 0 : fd_topob_wksp( topo, "genesi" );
179 0 : fd_topo_tile_t * genesi_tile = fd_topob_tile( topo, "genesi", "genesi", "metric_in", NEXT_CPU, 0, 0, 1 );
180 0 : genesi_tile->allow_shutdown = 1;
181 :
182 0 : fd_topob_wksp( topo, "genesi_out" );
183 0 : fd_topob_link( topo, "genesi_out", "genesi_out", 1UL,
184 0 : fd_genesi_tile_mtu( config->firedancer.development.genesis.max_file_size_mib<<20 ), 1UL );
185 0 : fd_topob_tile_out( topo, "genesi", 0UL, "genesi_out", 0UL );
186 0 : fd_topob_tile_in ( topo, "replay", 0UL, "metric_in", "genesi_out", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
187 :
188 : /**********************************************************************/
189 : /* Add the event + sign tiles to topo */
190 : /**********************************************************************/
191 0 : if( FD_LIKELY( telemetry_enabled ) ) {
192 0 : fd_topob_wksp( topo, "sign" );
193 0 : fd_topob_wksp( topo, "event" );
194 0 : fd_topob_wksp( topo, "event_sign" );
195 0 : fd_topob_wksp( topo, "sign_event" );
196 :
197 0 : fd_topob_tile( topo, "sign", "sign", "metric_in", NEXT_CPU, 1, 1, 0 );
198 0 : fd_topob_tile( topo, "event", "event", "metric_in", NEXT_CPU, 1, 0, 1 );
199 :
200 0 : topo->resolved_config_json_len = fd_config_to_json( config, topo->resolved_config_json, sizeof(topo->resolved_config_json) );
201 0 : topo->user_config_json_len = fd_config_user_toml_to_json( config, topo->user_config_json, sizeof(topo->user_config_json) );
202 :
203 0 : fd_topob_link( topo, "event_sign", "event_sign", 128UL, 317UL, 1UL );
204 0 : fd_topob_link( topo, "sign_event", "sign_event", 128UL, 64UL, 1UL );
205 :
206 0 : fd_topob_tile_in ( topo, "sign", 0UL, "metric_in", "event_sign", 0UL, FD_TOPOB_UNRELIABLE, FD_TOPOB_POLLED );
207 0 : fd_topob_tile_out( topo, "event", 0UL, "event_sign", 0UL );
208 0 : fd_topob_tile_in ( topo, "event", 0UL, "metric_in", "sign_event", 0UL, FD_TOPOB_UNRELIABLE, FD_TOPOB_UNPOLLED );
209 0 : fd_topob_tile_out( topo, "sign", 0UL, "sign_event", 0UL );
210 0 : fd_topob_tile_in ( topo, "event", 0UL, "metric_in", "genesi_out", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
211 :
212 : /* No ipecho tile in backtest, so the backtest tile stands in and
213 : publishes the shred version to the event tile. */
214 0 : fd_topob_wksp( topo, "ipecho_out" );
215 0 : fd_topob_link( topo, "ipecho_out", "ipecho_out", 1024UL, 0UL, 1UL );
216 0 : fd_topob_tile_out( topo, "backt", 0UL, "ipecho_out", 0UL );
217 0 : fd_topob_tile_in ( topo, "event", 0UL, "metric_in", "ipecho_out", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
218 0 : }
219 :
220 : /**********************************************************************/
221 : /* Setup backtest->replay link (repair_out) in topo */
222 : /**********************************************************************/
223 :
224 : /* The repair tile is replaced by the backtest tile for the repair to
225 : replay link. The frag interface is a "slice", ie. entry batch,
226 : which is provided by the backtest tile, which reads in the entry
227 : batches from the CLI-specified source (eg. pcap/pcapng file). */
228 :
229 0 : fd_topob_wksp( topo, "repair_out" );
230 0 : fd_topob_link( topo, "repair_out", "repair_out", 65536UL, sizeof(fd_repair_out_fec_t), 1UL );
231 0 : fd_topob_tile_in( topo, "replay", 0UL, "metric_in", "repair_out", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
232 0 : fd_topob_tile_out( topo, "backt", 0UL, "repair_out", 0UL );
233 :
234 : /**********************************************************************/
235 : /* Setup snapshot links in topo */
236 : /**********************************************************************/
237 0 : if( FD_LIKELY( !disable_snap_loader ) ) {
238 0 : fd_topob_wksp( topo, "snapct_ld" );
239 0 : fd_topob_wksp( topo, "snapld_dc" );
240 0 : fd_topob_wksp( topo, "snapdc_in" );
241 :
242 0 : fd_topob_wksp( topo, "snapin_manif" );
243 0 : fd_topob_wksp( topo, "snapct_repr" );
244 :
245 0 : fd_topob_wksp( topo, "snapin_ct" );
246 :
247 0 : fd_topob_link( topo, "snapct_ld", "snapct_ld", 128UL, sizeof(fd_ssctrl_msg_t), 1UL );
248 0 : fd_topob_link( topo, "snapld_dc", "snapld_dc", FD_SNAPSHOT_DATA_DEPTH, FD_SNAPSHOT_DATA_MTU, 1UL );
249 0 : FOR(snapdc_tile_cnt) fd_topob_link( topo, "snapdc_in", "snapdc_in", FD_SNAPSHOT_DC_IN_DEPTH, FD_SNAPSHOT_DATA_MTU, 1UL );
250 :
251 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 */
252 0 : fd_topob_link( topo, "snapct_repr", "snapct_repr", 128UL, 0UL, 1UL )->permit_no_consumers = 1;
253 :
254 0 : FOR(snapin_tile_cnt) fd_topob_link( topo, "snapin_ct", "snapin_ct", 128UL, 0UL, 1UL );
255 :
256 0 : FOR(snapin_tile_cnt) fd_topob_tile_in( topo, "snapct", 0UL, "metric_in", "snapin_ct", i, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
257 0 : fd_topob_tile_in ( topo, "snapct", 0UL, "metric_in", "snapld_dc", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
258 0 : fd_topob_tile_out( topo, "snapct", 0UL, "snapct_ld", 0UL );
259 0 : fd_topob_tile_out( topo, "snapct", 0UL, "snapct_repr", 0UL );
260 0 : fd_topob_tile_in ( topo, "snapld", 0UL, "metric_in", "snapct_ld", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
261 0 : fd_topob_tile_out( topo, "snapld", 0UL, "snapld_dc", 0UL );
262 0 : FOR(snapdc_tile_cnt) fd_topob_tile_in ( topo, "snapdc", i, "metric_in", "snapld_dc", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
263 0 : FOR(snapdc_tile_cnt) fd_topob_tile_out( topo, "snapdc", i, "snapdc_in", i );
264 0 : for( ulong t=0UL; t<snapin_tile_cnt; t++ ) {
265 0 : FOR(snapdc_tile_cnt) fd_topob_tile_in( topo, "snapin", t, "metric_in", "snapdc_in", i, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
266 0 : fd_topob_tile_out( topo, "snapin", t, "snapin_ct", t );
267 0 : }
268 :
269 0 : fd_topob_tile_out( topo, "snapin", 0UL, "snapin_manif", 0UL );
270 0 : fd_topob_tile_in ( topo, "replay", 0UL, "metric_in", "snapin_manif", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
271 0 : }
272 :
273 : /**********************************************************************/
274 : /* More backtest->replay links in topo */
275 : /**********************************************************************/
276 :
277 : /* Under Alpenglow the votor tile is likewise replaced by the backtest
278 : tile, which certifies each rooted slot as fast finalized so replay
279 : roots it. Replay keys blocks by the synthetic ids the backtest tile
280 : feeds it, so the finalization certs in the ledger's footers can not
281 : root anything here. */
282 0 : fd_topob_wksp( topo, "votor_out" );
283 0 : fd_topob_link( topo, "votor_out", "votor_out", 1024UL, sizeof(fd_votor_msg_t), 1UL );
284 0 : fd_topob_tile_in( topo, "replay", 0UL, "metric_in", "votor_out", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
285 0 : fd_topob_tile_out( topo, "backt", 0UL, "votor_out", 0UL );
286 :
287 : /**********************************************************************/
288 : /* Setup replay->stake/send/poh links in topo w/o consumers */
289 : /**********************************************************************/
290 0 : fd_topob_wksp( topo, "replay_epoch" );
291 0 : fd_topob_link( topo, "replay_epoch", "replay_epoch", 32UL, FD_EPOCH_OUT_MTU, 1UL );
292 0 : fd_topob_tile_out( topo, "replay", 0UL, "replay_epoch", 0UL );
293 0 : topo->links[ replay_tile->out_link_id[ fd_topo_find_tile_out_link( topo, replay_tile, "replay_epoch", 0 ) ] ].permit_no_consumers = 1;
294 :
295 : /**********************************************************************/
296 : /* Setup replay->backtest link (replay_notif) in topo */
297 : /**********************************************************************/
298 :
299 0 : fd_topob_wksp( topo, "replay_out" );
300 0 : fd_topob_link( topo, "replay_out", "replay_out", 8192UL, sizeof( fd_replay_message_t ), 1UL )->permit_no_consumers = 1;
301 0 : fd_topob_tile_out( topo, "replay", 0UL, "replay_out", 0UL );
302 0 : fd_topob_wksp( topo, "replay_slot" );
303 0 : fd_topob_link( topo, "replay_slot", "replay_slot", 4096UL, sizeof( fd_replay_message_t ), 1UL );
304 0 : fd_topob_tile_out( topo, "replay", 0UL, "replay_slot", 0UL );
305 0 : fd_topob_tile_in ( topo, "backt", 0UL, "metric_in", "replay_slot", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
306 0 : fd_topob_tile_in ( topo, "backt", 0UL, "metric_in", "genesi_out", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
307 0 : if( FD_LIKELY( !disable_snap_loader ) ) {
308 0 : fd_topob_tile_in ( topo, "backt", 0UL, "metric_in", "snapin_manif", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
309 0 : }
310 :
311 : /**********************************************************************/
312 : /* Setup replay->exec link in topo */
313 : /**********************************************************************/
314 0 : fd_topob_wksp( topo, "replay_execrp" );
315 0 : for( ulong i=0UL; i<execrp_tile_cnt; i++ ) {
316 0 : fd_topob_link( topo, "replay_execrp", "replay_execrp", 256UL, sizeof(fd_execrp_task_msg_t), 1UL );
317 0 : fd_topob_tile_out( topo, "replay", 0UL, "replay_execrp", i );
318 0 : fd_topob_tile_in( topo, "execrp", i, "metric_in", "replay_execrp", i, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
319 0 : }
320 :
321 : /**********************************************************************/
322 : /* Setup exec->replay task completion links in topo */
323 : /**********************************************************************/
324 0 : fd_topob_wksp( topo, "execrp_replay" );
325 :
326 0 : FOR(execrp_tile_cnt) fd_topob_link( topo, "execrp_replay", "execrp_replay", 16384UL, sizeof(fd_execrp_task_done_msg_t), 1UL );
327 :
328 0 : FOR(execrp_tile_cnt) fd_topob_tile_out( topo, "execrp", i, "execrp_replay", i );
329 0 : FOR(execrp_tile_cnt) fd_topob_tile_in( topo, "replay", 0UL, "metric_in", "execrp_replay", i, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
330 :
331 : /**********************************************************************/
332 : /* Add the gui tile to topo */
333 : /**********************************************************************/
334 :
335 : /* The gui tile consumes the subset of its usual inputs that exist in
336 : the backtest topology (there is no networking, gossip, shred or
337 : leader pipeline here). This is enough for the frontend to show
338 : snapshot load and replay progress. Genesis-mode backtest (no
339 : snapshot loader) is not supported: the gui boot-progress sampler
340 : requires the snapshot tiles. */
341 0 : if( FD_UNLIKELY( config->tiles.gui.enabled && !disable_snap_loader ) ) {
342 0 : fd_topob_wksp( topo, "gui" );
343 0 : fd_topo_tile_t * gui_tile = fd_topob_tile( topo, "gui", "gui", "metric_in", NEXT_CPU, 1, 0, 1 );
344 :
345 0 : fd_topob_wksp( topo, "diag_gui" );
346 0 : fd_topob_link( topo, "diag_gui", "diag_gui", 4UL, sizeof(fd_diag_system_resources_t), 1UL );
347 0 : fd_topob_tile_out( topo, "diag", 0UL, "diag_gui", 0UL );
348 0 : fd_topob_tile_in( topo, "gui", 0UL, "metric_in", "diag_gui", 0UL, FD_TOPOB_UNRELIABLE, FD_TOPOB_POLLED );
349 :
350 0 : fd_topob_wksp( topo, "snapct_gui" );
351 0 : fd_topob_wksp( topo, "snapin_gui" );
352 0 : fd_topob_link( topo, "snapct_gui", "snapct_gui", 128UL, sizeof(fd_snapct_update_t), 1UL );
353 0 : FOR(snapin_tile_cnt) fd_topob_link( topo, "snapin_gui", "snapin_gui", 128UL, FD_GUI_CONFIG_PARSE_MAX_VALID_ACCT_SZ, 1UL );
354 0 : fd_topob_tile_out( topo, "snapct", 0UL, "snapct_gui", 0UL );
355 0 : FOR(snapin_tile_cnt) fd_topob_tile_out( topo, "snapin", i, "snapin_gui", i );
356 :
357 0 : /**/ fd_topob_tile_in( topo, "gui", 0UL, "metric_in", "replay_out", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
358 0 : /**/ fd_topob_tile_in( topo, "gui", 0UL, "metric_in", "replay_epoch", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
359 0 : /**/ fd_topob_tile_in( topo, "gui", 0UL, "metric_in", "genesi_out", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
360 0 : FOR(execrp_tile_cnt) fd_topob_tile_in( topo, "gui", 0UL, "metric_in", "execrp_replay", i, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
361 0 : /**/ fd_topob_tile_in( topo, "gui", 0UL, "metric_in", "snapct_gui", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
362 0 : FOR(snapin_tile_cnt) fd_topob_tile_in( topo, "gui", 0UL, "metric_in", "snapin_gui", i, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
363 0 : /**/ fd_topob_tile_in( topo, "gui", 0UL, "metric_in", "snapin_manif", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
364 :
365 0 : fd_topob_tile_uses( topo, gui_tile, accdb_obj, FD_SHMEM_JOIN_MODE_READ_ONLY );
366 0 : }
367 :
368 : /**********************************************************************/
369 : /* Setup the shared objs used by replay and exec tiles */
370 : /**********************************************************************/
371 :
372 0 : if( FD_UNLIKELY( solcap_enabled ) ) {
373 : /* 32 sections of SOLCAP_WRITE_ACCOUNT_DATA_MTU bytes each ≈ 4MB.
374 : This is done to ideally avoid cache thrashing and allow for all
375 : the links to sit on L3 cache. */
376 0 : fd_topob_link( topo, "cap_repl", "solcap", 32UL, SOLCAP_WRITE_ACCOUNT_DATA_MTU, 1UL );
377 0 : fd_topob_tile_out( topo, "replay", 0UL, "cap_repl", 0UL );
378 0 : fd_topob_tile_in( topo, "solcap", 0UL, "metric_in", "cap_repl", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
379 0 : FOR(execrp_tile_cnt) fd_topob_link( topo, "cap_execrp", "solcap", 32UL, SOLCAP_WRITE_ACCOUNT_DATA_MTU, 1UL );
380 0 : FOR(execrp_tile_cnt) fd_topob_tile_out( topo, "execrp", i, "cap_execrp", i );
381 0 : FOR(execrp_tile_cnt) fd_topob_tile_in( topo, "solcap", 0UL, "metric_in", "cap_execrp", i, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
382 0 : }
383 :
384 0 : fd_topob_wksp( topo, "store" );
385 0 : fd_topo_link_t const * repair_out_link = &topo->links[ fd_topo_find_link( topo, "repair_out", 0UL ) ];
386 0 : ulong fec_sets_per_slot = fd_ulong_if( config->firedancer.development.fixed_fec_sets,
387 0 : config->limits.max_shreds_per_block/FD_FEC_SHRED_CNT, config->limits.max_shreds_per_block );
388 0 : ulong store_fec_max = config->firedancer.runtime.max_live_slots * fec_sets_per_slot + repair_out_link->depth + 1UL;
389 0 : ulong store_fec_data_max = fd_ulong_if( config->firedancer.development.fixed_fec_sets, 31840UL, 63985UL );
390 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 );
391 0 : fd_topob_tile_uses( topo, backt_tile, store_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
392 0 : fd_topob_tile_uses( topo, replay_tile, store_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
393 0 : FD_TEST( fd_pod_insertf_ulong( topo->props, store_obj->id, "store" ) );
394 :
395 0 : fd_topob_wksp( topo, "banks" );
396 0 : ulong min_bank_cache_cnt = fd_ulong_min( execrp_tile_cnt, FD_SCHED_MAX_STAGING_LANES )+2UL;
397 0 : if( FD_UNLIKELY( config->firedancer.runtime.bank_cache_cnt<min_bank_cache_cnt ) ) {
398 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 "
399 0 : "and %lu replay staging lanes). Increase [runtime] bank_cache_cnt.",
400 0 : config->firedancer.runtime.bank_cache_cnt, min_bank_cache_cnt, execrp_tile_cnt, FD_SCHED_MAX_STAGING_LANES ));
401 0 : }
402 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 );
403 0 : fd_topob_tile_uses( topo, replay_tile, banks_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
404 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 );
405 0 : setup_topo_footer_cache( topo, "banks" );
406 0 : setup_topo_execrp_result( topo, "execrp_replay", execrp_tile_cnt );
407 0 : if( FD_LIKELY( !disable_snap_loader ) ) {
408 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 );
409 0 : }
410 0 : FD_TEST( fd_pod_insertf_ulong( topo->props, banks_obj->id, "banks" ) );
411 :
412 0 : fd_topob_wksp( topo, "txncache" );
413 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, config->firedancer.runtime.transaction_cache_size_mib<<20 );
414 0 : fd_topob_tile_uses( topo, replay_tile, txncache_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
415 0 : if( FD_LIKELY( !disable_snap_loader ) ) {
416 0 : fd_topob_tile_uses( topo, &topo->tiles[ fd_topo_find_tile( topo, "snapin", 0UL ) ], txncache_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
417 0 : }
418 0 : for( ulong i=0UL; i<execrp_tile_cnt; i++ ) {
419 0 : fd_topob_tile_uses( topo, &topo->tiles[ fd_topo_find_tile( topo, "execrp", i ) ], txncache_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
420 0 : }
421 0 : FD_TEST( fd_pod_insertf_ulong( topo->props, txncache_obj->id, "txncache" ) );
422 :
423 : /* snapin (snapshot mode) and genesi (genesis mode) are mutually
424 : exclusive accdb writers */
425 0 : if( FD_LIKELY( !disable_snap_loader ) ) {
426 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 );
427 0 : } else fd_topob_tile_uses( topo, genesi_tile, accdb_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
428 0 : fd_topob_tile_uses( topo, accdb_tile, accdb_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
429 0 : fd_topob_tile_uses( topo, replay_tile, accdb_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
430 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 );
431 :
432 0 : if( FD_LIKELY( !disable_snap_loader ) ) {
433 0 : fd_topob_wksp( topo, "snapin_shmem" );
434 0 : fd_topo_obj_t * shmem_obj = fd_topob_obj( topo, "snapin_shmem", "snapin_shmem" );
435 0 : FD_TEST( fd_pod_insertf_ulong( topo->props, shmem_obj->id, "snapin_shmem" ) );
436 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 );
437 :
438 0 : fd_topo_obj_t * dc_ticket_obj = fd_topob_obj_named( topo, "fseq", "snapdc", "frame_ticket" );
439 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 );
440 0 : }
441 :
442 0 : for( ulong i=0UL; i<topo->tile_cnt; i++ ) {
443 0 : fd_topo_tile_t * tile = &topo->tiles[ i ];
444 0 : fd_topo_configure_tile( tile, config );
445 :
446 0 : if( FD_UNLIKELY( !strcmp( tile->name, "gui" ) ) ) {
447 0 : tile->gui.tile_cnt = topo->tile_cnt;
448 0 : }
449 :
450 0 : if( !strcmp( tile->name, "replay" ) ) {
451 0 : tile->replay.enable_features_cnt = config->tiles.replay.enable_features_cnt;
452 0 : for( ulong i = 0; i < tile->replay.enable_features_cnt; i++ ) {
453 0 : fd_cstr_ncpy( tile->replay.enable_features[i], config->tiles.replay.enable_features[i], sizeof(tile->replay.enable_features[i]) );
454 0 : }
455 0 : }
456 0 : }
457 :
458 0 : if( FD_LIKELY( telemetry_enabled ) ) {
459 0 : wire_event_links( topo );
460 0 : wire_replay_diff_link( topo );
461 0 : }
462 :
463 0 : if( FD_UNLIKELY( !is_auto_affinity ) ) {
464 0 : if( FD_UNLIKELY( affinity_tile_cnt<topo->tile_cnt ) )
465 0 : FD_LOG_ERR(( "topology has %lu tiles but [layout.affinity] only provides %lu entries", topo->tile_cnt, affinity_tile_cnt ));
466 0 : if( FD_UNLIKELY( affinity_tile_cnt>topo->tile_cnt ) )
467 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 ));
468 0 : } else {
469 0 : ushort blocklist_cores[ FD_TILE_MAX ];
470 0 : topo->blocklist_cores_cnt = fd_topob_parse_affinity_cstr( config->layout.blocklist_cores, blocklist_cores, 0, 0 );
471 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 ));
472 0 : for( ulong i=0UL; i<topo->blocklist_cores_cnt; i++ ) {
473 0 : if( FD_UNLIKELY( blocklist_cores[ i ]==USHORT_MAX ) ) FD_LOG_ERR(( "[layout.blocklist_cores] must not contain `f`" ));
474 0 : topo->blocklist_cores_cpu_idx[ i ] = blocklist_cores[ i ];
475 0 : }
476 0 : fd_topob_auto_layout( topo );
477 0 : }
478 :
479 0 : fd_topob_waker( topo );
480 0 : fd_topob_sleep_finish( topo );
481 :
482 0 : fd_topob_finish( topo, CALLBACKS );
483 0 : }
484 :
485 : static void
486 0 : backtest_cmd_topo( config_t * config ) {
487 0 : backtest_topo( config );
488 0 : }
489 :
490 : extern configure_stage_t fd_cfg_stage_keys;
491 :
492 : static args_t
493 0 : configure_args( void ) {
494 0 : args_t args = {
495 0 : .configure.command = CONFIGURE_CMD_INIT,
496 0 : };
497 :
498 : /* FD_BACKTEST_SHARED_HOST skips the host-global stages (irq-affinity,
499 : kworkers) so multiple backtest instances can share a host. */
500 0 : int shared_host = !!getenv( "FD_BACKTEST_SHARED_HOST" );
501 :
502 0 : ulong stage_idx = 0UL;
503 0 : args.configure.stages[ stage_idx++ ] = &fd_cfg_stage_hugetlbfs;
504 0 : if( FD_LIKELY( !shared_host ) ) {
505 0 : args.configure.stages[ stage_idx++ ] = &fd_cfg_stage_irq_affinity;
506 0 : args.configure.stages[ stage_idx++ ] = &fd_cfg_stage_kworkers;
507 0 : }
508 0 : args.configure.stages[ stage_idx++ ] = &fd_cfg_stage_cpuset;
509 0 : args.configure.stages[ stage_idx++ ] = &fd_cfg_stage_snapshots;
510 0 : args.configure.stages[ stage_idx++ ] = &fd_cfg_stage_keys;
511 0 : args.configure.stages[ stage_idx++ ] = NULL;
512 :
513 0 : return args;
514 0 : }
515 :
516 : void
517 : backtest_cmd_perm( args_t * args FD_PARAM_UNUSED,
518 : fd_cap_chk_t * chk,
519 0 : config_t const * config ) {
520 0 : args_t c_args = configure_args();
521 0 : configure_cmd_perm( &c_args, chk, config );
522 0 : run_cmd_perm( NULL, chk, config );
523 0 : }
524 :
525 : static void
526 : backtest_cmd_fn( args_t * args,
527 0 : config_t * config ) {
528 0 : args_t c_args = configure_args();
529 0 : configure_cmd_fn( &c_args, config );
530 :
531 0 : initialize_workspaces( config );
532 0 : initialize_stacks( config );
533 0 : initialize_accdb_fd( config );
534 0 : initialize_epoch_credits_fd( config );
535 0 : initialize_cost_tracker_fd( config );
536 0 : initialize_vote_stakes_fd( config );
537 0 : initialize_stake_delegations_fd( config );
538 0 : initialize_txncache_fd( config );
539 0 : initialize_store_fds( config );
540 0 : initialize_snapshot_fds( config );
541 :
542 0 : fd_topo_join_workspaces( &config->topo, FD_SHMEM_JOIN_MODE_READ_WRITE, FD_TOPO_CORE_DUMP_LEVEL_DISABLED );
543 0 : fd_topo_fill( &config->topo );
544 :
545 0 : args_t watch_args;
546 0 : int pipefd[2];
547 0 : if( !args->backtest.no_watch ) {
548 0 : if( FD_UNLIKELY( pipe2( pipefd, O_NONBLOCK ) ) ) FD_LOG_ERR(( "pipe2() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
549 :
550 0 : watch_args.watch.drain_output_fd = pipefd[0];
551 0 : watch_args.watch.full = 1;
552 0 : if( FD_UNLIKELY( -1==dup2( pipefd[ 1 ], STDERR_FILENO ) ) ) FD_LOG_ERR(( "dup2() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
553 0 : if( FD_UNLIKELY( -1==close( pipefd[1] ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
554 0 : }
555 :
556 0 : fd_topo_run_single_process( &config->topo, config->uid, config->gid, fdctl_tile_run );
557 0 : if( args->backtest.no_watch ) {
558 0 : for(;;) pause();
559 0 : } else {
560 0 : watch_cmd_fn( &watch_args, config );
561 0 : }
562 0 : }
563 :
564 : action_t fd_action_backtest = {
565 : .name = "backtest",
566 : .args = backtest_cmd_args,
567 : .fn = backtest_cmd_fn,
568 : .perm = backtest_cmd_perm,
569 : .topo = backtest_cmd_topo,
570 : .description = "Replay a ledger offline through the replay tile with mocked consensus",
571 : };
|