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

Generated by: LCOV version 1.14