LCOV - code coverage report
Current view: top level - app/shared_dev/commands - dump.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 240 0.0 %
Date: 2026-10-22 18:10:43 Functions: 0 11 0.0 %

          Line data    Source code
       1             : #include "../../shared/fd_config.h"
       2             : #include "../../shared/fd_bootinfo.h"
       3             : #include "../../shared/fd_action.h"
       4             : #include "../../../disco/metrics/fd_metrics.h"
       5             : #include "../../../disco/fd_clock_tile.h"
       6             : #include "../../../disco/net/fd_net_tile.h"
       7             : #include "../../../util/clock/fd_clock.h"
       8             : #include "../../../util/net/fd_pcap.h"
       9             : 
      10             : #include <errno.h>
      11             : #include <fcntl.h>
      12             : #include <signal.h>
      13             : #include <stdio.h>
      14             : #include <unistd.h>
      15             : 
      16           0 : #define FD_DUMP_FLAGS_NO_DATA 1
      17           0 : #define FD_DUMP_FLAGS_NET_RX  2
      18             : 
      19             : struct dump_ctx {
      20             :   fd_io_buffered_ostream_t ostream;
      21             :   ulong                    pending_sz;
      22             : 
      23             :   struct link {
      24             :     fd_topo_link_t const * topo;
      25             :     union {
      26             :       void const *           dcache_base;
      27             :       fd_net_rx_bounds_t     net_rx_bounds[1];
      28             :     };
      29             :     uint                   link_hash;
      30             :     int                    flags; /* FD_DUMP_FLAGS_* */
      31             :   }                        links[ FD_TOPO_MAX_LINKS ];
      32             :   ulong                    link_cnt;
      33             : 
      34             :   ulong *                  metrics_base;
      35             :   long                     next_stat_log;
      36             :   ulong                    frags;
      37             :   ulong                    bytes;
      38             :   ulong                    last_overrun_frags;
      39             : 
      40             :   long                     warmup_until;
      41             : 
      42             :   fd_clock_tile_t          clock[ 1 ];
      43             : };
      44             : typedef struct dump_ctx dump_ctx_t;
      45             : 
      46             : void
      47             : dump_cmd_args( int      * argc,
      48             :                char * * * argv,
      49           0 :                args_t   * args ) {
      50           0 :   char const * out_file = fd_env_strip_cmdline_cstr( argc, argv, "--out-file", NULL, "dump.pcap" );
      51           0 :   char const * link     = fd_env_strip_cmdline_cstr( argc, argv, "--link",     NULL, ""          );
      52           0 :   args->dump.once       = fd_env_strip_cmdline_int(  argc, argv, "--once",     NULL, 1           );
      53           0 :   fd_cstr_fini( fd_cstr_append_cstr_safe( fd_cstr_init( args->dump.pcap_path ), out_file, sizeof(args->dump.pcap_path)-1UL ) );
      54           0 :   fd_cstr_fini( fd_cstr_append_cstr_safe( fd_cstr_init( args->dump.link_name ), link,     sizeof(args->dump.link_name)-1UL ) );
      55           0 : }
      56             : 
      57             : static FD_FN_CONST ulong
      58             : frag_sz( int   flags,
      59           0 :          ulong line_sz ) {
      60           0 :   return fd_ulong_if( flags & FD_DUMP_FLAGS_NO_DATA, 0UL, line_sz );
      61           0 : }
      62             : static FD_FN_PURE void const *
      63             : frag_ptr( int                    flags,
      64             :           struct link const *    link,
      65             :           ulong                  chunk,
      66           0 :           ulong                  ctl ) {
      67           0 :   if( FD_UNLIKELY( flags & FD_DUMP_FLAGS_NET_RX ) ) return fd_net_rx_translate_frag( link->net_rx_bounds, chunk, ctl, 0UL );
      68           0 :   else                                              return fd_chunk_to_laddr_const ( link->dcache_base,   chunk           );
      69           0 : }
      70             : 
      71             : static void
      72             : dump_link_once( dump_ctx_t *        ctx,
      73           0 :                 struct link const * link ) {
      74           0 :   fd_frag_meta_t const * mcache = link->topo->mcache;
      75           0 :   ulong seq0 = fd_mcache_seq0( mcache );
      76           0 :   ulong seq_init = fd_mcache_seq_query( fd_mcache_seq_laddr_const( mcache ) );
      77           0 :   ulong depth = fd_mcache_depth( mcache );
      78             : 
      79           0 :   ulong frags = 0UL;
      80           0 :   ulong bytes = 0UL;
      81             : 
      82           0 :   int flags = link->flags;
      83             : 
      84             :   /* For links that are published reliably, we can trust the value of
      85             :      seq_init.  For unreliable links, we'll just publish one whole
      86             :      depth. */
      87             : 
      88             :   /* We know at this point [seq0, seq_init) were published, but they may
      89             :      be long overwritten, and there may be more published than that. */
      90             : 
      91           0 :   ulong min_seq_seen = seq_init; /* What we actually dumped is [min_seq_seen, seq_init) */
      92           0 :   for( ulong seq=fd_seq_dec( seq_init, 1UL ); fd_seq_ge( seq, seq0 ); seq=fd_seq_dec( seq, 1UL ) ) {
      93             :     /* It's not necessary for this to be atomic, since this is a
      94             :        post-mortem tool. */
      95           0 :     fd_frag_meta_t const * line = mcache+fd_mcache_line_idx( seq, depth );
      96           0 :     ulong read_seq = fd_frag_meta_seq_query( line );
      97           0 :     if( FD_UNLIKELY( read_seq!=seq ) ) break;
      98             : 
      99           0 :     min_seq_seen = seq;
     100             : 
     101           0 :     ulong        sz     = frag_sz ( flags, line->sz                     );
     102           0 :     void const * buffer = frag_ptr( flags, link, line->chunk, line->ctl );
     103             : 
     104           0 :     fd_pcap_ostream_pkt( &ctx->ostream, (long)seq, line, sizeof(fd_frag_meta_t), buffer, sz, link->link_hash );
     105           0 :     bytes += sz;
     106           0 :     frags++;
     107           0 :   }
     108             : 
     109             :   /* Now check everything after seq_init.  This could potentially loop
     110             :      forever if the producer is still going, so we cap it at one depth. */
     111           0 :   for( ulong off=0UL; off<depth; off++ ) {
     112           0 :     ulong seq = fd_seq_inc( seq_init, off );
     113             : 
     114           0 :     fd_frag_meta_t const * line = mcache+fd_mcache_line_idx( seq, depth );
     115           0 :     ulong read_seq = fd_frag_meta_seq_query( line );
     116             : 
     117             :     /* Skip anything we processed in the first loop, also skipping any
     118             :        with the err control bit set.  When an mcache is initialized, all
     119             :        the frags have err set to true, so this will skip those in
     120             :        particular as well. */
     121           0 :     if( FD_UNLIKELY( (fd_seq_le( min_seq_seen, read_seq ) & fd_seq_lt( read_seq, seq_init )) | (line->ctl & (1<<2)) ) ) continue;
     122             : 
     123           0 :     ulong        sz     = frag_sz ( flags, line->sz                     );
     124           0 :     void const * buffer = frag_ptr( flags, link, line->chunk, line->ctl );
     125             : 
     126           0 :     fd_pcap_ostream_pkt( &ctx->ostream, (long)seq, line, sizeof(fd_frag_meta_t), buffer, sz, link->link_hash );
     127           0 :     bytes += sz;
     128           0 :     frags++;
     129           0 :   }
     130           0 :   FD_LOG_NOTICE(( "dumped %lu frags, %lu bytes in total from %s:%lu. Link hash: 0x%x",
     131           0 :                   frags, bytes, link->topo->name, link->topo->kind_id, link->link_hash ));
     132           0 : }
     133             : 
     134             : static int running = 1;
     135             : 
     136             : static void
     137           0 : exit_signal( int sig FD_PARAM_UNUSED ) {
     138           0 :   running = 0;
     139           0 : }
     140             : 
     141             : static int
     142           0 : should_shutdown( dump_ctx_t * ctx FD_PARAM_UNUSED ) {
     143           0 :   return !running;
     144           0 : }
     145             : 
     146             : static void
     147             : during_frag( dump_ctx_t * ctx,
     148             :              ulong        in_idx,
     149             :              ulong        seq,
     150             :              ulong        sig FD_PARAM_UNUSED ,
     151             :              ulong        chunk,
     152             :              ulong        sz,
     153           0 :              ulong        ctl ) {
     154             :   /* We have a new candidate fragment to copy into the dump file.
     155             :      Because we attach read-only and do not backpressure any producers,
     156             :      this fragment could be overrun at any point during this function.
     157             :      So we copy the relevant data into local memory (in the buffered
     158             :      ostream peek space) and only commit it to the file later in
     159             :      after_frag.  after_frag is only called if the fragment was not
     160             :      overrun while we were processing it. */
     161             : 
     162           0 :   ctx->pending_sz = 0UL;
     163             : 
     164           0 :   struct link const * llink = ctx->links+in_idx;
     165           0 :   int flags = llink->flags;
     166             : 
     167           0 :   sz = frag_sz( flags, sz );
     168             : 
     169             : 
     170           0 :   long pcap_sz = fd_pcap_pkt_sz( sizeof(fd_frag_meta_t), sz );
     171           0 :   if( FD_UNLIKELY( pcap_sz < 0L ) ) return;
     172             : 
     173           0 :   if( (ulong)pcap_sz > fd_io_buffered_ostream_peek_sz( &ctx->ostream ) ) {
     174           0 :     fd_io_buffered_ostream_flush( &ctx->ostream );
     175           0 :     if( FD_UNLIKELY( (ulong)pcap_sz > fd_io_buffered_ostream_peek_sz( &ctx->ostream ) ) ) {
     176           0 :       FD_LOG_ERR(( "packet size %ld too large for pcap, increase write buffer size (%lu)",
     177           0 :                    pcap_sz, fd_io_buffered_ostream_peek_sz( &ctx->ostream ) ));
     178           0 :     }
     179           0 :   }
     180             : 
     181             : 
     182           0 :   fd_topo_link_t const * link      = ctx->links[ in_idx ].topo;
     183           0 :   fd_frag_meta_t const * mline     = link->mcache + fd_mcache_line_idx( seq, fd_mcache_depth( link->mcache ) );
     184           0 :   void const *           frag_data = frag_ptr( flags, llink, chunk, ctl );
     185             : 
     186           0 :   fd_pcap_pkt( fd_io_buffered_ostream_peek( &ctx->ostream ),
     187           0 :                0L,
     188           0 :                mline,
     189           0 :                sizeof(fd_frag_meta_t),
     190           0 :                frag_data,
     191           0 :                sz,
     192           0 :                ctx->links[ in_idx ].link_hash );
     193           0 :   ctx->pending_sz = (ulong)pcap_sz;
     194           0 : }
     195             : 
     196             : static void
     197             : after_frag( dump_ctx_t *        ctx,
     198             :             ulong               in_idx FD_PARAM_UNUSED,
     199             :             ulong               seq FD_PARAM_UNUSED,
     200             :             ulong               sig FD_PARAM_UNUSED,
     201             :             ulong               sz,
     202             :             ulong               tsorig FD_PARAM_UNUSED,
     203             :             ulong               tspub,
     204           0 :             fd_stem_context_t * stem FD_PARAM_UNUSED ) {
     205           0 :   if( FD_UNLIKELY( !ctx->pending_sz ) ) return;
     206             : 
     207           0 :   sz = frag_sz( ctx->links[ in_idx ].flags, sz );
     208           0 :   long pcap_sz = fd_pcap_pkt_sz( sizeof(fd_frag_meta_t), sz );
     209           0 :   FD_TEST( (ulong)pcap_sz == ctx->pending_sz );
     210             : 
     211           0 :   long tickcount = fd_tickcount();
     212           0 :   long now       = fd_clock_epoch_y( ctx->clock->shmem->epoch, tickcount );
     213           0 :   if( FD_LIKELY( now>ctx->warmup_until ) ) {
     214           0 :     if( FD_LIKELY( tspub!=0UL ) ) {
     215           0 :       long   tspub_decomp    = fd_frag_meta_ts_decomp( tspub, tickcount );
     216           0 :       long   tspub_wallclock = fd_clock_epoch_y( ctx->clock->shmem->epoch, tspub_decomp );
     217           0 :       uint * pcap_tss        = fd_io_buffered_ostream_peek( &ctx->ostream );
     218           0 :       pcap_tss[ 0 ] = (uint)(((ulong)tspub_wallclock) / (ulong)1e9);
     219           0 :       pcap_tss[ 1 ] = (uint)(((ulong)tspub_wallclock) % (ulong)1e9); /* Actually nsec */
     220           0 :     }
     221           0 :     fd_io_buffered_ostream_seek( &ctx->ostream, ctx->pending_sz );
     222           0 :     ctx->frags += 1UL;
     223           0 :     ctx->bytes += sz;
     224           0 :   }
     225           0 :   ctx->pending_sz = 0UL;
     226           0 : }
     227             : 
     228             : static void
     229           0 : log_metrics( dump_ctx_t * ctx ) {
     230           0 :   FD_LOG_NOTICE(( "dumped %lu frags, %lu bytes", ctx->frags, ctx->bytes ));
     231           0 :   ctx->frags = 0UL;
     232           0 :   ctx->bytes = 0UL;
     233             : 
     234           0 :   ulong overrun_frags = 0UL;
     235           0 :   for( ulong i=0UL; i<ctx->link_cnt; i++) {
     236           0 :     volatile ulong const * link_metrics = fd_metrics_link_in( ctx->metrics_base, i );
     237           0 :     overrun_frags += link_metrics[ FD_METRICS_COUNTER_LINK_FRAG_POLLING_OVERRUN_OFF ];
     238           0 :     overrun_frags += link_metrics[ FD_METRICS_COUNTER_LINK_FRAG_READING_OVERRUN_OFF ];
     239           0 :   }
     240           0 :   if( FD_UNLIKELY( overrun_frags != ctx->last_overrun_frags ) ) {
     241             :     /* Note: We expect overruns at startup because we have no way to
     242             :        know the current seq to start polling from, so we start at 0
     243             :        which has often been overrun. */
     244           0 :     if( FD_LIKELY( ctx->last_overrun_frags != 0 ) ) {
     245           0 :       long overrun_diff = fd_seq_diff( overrun_frags, ctx->last_overrun_frags );
     246           0 :       FD_LOG_WARNING(( "overrun detected, %ld frags dropped", overrun_diff ));
     247           0 :     }
     248           0 :     ctx->last_overrun_frags = overrun_frags;
     249           0 :   }
     250           0 : }
     251             : 
     252             : static void
     253           0 : during_housekeeping( dump_ctx_t * ctx ) {
     254           0 :   long now = fd_clock_tile_now( ctx->clock );
     255           0 :   if( FD_UNLIKELY( now >= fd_clock_tile_recal_next( ctx->clock ) ) ) {
     256           0 :     fd_clock_tile_recal( ctx->clock );
     257           0 :   }
     258           0 :   if( FD_UNLIKELY( now > ctx->next_stat_log ) ) {
     259           0 :     ctx->next_stat_log = now + (long)1e9;
     260           0 :     log_metrics( ctx );
     261           0 :   }
     262           0 : }
     263             : 
     264             : #define STEM_BURST                        (0UL)
     265             : #define STEM_CALLBACK_CONTEXT_TYPE        dump_ctx_t
     266             : #define STEM_CALLBACK_CONTEXT_ALIGN       alignof(dump_ctx_t)
     267           0 : #define STEM_CALLBACK_DURING_FRAG         during_frag
     268           0 : #define STEM_CALLBACK_AFTER_FRAG          after_frag
     269           0 : #define STEM_CALLBACK_SHOULD_SHUTDOWN     should_shutdown
     270           0 : #define STEM_CALLBACK_DURING_HOUSEKEEPING during_housekeeping
     271             : #include "../../../disco/stem/fd_stem.c"
     272             : 
     273             : void
     274             : dump_cmd_fn( args_t      * args,
     275           0 :              fd_config_t * config ) {
     276           0 :   char * tokens[ 16 ];
     277           0 :   ulong token_count = fd_cstr_tokenize( tokens, 16UL, args->dump.link_name, ',' );
     278             : 
     279           0 :   dump_ctx_t ctx = { 0 };
     280             : 
     281           0 :   uchar write_buf[ 131072 ];
     282           0 :   int fd = open( args->dump.pcap_path, O_WRONLY | O_CREAT | O_TRUNC,
     283           0 :                  S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH );
     284           0 :   if( FD_UNLIKELY( fd < 0 ) )
     285           0 :     FD_LOG_ERR(( "open() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     286           0 :   if( FD_UNLIKELY( &ctx.ostream != fd_io_buffered_ostream_init( &ctx.ostream, fd, write_buf, sizeof(write_buf) ) ) )
     287           0 :     FD_LOG_ERR(( "fd_io_buffered_ostream_init() failed" ));
     288             : 
     289           0 :   FD_TEST( 0==fd_pcap_ostream_hdr( &ctx.ostream, FD_PCAP_LINK_LAYER_USER0 ) );
     290             : 
     291           0 :   fd_topo_t * topo = &config->topo;
     292           0 :   fd_bootinfo_adopt( config );
     293           0 :   fd_bootinfo_check_layout( config );
     294           0 :   fd_topo_join_workspaces( topo, FD_SHMEM_JOIN_MODE_READ_ONLY, FD_TOPO_CORE_DUMP_LEVEL_DISABLED );
     295           0 :   fd_topo_fill( topo );
     296             : 
     297           0 :   for( ulong i=0UL; i<topo->link_cnt; i++) {
     298           0 :     fd_topo_link_t * link = &topo->links[ i ];
     299           0 :     int found = (token_count==0UL);
     300           0 :     for( ulong j=0UL; (!found)&(j<token_count); j++ ) found |= !strcmp( tokens[ j ], topo->links[ i ].name );
     301           0 :     if( found ) {
     302           0 :       if( FD_UNLIKELY( NULL==link->mcache ) )
     303           0 :         FD_LOG_ERR(( "link %s:%lu mcache is null", link->name, link->kind_id ));
     304           0 :       ctx.links[ ctx.link_cnt ].topo        = link;
     305           0 :       ctx.links[ ctx.link_cnt ].dcache_base = link->mtu ? fd_topo_obj_wksp_base( topo, link->dcache_obj_id ) : NULL;
     306           0 :       ctx.links[ ctx.link_cnt ].link_hash   = (uint)((fd_hash( 17UL, link->name, strlen( link->name ) ) << 8) | link->kind_id);
     307             : 
     308           0 :       int flags = fd_int_if( !link->mtu, FD_DUMP_FLAGS_NO_DATA, 0 );
     309           0 :       if( FD_UNLIKELY( strstr( link->name, "net_" ) ) ) {
     310           0 :         flags |= FD_DUMP_FLAGS_NET_RX;
     311             :         /* this overwrites dcache_base since it's part of the union, but
     312             :            that's okay. */
     313           0 :         uchar * dcache = fd_dcache_join( fd_topo_obj_laddr( topo, link->dcache_obj_id ) );
     314           0 :         fd_net_rx_bounds_init( ctx.links[ ctx.link_cnt ].net_rx_bounds, dcache );
     315           0 :         fd_dcache_leave( dcache );
     316           0 :       }
     317           0 :       ctx.links[ ctx.link_cnt ].flags = flags;
     318             : 
     319           0 :       ctx.link_cnt++;
     320           0 :     }
     321           0 :   }
     322             : 
     323           0 :   if( args->dump.once ) {
     324           0 :     for( ulong i=0UL; i<ctx.link_cnt; i++ ) {
     325           0 :       dump_link_once( &ctx, &ctx.links[ i ] );
     326           0 :     }
     327           0 :   } else {
     328           0 :     struct sigaction sa = {
     329           0 :       .sa_handler = exit_signal,
     330           0 :       .sa_flags   = 0,
     331           0 :     };
     332           0 :     if( FD_UNLIKELY( sigaction( SIGTERM, &sa, NULL ) ) )
     333           0 :       FD_LOG_ERR(( "sigaction(SIGTERM) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     334           0 :     if( FD_UNLIKELY( sigaction( SIGINT, &sa, NULL ) ) )
     335           0 :       FD_LOG_ERR(( "sigaction(SIGINT) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     336             : 
     337           0 :     fd_frag_meta_t const * mcaches[ FD_TOPO_MAX_LINKS ];
     338           0 :     for( ulong i=0UL; i<ctx.link_cnt; i++ ) {
     339           0 :       mcaches[ i ] = ctx.links[ i ].topo->mcache;
     340           0 :     }
     341             : 
     342           0 :     uchar fseq_mem[ FD_TOPO_MAX_LINKS ][ FD_FSEQ_FOOTPRINT ] __attribute__((aligned((FD_FSEQ_ALIGN))));
     343           0 :     ulong * fseqs[ FD_TOPO_MAX_LINKS ];
     344           0 :     for( ulong i=0UL; i<ctx.link_cnt; i++ ) {
     345           0 :       fseqs[ i ] = fd_fseq_join( fd_fseq_new( fseq_mem[ i ], 0UL ) );
     346           0 :     }
     347             : 
     348           0 :     fd_rng_t _rng[1];
     349           0 :     fd_rng_t * rng = fd_rng_join( fd_rng_new( _rng, (uint)fd_tickcount(), 0UL ) );
     350             : 
     351           0 :     uchar __attribute__((aligned(FD_STEM_SCRATCH_ALIGN))) scratch[ stem_scratch_footprint( ctx.link_cnt, 0UL, 0UL ) ];
     352           0 :     uchar __attribute__((aligned(FD_METRICS_ALIGN))) metrics_mem[ FD_METRICS_FOOTPRINT( ctx.link_cnt ) ];
     353             : 
     354           0 :     ctx.metrics_base = fd_metrics_join( fd_metrics_new( metrics_mem, ctx.link_cnt ) );
     355           0 :     fd_metrics_register( ctx.metrics_base );
     356             : 
     357             :     /* The purpose of the warmup period is to ensure that all frags in
     358             :        the pcap were from after the dump was started.  fd_stem currently
     359             :        has no way to start from the most recently published frags when
     360             :        joining links, so at startup we get a full ~depth set of callbacks
     361             :        for old frags.  This code assumes that we are caught up and only
     362             :        processing new frags by the time the warmup timer expires. */
     363           0 :     fd_clock_tile_init( ctx.clock );
     364           0 :     ctx.warmup_until = fd_clock_tile_now( ctx.clock ) + (long)1e9;
     365           0 :     ctx.next_stat_log = ctx.warmup_until + (long)1e9;
     366             : 
     367           0 :     fd_stem_sleep_t sleep[ 1 ] = {{ .shmem = NULL }}; /* no sleep object: spin */
     368           0 :     stem_run1( ctx.link_cnt, /* in_cnt */
     369           0 :                mcaches,      /* in_mcache */
     370           0 :                fseqs,        /* in_fseq */
     371           0 :                0UL,          /* out_cnt */
     372           0 :                NULL,         /* out_mcache */
     373           0 :                0UL,          /* cons_cnt */
     374           0 :                NULL,         /* _cons_out */
     375           0 :                NULL,         /* _cons_fseq */
     376           0 :                NULL,         /* cons_slow */
     377           0 :                0UL,          /* burst */
     378           0 :                0UL,          /* lazy */
     379           0 :                rng,          /* rng */
     380           0 :                scratch,      /* scratch */
     381           0 :                &ctx,         /* ctx */
     382           0 :                sleep );      /* sleep */
     383             : 
     384           0 :     for( ulong i=0UL; i<ctx.link_cnt; i++ ) {
     385           0 :       struct link const * link = &ctx.links[ i ];
     386           0 :       volatile ulong const * link_metrics = fd_metrics_link_in( ctx.metrics_base, i );
     387           0 :       ulong frags = link_metrics[ FD_METRICS_COUNTER_LINK_FRAG_CONSUMED_OFF ];
     388           0 :       ulong bytes = link_metrics[ FD_METRICS_COUNTER_LINK_FRAG_CONSUMED_BYTES_OFF ];
     389           0 :       FD_LOG_NOTICE(( "dumped %lu frags, %lu bytes in total from %s:%lu. Link hash: 0x%x",
     390           0 :                       frags, bytes, link->topo->name, link->topo->kind_id, link->link_hash ));
     391           0 :     }
     392             : 
     393           0 :     fd_metrics_delete( fd_metrics_leave( ctx.metrics_base ) );
     394           0 :     ctx.metrics_base = NULL;
     395             : 
     396           0 :     fd_rng_delete( fd_rng_leave( rng ) );
     397             : 
     398           0 :     for( ulong i=0UL; i<ctx.link_cnt; i++ ) {
     399           0 :       fd_fseq_delete( fd_fseq_leave( fseqs[ i ] ) );
     400           0 :     }
     401           0 :   }
     402             : 
     403           0 :   fd_topo_leave_workspaces( topo );
     404             : 
     405           0 :   fd_io_buffered_ostream_flush( &ctx.ostream );
     406           0 :   fd_io_buffered_ostream_fini( &ctx.ostream );
     407           0 :   close( fd );
     408           0 : }
     409             : 
     410             : action_t fd_action_dump = {
     411             :   .name          = "dump",
     412             :   .args          = dump_cmd_args,
     413             :   .fn            = dump_cmd_fn,
     414             :   .perm          = NULL,
     415             :   .description   = "Dump tango links to a packet capture file",
     416             :   .is_diagnostic = 1
     417             : };

Generated by: LCOV version 1.14