LCOV - code coverage report
Current view: top level - disco/gui - fd_gui.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 437 0.0 %
Date: 2026-11-11 12:40:43 Functions: 0 297 0.0 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_disco_gui_fd_gui_h
       2             : #define HEADER_fd_src_disco_gui_fd_gui_h
       3             : 
       4             : #include "fd_gui_peers.h"
       5             : #include "fd_gui_shred.h"
       6             : #include "fd_gui_ema.h"
       7             : 
       8             : #include "../topo/fd_topo.h"
       9             : #include "../diag/fd_diag_tile.h"
      10             : 
      11             : #include "../../ballet/txn/fd_txn.h"
      12             : #include "../../disco/tiles.h"
      13             : #include "../../disco/fd_txn_p.h"
      14             : #include "../../disco/bundle/fd_bundle_tile.h"
      15             : #include "../../discof/backup/fd_snapsv_tile.h"
      16             : #include "../../discof/restore/fd_snapct_tile.h"
      17             : #include "../../discof/restore/utils/fd_ssmsg.h"
      18             : #include "../../discof/restore/utils/fd_wfs.h"
      19             : #include "../../discof/replay/fd_replay_tile.h"
      20             : #include "../../flamenco/leaders/fd_leaders.h"
      21             : #include "../../flamenco/runtime/sysvar/fd_sysvar_epoch_schedule.h" /* fd_slot_to_epoch */
      22             : #include "../../util/fd_util_base.h"
      23             : #include "../../util/hist/fd_histf.h"
      24             : #include "../../waltz/http/fd_http_server.h"
      25             : #include "../../waltz/http/fd_url.h"
      26             : #include "../../flamenco/accdb/fd_accdb_cache.h"
      27             : #include "../../flamenco/accdb/fd_accdb_shmem.h"
      28             : #include "../../flamenco/alpenglow/fd_block_marker.h" /* FD_NUM_SLOTS_FOR_REWARD */
      29             : 
      30             : 
      31             : /* ---- Network Bandwidth Monitoring ----------------------------------- */
      32             : 
      33           0 : #define FD_GUI_NETWORK_EMA_HALF_LIFE_NS (1000000000L) /* 1 second in nanoseconds */
      34           0 : #define FD_GUI_NET_PROTO_CNT            (7UL)         /* turbine, gossip, tpu, repair, rserve, metric, votor */
      35           0 : #define FD_GUI_NET_RATE_MAX_WINDOW_NS   (300L*1000L*1000L*1000L) /* 5 minutes in nanoseconds */
      36             : 
      37             : /* Monotonic deque element for sliding-window max tracking of
      38             :    EMA-smoothed network throughput. */
      39             : struct fd_gui_rate_entry {
      40             :   long   ts_nanos;
      41             :   double value;
      42             : };
      43             : typedef struct fd_gui_rate_entry fd_gui_rate_entry_t;
      44             : 
      45             : /* At 100 ms sampling, 5 minutes = 3000 samples. */
      46             : #define DEQUE_NAME fd_gui_rate_deque
      47           0 : #define DEQUE_T    fd_gui_rate_entry_t
      48           0 : #define DEQUE_MAX  4096UL
      49             : #include "../../util/tmpl/fd_deque.c"
      50             : 
      51             : /* ---- History Buffers ------------------------------------------------ */
      52             : 
      53           0 : #define FD_GUI_TPS_HISTORY_WINDOW_DURATION_SECONDS (10L)
      54           0 : #define FD_GUI_TPS_HISTORY_SAMPLE_CNT              (300UL)
      55             : 
      56           0 : #define FD_GUI_PROGCACHE_HISTORY_CNT               (600UL) /* 60s / 100ms */
      57             : 
      58           0 : #define FD_GUI_TURBINE_RECV_TIMESTAMPS (750UL)
      59             : 
      60             : /* One use case for tracking ingress shred slot is to estimate when we
      61             :    have caught up to the tip of the blockchain.  A naive approach would
      62             :    be to track the maximum seen slot.
      63             : 
      64             :    maximum_seen_slot = fd_ulong_max( maximum_seen_slot, new_slot_from_shred_tile );
      65             : 
      66             :    Unfortunately, this doesn't always work because a validator can send
      67             :    a slot number that is arbitrarily large on a false fork. Also, these
      68             :    shreds can be for a repair response, which can be arbitrarily small.
      69             : 
      70             :    The prospects here seem bleak, but not all hope is lost!  We know
      71             :    that for a sufficiently large historical time window there is a high
      72             :    probability that at least some of the slots we observe will be valid
      73             :    recent turbine slots. For a sufficiently small window there is a high
      74             :    probability that all the observed shred slots are non-malicious (i.e.
      75             :    not arbitrarily large).
      76             : 
      77             :    In practice shred slots are almost always non-malicious. We can keep
      78             :    a history of the 12 largest slots we've seen in the past 4.8 seconds.
      79             :    We'll consider the "tip" of the blockchain to be the maximum slot in
      80             :    our history. This way, if we receive maliciously large slot number,
      81             :    it will be evicted after 4.8 seconds. If we receive a small slot from
      82             :    a repair response it will be ignored because we've seen other larger
      83             :    slots, meaning that our estimate is eventually consistent. For
      84             :    monitoring purposes this is sufficient.
      85             : 
      86             :    The worst case scenario is that this validator receives an incorrect
      87             :    shred slot slot more than once every 3 leader rotations. Before the
      88             :    previous incorrect slot is evicted from the history, a new one takes
      89             :    it's place and we wouldn't never get a correct estimate of the tip of
      90             :    the chain.  We also would indefinitely think that that we haven't
      91             :    caught up. This would require the chain having perpetually malicious
      92             :    leaders with adjacent rotations.  If this happens, Solana has bigger
      93             :    problems. */
      94           0 : #define FD_GUI_TURBINE_SLOT_HISTORY_SZ (  12UL )
      95             : 
      96             : /* Like the turbine slot, the latest repair slot can also swing to
      97             :    arbitrarily large values due to a malicious fork switch.  The gui
      98             :    provides the same guarantees for freshness and accuracy.  This
      99             :    history is somewhat larger to handle the increased repair bandwidth
     100             :    during catch up. */
     101           0 : #define FD_GUI_REPAIR_SLOT_HISTORY_SZ  ( 512UL )
     102             : 
     103             : /* FD_GUI_*_CATCH_UP_HISTORY_SZ is the capacity of the record of slots
     104             :    seen from repair or turbine during the catch up stage at startup.
     105             :    These buffers are run-length encoded, so they will typically be very
     106             :    small.  The worst-case scenario is unbounded, so bounds here are
     107             :    determined heuristically. */
     108           0 : #define FD_GUI_REPAIR_CATCH_UP_HISTORY_SZ  (4096UL)
     109           0 : #define FD_GUI_TURBINE_CATCH_UP_HISTORY_SZ (4096UL)
     110             : 
     111           0 : #define FD_GUI_SLOT_RANKINGS_SZ (100UL)
     112           0 : #define FD_GUI_SLOT_RANKING_TYPE_ASC  (0)
     113           0 : #define FD_GUI_SLOT_RANKING_TYPE_DESC (1)
     114             : 
     115             : /* ---- Boot ----------------------------------------------------------- */
     116             : 
     117           0 : #define FD_GUI_BOOT_PROGRESS_TYPE_JOINING_GOSSIP               (1)
     118           0 : #define FD_GUI_BOOT_PROGRESS_TYPE_LOADING_FULL_SNAPSHOT        (2)
     119           0 : #define FD_GUI_BOOT_PROGRESS_TYPE_LOADING_INCREMENTAL_SNAPSHOT (3)
     120           0 : #define FD_GUI_BOOT_PROGRESS_TYPE_WAITING_FOR_SUPERMAJORITY    (4)
     121           0 : #define FD_GUI_BOOT_PROGRESS_TYPE_CATCHING_UP                  (5)
     122           0 : #define FD_GUI_BOOT_PROGRESS_TYPE_RUNNING                      (6)
     123             : 
     124           0 : #define FD_GUI_BOOT_PROGRESS_FULL_SNAPSHOT_IDX        (0UL)
     125           0 : #define FD_GUI_BOOT_PROGRESS_INCREMENTAL_SNAPSHOT_IDX (1UL)
     126           0 : #define FD_GUI_BOOT_PROGRESS_SNAPSHOT_CNT             (2UL)
     127             : 
     128           0 : #define FD_GUI_SLOT_LEVEL_INCOMPLETE               (0)
     129           0 : #define FD_GUI_SLOT_LEVEL_COMPLETED                (1)
     130           0 : #define FD_GUI_SLOT_LEVEL_OPTIMISTICALLY_CONFIRMED (2) /* "notarized" under Alpenglow */
     131           0 : #define FD_GUI_SLOT_LEVEL_ROOTED                   (3)
     132           0 : #define FD_GUI_SLOT_LEVEL_FINALIZED                (4)
     133             : 
     134             : /* Strength of the notarization and finalization proofs known for a
     135             :    block.*/
     136             : 
     137           0 : #define FD_GUI_AG_NOTAR_NONE     ((uchar)0)
     138           0 : #define FD_GUI_AG_NOTAR_FALLBACK ((uchar)1)
     139           0 : #define FD_GUI_AG_NOTAR_REGULAR  ((uchar)2) /* supersedes fallback */
     140             : 
     141           0 : #define FD_GUI_AG_FINAL_NONE     ((uchar)0)
     142           0 : #define FD_GUI_AG_FINAL_IMPLICIT ((uchar)1) /* an ancestor of a finalized block */
     143           0 : #define FD_GUI_AG_FINAL_SLOW     ((uchar)2) /* supersedes implicit */
     144           0 : #define FD_GUI_AG_FINAL_FAST     ((uchar)3) /* supersedes slow */
     145             : 
     146             : /* Whether our ordinary notarize or skip vote for a slot made it into
     147             :    the reward certificate. */
     148             : 
     149           0 : #define FD_GUI_VOTE_REWARDED_UNKNOWN ((uchar)0)
     150           0 : #define FD_GUI_VOTE_REWARDED_YES     ((uchar)1)
     151           0 : #define FD_GUI_VOTE_REWARDED_NO      ((uchar)2)
     152             : 
     153           0 : #define FD_GUI_IS_VOTER_UNKNOWN ((uchar)0)
     154           0 : #define FD_GUI_IS_VOTER_YES     ((uchar)1)
     155           0 : #define FD_GUI_IS_VOTER_NO      ((uchar)2)
     156             : 
     157             : /* UNKNOWN is used while fork choice has not resolved a replayed
     158             :    slot. */
     159           0 : #define FD_GUI_SKIP_STATUS_UNKNOWN     ((uchar)0)
     160           0 : #define FD_GUI_SKIP_STATUS_NOT_SKIPPED ((uchar)1)
     161           0 : #define FD_GUI_SKIP_STATUS_NOTARIZED   ((uchar)2)
     162           0 : #define FD_GUI_SKIP_STATUS_FINALIZED   ((uchar)3)
     163             : 
     164           0 : #define FD_GUI_TXN_FLAGS_STARTED         ( 1U)
     165           0 : #define FD_GUI_TXN_FLAGS_ENDED           ( 2U)
     166           0 : #define FD_GUI_TXN_FLAGS_FROM_BUNDLE     ( 8U)
     167           0 : #define FD_GUI_TXN_FLAGS_LANDED_IN_BLOCK (16U)
     168             : 
     169           0 : #define FD_GUI_VOTE_STATE_NON_VOTING (0)
     170           0 : #define FD_GUI_VOTE_STATE_VOTING     (1)
     171           0 : #define FD_GUI_VOTE_STATE_DELINQUENT (2)
     172             : 
     173           0 : #define FD_GUI_HTTP_MIN_SEND_BUFFER_SZ        (256UL<<20)
     174             : 
     175           0 : #define FD_GUI_TIMELINE_STORED_GRANULARITY_CNT (8UL)
     176           0 : #define FD_GUI_TIMELINE_GRANULARITY_CNT        (23UL)
     177             : #define FD_GUI_TIMELINE_QUERY_SHRED_MAX        (262144UL)
     178           0 : #define FD_GUI_TIMELINE_QUERY_SLOT_MAX         (65536UL)
     179           0 : #define FD_GUI_TIMELINE_QUERY_EVENT_MAX        (65536UL)
     180             : 
     181             : /* Event kinds returned by timeline.query_events. */
     182           0 : #define FD_GUI_TIMELINE_EVENT_LEADER_SKIP      (0U)
     183             : /* At least one bucket per pixel on a 4K display, and enough to cover
     184             :    the widest raw txn_batch view (1.5h) at 2s in a single request. */
     185             : #define FD_GUI_TIMELINE_QUERY_MAX_BUCKETS      (4096UL)
     186             : 
     187             : /* fd_gui_timeline_granularities lists the bucket durations accepted by
     188             :    the timeline aggregate queries, in ascending order. */
     189             : 
     190             : struct fd_gui_timeline_granularity {
     191             :   char const * name;
     192             :   ulong        ns;   /* bucket duration in nanoseconds */
     193             : };
     194             : typedef struct fd_gui_timeline_granularity fd_gui_timeline_granularity_t;
     195             : extern fd_gui_timeline_granularity_t const fd_gui_timeline_granularities[ FD_GUI_TIMELINE_GRANULARITY_CNT ];
     196             : extern ulong const fd_gui_timeline_stored_granularity_ns[ FD_GUI_TIMELINE_STORED_GRANULARITY_CNT ];
     197             : 
     198             : /* fd_gui_timeline_granularity_ns returns the bucket duration of
     199             :    granularity g in nanoseconds. */
     200             : 
     201             : static inline ulong
     202           0 : fd_gui_timeline_granularity_ns( ulong g ) {
     203           0 :   return fd_gui_timeline_granularities[ g ].ns;
     204           0 : }
     205             : 
     206             : /* Stored timeline tiers, in ascending order.  The 25ms tier only exists
     207             :    in the per-tile CPU record (fd_gui_timeline_tile_day_t); the day
     208             :    record (fd_gui_timeline_day_t) holds the 250ms to 12h tiers. */
     209           0 : #define FD_GUI_TIMELINE_DAY_NS                 (86400000000000L)
     210             : 
     211           0 : #define FD_GUI_TIMELINE_GRANULARITY_25MS  (0)
     212           0 : #define FD_GUI_TIMELINE_GRANULARITY_250MS (1)
     213           0 : #define FD_GUI_TIMELINE_GRANULARITY_2S    (2)
     214           0 : #define FD_GUI_TIMELINE_GRANULARITY_15S   (3)
     215           0 : #define FD_GUI_TIMELINE_GRANULARITY_2M    (4)
     216           0 : #define FD_GUI_TIMELINE_GRANULARITY_15M   (5)
     217           0 : #define FD_GUI_TIMELINE_GRANULARITY_2H    (6)
     218           0 : #define FD_GUI_TIMELINE_GRANULARITY_12H   (7)
     219             : 
     220           0 : #define FD_GUI_TIMELINE_25MS_CNT  (FD_GUI_TIMELINE_DAY_NS / (              25L*1000L*1000L))
     221           0 : #define FD_GUI_TIMELINE_250MS_CNT (FD_GUI_TIMELINE_DAY_NS / (             250L*1000L*1000L))
     222           0 : #define FD_GUI_TIMELINE_2S_CNT    (FD_GUI_TIMELINE_DAY_NS / (         2L*1000L*1000L*1000L))
     223           0 : #define FD_GUI_TIMELINE_15S_CNT   (FD_GUI_TIMELINE_DAY_NS / (        15L*1000L*1000L*1000L))
     224           0 : #define FD_GUI_TIMELINE_2M_CNT    (FD_GUI_TIMELINE_DAY_NS / (     2L*60L*1000L*1000L*1000L))
     225           0 : #define FD_GUI_TIMELINE_15M_CNT   (FD_GUI_TIMELINE_DAY_NS / (    15L*60L*1000L*1000L*1000L))
     226           0 : #define FD_GUI_TIMELINE_2H_CNT    (FD_GUI_TIMELINE_DAY_NS / ( 2L*60L*60L*1000L*1000L*1000L))
     227           0 : #define FD_GUI_TIMELINE_12H_CNT   (FD_GUI_TIMELINE_DAY_NS / (12L*60L*60L*1000L*1000L*1000L))
     228             : 
     229             : /* fd_gui_timeline_tier resolves granularity g of the aggregate query
     230             :    key to the longest stored tier the query has whose duration divides
     231             :    the granularity's.  On success, sets *stored to the tier, *merge_cnt
     232             :    to the number of tier buckets merged into one query bucket and
     233             :    returns 0.  Returns -1 if key is unknown, g is below the query's
     234             :    minimum granularity, or no tier divides it. */
     235             : 
     236             : int
     237             : fd_gui_timeline_tier( char const * key,
     238             :                       ulong        g,
     239             :                       int *        stored,
     240             :                       ulong *      merge_cnt );
     241             : 
     242             : #define FD_GUI_TIMELINE_FIELD_MAP( X )                                                           \
     243             :   /* field                              250ms   2s      15s     2m      15m     2h      12h   */ \
     244           0 :   X( START_SLOT,      start_slot,       ulong,  ulong,  ulong,  ulong,  ulong,  ulong,  ulong  ) \
     245           0 :   X( END_SLOT,        end_slot,         ulong,  ulong,  ulong,  ulong,  ulong,  ulong,  ulong  ) \
     246           0 :   X( TURBINE,         turbine,          uint,   uint,   uint,   uint,   ulong,  ulong,  ulong  ) \
     247           0 :   X( REPAIR,          repair,           uint,   uint,   uint,   uint,   ulong,  ulong,  ulong  ) \
     248           0 :   X( RECONSTRUCTED,   reconstructed,    uint,   uint,   uint,   uint,   ulong,  ulong,  ulong  ) \
     249           0 :   X( PUBLISHED,       published,        uint,   uint,   uint,   uint,   uint,   ulong,  ulong  ) \
     250           0 :   X( COMPUTE_UNITS,   compute_units,    uint,   uint,   ulong,  ulong,  ulong,  ulong,  ulong  ) \
     251           0 :   X( MAX_COMPUTE,     max_compute,      uint,   uint,   uint,   uint,   uint,   uint,   uint   ) \
     252           0 :   X( TXN_FEES,        txn_fees,         ulong,  ulong,  ulong,  ulong,  ulong,  ulong,  ulong  ) \
     253           0 :   X( PRIO_FEES,       prio_fees,        ulong,  ulong,  ulong,  ulong,  ulong,  ulong,  ulong  ) \
     254           0 :   X( TIPS,            tips,             ulong,  ulong,  ulong,  ulong,  ulong,  ulong,  ulong  ) \
     255           0 :   X( NONVOTE_SUCCESS, nonvote_success,  uint,   uint,   uint,   uint,   uint,   ulong,  ulong  ) \
     256           0 :   X( NONVOTE_FAILED,  nonvote_failed,   uint,   uint,   uint,   uint,   uint,   ulong,  ulong  ) \
     257           0 :   X( VOTE_SUCCESS,    vote_success,     uint,   uint,   uint,   uint,   uint,   ulong,  ulong  ) \
     258           0 :   X( VOTE_FAILED,     vote_failed,      uint,   uint,   uint,   uint,   uint,   ulong,  ulong  ) \
     259           0 :   X( SKIPPED,         skipped,          ushort, ushort, ushort, ushort, ushort, ushort, uint   ) \
     260           0 :   X( MINE,            mine,             uint,   uint,   uint,   uint,   uint,   uint,   uint   ) \
     261           0 :   X( MINE_SKIPPED,    mine_skipped,     uint,   uint,   uint,   uint,   uint,   uint,   uint   ) \
     262           0 :   X( DURATION_WALL,   duration_wall,    ulong,  ulong,  ulong,  ulong,  ulong,  ulong,  ulong  ) \
     263           0 :   X( DURATION_SLOTS,  duration_slots,   ulong,  ulong,  ulong,  ulong,  ulong,  ulong,  ulong  )
     264             : 
     265             : #define FD_GUI_TIMELINE_FIELD_ENUM( id, name, t0, t1, t2, t3, t4, t5, t6 ) FD_GUI_TIMELINE_FIELD_##id,
     266             : enum fd_gui_timeline_field {
     267             :   FD_GUI_TIMELINE_FIELD_MAP( FD_GUI_TIMELINE_FIELD_ENUM )
     268             :   FD_GUI_TIMELINE_FIELD_CNT
     269             : };
     270             : #undef FD_GUI_TIMELINE_FIELD_ENUM
     271             : 
     272             : #define FD_GUI_TIMELINE_DECL_250MS(id, name, t0, t1, t2, t3, t4, t5, t6) t0 name[ FD_GUI_TIMELINE_250MS_CNT ];
     273             : #define FD_GUI_TIMELINE_DECL_2S(id, name, t0, t1, t2, t3, t4, t5, t6)    t1 name[ FD_GUI_TIMELINE_2S_CNT    ];
     274             : #define FD_GUI_TIMELINE_DECL_15S(id, name, t0, t1, t2, t3, t4, t5, t6)   t2 name[ FD_GUI_TIMELINE_15S_CNT   ];
     275             : #define FD_GUI_TIMELINE_DECL_2M(id, name, t0, t1, t2, t3, t4, t5, t6)    t3 name[ FD_GUI_TIMELINE_2M_CNT    ];
     276             : #define FD_GUI_TIMELINE_DECL_15M(id, name, t0, t1, t2, t3, t4, t5, t6)   t4 name[ FD_GUI_TIMELINE_15M_CNT   ];
     277             : #define FD_GUI_TIMELINE_DECL_2H(id, name, t0, t1, t2, t3, t4, t5, t6)    t5 name[ FD_GUI_TIMELINE_2H_CNT    ];
     278             : #define FD_GUI_TIMELINE_DECL_12H(id, name, t0, t1, t2, t3, t4, t5, t6)   t6 name[ FD_GUI_TIMELINE_12H_CNT   ];
     279             : 
     280             : typedef struct fd_gui_timeline_250ms { FD_GUI_TIMELINE_FIELD_MAP( FD_GUI_TIMELINE_DECL_250MS ) } fd_gui_timeline_250ms_t;
     281             : typedef struct fd_gui_timeline_2s    { FD_GUI_TIMELINE_FIELD_MAP( FD_GUI_TIMELINE_DECL_2S    ) } fd_gui_timeline_2s_t;
     282             : typedef struct fd_gui_timeline_15s   { FD_GUI_TIMELINE_FIELD_MAP( FD_GUI_TIMELINE_DECL_15S   ) } fd_gui_timeline_15s_t;
     283             : typedef struct fd_gui_timeline_2m    { FD_GUI_TIMELINE_FIELD_MAP( FD_GUI_TIMELINE_DECL_2M    ) } fd_gui_timeline_2m_t;
     284             : typedef struct fd_gui_timeline_15m   { FD_GUI_TIMELINE_FIELD_MAP( FD_GUI_TIMELINE_DECL_15M   ) } fd_gui_timeline_15m_t;
     285             : typedef struct fd_gui_timeline_2h    { FD_GUI_TIMELINE_FIELD_MAP( FD_GUI_TIMELINE_DECL_2H    ) } fd_gui_timeline_2h_t;
     286             : typedef struct fd_gui_timeline_12h   { FD_GUI_TIMELINE_FIELD_MAP( FD_GUI_TIMELINE_DECL_12H   ) } fd_gui_timeline_12h_t;
     287             : 
     288             : #undef FD_GUI_TIMELINE_DECL_250MS
     289             : #undef FD_GUI_TIMELINE_DECL_2S
     290             : #undef FD_GUI_TIMELINE_DECL_15S
     291             : #undef FD_GUI_TIMELINE_DECL_2M
     292             : #undef FD_GUI_TIMELINE_DECL_15M
     293             : #undef FD_GUI_TIMELINE_DECL_2H
     294             : #undef FD_GUI_TIMELINE_DECL_12H
     295             : 
     296             : struct fd_gui_timeline_day {
     297             :   long insert_time_ns;
     298             :   long end_time_ns; /* exclusive UTC day end (the following midnight) */
     299             :   fd_gui_timeline_250ms_t bucket_250ms;
     300             :   fd_gui_timeline_2s_t    bucket_2s;
     301             :   fd_gui_timeline_15s_t   bucket_15s;
     302             :   fd_gui_timeline_2m_t    bucket_2m;
     303             :   fd_gui_timeline_15m_t   bucket_15m;
     304             :   fd_gui_timeline_2h_t    bucket_2h;
     305             :   fd_gui_timeline_12h_t   bucket_12h;
     306             : };
     307             : typedef struct fd_gui_timeline_day fd_gui_timeline_day_t;
     308             : 
     309             : static inline ulong
     310           0 : fd_gui_timeline_bucket_cnt( int granularity ) {
     311           0 :   switch( granularity ) {
     312           0 :     case FD_GUI_TIMELINE_GRANULARITY_25MS:  return FD_GUI_TIMELINE_25MS_CNT;
     313           0 :     case FD_GUI_TIMELINE_GRANULARITY_250MS: return FD_GUI_TIMELINE_250MS_CNT;
     314           0 :     case FD_GUI_TIMELINE_GRANULARITY_2S:    return FD_GUI_TIMELINE_2S_CNT;
     315           0 :     case FD_GUI_TIMELINE_GRANULARITY_15S:   return FD_GUI_TIMELINE_15S_CNT;
     316           0 :     case FD_GUI_TIMELINE_GRANULARITY_2M:    return FD_GUI_TIMELINE_2M_CNT;
     317           0 :     case FD_GUI_TIMELINE_GRANULARITY_15M:   return FD_GUI_TIMELINE_15M_CNT;
     318           0 :     case FD_GUI_TIMELINE_GRANULARITY_2H:    return FD_GUI_TIMELINE_2H_CNT;
     319           0 :     case FD_GUI_TIMELINE_GRANULARITY_12H:   return FD_GUI_TIMELINE_12H_CNT;
     320           0 :     default: FD_LOG_ERR(( "invalid timeline granularity %d", granularity ));
     321           0 :   }
     322           0 : }
     323             : 
     324             : static inline void *
     325             : fd_gui_timeline_field_ptr( fd_gui_timeline_day_t * day,
     326             :                            int                     granularity,
     327             :                            int                     field,
     328             :                            ulong                   idx,
     329           0 :                            ulong *                 sz ) {
     330           0 :   if( FD_UNLIKELY( !day ) ) FD_LOG_ERR(( "NULL timeline day" ));
     331           0 :   ulong bucket_cnt = fd_gui_timeline_bucket_cnt( granularity );
     332           0 :   if( FD_UNLIKELY( idx>=bucket_cnt ) ) FD_LOG_ERR(( "timeline bucket index %lu out of range [0,%lu)", idx, bucket_cnt ));
     333           0 : #define FD_GUI_TIMELINE_FIELD_CASE( id, name, t0, t1, t2, t3, t4, t5, t6 ) \
     334           0 :   case FD_GUI_TIMELINE_FIELD_##id: \
     335           0 :     switch( granularity ) { \
     336           0 :       case FD_GUI_TIMELINE_GRANULARITY_250MS: *sz=sizeof(t0); return &day->bucket_250ms.name[ idx ]; \
     337           0 :       case FD_GUI_TIMELINE_GRANULARITY_2S:    *sz=sizeof(t1); return &day->bucket_2s.name   [ idx ]; \
     338           0 :       case FD_GUI_TIMELINE_GRANULARITY_15S:   *sz=sizeof(t2); return &day->bucket_15s.name  [ idx ]; \
     339           0 :       case FD_GUI_TIMELINE_GRANULARITY_2M:    *sz=sizeof(t3); return &day->bucket_2m.name   [ idx ]; \
     340           0 :       case FD_GUI_TIMELINE_GRANULARITY_15M:   *sz=sizeof(t4); return &day->bucket_15m.name  [ idx ]; \
     341           0 :       case FD_GUI_TIMELINE_GRANULARITY_2H:    *sz=sizeof(t5); return &day->bucket_2h.name   [ idx ]; \
     342           0 :       case FD_GUI_TIMELINE_GRANULARITY_12H:   *sz=sizeof(t6); return &day->bucket_12h.name  [ idx ]; \
     343           0 :       default: FD_LOG_ERR(( "invalid timeline granularity %d", granularity )); \
     344           0 :     }
     345           0 :   switch( field ) {
     346           0 :     FD_GUI_TIMELINE_FIELD_MAP( FD_GUI_TIMELINE_FIELD_CASE )
     347           0 :     default: FD_LOG_ERR(( "invalid timeline field %d", field ));
     348           0 :   }
     349           0 : #undef FD_GUI_TIMELINE_FIELD_CASE
     350           0 : }
     351             : 
     352             : static inline ulong
     353             : fd_gui_timeline_field_get( fd_gui_timeline_day_t const * day,
     354             :                            int                           granularity,
     355             :                            int                           field,
     356           0 :                            ulong                         idx ) {
     357           0 :   ulong sz = 0UL;
     358           0 :   void const * ptr = fd_gui_timeline_field_ptr( (fd_gui_timeline_day_t *)day, granularity, field, idx, &sz );
     359           0 :   if( sz==sizeof(ushort) ) { ushort v = *(ushort const *)ptr; return v==USHORT_MAX ? ULONG_MAX : (ulong)v; }
     360           0 :   if( sz==sizeof(uint  ) ) { uint   v = *(uint   const *)ptr; return v==UINT_MAX   ? ULONG_MAX : (ulong)v; }
     361           0 :   ulong v = *(ulong const *)ptr;
     362           0 :   return v;
     363           0 : }
     364             : 
     365             : static inline void
     366             : fd_gui_timeline_field_set( fd_gui_timeline_day_t * day,
     367             :                            int                     granularity,
     368             :                            int                     field,
     369             :                            ulong                   idx,
     370           0 :                            ulong                   value ) {
     371           0 :   ulong sz = 0UL;
     372           0 :   void * ptr = fd_gui_timeline_field_ptr( day, granularity, field, idx, &sz );
     373           0 :   if( sz==sizeof(ushort) ) { *(ushort *)ptr = value==ULONG_MAX ? USHORT_MAX : (ushort)fd_ulong_min( value, (ulong)USHORT_MAX-1UL ); return; }
     374           0 :   if( sz==sizeof(uint  ) ) { *(uint   *)ptr = value==ULONG_MAX ? UINT_MAX   : (uint  )fd_ulong_min( value, (ulong)UINT_MAX-1UL   ); return; }
     375           0 :   *(ulong *)ptr = value;
     376           0 : }
     377             : 
     378             : /* fd_gui_timeline_combine returns the result of folding value into the
     379             :    accumulator acc for field. */
     380             : 
     381             : static inline ulong
     382             : fd_gui_timeline_combine( ulong acc,
     383             :                          ulong value,
     384           0 :                          int   field ) {
     385           0 :   if( value==ULONG_MAX ) return acc;
     386           0 :   if( acc==ULONG_MAX )   return value;
     387           0 :   switch( field ) {
     388           0 :     case FD_GUI_TIMELINE_FIELD_START_SLOT:  return fd_ulong_min( acc, value );
     389           0 :     case FD_GUI_TIMELINE_FIELD_END_SLOT:
     390           0 :     case FD_GUI_TIMELINE_FIELD_MAX_COMPUTE: return fd_ulong_max( acc, value );
     391           0 :     default:                                return fd_ulong_min( ULONG_MAX-1UL, fd_ulong_sat_add( acc, value ) );
     392           0 :   }
     393           0 : }
     394             : 
     395             : /* fd_gui_timeline_field_accum folds value into a stored bucket. */
     396             : 
     397             : static inline void
     398             : fd_gui_timeline_field_accum( fd_gui_timeline_day_t * day,
     399             :                              int                     granularity,
     400             :                              int                     field,
     401             :                              ulong                   idx,
     402           0 :                              ulong                   value ) {
     403           0 :   ulong acc = fd_gui_timeline_field_get( day, granularity, field, idx );
     404           0 :   fd_gui_timeline_field_set( day, granularity, field, idx, fd_gui_timeline_combine( acc, value, field ) );
     405           0 : }
     406             : 
     407             : /* Each time bucket counts how many slots the chain advanced in it.  A
     408             :    block can start in one bucket and end in a different bucket, so a
     409             :    bucket can get only part of a slot.  To keep these parts, the count
     410             :    uses units of 1/2^24 slot.  FD_GUI_TIMELINE_SLOT_UNIT is the count
     411             :    for one full slot. */
     412           0 : #define FD_GUI_TIMELINE_SLOT_UNIT (1UL<<24)
     413             : 
     414             : /* Per-tile CPU utilization is sampled every 25ms from the tile regime
     415             :    timers as 0 (idle) to FD_GUI_CPU_UTIL_MAX (busy). */
     416           0 : #define FD_GUI_CPU_UTIL_SAMPLE_NS     (25000000L)
     417           0 : #define FD_GUI_CPU_UTIL_MAX           ((uchar)250)
     418           0 : #define FD_GUI_CPU_UTIL_UNKNOWN       ((uchar)255)
     419             : 
     420             : #define FD_GUI_CPU_UTIL_CREATE_MAX    (1UL)
     421             : 
     422             : /* Per-tile CPU utilization sum/count tiers (250ms and longer).  Each
     423             :    holds an exact sample sum and count per bucket. */
     424             : #define FD_GUI_TIMELINE_CPU_MAP( X ) \
     425             :   /* tier   sum     cnt    */        \
     426           0 :   X( 250MS, ushort, uchar  )         \
     427           0 :   X( 2S,    ushort, uchar  )         \
     428           0 :   X( 15S,   uint,   ushort )         \
     429           0 :   X( 2M,    uint,   ushort )         \
     430           0 :   X( 15M,   uint,   ushort )         \
     431           0 :   X( 2H,    uint,   uint   )         \
     432           0 :   X( 12H,   uint,   uint   )
     433             : 
     434             : #define FD_GUI_TIMELINE_CPU_DECL( g, s, c ) \
     435             :   s sum_##g[ FD_GUI_TIMELINE_##g##_CNT ];   \
     436             :   c cnt_##g[ FD_GUI_TIMELINE_##g##_CNT ];
     437             : 
     438             : struct fd_gui_timeline_tile_day {
     439             :   ulong day_idx;
     440             :   ulong tile_idx;
     441             :   long  insert_time_ns;
     442             :   uchar util_25ms[ FD_GUI_TIMELINE_DAY_NS/FD_GUI_CPU_UTIL_SAMPLE_NS ];
     443             :   FD_GUI_TIMELINE_CPU_MAP( FD_GUI_TIMELINE_CPU_DECL )
     444             : };
     445             : typedef struct fd_gui_timeline_tile_day fd_gui_timeline_tile_day_t;
     446             : 
     447             : #undef FD_GUI_TIMELINE_CPU_DECL
     448             : 
     449             : /* fd_gui_timeline_cpu_get reads the sample sum and count in bucket idx
     450             :    of CPU sum/count tier granularity. */
     451             : 
     452             : static inline void
     453             : fd_gui_timeline_cpu_get( fd_gui_timeline_tile_day_t const * rec,
     454             :                          int                                granularity,
     455             :                          ulong                              idx,
     456             :                          ulong *                            sum,
     457           0 :                          ulong *                            cnt ) {
     458           0 :   switch( granularity ) {
     459           0 : #define FD_GUI_TIMELINE_CPU_GET( g, s, c )                                                       \
     460           0 :     case FD_GUI_TIMELINE_GRANULARITY_##g:                                                        \
     461           0 :       if( FD_UNLIKELY( idx>=FD_GUI_TIMELINE_##g##_CNT ) ) FD_LOG_ERR(( "cpu bucket %lu out of range", idx )); \
     462           0 :       *sum = rec->sum_##g[ idx ];                                                                \
     463           0 :       *cnt = rec->cnt_##g[ idx ];                                                                \
     464           0 :       return;
     465           0 :     FD_GUI_TIMELINE_CPU_MAP( FD_GUI_TIMELINE_CPU_GET )
     466           0 : #undef FD_GUI_TIMELINE_CPU_GET
     467           0 :     default: FD_LOG_ERR(( "invalid timeline cpu granularity %d", granularity ));
     468           0 :   }
     469           0 : }
     470             : 
     471             : /* fd_gui_timeline_cpu_add folds one known sample into every CPU sum/count
     472             :    tier bucket covering day_ns.  Tier types hold a full bucket of
     473             :    samples; a count at its type maximum drops further samples. */
     474             : 
     475             : static inline void
     476             : fd_gui_timeline_cpu_add( fd_gui_timeline_tile_day_t * rec,
     477             :                          ulong                        day_ns,
     478           0 :                          uchar                        util ) {
     479           0 : #define FD_GUI_TIMELINE_CPU_ADD( g, s, c ) do {                                                  \
     480           0 :     ulong idx = day_ns/fd_gui_timeline_stored_granularity_ns[ FD_GUI_TIMELINE_GRANULARITY_##g ]; \
     481           0 :     if( FD_LIKELY( rec->cnt_##g[ idx ]<(c)~(c)0 ) ) {                                            \
     482           0 :       rec->sum_##g[ idx ] = (s)( rec->sum_##g[ idx ]+util );                                     \
     483           0 :       rec->cnt_##g[ idx ]++;                                                                     \
     484           0 :     }                                                                                            \
     485           0 :   } while(0);
     486           0 :   FD_GUI_TIMELINE_CPU_MAP( FD_GUI_TIMELINE_CPU_ADD )
     487           0 : #undef FD_GUI_TIMELINE_CPU_ADD
     488           0 : }
     489             : 
     490             : struct fd_gui_tile_timers {
     491             :   long   sample_time_nanos; /* wallclock ns this sample was taken; identical across the per-tile records. */
     492             :   ulong  tile_idx;          /* global tile index into topo->tiles. */
     493             :   ulong timers[ FD_METRICS_ENUM_TILE_REGIME_CNT ];
     494             :   ulong sched_timers[ FD_METRICS_ENUM_THREAD_REGIME_CNT ];
     495             : 
     496             :   int    in_backp;
     497             :   ushort last_cpu;
     498             :   uchar  status;
     499             :   ulong  heartbeat;
     500             :   ulong  backp_cnt;
     501             :   ulong  nvcsw;
     502             :   ulong  nivcsw;
     503             :   ulong  minflt;
     504             :   ulong  majflt;
     505             :   ulong  interrupts;
     506             :   ulong  tlb_shootdowns;
     507             :   ulong  timer_ticks;
     508             : };
     509             : 
     510             : typedef struct fd_gui_tile_timers fd_gui_tile_timers_t;
     511             : 
     512           0 : #define FD_GUI_EXEC_DONE_SLOT_CNT (16UL)
     513             : 
     514             : struct fd_gui_tile_timers_hist {
     515             :   long   sample_time_nanos;
     516             :   ushort tile_idx;
     517             :   uchar  alive;
     518             :   uchar  in_backp;
     519             :   ushort timers[ FD_METRICS_ENUM_TILE_REGIME_CNT ];
     520             :   ushort sched_timers[ FD_METRICS_ENUM_THREAD_REGIME_CNT ];
     521             :   ushort idle_ratio;
     522             :   ushort last_cpu;
     523             :   ulong  backp_msgs;
     524             :   ulong  nvcsw;
     525             :   ulong  nivcsw;
     526             :   ulong  minflt;
     527             :   ulong  majflt;
     528             :   ulong  interrupts;
     529             :   ulong  tlb_shootdowns;
     530             :   ulong  timer_ticks;
     531             : };
     532             : 
     533             : typedef struct fd_gui_tile_timers_hist fd_gui_tile_timers_hist_t;
     534             : 
     535             : struct fd_gui_scheduler_counts {
     536             :   long  sample_time_ns;
     537             :   ulong regular;
     538             :   ulong votes;
     539             :   ulong conflicting;
     540             :   ulong bundles;
     541             :   ulong next_auction;
     542             : };
     543             : 
     544             : typedef struct fd_gui_scheduler_counts fd_gui_scheduler_counts_t;
     545             : 
     546             : struct fd_gui_network_stats {
     547             :   /* total bytes accumulated */
     548             :   struct {
     549             :     ulong turbine;
     550             :     ulong gossip;
     551             :     ulong tpu;
     552             :     ulong repair;
     553             :     ulong rserve;
     554             :     ulong metric;
     555             :     ulong votor;
     556             :   } in, out;
     557             : };
     558             : 
     559             : typedef struct fd_gui_network_stats fd_gui_network_stats_t;
     560             : 
     561             : struct fd_gui_accounts_stats {
     562             :   /* Raw counters/gauges read from tile metric pages.  Stored so we can
     563             :      compute deltas (and per-second rates) against a previous snapshot. */
     564             :   long  sample_time_nanos;
     565             : 
     566             :   /* Disk (gauges from accdb tile). */
     567             :   ulong accounts_total;
     568             :   ulong accounts_capacity;
     569             :   ulong disk_allocated_bytes;
     570             :   ulong disk_current_bytes;
     571             :   ulong disk_used_bytes;
     572             : 
     573             :   /* Compaction (gauges + counters from accdb tile). */
     574             :   ulong in_compaction;
     575             :   ulong compactions_requested;
     576             :   ulong compactions_completed;
     577             :   ulong accounts_relocated_bytes;
     578             : 
     579             :   /* Cache occupancy (gauges from accdb tile, per class). */
     580             :   ulong cache_class_used           [ FD_ACCDB_CACHE_CLASS_CNT ];
     581             :   ulong cache_class_max            [ FD_ACCDB_CACHE_CLASS_CNT ];
     582             :   ulong cache_class_reserved       [ FD_ACCDB_CACHE_CLASS_CNT ];
     583             :   /* Preeviction thresholds, expressed as used-slot counts directly
     584             :      comparable to cache_class_used / cache_class_max. */
     585             :   ulong cache_class_target_used    [ FD_ACCDB_CACHE_CLASS_CNT ];
     586             :   ulong cache_class_low_water_used [ FD_ACCDB_CACHE_CLASS_CNT ];
     587             : 
     588             :   /* Aggregate counters summed across all accdb consumer tiles. */
     589             :   ulong acquired;                   /* total acquires */
     590             :   ulong acquired_writable;          /* writable subset of acquires (RW tiles only) */
     591             :   ulong acquired_per_class           [ FD_ACCDB_CACHE_CLASS_CNT ]; /* acquires attributed to a class (RW tiles only) */
     592             :   ulong acquired_writable_per_class  [ FD_ACCDB_CACHE_CLASS_CNT ]; /* writable acquires attributed to a class (RW tiles only) */
     593             :   ulong not_found_per_class          [ FD_ACCDB_CACHE_CLASS_CNT ]; /* misses, per class */
     594             :   ulong evicted_per_class            [ FD_ACCDB_CACHE_CLASS_CNT ];
     595             :   ulong preevicted_per_class         [ FD_ACCDB_CACHE_CLASS_CNT ];
     596             :   ulong committed_new_per_class      [ FD_ACCDB_CACHE_CLASS_CNT ];
     597             :   ulong committed_overwrite_per_class[ FD_ACCDB_CACHE_CLASS_CNT ];
     598             : 
     599             :   /* IO counters.  bytes_written/read_ops/write_ops are summed across
     600             :      all consumer tiles; bytes_written_accdb is the bytes written by
     601             :      the accdb tile itself (background preevict + compaction), used to
     602             :      compute the prewrite ratio. */
     603             :   ulong bytes_read;
     604             :   ulong bytes_copied;
     605             :   ulong bytes_written;
     606             :   ulong bytes_written_accdb;
     607             :   ulong read_ops;
     608             :   ulong write_ops;
     609             : };
     610             : 
     611             : typedef struct fd_gui_accounts_stats fd_gui_accounts_stats_t;
     612             : 
     613             : struct fd_gui_turbine_slot {
     614             :  ulong slot;
     615             :  long timestamp;
     616             : };
     617             : 
     618             : typedef struct fd_gui_turbine_slot fd_gui_turbine_slot_t;
     619             : 
     620             : struct __attribute__((packed)) fd_gui_slot {
     621             :   ulong     slot;             /* this record's slot number. */
     622             :   ulong     bank_seq;         /* this block's fork discriminator (the record's own key bank_seq).*/
     623             :   ulong     parent_bank_seq;  /* parent block's fork discriminator (ULONG_MAX if unknown) */
     624             :   long      completed_time;   /* slot completion wallclock ns (LONG_MAX if unknown) */
     625             :   uint      shred_cnt;        /* slot->shred_cnt at completion */
     626             :   fd_hash_t block_hash;       /* block hash of the slot */
     627             :   uchar     mine:1;           /* 1 if this was our leader slot */
     628             :   uchar     is_voter:2;       /* one of FD_GUI_IS_VOTER_* */
     629             :   uchar     timeline_mine_accounted:1;
     630             :   uint      vote_success;     /* successful vote txn count     (UINT_MAX if unknown) */
     631             :   uint      vote_failed;      /* failed vote txn count         (UINT_MAX if unknown) */
     632             :   uint      nonvote_success;  /* successful nonvote txn count  (UINT_MAX if unknown) */
     633             :   uint      nonvote_failed;   /* failed nonvote txn count      (UINT_MAX if unknown) */
     634             :   uint      max_compute_units;/* block compute unit limit      (UINT_MAX if unknown) */
     635             :   uint      compute_units;    /* block compute units consumed  (UINT_MAX if unknown) */
     636             :   ulong     transaction_fee;  /* total transaction fee         (ULONG_MAX if unknown) */
     637             :   ulong     priority_fee;     /* total priority fee            (ULONG_MAX if unknown) */
     638             :   ulong     tips;             /* total tips                    (ULONG_MAX if unknown) */
     639             :   ulong     parent_slot;      /* parent (on-fork) slot number  (ULONG_MAX if unknown) */
     640             :   long      parent_completed_time; /* parent block completion wallclock ns (LONG_MAX if unknown) */
     641             :   ulong     vote_slot;        /* most recent slot we had landed a vote for as of this slot's replay (ULONG_MAX if unknown) */
     642             :   uchar     skip;             /* one of FD_GUI_SKIP_STATUS_* */
     643             :   uchar     level;            /* one of FD_GUI_SLOT_LEVEL_* */
     644             :   uchar     notarization_kind; /* Alpenglow: one of FD_GUI_AG_NOTAR_* */
     645             :   uchar     finalization_kind; /* Alpenglow: one of FD_GUI_AG_FINAL_* */
     646             :   uchar     vote_rewarded;     /* Alpenglow: one of FD_GUI_VOTE_REWARDED_* */
     647             :   ushort    vote_count;        /* Alpenglow: distinct signers of the reward certs carried by this (cert) slot, USHORT_MAX if unknown */
     648             : };
     649             : 
     650             : typedef struct fd_gui_slot fd_gui_slot_t;
     651             : 
     652             : static inline void
     653             : fd_gui_slot_set_voter_state( fd_gui_slot_t * slot,
     654           0 :                              uchar           state ) {
     655           0 :   slot->is_voter = FD_GUI_IS_VOTER_UNKNOWN;
     656           0 :   if(      state==FD_GUI_IS_VOTER_YES ) slot->is_voter = FD_GUI_IS_VOTER_YES;
     657           0 :   else if( state==FD_GUI_IS_VOTER_NO  ) slot->is_voter = FD_GUI_IS_VOTER_NO;
     658           0 : }
     659             : 
     660             : /* Alpenglow certificates name a block by (slot, block_id), while slot
     661             :    records are keyed by (slot, bank_seq).  Certificates are buffered as
     662             :    they arrive before replay completes their block. They are then
     663             :    eagerly joined by their block_id.  The buffer covers max_live_slots
     664             :    so replay can consume any block retained by the execution pipeline. */
     665             : 
     666             : struct fd_gui_ag_slot {
     667             :   ulong     slot;              /* ULONG_MAX when this entry is empty */
     668             :   fd_hash_t block_id;          /* Double merkle root of the block data. { 0 } by default. */
     669             :   ulong     bank_seq;          /* ULONG_MAX until this block completes replay */
     670             :   uchar     notarization_kind; /* one of FD_GUI_AG_NOTAR_* */
     671             :   uchar     finalization_kind; /* one of FD_GUI_AG_FINAL_* */
     672             :   uchar     skip;              /* one of FD_GUI_SKIP_STATUS_* */
     673             : };
     674             : 
     675             : typedef struct fd_gui_ag_slot fd_gui_ag_slot_t;
     676             : 
     677             : struct fd_gui_slot_ranking {
     678             :   ulong slot;
     679             :   ulong value;
     680             :   int   type;
     681             : };
     682             : typedef struct fd_gui_slot_ranking fd_gui_slot_ranking_t;
     683             : 
     684             : struct fd_gui_slot_rankings {
     685             :   fd_gui_slot_ranking_t largest_tips           [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
     686             :   fd_gui_slot_ranking_t largest_fees           [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
     687             :   fd_gui_slot_ranking_t largest_rewards        [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
     688             :   fd_gui_slot_ranking_t largest_duration       [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
     689             :   fd_gui_slot_ranking_t largest_compute_units  [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
     690             :   fd_gui_slot_ranking_t largest_skipped        [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
     691             :   fd_gui_slot_ranking_t largest_rewards_per_cu [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
     692             :   fd_gui_slot_ranking_t smallest_tips          [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
     693             :   fd_gui_slot_ranking_t smallest_fees          [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
     694             :   fd_gui_slot_ranking_t smallest_rewards       [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
     695             :   fd_gui_slot_ranking_t smallest_rewards_per_cu[ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
     696             :   fd_gui_slot_ranking_t smallest_duration      [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
     697             :   fd_gui_slot_ranking_t smallest_compute_units [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
     698             :   fd_gui_slot_ranking_t smallest_skipped       [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
     699             : };
     700             : 
     701             : typedef struct fd_gui_slot_rankings fd_gui_slot_rankings_t;
     702             : 
     703           0 : #define FD_GUI_EPOCH_SCHED_CNT ((MAX_SLOTS_PER_EPOCH+FD_EPOCH_SLOTS_PER_ROTATION-1UL)/FD_EPOCH_SLOTS_PER_ROTATION)
     704             : #define FD_GUI_EPOCH_PUB_CNT   (MAX_STAKE_WEIGHTS)
     705             : 
     706           0 : #define FD_GUI_TIMELINE_SLOT_STATE_VALID     ((uchar)1U)  /* rooted; interval is set */
     707           0 : #define FD_GUI_TIMELINE_SLOT_STATE_SKIPPED   ((uchar)2U)
     708           0 : #define FD_GUI_TIMELINE_SLOT_STATE_MINE      ((uchar)4U)
     709           0 : #define FD_GUI_TIMELINE_SLOT_STATE_STARTED   ((uchar)8U)  /* first_shred_ns is set */
     710             : 
     711             : struct fd_gui_epoch {
     712             :   ulong epoch;
     713             :   /* Per-slot timeline intervals; idx is slot-start_slot. */
     714             :   ulong timeline_slot_lo_idx;                                  /* lowest idx written (ULONG_MAX if none) */
     715             :   ulong timeline_slot_hi_idx;                                  /* highest idx written (ULONG_MAX if none) */
     716             :   long  timeline_slot_min_start_ns;                            /* earliest start ever written (LONG_MAX if none) */
     717             :   long  timeline_slot_max_end_ns;                              /* latest end ever written (LONG_MIN if none) */
     718             :   long  timeline_slot_first_shred_ns[ MAX_SLOTS_PER_EPOCH ];   /* first shred arrival, valid if STARTED */
     719             :   long  timeline_slot_start_ns      [ MAX_SLOTS_PER_EPOCH ];   /* interval start (LONG_MAX if unknown) */
     720             :   long  timeline_slot_end_ns        [ MAX_SLOTS_PER_EPOCH ];   /* replay completion, or interpolated if skipped and never replayed */
     721             :   uint  timeline_slot_shred_cnt     [ MAX_SLOTS_PER_EPOCH ];   /* data shreds of the block ending the interval (UINT_MAX if unknown) */
     722             :   uchar timeline_slot_state         [ MAX_SLOTS_PER_EPOCH ];   /* FD_GUI_TIMELINE_SLOT_STATE_* flags */
     723             :   ulong start_slot;
     724             :   ulong slot_cnt;                        /* end_slot = start_slot + slot_cnt - 1 */
     725             :   long  start_time;                      /* epoch start wallclock ns (LONG_MAX if unknown) */
     726             :   long  end_time;                        /* epoch end wallclock ns   (LONG_MAX if unknown) */
     727             :   long  target_slot_duration_ns;         /* protocol target slot duration */
     728             :   ulong my_total_slots;                  /* our leader slots seen this epoch */
     729             :   ulong my_skipped_slots;                /* our skipped leader slots this epoch */
     730             :   ulong rankings_slot;                   /* one more than the largest slot processed into rankings */
     731             :   fd_gui_slot_rankings_t rankings   [ 1 ]; /* global slot rankings */
     732             :   fd_gui_slot_rankings_t my_rankings[ 1 ]; /* my slots only */
     733             : 
     734             :   uchar  is_voter     [ MAX_SLOTS_PER_EPOCH ]; /* Alpenglow: one of FD_GUI_IS_VOTER_* */
     735             :   uchar  skipped      [ MAX_SLOTS_PER_EPOCH ]; /* 1 if the slot was skipped on the rooted fork */
     736             :   uchar  vote_rewarded[ MAX_SLOTS_PER_EPOCH ]; /* Alpenglow: one of FD_GUI_VOTE_REWARDED_* */
     737             :   ushort vote_count   [ MAX_SLOTS_PER_EPOCH ]; /* Alpenglow: distinct signers of the reward certs carried by this (cert) slot, USHORT_MAX if unknown */
     738             : 
     739             :   fd_epoch_schedule_t epoch_schedule;    /* slot<->epoch conversion (fd_slot_to_epoch) */
     740             :   ulong               pub_cnt;           /* number of leader pubkeys in pub[] */
     741             :   ulong               stakes_cnt;        /* number of entries in stakes[] */
     742             :   uint                sched[ FD_GUI_EPOCH_SCHED_CNT ]; /* rotation -> index into pub[] */
     743             :   fd_pubkey_t         pub[ FD_GUI_EPOCH_PUB_CNT ];     /* leaders, one per vote account */
     744             :   fd_vote_stake_weight_t stakes[ MAX_STAKE_WEIGHTS ];
     745             : };
     746             : 
     747             : typedef struct fd_gui_epoch fd_gui_epoch_t;
     748             : 
     749             : struct fd_gui_ephemeral_slot {
     750             :   ulong slot; /* ULONG_MAX indicates invalid/evicted */
     751             :   long timestamp_arrival_nanos;
     752             : };
     753             : typedef struct fd_gui_ephemeral_slot fd_gui_ephemeral_slot_t;
     754             : 
     755             : struct __attribute__((packed)) fd_gui_store_txn_start {
     756             :   ulong slot;
     757             :   ulong bank_seq;               /* per-fork bank sequence */
     758             :   long  insert_time_ns;
     759             :   ulong txn_idx;                /* per-(bank_seq) transaction index */
     760             :   uchar signature[ FD_TXN_SIGNATURE_SZ ];
     761             :   ulong transaction_fee;
     762             :   ulong priority_fee;
     763             :   long  timestamp_arrival_nanos;
     764             :   long  microblock_start_ns;    /* absolute ns (begin tspub) */
     765             :   uint  compute_units_requested;
     766             :   uint  microblock_idx;
     767             :   uint  source_ipv4;
     768             :   uchar source_tpu;
     769             :   uchar flags;                  /* STARTED | FROM_BUNDLE */
     770             : };
     771             : typedef struct fd_gui_store_txn_start fd_gui_store_txn_start_t;
     772             : 
     773             : struct __attribute__((packed)) fd_gui_store_txn_end {
     774             :   ulong slot;
     775             :   ulong bank_seq;               /* per-fork bank sequence */
     776             :   long  insert_time_ns;
     777             :   ulong txn_idx;
     778             :   long  timestamp_arrival_nanos;
     779             :   long  microblock_end_ns;      /* absolute ns (end tspub) */
     780             :   fd_txn_ns_dt_t txn_ns_dt;     /* relative to microblock_start */
     781             :   ulong tips;
     782             :   uint  compute_units_consumed;
     783             :   uint  bank_idx;
     784             :   uint  error_code;
     785             :   uchar flags;                  /* ENDED | LANDED_IN_BLOCK */
     786             : };
     787             : typedef struct fd_gui_store_txn_end fd_gui_store_txn_end_t;
     788             : 
     789             : struct fd_gui_store_replay_txn {
     790             :   long  insert_time_ns;
     791             :   long  completion_time_ns;
     792             :   ulong block_compute_unit_limit;
     793             :   ulong slot;
     794             :   ulong txn_idx;
     795             :   ulong txn_exec_idx;
     796             :   ulong txn_sigverify_exec_idx;
     797             :   uint  txn_start_shred_idx; /* UINT_MAX if unknown; USHORT_MAX is a valid saturated index */
     798             :   uint  txn_end_shred_idx;   /* Exclusive end, capped at the block's last shred; UINT_MAX if unknown */
     799             :   uchar signature[ FD_TXN_SIGNATURE_SZ ];
     800             : 
     801             :   long sigverify_start_ns;
     802             :   long sigverify_end_ns;
     803             :   long load_start_ns;
     804             :   long check_start_ns;
     805             :   long exec_start_ns;
     806             :   long commit_start_ns;
     807             :   long commit_end_ns;
     808             : 
     809             :   ulong transaction_fee;
     810             :   ulong priority_fee;
     811             :   ulong tips;
     812             :   uint  compute_units_requested; /* 0 means unavailable */
     813             :   uint  compute_units_consumed;
     814             :   uint  error_code;
     815             :   uchar is_committable;
     816             :   uchar is_fees_only;
     817             : };
     818             : typedef struct fd_gui_store_replay_txn fd_gui_store_replay_txn_t;
     819             : 
     820             : struct fd_gui_slot_txn_join {
     821             :   fd_gui_store_txn_start_t const * start;
     822             :   fd_gui_store_txn_end_t   const * end;
     823             : };
     824             : 
     825             : typedef struct fd_gui_slot_txn_join fd_gui_slot_txn_join_t;
     826             : 
     827             : struct fd_gui_txn_waterfall {
     828             :   long sample_time_nanos; /* wallclock ns this sample was taken. */
     829             :   struct {
     830             :     ulong quic;
     831             :     ulong udp;
     832             :     ulong block_engine;
     833             :     ulong pack_cranked;
     834             :   } in;
     835             : 
     836             :   struct {
     837             :     ulong net_overrun;
     838             :     ulong quic_overrun;
     839             :     ulong quic_frag_drop;
     840             :     ulong quic_abandoned;
     841             :     ulong tpu_quic_invalid;
     842             :     ulong tpu_udp_invalid;
     843             :     ulong verify_overrun;
     844             :     ulong verify_parse;
     845             :     ulong verify_failed;
     846             :     ulong verify_duplicate;
     847             :     ulong dedup_duplicate;
     848             :     ulong resolv_lut_failed;
     849             :     ulong resolv_expired;
     850             :     ulong resolv_ancient;
     851             :     ulong resolv_no_ledger;
     852             :     ulong resolv_retained;
     853             :     ulong pack_invalid;
     854             :     ulong pack_invalid_bundle;
     855             :     ulong pack_expired;
     856             :     ulong pack_already_executed;
     857             :     ulong pack_retained;
     858             :     ulong pack_leader_slow;
     859             :     ulong bank_invalid;
     860             :     ulong bank_nonce_already_advanced;
     861             :     ulong bank_nonce_advance_failed;
     862             :     ulong bank_nonce_wrong_blockhash;
     863             :     ulong block_success;
     864             :     ulong block_fail;
     865             :   } out;
     866             : };
     867             : 
     868             : typedef struct fd_gui_txn_waterfall fd_gui_txn_waterfall_t;
     869             : 
     870             : struct __attribute__((packed)) fd_gui_leader_slot {
     871             :   ulong     slot;                         /* this record's slot number (the record's own key). */
     872             :   ulong     bank_seq;                     /* this block's fork discriminator (the record's own key bank_seq). */
     873             :   long      leader_start_time;            /* wallclock ns we became leader */
     874             :   long      leader_end_time;              /* wallclock ns we stopped being leader */
     875             :   long      txn_insert_time_min_ns;
     876             :   long      txn_insert_time_max_ns;
     877             :   fd_hash_t block_hash;                   /* block hash of the produced block */
     878             :   ulong     max_microblocks;              /* initial max microblocks packable into the slot */
     879             :   ulong     max_compute_units;            /* block CU limit announced on becoming leader */
     880             :   uint      microblocks_upper_bound;      /* final/exact microblock upper bound */
     881             :   uint      begin_microblocks;            /* microblocks started (pack -> bank) */
     882             :   uint      end_microblocks;              /* microblocks ended (bank -> poh) */
     883             :   fd_done_packing_t scheduler_stats[ 1 ]; /* pack "done packing" record (limits, usage, results) */
     884             :   fd_gui_txn_waterfall_t waterfall_reference[ 1 ];
     885             :   fd_gui_txn_waterfall_t waterfall[ 1 ];
     886             :   uchar     has_waterfall;                /* 1 once waterfall has been finalized */
     887             :   uchar     unbecame_leader;              /* 1 if we relinquished leadership for this slot */
     888             : };
     889             : 
     890             : typedef struct fd_gui_leader_slot fd_gui_leader_slot_t;
     891             : 
     892             : struct fd_gui_tile_stats {
     893             :   long  sample_time_nanos;
     894             : 
     895             :   ulong net_in_rx_bytes;           /* Number of bytes received by the net or sock tile*/
     896             :   ulong quic_conn_cnt;             /* Number of active QUIC connections */
     897             :   fd_histf_t bundle_rx_delay_hist; /* Histogram of bundle rx delay */
     898             :   ulong bundle_rtt_smoothed_nanos; /* RTT (nanoseconds) moving average */
     899             :   ulong verify_drop_cnt;           /* Number of transactions dropped by verify tiles */
     900             :   ulong verify_total_cnt;          /* Number of transactions received by verify tiles */
     901             :   ulong dedup_drop_cnt;            /* Number of transactions dropped by dedup tile */
     902             :   ulong dedup_total_cnt;           /* Number of transactions received by dedup tile */
     903             :   ulong pack_buffer_cnt;           /* Number of buffered transactions in the pack tile */
     904             :   ulong pack_buffer_capacity;      /* Total size of the pack transaction buffer */
     905             :   ulong bank_txn_exec_cnt;         /* Number of transactions processed by the bank tile */
     906             :   ulong net_out_tx_bytes;          /* Number of bytes sent by the net or sock tile */
     907             : };
     908             : 
     909             : typedef struct fd_gui_tile_stats fd_gui_tile_stats_t;
     910             : 
     911             : 
     912             : struct fd_gui_boot_progress {
     913             :   uchar phase;
     914             :   ulong boot_target_slot_duration_nanos;
     915             :   long joining_gossip_time_nanos;
     916             :   struct {
     917             :     ulong  slot;
     918             :     uint   peer_addr;
     919             :     ushort peer_port;
     920             :     ulong  total_bytes_compressed;
     921             :     long   reset_time_nanos;          /* UNIX nanosecond timestamp */
     922             :     long   sample_time_nanos;
     923             :     ulong  reset_cnt;
     924             : 
     925             :     ulong read_bytes_compressed;
     926             :     char  read_path[ PATH_MAX+30UL ]; /* URL or filesystem path.  30 is fd_cstr_nlen( "https://255.255.255.255:12345/", ULONG_MAX ) */
     927             : 
     928             :     ulong decompress_bytes_decompressed;
     929             :     ulong decompress_bytes_compressed;
     930             : 
     931             :     ulong insert_bytes_decompressed;
     932             :     char  insert_path[ PATH_MAX ];
     933             :     ulong insert_accounts_current;
     934             :   } loading_snapshot[ FD_GUI_BOOT_PROGRESS_SNAPSHOT_CNT ];
     935             : 
     936             :   ulong wfs_total_stake;
     937             :   ulong wfs_connected_stake;
     938             :   ulong wfs_total_peers;
     939             :   ulong wfs_connected_peers;
     940             :   ulong wfs_attempt;
     941             : 
     942             :   long  catching_up_time_nanos;
     943             :   ulong catching_up_first_replay_slot;
     944             : };
     945             : 
     946             : typedef struct fd_gui_boot_progress fd_gui_boot_progress_t;
     947             : 
     948             : #define FD_GUI_SNAPSV_PENDING_MAX (16384UL)
     949             : #define FD_GUI_SNAPSV_PUBLISH_INTERVAL_NANOS ( 50L*1000L*1000L)
     950             : 
     951             : struct fd_gui_snapsv_pending {
     952             :   fd_snapsv_msg_snap_t msg;
     953             :   long sample_time_nanos;
     954             :   int  closed;
     955             : };
     956             : 
     957             : typedef struct fd_gui_snapsv_pending fd_gui_snapsv_pending_t;
     958             : 
     959             : /* Triangular-weighted moving window over per-snap deltas.  Snap
     960             :    cadence is ~100ms, window is FD_GUI_ACCDB_WIN_SAMPLES samples
     961             :    (~5s).  The output rate for any metric is:
     962             : 
     963             :      rate = sum( w[i] * delta[i] ) / sum( w[i] * dt[i] )
     964             : 
     965             :    where w[i] is the triangular weight (newest sample heaviest,
     966             :    oldest weight=1).  This is fully caught up to a new rate after
     967             :    the window length but smoother than a boxcar (no cliff edge as
     968             :    samples age out). */
     969           0 : #define FD_GUI_ACCDB_WIN_SAMPLES 50UL
     970             : 
     971             : /* Per-partition triangular-weighted windows for read/write rates.
     972             :    Sized to match the accdb partition pool ceiling (8192).  Rates
     973             :    are derived in fd_gui_printf via fd_gui_accdb_weighted_rate. */
     974           0 : #define FD_GUI_MAX_PARTITIONS 8192UL
     975             : 
     976           0 : #define FD_GUI_ACCDB_EMA_HALF_LIFE_NS (600L*1000L*1000L*1000L) /* 10 minutes */
     977             : 
     978             : /* Tile kinds.  Determines which subset of metrics to read. */
     979           0 : #define FD_GUI_ACCDB_TILE_KIND_RW     0  /* execle, execrp, replay */
     980           0 : #define FD_GUI_ACCDB_TILE_KIND_RO     1  /* rpc, resolv */
     981           0 : #define FD_GUI_ACCDB_TILE_KIND_SNAPIN 2  /* snapin (direct snapshot disk writer) */
     982           0 : #define FD_GUI_ACCDB_TILE_KIND_ACCDB  3  /* accdb tile itself (prewrite + compaction writes) */
     983             : 
     984             : /* 60s-history rings for the per-tile sparkline.  Each bucket is the
     985             :    sum of per-snap deltas that fell into that bucket window.  When a
     986             :    bucket interval elapses the per-second rate is formatted once, as
     987             :    the "%.2f," JSON array element it is emitted as, and appended to a
     988             :    packed text ring (oldest first, the oldest dropping off the front
     989             :    once full) so each emit is one copy rather than a printf per sample.
     990             :    240 buckets x 250ms = 60 second window.  The longest element is the
     991             :    rate of a wrapped counter, 2^64/0.25 s: 20 digits, ".00" and the
     992             :    comma. */
     993           0 : #define FD_GUI_ACCDB_SPARKLINE_SAMPLES   240UL
     994           0 : #define FD_GUI_ACCDB_SPARKLINE_BUCKET_NS 250000000L
     995             : #define FD_GUI_ACCDB_SPARKLINE_TEXT_MAX  24UL
     996             : 
     997             : struct fd_gui_accdb_stats {
     998             :   ulong accdb_win_idx;        /* next write index */
     999             :   ulong accdb_win_count;      /* samples filled, capped at FD_GUI_ACCDB_WIN_SAMPLES */
    1000             :   long  accdb_win_dt_nanos [ FD_GUI_ACCDB_WIN_SAMPLES ];
    1001             : 
    1002             :   /* Aggregate delta rings (units per snap). */
    1003             :   ulong agg_acquired_win          [ FD_GUI_ACCDB_WIN_SAMPLES ];
    1004             :   ulong agg_acquired_writable_win [ FD_GUI_ACCDB_WIN_SAMPLES ];
    1005             :   ulong agg_bytes_read_win        [ FD_GUI_ACCDB_WIN_SAMPLES ];
    1006             :   ulong agg_bytes_copied_win      [ FD_GUI_ACCDB_WIN_SAMPLES ];
    1007             :   ulong agg_bytes_written_win     [ FD_GUI_ACCDB_WIN_SAMPLES ];
    1008             :   ulong agg_bytes_written_accdb_win[FD_GUI_ACCDB_WIN_SAMPLES ];
    1009             :   ulong agg_read_ops_win          [ FD_GUI_ACCDB_WIN_SAMPLES ];
    1010             :   ulong agg_write_ops_win         [ FD_GUI_ACCDB_WIN_SAMPLES ];
    1011             :   ulong agg_relocated_bytes_win   [ FD_GUI_ACCDB_WIN_SAMPLES ];
    1012             :   ulong agg_misses_win            [ FD_GUI_ACCDB_WIN_SAMPLES ];
    1013             : 
    1014             :   /* Per-class delta rings (units per snap). */
    1015             :   ulong class_acq_win         [ FD_ACCDB_CACHE_CLASS_CNT ][ FD_GUI_ACCDB_WIN_SAMPLES ];
    1016             :   ulong class_acq_wr_win      [ FD_ACCDB_CACHE_CLASS_CNT ][ FD_GUI_ACCDB_WIN_SAMPLES ];
    1017             :   ulong class_not_found_win   [ FD_ACCDB_CACHE_CLASS_CNT ][ FD_GUI_ACCDB_WIN_SAMPLES ];
    1018             :   ulong class_evicted_win     [ FD_ACCDB_CACHE_CLASS_CNT ][ FD_GUI_ACCDB_WIN_SAMPLES ];
    1019             :   ulong class_preevicted_win  [ FD_ACCDB_CACHE_CLASS_CNT ][ FD_GUI_ACCDB_WIN_SAMPLES ];
    1020             :   ulong class_commit_new_win  [ FD_ACCDB_CACHE_CLASS_CNT ][ FD_GUI_ACCDB_WIN_SAMPLES ];
    1021             :   ulong class_commit_over_win [ FD_ACCDB_CACHE_CLASS_CNT ][ FD_GUI_ACCDB_WIN_SAMPLES ];
    1022             : 
    1023             :   ulong partition_cnt;            /* live count from accdb_shmem; <= FD_GUI_MAX_PARTITIONS */
    1024             :   ulong partition_read_ops_win    [ FD_GUI_MAX_PARTITIONS ][ FD_GUI_ACCDB_WIN_SAMPLES ];
    1025             :   ulong partition_bytes_read_win  [ FD_GUI_MAX_PARTITIONS ][ FD_GUI_ACCDB_WIN_SAMPLES ];
    1026             :   ulong partition_write_ops_win   [ FD_GUI_MAX_PARTITIONS ][ FD_GUI_ACCDB_WIN_SAMPLES ];
    1027             :   ulong partition_bytes_written_win[FD_GUI_MAX_PARTITIONS ][ FD_GUI_ACCDB_WIN_SAMPLES ];
    1028             : 
    1029             :   /* Per-partition snapshots (most recent values, for non-rate fields:
    1030             :      offset, layer, write_offset, bytes_freed, ticks, compaction state). */
    1031             :   fd_accdb_shmem_partition_info_t partitions[ FD_GUI_MAX_PARTITIONS ];
    1032             : 
    1033             :   /* Cumulative counters from the previous snap for delta computation. */
    1034             :   ulong partition_prev_read_ops    [ FD_GUI_MAX_PARTITIONS ];
    1035             :   ulong partition_prev_bytes_read  [ FD_GUI_MAX_PARTITIONS ];
    1036             :   ulong partition_prev_write_ops   [ FD_GUI_MAX_PARTITIONS ];
    1037             :   ulong partition_prev_bytes_written[FD_GUI_MAX_PARTITIONS ];
    1038             : 
    1039             :   /* Per-tier (i.e. hot, warm, cold) EMAs (10 min half-life) used to project when the next
    1040             :      compaction will trigger. */
    1041             :   long         tier_sample_nanos;
    1042             :   fd_gui_ema_t tier_fill_bps_ema[ FD_ACCDB_COMPACTION_LAYER_CNT ];
    1043             :   fd_gui_ema_t tier_free_bps_ema[ FD_ACCDB_COMPACTION_LAYER_CNT ];
    1044             :   double next_compaction_remaining_secs;
    1045             :   ulong  next_compaction_partition_idx;
    1046             : 
    1047             :   /* Per-tile accdb stats.  At init we walk the topology and assign a
    1048             :      slot to each tile that uses the account database (execle, execrp,
    1049             :      replay, rpc, resolv, accdb).  Each slot keeps cumulative
    1050             :      previous values for delta computation and a triangular-weighted
    1051             :      delta ring (same cadence / weighting as the aggregate rings). */
    1052             :   ulong  accdb_tile_cnt;
    1053             :   ushort accdb_tile_topo_idx [ FD_TOPO_MAX_TILES ]; /* index into topo->tiles */
    1054             :   uchar  accdb_tile_kind     [ FD_TOPO_MAX_TILES ];
    1055             : 
    1056             :   /* Most-recent cumulative values per tile, plus the snapshot from
    1057             :      the previous snap for delta computation. */
    1058             :   ulong tile_cur_acquired          [ FD_TOPO_MAX_TILES ];
    1059             :   ulong tile_cur_acquired_writable [ FD_TOPO_MAX_TILES ];
    1060             :   ulong tile_cur_bytes_read        [ FD_TOPO_MAX_TILES ];
    1061             :   ulong tile_cur_bytes_copied      [ FD_TOPO_MAX_TILES ];
    1062             :   ulong tile_cur_bytes_written     [ FD_TOPO_MAX_TILES ];
    1063             :   ulong tile_cur_read_ops          [ FD_TOPO_MAX_TILES ];
    1064             :   ulong tile_cur_write_ops         [ FD_TOPO_MAX_TILES ];
    1065             :   ulong tile_cur_misses            [ FD_TOPO_MAX_TILES ];
    1066             :   ulong tile_cur_evicted           [ FD_TOPO_MAX_TILES ];
    1067             :   ulong tile_cur_committed         [ FD_TOPO_MAX_TILES ];
    1068             :   ulong tile_cur_acquire_calls     [ FD_TOPO_MAX_TILES ];
    1069             :   uchar tile_cur_status            [ FD_TOPO_MAX_TILES ]; /* 1=running, 2=shutdown */
    1070             : 
    1071             :   ulong tile_prev_acquired         [ FD_TOPO_MAX_TILES ];
    1072             :   ulong tile_prev_acquired_writable[ FD_TOPO_MAX_TILES ];
    1073             :   ulong tile_prev_bytes_read       [ FD_TOPO_MAX_TILES ];
    1074             :   ulong tile_prev_bytes_copied     [ FD_TOPO_MAX_TILES ];
    1075             :   ulong tile_prev_bytes_written    [ FD_TOPO_MAX_TILES ];
    1076             :   ulong tile_prev_read_ops         [ FD_TOPO_MAX_TILES ];
    1077             :   ulong tile_prev_write_ops        [ FD_TOPO_MAX_TILES ];
    1078             :   ulong tile_prev_misses           [ FD_TOPO_MAX_TILES ];
    1079             :   ulong tile_prev_evicted          [ FD_TOPO_MAX_TILES ];
    1080             :   ulong tile_prev_committed        [ FD_TOPO_MAX_TILES ];
    1081             :   ulong tile_prev_acquire_calls    [ FD_TOPO_MAX_TILES ];
    1082             : 
    1083             :   /* Per-tile delta rings. */
    1084             :   ulong tile_acquired_win         [ FD_TOPO_MAX_TILES ][ FD_GUI_ACCDB_WIN_SAMPLES ];
    1085             :   ulong tile_acquired_writable_win[ FD_TOPO_MAX_TILES ][ FD_GUI_ACCDB_WIN_SAMPLES ];
    1086             :   ulong tile_bytes_read_win       [ FD_TOPO_MAX_TILES ][ FD_GUI_ACCDB_WIN_SAMPLES ];
    1087             :   ulong tile_bytes_copied_win     [ FD_TOPO_MAX_TILES ][ FD_GUI_ACCDB_WIN_SAMPLES ];
    1088             :   ulong tile_bytes_written_win    [ FD_TOPO_MAX_TILES ][ FD_GUI_ACCDB_WIN_SAMPLES ];
    1089             :   ulong tile_read_ops_win         [ FD_TOPO_MAX_TILES ][ FD_GUI_ACCDB_WIN_SAMPLES ];
    1090             :   ulong tile_write_ops_win        [ FD_TOPO_MAX_TILES ][ FD_GUI_ACCDB_WIN_SAMPLES ];
    1091             :   ulong tile_misses_win           [ FD_TOPO_MAX_TILES ][ FD_GUI_ACCDB_WIN_SAMPLES ];
    1092             :   ulong tile_evicted_win          [ FD_TOPO_MAX_TILES ][ FD_GUI_ACCDB_WIN_SAMPLES ];
    1093             :   ulong tile_committed_win        [ FD_TOPO_MAX_TILES ][ FD_GUI_ACCDB_WIN_SAMPLES ];
    1094             :   ulong tile_acquire_calls_win    [ FD_TOPO_MAX_TILES ][ FD_GUI_ACCDB_WIN_SAMPLES ];
    1095             : 
    1096             :   long  tile_sparkline_bucket_start_nanos [ FD_TOPO_MAX_TILES ];
    1097             :   ulong tile_sparkline_acq_bucket         [ FD_TOPO_MAX_TILES ];
    1098             :   ulong tile_sparkline_acq_wr_bucket      [ FD_TOPO_MAX_TILES ];
    1099             :   /* Per-second rates of the last N completed buckets as the JSON array
    1100             :      body they are emitted as: the "%.2f," elements concatenated oldest
    1101             :      first in text[ 0, text_len ).  Elements contain no comma but the
    1102             :      one that ends them, so the boundaries are the commas. */
    1103             :   char  tile_sparkline_acq_text        [ FD_TOPO_MAX_TILES ][ FD_GUI_ACCDB_SPARKLINE_SAMPLES*FD_GUI_ACCDB_SPARKLINE_TEXT_MAX ];
    1104             :   char  tile_sparkline_acq_wr_text     [ FD_TOPO_MAX_TILES ][ FD_GUI_ACCDB_SPARKLINE_SAMPLES*FD_GUI_ACCDB_SPARKLINE_TEXT_MAX ];
    1105             :   ulong tile_sparkline_acq_text_len    [ FD_TOPO_MAX_TILES ];
    1106             :   ulong tile_sparkline_acq_wr_text_len [ FD_TOPO_MAX_TILES ];
    1107             :   ulong tile_sparkline_count           [ FD_TOPO_MAX_TILES ];  /* completed buckets, capped at FD_GUI_ACCDB_SPARKLINE_SAMPLES */
    1108             : };
    1109             : 
    1110             : typedef struct fd_gui_accdb_stats fd_gui_accdb_stats_t;
    1111             : 
    1112             : /* fd_gui_summary_t holds the aggregate node-level state rendered by the
    1113             :    "summary" websocket topic and the terminal dashboard. */
    1114             : 
    1115             : struct fd_gui_summary {
    1116             :   fd_pubkey_t identity_key[ 1 ];
    1117             :   int         has_vote_key;
    1118             :   fd_pubkey_t vote_key[ 1 ];
    1119             :   char vote_key_base58[ FD_BASE58_ENCODED_32_SZ ];
    1120             :   char identity_key_base58[ FD_BASE58_ENCODED_32_SZ ];
    1121             : 
    1122             :   int          is_voting;      /* configured to vote (vote account set) */
    1123             :   char const * version;
    1124             :   char const * cluster;
    1125             :   char         accounts_database_path[ PATH_MAX ];
    1126             :   char         gui_database_path     [ PATH_MAX ];
    1127             : 
    1128             :   char   wfs_bank_hash[ FD_BASE58_ENCODED_32_SZ ];
    1129             :   ulong  wfs_slot;
    1130             :   ushort expected_shred_version;
    1131             :   int    wfs_enabled;
    1132             : 
    1133             :   int   vote_state;
    1134             : 
    1135             :   long  startup_time_nanos;
    1136             : 
    1137             :   fd_gui_boot_progress_t boot_progress;
    1138             :   fd_gui_boot_progress_t prev_boot_progress;
    1139             : 
    1140             :   int schedule_strategy;
    1141             : 
    1142             :   ulong  identity_account_balance;
    1143             :   ulong  vote_account_balance;
    1144             :   ushort vote_commission;
    1145             :   ulong  estimated_slot_duration_nanos;
    1146             : 
    1147             :   ulong sock_tile_cnt;
    1148             :   ulong mlx5_tile_cnt;
    1149             :   ulong iavf_tile_cnt;
    1150             :   ulong net_tile_cnt;
    1151             :   ulong quic_tile_cnt;
    1152             :   ulong verify_tile_cnt;
    1153             :   ulong resolv_tile_cnt;
    1154             :   ulong execle_tile_cnt;
    1155             :   ulong execrp_tile_cnt;
    1156             :   ulong shred_tile_cnt;
    1157             : 
    1158             :   ulong slot_rooted;
    1159             :   ulong slot_finalized;
    1160             :   ulong slot_notarized;
    1161             :   ulong slot_estimated;
    1162             :   ulong slot_caught_up;
    1163             :   ulong slot_repair;
    1164             :   ulong slot_turbine;
    1165             :   ulong slot_reset;
    1166             :   ulong slot_storage;
    1167             :   ulong slot_tower;
    1168             :   ulong slot_tower_bank_seq; /* tracks canonical fork frontier */
    1169             :   ulong slot_voted;
    1170             :   ulong active_fork_cnt;
    1171             : 
    1172             :   struct {
    1173             :     ulong epoch;
    1174             :     ulong skipped;
    1175             :     ulong total;
    1176             :   } skip_rate[ 2 ];
    1177             : 
    1178             :   fd_gui_ephemeral_slot_t slots_max_turbine[ FD_GUI_TURBINE_SLOT_HISTORY_SZ+1UL ];
    1179             :   fd_gui_ephemeral_slot_t slots_max_repair [ FD_GUI_REPAIR_SLOT_HISTORY_SZ +1UL ];
    1180             : 
    1181             :   /* catchup_* is run-length encoded. i.e. adjacent pairs represent
    1182             :      contiguous runs */
    1183             :   ulong catch_up_turbine[ FD_GUI_TURBINE_CATCH_UP_HISTORY_SZ ];
    1184             :   ulong catch_up_turbine_sz;
    1185             : 
    1186             :   ulong catch_up_repair[ FD_GUI_REPAIR_CATCH_UP_HISTORY_SZ ];
    1187             :   ulong catch_up_repair_sz;
    1188             : 
    1189             :   ulong estimated_tps_history_idx;
    1190             :   struct {
    1191             :    ulong vote_failed;
    1192             :    ulong vote_success;
    1193             :    ulong nonvote_success;
    1194             :    ulong nonvote_failed;
    1195             :   } estimated_tps_history[ FD_GUI_TPS_HISTORY_SAMPLE_CNT ];
    1196             : 
    1197             :   fd_gui_network_stats_t network_stats_current[ 1 ];
    1198             :   fd_gui_network_stats_t network_stats_prev   [ 1 ];
    1199             :   int                    network_stats_has_prev;
    1200             : 
    1201             :   /* EMA-smoothed network throughput (bytes/sec) with a 1-second
    1202             :      half-life. */
    1203             :   fd_gui_ema_t             ingress_ema[ FD_GUI_NET_PROTO_CNT ];
    1204             :   fd_gui_ema_t             egress_ema[ FD_GUI_NET_PROTO_CNT ];
    1205             :   long                     net_rate_prev_ts;
    1206             :   fd_gui_rate_entry_t *    ingress_maxq;
    1207             :   fd_gui_rate_entry_t *    egress_maxq;
    1208             : 
    1209             :   fd_gui_accdb_stats_t    accdb[ 1 ];
    1210             : 
    1211             :   fd_gui_accounts_stats_t accounts_stats_reference[ 1 ];
    1212             :   fd_gui_accounts_stats_t accounts_stats_current  [ 1 ];
    1213             :   int                     accounts_stats_have_reference;
    1214             : 
    1215             :   fd_gui_txn_waterfall_t txn_waterfall_reference[ 1 ];
    1216             :   fd_gui_txn_waterfall_t txn_waterfall_current  [ 1 ];
    1217             : 
    1218             :   fd_gui_tile_stats_t tile_stats_reference[ 1 ];
    1219             :   fd_gui_tile_stats_t tile_stats_current  [ 1 ];
    1220             : 
    1221             :   ulong progcache_history_idx;
    1222             :   ulong progcache_hits_history   [ FD_GUI_PROGCACHE_HISTORY_CNT ];
    1223             :   ulong progcache_lookups_history[ FD_GUI_PROGCACHE_HISTORY_CNT ];
    1224             :   ulong progcache_hits_1min;
    1225             :   ulong progcache_lookups_1min;
    1226             : 
    1227             :   fd_gui_tile_timers_t      tile_timers_reference[ FD_TOPO_MAX_TILES ];
    1228             :   fd_gui_tile_timers_t      tile_timers_current  [ FD_TOPO_MAX_TILES ];
    1229             :   fd_gui_tile_timers_hist_t tile_timers_packed   [ FD_TOPO_MAX_TILES ];
    1230             : 
    1231             :   /* Topo tile indices in display order, built once on init. */
    1232             :   ulong tile[ FD_TOPO_MAX_TILES ];
    1233             :   ulong tile_cnt;
    1234             : };
    1235             : 
    1236             : typedef struct fd_gui_summary fd_gui_summary_t;
    1237             : 
    1238             : struct fd_gui_timeline_query_slot {
    1239             :   ulong slot;
    1240             :   long  start_ns;
    1241             :   long  end_ns;
    1242             :   uint  shred_cnt; /* data shreds, UINT_MAX if unknown */
    1243             :   uchar skipped;
    1244             :   uchar mine;
    1245             : };
    1246             : typedef struct fd_gui_timeline_query_slot fd_gui_timeline_query_slot_t;
    1247             : 
    1248             : struct fd_gui_timeline_query_event {
    1249             :   ulong slot;
    1250             :   long  ts;
    1251             :   uint  event; /* one of FD_GUI_TIMELINE_EVENT_* */
    1252             : };
    1253             : typedef struct fd_gui_timeline_query_event fd_gui_timeline_query_event_t;
    1254             : 
    1255             : struct fd_gui_timeline_query_bucket {
    1256             :   ulong fields[ FD_GUI_TIMELINE_FIELD_CNT ];
    1257             :   ulong slot_duration;
    1258             :   int   has_day;
    1259             : };
    1260             : typedef struct fd_gui_timeline_query_bucket fd_gui_timeline_query_bucket_t;
    1261             : 
    1262             : /* Workspace for timeline queries.  The GUI tile is single threaded and
    1263             :    each user runs to completion, so the members alias. */
    1264             : 
    1265             : union fd_gui_timeline_scratch {
    1266             :   fd_gui_shred_event_t           events     [ FD_GUI_TIMELINE_QUERY_SHRED_MAX ];
    1267             :   fd_gui_timeline_query_slot_t   slots      [ FD_GUI_TIMELINE_QUERY_SLOT_MAX ];
    1268             :   fd_gui_timeline_query_event_t  slot_events[ FD_GUI_TIMELINE_QUERY_EVENT_MAX ];
    1269             :   fd_gui_timeline_query_bucket_t buckets    [ FD_GUI_TIMELINE_QUERY_MAX_BUCKETS ];
    1270             :   struct { uint sum; uint cnt; } cpu[ FD_GUI_TIMELINE_QUERY_MAX_BUCKETS ];
    1271             : };
    1272             : typedef union fd_gui_timeline_scratch fd_gui_timeline_scratch_t;
    1273             : 
    1274             : struct fd_gui {
    1275             :   fd_gui_timeline_scratch_t timeline_scratch;
    1276             : 
    1277             :   fd_http_server_t * http;
    1278             :   fd_topo_t const * topo;
    1279             :   fd_accdb_shmem_t const * accdb_shmem;
    1280             : 
    1281             :   void * db; /* GUI database */
    1282             :   void * hist;
    1283             : 
    1284             :   double tick_per_ns;
    1285             :   ulong  tile_cnt;
    1286             : 
    1287             :   long next_sample_1sec;
    1288             :   long next_sample_200millis;
    1289             :   long next_sample_100millis;
    1290             :   long next_sample_50millis;
    1291             :   long next_sample_40millis;
    1292             :   long next_sample_25millis;
    1293             :   long next_sample_10millis;
    1294             : 
    1295             :   /* CPU utilization sampler: per-tile regime timer references.
    1296             :      Samples are written to the FD_GUI_HIST_TIMELINE_TILE_DAY records. */
    1297             :   struct {
    1298             :     ulong total[ FD_TOPO_MAX_TILES ];
    1299             :     ulong idle [ FD_TOPO_MAX_TILES ];
    1300             :     ulong last_slot; /* absolute 25ms slot of the last sample, ULONG_MAX if none */
    1301             :     ulong cur_day;   /* newest day with a tile-day record, ULONG_MAX if unknown */
    1302             :   } cpu_util;
    1303             : 
    1304             :   ulong leader_begin_slot;
    1305             :   ulong leader_end_slot;
    1306             :   ulong leader_slot_pending;
    1307             :   ulong leader_bank_seq_pending;
    1308             : 
    1309             :   fd_gui_summary_t summary;
    1310             : 
    1311             :   struct {
    1312             :     int                        valid;
    1313             :     fd_diag_system_resources_t resources;
    1314             :   } system;
    1315             : 
    1316             :   /* Scratch record for the synthesized skipped slots returned by
    1317             :      fd_gui_slot_get_canon_safe. */
    1318             :   fd_gui_slot_t skipped_scratch[ 1 ];
    1319             : 
    1320             :   struct {
    1321             :     ulong              slot_cnt;
    1322             :     fd_gui_ag_slot_t * slot; /* [slot_cnt] */
    1323             :   } ag;
    1324             : 
    1325             :   /* max_consecutive_skips is the longest run of skipped slots the gui
    1326             :      expects, based on slot_far_in_future in
    1327             :      choreo/votor/fd_votor_pool.c. */
    1328             :   ulong max_consecutive_skips;
    1329             : 
    1330             :   /* used for estimating slot duration */
    1331             :   fd_gui_turbine_slot_t turbine_slots[ FD_GUI_TURBINE_RECV_TIMESTAMPS ];
    1332             : 
    1333             :   /* Reusable scratch for reassembling a single slot's transactions at
    1334             :      query time (fd_gui_printf_slot_transactions_request). */
    1335             :   struct {
    1336             :     ulong                      max;
    1337             :     fd_gui_store_txn_start_t * starts; /* [max] */
    1338             :     fd_gui_store_txn_end_t *   ends;   /* [max] */
    1339             :     fd_gui_slot_txn_join_t *   joined; /* [max] */
    1340             :   } slot_txn_scratch;
    1341             : 
    1342             : 
    1343             :   struct {
    1344             :     int has_block_engine;
    1345             :     char name[ 16 ];
    1346             :     char url[ FD_URL_MAX ];
    1347             :     char ip_cstr[ 40 ]; /* IPv4 or IPv6 cstr */
    1348             :     int status;
    1349             :   } block_engine;
    1350             : 
    1351             :   struct {
    1352             :     /* The epoch we are currently in, advanced at epoch_info ingest. */
    1353             :     ulong current_epoch;
    1354             : 
    1355             :     int                 has_epoch_schedule;
    1356             :     fd_epoch_schedule_t epoch_schedule;
    1357             : 
    1358             :     ulong missed_vote_history_dirty; /* epoch whose missed vote history changed since the last broadcast, ULONG_MAX if none */
    1359             : 
    1360             :     uchar __attribute__((aligned(FD_EPOCH_LEADERS_ALIGN))) lsched_scratch[ FD_EPOCH_LEADERS_FOOTPRINT(MAX_STAKE_WEIGHTS, MAX_SLOTS_PER_EPOCH) ];
    1361             :     fd_vote_stake_weight_t stakes_scratch[ MAX_STAKE_WEIGHTS ];
    1362             :   } epoch;
    1363             : 
    1364             :   fd_gui_peers_ctx_t * peers; /* full-client */
    1365             : 
    1366             :   /* One decoded shred event for fd_gui_printf_shreds_window, which
    1367             :      prints a window's events column by column and would otherwise
    1368             :      decode the batches once per column. */
    1369             :   struct {
    1370             :     fd_gui_shred_scratch_t * ev;  /* [max] */
    1371             :     ulong                    max;
    1372             :   } shred_scratch;
    1373             : 
    1374             :   /* Earliest REPLAY_EXEC_DONE timestamp per shred of the
    1375             :      FD_GUI_EXEC_DONE_SLOT_CNT most recent slots, LONG_MAX if none */
    1376             :   struct {
    1377             :     ulong  slot[ FD_GUI_EXEC_DONE_SLOT_CNT ];
    1378             :     long * ts; /* [FD_GUI_EXEC_DONE_SLOT_CNT][FD_SHRED_BLK_MAX] */
    1379             :   } exec_done;
    1380             : 
    1381             :   struct {
    1382             :     ulong leader_shred_cnt;      /* A gauge counting the number of leader shreds seen on the SHRED_OUT link.  Resets at
    1383             :                                     the end of a leader slot.  This works because leader fecs are published in order. */
    1384             :     ulong leader_shred_slot;     /* The slot leader_shred_cnt is currently counting, ULONG_MAX if none. Used to reset
    1385             :                                     counter after an abandoned/ended slot. This works because leader fecs are published
    1386             :                                     in order. */
    1387             : 
    1388             :     fd_gui_shred_builder_t builder;
    1389             :     ulong                  dropped_event_cnt;
    1390             : 
    1391             :     /* FEC event staging (see fd_gui_fec_event_append). */
    1392             :     ulong closed_slot_wmark;     /* Slots below this are permanently closed */
    1393             :     ulong fec_marker_sequence;   /* Last slot complete marker sequence */
    1394             :     long  fec_insert_time_ns;    /* Last staged admission time */
    1395             :     ulong dropped_fec_event_cnt;
    1396             :     ulong clamped_fec_event_cnt;
    1397             : 
    1398             :     /* Events with insert_time_ns below this watermark have already
    1399             :        been pushed to clients. */
    1400             :     long broadcast_watermark_ns;
    1401             :   } shreds;
    1402             : 
    1403             :   struct {
    1404             :     ulong                   pending_cnt;
    1405             :     fd_gui_snapsv_pending_t pending[ FD_GUI_SNAPSV_PENDING_MAX ];
    1406             :   } snapsv;
    1407             : };
    1408             : 
    1409             : typedef struct fd_gui fd_gui_t;
    1410             : 
    1411             : FD_PROTOTYPES_BEGIN
    1412             : 
    1413             : /* fd_gui_cpu_util_snap samples per-tile cpu utilization from the regime
    1414             :    timers into the tiles' day records. */
    1415             : 
    1416             : void
    1417             : fd_gui_cpu_util_snap( fd_gui_t * gui,
    1418             :                       long       now );
    1419             : 
    1420             : /* fd_gui_timeline_day_get returns the retained calendar day for day_idx, or NULL if not found. */
    1421             : 
    1422             : fd_gui_timeline_day_t *
    1423             : fd_gui_timeline_day_get( fd_gui_t * gui,
    1424             :                          ulong      day_idx );
    1425             : 
    1426             : /* fd_gui_timeline_tile_day_get returns the retained CPU record of
    1427             :    tile_idx for calendar day day_idx, or NULL if not found. */
    1428             : 
    1429             : fd_gui_timeline_tile_day_t *
    1430             : fd_gui_timeline_tile_day_get( fd_gui_t * gui,
    1431             :                               ulong      day_idx,
    1432             :                               ulong      tile_idx );
    1433             : 
    1434             : /* fd_gui_timeline_tile_day_bounds scans the retained per-tile CPU day
    1435             :    records.  Returns 1 and sets *lo_day / *hi_day to the oldest / newest
    1436             :    day covered by any of them, or returns 0 if there are none. */
    1437             : 
    1438             : int
    1439             : fd_gui_timeline_tile_day_bounds( fd_gui_t * gui,
    1440             :                                  ulong *    lo_day,
    1441             :                                  ulong *    hi_day );
    1442             : 
    1443             : /* fd_gui_timeline_handle_fec adds the shred counts of one completed FEC
    1444             :    set to the calendar-day aggregates.  It ignores invalid input. */
    1445             : 
    1446             : void
    1447             : fd_gui_timeline_handle_fec( fd_gui_t * gui,
    1448             :                             ulong      slot,
    1449             :                             int        published,
    1450             :                             long       timestamp_ns,
    1451             :                             ulong      turbine,
    1452             :                             ulong      repair,
    1453             :                             ulong      reconstructed,
    1454             :                             long       now );
    1455             : 
    1456             : /* fd_gui_handle_replay_txn records replay transaction metadata. */
    1457             : 
    1458             : void
    1459             : fd_gui_handle_replay_txn( fd_gui_t *                       gui,
    1460             :                           fd_replay_txn_executed_t const * txn,
    1461             :                           long                             now );
    1462             : 
    1463             : /* fd_gui_timeline_slots_collect writes the rooted slot intervals that
    1464             :    overlap [start_ns,end_ns) into rows[0,max), in slot order, and the
    1465             :    number written into *cnt.  Returns 0 on success, 1 if more than max
    1466             :    intervals match, or -1 for invalid arguments. */
    1467             : 
    1468             : int
    1469             : fd_gui_timeline_slots_collect( fd_gui_t *                     gui,
    1470             :                                long                           start_ns,
    1471             :                                long                           end_ns,
    1472             :                                fd_gui_timeline_query_slot_t * rows,
    1473             :                                ulong                          max,
    1474             :                                ulong *                        cnt );
    1475             : 
    1476             : /* fd_gui_timeline_events_collect writes the events whose timestamp is
    1477             :    in [start_ns,end_ns) into rows[0,max), in slot order, and the number
    1478             :    written into *cnt.  Returns 0 on success, 1 if more than max events
    1479             :    match, or -1 for invalid arguments.  The only event is leader_skip: a
    1480             :    skipped slot scheduled to our current identity, stamped at the
    1481             :    midpoint of its interval. */
    1482             : 
    1483             : int
    1484             : fd_gui_timeline_events_collect( fd_gui_t *                      gui,
    1485             :                                 long                            start_ns,
    1486             :                                 long                            end_ns,
    1487             :                                 fd_gui_timeline_query_event_t * rows,
    1488             :                                 ulong                           max,
    1489             :                                 ulong *                         cnt );
    1490             : 
    1491             : /* fd_gui_tile_timers_diff computes the compact, display-ready diff of a
    1492             :    single tile's timers between two raw cumulative samples `prev` and
    1493             :    `cur`, writing it into `out`. */
    1494             : 
    1495             : void
    1496             : fd_gui_tile_timers_diff( fd_gui_tile_timers_hist_t *  out,
    1497             :                          fd_gui_tile_timers_t const * prev,
    1498             :                          fd_gui_tile_timers_t const * cur,
    1499             :                          ulong                        tile_idx,
    1500             :                          long                         sample_time_nanos );
    1501             : 
    1502             : /* fd_gui_boot_snapshot_slot returns the boot slot: the incremental's
    1503             :    if one loaded, else the full's, ULONG_MAX before any load.  Parsed
    1504             :    from archive filenames, so best-effort. */
    1505             : 
    1506             : FD_FN_PURE static inline ulong
    1507           0 : fd_gui_boot_snapshot_slot( fd_gui_t const * gui ) {
    1508           0 :   for( ulong i=FD_GUI_BOOT_PROGRESS_SNAPSHOT_CNT; i>0UL; i-- ) {
    1509           0 :     ulong slot = gui->summary.boot_progress.loading_snapshot[ i-1UL ].slot;
    1510           0 :     if( slot!=ULONG_MAX ) return slot;
    1511           0 :   }
    1512           0 :   return ULONG_MAX;
    1513           0 : }
    1514             : 
    1515             : /* fd_gui_wfs_mode recomputes the WFS verdict (see fd_wfs.h); gossip's
    1516             :    WAIT_FOR_SUPERMAJORITY_STATE gauge cannot tell MATCH from NOOP, as
    1517             :    both end in STATE_DONE. */
    1518             : 
    1519             : FD_FN_PURE static inline int
    1520           0 : fd_gui_wfs_mode( fd_gui_t const * gui ) {
    1521           0 :   return fd_wfs_mode( gui->summary.wfs_slot,
    1522           0 :                       gui->summary.wfs_bank_hash[ 0 ]=='\0',
    1523           0 :                       (ulong)gui->summary.expected_shred_version,
    1524           0 :                       fd_gui_boot_snapshot_slot( gui ) );
    1525           0 : }
    1526             : 
    1527             : FD_FN_CONST ulong
    1528             : fd_gui_align( void );
    1529             : 
    1530             : ulong
    1531             : fd_gui_footprint( ulong tile_cnt,
    1532             :                   ulong max_live_slots,
    1533             :                   ulong max_txn_per_slot );
    1534             : 
    1535             : void *
    1536             : fd_gui_new( void *                   shmem,
    1537             :             fd_http_server_t *       http,
    1538             :             char const *             version,
    1539             :             char const *             cluster,
    1540             :             uchar const *            identity_key,
    1541             :             int                      has_vote_key,
    1542             :             uchar const *            vote_key,
    1543             :             ulong                    max_live_slots,
    1544             :             ulong                    max_txn_per_slot,
    1545             :             int                      snapshots_enabled,
    1546             :             int                      is_voting,
    1547             :             int                      schedule_strategy,
    1548             :             char const *             wfs_expected_bank_hash_cstr,
    1549             :             ushort                   expected_shred_version,
    1550             :             ulong                    wfs_slot,
    1551             :             char const *             accounts_database_path,
    1552             :             char const *             gui_database_path,
    1553             :             void *                   db,
    1554             :             fd_topo_t const *        topo,
    1555             :             fd_accdb_shmem_t const * accdb_shmem,
    1556             :             long                     now );
    1557             : 
    1558             : fd_gui_t *
    1559             : fd_gui_join( void * shmem );
    1560             : 
    1561             : void
    1562             : fd_gui_set_identity( fd_gui_t *    gui,
    1563             :                      uchar const * identity_pubkey );
    1564             : 
    1565             : void
    1566             : fd_gui_handle_diag_snapshot( fd_gui_t *    gui,
    1567             :                              void const *  data,
    1568             :                              ulong         data_sz );
    1569             : 
    1570             : void
    1571             : fd_gui_ws_open( fd_gui_t *  gui,
    1572             :                 ulong       conn_id,
    1573             :                 long now );
    1574             : 
    1575             : int
    1576             : fd_gui_ws_message( fd_gui_t *    gui,
    1577             :                    ulong         ws_conn_id,
    1578             :                    uchar const * data,
    1579             :                    ulong         data_len );
    1580             : 
    1581             : void
    1582             : fd_gui_became_leader( fd_gui_t * gui,
    1583             :                       ulong      slot,
    1584             :                       long       start_time_nanos,
    1585             :                       long       end_time_nanos,
    1586             :                       ulong      max_compute_units,
    1587             :                       ulong      max_microblocks,
    1588             :                       ulong      bank_seq );
    1589             : 
    1590             : void
    1591             : fd_gui_unbecame_leader( fd_gui_t *                gui,
    1592             :                         ulong                     _slot,
    1593             :                         fd_done_packing_t const * done_packing );
    1594             : 
    1595             : void
    1596             : fd_gui_done_draining( fd_gui_t * gui,
    1597             :                       long       now );
    1598             : 
    1599             : void
    1600             : fd_gui_microblock_execution_begin( fd_gui_t *   gui,
    1601             :                                    long         tspub_ns,
    1602             :                                    ulong        _slot,
    1603             :                                    fd_txn_e_t * txns,
    1604             :                                    ulong        txn_cnt,
    1605             :                                    uint         microblock_idx,
    1606             :                                    ulong        pack_txn_idx,
    1607             :                                    ulong        bank_seq,
    1608             :                                    long         now );
    1609             : 
    1610             : void
    1611             : fd_gui_microblock_execution_end( fd_gui_t *     gui,
    1612             :                                  long           tspub_ns,
    1613             :                                  ulong          bank_idx,
    1614             :                                  ulong          _slot,
    1615             :                                  ulong          txn_cnt,
    1616             :                                  fd_txn_p_t *   txns,
    1617             :                                  ulong          pack_txn_idx,
    1618             :                                  fd_txn_ns_dt_t txn_ns_dt,
    1619             :                                  long           exec_end_ticks,
    1620             :                                  ulong          tips,
    1621             :                                  ulong          bank_seq,
    1622             :                                  long           now );
    1623             : 
    1624             : int
    1625             : fd_gui_poll( fd_gui_t * gui, long now );
    1626             : 
    1627             : long
    1628             : fd_gui_next_deadline( fd_gui_t const * gui );
    1629             : 
    1630             : void
    1631             : fd_gui_handle_block_engine_update( fd_gui_t *                              gui,
    1632             :                                    fd_bundle_block_engine_update_t const * update );
    1633             : 
    1634             : void
    1635             : fd_gui_handle_shred( fd_gui_t * gui,
    1636             :                      ulong      slot,
    1637             :                      ulong      shred_idx,
    1638             :                      ulong      fec_set_idx,
    1639             :                      int        is_turbine,
    1640             :                      long       tsorig,
    1641             :                      long       now );
    1642             : 
    1643             : void
    1644             : fd_gui_handle_leader_fec( fd_gui_t * gui,
    1645             :                           ulong      slot,
    1646             :                           ulong      fec_shred_cnt,
    1647             :                           int        is_end_of_slot,
    1648             :                           long       tsorig,
    1649             :                           long       now );
    1650             : 
    1651             : void
    1652             : fd_gui_handle_exec_txn_done( fd_gui_t * gui,
    1653             :                              ulong      slot,
    1654             :                              ulong      start_shred_idx,
    1655             :                              ulong      end_shred_idx,
    1656             :                              long       tsorig_ns,
    1657             :                              long       tspub_ns,
    1658             :                              long       now );
    1659             : 
    1660             : void
    1661             : fd_gui_handle_repair_slot( fd_gui_t * gui, ulong slot, long now );
    1662             : 
    1663             : void
    1664             : fd_gui_handle_repair_request( fd_gui_t * gui, ulong slot, ulong shred_idx, long tsorig, long now );
    1665             : 
    1666             : void
    1667             : fd_gui_handle_snapshot_update( fd_gui_t *                 gui,
    1668             :                                fd_snapct_update_t const * msg );
    1669             : 
    1670             : void
    1671             : fd_gui_handle_snapsv_update( fd_gui_t *              gui,
    1672             :                              ulong                   sig,
    1673             :                              fd_snapsv_msg_t const * msg,
    1674             :                              ulong                   sz,
    1675             :                              int                     eom,
    1676             :                              long                    now );
    1677             : 
    1678             : void
    1679             : fd_gui_stage_snapshot_manifest( fd_gui_t *                       gui,
    1680             :                                 fd_snapshot_manifest_t const *   manifest );
    1681             : 
    1682             : void
    1683             : fd_gui_handle_epoch_info( fd_gui_t *                  gui,
    1684             :                           fd_epoch_info_msg_t const * epoch_info,
    1685             :                           long                        now );
    1686             : 
    1687             : void
    1688             : fd_gui_handle_replay_update( fd_gui_t *                         gui,
    1689             :                              fd_replay_slot_completed_t const * slot_completed,
    1690             :                              long                               now );
    1691             : 
    1692             : void
    1693             : fd_gui_handle_genesis_hash( fd_gui_t *        gui,
    1694             :                             fd_hash_t const * msg );
    1695             : 
    1696             : /* fd_gui_handle_root_advanced is invoked on REPLAY_SIG_ROOT_ADVANCED.
    1697             :    It roots the (slot, bank_seq) fork and updates the dependent state. */
    1698             : void
    1699             : fd_gui_handle_root_advanced( fd_gui_t * gui,
    1700             :                              ulong      slot,
    1701             :                              ulong      bank_seq,
    1702             :                              long       now );
    1703             : 
    1704             : void
    1705             : fd_gui_handle_ag_notarized( fd_gui_t *        gui,
    1706             :                             ulong             slot,
    1707             :                             fd_hash_t const * block_id,
    1708             :                             uchar             notarization_kind );
    1709             : 
    1710             : void
    1711             : fd_gui_handle_ag_finalized( fd_gui_t *        gui,
    1712             :                             ulong             slot,
    1713             :                             fd_hash_t const * block_id,
    1714             :                             uchar             finalization_kind );
    1715             : 
    1716             : void
    1717             : fd_gui_handle_ag_skip_cert( fd_gui_t * gui,
    1718             :                             ulong      slot );
    1719             : 
    1720             : void
    1721             : fd_gui_handle_ag_leader( fd_gui_t * gui,
    1722             :                          ulong      parent_slot );
    1723             : 
    1724             : void
    1725             : fd_gui_ag_register_block( fd_gui_t *        gui,
    1726             :                           ulong             slot,
    1727             :                           fd_hash_t const * block_id,
    1728             :                           ulong             bank_seq );
    1729             : 
    1730             : int
    1731             : fd_gui_ag_slot_is_skip_notarized( fd_gui_t const * gui,
    1732             :                                   ulong            slot );
    1733             : 
    1734             : /* fd_gui_slot_get_canon_safe resolves slot number `_slot` on the
    1735             :    canonical fork and returns a renderable record. */
    1736             : fd_gui_slot_t *
    1737             : fd_gui_slot_get_canon_safe( fd_gui_t * gui, ulong _slot );
    1738             : 
    1739             : 
    1740             : /* fd_gui_epoch returns the DB EPOCH record for `epoch`, or NULL if no
    1741             :    record for that epoch is durable in the store.  The returned pointer
    1742             :    may be written in place. */
    1743             : static inline fd_gui_epoch_t *
    1744           0 : fd_gui_epoch( fd_gui_t * gui, ulong epoch ) {
    1745           0 :   fd_gui_hist_epoch_key_t key[ 1 ]; key->epoch = epoch;
    1746           0 :   return (fd_gui_epoch_t *)fd_gui_hist_kv_get( gui, FD_GUI_HIST_EPOCH, key );
    1747           0 : }
    1748             : 
    1749             : /* fd_gui_current_epoch returns the record for the current epoch,
    1750             :    or NULL if no epoch has been ingested yet. */
    1751             : static inline fd_gui_epoch_t *
    1752           0 : fd_gui_current_epoch( fd_gui_t * gui ) {
    1753           0 :   if( FD_UNLIKELY( gui->epoch.current_epoch==ULONG_MAX ) ) return NULL;
    1754           0 :   return fd_gui_epoch( gui, gui->epoch.current_epoch );
    1755           0 : }
    1756             : 
    1757             : /* fd_gui_epoch_get_or_create returns the mutable DB EPOCH record
    1758             :    for `epoch`, creating it if none exists yet. */
    1759             : static inline fd_gui_epoch_t *
    1760             : fd_gui_epoch_get_or_create( fd_gui_t * gui,
    1761             :                             ulong      epoch,
    1762           0 :                             int *      created_out ) {
    1763           0 :   fd_gui_epoch_t * rec = fd_gui_epoch( gui, epoch );
    1764           0 :   if( FD_LIKELY( rec ) ) { if( created_out ) *created_out = 0; return rec; }
    1765             : 
    1766           0 :   fd_gui_hist_epoch_key_t key[ 1 ]; key->epoch = epoch;
    1767           0 :   rec = fd_gui_hist_kv_get_or_create( gui, FD_GUI_HIST_EPOCH, key );
    1768           0 :   if( FD_UNLIKELY( !rec ) ) return NULL;
    1769           0 :   if( created_out ) *created_out = 1;
    1770           0 :   return rec;
    1771           0 : }
    1772             : 
    1773             : /* fd_gui_first_replay_slot returns the lowest slot number the validator
    1774             :    has authoritative knowledge of during this run. */
    1775             : static inline ulong
    1776           0 : fd_gui_first_replay_slot( fd_gui_t const * gui ) {
    1777           0 :   ulong slot_incremental = gui->summary.boot_progress.loading_snapshot[ FD_GUI_BOOT_PROGRESS_INCREMENTAL_SNAPSHOT_IDX ].slot;
    1778           0 :   ulong slot_full        = gui->summary.boot_progress.loading_snapshot[ FD_GUI_BOOT_PROGRESS_FULL_SNAPSHOT_IDX ].slot;
    1779           0 :   return fd_ulong_if( slot_incremental!=ULONG_MAX, slot_incremental+1UL,
    1780           0 :                       fd_ulong_if( slot_full!=ULONG_MAX, slot_full+1UL, ULONG_MAX ) );
    1781           0 : }
    1782             : 
    1783             : static inline fd_gui_leader_slot_t *
    1784             : fd_gui_slot_leader_get( fd_gui_t * gui,
    1785             :                         ulong      slot,
    1786           0 :                         ulong      bank_seq ) {
    1787           0 :   if( FD_UNLIKELY( !gui->db || slot==ULONG_MAX || bank_seq==ULONG_MAX ) ) return NULL;
    1788           0 :   fd_gui_hist_leader_slot_key_t key = { .slot = slot, .bank_seq = bank_seq };
    1789           0 :   return fd_gui_hist_kv_get( gui, FD_GUI_HIST_LEADER_SLOT, &key );
    1790           0 : }
    1791             : 
    1792             : /* fd_gui_slot_leader_get_or_create returns the mutable returns the
    1793             :    DB LEADER_SLOT record for (slot, bank_seq), creating it if none
    1794             :    exists yet. */
    1795             : 
    1796             : static inline fd_gui_leader_slot_t *
    1797             : fd_gui_slot_leader_get_or_create( fd_gui_t * gui,
    1798             :                                   ulong      slot,
    1799           0 :                                   ulong      bank_seq ) {
    1800           0 :   if( FD_UNLIKELY( !gui->db || slot==ULONG_MAX ) ) return NULL;
    1801             : 
    1802           0 :   fd_gui_leader_slot_t * rec = fd_gui_slot_leader_get( gui, slot, bank_seq );
    1803           0 :   if( FD_LIKELY( rec ) ) return rec;
    1804             : 
    1805           0 :   fd_gui_hist_leader_slot_key_t key = { .slot = slot, .bank_seq = bank_seq };
    1806           0 :   fd_gui_leader_slot_t * seed = fd_gui_hist_kv_get_or_create( gui, FD_GUI_HIST_LEADER_SLOT, &key );
    1807           0 :   if( FD_UNLIKELY( !seed ) ) return NULL;
    1808             : 
    1809           0 :   *seed = (fd_gui_leader_slot_t){
    1810           0 :     .slot                    = slot,
    1811           0 :     .bank_seq                = bank_seq,
    1812           0 :     .leader_start_time       = LONG_MAX,
    1813           0 :     .leader_end_time         = LONG_MAX,
    1814           0 :     .txn_insert_time_min_ns  = LONG_MAX,
    1815           0 :     .txn_insert_time_max_ns  = LONG_MIN,
    1816           0 :     .max_microblocks         = ULONG_MAX,
    1817           0 :     .max_compute_units       = ULONG_MAX,
    1818           0 :     .microblocks_upper_bound = UINT_MAX,
    1819           0 :     .begin_microblocks       = 0U,
    1820           0 :     .end_microblocks         = 0U,
    1821           0 :     .has_waterfall           = 0,
    1822           0 :     .unbecame_leader         = 0
    1823           0 :   };
    1824           0 :   return seed;
    1825           0 : }
    1826             : 
    1827             : /* fd_gui_slot_leader_get_any returns the DB record for key matching
    1828             :    (lslot, *), regardless of fork, or NULL if no record exists for lslot. */
    1829             : 
    1830             : static inline fd_gui_leader_slot_t *
    1831           0 : fd_gui_slot_leader_get_any( fd_gui_t * gui, ulong _lslot ) {
    1832           0 :   if( FD_UNLIKELY( !gui->db || _lslot==ULONG_MAX ) ) return NULL;
    1833           0 :   return fd_gui_hist_kv_get_slot_any( gui, FD_GUI_HIST_LEADER_SLOT, _lslot );
    1834           0 : }
    1835             : 
    1836             : /* fd_gui_slot_get returns the DB record for the exact key
    1837             :    (slot, bank_seq), or NULL if none exists. */
    1838             : 
    1839             : static inline fd_gui_slot_t *
    1840           0 : fd_gui_slot_get( fd_gui_t * gui, ulong slot, ulong bank_seq ) {
    1841           0 :   if( FD_UNLIKELY( slot==ULONG_MAX || bank_seq==ULONG_MAX ) ) return NULL;
    1842           0 :   fd_gui_hist_slot_key_t key;
    1843           0 :   key.slot = slot; key.bank_seq = bank_seq;
    1844           0 :   return (fd_gui_slot_t *)fd_gui_hist_kv_get( gui, FD_GUI_HIST_SLOT, &key );
    1845           0 : }
    1846             : 
    1847             : /* fd_gui_slot_parent_get returns the DB record for the parent of `c` on
    1848             :    c's own fork or NULL if the parent is unknown or has no record. */
    1849             : 
    1850             : static inline fd_gui_slot_t *
    1851           0 : fd_gui_slot_parent_get( fd_gui_t * gui, fd_gui_slot_t const * c ) {
    1852           0 :   if( FD_UNLIKELY( !c ) ) return NULL;
    1853           0 :   return fd_gui_slot_get( gui, c->parent_slot, c->parent_bank_seq );
    1854           0 : }
    1855             : 
    1856             : /* fd_gui_slot_get_canon returns the DB record for slot number `_slot`
    1857             :    on the canonical fork, or NULL if `_slot` is skipped, above the
    1858             :    canonical tip, or the tip is not yet resolved.  The canonical fork is
    1859             :    the fork of the current reset slot. */
    1860             : 
    1861             : static inline fd_gui_slot_t *
    1862           0 : fd_gui_slot_get_canon( fd_gui_t * gui, ulong _slot ) {
    1863           0 :   if( FD_UNLIKELY( _slot==ULONG_MAX ) ) return NULL;
    1864             : 
    1865             :   /* At or below the root the canonical fork has settled, meaning there
    1866             :      should be exactly one NOT_SKIPPED entry per slot. */
    1867           0 :   if( FD_LIKELY( gui->summary.slot_rooted!=ULONG_MAX && _slot<=gui->summary.slot_rooted ) ) {
    1868           0 :     fd_gui_hist_kv_slot_iter_t it[ 1 ];
    1869           0 :     for( fd_gui_hist_kv_iter_begin( gui, it, FD_GUI_HIST_SLOT, _slot ); it->rec; fd_gui_hist_kv_iter_next( it ) ) {
    1870           0 :       fd_gui_slot_t const * rec = (fd_gui_slot_t const *)it->rec;
    1871           0 :       if( FD_LIKELY( rec->skip==FD_GUI_SKIP_STATUS_NOT_SKIPPED ) ) return fd_gui_slot_get( gui, _slot, it->bank_seq );
    1872           0 :     }
    1873           0 :     return NULL; /* skipped on the canonical fork, or no record */
    1874           0 :   }
    1875             : 
    1876             :   /* Otherwise do a fork-walk from the canonical frontier. */
    1877           0 :   ulong slot     = gui->summary.slot_tower;
    1878           0 :   ulong bank_seq = gui->summary.slot_tower_bank_seq;
    1879           0 :   for(;;) {
    1880           0 :     if( FD_UNLIKELY( slot==ULONG_MAX || bank_seq==ULONG_MAX ) ) return NULL;
    1881           0 :     if( FD_UNLIKELY( slot<_slot ) ) return NULL; /* walked past _slot: skipped or above-tip */
    1882           0 :     fd_gui_slot_t * n = fd_gui_slot_get( gui, slot, bank_seq );
    1883           0 :     if( FD_UNLIKELY( !n ) ) return NULL; /* evicted / no record */
    1884           0 :     if( FD_LIKELY( slot==_slot ) ) return n;
    1885           0 :     if( FD_UNLIKELY( n->parent_slot>=slot ) ) return NULL; /* corrupt ancestry */
    1886           0 :     slot     = n->parent_slot;
    1887           0 :     bank_seq = n->parent_bank_seq;
    1888           0 :   }
    1889           0 : }
    1890             : 
    1891             : /* fd_gui_slot_get_any returns the DB record for slot number `_slot`
    1892             :    without any fork awareness: it is a simple store lookup that returns
    1893             :    the first record that exists for `_slot`. */
    1894             : 
    1895             : static inline fd_gui_slot_t *
    1896           0 : fd_gui_slot_get_any( fd_gui_t * gui, ulong _slot ) {
    1897           0 :   if( FD_UNLIKELY( _slot==ULONG_MAX ) ) return NULL;
    1898             : 
    1899           0 :   fd_gui_hist_kv_slot_iter_t it[ 1 ];
    1900           0 :   fd_gui_hist_kv_iter_begin( gui, it, FD_GUI_HIST_SLOT, _slot );
    1901           0 :   if( FD_UNLIKELY( !it->rec ) ) return NULL;
    1902           0 :   return fd_gui_slot_get( gui, _slot, it->bank_seq );
    1903           0 : }
    1904             : 
    1905             : /* fd_gui_slot_is_ancestor returns 1 if (ancestor_slot,
    1906             :    ancestor_bank_seq) is known to be an ancestor of (slot, bank_seq), 0
    1907             :    otherwise. */
    1908             : 
    1909             : static inline int
    1910             : fd_gui_slot_is_ancestor( fd_gui_t * gui,
    1911             :                          ulong      ancestor_slot,
    1912             :                          ulong      ancestor_bank_seq,
    1913             :                          ulong      slot,
    1914           0 :                          ulong      bank_seq ) {
    1915           0 :   for(;;) {
    1916           0 :     if( FD_UNLIKELY( slot==ancestor_slot ) ) return bank_seq==ancestor_bank_seq;
    1917           0 :     if( FD_UNLIKELY( slot<ancestor_slot ) ) return 0;
    1918             : 
    1919           0 :     fd_gui_slot_t const * rec = fd_gui_slot_get( gui, slot, bank_seq );
    1920           0 :     if( FD_UNLIKELY( !rec ) ) return 0;
    1921           0 :     slot     = rec->parent_slot;
    1922           0 :     bank_seq = rec->parent_bank_seq;
    1923           0 :   }
    1924           0 : }
    1925             : 
    1926             : /* fd_gui_slot_is_skipped returns 1 if `slot` is skipped on the fork
    1927             :    whose tip is (des, des_bank_seq) and whose root is `root`, 0
    1928             :    otherwise. */
    1929             : 
    1930             : static inline int
    1931           0 : fd_gui_slot_is_skipped( fd_gui_t * gui, ulong root, ulong des, ulong des_bank_seq, ulong slot ) {
    1932           0 :   fd_gui_slot_t * c = fd_gui_slot_get( gui, des, des_bank_seq );
    1933           0 :   ulong cslot = des;
    1934           0 :   while( c ) {
    1935           0 :     if( FD_UNLIKELY( root==cslot ) ) return 0; /* on the fork, not skipped */
    1936           0 :     ulong pslot = c->parent_slot;
    1937           0 :     fd_gui_slot_t * p = fd_gui_slot_parent_get( gui, c );
    1938           0 :     if( FD_UNLIKELY( p && pslot<slot && cslot>slot ) ) return 1; /* in-between two on-fork records, skipped */
    1939           0 :     c     = p;
    1940           0 :     cslot = pslot;
    1941           0 :   }
    1942           0 :   return 0; /* slot not between root and des, or is unknown */
    1943           0 : }
    1944             : 
    1945             : /* fd_gui_get_epoch_by_slot returns the mutable DB EPOCH record covering
    1946             :    `_slot` (resolved via the epoch schedule), or NULL if no epoch
    1947             :    schedule is known yet or no record for that epoch is present. */
    1948             : 
    1949             : /* fd_gui_epoch_timeline_slot_note widens epoch's retained slot range
    1950             :    and interval hull to include the interval at idx. */
    1951             : 
    1952             : static inline void
    1953             : fd_gui_epoch_timeline_slot_note( fd_gui_epoch_t * epoch,
    1954           0 :                                  ulong            idx ) {
    1955           0 :   epoch->timeline_slot_lo_idx = epoch->timeline_slot_lo_idx==ULONG_MAX ? idx : fd_ulong_min( epoch->timeline_slot_lo_idx, idx );
    1956           0 :   epoch->timeline_slot_hi_idx = epoch->timeline_slot_hi_idx==ULONG_MAX ? idx : fd_ulong_max( epoch->timeline_slot_hi_idx, idx );
    1957           0 :   long rs = epoch->timeline_slot_start_ns[ idx ];
    1958           0 :   long re = epoch->timeline_slot_end_ns[ idx ];
    1959           0 :   if( rs<0L || re<=rs ) return;
    1960           0 :   epoch->timeline_slot_min_start_ns = fd_long_min( epoch->timeline_slot_min_start_ns, rs );
    1961           0 :   epoch->timeline_slot_max_end_ns   = fd_long_max( epoch->timeline_slot_max_end_ns,   re );
    1962           0 : }
    1963             : 
    1964             : static inline fd_gui_epoch_t *
    1965           0 : fd_gui_get_epoch_by_slot( fd_gui_t * gui, ulong _slot ) {
    1966           0 :   if( FD_UNLIKELY( !gui->epoch.has_epoch_schedule ) ) return NULL;
    1967           0 :   ulong epoch = fd_slot_to_epoch( &gui->epoch.epoch_schedule, _slot, NULL );
    1968           0 :   return fd_gui_epoch( gui, epoch );
    1969           0 : }
    1970             : 
    1971             : /* fd_gui_slot_voter_state returns one of FD_GUI_IS_VOTER_* for
    1972             :    `_slot`. */
    1973             : 
    1974             : static inline uchar
    1975           0 : fd_gui_slot_voter_state( fd_gui_t * gui, ulong _slot ) {
    1976           0 :   fd_gui_epoch_t const * epoch = fd_gui_get_epoch_by_slot( gui, _slot );
    1977           0 :   if( FD_UNLIKELY( !epoch || _slot<epoch->start_slot ) ) return FD_GUI_IS_VOTER_UNKNOWN;
    1978           0 :   ulong idx = _slot - epoch->start_slot;
    1979           0 :   return idx<epoch->slot_cnt ? epoch->is_voter[ idx ] : FD_GUI_IS_VOTER_UNKNOWN;
    1980           0 : }
    1981             : 
    1982             : static inline uchar
    1983           0 : fd_gui_slot_vote_rewarded_state( fd_gui_t * gui, ulong _slot ) {
    1984           0 :   fd_gui_epoch_t const * epoch = fd_gui_get_epoch_by_slot( gui, _slot );
    1985           0 :   if( FD_UNLIKELY( !epoch || _slot<epoch->start_slot ) ) return FD_GUI_VOTE_REWARDED_UNKNOWN;
    1986           0 :   ulong idx = _slot - epoch->start_slot;
    1987           0 :   return idx<epoch->slot_cnt ? epoch->vote_rewarded[ idx ] : FD_GUI_VOTE_REWARDED_UNKNOWN;
    1988           0 : }
    1989             : 
    1990             : static inline ushort
    1991           0 : fd_gui_slot_vote_count( fd_gui_t * gui, ulong _slot ) {
    1992           0 :   fd_gui_epoch_t const * epoch = fd_gui_get_epoch_by_slot( gui, _slot );
    1993           0 :   if( FD_UNLIKELY( !epoch || _slot<epoch->start_slot ) ) return USHORT_MAX;
    1994           0 :   ulong idx = _slot-epoch->start_slot;
    1995           0 :   return idx<epoch->slot_cnt ? epoch->vote_count[ idx ] : USHORT_MAX;
    1996           0 : }
    1997             : 
    1998             : /* fd_gui_get_slot_leader returns the leader pubkey scheduled for
    1999             :    `_slot`, or NULL if `epoch` is NULL, `_slot` is outside the epoch,
    2000             :    or the schedule index is indeterminate. */
    2001             : 
    2002             : static inline fd_pubkey_t const *
    2003           0 : fd_gui_get_slot_leader( fd_gui_epoch_t const * epoch, ulong _slot ) {
    2004           0 :   if( FD_UNLIKELY( !epoch ) ) return NULL;
    2005           0 :   if( FD_UNLIKELY( _slot<epoch->start_slot || _slot>=epoch->start_slot+epoch->slot_cnt ) ) return NULL;
    2006           0 :   ulong rot = (_slot - epoch->start_slot)/FD_EPOCH_SLOTS_PER_ROTATION;
    2007           0 :   uint  idx = epoch->sched[ rot ];
    2008           0 :   if( FD_UNLIKELY( idx>=epoch->pub_cnt ) ) return NULL; /* indeterminate */
    2009           0 :   return &epoch->pub[ idx ];
    2010           0 : }
    2011             : 
    2012             : static inline int
    2013           0 : fd_gui_slot_is_mine( fd_gui_t * gui, ulong _slot ) {
    2014           0 :   fd_gui_epoch_t const * epoch = fd_gui_get_epoch_by_slot( gui, _slot );
    2015           0 :   fd_pubkey_t const * slot_leader = fd_gui_get_slot_leader( epoch, _slot );
    2016           0 :   if( FD_UNLIKELY( !slot_leader ) ) return 0;
    2017           0 :   return !memcmp( slot_leader->uc, gui->summary.identity_key->uc, 32UL );
    2018           0 : }
    2019             : 
    2020             : /* fd_gui_slot_get_or_create returns the mutable returns the mutable DB
    2021             :    SLOT record for (_slot, bank_seq), creating it if none exists yet. */
    2022             : 
    2023             : static inline fd_gui_slot_t *
    2024             : fd_gui_slot_get_or_create( fd_gui_t * gui,
    2025             :                            ulong      _slot,
    2026             :                            ulong      _parent_slot,
    2027             :                            ulong      bank_seq,
    2028           0 :                            ulong      parent_bank_seq ) {
    2029           0 :   fd_gui_slot_t * rec = fd_gui_slot_get( gui, _slot, bank_seq );
    2030           0 :   if( FD_LIKELY( rec ) ) return rec;
    2031             : 
    2032           0 :   fd_gui_epoch_t * epoch = fd_gui_get_epoch_by_slot( gui, _slot );
    2033           0 :   fd_pubkey_t const * slot_leader = fd_gui_get_slot_leader( epoch, _slot );
    2034           0 :   int mine = !!slot_leader && !memcmp( slot_leader->uc, gui->summary.identity_key->uc, 32UL );
    2035             : 
    2036           0 :   fd_gui_hist_slot_key_t key;
    2037           0 :   key.slot = _slot; key.bank_seq = bank_seq;
    2038           0 :   fd_gui_slot_t * meta = fd_gui_hist_kv_get_or_create( gui, FD_GUI_HIST_SLOT, &key );
    2039           0 :   if( FD_UNLIKELY( !meta ) ) return NULL;
    2040             : 
    2041           0 :   meta->slot              = _slot;
    2042           0 :   meta->bank_seq          = bank_seq;
    2043           0 :   meta->parent_bank_seq   = parent_bank_seq;
    2044           0 :   meta->parent_slot       = _parent_slot;
    2045           0 :   meta->vote_slot          = ULONG_MAX;
    2046           0 :   meta->max_compute_units = UINT_MAX;
    2047           0 :   meta->completed_time    = LONG_MAX;
    2048           0 :   meta->parent_completed_time = LONG_MAX;
    2049           0 :   meta->mine              = (uchar)(mine & 1);
    2050           0 :   meta->timeline_mine_accounted = 0U;
    2051           0 :   meta->is_voter          = FD_GUI_IS_VOTER_UNKNOWN;
    2052           0 :   meta->skip              = FD_GUI_SKIP_STATUS_UNKNOWN;
    2053           0 :   meta->level             = (uchar)( _slot ? FD_GUI_SLOT_LEVEL_INCOMPLETE : FD_GUI_SLOT_LEVEL_ROOTED ); /* slot 0 always rooted */
    2054           0 :   meta->notarization_kind = FD_GUI_AG_NOTAR_NONE;
    2055           0 :   meta->finalization_kind = FD_GUI_AG_FINAL_NONE;
    2056           0 :   meta->vote_rewarded     = FD_GUI_VOTE_REWARDED_UNKNOWN;
    2057           0 :   meta->vote_count        = USHORT_MAX;
    2058           0 :   meta->vote_failed       = UINT_MAX;
    2059           0 :   meta->vote_success      = UINT_MAX;
    2060           0 :   meta->nonvote_success   = UINT_MAX;
    2061           0 :   meta->nonvote_failed    = UINT_MAX;
    2062           0 :   meta->compute_units     = UINT_MAX;
    2063           0 :   meta->transaction_fee   = ULONG_MAX;
    2064           0 :   meta->priority_fee      = ULONG_MAX;
    2065           0 :   meta->tips              = ULONG_MAX;
    2066           0 :   meta->shred_cnt         = UINT_MAX;
    2067           0 :   memset( meta->block_hash.uc, 0, sizeof(fd_hash_t) );
    2068             : 
    2069           0 :   if( FD_UNLIKELY( mine && epoch ) ) epoch->my_total_slots++;
    2070             : 
    2071           0 :   return meta;
    2072           0 : }
    2073             : 
    2074             : FD_PROTOTYPES_END
    2075             : 
    2076             : #endif /* HEADER_fd_src_disco_gui_fd_gui_h */

Generated by: LCOV version 1.14