LCOV - code coverage report
Current view: top level - disco/topo - fd_topo.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 597 0.0 %
Date: 2026-10-22 18:10:43 Functions: 0 27 0.0 %

          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           0 :                    ulong             obj_id ) {
      18           0 :   fd_topo_obj_t const * obj = &topo->objs[ obj_id ];
      19           0 :   if( FD_UNLIKELY( obj_id==ULONG_MAX ) ) FD_LOG_CRIT(( "invalid obj_id ULONG_MAX" ));
      20           0 :   if( FD_UNLIKELY( obj_id>=FD_TOPO_MAX_OBJS ) ) FD_LOG_CRIT(( "invalid obj_id %lu", obj_id ));
      21           0 :   FD_TEST( obj->id == obj_id );
      22           0 :   FD_TEST( obj->offset );
      23           0 :   return (void *)((ulong)topo->workspaces[ obj->wksp_id ].wksp + obj->offset);
      24           0 : }
      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           0 : 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           0 :   (void)tile;
     219           0 :   return (FD_TILE_PRIVATE_STACK_SZ/FD_SHMEM_HUGE_PAGE_SZ)+2UL;
     220           0 : }
     221             : 
     222             : FD_FN_PURE static ulong
     223           0 : fd_topo_tile_extra_normal_pages( fd_topo_tile_t const * tile ) {
     224           0 :   ulong key_pages = 0UL;
     225           0 :   if( FD_UNLIKELY( tile->id_keyswitch_obj_id!=ULONG_MAX ) ) {
     226             :     /* Certain tiles using fd_keyload_load need normal pages to hold
     227             :        key material. */
     228           0 :     key_pages += 5UL;
     229           0 :   }
     230           0 :   if( FD_UNLIKELY( tile->av_keyswitch_obj_id!=ULONG_MAX ) ) {
     231             :     /* Certain tiles using fd_keyload_load need normal pages to hold
     232             :        key material. */
     233           0 :     key_pages += 5UL;
     234           0 :   }
     235             : 
     236           0 :   if( !strcmp( tile->name, "net" ) ) {
     237             :       /* net tile uses normal pages to hold xsk rings */
     238             : 
     239             :       /* xdp_desc struct is in linux UAPI so its size can be
     240             :          safely assumed */
     241           0 :       ulong xdp_desc_sz_bytes    = 16UL;
     242           0 :       ulong xsk_rings_sz_bytes   = 0UL;
     243           0 :       ulong xdp_address_sz_bytes = sizeof(ulong);
     244             : 
     245             :       /* rx ring */
     246           0 :       xsk_rings_sz_bytes += tile->xdp.xdp_rx_queue_size * xdp_desc_sz_bytes;
     247             :       /* tx ring */
     248           0 :       xsk_rings_sz_bytes += tile->xdp.xdp_tx_queue_size * xdp_desc_sz_bytes;
     249             : 
     250             :       /* completion ring */
     251           0 :       xsk_rings_sz_bytes += tile->xdp.xdp_tx_queue_size * xdp_address_sz_bytes;
     252             :       /* fill ring */
     253           0 :       xsk_rings_sz_bytes += tile->xdp.xdp_rx_queue_size * xdp_address_sz_bytes * 2UL;
     254             : 
     255           0 :       ulong xsk_cnt = ( strcmp( tile->xdp.if_virt, "lo" ) && !tile->kind_id ) ? 2UL : 1UL;
     256           0 :       key_pages += xsk_cnt * fd_ulong_align_up( xsk_rings_sz_bytes, FD_SHMEM_NORMAL_PAGE_SZ ) / FD_SHMEM_NORMAL_PAGE_SZ;
     257             : 
     258             :       /* All 4 rings must store a ring header. This is 320 bytes
     259             :          per ring as of linux v6.18.3, however could change in
     260             :          the future so allow up to a full 4KB page per ring to
     261             :          be safe. */
     262           0 :       key_pages += xsk_cnt * 4UL;
     263           0 :   }
     264             : 
     265           0 :   return key_pages;
     266           0 : }
     267             : 
     268             : FD_FN_PURE static ulong
     269             : fd_topo_mlock_max_tile1( fd_topo_t const *      topo,
     270           0 :                          fd_topo_tile_t const * tile ) {
     271           0 :   ulong tile_mem = 0UL;
     272             : 
     273           0 :   for( ulong i=0UL; i<topo->wksp_cnt; i++ ) {
     274           0 :     if( FD_UNLIKELY( -1!=tile_needs_wksp( topo, tile, i ) ) )
     275           0 :       tile_mem += topo->workspaces[ i ].page_cnt * topo->workspaces[ i ].page_sz;
     276           0 :   }
     277             : 
     278           0 :   return tile_mem +
     279           0 :       fd_topo_tile_extra_huge_pages( tile ) * FD_SHMEM_HUGE_PAGE_SZ +
     280           0 :       fd_topo_tile_extra_normal_pages( tile ) * FD_SHMEM_NORMAL_PAGE_SZ;
     281           0 : }
     282             : 
     283             : FD_FN_PURE ulong
     284           0 : fd_topo_mlock_max_tile( fd_topo_t const * topo ) {
     285           0 :   ulong highest_tile_mem = 0UL;
     286           0 :   for( ulong i=0UL; i<topo->tile_cnt; i++ ) {
     287           0 :     fd_topo_tile_t const * tile = &topo->tiles[ i ];
     288           0 :     highest_tile_mem = fd_ulong_max( highest_tile_mem, fd_topo_mlock_max_tile1( topo, tile ) );
     289           0 :   }
     290             : 
     291           0 :   return highest_tile_mem + fd_topo_code_footprint( 0 ) + fd_topo_code_footprint( 1 );
     292           0 : }
     293             : 
     294             : static int
     295             : fd_topo_code_cb( struct dl_phdr_info * info,
     296             :                  size_t                info_sz,
     297           0 :                  void *                ctx ) {
     298           0 :   (void)info_sz;
     299             : 
     300           0 :   for( ulong i=0UL; i<info->dlpi_phnum; i++ ) {
     301           0 :     ElfW(Phdr) const * ph = &info->dlpi_phdr[ i ];
     302           0 :     if( ph->p_type!=PT_LOAD ) continue;
     303             : 
     304           0 :     ulong sz = (ph->p_flags & PF_W) ? ph->p_filesz : ph->p_memsz;
     305           0 :     ulong lo = fd_ulong_align_dn( info->dlpi_addr+ph->p_vaddr,    FD_SHMEM_NORMAL_PAGE_SZ );
     306           0 :     ulong hi = fd_ulong_align_up( info->dlpi_addr+ph->p_vaddr+sz, FD_SHMEM_NORMAL_PAGE_SZ );
     307           0 :     if( FD_UNLIKELY( hi<=lo ) ) continue;
     308             : 
     309           0 :     if( FD_UNLIKELY( ctx ) ) {
     310           0 :       ((ulong *)ctx)[ !!(ph->p_flags & PF_W) ] += hi-lo;
     311           0 :     } else {
     312           0 :       if( FD_UNLIKELY( -1==fd_numa_mlock( (void *)lo, hi-lo ) ) ) {
     313           0 :         FD_LOG_WARNING(( "mlock(\"%s\",%lu KiB) failed (%i-%s); code may page fault under memory pressure",
     314           0 :                          info->dlpi_name[0] ? info->dlpi_name : "main", (hi-lo)>>10, errno, fd_io_strerror( errno ) ));
     315           0 :       }
     316           0 :     }
     317           0 :   }
     318             : 
     319           0 :   return 0;
     320           0 : }
     321             : 
     322             : ulong
     323           0 : fd_topo_code_footprint( int writable ) {
     324           0 :   ulong sz[ 2 ] = { 0UL, 0UL };
     325           0 :   dl_iterate_phdr( fd_topo_code_cb, sz );
     326           0 :   return sz[ !!writable ];
     327           0 : }
     328             : 
     329             : void
     330           0 : fd_topo_mlock_code( void ) {
     331           0 :   dl_iterate_phdr( fd_topo_code_cb, NULL );
     332           0 : }
     333             : 
     334             : FD_FN_PURE ulong
     335             : fd_topo_gigantic_page_cnt( fd_topo_t const * topo,
     336           0 :                            ulong             numa_idx ) {
     337           0 :   ulong result = 0UL;
     338           0 :   for( ulong i=0UL; i<topo->wksp_cnt; i++ ) {
     339           0 :     fd_topo_wksp_t const * wksp = &topo->workspaces[ i ];
     340           0 :     if( FD_LIKELY( wksp->numa_idx!=numa_idx ) ) continue;
     341             : 
     342           0 :     if( FD_LIKELY( wksp->page_sz==FD_SHMEM_GIGANTIC_PAGE_SZ ) ) {
     343           0 :       result += wksp->page_cnt;
     344           0 :     }
     345           0 :   }
     346           0 :   return result;
     347           0 : }
     348             : 
     349             : FD_FN_PURE ulong
     350             : fd_topo_huge_page_cnt( fd_topo_t const * topo,
     351             :                        ulong             numa_idx,
     352           0 :                        int               include_anonymous ) {
     353           0 :   ulong result = 0UL;
     354           0 :   for( ulong i=0UL; i<topo->wksp_cnt; i++ ) {
     355           0 :     fd_topo_wksp_t const * wksp = &topo->workspaces[ i ];
     356           0 :     if( FD_LIKELY( wksp->numa_idx!=numa_idx ) ) continue;
     357             : 
     358           0 :     if( FD_LIKELY( wksp->page_sz==FD_SHMEM_HUGE_PAGE_SZ ) ) {
     359           0 :       result += wksp->page_cnt;
     360           0 :     }
     361           0 :   }
     362             : 
     363             :   /* The stack huge pages are also placed in the hugetlbfs. */
     364           0 :   for( ulong i=0UL; i<topo->tile_cnt; i++ ) {
     365           0 :     result += fd_topo_tile_extra_huge_pages( &topo->tiles[ i ] );
     366           0 :   }
     367             : 
     368             :   /* No anonymous huge pages in use yet. */
     369           0 :   (void)include_anonymous;
     370             : 
     371           0 :   return result;
     372           0 : }
     373             : 
     374             : FD_FN_PURE ulong
     375           0 : fd_topo_normal_page_cnt( fd_topo_t const * topo ) {
     376           0 :   ulong result = 0UL;
     377           0 :   for( ulong i=0UL; i<topo->tile_cnt; i++ ) {
     378           0 :     result += fd_topo_tile_extra_normal_pages( &topo->tiles[ i ] );
     379           0 :   }
     380           0 :   ulong code_sz = fd_topo_code_footprint( 0 ) + topo->tile_cnt*fd_topo_code_footprint( 1 );
     381           0 :   return result + code_sz/FD_SHMEM_NORMAL_PAGE_SZ;
     382           0 : }
     383             : 
     384             : FD_FN_PURE ulong
     385           0 : fd_topo_mlock( fd_topo_t const * topo ) {
     386           0 :   ulong result = 0UL;
     387           0 :   for( ulong i=0UL; i<topo->wksp_cnt; i++ ) {
     388           0 :     result += topo->workspaces[ i ].page_cnt * topo->workspaces[ i ].page_sz;
     389           0 :   }
     390           0 :   return result;
     391           0 : }
     392             : 
     393             : static void
     394           0 : fd_topo_mem_sz_string( ulong sz, char out[static 24] ) {
     395           0 :   if( FD_LIKELY( sz >= FD_SHMEM_GIGANTIC_PAGE_SZ ) ) {
     396           0 :     FD_TEST( fd_cstr_printf_check( out, 24, NULL, "%.1f GiB", (double)sz / (double)(1UL << 30) ) );
     397           0 :   } else if( FD_LIKELY( sz >= 1048576 ) ) {
     398           0 :     FD_TEST( fd_cstr_printf_check( out, 24, NULL, "%.1f MiB", (double)sz / (double)(1UL << 20) ) );
     399           0 :   } else if( FD_LIKELY( sz >= 1024 ) ) {
     400           0 :     FD_TEST( fd_cstr_printf_check( out, 24, NULL, "%.1f KiB", (double)sz / (double)(1UL << 10) ) );
     401           0 :   } else {
     402           0 :     FD_TEST( fd_cstr_printf_check( out, 24, NULL, "%lu B", sz ) );
     403           0 :   }
     404           0 : }
     405             : 
     406             : /* Style sizes by magnitude so the big numbers pop */
     407             : 
     408             : static char const *
     409             : fd_topo_mem_sz_style( ulong        sz,
     410             :                       char const * bold,
     411           0 :                       char const * dim ) {
     412           0 :   if( sz>=(1UL<<30) ) return bold;
     413           0 :   if( sz< (1UL<<20) ) return dim;
     414           0 :   return "";
     415           0 : }
     416             : 
     417             : void
     418             : fd_topo_print_log( int         stdout,
     419           0 :                    fd_topo_t * topo ) {
     420           0 :   char message[ 32UL*4096UL ] = {0}; /* Same as FD_LOG_BUF_SZ */
     421             : 
     422           0 :   char * cur = message;
     423           0 :   ulong remaining = sizeof(message) - 1; /* Leave one character at the end to ensure NUL terminated */
     424             : 
     425           0 : #define PRINT( ... ) do {                                                             \
     426           0 :     int n = snprintf( cur, remaining, __VA_ARGS__ );                                  \
     427           0 :     if( FD_UNLIKELY( n < 0 ) ) FD_LOG_ERR(( "snprintf failed" ));                     \
     428           0 :     if( FD_UNLIKELY( (ulong)n >= remaining ) ) {                                      \
     429             :       /* stdout mode flushes and retries, log mode keeps one message */               \
     430           0 :       if( FD_UNLIKELY( !stdout ) ) FD_LOG_ERR(( "snprintf overflow" ));               \
     431           0 :       *cur = '\0';                                                                    \
     432           0 :       FD_LOG_STDOUT(( "%s", message ));                                               \
     433           0 :       cur = message; remaining = sizeof(message) - 1;                                 \
     434           0 :       n = snprintf( cur, remaining, __VA_ARGS__ );                                    \
     435           0 :       if( FD_UNLIKELY( n < 0 ) ) FD_LOG_ERR(( "snprintf failed" ));                   \
     436           0 :       if( FD_UNLIKELY( (ulong)n >= remaining ) ) FD_LOG_ERR(( "snprintf overflow" )); \
     437           0 :     }                                                                                 \
     438           0 :     remaining -= (ulong)n;                                                            \
     439           0 :     cur += n;                                                                         \
     440           0 :   } while( 0 )
     441             : 
     442           0 :   int color = stdout && fd_log_colorize() && isatty( STDOUT_FILENO );
     443           0 :   char const * c_bold   = color ? "\033[1m" : "";
     444           0 :   char const * c_dim    = color ? "\033[2m" : "";
     445           0 :   char const * c_normal = color ? "\033[0m" : "";
     446             : 
     447           0 :   char const * sep = "";
     448             : 
     449           0 : #define SECTION( ... ) do {                                                            \
     450           0 :     char _title[ 64 ];                                                                \
     451           0 :     FD_TEST( fd_cstr_printf_check( _title, sizeof(_title), NULL, __VA_ARGS__ ) );     \
     452           0 :     PRINT( "%s%s──%s %s%s%s %s", sep, c_dim, c_normal, c_bold, _title, c_normal, c_dim ); \
     453           0 :     for( ulong _i=strlen( _title ); _i<76UL; _i++ ) PRINT( "─" );                     \
     454           0 :     PRINT( "%s\n", c_normal );                                                        \
     455           0 :     sep = "\n";                                                                       \
     456           0 :   } while( 0 )
     457             : 
     458           0 :   SECTION( "Summary" );
     459             : 
     460             :   /* The logic to compute number of stack pages is taken from
     461             :      fd_tile_thread.cxx, in function fd_topo_tile_stack_join, and this
     462             :      should match that. */
     463           0 :   ulong stack_pages = topo->tile_cnt * FD_SHMEM_HUGE_PAGE_SZ * ((FD_TILE_PRIVATE_STACK_SZ/FD_SHMEM_HUGE_PAGE_SZ)+2UL);
     464             : 
     465           0 :   ulong normal_page_bytes = fd_topo_normal_page_cnt( topo ) * FD_SHMEM_NORMAL_PAGE_SZ;
     466           0 :   ulong total_bytes = fd_topo_mlock( topo ) + stack_pages + normal_page_bytes;
     467             : 
     468           0 :   PRINT("  %s%-23s%s  %lu\n", c_dim, "Total Tiles", c_normal, topo->tile_cnt );
     469           0 :   PRINT("  %s%-23s%s  %s%lu GiB + %lu MiB + %lu KiB%s  %s(%lu bytes)%s\n",
     470           0 :     c_dim, "Total Memory Locked", c_normal,
     471           0 :     c_bold,
     472           0 :     total_bytes / (1 << 30),
     473           0 :     (total_bytes % (1 << 30)) / (1 << 20),
     474           0 :     (total_bytes % (1 << 20)) / (1 << 10),
     475           0 :     c_normal,
     476           0 :     c_dim, total_bytes, c_normal );
     477             : 
     478           0 :   ulong required_gigantic_pages = 0UL;
     479           0 :   ulong required_huge_pages = 0UL;
     480             : 
     481           0 :   ulong numa_node_cnt = fd_shmem_numa_cnt();
     482           0 :   for( ulong i=0UL; i<numa_node_cnt; i++ ) {
     483           0 :     required_gigantic_pages += fd_topo_gigantic_page_cnt( topo, i );
     484           0 :     required_huge_pages += fd_topo_huge_page_cnt( topo, i, 0 );
     485           0 :   }
     486           0 :   PRINT("  %s%-23s%s  %lu\n", c_dim, "Required Gigantic Pages", c_normal, required_gigantic_pages );
     487           0 :   PRINT("  %s%-23s%s  %lu\n", c_dim, "Required Huge Pages", c_normal, required_huge_pages );
     488           0 :   PRINT("  %s%-23s%s  %lu\n", c_dim, "Required Normal Pages", c_normal, fd_topo_normal_page_cnt( topo ) );
     489           0 :   if( FD_UNLIKELY( numa_node_cnt>1UL ) ) {
     490           0 :     for( ulong i=0UL; i<numa_node_cnt; i++ ) {
     491           0 :       char label[ 40 ];
     492           0 :       FD_TEST( fd_cstr_printf_check( label, sizeof(label), NULL, "NUMA node %lu", i ) );
     493           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 ) );
     494           0 :     }
     495           0 :   }
     496             : 
     497           0 :   if( FD_LIKELY( !stdout ) ) {
     498           0 :     if( FD_LIKELY( cur>message ) ) cur[ -1 ] = '\0'; /* Strip trailing newline */
     499           0 :     FD_LOG_INFO(( "%s", message ));
     500           0 :     return;
     501           0 :   }
     502             : 
     503           0 :   SECTION( "Workspaces (%lu)", topo->wksp_cnt );
     504           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 );
     505           0 :   for( ulong i=0UL; i<topo->wksp_cnt; i++ ) {
     506           0 :     fd_topo_wksp_t * wksp = &topo->workspaces[ i ];
     507             : 
     508           0 :     ulong sz = wksp->page_sz * wksp->page_cnt;
     509           0 :     char size[ 24 ];
     510           0 :     fd_topo_mem_sz_string( sz, size );
     511           0 :     PRINT( "  %3lu  %s%10s%s  %-13s  %5lu  %-8s  %4lu  %12lu  %s%12lu%s\n",
     512           0 :            i, fd_topo_mem_sz_style( sz, c_bold, c_dim ), size, c_normal,
     513           0 :            wksp->name, wksp->page_cnt, fd_shmem_page_sz_to_cstr( wksp->page_sz ), wksp->numa_idx,
     514           0 :            wksp->known_footprint, c_dim, wksp->total_footprint - wksp->known_footprint, c_normal );
     515           0 :   }
     516             : 
     517           0 :   SECTION( "Objects (%lu)", topo->obj_cnt );
     518           0 :   PRINT( "  %s%4s  %10s  %-13s  %-13s  %4s  %12s  %s%s\n", c_dim, "ID", "SIZE", "WORKSPACE", "OBJECT", "WKSP", "OFFSET", "PROPERTIES", c_normal );
     519           0 :   for( ulong i=0UL; i<topo->obj_cnt; i++ ) {
     520           0 :     fd_topo_obj_t * obj = &topo->objs[ i ];
     521             : 
     522           0 :     char size[ 24 ];
     523           0 :     fd_topo_mem_sz_string( obj->footprint, size );
     524           0 :     PRINT( "  %4lu  %s%10s%s  %-13s  %-13s  %4lu  %12lu ",
     525           0 :            i, fd_topo_mem_sz_style( obj->footprint, c_bold, c_dim ), size, c_normal,
     526           0 :            topo->workspaces[ obj->wksp_id ].name, obj->name,
     527           0 :            obj->wksp_id, obj->offset );
     528           0 :     for( fd_pod_iter_t iter=fd_pod_iter_init( fd_pod_queryf_subpod( topo->props, "obj.%lu", obj->id ) );
     529           0 :          !fd_pod_iter_done( iter );
     530           0 :          iter=fd_pod_iter_next( iter ) ) {
     531           0 :       fd_pod_info_t info = fd_pod_iter_info( iter );
     532           0 :       if( !strcmp( info.key, "seed" ) ) continue; /* key is a cstr (key_sz includes NUL) */
     533           0 :       PRINT( " %s%.*s=%s", c_dim, (int)info.key_sz, info.key, c_normal );
     534           0 :       switch( info.val_type ) {
     535           0 :       case FD_POD_VAL_TYPE_CSTR:
     536           0 :         PRINT( "%.*s", (int)info.val_sz, (char const *)info.val );
     537           0 :         break;
     538           0 :       case FD_POD_VAL_TYPE_ULONG: {
     539           0 :         ulong val; fd_ulong_svw_dec( info.val, &val );
     540           0 :         PRINT( "%lu", val );
     541           0 :         break;
     542           0 :       }
     543           0 :       case FD_POD_VAL_TYPE_INT: {
     544           0 :         ulong u; fd_ulong_svw_dec( info.val, &u );
     545           0 :         PRINT( "%i", (int)fd_long_zz_dec( u ) );
     546           0 :         break;
     547           0 :       }
     548           0 :       }
     549           0 :     }
     550           0 :     PRINT( "\n" );
     551           0 :   }
     552             : 
     553           0 :   SECTION( "Links (%lu)", topo->link_cnt );
     554           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 );
     555           0 :   for( ulong i=0UL; i<topo->link_cnt; i++ ) {
     556           0 :     fd_topo_link_t * link = &topo->links[ i ];
     557             : 
     558           0 :     ulong sz = fd_dcache_req_data_sz( link->mtu, link->depth, link->burst, 1 );
     559           0 :     char size[ 24 ];
     560           0 :     fd_topo_mem_sz_string( sz, size );
     561           0 :     PRINT( "  %3lu  %s%10s%s  %-13s  %4lu  %4lu  %8lu  %9lu  %5lu\n",
     562           0 :            i, fd_topo_mem_sz_style( sz, c_bold, c_dim ), size, c_normal,
     563           0 :            link->name, link->kind_id, topo->objs[ link->mcache_obj_id ].wksp_id, link->depth, link->mtu, link->burst );
     564           0 :   }
     565             : 
     566           0 : #define PRINTIN( ... ) do {                                                            \
     567           0 :     int n = snprintf( cur_in, remaining_in, __VA_ARGS__ );                             \
     568           0 :     if( FD_UNLIKELY( n < 0 ) ) FD_LOG_ERR(( "snprintf failed" ));                      \
     569           0 :     if( FD_UNLIKELY( (ulong)n >= remaining_in ) ) FD_LOG_ERR(( "snprintf overflow" )); \
     570           0 :     remaining_in -= (ulong)n;                                                          \
     571           0 :     cur_in += n;                                                                       \
     572           0 :   } while( 0 )
     573             : 
     574           0 : #define PRINTOUT( ... ) do {                                                            \
     575           0 :     int n = snprintf( cur_out, remaining_out, __VA_ARGS__ );                            \
     576           0 :     if( FD_UNLIKELY( n < 0 ) ) FD_LOG_ERR(( "snprintf failed" ));                       \
     577           0 :     if( FD_UNLIKELY( (ulong)n >= remaining_out ) ) FD_LOG_ERR(( "snprintf overflow" )); \
     578           0 :     remaining_out -= (ulong)n;                                                          \
     579           0 :     cur_out += n;                                                                       \
     580           0 :   } while( 0 )
     581             : 
     582           0 :   SECTION( "Tiles (%lu)", topo->tile_cnt );
     583           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 );
     584           0 :   for( ulong i=0UL; i<topo->tile_cnt; i++ ) {
     585           0 :     fd_topo_tile_t * tile = &topo->tiles[ i ];
     586             : 
     587             :     /* Worst case ", -1023" per link */
     588           0 :     char in[ 8UL*FD_TOPO_MAX_TILE_IN_LINKS ] = {0};
     589           0 :     char * cur_in = in;
     590           0 :     ulong remaining_in = sizeof( in ) - 1;
     591             : 
     592           0 :     for( ulong j=0UL; j<tile->in_cnt; j++ ) {
     593           0 :       if( FD_LIKELY( j != 0 ) ) PRINTIN( ", " );
     594           0 :       if( FD_LIKELY( tile->in_link_reliable[ j ] ) ) PRINTIN( "%2lu", tile->in_link_id[ j ] );
     595           0 :       else PRINTIN( "%2ld", (long)-tile->in_link_id[ j ] );
     596           0 :     }
     597             : 
     598           0 :     char out[ 8UL*FD_TOPO_MAX_TILE_OUT_LINKS ] = {0};
     599           0 :     char * cur_out = out;
     600           0 :     ulong remaining_out = sizeof( out ) - 1;
     601             : 
     602           0 :     for( ulong j=0UL; j<tile->out_cnt; j++ ) {
     603           0 :       if( FD_LIKELY( j != 0 ) ) PRINTOUT( ", " );
     604           0 :       PRINTOUT( "%2lu", tile->out_link_id[ j ] );
     605           0 :     }
     606             : 
     607             :     /* Determine tile's NUMA node either based on CPU or wksp affinity */
     608           0 :     ulong tile_numa = 0UL;
     609           0 :     if( tile->cpu_idx!=ULONG_MAX ) {
     610           0 :       tile_numa = fd_shmem_numa_idx( tile->cpu_idx );
     611           0 :     } else {
     612           0 :       tile_numa = topo->workspaces[ topo->objs[ tile->tile_obj_id ].wksp_id ].numa_idx;
     613           0 :     }
     614             : 
     615           0 :     ulong mlock = fd_topo_mlock_max_tile1( topo, tile );
     616           0 :     char size[ 24 ];
     617           0 :     fd_topo_mem_sz_string( mlock, size );
     618           0 :     PRINT( "  %3lu  %s%10s%s  %-13s  %4lu  %4lu  ",
     619           0 :            i, fd_topo_mem_sz_style( mlock, c_bold, c_dim ), size, c_normal,
     620           0 :            tile->name, tile->kind_id, topo->objs[ tile->tile_obj_id ].wksp_id );
     621           0 :     if( tile->cpu_idx!=ULONG_MAX ) {
     622           0 :       PRINT( "%s%4lu%s", tile->floats ? c_dim : "", tile->cpu_idx, tile->floats ? c_normal : "" );
     623           0 :     } else {
     624           0 :       PRINT( "%s%4s%s", c_dim, "any", c_normal );
     625           0 :     }
     626             : 
     627           0 :     PRINT( "  %4lu  %sin=%s[%s]  %sout=%s[%s]  %sobjs=[", tile_numa, c_dim, c_normal, in, c_dim, c_normal, out, c_dim );
     628           0 :     for( ulong j=0UL; j<tile->uses_obj_cnt; j++ ) {
     629           0 :       if( FD_LIKELY( j!=0 ) ) PRINT( " " );
     630           0 :       int is_rw = tile->uses_obj_mode[ j ] == FD_SHMEM_JOIN_MODE_READ_WRITE;
     631           0 :       PRINT( "%lu:%s", tile->uses_obj_id[ j ], is_rw?"rw":"ro" );
     632           0 :     }
     633           0 :     PRINT( "]%s", c_normal );
     634             : 
     635             : #if 0
     636             :     PRINT( "  wksps=[" );
     637             :     for( ulong j=0UL; j<topo->wksp_cnt; j++ ) {
     638             :       int mode = tile_needs_wksp( topo, tile, j );
     639             :       if( FD_UNLIKELY( -1!=mode ) ) {
     640             :         if( FD_LIKELY( j!=0 ) ) PRINT( " " );
     641             :         PRINT( "%s:%s", topo->workspaces[ j ].name, mode==FD_SHMEM_JOIN_MODE_READ_WRITE?"rw":"ro" );
     642             :       }
     643             :     }
     644             :     PRINT( "]" );
     645             : #endif
     646             : 
     647           0 :     if( FD_LIKELY( i != topo->tile_cnt-1 ) ) PRINT( "\n" );
     648           0 :   }
     649             : 
     650           0 :   FD_LOG_STDOUT(( "%s\n", message ));
     651             : 
     652           0 : #undef PRINT
     653           0 : #undef PRINTIN
     654           0 : #undef PRINTOUT
     655           0 : #undef SECTION
     656           0 : }
     657             : 
     658             : /* PRINT_JSON_CSTR streams src (sz bytes, embedded NULs stop early)
     659             :    as a JSON string literal, escaping quotes, backslashes and control
     660             :    characters.  No fixed intermediate buffer: expansion cannot
     661             :    truncate regardless of src length or content. */
     662             : 
     663           0 : #define PRINT_JSON_CSTR( src, sz ) do {                                               \
     664           0 :     char const * _s  = (src);                                                         \
     665           0 :     ulong        _sz = (sz);                                                          \
     666           0 :     PRINT( "\"" );                                                                    \
     667           0 :     for( ulong _i=0UL; _i<_sz && _s[ _i ]; _i++ ) {                                   \
     668           0 :       uchar _c = (uchar)_s[ _i ];                                                     \
     669           0 :       if(      FD_UNLIKELY( _c=='"' || _c=='\\' ) ) PRINT( "\\%c", (char)_c );        \
     670           0 :       else if( FD_UNLIKELY( _c<0x20 ) )             PRINT( "\\u%04x", (uint)_c );     \
     671           0 :       else                                          PRINT( "%c", (char)_c );          \
     672           0 :     }                                                                                 \
     673           0 :     PRINT( "\"" );                                                                    \
     674           0 :   } while( 0 )
     675             : 
     676             : void
     677           0 : fd_topo_print_json( fd_topo_t * topo ) {
     678           0 :   char message[ 32UL*4096UL ] = {0};
     679             : 
     680           0 :   char * cur = message;
     681           0 :   ulong remaining = sizeof(message) - 1;
     682             : 
     683           0 : #define PRINT( ... ) do {                                                             \
     684           0 :     int n = snprintf( cur, remaining, __VA_ARGS__ );                                  \
     685           0 :     if( FD_UNLIKELY( n < 0 ) ) FD_LOG_ERR(( "snprintf failed" ));                     \
     686           0 :     if( FD_UNLIKELY( (ulong)n >= remaining ) ) {                                      \
     687           0 :       *cur = '\0';                                                                    \
     688           0 :       FD_LOG_STDOUT(( "%s", message ));                                               \
     689           0 :       cur = message; remaining = sizeof(message) - 1;                                 \
     690           0 :       n = snprintf( cur, remaining, __VA_ARGS__ );                                    \
     691           0 :       if( FD_UNLIKELY( n < 0 ) ) FD_LOG_ERR(( "snprintf failed" ));                   \
     692           0 :       if( FD_UNLIKELY( (ulong)n >= remaining ) ) FD_LOG_ERR(( "snprintf overflow" )); \
     693           0 :     }                                                                                 \
     694           0 :     remaining -= (ulong)n;                                                            \
     695           0 :     cur += n;                                                                         \
     696           0 :   } while( 0 )
     697             : 
     698           0 :   ulong stack_pages = topo->tile_cnt * FD_SHMEM_HUGE_PAGE_SZ * ((FD_TILE_PRIVATE_STACK_SZ/FD_SHMEM_HUGE_PAGE_SZ)+2UL);
     699           0 :   ulong normal_page_bytes = fd_topo_normal_page_cnt( topo ) * FD_SHMEM_NORMAL_PAGE_SZ;
     700           0 :   ulong total_bytes = fd_topo_mlock( topo ) + stack_pages + normal_page_bytes;
     701             : 
     702           0 :   ulong required_gigantic_pages = 0UL;
     703           0 :   ulong required_huge_pages = 0UL;
     704           0 :   ulong numa_node_cnt = fd_shmem_numa_cnt();
     705           0 :   for( ulong i=0UL; i<numa_node_cnt; i++ ) {
     706           0 :     required_gigantic_pages += fd_topo_gigantic_page_cnt( topo, i );
     707           0 :     required_huge_pages += fd_topo_huge_page_cnt( topo, i, 0 );
     708           0 :   }
     709             : 
     710           0 :   PRINT( "{\n  \"summary\": {\n" );
     711           0 :   PRINT( "    \"tile_cnt\": %lu,\n", topo->tile_cnt );
     712           0 :   PRINT( "    \"total_memory_locked_bytes\": %lu,\n", total_bytes );
     713           0 :   PRINT( "    \"required_gigantic_pages\": %lu,\n", required_gigantic_pages );
     714           0 :   PRINT( "    \"required_huge_pages\": %lu,\n", required_huge_pages );
     715           0 :   PRINT( "    \"required_normal_pages\": %lu,\n", fd_topo_normal_page_cnt( topo ) );
     716           0 :   PRINT( "    \"numa_nodes\": [" );
     717           0 :   for( ulong i=0UL; i<numa_node_cnt; i++ ) {
     718           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 ) );
     719           0 :   }
     720           0 :   PRINT( "\n    ]\n  },\n" );
     721             : 
     722           0 :   PRINT( "  \"workspaces\": [\n" );
     723           0 :   for( ulong i=0UL; i<topo->wksp_cnt; i++ ) {
     724           0 :     fd_topo_wksp_t * wksp = &topo->workspaces[ i ];
     725           0 :     PRINT( "    { \"id\": %lu, \"name\": ", i );
     726           0 :     PRINT_JSON_CSTR( wksp->name, sizeof(wksp->name) );
     727           0 :     PRINT( ", \"page_cnt\": %lu, \"page_sz\": \"%s\", \"numa_idx\": %lu, \"total_bytes\": %lu, \"footprint\": %lu, \"loose\": %lu }%s\n",
     728           0 :            wksp->page_cnt, fd_shmem_page_sz_to_cstr( wksp->page_sz ), wksp->numa_idx,
     729           0 :            wksp->page_cnt * wksp->page_sz, wksp->known_footprint, wksp->total_footprint - wksp->known_footprint,
     730           0 :            i==topo->wksp_cnt-1UL ? "" : "," );
     731           0 :   }
     732           0 :   PRINT( "  ],\n" );
     733             : 
     734           0 :   PRINT( "  \"objects\": [\n" );
     735           0 :   for( ulong i=0UL; i<topo->obj_cnt; i++ ) {
     736           0 :     fd_topo_obj_t * obj = &topo->objs[ i ];
     737           0 :     PRINT( "    { \"id\": %lu, \"name\": ", i );
     738           0 :     PRINT_JSON_CSTR( obj->name, sizeof(obj->name) );
     739           0 :     PRINT( ", \"wksp\": " );
     740           0 :     PRINT_JSON_CSTR( topo->workspaces[ obj->wksp_id ].name, sizeof(topo->workspaces[ obj->wksp_id ].name) );
     741           0 :     PRINT( ", \"wksp_id\": %lu, \"footprint\": %lu, \"offset\": %lu, \"props\": {",
     742           0 :            obj->wksp_id, obj->footprint, obj->offset );
     743           0 :     int first = 1;
     744           0 :     for( fd_pod_iter_t iter=fd_pod_iter_init( fd_pod_queryf_subpod( topo->props, "obj.%lu", obj->id ) );
     745           0 :          !fd_pod_iter_done( iter );
     746           0 :          iter=fd_pod_iter_next( iter ) ) {
     747           0 :       fd_pod_info_t info = fd_pod_iter_info( iter );
     748           0 :       if( !strcmp( info.key, "seed" ) ) continue; /* key is a cstr (key_sz includes NUL) */
     749           0 :       switch( info.val_type ) {
     750           0 :       case FD_POD_VAL_TYPE_CSTR: {
     751           0 :         PRINT( "%s ", first ? "" : "," );
     752           0 :         PRINT_JSON_CSTR( info.key, info.key_sz );
     753           0 :         PRINT( ": " );
     754           0 :         PRINT_JSON_CSTR( (char const *)info.val, info.val_sz );
     755           0 :         first = 0;
     756           0 :         break;
     757           0 :       }
     758           0 :       case FD_POD_VAL_TYPE_ULONG: {
     759           0 :         ulong val; fd_ulong_svw_dec( info.val, &val );
     760           0 :         PRINT( "%s ", first ? "" : "," );
     761           0 :         PRINT_JSON_CSTR( info.key, info.key_sz );
     762           0 :         PRINT( ": %lu", val );
     763           0 :         first = 0;
     764           0 :         break;
     765           0 :       }
     766           0 :       case FD_POD_VAL_TYPE_INT: {
     767           0 :         ulong u; fd_ulong_svw_dec( info.val, &u );
     768           0 :         PRINT( "%s ", first ? "" : "," );
     769           0 :         PRINT_JSON_CSTR( info.key, info.key_sz );
     770           0 :         PRINT( ": %i", (int)fd_long_zz_dec( u ) );
     771           0 :         first = 0;
     772           0 :         break;
     773           0 :       }
     774           0 :       }
     775           0 :     }
     776           0 :     PRINT( "%s} }%s\n", first ? "" : " ", i==topo->obj_cnt-1UL ? "" : "," );
     777           0 :   }
     778           0 :   PRINT( "  ],\n" );
     779             : 
     780           0 :   PRINT( "  \"links\": [\n" );
     781           0 :   for( ulong i=0UL; i<topo->link_cnt; i++ ) {
     782           0 :     fd_topo_link_t * link = &topo->links[ i ];
     783           0 :     PRINT( "    { \"id\": %lu, \"name\": ", i );
     784           0 :     PRINT_JSON_CSTR( link->name, sizeof(link->name) );
     785           0 :     PRINT( ", \"kind_id\": %lu, \"wksp_id\": %lu, \"depth\": %lu, \"mtu\": %lu, \"burst\": %lu, \"dcache_bytes\": %lu }%s\n",
     786           0 :            link->kind_id, topo->objs[ link->mcache_obj_id ].wksp_id, link->depth, link->mtu, link->burst,
     787           0 :            fd_dcache_req_data_sz( link->mtu, link->depth, link->burst, 1 ),
     788           0 :            i==topo->link_cnt-1UL ? "" : "," );
     789           0 :   }
     790           0 :   PRINT( "  ],\n" );
     791             : 
     792           0 :   PRINT( "  \"tiles\": [\n" );
     793           0 :   for( ulong i=0UL; i<topo->tile_cnt; i++ ) {
     794           0 :     fd_topo_tile_t * tile = &topo->tiles[ i ];
     795             : 
     796           0 :     ulong tile_numa = 0UL;
     797           0 :     if( tile->cpu_idx!=ULONG_MAX ) tile_numa = fd_shmem_numa_idx( tile->cpu_idx );
     798           0 :     else                           tile_numa = topo->workspaces[ topo->objs[ tile->tile_obj_id ].wksp_id ].numa_idx;
     799             : 
     800           0 :     PRINT( "    { \"id\": %lu, \"name\": ", i );
     801           0 :     PRINT_JSON_CSTR( tile->name, sizeof(tile->name) );
     802           0 :     PRINT( ", \"kind_id\": %lu, \"wksp_id\": %lu, ",
     803           0 :            tile->kind_id, topo->objs[ tile->tile_obj_id ].wksp_id );
     804           0 :     if( tile->cpu_idx!=ULONG_MAX ) PRINT( "\"cpu_idx\": %lu, ", tile->cpu_idx );
     805           0 :     else                           PRINT( "\"cpu_idx\": null, " );
     806           0 :     PRINT( "\"floats\": %s, ", tile->floats ? "true" : "false" );
     807           0 :     PRINT( "\"numa_idx\": %lu, \"mlock_bytes\": %lu, \"in_links\": [", tile_numa, fd_topo_mlock_max_tile1( topo, tile ) );
     808           0 :     for( ulong j=0UL; j<tile->in_cnt; j++ ) {
     809           0 :       PRINT( "%s{ \"link_id\": %lu, \"reliable\": %s }", j ? ", " : "", tile->in_link_id[ j ], tile->in_link_reliable[ j ] ? "true" : "false" );
     810           0 :     }
     811           0 :     PRINT( "], \"out_links\": [" );
     812           0 :     for( ulong j=0UL; j<tile->out_cnt; j++ ) PRINT( "%s%lu", j ? ", " : "", tile->out_link_id[ j ] );
     813           0 :     PRINT( "], \"objects\": [" );
     814           0 :     for( ulong j=0UL; j<tile->uses_obj_cnt; j++ ) {
     815           0 :       int is_rw = tile->uses_obj_mode[ j ] == FD_SHMEM_JOIN_MODE_READ_WRITE;
     816           0 :       PRINT( "%s{ \"obj_id\": %lu, \"mode\": \"%s\" }", j ? ", " : "", tile->uses_obj_id[ j ], is_rw ? "rw" : "ro" );
     817           0 :     }
     818           0 :     PRINT( "] }%s\n", i==topo->tile_cnt-1UL ? "" : "," );
     819           0 :   }
     820           0 :   PRINT( "  ]\n}" );
     821             : 
     822           0 : #undef PRINT
     823             : 
     824           0 :   FD_LOG_STDOUT(( "%s\n", message ));
     825           0 : }

Generated by: LCOV version 1.14