Line data Source code
1 : #define _GNU_SOURCE
2 : #include "fd_topo.h"
3 :
4 : #include "../metrics/fd_metrics.h"
5 : #include "../../util/pod/fd_pod_format.h"
6 : #include "../../util/wksp/fd_wksp_private.h"
7 : #include "../../util/shmem/fd_shmem_private.h"
8 :
9 : #include <stdio.h>
10 : #include <errno.h>
11 : #include <link.h>
12 : #include <unistd.h>
13 : #include <sys/stat.h>
14 :
15 : void *
16 : fd_topo_obj_laddr( fd_topo_t const * topo,
17 1188 : ulong obj_id ) {
18 1188 : fd_topo_obj_t const * obj = &topo->objs[ obj_id ];
19 1188 : if( FD_UNLIKELY( obj_id==ULONG_MAX ) ) FD_LOG_CRIT(( "invalid obj_id ULONG_MAX" ));
20 1188 : if( FD_UNLIKELY( obj_id>=FD_TOPO_MAX_OBJS ) ) FD_LOG_CRIT(( "invalid obj_id %lu", obj_id ));
21 1188 : FD_TEST( obj->id == obj_id );
22 1188 : FD_TEST( obj->offset );
23 1188 : return (void *)((ulong)topo->workspaces[ obj->wksp_id ].wksp + obj->offset);
24 1188 : }
25 :
26 : void
27 : fd_topo_join_workspace( fd_topo_t * topo,
28 : fd_topo_wksp_t * wksp,
29 : int mode,
30 0 : int dump ) {
31 0 : char name[ PATH_MAX ];
32 0 : FD_TEST( fd_cstr_printf_check( name, PATH_MAX, NULL, "%s_%s.wksp", topo->app_name, wksp->name ) );
33 :
34 0 : wksp->wksp = fd_wksp_join( fd_shmem_join( name, mode, dump, NULL, NULL, NULL ) );
35 0 : if( FD_UNLIKELY( !wksp->wksp ) ) FD_LOG_ERR(( "fd_wksp_join failed" ));
36 0 : }
37 :
38 : FD_FN_PURE static int
39 0 : tile_needs_wksp( fd_topo_t const * topo, fd_topo_tile_t const * tile, ulong wksp_id ) {
40 0 : int mode = -1;
41 0 : for( ulong i=0UL; i<tile->uses_obj_cnt; i++ ) {
42 0 : if( FD_UNLIKELY( topo->objs[ tile->uses_obj_id[ i ] ].wksp_id==wksp_id ) ) {
43 0 : mode = fd_int_max( mode, tile->uses_obj_mode[ i ] );
44 0 : }
45 0 : }
46 0 : return mode;
47 0 : }
48 :
49 : void
50 : fd_topo_join_tile_workspaces( fd_topo_t * topo,
51 : fd_topo_tile_t * tile,
52 0 : int core_dump_level ) {
53 0 : for( ulong i=0UL; i<topo->wksp_cnt; i++ ) {
54 0 : int needs_wksp = tile_needs_wksp( topo, tile, i );
55 0 : if( FD_LIKELY( -1!=needs_wksp ) ) {
56 0 : int dump = core_dump_level >= topo->workspaces[ i ].core_dump_level ? 1 : 0;
57 0 : fd_topo_join_workspace( topo, &topo->workspaces[ i ], needs_wksp, dump );
58 0 : }
59 0 : }
60 0 : }
61 :
62 : void
63 : fd_topo_join_workspaces( fd_topo_t * topo,
64 : int mode,
65 0 : int core_dump_level ) {
66 0 : for( ulong i=0UL; i<topo->wksp_cnt; i++ ) {
67 0 : int dump = core_dump_level >= topo->workspaces[ i ].core_dump_level ? 1 : 0;
68 0 : fd_topo_join_workspace( topo, &topo->workspaces[ i ], mode, dump );
69 0 : }
70 0 : }
71 :
72 : void
73 : fd_topo_leave_workspace( fd_topo_t * topo FD_PARAM_UNUSED,
74 0 : fd_topo_wksp_t * wksp ) {
75 0 : if( FD_LIKELY( wksp->wksp ) ) {
76 0 : if( FD_UNLIKELY( fd_wksp_detach( wksp->wksp ) ) ) FD_LOG_ERR(( "fd_wksp_detach failed" ));
77 0 : wksp->wksp = NULL;
78 0 : wksp->known_footprint = 0UL;
79 0 : wksp->total_footprint = 0UL;
80 0 : }
81 0 : }
82 :
83 : void
84 0 : fd_topo_leave_workspaces( fd_topo_t * topo ) {
85 0 : for( ulong i=0UL; i<topo->wksp_cnt; i++ ) {
86 0 : fd_topo_leave_workspace( topo, &topo->workspaces[ i ] );
87 0 : }
88 0 : }
89 :
90 : extern char fd_shmem_private_base[ FD_SHMEM_PRIVATE_BASE_MAX ];
91 :
92 : int
93 : fd_topo_create_workspace( fd_topo_t * topo,
94 : fd_topo_wksp_t * wksp,
95 0 : int update_existing ) {
96 0 : char name[ PATH_MAX ];
97 0 : FD_TEST( fd_cstr_printf_check( name, PATH_MAX, NULL, "%s_%s.wksp", topo->app_name, wksp->name ) );
98 :
99 0 : ulong sub_page_cnt[ 1 ] = { wksp->page_cnt };
100 0 : ulong sub_cpu_idx [ 1 ] = { fd_shmem_cpu_idx( wksp->numa_idx ) };
101 :
102 0 : int err;
103 0 : if( FD_UNLIKELY( update_existing ) ) {
104 0 : err = fd_shmem_update_multi( name, wksp->page_sz, 1, sub_page_cnt, sub_cpu_idx, S_IRUSR | S_IWUSR ); /* logs details */
105 0 : } else {
106 0 : err = fd_shmem_create_multi( name, wksp->page_sz, 1, sub_page_cnt, sub_cpu_idx, S_IRUSR | S_IWUSR ); /* logs details */
107 0 : }
108 0 : if( FD_UNLIKELY( err && errno==ENOMEM ) ) return -1;
109 0 : else if( FD_UNLIKELY( err ) ) FD_LOG_ERR(( "fd_shmem_create_multi failed" ));
110 :
111 0 : void * shmem = fd_shmem_join( name, FD_SHMEM_JOIN_MODE_READ_WRITE, 0, NULL, NULL, NULL ); /* logs details */
112 :
113 0 : void * wkspmem = fd_wksp_new( shmem, name, 0U, wksp->part_max, wksp->total_footprint ); /* logs details */
114 0 : if( FD_UNLIKELY( !wkspmem ) ) FD_LOG_ERR(( "fd_wksp_new failed" ));
115 :
116 0 : fd_wksp_t * join = fd_wksp_join( wkspmem );
117 0 : if( FD_UNLIKELY( !join ) ) FD_LOG_ERR(( "fd_wksp_join failed" ));
118 :
119 : /* Footprint has been predetermined so that this alloc() call must
120 : succeed inside the data region. The difference between total_footprint
121 : and known_footprint is given to "loose" data, that may be dynamically
122 : allocated out of the workspace at runtime. */
123 0 : if( FD_LIKELY( wksp->known_footprint ) ) {
124 0 : ulong offset = fd_wksp_alloc( join, fd_topo_workspace_align(), wksp->known_footprint, 1UL );
125 0 : if( FD_UNLIKELY( !offset ) ) FD_LOG_ERR(( "fd_wksp_alloc failed" ));
126 :
127 : /* gaddr_lo is the start of the workspace data region that can be
128 : given out in response to wksp alloc requests. We rely on an
129 : implicit assumption everywhere that the bytes we are given by
130 : this single allocation will be at gaddr_lo, so that we can find
131 : them, so we verify this here for paranoia in case the workspace
132 : alloc implementation changes. */
133 0 : if( FD_UNLIKELY( fd_ulong_align_up( ((struct fd_wksp_private*)join)->gaddr_lo, fd_topo_workspace_align() ) != offset ) )
134 0 : FD_LOG_ERR(( "wksp gaddr_lo %lu != offset %lu", fd_ulong_align_up( ((struct fd_wksp_private*)join)->gaddr_lo, fd_topo_workspace_align() ), offset ));
135 0 : }
136 :
137 0 : fd_wksp_leave( join );
138 :
139 0 : if( FD_UNLIKELY( fd_shmem_leave( shmem, NULL, NULL ) ) ) /* logs details */
140 0 : FD_LOG_ERR(( "fd_shmem_leave failed" ));
141 :
142 0 : return 0;
143 0 : }
144 :
145 : void
146 : fd_topo_wksp_new( fd_topo_t const * topo,
147 : fd_topo_wksp_t const * wksp,
148 0 : fd_topo_obj_callbacks_t ** callbacks ) {
149 0 : for( ulong i=0UL; i<topo->obj_cnt; i++ ) {
150 0 : fd_topo_obj_t const * obj = &topo->objs[ i ];
151 0 : if( FD_LIKELY( obj->wksp_id!=wksp->id ) ) continue;
152 :
153 0 : for( ulong j=0UL; callbacks[ j ]; j++ ) {
154 0 : if( FD_LIKELY( strcmp( callbacks[ j ]->name, obj->name ) ) ) continue;
155 :
156 0 : long ts = -fd_log_wallclock();
157 0 : if( FD_LIKELY( callbacks[ j ]->new ) ) callbacks[ j ]->new( topo, obj );
158 0 : long elapsed = fd_log_wallclock() + ts;
159 0 : if( FD_UNLIKELY( elapsed>(1000L*1000L*150L ) ) ) FD_LOG_WARNING(( "fd_topo_wksp_new(%s) took %ld ms", obj->name, elapsed/(1000L*1000L) ));
160 0 : else if( FD_UNLIKELY( elapsed>(1000L*1000L*5L ) ) ) FD_LOG_INFO(( "fd_topo_wksp_new(%s) took %ld ms", obj->name, elapsed/(1000L*1000L) ));
161 0 : break;
162 0 : }
163 0 : }
164 0 : }
165 :
166 : void
167 : fd_topo_workspace_fill( fd_topo_t * topo,
168 0 : fd_topo_wksp_t * wksp ) {
169 0 : for( ulong i=0UL; i<topo->link_cnt; i++ ) {
170 0 : fd_topo_link_t * link = &topo->links[ i ];
171 :
172 0 : if( FD_UNLIKELY( topo->objs[ link->mcache_obj_id ].wksp_id!=wksp->id ) ) continue;
173 0 : link->mcache = fd_mcache_join( fd_topo_obj_laddr( topo, link->mcache_obj_id ) );
174 0 : FD_TEST( link->mcache );
175 :
176 0 : if( link->mtu ) {
177 0 : if( FD_UNLIKELY( topo->objs[ link->dcache_obj_id ].wksp_id!=wksp->id ) ) continue;
178 0 : link->dcache = fd_dcache_join( fd_topo_obj_laddr( topo, link->dcache_obj_id ) );
179 0 : FD_TEST( link->dcache );
180 0 : }
181 0 : }
182 :
183 0 : for( ulong i=0UL; i<topo->tile_cnt; i++ ) {
184 0 : fd_topo_tile_t * tile = &topo->tiles[ i ];
185 :
186 0 : if( FD_LIKELY( topo->objs[ tile->metrics_obj_id ].wksp_id==wksp->id ) ) {
187 0 : tile->metrics = fd_metrics_join( fd_topo_obj_laddr( topo, tile->metrics_obj_id ) );
188 0 : FD_TEST( tile->metrics );
189 0 : }
190 :
191 0 : for( ulong j=0UL; j<tile->in_cnt; j++ ) {
192 0 : if( FD_UNLIKELY( topo->objs[ tile->in_link_fseq_obj_id[ j ] ].wksp_id!=wksp->id ) ) continue;
193 0 : tile->in_link_fseq[ j ] = fd_fseq_join( fd_topo_obj_laddr( topo, tile->in_link_fseq_obj_id[ j ] ) );
194 0 : FD_TEST( tile->in_link_fseq[ j ] );
195 0 : }
196 0 : }
197 0 : }
198 :
199 : void
200 : fd_topo_fill_tile( fd_topo_t * topo,
201 0 : fd_topo_tile_t * tile ) {
202 0 : for( ulong i=0UL; i<topo->wksp_cnt; i++ ) {
203 0 : if( FD_UNLIKELY( -1!=tile_needs_wksp( topo, tile, i ) ) )
204 0 : fd_topo_workspace_fill( topo, &topo->workspaces[ i ] );
205 0 : }
206 0 : }
207 :
208 : void
209 0 : fd_topo_fill( fd_topo_t * topo ) {
210 0 : for( ulong i=0UL; i<topo->wksp_cnt; i++ ) {
211 0 : fd_topo_workspace_fill( topo, &topo->workspaces[ i ] );
212 0 : }
213 0 : }
214 :
215 : FD_FN_CONST static ulong
216 81 : fd_topo_tile_extra_huge_pages( fd_topo_tile_t const * tile ) {
217 : /* Every tile maps an additional set of pages for the stack. */
218 81 : (void)tile;
219 81 : return (FD_TILE_PRIVATE_STACK_SZ/FD_SHMEM_HUGE_PAGE_SZ)+2UL;
220 81 : }
221 :
222 : FD_FN_PURE static ulong
223 162 : fd_topo_tile_extra_normal_pages( fd_topo_tile_t const * tile ) {
224 162 : ulong key_pages = 0UL;
225 162 : if( FD_UNLIKELY( !strcmp( tile->name, "sign" ) && tile->sign.failover_enabled ) ) {
226 : /* The junk key and the failover identity, each with its guard
227 : pages, next to the identity key. */
228 0 : key_pages += 10UL;
229 0 : }
230 162 : if( FD_UNLIKELY( tile->id_keyswitch_obj_id!=ULONG_MAX ) ) {
231 : /* Certain tiles using fd_keyload_load need normal pages to hold
232 : key material. */
233 162 : key_pages += 5UL;
234 162 : }
235 162 : if( FD_UNLIKELY( tile->av_keyswitch_obj_id!=ULONG_MAX ) ) {
236 : /* Certain tiles using fd_keyload_load need normal pages to hold
237 : key material. */
238 162 : key_pages += 5UL;
239 162 : }
240 162 : if( FD_UNLIKELY( !strcmp( tile->name, "failov" ) ) ) {
241 : /* The failover tile loads one public key at a time at boot and
242 : unloads it before the next. */
243 0 : key_pages += 5UL;
244 0 : }
245 :
246 162 : if( !strcmp( tile->name, "net" ) ) {
247 : /* net tile uses normal pages to hold xsk rings */
248 :
249 : /* xdp_desc struct is in linux UAPI so its size can be
250 : safely assumed */
251 36 : ulong xdp_desc_sz_bytes = 16UL;
252 36 : ulong xsk_rings_sz_bytes = 0UL;
253 36 : ulong xdp_address_sz_bytes = sizeof(ulong);
254 :
255 : /* rx ring */
256 36 : xsk_rings_sz_bytes += tile->xdp.xdp_rx_queue_size * xdp_desc_sz_bytes;
257 : /* tx ring */
258 36 : xsk_rings_sz_bytes += tile->xdp.xdp_tx_queue_size * xdp_desc_sz_bytes;
259 :
260 : /* completion ring */
261 36 : xsk_rings_sz_bytes += tile->xdp.xdp_tx_queue_size * xdp_address_sz_bytes;
262 : /* fill ring */
263 36 : xsk_rings_sz_bytes += tile->xdp.xdp_rx_queue_size * xdp_address_sz_bytes * 2UL;
264 :
265 36 : ulong xsk_cnt = ( strcmp( tile->xdp.if_virt, "lo" ) && !tile->kind_id ) ? 2UL : 1UL;
266 36 : key_pages += xsk_cnt * fd_ulong_align_up( xsk_rings_sz_bytes, FD_SHMEM_NORMAL_PAGE_SZ ) / FD_SHMEM_NORMAL_PAGE_SZ;
267 :
268 : /* All 4 rings must store a ring header. This is 320 bytes
269 : per ring as of linux v6.18.3, however could change in
270 : the future so allow up to a full 4KB page per ring to
271 : be safe. */
272 36 : key_pages += xsk_cnt * 4UL;
273 36 : }
274 :
275 162 : return key_pages;
276 162 : }
277 :
278 : FD_FN_PURE static ulong
279 : fd_topo_mlock_max_tile1( fd_topo_t const * topo,
280 0 : fd_topo_tile_t const * tile ) {
281 0 : ulong tile_mem = 0UL;
282 :
283 0 : for( ulong i=0UL; i<topo->wksp_cnt; i++ ) {
284 0 : if( FD_UNLIKELY( -1!=tile_needs_wksp( topo, tile, i ) ) )
285 0 : tile_mem += topo->workspaces[ i ].page_cnt * topo->workspaces[ i ].page_sz;
286 0 : }
287 :
288 0 : return tile_mem +
289 0 : fd_topo_tile_extra_huge_pages( tile ) * FD_SHMEM_HUGE_PAGE_SZ +
290 0 : fd_topo_tile_extra_normal_pages( tile ) * FD_SHMEM_NORMAL_PAGE_SZ;
291 0 : }
292 :
293 : FD_FN_PURE ulong
294 0 : fd_topo_mlock_max_tile( fd_topo_t const * topo ) {
295 0 : ulong highest_tile_mem = 0UL;
296 0 : for( ulong i=0UL; i<topo->tile_cnt; i++ ) {
297 0 : fd_topo_tile_t const * tile = &topo->tiles[ i ];
298 0 : highest_tile_mem = fd_ulong_max( highest_tile_mem, fd_topo_mlock_max_tile1( topo, tile ) );
299 0 : }
300 :
301 0 : return highest_tile_mem + fd_topo_code_footprint( 0 ) + fd_topo_code_footprint( 1 );
302 0 : }
303 :
304 : static int
305 : fd_topo_code_cb( struct dl_phdr_info * info,
306 : size_t info_sz,
307 288 : void * ctx ) {
308 288 : (void)info_sz;
309 :
310 3456 : for( ulong i=0UL; i<info->dlpi_phnum; i++ ) {
311 3168 : ElfW(Phdr) const * ph = &info->dlpi_phdr[ i ];
312 3168 : if( ph->p_type!=PT_LOAD ) continue;
313 :
314 1008 : ulong sz = (ph->p_flags & PF_W) ? ph->p_filesz : ph->p_memsz;
315 1008 : ulong lo = fd_ulong_align_dn( info->dlpi_addr+ph->p_vaddr, FD_SHMEM_NORMAL_PAGE_SZ );
316 1008 : ulong hi = fd_ulong_align_up( info->dlpi_addr+ph->p_vaddr+sz, FD_SHMEM_NORMAL_PAGE_SZ );
317 1008 : if( FD_UNLIKELY( hi<=lo ) ) continue;
318 :
319 1008 : if( FD_UNLIKELY( ctx ) ) {
320 1008 : ((ulong *)ctx)[ !!(ph->p_flags & PF_W) ] += hi-lo;
321 1008 : } else {
322 0 : if( FD_UNLIKELY( -1==fd_numa_mlock( (void *)lo, hi-lo ) ) ) {
323 0 : FD_LOG_WARNING(( "mlock(\"%s\",%lu KiB) failed (%i-%s); code may page fault under memory pressure",
324 0 : info->dlpi_name[0] ? info->dlpi_name : "main", (hi-lo)>>10, errno, fd_io_strerror( errno ) ));
325 0 : }
326 0 : }
327 1008 : }
328 :
329 288 : return 0;
330 288 : }
331 :
332 : ulong
333 48 : fd_topo_code_footprint( int writable ) {
334 48 : ulong sz[ 2 ] = { 0UL, 0UL };
335 48 : dl_iterate_phdr( fd_topo_code_cb, sz );
336 48 : return sz[ !!writable ];
337 48 : }
338 :
339 : void
340 0 : fd_topo_mlock_code( void ) {
341 0 : dl_iterate_phdr( fd_topo_code_cb, NULL );
342 0 : }
343 :
344 : FD_FN_PURE ulong
345 : fd_topo_gigantic_page_cnt( fd_topo_t const * topo,
346 12 : ulong numa_idx ) {
347 12 : ulong result = 0UL;
348 21 : for( ulong i=0UL; i<topo->wksp_cnt; i++ ) {
349 9 : fd_topo_wksp_t const * wksp = &topo->workspaces[ i ];
350 9 : if( FD_LIKELY( wksp->numa_idx!=numa_idx ) ) continue;
351 :
352 9 : if( FD_LIKELY( wksp->page_sz==FD_SHMEM_GIGANTIC_PAGE_SZ ) ) {
353 0 : result += wksp->page_cnt;
354 0 : }
355 9 : }
356 12 : return result;
357 12 : }
358 :
359 : FD_FN_PURE ulong
360 : fd_topo_huge_page_cnt( fd_topo_t const * topo,
361 : ulong numa_idx,
362 12 : int include_anonymous ) {
363 12 : ulong result = 0UL;
364 21 : for( ulong i=0UL; i<topo->wksp_cnt; i++ ) {
365 9 : fd_topo_wksp_t const * wksp = &topo->workspaces[ i ];
366 9 : if( FD_LIKELY( wksp->numa_idx!=numa_idx ) ) continue;
367 :
368 9 : if( FD_LIKELY( wksp->page_sz==FD_SHMEM_HUGE_PAGE_SZ ) ) {
369 9 : result += wksp->page_cnt;
370 9 : }
371 9 : }
372 :
373 : /* The stack huge pages are also placed in the hugetlbfs. */
374 93 : for( ulong i=0UL; i<topo->tile_cnt; i++ ) {
375 81 : result += fd_topo_tile_extra_huge_pages( &topo->tiles[ i ] );
376 81 : }
377 :
378 : /* No anonymous huge pages in use yet. */
379 12 : (void)include_anonymous;
380 :
381 12 : return result;
382 12 : }
383 :
384 : FD_FN_PURE ulong
385 24 : fd_topo_normal_page_cnt( fd_topo_t const * topo ) {
386 24 : ulong result = 0UL;
387 186 : for( ulong i=0UL; i<topo->tile_cnt; i++ ) {
388 162 : result += fd_topo_tile_extra_normal_pages( &topo->tiles[ i ] );
389 162 : }
390 24 : ulong code_sz = fd_topo_code_footprint( 0 ) + topo->tile_cnt*fd_topo_code_footprint( 1 );
391 24 : return result + code_sz/FD_SHMEM_NORMAL_PAGE_SZ;
392 24 : }
393 :
394 : FD_FN_PURE ulong
395 12 : fd_topo_mlock( fd_topo_t const * topo ) {
396 12 : ulong result = 0UL;
397 21 : for( ulong i=0UL; i<topo->wksp_cnt; i++ ) {
398 9 : result += topo->workspaces[ i ].page_cnt * topo->workspaces[ i ].page_sz;
399 9 : }
400 12 : return result;
401 12 : }
402 :
403 : static void
404 0 : fd_topo_mem_sz_string( ulong sz, char out[static 24] ) {
405 0 : if( FD_LIKELY( sz >= FD_SHMEM_GIGANTIC_PAGE_SZ ) ) {
406 0 : FD_TEST( fd_cstr_printf_check( out, 24, NULL, "%.1f GiB", (double)sz / (double)(1UL << 30) ) );
407 0 : } else if( FD_LIKELY( sz >= 1048576 ) ) {
408 0 : FD_TEST( fd_cstr_printf_check( out, 24, NULL, "%.1f MiB", (double)sz / (double)(1UL << 20) ) );
409 0 : } else if( FD_LIKELY( sz >= 1024 ) ) {
410 0 : FD_TEST( fd_cstr_printf_check( out, 24, NULL, "%.1f KiB", (double)sz / (double)(1UL << 10) ) );
411 0 : } else {
412 0 : FD_TEST( fd_cstr_printf_check( out, 24, NULL, "%lu B", sz ) );
413 0 : }
414 0 : }
415 :
416 : /* Style sizes by magnitude so the big numbers pop */
417 :
418 : static char const *
419 : fd_topo_mem_sz_style( ulong sz,
420 : char const * bold,
421 0 : char const * dim ) {
422 0 : if( sz>=(1UL<<30) ) return bold;
423 0 : if( sz< (1UL<<20) ) return dim;
424 0 : return "";
425 0 : }
426 :
427 : void
428 : fd_topo_print_log( int stdout,
429 0 : fd_topo_t * topo ) {
430 0 : char message[ 32UL*4096UL ] = {0}; /* Same as FD_LOG_BUF_SZ */
431 :
432 0 : char * cur = message;
433 0 : ulong remaining = sizeof(message) - 1; /* Leave one character at the end to ensure NUL terminated */
434 :
435 0 : #define PRINT( ... ) do { \
436 0 : int n = snprintf( cur, remaining, __VA_ARGS__ ); \
437 0 : if( FD_UNLIKELY( n < 0 ) ) FD_LOG_ERR(( "snprintf failed" )); \
438 0 : if( FD_UNLIKELY( (ulong)n >= remaining ) ) { \
439 : /* stdout mode flushes and retries, log mode keeps one message */ \
440 0 : if( FD_UNLIKELY( !stdout ) ) FD_LOG_ERR(( "snprintf overflow" )); \
441 0 : *cur = '\0'; \
442 0 : FD_LOG_STDOUT(( "%s", message )); \
443 0 : cur = message; remaining = sizeof(message) - 1; \
444 0 : n = snprintf( cur, remaining, __VA_ARGS__ ); \
445 0 : if( FD_UNLIKELY( n < 0 ) ) FD_LOG_ERR(( "snprintf failed" )); \
446 0 : if( FD_UNLIKELY( (ulong)n >= remaining ) ) FD_LOG_ERR(( "snprintf overflow" )); \
447 0 : } \
448 0 : remaining -= (ulong)n; \
449 0 : cur += n; \
450 0 : } while( 0 )
451 :
452 0 : int color = stdout && fd_log_colorize() && isatty( STDOUT_FILENO );
453 0 : char const * c_bold = color ? "\033[1m" : "";
454 0 : char const * c_dim = color ? "\033[2m" : "";
455 0 : char const * c_normal = color ? "\033[0m" : "";
456 :
457 0 : char const * sep = "";
458 :
459 0 : #define SECTION( ... ) do { \
460 0 : char _title[ 64 ]; \
461 0 : FD_TEST( fd_cstr_printf_check( _title, sizeof(_title), NULL, __VA_ARGS__ ) ); \
462 0 : PRINT( "%s%sāā%s %s%s%s %s", sep, c_dim, c_normal, c_bold, _title, c_normal, c_dim ); \
463 0 : for( ulong _i=strlen( _title ); _i<76UL; _i++ ) PRINT( "ā" ); \
464 0 : PRINT( "%s\n", c_normal ); \
465 0 : sep = "\n"; \
466 0 : } while( 0 )
467 :
468 0 : SECTION( "Summary" );
469 :
470 : /* The logic to compute number of stack pages is taken from
471 : fd_tile_thread.cxx, in function fd_topo_tile_stack_join, and this
472 : should match that. */
473 0 : ulong stack_pages = topo->tile_cnt * FD_SHMEM_HUGE_PAGE_SZ * ((FD_TILE_PRIVATE_STACK_SZ/FD_SHMEM_HUGE_PAGE_SZ)+2UL);
474 :
475 0 : ulong normal_page_bytes = fd_topo_normal_page_cnt( topo ) * FD_SHMEM_NORMAL_PAGE_SZ;
476 0 : ulong total_bytes = fd_topo_mlock( topo ) + stack_pages + normal_page_bytes;
477 :
478 0 : PRINT(" %s%-23s%s %lu\n", c_dim, "Total Tiles", c_normal, topo->tile_cnt );
479 0 : PRINT(" %s%-23s%s %s%lu GiB + %lu MiB + %lu KiB%s %s(%lu bytes)%s\n",
480 0 : c_dim, "Total Memory Locked", c_normal,
481 0 : c_bold,
482 0 : total_bytes / (1 << 30),
483 0 : (total_bytes % (1 << 30)) / (1 << 20),
484 0 : (total_bytes % (1 << 20)) / (1 << 10),
485 0 : c_normal,
486 0 : c_dim, total_bytes, c_normal );
487 :
488 0 : ulong required_gigantic_pages = 0UL;
489 0 : ulong required_huge_pages = 0UL;
490 :
491 0 : ulong numa_node_cnt = fd_shmem_numa_cnt();
492 0 : for( ulong i=0UL; i<numa_node_cnt; i++ ) {
493 0 : required_gigantic_pages += fd_topo_gigantic_page_cnt( topo, i );
494 0 : required_huge_pages += fd_topo_huge_page_cnt( topo, i, 0 );
495 0 : }
496 0 : PRINT(" %s%-23s%s %lu\n", c_dim, "Required Gigantic Pages", c_normal, required_gigantic_pages );
497 0 : PRINT(" %s%-23s%s %lu\n", c_dim, "Required Huge Pages", c_normal, required_huge_pages );
498 0 : PRINT(" %s%-23s%s %lu\n", c_dim, "Required Normal Pages", c_normal, fd_topo_normal_page_cnt( topo ) );
499 0 : if( FD_UNLIKELY( numa_node_cnt>1UL ) ) {
500 0 : for( ulong i=0UL; i<numa_node_cnt; i++ ) {
501 0 : char label[ 40 ];
502 0 : FD_TEST( fd_cstr_printf_check( label, sizeof(label), NULL, "NUMA node %lu", i ) );
503 0 : PRINT(" %s%-23s%s %lu gigantic, %lu huge\n", c_dim, label, c_normal, fd_topo_gigantic_page_cnt( topo, i ), fd_topo_huge_page_cnt( topo, i, 0 ) );
504 0 : }
505 0 : }
506 :
507 0 : if( FD_LIKELY( !stdout ) ) {
508 0 : if( FD_LIKELY( cur>message ) ) cur[ -1 ] = '\0'; /* Strip trailing newline */
509 0 : FD_LOG_INFO(( "%s", message ));
510 0 : return;
511 0 : }
512 :
513 0 : SECTION( "Workspaces (%lu)", topo->wksp_cnt );
514 0 : PRINT( " %s%3s %10s %-13s %5s %-8s %4s %12s %12s%s\n", c_dim, "ID", "SIZE", "NAME", "PAGES", "PAGE SZ", "NUMA", "FOOTPRINT", "LOOSE", c_normal );
515 0 : for( ulong i=0UL; i<topo->wksp_cnt; i++ ) {
516 0 : fd_topo_wksp_t * wksp = &topo->workspaces[ i ];
517 :
518 0 : ulong sz = wksp->page_sz * wksp->page_cnt;
519 0 : char size[ 24 ];
520 0 : fd_topo_mem_sz_string( sz, size );
521 0 : PRINT( " %3lu %s%10s%s %-13s %5lu %-8s %4lu %12lu %s%12lu%s\n",
522 0 : i, fd_topo_mem_sz_style( sz, c_bold, c_dim ), size, c_normal,
523 0 : wksp->name, wksp->page_cnt, fd_shmem_page_sz_to_cstr( wksp->page_sz ), wksp->numa_idx,
524 0 : wksp->known_footprint, c_dim, wksp->total_footprint - wksp->known_footprint, c_normal );
525 0 : }
526 :
527 0 : SECTION( "Objects (%lu)", topo->obj_cnt );
528 0 : PRINT( " %s%4s %10s %-13s %-13s %4s %12s %s%s\n", c_dim, "ID", "SIZE", "WORKSPACE", "OBJECT", "WKSP", "OFFSET", "PROPERTIES", c_normal );
529 0 : for( ulong i=0UL; i<topo->obj_cnt; i++ ) {
530 0 : fd_topo_obj_t * obj = &topo->objs[ i ];
531 :
532 0 : char size[ 24 ];
533 0 : fd_topo_mem_sz_string( obj->footprint, size );
534 0 : PRINT( " %4lu %s%10s%s %-13s %-13s %4lu %12lu ",
535 0 : i, fd_topo_mem_sz_style( obj->footprint, c_bold, c_dim ), size, c_normal,
536 0 : topo->workspaces[ obj->wksp_id ].name, obj->name,
537 0 : obj->wksp_id, obj->offset );
538 0 : for( fd_pod_iter_t iter=fd_pod_iter_init( fd_pod_queryf_subpod( topo->props, "obj.%lu", obj->id ) );
539 0 : !fd_pod_iter_done( iter );
540 0 : iter=fd_pod_iter_next( iter ) ) {
541 0 : fd_pod_info_t info = fd_pod_iter_info( iter );
542 0 : if( !strcmp( info.key, "seed" ) ) continue; /* key is a cstr (key_sz includes NUL) */
543 0 : PRINT( " %s%.*s=%s", c_dim, (int)info.key_sz, info.key, c_normal );
544 0 : switch( info.val_type ) {
545 0 : case FD_POD_VAL_TYPE_CSTR:
546 0 : PRINT( "%.*s", (int)info.val_sz, (char const *)info.val );
547 0 : break;
548 0 : case FD_POD_VAL_TYPE_ULONG: {
549 0 : ulong val; fd_ulong_svw_dec( info.val, &val );
550 0 : PRINT( "%lu", val );
551 0 : break;
552 0 : }
553 0 : case FD_POD_VAL_TYPE_INT: {
554 0 : ulong u; fd_ulong_svw_dec( info.val, &u );
555 0 : PRINT( "%i", (int)fd_long_zz_dec( u ) );
556 0 : break;
557 0 : }
558 0 : }
559 0 : }
560 0 : PRINT( "\n" );
561 0 : }
562 :
563 0 : SECTION( "Links (%lu)", topo->link_cnt );
564 0 : PRINT( " %s%3s %10s %-13s %4s %4s %8s %9s %5s%s\n", c_dim, "ID", "SIZE", "NAME", "KIND", "WKSP", "DEPTH", "MTU", "BURST", c_normal );
565 0 : for( ulong i=0UL; i<topo->link_cnt; i++ ) {
566 0 : fd_topo_link_t * link = &topo->links[ i ];
567 :
568 0 : ulong sz = fd_dcache_req_data_sz( link->mtu, link->depth, link->burst, 1 );
569 0 : char size[ 24 ];
570 0 : fd_topo_mem_sz_string( sz, size );
571 0 : PRINT( " %3lu %s%10s%s %-13s %4lu %4lu %8lu %9lu %5lu\n",
572 0 : i, fd_topo_mem_sz_style( sz, c_bold, c_dim ), size, c_normal,
573 0 : link->name, link->kind_id, topo->objs[ link->mcache_obj_id ].wksp_id, link->depth, link->mtu, link->burst );
574 0 : }
575 :
576 0 : #define PRINTIN( ... ) do { \
577 0 : int n = snprintf( cur_in, remaining_in, __VA_ARGS__ ); \
578 0 : if( FD_UNLIKELY( n < 0 ) ) FD_LOG_ERR(( "snprintf failed" )); \
579 0 : if( FD_UNLIKELY( (ulong)n >= remaining_in ) ) FD_LOG_ERR(( "snprintf overflow" )); \
580 0 : remaining_in -= (ulong)n; \
581 0 : cur_in += n; \
582 0 : } while( 0 )
583 :
584 0 : #define PRINTOUT( ... ) do { \
585 0 : int n = snprintf( cur_out, remaining_out, __VA_ARGS__ ); \
586 0 : if( FD_UNLIKELY( n < 0 ) ) FD_LOG_ERR(( "snprintf failed" )); \
587 0 : if( FD_UNLIKELY( (ulong)n >= remaining_out ) ) FD_LOG_ERR(( "snprintf overflow" )); \
588 0 : remaining_out -= (ulong)n; \
589 0 : cur_out += n; \
590 0 : } while( 0 )
591 :
592 0 : SECTION( "Tiles (%lu)", topo->tile_cnt );
593 0 : PRINT( " %s%3s %10s %-13s %4s %4s %4s %4s %s%s\n", c_dim, "ID", "MLOCK", "NAME", "KIND", "WKSP", "CPU", "NUMA", "LINKS / OBJECTS", c_normal );
594 0 : for( ulong i=0UL; i<topo->tile_cnt; i++ ) {
595 0 : fd_topo_tile_t * tile = &topo->tiles[ i ];
596 :
597 : /* Worst case ", -1023" per link */
598 0 : char in[ 8UL*FD_TOPO_MAX_TILE_IN_LINKS ] = {0};
599 0 : char * cur_in = in;
600 0 : ulong remaining_in = sizeof( in ) - 1;
601 :
602 0 : for( ulong j=0UL; j<tile->in_cnt; j++ ) {
603 0 : if( FD_LIKELY( j != 0 ) ) PRINTIN( ", " );
604 0 : if( FD_LIKELY( tile->in_link_reliable[ j ] ) ) PRINTIN( "%2lu", tile->in_link_id[ j ] );
605 0 : else PRINTIN( "%2ld", (long)-tile->in_link_id[ j ] );
606 0 : }
607 :
608 0 : char out[ 8UL*FD_TOPO_MAX_TILE_OUT_LINKS ] = {0};
609 0 : char * cur_out = out;
610 0 : ulong remaining_out = sizeof( out ) - 1;
611 :
612 0 : for( ulong j=0UL; j<tile->out_cnt; j++ ) {
613 0 : if( FD_LIKELY( j != 0 ) ) PRINTOUT( ", " );
614 0 : PRINTOUT( "%2lu", tile->out_link_id[ j ] );
615 0 : }
616 :
617 : /* Determine tile's NUMA node either based on CPU or wksp affinity */
618 0 : ulong tile_numa = 0UL;
619 0 : if( tile->cpu_idx!=ULONG_MAX ) {
620 0 : tile_numa = fd_shmem_numa_idx( tile->cpu_idx );
621 0 : } else {
622 0 : tile_numa = topo->workspaces[ topo->objs[ tile->tile_obj_id ].wksp_id ].numa_idx;
623 0 : }
624 :
625 0 : ulong mlock = fd_topo_mlock_max_tile1( topo, tile );
626 0 : char size[ 24 ];
627 0 : fd_topo_mem_sz_string( mlock, size );
628 0 : PRINT( " %3lu %s%10s%s %-13s %4lu %4lu ",
629 0 : i, fd_topo_mem_sz_style( mlock, c_bold, c_dim ), size, c_normal,
630 0 : tile->name, tile->kind_id, topo->objs[ tile->tile_obj_id ].wksp_id );
631 0 : if( tile->cpu_idx!=ULONG_MAX ) {
632 0 : PRINT( "%s%4lu%s", tile->floats ? c_dim : "", tile->cpu_idx, tile->floats ? c_normal : "" );
633 0 : } else {
634 0 : PRINT( "%s%4s%s", c_dim, "any", c_normal );
635 0 : }
636 :
637 0 : PRINT( " %4lu %sin=%s[%s] %sout=%s[%s] %sobjs=[", tile_numa, c_dim, c_normal, in, c_dim, c_normal, out, c_dim );
638 0 : for( ulong j=0UL; j<tile->uses_obj_cnt; j++ ) {
639 0 : if( FD_LIKELY( j!=0 ) ) PRINT( " " );
640 0 : int is_rw = tile->uses_obj_mode[ j ] == FD_SHMEM_JOIN_MODE_READ_WRITE;
641 0 : PRINT( "%lu:%s", tile->uses_obj_id[ j ], is_rw?"rw":"ro" );
642 0 : }
643 0 : PRINT( "]%s", c_normal );
644 :
645 : #if 0
646 : PRINT( " wksps=[" );
647 : for( ulong j=0UL; j<topo->wksp_cnt; j++ ) {
648 : int mode = tile_needs_wksp( topo, tile, j );
649 : if( FD_UNLIKELY( -1!=mode ) ) {
650 : if( FD_LIKELY( j!=0 ) ) PRINT( " " );
651 : PRINT( "%s:%s", topo->workspaces[ j ].name, mode==FD_SHMEM_JOIN_MODE_READ_WRITE?"rw":"ro" );
652 : }
653 : }
654 : PRINT( "]" );
655 : #endif
656 :
657 0 : if( FD_LIKELY( i != topo->tile_cnt-1 ) ) PRINT( "\n" );
658 0 : }
659 :
660 0 : FD_LOG_STDOUT(( "%s\n", message ));
661 :
662 0 : #undef PRINT
663 0 : #undef PRINTIN
664 0 : #undef PRINTOUT
665 0 : #undef SECTION
666 0 : }
667 :
668 : /* PRINT_JSON_CSTR streams src (sz bytes, embedded NULs stop early)
669 : as a JSON string literal, escaping quotes, backslashes and control
670 : characters. No fixed intermediate buffer: expansion cannot
671 : truncate regardless of src length or content. */
672 :
673 0 : #define PRINT_JSON_CSTR( src, sz ) do { \
674 0 : char const * _s = (src); \
675 0 : ulong _sz = (sz); \
676 0 : PRINT( "\"" ); \
677 0 : for( ulong _i=0UL; _i<_sz && _s[ _i ]; _i++ ) { \
678 0 : uchar _c = (uchar)_s[ _i ]; \
679 0 : if( FD_UNLIKELY( _c=='"' || _c=='\\' ) ) PRINT( "\\%c", (char)_c ); \
680 0 : else if( FD_UNLIKELY( _c<0x20 ) ) PRINT( "\\u%04x", (uint)_c ); \
681 0 : else PRINT( "%c", (char)_c ); \
682 0 : } \
683 0 : PRINT( "\"" ); \
684 0 : } while( 0 )
685 :
686 : void
687 0 : fd_topo_print_json( fd_topo_t * topo ) {
688 0 : char message[ 32UL*4096UL ] = {0};
689 :
690 0 : char * cur = message;
691 0 : ulong remaining = sizeof(message) - 1;
692 :
693 0 : #define PRINT( ... ) do { \
694 0 : int n = snprintf( cur, remaining, __VA_ARGS__ ); \
695 0 : if( FD_UNLIKELY( n < 0 ) ) FD_LOG_ERR(( "snprintf failed" )); \
696 0 : if( FD_UNLIKELY( (ulong)n >= remaining ) ) { \
697 0 : *cur = '\0'; \
698 0 : FD_LOG_STDOUT(( "%s", message )); \
699 0 : cur = message; remaining = sizeof(message) - 1; \
700 0 : n = snprintf( cur, remaining, __VA_ARGS__ ); \
701 0 : if( FD_UNLIKELY( n < 0 ) ) FD_LOG_ERR(( "snprintf failed" )); \
702 0 : if( FD_UNLIKELY( (ulong)n >= remaining ) ) FD_LOG_ERR(( "snprintf overflow" )); \
703 0 : } \
704 0 : remaining -= (ulong)n; \
705 0 : cur += n; \
706 0 : } while( 0 )
707 :
708 0 : ulong stack_pages = topo->tile_cnt * FD_SHMEM_HUGE_PAGE_SZ * ((FD_TILE_PRIVATE_STACK_SZ/FD_SHMEM_HUGE_PAGE_SZ)+2UL);
709 0 : ulong normal_page_bytes = fd_topo_normal_page_cnt( topo ) * FD_SHMEM_NORMAL_PAGE_SZ;
710 0 : ulong total_bytes = fd_topo_mlock( topo ) + stack_pages + normal_page_bytes;
711 :
712 0 : ulong required_gigantic_pages = 0UL;
713 0 : ulong required_huge_pages = 0UL;
714 0 : ulong numa_node_cnt = fd_shmem_numa_cnt();
715 0 : for( ulong i=0UL; i<numa_node_cnt; i++ ) {
716 0 : required_gigantic_pages += fd_topo_gigantic_page_cnt( topo, i );
717 0 : required_huge_pages += fd_topo_huge_page_cnt( topo, i, 0 );
718 0 : }
719 :
720 0 : PRINT( "{\n \"summary\": {\n" );
721 0 : PRINT( " \"tile_cnt\": %lu,\n", topo->tile_cnt );
722 0 : PRINT( " \"total_memory_locked_bytes\": %lu,\n", total_bytes );
723 0 : PRINT( " \"required_gigantic_pages\": %lu,\n", required_gigantic_pages );
724 0 : PRINT( " \"required_huge_pages\": %lu,\n", required_huge_pages );
725 0 : PRINT( " \"required_normal_pages\": %lu,\n", fd_topo_normal_page_cnt( topo ) );
726 0 : PRINT( " \"numa_nodes\": [" );
727 0 : for( ulong i=0UL; i<numa_node_cnt; i++ ) {
728 0 : PRINT( "%s\n { \"node\": %lu, \"gigantic_pages\": %lu, \"huge_pages\": %lu }", i ? "," : "", i, fd_topo_gigantic_page_cnt( topo, i ), fd_topo_huge_page_cnt( topo, i, 0 ) );
729 0 : }
730 0 : PRINT( "\n ]\n },\n" );
731 :
732 0 : PRINT( " \"workspaces\": [\n" );
733 0 : for( ulong i=0UL; i<topo->wksp_cnt; i++ ) {
734 0 : fd_topo_wksp_t * wksp = &topo->workspaces[ i ];
735 0 : PRINT( " { \"id\": %lu, \"name\": ", i );
736 0 : PRINT_JSON_CSTR( wksp->name, sizeof(wksp->name) );
737 0 : PRINT( ", \"page_cnt\": %lu, \"page_sz\": \"%s\", \"numa_idx\": %lu, \"total_bytes\": %lu, \"footprint\": %lu, \"loose\": %lu }%s\n",
738 0 : wksp->page_cnt, fd_shmem_page_sz_to_cstr( wksp->page_sz ), wksp->numa_idx,
739 0 : wksp->page_cnt * wksp->page_sz, wksp->known_footprint, wksp->total_footprint - wksp->known_footprint,
740 0 : i==topo->wksp_cnt-1UL ? "" : "," );
741 0 : }
742 0 : PRINT( " ],\n" );
743 :
744 0 : PRINT( " \"objects\": [\n" );
745 0 : for( ulong i=0UL; i<topo->obj_cnt; i++ ) {
746 0 : fd_topo_obj_t * obj = &topo->objs[ i ];
747 0 : PRINT( " { \"id\": %lu, \"name\": ", i );
748 0 : PRINT_JSON_CSTR( obj->name, sizeof(obj->name) );
749 0 : PRINT( ", \"wksp\": " );
750 0 : PRINT_JSON_CSTR( topo->workspaces[ obj->wksp_id ].name, sizeof(topo->workspaces[ obj->wksp_id ].name) );
751 0 : PRINT( ", \"wksp_id\": %lu, \"footprint\": %lu, \"offset\": %lu, \"props\": {",
752 0 : obj->wksp_id, obj->footprint, obj->offset );
753 0 : int first = 1;
754 0 : for( fd_pod_iter_t iter=fd_pod_iter_init( fd_pod_queryf_subpod( topo->props, "obj.%lu", obj->id ) );
755 0 : !fd_pod_iter_done( iter );
756 0 : iter=fd_pod_iter_next( iter ) ) {
757 0 : fd_pod_info_t info = fd_pod_iter_info( iter );
758 0 : if( !strcmp( info.key, "seed" ) ) continue; /* key is a cstr (key_sz includes NUL) */
759 0 : switch( info.val_type ) {
760 0 : case FD_POD_VAL_TYPE_CSTR: {
761 0 : PRINT( "%s ", first ? "" : "," );
762 0 : PRINT_JSON_CSTR( info.key, info.key_sz );
763 0 : PRINT( ": " );
764 0 : PRINT_JSON_CSTR( (char const *)info.val, info.val_sz );
765 0 : first = 0;
766 0 : break;
767 0 : }
768 0 : case FD_POD_VAL_TYPE_ULONG: {
769 0 : ulong val; fd_ulong_svw_dec( info.val, &val );
770 0 : PRINT( "%s ", first ? "" : "," );
771 0 : PRINT_JSON_CSTR( info.key, info.key_sz );
772 0 : PRINT( ": %lu", val );
773 0 : first = 0;
774 0 : break;
775 0 : }
776 0 : case FD_POD_VAL_TYPE_INT: {
777 0 : ulong u; fd_ulong_svw_dec( info.val, &u );
778 0 : PRINT( "%s ", first ? "" : "," );
779 0 : PRINT_JSON_CSTR( info.key, info.key_sz );
780 0 : PRINT( ": %i", (int)fd_long_zz_dec( u ) );
781 0 : first = 0;
782 0 : break;
783 0 : }
784 0 : }
785 0 : }
786 0 : PRINT( "%s} }%s\n", first ? "" : " ", i==topo->obj_cnt-1UL ? "" : "," );
787 0 : }
788 0 : PRINT( " ],\n" );
789 :
790 0 : PRINT( " \"links\": [\n" );
791 0 : for( ulong i=0UL; i<topo->link_cnt; i++ ) {
792 0 : fd_topo_link_t * link = &topo->links[ i ];
793 0 : PRINT( " { \"id\": %lu, \"name\": ", i );
794 0 : PRINT_JSON_CSTR( link->name, sizeof(link->name) );
795 0 : PRINT( ", \"kind_id\": %lu, \"wksp_id\": %lu, \"depth\": %lu, \"mtu\": %lu, \"burst\": %lu, \"dcache_bytes\": %lu }%s\n",
796 0 : link->kind_id, topo->objs[ link->mcache_obj_id ].wksp_id, link->depth, link->mtu, link->burst,
797 0 : fd_dcache_req_data_sz( link->mtu, link->depth, link->burst, 1 ),
798 0 : i==topo->link_cnt-1UL ? "" : "," );
799 0 : }
800 0 : PRINT( " ],\n" );
801 :
802 0 : PRINT( " \"tiles\": [\n" );
803 0 : for( ulong i=0UL; i<topo->tile_cnt; i++ ) {
804 0 : fd_topo_tile_t * tile = &topo->tiles[ i ];
805 :
806 0 : ulong tile_numa = 0UL;
807 0 : if( tile->cpu_idx!=ULONG_MAX ) tile_numa = fd_shmem_numa_idx( tile->cpu_idx );
808 0 : else tile_numa = topo->workspaces[ topo->objs[ tile->tile_obj_id ].wksp_id ].numa_idx;
809 :
810 0 : PRINT( " { \"id\": %lu, \"name\": ", i );
811 0 : PRINT_JSON_CSTR( tile->name, sizeof(tile->name) );
812 0 : PRINT( ", \"kind_id\": %lu, \"wksp_id\": %lu, ",
813 0 : tile->kind_id, topo->objs[ tile->tile_obj_id ].wksp_id );
814 0 : if( tile->cpu_idx!=ULONG_MAX ) PRINT( "\"cpu_idx\": %lu, ", tile->cpu_idx );
815 0 : else PRINT( "\"cpu_idx\": null, " );
816 0 : PRINT( "\"floats\": %s, ", tile->floats ? "true" : "false" );
817 0 : PRINT( "\"numa_idx\": %lu, \"mlock_bytes\": %lu, \"in_links\": [", tile_numa, fd_topo_mlock_max_tile1( topo, tile ) );
818 0 : for( ulong j=0UL; j<tile->in_cnt; j++ ) {
819 0 : PRINT( "%s{ \"link_id\": %lu, \"reliable\": %s }", j ? ", " : "", tile->in_link_id[ j ], tile->in_link_reliable[ j ] ? "true" : "false" );
820 0 : }
821 0 : PRINT( "], \"out_links\": [" );
822 0 : for( ulong j=0UL; j<tile->out_cnt; j++ ) PRINT( "%s%lu", j ? ", " : "", tile->out_link_id[ j ] );
823 0 : PRINT( "], \"objects\": [" );
824 0 : for( ulong j=0UL; j<tile->uses_obj_cnt; j++ ) {
825 0 : int is_rw = tile->uses_obj_mode[ j ] == FD_SHMEM_JOIN_MODE_READ_WRITE;
826 0 : PRINT( "%s{ \"obj_id\": %lu, \"mode\": \"%s\" }", j ? ", " : "", tile->uses_obj_id[ j ], is_rw ? "rw" : "ro" );
827 0 : }
828 0 : PRINT( "] }%s\n", i==topo->tile_cnt-1UL ? "" : "," );
829 0 : }
830 0 : PRINT( " ]\n}" );
831 :
832 0 : #undef PRINT
833 :
834 0 : FD_LOG_STDOUT(( "%s\n", message ));
835 0 : }
|