LCOV - code coverage report
Current view: top level - discof/capture - fd_solcap_tile.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 208 0.0 %
Date: 2026-06-29 05:51:35 Functions: 0 9 0.0 %

          Line data    Source code
       1             : #include "../../disco/topo/fd_topo.h"
       2             : #include "../../discof/fd_startup.h"
       3             : #include "../../util/log/fd_log.h"
       4             : #include "../../tango/dcache/fd_dcache.h"
       5             : #include "../../tango/fd_tango_base.h"
       6             : 
       7             : #include <errno.h>
       8             : #include <fcntl.h>
       9             : #include <stdio.h>
      10             : #include <time.h>
      11             : #include <unistd.h>
      12             : #include <sys/stat.h>
      13             : 
      14             : #include "../../flamenco/capture/fd_capture_ctx.h"
      15             : #include "../../flamenco/capture/fd_solcap_writer.h"
      16             : #include "generated/fd_solcap_tile_seccomp.h"
      17             : 
      18             : 
      19             : /* The solcap tile is responsible for managing capture context
      20             :    for debugging runtime execution.
      21             : 
      22             :    The tile is enabled when capture context is enabled in the config.
      23             : 
      24             :    ```
      25             :    [capture]
      26             :     solcap_capture = "/path/to/filename.solcap.pcapng"
      27             :    ```
      28             : 
      29             :    When enabled, each tile that writes to solcap will initialize
      30             :    a mcache/dcache pair to use as a shared buffer to communicate with
      31             :    the solcap tile. Each tile that requires solcap writes will declare
      32             :    their own capture context to pass into runtime execution or post
      33             :    execution to write to the buffer via API's provided in the capture
      34             :    context and notify the solcap tile. The solcap tile will then
      35             :    process the messages from the link and write out to the file.
      36             : 
      37             :    More information about capture context in fd_capture_ctx.h
      38             : 
      39             :    Solcap Tile:
      40             : 
      41             :    The solcap tile is initialized with the incoming links from the
      42             :    topology, for each tile that requires solcap writes. The handling for
      43             :    messages is slightly altered from that of the normal stem run loop.
      44             : 
      45             :    The messages sent to the solcap tile are bounded by the size of the
      46             :    10mb (size of account data) + (much smaller) header information. In
      47             :    order to handle the larger messages in an efficient manner,
      48             :    the tile providing the data will send the data in chunks of at most
      49             :    128kb, on a link ~4mb. This is in order to avoid cache trashing. This
      50             :    is done by using a custom input selection control to shuffle the
      51             :    incoming frags and origin in-link only if the current message has
      52             :    been read completely using the SOM/EOM flags.
      53             : 
      54             :    The recent-only mode is a mode that allows for the capture of the
      55             :    last N slots of the execution. This is useful for debugging runtime
      56             :    on a live network. The mode is enabled by setting the recent_only
      57             :    configuration to 1. The number of slots per file is set by the
      58             :    recent_slots_per_file configuration. The default is 128 slots per
      59             :    file. The files are named recent_0.solcap, recent_1.solcap,. The
      60             :    files are rotated when the current file reaches the number of slots
      61             :    per file.
      62             : */
      63             : 
      64             : struct fd_solcap_tile_ctx {
      65             :   ulong tile_idx;
      66             : 
      67             :   ulong    msg_idx;
      68             :   ushort   msg_set_sig;
      69             :   ulong    msg_set_slot;
      70             :   uint     block_len;
      71             : 
      72             :   /* Capture context management */
      73             :   fd_capture_ctx_t  * capture_ctx;
      74             :   fd_capture_link_t * capctx_type;
      75             : 
      76             :   int fd;                             /* Current file descriptor */
      77             : 
      78             :   /* Recent-only rotating capture state */
      79             :   int   recent_only;                  /* 1 if using 2-file flip-flop, 0 for single file */
      80             :   int   recent_fds[2];                /* File descriptors for flip-flop and system calls */
      81             :   ulong recent_current_idx;           /* Current file index (0 or 1) */
      82             :   ulong recent_file_start_slot;       /* Slot number when current file was started (ULONG_MAX = uninitialized) */
      83             :   ulong recent_slots_per_file;        /* Number of slots per file */
      84             : 
      85             :   /* Incoming links for mcache/dcache processing */
      86             :   struct {
      87             :     fd_wksp_t * mem;
      88             :     ulong       chunk0;
      89             :     ulong       wmark;
      90             :     ulong       mtu;
      91             :   } in[32];
      92             : 
      93             :   ulong in_cnt;
      94             : 
      95             :   /* Track which input link we're currently processing */
      96             :   ulong current_in_idx;  /* ULONG_MAX means no active message */
      97             : 
      98             : };
      99             : 
     100             : typedef struct fd_solcap_tile_ctx fd_solcap_tile_ctx_t;
     101             : 
     102             : FD_FN_CONST static inline ulong
     103           0 : scratch_align( void ) {
     104           0 :   return 128UL;
     105           0 : }
     106             : 
     107             : FD_FN_PURE static inline ulong
     108           0 : scratch_footprint( fd_topo_tile_t const * tile ) {
     109           0 :   (void)tile;
     110           0 :   ulong l = FD_LAYOUT_INIT;
     111           0 :   l = FD_LAYOUT_APPEND ( l, alignof(fd_solcap_tile_ctx_t), sizeof(fd_solcap_tile_ctx_t) );
     112           0 :   l = FD_LAYOUT_APPEND ( l, fd_capture_ctx_align(),         fd_capture_ctx_footprint() );
     113           0 :   return FD_LAYOUT_FINI( l, scratch_align() );
     114           0 : }
     115             : 
     116             : static ulong
     117             : populate_allowed_seccomp( fd_topo_t const *      topo,
     118             :                           fd_topo_tile_t const * tile,
     119             :                           ulong                  out_cnt,
     120           0 :                           struct sock_filter *   out ) {
     121           0 :   void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
     122           0 :   fd_solcap_tile_ctx_t const * ctx = (fd_solcap_tile_ctx_t const *)scratch;
     123             : 
     124           0 :   uint solcap_fd_0 = ctx->recent_only ? (uint)ctx->recent_fds[0] : (uint)ctx->fd;
     125           0 :   uint solcap_fd_1 = ctx->recent_only ? (uint)ctx->recent_fds[1] : (uint)ctx->fd;
     126             : 
     127           0 :   populate_sock_filter_policy_fd_solcap_tile( out_cnt,
     128           0 :                                               out,
     129           0 :                                               (uint)fd_log_private_logfile_fd(),
     130           0 :                                               solcap_fd_0,
     131           0 :                                               solcap_fd_1 );
     132           0 :   return sock_filter_policy_fd_solcap_tile_instr_cnt;
     133           0 : }
     134             : 
     135             : static ulong
     136             : populate_allowed_fds( fd_topo_t const *      topo,
     137             :                       fd_topo_tile_t const * tile,
     138             :                       ulong                  out_fds_cnt FD_PARAM_UNUSED,
     139           0 :                       int *                  out_fds ) {
     140           0 :   void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
     141           0 :   fd_solcap_tile_ctx_t const * ctx = (fd_solcap_tile_ctx_t const *)scratch;
     142             : 
     143           0 :   ulong out_cnt = 0UL;
     144             : 
     145           0 :   out_fds[ out_cnt++ ] = 2; /* stderr */
     146           0 :   if( FD_LIKELY( -1!=fd_log_private_logfile_fd() ) )
     147           0 :     out_fds[ out_cnt++ ] = fd_log_private_logfile_fd();
     148             : 
     149           0 :   if( ctx->recent_only ) {
     150             :     /* In recent_only mode, allow both flip-flop file descriptors */
     151           0 :     if( FD_LIKELY( -1!=ctx->recent_fds[0] ) )
     152           0 :       out_fds[ out_cnt++ ] = ctx->recent_fds[0];
     153           0 :     if( FD_LIKELY( -1!=ctx->recent_fds[1] ) )
     154           0 :       out_fds[ out_cnt++ ] = ctx->recent_fds[1];
     155           0 :   } else {
     156             :     /* Traditional single file mode */
     157           0 :     if( FD_LIKELY( -1!=ctx->fd ) )
     158           0 :       out_fds[ out_cnt++ ] = ctx->fd;
     159           0 :   }
     160             : 
     161           0 :   return out_cnt;
     162           0 : }
     163             : 
     164             : /* fd_capctx_buf_process_msg processes a message fragment from the
     165             :    shared buffer and writes it to the solcap file using the solcap
     166             :    writer API.
     167             : 
     168             :    Returns block_len, the total PCAPNG Enhanced Packet Block (EPB)
     169             :    length in bytes. This value represents the complete size of the
     170             :    PCAPNG block including:
     171             :    - EPB header (28 bytes)
     172             :    - Internal chunk header
     173             :    - Message payload data
     174             :    - Padding (to align to 4-byte boundary)
     175             :    - Block footer (4 bytes)
     176             : 
     177             :    For messages that span multiple fragments:
     178             :    - Only the first fragment (SOM) returns a non-zero block_len,
     179             :      representing the total calculated block size
     180             :    - Continuation fragments return 0 as they don't write EPB headers
     181             :    - The block_len from SOM is saved and used when writing the EPB
     182             :      footer on the final fragment (EOM) */
     183             : /* fd_capctx_buf_process_som processes the first fragment (SOM) of a
     184             :    message and writes the appropriate header structures.
     185             : 
     186             :    Returns block_len, the total PCAPNG Enhanced Packet Block (EPB)
     187             :    length in bytes for this message. */
     188             : uint
     189             : fd_capctx_buf_process_som( fd_solcap_tile_ctx_t * ctx,
     190             :                            fd_solcap_buf_msg_t *  msg_hdr,
     191           0 :                            char *                 actual_data ) {
     192           0 :   uint block_len = 0U;
     193           0 :   FD_TEST( ctx->capture_ctx->capture != NULL );
     194             : 
     195           0 :   switch( msg_hdr->sig ) {
     196           0 :   case SOLCAP_WRITE_ACCOUNT: {
     197           0 :     fd_solcap_account_update_hdr_t * account_update = fd_type_pun( actual_data );
     198           0 :     block_len = fd_solcap_write_account_hdr( ctx->capture_ctx->capture, msg_hdr, account_update );
     199           0 :     break;
     200           0 :   }
     201           0 :   case SOLCAP_WRITE_BANK_PREIMAGE: {
     202           0 :     fd_solcap_bank_preimage_t * bank_preimage = fd_type_pun( actual_data );
     203           0 :     block_len = fd_solcap_write_bank_preimage( ctx->capture_ctx->capture, msg_hdr, bank_preimage );
     204           0 :     break;
     205           0 :   }
     206           0 :   case SOLCAP_STAKE_REWARDS_BEGIN: {
     207           0 :     fd_solcap_stake_rewards_begin_t * stake_rewards_begin = fd_type_pun( actual_data );
     208           0 :     block_len = fd_solcap_write_stake_rewards_begin( ctx->capture_ctx->capture, msg_hdr, stake_rewards_begin );
     209           0 :     break;
     210           0 :   }
     211           0 :   case SOLCAP_STAKE_REWARD_EVENT: {
     212           0 :     fd_solcap_stake_reward_event_t * stake_reward_event = fd_type_pun( actual_data );
     213           0 :     block_len = fd_solcap_write_stake_reward_event( ctx->capture_ctx->capture, msg_hdr, stake_reward_event );
     214           0 :     break;
     215           0 :   }
     216           0 :   case SOLCAP_STAKE_ACCOUNT_PAYOUT: {
     217           0 :     fd_solcap_stake_account_payout_t * stake_account_payout = fd_type_pun( actual_data );
     218           0 :     block_len = fd_solcap_write_stake_account_payout( ctx->capture_ctx->capture, msg_hdr, stake_account_payout );
     219           0 :     break;
     220           0 :   }
     221           0 :   default:
     222             :     /* Unknown signal received in message processing */
     223           0 :     FD_LOG_ERR(( "Unknown signal received in message processing: sig=%lu", (ulong)msg_hdr->sig ));
     224           0 :     break;
     225           0 :   }
     226           0 :   return block_len;
     227           0 : }
     228             : 
     229             : /* fd_capctx_buf_process_continuation processes continuation fragments
     230             :    (SOM=0) which contain raw data bytes to append to the current message.
     231             :    This is generic and works for any fragmented message type (account
     232             :    updates, or any future large message types). */
     233             : void
     234             : fd_capctx_buf_process_continuation( fd_solcap_tile_ctx_t * ctx,
     235             :                                     char *                 data,
     236           0 :                                     ulong                  data_sz ) {
     237           0 :   FD_TEST( ctx->capture_ctx->capture != NULL );
     238           0 :   fd_solcap_write_data( ctx->capture_ctx->capture, data, data_sz );
     239           0 : }
     240             : 
     241             : /* returnable_frag processes incoming message fragments and handles
     242             :    fragmented solcap messages using SOM (Start of Message) and EOM (End
     243             :    of Message) control flags.
     244             : 
     245             :    Message Fragmentation:
     246             :    ----------------------
     247             :    Solcap messages can be very large (up to 10MB for account data).  To
     248             :    avoid cache thrashing and fit within the ~4MB link size, large
     249             :    messages are split into smaller fragments of at most 128KB
     250             :    (SOLCAP_WRITE_ACCOUNT_DATA_MTU).
     251             : 
     252             :    SOM/EOM Control Flags:
     253             :    ----------------------
     254             :    Each fragment has two control flags set in the frag metadata:
     255             :    - SOM (Start of Message): Set on the first fragment of a message
     256             :    - EOM (End of Message):   Set on the last fragment of a message
     257             : 
     258             :    For a single-fragment message:
     259             :      Single Fragment:       SOM=1, EOM=1
     260             :    For a multi-fragment message:
     261             :      First fragment:        SOM=1, EOM=0
     262             :      Middle fragments:      SOM=0, EOM=0
     263             :      Last fragment:         SOM=0, EOM=1
     264             : 
     265             :    Fragment Processing:
     266             :    --------------------
     267             :    When SOM is set:
     268             :    - The fragment begins with a fd_solcap_buf_msg_t header containing
     269             :      the message type (sig), slot, and transaction index
     270             :    - This header is parsed and saved in the context (msg_set_sig,
     271             :      msg_set_slot)
     272             :    - The input link is locked (current_in_idx) to ensure all fragments
     273             :      of this message are processed sequentially from the same link
     274             :    - The actual data follows the header in the fragment
     275             :    - The PCAPNG block_len is calculated and saved (ctx->block_len) for
     276             :      use when writing the footer on EOM
     277             : 
     278             :    When SOM is not set (continuation fragment):
     279             :    - The entire fragment is data (no header)
     280             :    - The previously saved message state is used
     281             :    - No block_len is calculated (continuation data is appended)
     282             : 
     283             :    When EOM is set:
     284             :    - The PCAPNG block footer is written using the block_len from SOM
     285             :    - For bank preimage messages, the file is synced to disk
     286             :    - All message state is reset (msg_idx, block_len, msg_set_sig, etc.)
     287             :    - The input link is unlocked (current_in_idx = ULONG_MAX) to allow
     288             :      processing the next message
     289             : 
     290             :    This design allows the solcap tile to handle arbitrarily large
     291             :    messages while maintaining efficient memory usage and cache locality.
     292             : */
     293             : 
     294             : static inline int
     295             : returnable_frag( fd_solcap_tile_ctx_t * ctx,
     296             :                  ulong                  in_idx,
     297             :                  ulong                  seq    FD_PARAM_UNUSED,
     298             :                  ulong                  sig    FD_PARAM_UNUSED,
     299             :                  ulong                  chunk,
     300             :                  ulong                  sz,
     301             :                  ulong                  ctl,
     302             :                  ulong                  tsorig FD_PARAM_UNUSED,
     303             :                  ulong                  tspub  FD_PARAM_UNUSED,
     304           0 :                  fd_stem_context_t *    stem   FD_PARAM_UNUSED ) {
     305             : 
     306           0 :   if( FD_UNLIKELY( sz!=0UL && (chunk<ctx->in[ in_idx ].chunk0 || chunk>ctx->in[ in_idx ].wmark || sz>ctx->in[ in_idx ].mtu ) ) )
     307           0 :     FD_LOG_ERR(( "chunk %lu %lu from in %lu corrupt, not in range [%lu,%lu]", chunk, sz, in_idx, ctx->in[ in_idx ].chunk0, ctx->in[ in_idx ].wmark ));
     308             : 
     309             : 
     310             :   /* If we're processing a message from a different input, skip this
     311             :      fragment for now without incrementing input seq number */
     312           0 :   if( FD_UNLIKELY( ctx->current_in_idx != ULONG_MAX && ctx->current_in_idx != in_idx ) ) return 1;
     313             : 
     314           0 :   uchar const * data = fd_chunk_to_laddr_const( ctx->in[in_idx].mem, chunk );
     315             : 
     316           0 :   int som = fd_frag_meta_ctl_som( ctl );
     317           0 :   int eom = fd_frag_meta_ctl_eom( ctl );
     318             : 
     319           0 :   if( som ) {
     320           0 :     fd_solcap_buf_msg_t * msg_hdr = fd_type_pun( (void *)data );
     321           0 :     char * actual_data            = (char *)(data + sizeof(fd_solcap_buf_msg_t));
     322           0 :     FD_TEST( sz >= sizeof(fd_solcap_buf_msg_t) );
     323           0 :     ctx->msg_set_slot   = msg_hdr->slot;
     324           0 :     ctx->msg_set_sig    = (ushort)msg_hdr->sig;
     325           0 :     ctx->current_in_idx = in_idx;  /* Start tracking this input */
     326             : 
     327             :     /* Handle file rotation for recent_only mode */
     328           0 :     if( ctx->recent_only ) {
     329           0 :       if( ctx->recent_file_start_slot == ULONG_MAX ) {
     330           0 :         ctx->recent_file_start_slot = msg_hdr->slot;
     331           0 :       }
     332           0 :       else if( msg_hdr->slot >= ctx->recent_file_start_slot + ctx->recent_slots_per_file ) {
     333             :         /* Check if we need to rotate (>= slots_per_file slots from start) */
     334             :         /* Flip-flop to the other file */
     335           0 :         ulong next_idx = 1UL - ctx->recent_current_idx;
     336           0 :         int next_fd = ctx->recent_fds[next_idx];
     337             : 
     338             :         /* The following is a series of checks to ensure the file is
     339             :             synced and truncated correctly. This occurs via:
     340             :             1. Syncing the current file
     341             :             2. Syncing the next file
     342             :             3. Truncating the next file
     343             :             4. Resetting the file descriptor position to 0
     344             :             5. Reinitializing the solcap writer with the new file descriptor
     345             :         */
     346           0 :         FD_TEST( fsync( ctx->fd ) == 0 );
     347           0 :         FD_TEST( ftruncate( next_fd, 0L ) == 0 );
     348           0 :         FD_TEST( lseek( next_fd, 0L, SEEK_SET ) == 0L );
     349             : 
     350           0 :         fd_solcap_writer_init( ctx->capture_ctx->capture, next_fd );
     351           0 :         ctx->recent_current_idx = next_idx;
     352           0 :         ctx->recent_file_start_slot = msg_hdr->slot;
     353           0 :         ctx->fd = next_fd;
     354           0 :       }
     355           0 :     }
     356             : 
     357           0 :     uint block_len = fd_capctx_buf_process_som( ctx, msg_hdr, actual_data );
     358           0 :     FD_TEST( block_len > 0 );  /* SOM must return valid block length */
     359           0 :     ctx->block_len = block_len;
     360           0 :   } else {
     361             :     /* Continuation fragment: just raw data bytes */
     362           0 :     fd_capctx_buf_process_continuation( ctx, (char *)data, sz );
     363           0 :   }
     364             : 
     365             :   /* If message you receive has the eom flag, write footer */
     366           0 :   if( eom ) {
     367           0 :     FD_TEST( ctx->block_len > 0 );  /* Must have valid block length before writing footer */
     368           0 :     fd_solcap_write_ftr( ctx->capture_ctx->capture, ctx->block_len );
     369             : 
     370           0 :     ctx->msg_idx        = 0;
     371           0 :     ctx->block_len      = 0;
     372           0 :     ctx->msg_set_sig    = 0;
     373           0 :     ctx->msg_set_slot   = 0;
     374           0 :     ctx->current_in_idx = ULONG_MAX;  /* Reset to sentinel - ready for next message */
     375           0 :   } else {
     376           0 :     ctx->msg_idx++;
     377           0 :   }
     378             : 
     379           0 :   return 0;
     380           0 : }
     381             : 
     382             : static void
     383             : privileged_init( fd_topo_t const *      topo,
     384           0 :                  fd_topo_tile_t const * tile ) {
     385           0 :   void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
     386           0 :   FD_SCRATCH_ALLOC_INIT( l, scratch );
     387           0 :   fd_solcap_tile_ctx_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_solcap_tile_ctx_t), sizeof(fd_solcap_tile_ctx_t) );
     388           0 :   void * _capture_ctx        = FD_SCRATCH_ALLOC_APPEND( l, fd_capture_ctx_align(),         fd_capture_ctx_footprint() );
     389             : 
     390           0 :   ctx->tile_idx = tile->kind_id;
     391             : 
     392           0 :   ctx->capture_ctx = fd_capture_ctx_join( fd_capture_ctx_new( _capture_ctx ) );
     393           0 :   FD_TEST( ctx->capture_ctx );
     394             : 
     395           0 :   ctx->recent_only = tile->solcap.recent_only;
     396           0 :   ctx->recent_slots_per_file = tile->solcap.recent_slots_per_file ? tile->solcap.recent_slots_per_file : 128UL;
     397             : 
     398           0 :   struct stat path_stat;
     399           0 :   int stat_result = stat( tile->solcap.solcap_capture, &path_stat );
     400             : 
     401           0 :   if( ctx->recent_only ) {
     402             :     /* recent_only=1: Ensure path is a directory, create if not exists */
     403           0 :     if( stat_result != 0 ) {
     404           0 :       if( FD_UNLIKELY( mkdir(tile->solcap.solcap_capture, 0755) != 0 ) ) {
     405           0 :         FD_LOG_ERR(( "solcap_recent_only=1 but could not create directory: %s (%i-%s)",
     406           0 :                    tile->solcap.solcap_capture, errno, strerror(errno) ));
     407           0 :       }
     408           0 :     } else if( FD_UNLIKELY( !S_ISDIR(path_stat.st_mode) ) ) {
     409           0 :       FD_LOG_ERR(( "solcap_recent_only=1 but path is not a directory: %s", tile->solcap.solcap_capture ));
     410           0 :     }
     411             : 
     412           0 :     ctx->recent_current_idx = 0;
     413           0 :     ctx->recent_file_start_slot = ULONG_MAX;  /* Will be set on first fragment */
     414             : 
     415           0 :     for( ulong i = 0; i < 2; i++ ) {
     416           0 :       char filepath[PATH_MAX];
     417           0 :       int ret = snprintf( filepath, PATH_MAX, "%s/recent_%lu.solcap", tile->solcap.solcap_capture, i );
     418           0 :       if( FD_UNLIKELY( ret<0 || ret>=PATH_MAX ) ) {
     419           0 :         FD_LOG_ERR(( "snprintf failed or path too long for recent file %lu", i ));
     420           0 :       }
     421             : 
     422           0 :       ctx->recent_fds[i] = open( filepath, O_RDWR | O_CREAT | O_TRUNC, 0644 );
     423           0 :       if( FD_UNLIKELY( ctx->recent_fds[i] == -1 ) ) {
     424           0 :         FD_LOG_ERR(( "failed to open or create solcap recent file %s (%i-%s)",
     425           0 :                      filepath, errno, strerror(errno) ));
     426           0 :       }
     427           0 :     }
     428             : 
     429           0 :     ctx->fd = ctx->recent_fds[0];
     430             : 
     431           0 :   } else {
     432             :     /* recent_only=0: Validate that path is a file*/
     433           0 :     if( FD_UNLIKELY( stat_result == 0 && S_ISDIR(path_stat.st_mode) ) ) {
     434           0 :       FD_LOG_ERR(( "solcap_recent_only=0 but path is a directory: %s (should be a file path)", tile->solcap.solcap_capture ));
     435           0 :     }
     436             : 
     437           0 :     ctx->fd = open( tile->solcap.solcap_capture, O_RDWR | O_CREAT | O_TRUNC, 0644 );
     438           0 :     if( FD_UNLIKELY( ctx->fd == -1 ) ) {
     439           0 :       FD_LOG_ERR(( "failed to open or create solcap capture file %s (%i-%s)",
     440           0 :                    tile->solcap.solcap_capture, errno, strerror(errno) ));
     441           0 :     }
     442           0 :   }
     443             : 
     444           0 :   FD_TEST( ctx->capture_ctx->capture );
     445             : 
     446           0 :   ctx->capture_ctx->capture_solcap    = 1;
     447           0 :   ctx->capture_ctx->solcap_start_slot = tile->solcap.capture_start_slot;
     448           0 :   fd_solcap_writer_init( ctx->capture_ctx->capture, ctx->fd );
     449             : 
     450           0 :   ctx->current_in_idx = ULONG_MAX;  /* No active message initially */
     451           0 :   ctx->msg_idx      = 0UL;
     452           0 :   ctx->msg_set_sig  = 0U;
     453           0 :   ctx->msg_set_slot = 0UL;
     454           0 :   ctx->block_len    = 0U;
     455             : 
     456           0 :   ulong scratch_top = FD_SCRATCH_ALLOC_FINI( l, scratch_align() );
     457             : 
     458           0 :   if( FD_UNLIKELY( scratch_top > (ulong)scratch + scratch_footprint( tile ) ) )
     459           0 :     FD_LOG_ERR(( "scratch overflow %lu %lu %lu", scratch_top - (ulong)scratch - scratch_footprint( tile ), scratch_top, (ulong)scratch + scratch_footprint( tile ) ));
     460             : 
     461           0 : }
     462             : 
     463             : static void
     464             : unprivileged_init( fd_topo_t const *      topo,
     465           0 :                    fd_topo_tile_t const * tile ) {
     466             : 
     467           0 :   void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
     468           0 :   fd_solcap_tile_ctx_t * ctx = (fd_solcap_tile_ctx_t *)scratch;
     469             : 
     470           0 :   ctx->in_cnt = 0UL;
     471           0 :   FD_TEST( tile->in_cnt <= 32UL );
     472           0 :   for( ulong i = 0UL; i < tile->in_cnt; i++ ) {
     473           0 :     fd_topo_link_t const * link = &topo->links[ tile->in_link_id[ i ] ];
     474           0 :     fd_topo_wksp_t const * wksp = &topo->workspaces[ topo->objs[ link->dcache_obj_id ].wksp_id ];
     475             : 
     476           0 :     ctx->in[ctx->in_cnt].mem    = wksp->wksp;
     477           0 :     ctx->in[ctx->in_cnt].chunk0 = fd_dcache_compact_chunk0( wksp->wksp, link->dcache );
     478           0 :     ctx->in[ctx->in_cnt].wmark  = fd_dcache_compact_wmark( wksp->wksp, link->dcache, link->mtu );
     479           0 :     ctx->in[ctx->in_cnt].mtu    = link->mtu;
     480           0 :     ctx->in_cnt++;
     481           0 :   }
     482             : 
     483           0 :   fd_sleep_until_replay_started( topo );
     484           0 : }
     485             : 
     486           0 : #define STEM_BURST (1UL)
     487           0 : #define STEM_LAZY  (50UL)
     488             : 
     489           0 : #define STEM_CALLBACK_CONTEXT_TYPE  fd_solcap_tile_ctx_t
     490           0 : #define STEM_CALLBACK_CONTEXT_ALIGN alignof(fd_solcap_tile_ctx_t)
     491             : 
     492           0 : #define STEM_CALLBACK_RETURNABLE_FRAG returnable_frag
     493             : 
     494             : #include "../../disco/stem/fd_stem.c"
     495             : 
     496             : fd_topo_run_tile_t fd_tile_solcap = {
     497             :   .name                     = "solcap",
     498             :   .populate_allowed_seccomp = populate_allowed_seccomp,
     499             :   .populate_allowed_fds     = populate_allowed_fds,
     500             :   .scratch_align            = scratch_align,
     501             :   .scratch_footprint        = scratch_footprint,
     502             :   .privileged_init          = privileged_init,
     503             :   .unprivileged_init        = unprivileged_init,
     504             :   .run                      = stem_run
     505             : };

Generated by: LCOV version 1.14