Line data Source code
1 : /* The backtest command spawns a smaller topology for replaying shreds from
2 : rocksdb (or other sources TBD) and reproduce the behavior of replay tile.
3 :
4 : The smaller topology is:
5 : shred_out replay_execr
6 : backtest-------------->replay------------->execrp
7 : ^ |^ | ^ |
8 : |____________________|| | |________________|
9 : replay_out | | execrp_replay
10 : | |------------------------------>no consumer
11 : no producer------------- stake_out, txsend_out, poh_out
12 : store_replay
13 :
14 : */
15 : #define _GNU_SOURCE
16 : #include "../../firedancer/topology.h"
17 : #include "../../shared/commands/configure/configure.h"
18 : #include "../../shared/commands/run/run.h" /* initialize_workspaces */
19 : #include "../../shared/commands/watch/watch.h"
20 : #include "../../shared/fd_config.h" /* config_t */
21 : #include "../../../disco/tiles.h"
22 : #include "../../../disco/topo/fd_topob.h"
23 : #include "../../../disco/topo/fd_topob_vinyl.h"
24 : #include "../../../util/pod/fd_pod_format.h"
25 : #include "../../../discof/genesis/fd_genesi_tile.h"
26 : #include "../../../discof/reasm/fd_reasm.h"
27 : #include "../../../discof/replay/fd_replay_tile.h"
28 : #include "../../../discof/restore/fd_snapin_tile_private.h"
29 : #include "../../../discof/restore/fd_snaplv_tile_private.h"
30 : #include "../../../discof/restore/fd_snapwm_tile_private.h"
31 : #include "../../../discof/restore/utils/fd_slot_delta_parser.h"
32 : #include "../../../discof/restore/utils/fd_ssctrl.h"
33 : #include "../../../discof/restore/utils/fd_ssmsg.h"
34 : #include "../../../discof/tower/fd_tower_tile.h"
35 : #include "../../../discof/replay/fd_execrp.h"
36 : #include "../../../ballet/lthash/fd_lthash.h"
37 : #include "../../../flamenco/capture/fd_capture_ctx.h"
38 : #include "../../../disco/pack/fd_pack_cost.h"
39 : #include "../../../flamenco/progcache/fd_progcache_admin.h"
40 :
41 : #include <errno.h>
42 : #include <unistd.h>
43 : #include <fcntl.h>
44 :
45 : extern fd_topo_obj_callbacks_t * CALLBACKS[];
46 : fd_topo_run_tile_t fdctl_tile_run( fd_topo_tile_t const * tile );
47 :
48 : static void
49 0 : backtest_topo( config_t * config ) {
50 :
51 0 : config->development.sandbox = 0;
52 0 : config->development.no_clone = 1;
53 :
54 0 : ulong execrp_tile_cnt = config->firedancer.layout.execrp_tile_count;
55 0 : ulong lta_tile_cnt = config->firedancer.layout.snapshot_hash_tile_count;
56 0 : ulong snapwr_tile_cnt = config->firedancer.layout.snapwr_tile_count;
57 0 : ulong snaplh_tile_cnt = config->firedancer.layout.snapshot_hash_tile_count;
58 :
59 0 : int disable_snap_loader = !config->gossip.entrypoints_cnt;
60 0 : int vinyl_enabled = !config->firedancer.accounts.in_memory_only;
61 0 : int solcap_enabled = strlen( config->capture.solcap_capture )>0;
62 0 : int snapshot_lthash_disabled = config->development.snapshots.disable_lthash_verification;
63 :
64 0 : fd_topo_t * topo = { fd_topob_new( &config->topo, config->name ) };
65 0 : topo->max_page_size = fd_cstr_to_shmem_page_sz( config->hugetlbfs.max_page_size );
66 0 : topo->gigantic_page_threshold = config->hugetlbfs.gigantic_page_threshold_mib << 20;
67 :
68 0 : ulong cpu_idx = 0;
69 :
70 0 : fd_topob_wksp( topo, "metric" );
71 0 : fd_topob_wksp( topo, "metric_in" );
72 0 : fd_topob_tile( topo, "metric", "metric", "metric_in", ULONG_MAX, 0, 0, 0 );
73 :
74 0 : fd_topob_wksp( topo, "backt" );
75 0 : fd_topo_tile_t * backt_tile = fd_topob_tile( topo, "backt", "backt", "metric_in", cpu_idx++, 0, 0, 0 );
76 :
77 0 : fd_topob_wksp( topo, "replay" );
78 0 : fd_topo_tile_t * replay_tile = fd_topob_tile( topo, "replay", "replay", "metric_in", cpu_idx++, 0, 1, 0 );
79 :
80 : /* specified by [tiles.replay] */
81 :
82 0 : fd_topob_wksp( topo, "funk" );
83 0 : fd_topob_wksp( topo, "funk_locks" );
84 0 : setup_topo_funk( topo,
85 0 : config->firedancer.accounts.max_accounts,
86 0 : config->firedancer.runtime.max_live_slots + config->firedancer.accounts.write_delay_slots,
87 0 : config->firedancer.accounts.in_memory_only
88 0 : ? config->firedancer.accounts.file_size_gib
89 0 : : config->firedancer.accounts.max_unrooted_account_size_gib );
90 0 : ulong funk_obj_id; FD_TEST( (funk_obj_id = fd_pod_query_ulong( topo->props, "funk", ULONG_MAX ) )!=ULONG_MAX );
91 0 : ulong funk_locks_obj_id; FD_TEST( (funk_locks_obj_id = fd_pod_query_ulong( topo->props, "funk_locks", ULONG_MAX ) )!=ULONG_MAX );
92 0 : fd_topob_tile_uses( topo, replay_tile, &topo->objs[ funk_obj_id ], FD_SHMEM_JOIN_MODE_READ_WRITE );
93 0 : fd_topob_tile_uses( topo, replay_tile, &topo->objs[ funk_locks_obj_id ], FD_SHMEM_JOIN_MODE_READ_WRITE );
94 :
95 0 : fd_topob_wksp( topo, "progcache" );
96 0 : setup_topo_progcache( topo, "progcache",
97 0 : fd_progcache_est_rec_max( config->firedancer.runtime.program_cache.heap_size_mib<<20,
98 0 : config->firedancer.runtime.program_cache.mean_cache_entry_size ),
99 0 : config->firedancer.runtime.max_live_slots,
100 0 : config->firedancer.runtime.program_cache.heap_size_mib<<20 );
101 0 : ulong progcache_obj_id; FD_TEST( (progcache_obj_id = fd_pod_query_ulong( topo->props, "progcache", ULONG_MAX ) )!=ULONG_MAX );
102 0 : fd_topob_tile_uses( topo, replay_tile, &topo->objs[ progcache_obj_id ], FD_SHMEM_JOIN_MODE_READ_WRITE );
103 :
104 : /**********************************************************************/
105 : /* Add the executor tiles to topo */
106 : /**********************************************************************/
107 0 : fd_topob_wksp( topo, "execrp" );
108 0 : #define FOR(cnt) for( ulong i=0UL; i<cnt; i++ )
109 0 : FOR(execrp_tile_cnt) fd_topob_tile( topo, "execrp", "execrp", "metric_in", cpu_idx++, 0, 0, 0 );
110 :
111 : /**********************************************************************/
112 : /* Add the capture tile to topo */
113 : /**********************************************************************/
114 0 : if( solcap_enabled ) {
115 0 : fd_topob_wksp( topo, "solcap" );
116 0 : fd_topob_tile( topo, "solcap", "solcap", "metric_in", cpu_idx++, 0, 0, 0 );
117 0 : }
118 :
119 : /**********************************************************************/
120 : /* Add the snapshot tiles to topo */
121 : /**********************************************************************/
122 0 : fd_topo_tile_t * snapin_tile = NULL;
123 0 : if( FD_UNLIKELY( !disable_snap_loader ) ) {
124 0 : fd_topob_wksp( topo, "snapct" );
125 0 : fd_topob_wksp( topo, "snapld" );
126 0 : fd_topob_wksp( topo, "snapdc" );
127 0 : fd_topob_wksp( topo, "snapin" );
128 :
129 0 : fd_topo_tile_t * snapct_tile = fd_topob_tile( topo, "snapct", "snapct", "metric_in", cpu_idx++, 0, 0, 0 );
130 0 : fd_topo_tile_t * snapld_tile = fd_topob_tile( topo, "snapld", "snapld", "metric_in", cpu_idx++, 0, 0, 0 );
131 0 : fd_topo_tile_t * snapdc_tile = fd_topob_tile( topo, "snapdc", "snapdc", "metric_in", cpu_idx++, 0, 0, 0 );
132 0 : snapin_tile = fd_topob_tile( topo, "snapin", "snapin", "metric_in", cpu_idx++, 0, 0, 0 );
133 :
134 0 : snapct_tile->allow_shutdown = 1;
135 0 : snapld_tile->allow_shutdown = 1;
136 0 : snapdc_tile->allow_shutdown = 1;
137 0 : snapin_tile->allow_shutdown = 1;
138 :
139 0 : if( vinyl_enabled ) {
140 0 : fd_topob_wksp( topo, "snapwm" );
141 0 : fd_topo_tile_t * snapwm_tile = fd_topob_tile( topo, "snapwm", "snapwm", "metric_in", cpu_idx++, 0, 0, 0 );
142 0 : snapwm_tile->allow_shutdown = 1;
143 :
144 0 : fd_topob_wksp( topo, "snapwh" );
145 0 : fd_topo_tile_t * snapwh_tile = fd_topob_tile( topo, "snapwh", "snapwh", "metric_in", cpu_idx++, 0, 0, 0 );
146 0 : snapwh_tile->allow_shutdown = 1;
147 :
148 0 : fd_topob_wksp( topo, "snapwr" );
149 0 : FOR(snapwr_tile_cnt) fd_topob_tile( topo, "snapwr", "snapwr", "metric_in", cpu_idx++, 0, 0, 0 )->allow_shutdown = 1;
150 0 : }
151 :
152 0 : if( snapshot_lthash_disabled ) {
153 : /* nothing to do here */
154 0 : } else {
155 0 : if( vinyl_enabled ) {
156 0 : fd_topob_wksp( topo, "snaplh" );
157 0 : fd_topob_wksp( topo, "snaplv" );
158 0 : FOR(snaplh_tile_cnt) fd_topob_tile( topo, "snaplh", "snaplh", "metric_in", ULONG_MAX, 0, 0, 0 )->allow_shutdown = 1;
159 0 : /**/ fd_topob_tile( topo, "snaplv", "snaplv", "metric_in", ULONG_MAX, 0, 0, 0 )->allow_shutdown = 1;
160 0 : fd_topob_wksp( topo, "vinyl_admin" );
161 0 : } else {
162 0 : fd_topob_wksp( topo, "snapla" );
163 0 : fd_topob_wksp( topo, "snapls" );
164 0 : FOR(lta_tile_cnt) fd_topob_tile( topo, "snapla", "snapla", "metric_in", cpu_idx++, 0, 0, 0 )->allow_shutdown = 1;
165 0 : /**/ fd_topob_tile( topo, "snapls", "snapls", "metric_in", cpu_idx++, 0, 0, 0 )->allow_shutdown = 1;
166 0 : }
167 0 : }
168 :
169 0 : } else {
170 0 : fd_topob_wksp( topo, "genesi" );
171 0 : fd_topob_tile( topo, "genesi", "genesi", "metric_in", cpu_idx++, 0, 0, 0 )->allow_shutdown = 1;
172 0 : }
173 :
174 : /**********************************************************************/
175 : /* Setup backtest->replay link (repair_out) in topo */
176 : /**********************************************************************/
177 :
178 : /* The repair tile is replaced by the backtest tile for the repair to
179 : replay link. The frag interface is a "slice", ie. entry batch,
180 : which is provided by the backtest tile, which reads in the entry
181 : batches from the CLI-specified source (eg. RocksDB). */
182 :
183 0 : fd_topob_wksp( topo, "repair_out" );
184 0 : fd_topob_link( topo, "repair_out", "repair_out", 65536UL, FD_SHRED_OUT_MTU, 1UL );
185 0 : fd_topob_tile_in( topo, "replay", 0UL, "metric_in", "repair_out", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
186 0 : fd_topob_tile_out( topo, "backt", 0UL, "repair_out", 0UL );
187 :
188 : /**********************************************************************/
189 : /* Setup snapshot links in topo */
190 : /**********************************************************************/
191 0 : if( FD_LIKELY( !disable_snap_loader ) ) {
192 0 : fd_topob_wksp( topo, "snapct_ld" );
193 0 : fd_topob_wksp( topo, "snapld_dc" );
194 0 : fd_topob_wksp( topo, "snapdc_in" );
195 :
196 0 : fd_topob_wksp( topo, "snapin_manif" );
197 0 : fd_topob_wksp( topo, "snapct_repr" );
198 :
199 0 : if( vinyl_enabled ) {
200 0 : fd_topob_wksp( topo, "snapin_txn");
201 0 : fd_topob_wksp( topo, "snapin_wm" );
202 0 : fd_topob_wksp( topo, "snapwm_wr" );
203 0 : }
204 0 : if( snapshot_lthash_disabled ) {
205 0 : if( vinyl_enabled ) {
206 0 : fd_topob_wksp( topo, "snapwm_ct" );
207 0 : } else {
208 0 : fd_topob_wksp( topo, "snapin_ct" );
209 0 : }
210 0 : } else {
211 0 : if( vinyl_enabled ) {
212 0 : fd_topob_wksp( topo, "snaplv_lh" );
213 0 : fd_topob_wksp( topo, "snaplh_lv" );
214 0 : fd_topob_wksp( topo, "snapwm_lv" );
215 0 : fd_topob_wksp( topo, "snaplv_ct" );
216 0 : } else {
217 0 : fd_topob_wksp( topo, "snapla_ls" );
218 0 : fd_topob_wksp( topo, "snapin_ls" );
219 0 : fd_topob_wksp( topo, "snapls_ct" );
220 0 : }
221 0 : }
222 :
223 0 : fd_topob_link( topo, "snapct_ld", "snapct_ld", 128UL, sizeof(fd_ssctrl_init_t), 1UL );
224 0 : fd_topob_link( topo, "snapld_dc", "snapld_dc", 16384UL, USHORT_MAX, 1UL );
225 0 : fd_topob_link( topo, "snapdc_in", "snapdc_in", 16384UL, USHORT_MAX, 1UL );
226 :
227 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 */
228 0 : fd_topob_link( topo, "snapct_repr", "snapct_repr", 128UL, 0UL, 1UL )->permit_no_consumers = 1;
229 :
230 0 : if( vinyl_enabled ) {
231 : /* snapwm needs all txn_cache data in order to verify the slot
232 : deltas with the slot history. To make this possible, snapin
233 : uses the dcache of the snapin_txn link as the scratch memory.
234 : The depth of the link should match that of snapin_wm, just to
235 : guarantee enough mcache credits. The mtu needs to be adjusted
236 : so that the total dcache size matches what snapin requires.
237 : Round up the mtu (ulong) size using: (...+(depth-1))/depth. */
238 0 : fd_topob_link( topo, "snapin_txn", "snapin_txn", 16UL, (sizeof(fd_sstxncache_entry_t)*FD_SNAPIN_TXNCACHE_MAX_ENTRIES+15UL/*depth-1*/)/16UL/*depth*/, 1UL );
239 0 : fd_topob_link( topo, "snapin_wm", "snapin_wm", 16UL, FD_SNAPWM_PAIR_BATCH_SZ_MAX, 1UL );
240 : /* snapwh and snapwr both use snapwm_wh's dcache. snapwh sends
241 : control messages to snapwr, using snapwh_wr link, instructing
242 : which chunks in the dcache are ready to be consumed by snapwr. */
243 0 : fd_topo_link_t * snapwm_wh =
244 0 : fd_topob_link( topo, "snapwm_wh", "snapwm_wr", 64UL, FD_SNAPWM_WR_MTU, 1UL );
245 0 : fd_topob_link( topo, "snapwh_wr", "snapwm_wr", 64UL, 0UL, 1UL );
246 0 : fd_pod_insertf_ulong( topo->props, 8UL, "obj.%lu.app_sz", snapwm_wh->dcache_obj_id );
247 0 : }
248 0 : if( snapshot_lthash_disabled ) {
249 0 : if( vinyl_enabled ) {
250 0 : fd_topob_link( topo, "snapwm_ct", "snapwm_ct", 128UL, 0UL, 1UL );
251 0 : } else {
252 0 : fd_topob_link( topo, "snapin_ct", "snapin_ct", 128UL, 0UL, 1UL );
253 0 : }
254 0 : } else {
255 0 : if( vinyl_enabled ) {
256 0 : FOR(snaplh_tile_cnt) fd_topob_link( topo, "snaplh_lv", "snaplh_lv", 128UL, sizeof(fd_ssctrl_hash_result_t), 1UL );
257 0 : /**/ fd_topob_link( topo, "snapwm_lv", "snapwm_lv", 32768UL, FD_SNAPWM_DUP_META_BATCH_SZ, 1UL );
258 0 : /**/ fd_topob_link( topo, "snaplv_lh", "snaplv_lh", 262144UL, FD_SNAPLV_DUP_META_SZ, FD_SNAPLV_STEM_BURST ); /* FD_SNAPWM_DUP_META_BATCH_CNT_MAX times the depth of snapwm_lv */
259 0 : /**/ fd_topob_link( topo, "snaplv_ct", "snaplv_ct", 128UL, 0UL, 1UL );
260 0 : } else {
261 0 : FOR(lta_tile_cnt) fd_topob_link( topo, "snapla_ls", "snapla_ls", 128UL, sizeof(fd_lthash_value_t), 1UL );
262 0 : /**/ fd_topob_link( topo, "snapin_ls", "snapin_ls", 256UL, sizeof(fd_snapshot_full_account_t), 1UL );
263 0 : /**/ fd_topob_link( topo, "snapls_ct", "snapls_ct", 128UL, 0UL, 1UL );
264 0 : }
265 0 : }
266 :
267 0 : if( snapshot_lthash_disabled ) {
268 0 : if( vinyl_enabled ) {
269 0 : fd_topob_tile_in ( topo, "snapct", 0UL, "metric_in", "snapwm_ct", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
270 0 : } else {
271 0 : fd_topob_tile_in ( topo, "snapct", 0UL, "metric_in", "snapin_ct", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
272 0 : }
273 0 : } else {
274 0 : if( vinyl_enabled ) {
275 0 : fd_topob_tile_in ( topo, "snapct", 0UL, "metric_in", "snaplv_ct", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
276 0 : } else {
277 0 : fd_topob_tile_in ( topo, "snapct", 0UL, "metric_in", "snapls_ct", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
278 0 : }
279 0 : }
280 :
281 0 : fd_topob_tile_in ( topo, "snapct", 0UL, "metric_in", "snapld_dc", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
282 0 : fd_topob_tile_out( topo, "snapct", 0UL, "snapct_ld", 0UL );
283 0 : fd_topob_tile_out( topo, "snapct", 0UL, "snapct_repr", 0UL );
284 0 : fd_topob_tile_in ( topo, "snapld", 0UL, "metric_in", "snapct_ld", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
285 0 : fd_topob_tile_out( topo, "snapld", 0UL, "snapld_dc", 0UL );
286 0 : fd_topob_tile_in ( topo, "snapdc", 0UL, "metric_in", "snapld_dc", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
287 0 : fd_topob_tile_out( topo, "snapdc", 0UL, "snapdc_in", 0UL );
288 0 : fd_topob_tile_in ( topo, "snapin", 0UL, "metric_in", "snapdc_in", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
289 :
290 0 : fd_topob_tile_out( topo, "snapin", 0UL, "snapin_manif", 0UL );
291 0 : fd_topob_tile_in ( topo, "replay", 0UL, "metric_in", "snapin_manif", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
292 :
293 0 : if( vinyl_enabled ) {
294 0 : fd_topob_tile_out( topo, "snapin", 0UL, "snapin_wm", 0UL );
295 0 : fd_topob_tile_in ( topo, "snapwm", 0UL, "metric_in", "snapin_wm", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
296 0 : fd_topob_tile_out( topo, "snapin", 0UL, "snapin_txn",0UL );
297 0 : fd_topob_tile_in ( topo, "snapwm", 0UL, "metric_in", "snapin_txn",0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
298 0 : fd_topob_tile_out( topo, "snapwm", 0UL, "snapwm_wh", 0UL );
299 0 : fd_topob_tile_in ( topo, "snapwh", 0UL, "metric_in", "snapwm_wh", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
300 0 : fd_topob_tile_out( topo, "snapwh", 0UL, "snapwh_wr", 0UL );
301 : /* snapwh and snapwr both access snapwm_wh's dcache, avoiding a
302 : memcpy for every account (vinyl pair) that is being processed
303 : (loaded) from the snapshot. */
304 0 : FOR(snapwr_tile_cnt) fd_topob_tile_in ( topo, "snapwr", i, "metric_in", "snapwh_wr", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
305 0 : FOR(snapwr_tile_cnt) fd_topob_tile_uses( topo, &topo->tiles[ fd_topo_find_tile( topo, "snapwr", i ) ], &topo->objs[ topo->links[ fd_topo_find_link( topo, "snapwm_wh", 0UL ) ].dcache_obj_id ], FD_SHMEM_JOIN_MODE_READ_ONLY );
306 0 : }
307 0 : if( snapshot_lthash_disabled ) {
308 0 : if( vinyl_enabled ) {
309 0 : /**/ fd_topob_tile_out( topo, "snapwm", 0UL, "snapwm_ct", 0UL );
310 0 : } else {
311 0 : /**/ fd_topob_tile_out( topo, "snapin", 0UL, "snapin_ct", 0UL );
312 0 : }
313 0 : } else {
314 0 : if( vinyl_enabled ) {
315 0 : FOR(snaplh_tile_cnt) fd_topob_tile_in ( topo, "snaplh", i, "metric_in", "snapwh_wr", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
316 0 : FOR(snaplh_tile_cnt) fd_topob_tile_uses( topo, &topo->tiles[ fd_topo_find_tile( topo, "snaplh", i ) ], &topo->objs[ topo->links[ fd_topo_find_link( topo, "snapwm_wh", 0UL ) ].dcache_obj_id ], FD_SHMEM_JOIN_MODE_READ_ONLY );
317 0 : FOR(snaplh_tile_cnt) fd_topob_tile_in ( topo, "snaplh", i, "metric_in", "snaplv_lh", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
318 0 : /**/ fd_topob_tile_out( topo, "snaplv", 0UL, "snaplv_lh", 0UL );
319 0 : FOR(snaplh_tile_cnt) fd_topob_tile_out( topo, "snaplh", i, "snaplh_lv", i );
320 0 : /**/ fd_topob_tile_in ( topo, "snaplv", 0UL, "metric_in", "snapwm_lv", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
321 0 : FOR(snaplh_tile_cnt) fd_topob_tile_in ( topo, "snaplv", 0UL, "metric_in", "snaplh_lv", i, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
322 0 : /**/ fd_topob_tile_out( topo, "snaplv", 0UL, "snaplv_ct", 0UL );
323 0 : /**/ fd_topob_tile_out( topo, "snapwm", 0UL, "snapwm_lv", 0UL );
324 :
325 0 : fd_topo_obj_t * vinyl_admin_obj = setup_topo_vinyl_admin( topo, "vinyl_admin" );
326 0 : /**/ fd_topob_tile_uses( topo, &topo->tiles[ fd_topo_find_tile( topo, "snapwm", 0UL ) ], vinyl_admin_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
327 0 : FOR(snapwr_tile_cnt) fd_topob_tile_uses( topo, &topo->tiles[ fd_topo_find_tile( topo, "snapwr", i ) ], vinyl_admin_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
328 0 : /**/ fd_topob_tile_uses( topo, &topo->tiles[ fd_topo_find_tile( topo, "snaplv", 0UL ) ], vinyl_admin_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
329 0 : FOR(snaplh_tile_cnt) fd_topob_tile_uses( topo, &topo->tiles[ fd_topo_find_tile( topo, "snaplh", i ) ], vinyl_admin_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
330 0 : FD_TEST( fd_pod_insertf_ulong( topo->props, vinyl_admin_obj->id, "vinyl_admin" ) );
331 0 : } else {
332 0 : FOR(lta_tile_cnt) fd_topob_tile_in ( topo, "snapla", i, "metric_in", "snapdc_in", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
333 0 : FOR(lta_tile_cnt) fd_topob_tile_out( topo, "snapla", i, "snapla_ls", i );
334 0 : /**/ fd_topob_tile_in ( topo, "snapls", 0UL, "metric_in", "snapin_ls", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
335 0 : FOR(lta_tile_cnt) fd_topob_tile_in ( topo, "snapls", 0UL, "metric_in", "snapla_ls", i, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
336 0 : /**/ fd_topob_tile_out( topo, "snapls", 0UL, "snapls_ct", 0UL );
337 0 : /**/ fd_topob_tile_out( topo, "snapin", 0UL, "snapin_ls", 0UL );
338 0 : }
339 0 : }
340 0 : } else {
341 0 : fd_topob_wksp( topo, "genesi_out" );
342 0 : fd_topob_link( topo, "genesi_out", "genesi_out", 1UL, FD_GENESIS_TILE_MTU, 0UL );
343 0 : fd_topob_tile_out( topo, "genesi", 0UL, "genesi_out", 0UL );
344 0 : fd_topob_tile_in ( topo, "replay", 0UL, "metric_in", "genesi_out", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
345 0 : }
346 :
347 0 : if( vinyl_enabled ) {
348 0 : setup_topo_accdb_meta( topo, &config->firedancer );
349 0 : ulong vinyl_map_obj_id = fd_pod_query_ulong( topo->props, "accdb.meta_map", ULONG_MAX ); FD_TEST( vinyl_map_obj_id !=ULONG_MAX );
350 0 : ulong vinyl_pool_obj_id = fd_pod_query_ulong( topo->props, "accdb.meta_pool", ULONG_MAX ); FD_TEST( vinyl_pool_obj_id!=ULONG_MAX );
351 0 : fd_topo_obj_t * accdb_map_obj = &topo->objs[ vinyl_map_obj_id ];
352 0 : fd_topo_obj_t * accdb_pool_obj = &topo->objs[ vinyl_pool_obj_id ];
353 :
354 0 : fd_topo_obj_t * accdb_data = setup_topo_accdb_cache( topo, &config->firedancer );
355 :
356 0 : fd_topob_wksp( topo, "accdb_execrp" );
357 0 : fd_topo_tile_t * accdb_tile = fd_topob_tile( topo, "accdb", "accdb_execrp", "metric_in", cpu_idx++, 0, 0, 0 );
358 0 : fd_topob_tile_uses( topo, accdb_tile, accdb_data, FD_SHMEM_JOIN_MODE_READ_WRITE );
359 0 : fd_topob_tile_uses( topo, accdb_tile, accdb_map_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
360 0 : fd_topob_tile_uses( topo, accdb_tile, accdb_pool_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
361 :
362 0 : fd_topob_tile_uses( topo, replay_tile, accdb_data, FD_SHMEM_JOIN_MODE_READ_WRITE );
363 0 : for( ulong i=0UL; i<execrp_tile_cnt; i++ ) {
364 0 : fd_topob_tile_uses( topo, &topo->tiles[ fd_topo_find_tile( topo, "execrp", i ) ], accdb_data, FD_SHMEM_JOIN_MODE_READ_WRITE );
365 0 : }
366 :
367 0 : fd_topob_wksp( topo, "accdb_replay" );
368 0 : }
369 :
370 : /**********************************************************************/
371 : /* More backtest->replay links in topo */
372 : /**********************************************************************/
373 :
374 : /* The tower tile is replaced by the backtest tile for the tower to
375 : replay link. The backtest tile simply sends monotonically
376 : increasing rooted slot numbers to the replay tile, once after each
377 : "replayed a full slot" notification received from the replay tile.
378 : This allows the replay tile to advance its watermark, and publish
379 : various data structures. This is an oversimplified barebones mock
380 : of the tower tile. */
381 0 : fd_topob_wksp( topo, "tower_out" );
382 0 : fd_topob_link( topo, "tower_out", "tower_out", 1024UL, sizeof(fd_tower_slot_done_t), 1UL );
383 0 : fd_topob_tile_in( topo, "replay", 0UL, "metric_in", "tower_out", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
384 0 : fd_topob_tile_out( topo, "backt", 0UL, "tower_out", 0UL );
385 :
386 : /**********************************************************************/
387 : /* Setup replay->stake/send/poh links in topo w/o consumers */
388 : /**********************************************************************/
389 0 : fd_topob_wksp( topo, "replay_epoch" );
390 0 : fd_topob_link( topo, "replay_epoch", "replay_epoch", 128UL, FD_EPOCH_OUT_MTU, 1UL );
391 0 : fd_topob_tile_out( topo, "replay", 0UL, "replay_epoch", 0UL );
392 0 : topo->links[ replay_tile->out_link_id[ fd_topo_find_tile_out_link( topo, replay_tile, "replay_epoch", 0 ) ] ].permit_no_consumers = 1;
393 :
394 : /**********************************************************************/
395 : /* Setup replay->backtest link (replay_notif) in topo */
396 : /**********************************************************************/
397 :
398 0 : fd_topob_wksp( topo, "replay_out" );
399 0 : fd_topob_link( topo, "replay_out", "replay_out", 8192UL, sizeof( fd_replay_message_t ), 1UL );
400 0 : fd_topob_tile_out( topo, "replay", 0UL, "replay_out", 0UL );
401 0 : fd_topob_tile_in ( topo, "backt", 0UL, "metric_in", "replay_out", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
402 0 : if( FD_LIKELY( !disable_snap_loader ) ) {
403 0 : fd_topob_tile_in ( topo, "backt", 0UL, "metric_in", "snapin_manif", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
404 0 : } else {
405 0 : fd_topob_tile_in ( topo, "backt", 0UL, "metric_in", "genesi_out", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
406 0 : }
407 :
408 : /**********************************************************************/
409 : /* Setup replay->exec link in topo */
410 : /**********************************************************************/
411 0 : fd_topob_wksp( topo, "replay_execrp" );
412 0 : fd_topob_link( topo, "replay_execrp", "replay_execrp", 16384UL, 2240UL, 1UL );
413 0 : fd_topob_tile_out( topo, "replay", 0UL, "replay_execrp", 0UL );
414 0 : for( ulong i=0UL; i<execrp_tile_cnt; i++ ) {
415 0 : fd_topob_tile_in( topo, "execrp", i, "metric_in", "replay_execrp", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
416 0 : }
417 :
418 : /**********************************************************************/
419 : /* Setup exec->replay links in topo, to send solcap account updates
420 : so that they are serialized, and to notify replay tile that a txn
421 : has been finalized by the exec tile. */
422 : /**********************************************************************/
423 0 : fd_topob_wksp( topo, "execrp_replay" );
424 :
425 0 : FOR(execrp_tile_cnt) fd_topob_link( topo, "execrp_replay", "execrp_replay", 16384UL, sizeof(fd_execrp_task_done_msg_t), 1UL );
426 :
427 0 : FOR(execrp_tile_cnt) fd_topob_tile_out( topo, "execrp", i, "execrp_replay", i );
428 0 : FOR(execrp_tile_cnt) fd_topob_tile_in( topo, "replay", 0UL, "metric_in", "execrp_replay", i, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
429 :
430 : /**********************************************************************/
431 : /* Setup the shared objs used by replay and exec tiles */
432 : /**********************************************************************/
433 :
434 0 : if( FD_UNLIKELY( solcap_enabled ) ) {
435 : /* 32 sections of SOLCAP_WRITE_ACCOUNT_DATA_MTU bytes each ≈ 4MB.
436 : This is done to ideally avoid cache thrashing and allow for all
437 : the links to sit on L3 cache. */
438 0 : fd_topob_link( topo, "cap_repl", "solcap", 32UL, SOLCAP_WRITE_ACCOUNT_DATA_MTU, 1UL );
439 0 : fd_topob_tile_out( topo, "replay", 0UL, "cap_repl", 0UL );
440 0 : fd_topob_tile_in( topo, "solcap", 0UL, "metric_in", "cap_repl", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
441 0 : FOR(execrp_tile_cnt) fd_topob_link( topo, "cap_execrp", "solcap", 32UL, SOLCAP_WRITE_ACCOUNT_DATA_MTU, 1UL );
442 0 : FOR(execrp_tile_cnt) fd_topob_tile_out( topo, "execrp", i, "cap_execrp", i );
443 0 : FOR(execrp_tile_cnt) fd_topob_tile_in( topo, "solcap", 0UL, "metric_in", "cap_execrp", i, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
444 0 : }
445 :
446 0 : fd_topob_wksp( topo, "store" );
447 0 : fd_topo_obj_t * store_obj = setup_topo_store( topo, "store", config->firedancer.runtime.max_live_slots * FD_SHRED_BLK_MAX, 1 );
448 0 : fd_topob_tile_uses( topo, backt_tile, store_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
449 0 : fd_topob_tile_uses( topo, replay_tile, store_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
450 0 : FD_TEST( fd_pod_insertf_ulong( topo->props, store_obj->id, "store" ) );
451 :
452 0 : fd_topo_obj_t * acc_pool_obj = setup_topo_acc_pool( topo, config->firedancer.runtime.max_account_cnt );
453 0 : fd_topob_tile_uses( topo, replay_tile, acc_pool_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
454 0 : FOR(execrp_tile_cnt) fd_topob_tile_uses( topo, &topo->tiles[ fd_topo_find_tile( topo, "execrp", i ) ], acc_pool_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
455 0 : FD_TEST( fd_pod_insertf_ulong( topo->props, acc_pool_obj->id, "acc_pool" ) );
456 :
457 : /* banks_obj shared by replay and exec tiles */
458 0 : fd_topob_wksp( topo, "banks" );
459 0 : fd_topo_obj_t * banks_obj = setup_topo_banks( topo, "banks", config->firedancer.runtime.max_live_slots, config->firedancer.runtime.max_fork_width, 0 );
460 0 : fd_topob_tile_uses( topo, replay_tile, banks_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
461 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 );
462 0 : FD_TEST( fd_pod_insertf_ulong( topo->props, banks_obj->id, "banks" ) );
463 :
464 : /* banks_locks_obj shared by replay and exec tiles */
465 0 : fd_topob_wksp( topo, "banks_locks" );
466 0 : fd_topo_obj_t * banks_locks_obj = setup_topo_banks_locks( topo, "banks_locks" );
467 0 : fd_topob_tile_uses( topo, replay_tile, banks_locks_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
468 0 : FOR(execrp_tile_cnt) fd_topob_tile_uses( topo, &topo->tiles[ fd_topo_find_tile( topo, "execrp", i ) ], banks_locks_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
469 0 : FD_TEST( fd_pod_insertf_ulong( topo->props, banks_locks_obj->id, "banks_locks" ) );
470 :
471 : /* txncache_obj shared by replay, snapin, and execrp tiles */
472 0 : fd_topob_wksp( topo, "txncache" );
473 0 : fd_topo_obj_t * txncache_obj = setup_topo_txncache( topo, "txncache",
474 0 : config->firedancer.runtime.max_live_slots,
475 0 : FD_PACK_MAX_TXNCACHE_TXN_PER_SLOT );
476 0 : fd_topob_tile_uses( topo, replay_tile, txncache_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
477 0 : if( FD_LIKELY( !disable_snap_loader ) ) {
478 0 : fd_topob_tile_uses( topo, snapin_tile, txncache_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
479 0 : if( vinyl_enabled ) {
480 0 : ulong vinyl_map_obj_id = fd_pod_query_ulong( topo->props, "accdb.meta_map", ULONG_MAX ); FD_TEST( vinyl_map_obj_id !=ULONG_MAX );
481 0 : ulong vinyl_pool_obj_id = fd_pod_query_ulong( topo->props, "accdb.meta_pool", ULONG_MAX ); FD_TEST( vinyl_pool_obj_id!=ULONG_MAX );
482 0 : fd_topo_obj_t * vinyl_map_obj = &topo->objs[ vinyl_map_obj_id ];
483 0 : fd_topo_obj_t * vinyl_pool_obj = &topo->objs[ vinyl_pool_obj_id ];
484 0 : fd_topob_tile_uses( topo, snapin_tile, vinyl_map_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
485 0 : fd_topob_tile_uses( topo, snapin_tile, vinyl_pool_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
486 0 : fd_topob_tile_uses( topo, &topo->tiles[ fd_topo_find_tile( topo, "snapwm", 0UL ) ], vinyl_map_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
487 0 : fd_topob_tile_uses( topo, &topo->tiles[ fd_topo_find_tile( topo, "snapwm", 0UL ) ], vinyl_pool_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
488 0 : }
489 0 : }
490 0 : for( ulong i=0UL; i<execrp_tile_cnt; i++ ) {
491 0 : fd_topob_tile_uses( topo, &topo->tiles[ fd_topo_find_tile( topo, "execrp", i ) ], txncache_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
492 0 : }
493 0 : FD_TEST( fd_pod_insertf_ulong( topo->props, txncache_obj->id, "txncache" ) );
494 :
495 0 : if( FD_LIKELY( !disable_snap_loader ) ) {
496 0 : fd_topob_tile_uses( topo, snapin_tile, &topo->objs[ funk_obj_id ], FD_SHMEM_JOIN_MODE_READ_WRITE );
497 0 : fd_topob_tile_uses( topo, snapin_tile, &topo->objs[ funk_locks_obj_id ], FD_SHMEM_JOIN_MODE_READ_WRITE );
498 0 : }
499 :
500 0 : if( vinyl_enabled ) {
501 0 : fd_topob_vinyl_rq( topo, "replay", 0UL, "accdb_replay", "replay", 4UL, 1024UL, 1024UL );
502 0 : for( ulong i=0UL; i<execrp_tile_cnt; i++ ) {
503 0 : fd_topob_vinyl_rq( topo, "execrp", i, "accdb_execrp", "execrp", 4UL, 1024UL, 1024UL );
504 0 : }
505 0 : }
506 :
507 0 : for( ulong i=0UL; i<topo->tile_cnt; i++ ) {
508 0 : fd_topo_tile_t * tile = &topo->tiles[ i ];
509 0 : fd_topo_configure_tile( tile, config );
510 :
511 0 : if( !strcmp( tile->name, "replay" ) ) {
512 0 : tile->replay.enable_features_cnt = config->tiles.replay.enable_features_cnt;
513 0 : for( ulong i = 0; i < tile->replay.enable_features_cnt; i++ ) {
514 0 : fd_cstr_ncpy( tile->replay.enable_features[i], config->tiles.replay.enable_features[i], sizeof(tile->replay.enable_features[i]) );
515 0 : }
516 0 : }
517 0 : }
518 :
519 : // fd_topob_auto_layout( topo, 0 );
520 0 : fd_topob_finish( topo, CALLBACKS );
521 0 : }
522 :
523 : extern int * fd_log_private_shared_lock;
524 :
525 : static void
526 0 : backtest_cmd_topo( config_t * config ) {
527 0 : config->firedancer.development.replay.scheduler_depth = 8192UL;
528 0 : backtest_topo( config );
529 0 : }
530 :
531 : extern configure_stage_t fd_cfg_stage_accdb;
532 : extern configure_stage_t fd_cfg_stage_keys;
533 :
534 : static args_t
535 0 : configure_args( void ) {
536 0 : args_t args = {
537 0 : .configure.command = CONFIGURE_CMD_INIT,
538 0 : };
539 :
540 0 : ulong stage_idx = 0UL;
541 0 : args.configure.stages[ stage_idx++ ] = &fd_cfg_stage_hugetlbfs;
542 0 : args.configure.stages[ stage_idx++ ] = &fd_cfg_stage_snapshots;
543 0 : args.configure.stages[ stage_idx++ ] = &fd_cfg_stage_accdb;
544 0 : args.configure.stages[ stage_idx++ ] = &fd_cfg_stage_keys;
545 0 : args.configure.stages[ stage_idx++ ] = NULL;
546 :
547 0 : return args;
548 0 : }
549 :
550 : void
551 : backtest_cmd_args( int * pargc,
552 : char *** pargv,
553 0 : args_t * args ) {
554 0 : char const * db = fd_env_strip_cmdline_cstr( pargc, pargv, "--db", NULL, "funk" );
555 0 : char const * vinyl_path = fd_env_strip_cmdline_cstr( pargc, pargv, "--vinyl-path", NULL, NULL );
556 0 : char const * vinyl_io = fd_env_strip_cmdline_cstr( pargc, pargv, "--vinyl-io", NULL, "bd" );
557 :
558 0 : args->backtest.no_watch = fd_env_strip_cmdline_contains( pargc, pargv, "--no-watch" );
559 :
560 0 : if( 0==strcmp( db, "funk" ) ) args->backtest.is_vinyl = 0;
561 0 : else if( 0==strcmp( db, "vinyl" ) ) args->backtest.is_vinyl = 1;
562 0 : else FD_LOG_ERR(( "invalid --db '%s' (must be 'funk' or 'vinyl')", db ));
563 :
564 0 : fd_cstr_ncpy( args->backtest.vinyl_path, vinyl_path, sizeof(args->backtest.vinyl_path) );
565 :
566 0 : if( FD_UNLIKELY( strlen( vinyl_io )!=2UL ) ) FD_LOG_ERR(( "invalid --vinyl-io '%s'", vinyl_io ));
567 0 : fd_cstr_ncpy( args->backtest.vinyl_io, vinyl_io, sizeof(args->backtest.vinyl_io) );
568 0 : }
569 :
570 : void
571 : backtest_cmd_perm( args_t * args FD_PARAM_UNUSED,
572 : fd_cap_chk_t * chk,
573 0 : config_t const * config ) {
574 0 : args_t c_args = configure_args();
575 0 : configure_cmd_perm( &c_args, chk, config );
576 0 : run_cmd_perm( NULL, chk, config );
577 0 : }
578 :
579 : static void
580 : fixup_config( config_t * config,
581 0 : args_t const * args ) {
582 0 : if( args->backtest.vinyl_path[0] ) {
583 0 : fd_cstr_ncpy( config->paths.accounts, args->backtest.vinyl_path, sizeof(config->paths.accounts) );
584 0 : }
585 :
586 0 : if( args->backtest.is_vinyl ) {
587 0 : config->firedancer.accounts.in_memory_only = 0;
588 :
589 0 : char const * io_mode = args->backtest.vinyl_io;
590 0 : if( 0==strcmp( io_mode, "ur" ) ) fd_cstr_ncpy( config->firedancer.accounts.io_provider, "io_uring", sizeof(config->firedancer.accounts.io_provider) );
591 0 : else if( 0==strcmp( io_mode, "bd" ) ) fd_cstr_ncpy( config->firedancer.accounts.io_provider, "portable", sizeof(config->firedancer.accounts.io_provider) );
592 0 : else FD_LOG_ERR(( "unsupported --vinyl-io '%s' (valid options are 'bd' and 'ur')", io_mode ));
593 0 : }
594 :
595 : /* FIXME Unfortunately, the fdctl boot procedure constructs the
596 : topology before parsing command-line arguments. So, here,
597 : we construct the topology again (a third time ... sigh). */
598 0 : backtest_topo( config );
599 0 : }
600 :
601 : static void
602 : backtest_cmd_fn( args_t * args,
603 0 : config_t * config ) {
604 0 : fixup_config( config, args );
605 0 : args_t c_args = configure_args();
606 0 : configure_cmd_fn( &c_args, config );
607 :
608 0 : initialize_workspaces( config );
609 0 : initialize_stacks( config );
610 :
611 0 : fd_log_private_shared_lock[ 1 ] = 0;
612 0 : fd_topo_join_workspaces( &config->topo, FD_SHMEM_JOIN_MODE_READ_WRITE, FD_TOPO_CORE_DUMP_LEVEL_DISABLED );
613 0 : fd_topo_fill( &config->topo );
614 :
615 0 : args_t watch_args;
616 0 : int pipefd[2];
617 0 : if( !args->backtest.no_watch ) {
618 0 : if( FD_UNLIKELY( pipe2( pipefd, O_NONBLOCK ) ) ) FD_LOG_ERR(( "pipe2() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
619 :
620 0 : watch_args.watch.drain_output_fd = pipefd[0];
621 0 : if( FD_UNLIKELY( -1==dup2( pipefd[ 1 ], STDERR_FILENO ) ) ) FD_LOG_ERR(( "dup2() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
622 0 : }
623 :
624 0 : fd_topo_run_single_process( &config->topo, 2, config->uid, config->gid, fdctl_tile_run );
625 0 : if( args->backtest.no_watch ) {
626 0 : for(;;) pause();
627 0 : } else {
628 0 : watch_cmd_fn( &watch_args, config );
629 0 : }
630 0 : }
631 :
632 : action_t fd_action_backtest = {
633 : .name = "backtest",
634 : .args = backtest_cmd_args,
635 : .fn = backtest_cmd_fn,
636 : .perm = backtest_cmd_perm,
637 : .topo = backtest_cmd_topo,
638 : };
|