LCOV - code coverage report
Current view: top level - disco/topo - fd_topo.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 95 603 15.8 %
Date: 2026-11-05 04:25:06 Functions: 9 27 33.3 %

          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 : }

Generated by: LCOV version 1.14