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 :
6 : #include "../topo/fd_topo.h"
7 :
8 : #include "../../ballet/txn/fd_txn.h"
9 : #include "../../disco/tiles.h"
10 : #include "../../disco/fd_txn_p.h"
11 : #include "../../disco/bundle/fd_bundle_tile.h"
12 : #include "../../discof/restore/fd_snapct_tile.h"
13 : #include "../../discof/restore/utils/fd_ssmsg.h"
14 : #include "../../discof/tower/fd_tower_tile.h"
15 : #include "../../discof/replay/fd_replay_tile.h"
16 : #include "../../choreo/tower/fd_tower.h"
17 : #include "../../choreo/tower/fd_tower_serdes.h"
18 : #include "../../flamenco/leaders/fd_leaders.h"
19 : #include "../../util/fd_util_base.h"
20 : #include "../../util/hist/fd_histf.h"
21 : #include "../../waltz/http/fd_http_server.h"
22 : #include "../../flamenco/accdb/fd_accdb_cache.h"
23 : #include "../../flamenco/accdb/fd_accdb_shmem.h"
24 :
25 : /* frankendancer only */
26 : #define FD_GUI_MAX_PEER_CNT (108000UL)
27 :
28 : /* frankendancer only */
29 : #define FD_GUI_START_PROGRESS_TYPE_INITIALIZING ( 0)
30 : #define FD_GUI_START_PROGRESS_TYPE_SEARCHING_FOR_FULL_SNAPSHOT ( 1)
31 : #define FD_GUI_START_PROGRESS_TYPE_DOWNLOADING_FULL_SNAPSHOT ( 2)
32 : #define FD_GUI_START_PROGRESS_TYPE_SEARCHING_FOR_INCREMENTAL_SNAPSHOT ( 3)
33 : #define FD_GUI_START_PROGRESS_TYPE_DOWNLOADING_INCREMENTAL_SNAPSHOT ( 4)
34 : #define FD_GUI_START_PROGRESS_TYPE_CLEANING_BLOCK_STORE ( 5)
35 : #define FD_GUI_START_PROGRESS_TYPE_CLEANING_ACCOUNTS ( 6)
36 : #define FD_GUI_START_PROGRESS_TYPE_LOADING_LEDGER ( 7)
37 : #define FD_GUI_START_PROGRESS_TYPE_PROCESSING_LEDGER ( 8)
38 : #define FD_GUI_START_PROGRESS_TYPE_STARTING_SERVICES ( 9)
39 : #define FD_GUI_START_PROGRESS_TYPE_HALTED (10)
40 : #define FD_GUI_START_PROGRESS_TYPE_WAITING_FOR_SUPERMAJORITY (11)
41 : #define FD_GUI_START_PROGRESS_TYPE_RUNNING (12)
42 :
43 0 : #define FD_GUI_NETWORK_EMA_HALF_LIFE_NS (1000000000L) /* 1 second in nanoseconds */
44 0 : #define FD_GUI_NET_PROTO_CNT (6UL) /* turbine, gossip, tpu, repair, rserve, metric */
45 0 : #define FD_GUI_NET_RATE_MAX_WINDOW_NS (300L*1000L*1000L*1000L) /* 5 minutes in nanoseconds */
46 :
47 : /* Monotonic deque element for sliding-window max tracking of
48 : EMA-smoothed network throughput. */
49 : struct fd_gui_rate_entry {
50 : long ts_nanos;
51 : double value;
52 : };
53 : typedef struct fd_gui_rate_entry fd_gui_rate_entry_t;
54 :
55 : /* At 100 ms sampling, 5 minutes = 3000 samples. */
56 : #define DEQUE_NAME fd_gui_rate_deque
57 0 : #define DEQUE_T fd_gui_rate_entry_t
58 0 : #define DEQUE_MAX 4096UL
59 : #include "../../util/tmpl/fd_deque.c"
60 :
61 : /* frankendancer only */
62 : struct fd_gui_gossip_peer {
63 : fd_pubkey_t pubkey[ 1 ];
64 : ulong wallclock;
65 : ushort shred_version;
66 :
67 : int has_version;
68 : struct {
69 : ushort major;
70 : ushort minor;
71 : ushort patch;
72 :
73 : int has_commit;
74 : uint commit;
75 :
76 : uint feature_set;
77 : } version;
78 :
79 : struct {
80 : uint ipv4;
81 : ushort port;
82 : } sockets[ 12 ];
83 : };
84 :
85 : /* frankendancer only */
86 : struct fd_gui_vote_account {
87 : fd_pubkey_t pubkey[ 1 ];
88 : fd_pubkey_t vote_account[ 1 ];
89 :
90 : ulong activated_stake;
91 : ulong last_vote;
92 : ulong root_slot;
93 : ulong epoch_credits;
94 : uchar commission;
95 : int delinquent;
96 : };
97 :
98 : /* frankendancer only */
99 : struct fd_gui_validator_info {
100 : fd_pubkey_t pubkey[ 1 ];
101 :
102 : char name[ 64 ];
103 : char website[ 128 ];
104 : char details[ 256 ];
105 : char icon_uri[ 128 ];
106 : };
107 :
108 : /* frankendancer only */
109 : #define FD_GUI_SLOT_LEADER_UNSTARTED (0UL)
110 : #define FD_GUI_SLOT_LEADER_STARTED (1UL)
111 : #define FD_GUI_SLOT_LEADER_ENDED (2UL)
112 :
113 0 : #define FD_GUI_SLOTS_CNT (864000UL) /* 2x 432000 */
114 0 : #define FD_GUI_LEADER_CNT (4096UL)
115 :
116 0 : #define FD_GUI_TPS_HISTORY_WINDOW_DURATION_SECONDS (10L)
117 0 : #define FD_GUI_TPS_HISTORY_SAMPLE_CNT (150UL)
118 :
119 0 : #define FD_GUI_PROGCACHE_HISTORY_CNT (600UL) /* 60s / 100ms */
120 :
121 0 : #define FD_GUI_TILE_TIMER_SNAP_CNT (512UL)
122 0 : #define FD_GUI_TILE_TIMER_LEADER_DOWNSAMPLE_CNT (50UL) /* 500ms / 10ms */
123 0 : #define FD_GUI_SCHEDULER_COUNT_SNAP_CNT (512UL)
124 : #define FD_GUI_SCHEDULER_COUNT_LEADER_DOWNSAMPLE_CNT (50UL) /* 500ms / 10ms */
125 :
126 0 : #define FD_GUI_VOTE_STATE_NON_VOTING (0)
127 0 : #define FD_GUI_VOTE_STATE_VOTING (1)
128 0 : #define FD_GUI_VOTE_STATE_DELINQUENT (2)
129 :
130 0 : #define FD_GUI_BOOT_PROGRESS_TYPE_JOINING_GOSSIP (1)
131 0 : #define FD_GUI_BOOT_PROGRESS_TYPE_LOADING_FULL_SNAPSHOT (2)
132 0 : #define FD_GUI_BOOT_PROGRESS_TYPE_LOADING_INCREMENTAL_SNAPSHOT (3)
133 0 : #define FD_GUI_BOOT_PROGRESS_TYPE_WAITING_FOR_SUPERMAJORITY (4)
134 0 : #define FD_GUI_BOOT_PROGRESS_TYPE_CATCHING_UP (5)
135 0 : #define FD_GUI_BOOT_PROGRESS_TYPE_RUNNING (6)
136 :
137 0 : #define FD_GUI_BOOT_PROGRESS_FULL_SNAPSHOT_IDX (0UL)
138 0 : #define FD_GUI_BOOT_PROGRESS_INCREMENTAL_SNAPSHOT_IDX (1UL)
139 0 : #define FD_GUI_BOOT_PROGRESS_SNAPSHOT_CNT (2UL)
140 :
141 0 : #define FD_GUI_SLOT_LEVEL_INCOMPLETE (0)
142 0 : #define FD_GUI_SLOT_LEVEL_COMPLETED (1)
143 0 : #define FD_GUI_SLOT_LEVEL_OPTIMISTICALLY_CONFIRMED (2)
144 0 : #define FD_GUI_SLOT_LEVEL_ROOTED (3)
145 0 : #define FD_GUI_SLOT_LEVEL_FINALIZED (4)
146 :
147 : /* Ideally, we would store an entire epoch's worth of transactions. If
148 : we assume any given validator will have at most 5% stake, and average
149 : transactions per slot is around 10_000, then an epoch will have about
150 : 432_000*10_000*0.05 transactions (~2^28).
151 :
152 : Unfortunately, the transaction struct is 100+ bytes. If we sized the
153 : array to 2^28 entries then the memory required would be ~26GB. In
154 : order to keep memory usage to a more reasonable level, we'll
155 : arbitrarily use a fourth of that size. */
156 0 : #define FD_GUI_TXN_HISTORY_SZ (1UL<<26UL)
157 :
158 0 : #define FD_GUI_TXN_FLAGS_STARTED ( 1U)
159 0 : #define FD_GUI_TXN_FLAGS_ENDED ( 2U)
160 0 : #define FD_GUI_TXN_FLAGS_IS_SIMPLE_VOTE ( 4U)
161 0 : #define FD_GUI_TXN_FLAGS_FROM_BUNDLE ( 8U)
162 0 : #define FD_GUI_TXN_FLAGS_LANDED_IN_BLOCK (16U)
163 :
164 0 : #define FD_GUI_TURBINE_RECV_TIMESTAMPS (750UL)
165 :
166 : /* One use case for tracking ingress shred slot is to estimate when we
167 : have caught up to the tip of the blockchain. A naive approach would
168 : be to track the maximum seen slot.
169 :
170 : maximum_seen_slot = fd_ulong_max( maximum_seen_slot, new_slot_from_shred_tile );
171 :
172 : Unfortunately, this doesn't always work because a validator can send
173 : a slot number that is arbitrarily large on a false fork. Also, these
174 : shreds can be for a repair response, which can be arbitrarily small.
175 :
176 : The prospects here seem bleak, but not all hope is lost! We know
177 : that for a sufficiently large historical time window there is a high
178 : probability that at least some of the slots we observe will be valid
179 : recent turbine slots. For a sufficiently small window there is a high
180 : probability that all the observed shred slots are non-malicious (i.e.
181 : not arbitrarily large).
182 :
183 : In practice shred slots are almost always non-malicious. We can keep
184 : a history of the 12 largest slots we've seen in the past 4.8 seconds.
185 : We'll consider the "tip" of the blockchain to be the maximum slot in
186 : our history. This way, if we receive maliciously large slot number,
187 : it will be evicted after 4.8 seconds. If we receive a small slot from
188 : a repair response it will be ignored because we've seen other larger
189 : slots, meaning that our estimate is eventually consistent. For
190 : monitoring purposes this is sufficient.
191 :
192 : The worst case scenario is that this validator receives an incorrect
193 : shred slot slot more than once every 3 leader rotations. Before the
194 : previous incorrect slot is evicted from the history, a new one takes
195 : it's place and we wouldn't never get a correct estimate of the tip of
196 : the chain. We also would indefinitely think that that we haven't
197 : caught up. This would require the chain having perpetually malicious
198 : leaders with adjacent rotations. If this happens, Solana has bigger
199 : problems. */
200 0 : #define FD_GUI_TURBINE_SLOT_HISTORY_SZ ( 12UL )
201 :
202 : /* Like the turbine slot, the latest repair slot can also swing to
203 : arbitrarily large values due to a malicious fork switch. The gui
204 : provides the same guarantees for freshness and accuracy. This
205 : history is somewhat larger to handle the increased repair bandwidth
206 : during catch up. */
207 0 : #define FD_GUI_REPAIR_SLOT_HISTORY_SZ ( 512UL )
208 :
209 : /* FD_GUI_*_CATCH_UP_HISTORY_SZ is the capacity of the record of slots
210 : seen from repair or turbine during the catch up stage at startup.
211 : These buffers are run-length encoded, so they will typically be very
212 : small. The worst-case scenario is unbounded, so bounds here are
213 : determined heuristically. */
214 0 : #define FD_GUI_REPAIR_CATCH_UP_HISTORY_SZ (4096UL)
215 0 : #define FD_GUI_TURBINE_CATCH_UP_HISTORY_SZ (4096UL)
216 :
217 : /* FD_GUI_SHREDS_STAGING_SZ is number of shred events we'll retain in
218 : in a small staging area. The lifecycle of a shred looks something
219 : like the following
220 :
221 : states] turbine -> repairing (optional) -> processing -> waiting_for_siblings -> slot_complete
222 : events] ^-repair_requested ^-shred_received/shred_repaired ^-shred_replayed ^-max(shred_replayed)
223 :
224 : We're interested in recording timestamps for state transitions (which
225 : these docs call "shred events"). Unfortunately, due to forking,
226 : duplicate packets, etc we can't make any guarantees about ordering or
227 : uniqueness for these event timestamps. Instead the GUI just records
228 : timestamps for all events as they occur and put them into an array.
229 : Newly recorded event timestamps are also broadcast live to WebSocket
230 : consumers.
231 :
232 : The amount of shred events for non-finalized blocks can't really be
233 : bounded, so we use generous estimates here to set a memory bound. */
234 0 : #define FD_GUI_MAX_SHREDS_PER_BLOCK (32UL*1024UL)
235 0 : #define FD_GUI_MAX_EVENTS_PER_SHRED ( 32UL)
236 0 : #define FD_GUI_SHREDS_STAGING_SZ (32UL * FD_GUI_MAX_SHREDS_PER_BLOCK * FD_GUI_MAX_EVENTS_PER_SHRED)
237 :
238 : /* FD_GUI_SHREDS_HISTORY_SZ the number of shred events in our historical
239 : shred store. Shred events here belong to finalized slots which means
240 : we won't record any additional shred updates for these slots.
241 :
242 : All shred events for a given slot will be places in a contiguous
243 : chunk in the array, and the bounding indicies are stored in the
244 : fd_gui_slot_t slot history. Within a slot chunk, shred events are
245 : ordered in the ordered they were recorded by the gui tile.
246 :
247 : Ideally, we have enough space to store an epoch's worth of events,
248 : but we are limited by realistic memory consumption. Instead, we pick
249 : bound heuristically. */
250 0 : #define FD_GUI_SHREDS_HISTORY_SZ (432000UL*2000UL*4UL / 12UL)
251 :
252 0 : #define FD_GUI_SLOT_SHRED_REPAIR_REQUEST (0UL)
253 0 : #define FD_GUI_SLOT_SHRED_SHRED_RECEIVED_TURBINE (1UL)
254 0 : #define FD_GUI_SLOT_SHRED_SHRED_RECEIVED_REPAIR (2UL)
255 0 : #define FD_GUI_SLOT_SHRED_SHRED_REPLAY_EXEC_DONE (3UL)
256 0 : #define FD_GUI_SLOT_SHRED_SHRED_SLOT_COMPLETE (4UL)
257 : /* #define FD_GUI_SLOT_SHRED_SHRED_REPLAY_EXEC_START (5UL) // UNUSED */
258 0 : #define FD_GUI_SLOT_SHRED_SHRED_PUBLISHED (6UL)
259 :
260 0 : #define FD_GUI_SLOT_RANKINGS_SZ (100UL)
261 0 : #define FD_GUI_SLOT_RANKING_TYPE_ASC (0)
262 0 : #define FD_GUI_SLOT_RANKING_TYPE_DESC (1)
263 :
264 : struct fd_gui_tile_timers {
265 : ulong timers[ FD_METRICS_ENUM_TILE_REGIME_CNT ];
266 : ulong sched_timers[ FD_METRICS_ENUM_CPU_REGIME_CNT ];
267 :
268 : int in_backp;
269 : ushort last_cpu;
270 : uchar status;
271 : ulong heartbeat;
272 : ulong backp_cnt;
273 : ulong nvcsw;
274 : ulong nivcsw;
275 : ulong minflt;
276 : ulong majflt;
277 : ulong interrupts;
278 : };
279 :
280 : typedef struct fd_gui_tile_timers fd_gui_tile_timers_t;
281 :
282 : struct fd_gui_scheduler_counts {
283 : long sample_time_ns;
284 : ulong regular;
285 : ulong votes;
286 : ulong conflicting;
287 : ulong bundles;
288 : };
289 :
290 : typedef struct fd_gui_scheduler_counts fd_gui_scheduler_counts_t;
291 :
292 : struct fd_gui_network_stats {
293 : /* total bytes accumulated */
294 : struct {
295 : ulong turbine;
296 : ulong gossip;
297 : ulong tpu;
298 : ulong repair;
299 : ulong rserve;
300 : ulong metric;
301 : } in, out;
302 : };
303 :
304 : typedef struct fd_gui_network_stats fd_gui_network_stats_t;
305 :
306 : struct fd_gui_accounts_stats {
307 : /* Raw counters/gauges read from tile metric pages. Stored so we can
308 : compute deltas (and per-second rates) against a previous snapshot. */
309 : long sample_time_nanos;
310 :
311 : /* Disk (gauges from accdb tile). */
312 : ulong accounts_total;
313 : ulong accounts_capacity;
314 : ulong disk_allocated_bytes;
315 : ulong disk_current_bytes;
316 : ulong disk_used_bytes;
317 :
318 : /* Compaction (gauges + counters from accdb tile). */
319 : ulong in_compaction;
320 : ulong compactions_requested;
321 : ulong compactions_completed;
322 : ulong accounts_relocated_bytes;
323 :
324 : /* Cache occupancy (gauges from accdb tile, per class). */
325 : ulong cache_class_used [ FD_ACCDB_CACHE_CLASS_CNT ];
326 : ulong cache_class_max [ FD_ACCDB_CACHE_CLASS_CNT ];
327 : ulong cache_class_reserved [ FD_ACCDB_CACHE_CLASS_CNT ];
328 : /* Preeviction thresholds, expressed as used-slot counts directly
329 : comparable to cache_class_used / cache_class_max. */
330 : ulong cache_class_target_used [ FD_ACCDB_CACHE_CLASS_CNT ];
331 : ulong cache_class_low_water_used [ FD_ACCDB_CACHE_CLASS_CNT ];
332 :
333 : /* Aggregate counters summed across all accdb consumer tiles. */
334 : ulong acquired; /* total acquires */
335 : ulong acquired_writable; /* writable subset of acquires (RW tiles only) */
336 : ulong acquired_per_class [ FD_ACCDB_CACHE_CLASS_CNT ]; /* acquires attributed to a class (RW tiles only) */
337 : ulong acquired_writable_per_class [ FD_ACCDB_CACHE_CLASS_CNT ]; /* writable acquires attributed to a class (RW tiles only) */
338 : ulong not_found_per_class [ FD_ACCDB_CACHE_CLASS_CNT ]; /* misses, per class */
339 : ulong evicted_per_class [ FD_ACCDB_CACHE_CLASS_CNT ];
340 : ulong preevicted_per_class [ FD_ACCDB_CACHE_CLASS_CNT ];
341 : ulong committed_new_per_class [ FD_ACCDB_CACHE_CLASS_CNT ];
342 : ulong committed_overwrite_per_class[ FD_ACCDB_CACHE_CLASS_CNT ];
343 :
344 : /* IO counters. bytes_written/read_ops/write_ops are summed across
345 : all consumer tiles; bytes_written_accdb is the bytes written by
346 : the accdb tile itself (background preevict + compaction), used to
347 : compute the prewrite ratio. */
348 : ulong bytes_read;
349 : ulong bytes_copied;
350 : ulong bytes_written;
351 : ulong bytes_written_accdb;
352 : ulong read_ops;
353 : ulong write_ops;
354 : };
355 :
356 : typedef struct fd_gui_accounts_stats fd_gui_accounts_stats_t;
357 :
358 : struct fd_gui_leader_slot {
359 : ulong slot;
360 : fd_hash_t block_hash;
361 : long leader_start_time; /* UNIX timestamp of when we first became leader in this slot */
362 : long leader_end_time; /* UNIX timestamp of when we stopped being leader in this slot */
363 :
364 : /* Stem tiles can exist in one of 8 distinct activity regimes at any
365 : given moment. One of these regimes, caughtup_postfrag, is the
366 : only regime where a tile is in a spin loop without doing any
367 : useful work. This info is useful from a monitoring perspective
368 : because it lets us estimate CPU utilization on a pinned core.
369 :
370 : Every 10ms, the gui tile samples the amount of time tiles spent
371 : in each regime in the past 10ms. This sample is used to infer
372 : the CPU utilization in the past 10ms. This utilization is
373 : streamed live to WebSocket clients.
374 :
375 : In additional to live utilization, we are interested in recording
376 : utilization during one of this validator's leader slots. The gui
377 : tile is continuously recording samples to storage with capacity
378 : FD_GUI_TILE_TIMER_SNAP_CNT. The sample index is recorded at the
379 : start and end of a leader slot, and the number of samples is
380 : downsampled to be at most FD_GUI_TILE_TIMER_LEADER_DOWNSAMPLE_CNT
381 : samples (e.g. if there was an unusually long leader slot) and
382 : inserted into historical storage with capacity FD_GUI_LEADER_CNT.
383 :
384 : The tile_timers pointer references trailing storage allocated
385 : in fd_gui_footprint, with gui->tile_cnt elements per sample.
386 : Sized as tile_timers[ FD_GUI_TILE_TIMER_LEADER_DOWNSAMPLE_CNT ][ tile_cnt ]. */
387 : fd_gui_tile_timers_t * tile_timers;
388 : ulong tile_timers_sample_cnt;
389 :
390 : fd_gui_scheduler_counts_t scheduler_counts[ FD_GUI_SCHEDULER_COUNT_LEADER_DOWNSAMPLE_CNT ][ 1 ];
391 : ulong scheduler_counts_sample_cnt;
392 :
393 : struct {
394 : uint microblocks_upper_bound; /* An upper bound on the number of microblocks in the slot. If the number of
395 : microblocks observed is equal to this, the slot can be considered over.
396 : Generally, the bound is set to a "final" state by a done packing message,
397 : which sets it to the exact number of microblocks, but sometimes this message
398 : is not sent, if the max upper bound published by poh was already correct. */
399 : uint begin_microblocks; /* The number of microblocks we have seen be started (sent) from pack to banks. */
400 : uint end_microblocks; /* The number of microblocks we have seen be ended (sent) from banks to poh. The
401 : slot is only considered over if the begin and end microblocks seen are both equal
402 : to the microblock upper bound. */
403 :
404 : ulong start_offset; /* The smallest pack transaction index for this slot. The first transaction for this slot will
405 : be written to gui->txs[ start_offset%FD_GUI_TXN_HISTORY_SZ ]. */
406 : ulong end_offset; /* The largest pack transaction index for this slot, plus 1. The last transaction for this
407 : slot will be written to gui->txs[ (end_offset-1)%FD_GUI_TXN_HISTORY_SZ ]. */
408 : } txs;
409 :
410 : fd_done_packing_t scheduler_stats[ 1 ];
411 :
412 : /* The initial, maximum number of microblocks that could be packed
413 : into this slot. */
414 : ulong max_microblocks;
415 :
416 : uchar unbecame_leader: 1;
417 : };
418 :
419 : typedef struct fd_gui_leader_slot fd_gui_leader_slot_t;
420 :
421 : struct fd_gui_turbine_slot {
422 : ulong slot;
423 : long timestamp;
424 : };
425 :
426 : typedef struct fd_gui_turbine_slot fd_gui_turbine_slot_t;
427 :
428 : struct fd_gui_slot_staged_shred_event {
429 : long timestamp;
430 : ulong slot;
431 : ushort shred_idx;
432 : uchar event;
433 : };
434 :
435 : typedef struct fd_gui_slot_staged_shred_event fd_gui_slot_staged_shred_event_t;
436 :
437 : struct __attribute__((packed)) fd_gui_slot_history_shred_event {
438 : long timestamp;
439 : ushort shred_idx;
440 : uchar event;
441 : };
442 :
443 : typedef struct fd_gui_slot_history_shred_event fd_gui_slot_history_shred_event_t;
444 :
445 : struct fd_gui_slot_ranking {
446 : ulong slot;
447 : ulong value;
448 : int type;
449 : };
450 : typedef struct fd_gui_slot_ranking fd_gui_slot_ranking_t;
451 :
452 : struct fd_gui_slot_rankings {
453 : fd_gui_slot_ranking_t largest_tips [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
454 : fd_gui_slot_ranking_t largest_fees [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
455 : fd_gui_slot_ranking_t largest_rewards [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
456 : fd_gui_slot_ranking_t largest_duration [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
457 : fd_gui_slot_ranking_t largest_compute_units [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
458 : fd_gui_slot_ranking_t largest_skipped [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
459 : fd_gui_slot_ranking_t largest_rewards_per_cu [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
460 : fd_gui_slot_ranking_t smallest_tips [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
461 : fd_gui_slot_ranking_t smallest_fees [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
462 : fd_gui_slot_ranking_t smallest_rewards [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
463 : fd_gui_slot_ranking_t smallest_rewards_per_cu[ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
464 : fd_gui_slot_ranking_t smallest_duration [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
465 : fd_gui_slot_ranking_t smallest_compute_units [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
466 : fd_gui_slot_ranking_t smallest_skipped [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
467 : };
468 :
469 : typedef struct fd_gui_slot_rankings fd_gui_slot_rankings_t;
470 :
471 : struct fd_gui_ephemeral_slot {
472 : ulong slot; /* ULONG_MAX indicates invalid/evicted */
473 : long timestamp_arrival_nanos;
474 : };
475 : typedef struct fd_gui_ephemeral_slot fd_gui_ephemeral_slot_t;
476 :
477 : struct __attribute__((packed)) fd_gui_txn {
478 : uchar signature[ FD_TXN_SIGNATURE_SZ ];
479 : ulong transaction_fee;
480 : ulong priority_fee;
481 : ulong tips;
482 : long timestamp_arrival_nanos;
483 :
484 : /* compute_units_requested has both execution and non-execution cus */
485 : uint compute_units_requested : 21; /* <= 1.4M */
486 : uint compute_units_consumed : 21; /* <= 1.4M */
487 : uint bank_idx : 6; /* in [0, 64) */
488 : uint error_code : 6; /* in [0, 64) */
489 :
490 : /* relative to leader start */
491 : float microblock_start_ns_dt;
492 : float microblock_end_ns_dt;
493 :
494 : /* relative to microblock_start */
495 : fd_txn_ns_dt_t txn_ns_dt;
496 :
497 : uchar flags; /* assigned with the FD_GUI_TXN_FLAGS_* macros */
498 : uchar source_tpu; /* FD_TXN_M_TPU_SOURCE_* */
499 : uint source_ipv4;
500 : uint microblock_idx;
501 : };
502 :
503 : typedef struct fd_gui_txn fd_gui_txn_t;
504 :
505 : struct fd_gui_txn_waterfall {
506 : struct {
507 : ulong quic;
508 : ulong udp;
509 : ulong gossip;
510 : ulong block_engine;
511 : ulong pack_cranked;
512 : } in;
513 :
514 : struct {
515 : ulong net_overrun;
516 : ulong quic_overrun;
517 : ulong quic_frag_drop;
518 : ulong quic_abandoned;
519 : ulong tpu_quic_invalid;
520 : ulong tpu_udp_invalid;
521 : ulong verify_overrun;
522 : ulong verify_parse;
523 : ulong verify_failed;
524 : ulong verify_duplicate;
525 : ulong dedup_duplicate;
526 : ulong resolv_lut_failed;
527 : ulong resolv_expired;
528 : ulong resolv_ancient;
529 : ulong resolv_no_ledger;
530 : ulong resolv_retained;
531 : ulong pack_invalid;
532 : ulong pack_invalid_bundle;
533 : ulong pack_expired;
534 : ulong pack_already_executed;
535 : ulong pack_retained;
536 : ulong pack_wait_full;
537 : ulong pack_leader_slow;
538 : ulong bank_invalid;
539 : ulong bank_nonce_already_advanced;
540 : ulong bank_nonce_advance_failed;
541 : ulong bank_nonce_wrong_blockhash;
542 : ulong block_success;
543 : ulong block_fail;
544 : } out;
545 : };
546 :
547 : typedef struct fd_gui_txn_waterfall fd_gui_txn_waterfall_t;
548 :
549 : struct fd_gui_tile_stats {
550 : long sample_time_nanos;
551 :
552 : ulong net_in_rx_bytes; /* Number of bytes received by the net or sock tile*/
553 : ulong quic_conn_cnt; /* Number of active QUIC connections */
554 : fd_histf_t bundle_rx_delay_hist; /* Histogram of bundle rx delay */
555 : ulong bundle_rtt_smoothed_nanos; /* RTT (nanoseconds) moving average */
556 : ulong verify_drop_cnt; /* Number of transactions dropped by verify tiles */
557 : ulong verify_total_cnt; /* Number of transactions received by verify tiles */
558 : ulong dedup_drop_cnt; /* Number of transactions dropped by dedup tile */
559 : ulong dedup_total_cnt; /* Number of transactions received by dedup tile */
560 : ulong pack_buffer_cnt; /* Number of buffered transactions in the pack tile */
561 : ulong pack_buffer_capacity; /* Total size of the pack transaction buffer */
562 : ulong bank_txn_exec_cnt; /* Number of transactions processed by the bank tile */
563 : ulong net_out_tx_bytes; /* Number of bytes sent by the net or sock tile */
564 : };
565 :
566 : typedef struct fd_gui_tile_stats fd_gui_tile_stats_t;
567 :
568 : struct fd_gui_slot {
569 : ulong slot;
570 : ulong parent_slot;
571 : ulong vote_slot;
572 : ulong reset_slot;
573 : long completed_time;
574 : uint max_compute_units;
575 : int mine;
576 : int skipped;
577 : int must_republish;
578 : int level;
579 : uint compute_units;
580 : ulong transaction_fee;
581 : ulong priority_fee;
582 : ulong tips;
583 : uint shred_cnt;
584 : uchar vote_latency;
585 :
586 : uint vote_success;
587 : uint vote_failed;
588 : uint nonvote_success;
589 : uint nonvote_failed;
590 :
591 : /* Some slot info is only tracked for our own leader slots. These
592 : slots are kept in a separate buffer. */
593 : ulong leader_history_idx;
594 :
595 : fd_gui_txn_waterfall_t waterfall_begin[ 1 ];
596 : fd_gui_txn_waterfall_t waterfall_end[ 1 ];
597 :
598 : fd_gui_tile_stats_t tile_stats_begin[ 1 ];
599 : fd_gui_tile_stats_t tile_stats_end[ 1 ];
600 :
601 : struct {
602 : ulong start_offset; /* gui->shreds.history[ start_offset % FD_GUI_SHREDS_HISTORY_SZ ] is the first shred event in
603 : contiguous chunk of events in the shred history corresponding to this slot. */
604 : ulong end_offset; /* One past the last shred event in the contiguous chunk of events in the shred history
605 : corresponding to this slot. */
606 : } shreds;
607 : };
608 :
609 : typedef struct fd_gui_slot fd_gui_slot_t;
610 :
611 : struct fd_gui_boot_progress {
612 : uchar phase;
613 : long joining_gossip_time_nanos;
614 : struct {
615 : ulong slot;
616 : uint peer_addr;
617 : ushort peer_port;
618 : ulong total_bytes_compressed;
619 : long reset_time_nanos; /* UNIX nanosecond timestamp */
620 : long sample_time_nanos;
621 : ulong reset_cnt;
622 :
623 : ulong read_bytes_compressed;
624 : char read_path[ PATH_MAX+30UL ]; /* URL or filesystem path. 30 is fd_cstr_nlen( "https://255.255.255.255:12345/", ULONG_MAX ) */
625 :
626 : ulong decompress_bytes_decompressed;
627 : ulong decompress_bytes_compressed;
628 :
629 : ulong insert_bytes_decompressed;
630 : char insert_path[ PATH_MAX ];
631 : ulong insert_accounts_current;
632 :
633 : ulong snapwr_in_bytes_decompressed;
634 : ulong snapwr_out_bytes_decompressed;
635 : ulong snapwr_accounts_current;
636 : } loading_snapshot[ FD_GUI_BOOT_PROGRESS_SNAPSHOT_CNT ];
637 :
638 : ulong wfs_total_stake;
639 : ulong wfs_connected_stake;
640 : ulong wfs_total_peers;
641 : ulong wfs_connected_peers;
642 : ulong wfs_attempt;
643 :
644 : long catching_up_time_nanos;
645 : ulong catching_up_first_replay_slot;
646 : };
647 :
648 : typedef struct fd_gui_boot_progress fd_gui_boot_progress_t;
649 :
650 : struct fd_gui {
651 : fd_http_server_t * http;
652 : fd_topo_t const * topo;
653 : fd_accdb_shmem_t const * accdb_shmem;
654 :
655 : double tick_per_ns;
656 :
657 : ulong tile_cnt;
658 :
659 : long next_sample_400millis;
660 : long next_sample_100millis;
661 : long next_sample_50millis;
662 : long next_sample_25millis;
663 : long next_sample_10millis;
664 :
665 : ulong leader_slot;
666 :
667 : struct {
668 : fd_pubkey_t identity_key[ 1 ];
669 : int has_vote_key;
670 : fd_pubkey_t vote_key[ 1 ];
671 : char vote_key_base58[ FD_BASE58_ENCODED_32_SZ ];
672 : char identity_key_base58[ FD_BASE58_ENCODED_32_SZ ];
673 :
674 : int is_full_client;
675 : char const * version;
676 : char const * cluster;
677 :
678 : char wfs_bank_hash[ FD_BASE58_ENCODED_32_SZ ];
679 : ushort expected_shred_version;
680 : int wfs_enabled;
681 :
682 : ulong vote_distance;
683 : int vote_state;
684 :
685 : long startup_time_nanos;
686 :
687 : union {
688 : struct { /* frankendancer only */
689 : uchar phase;
690 : int startup_got_full_snapshot;
691 :
692 : ulong startup_incremental_snapshot_slot;
693 : uint startup_incremental_snapshot_peer_ip_addr;
694 : ushort startup_incremental_snapshot_peer_port;
695 : double startup_incremental_snapshot_elapsed_secs;
696 : double startup_incremental_snapshot_remaining_secs;
697 : double startup_incremental_snapshot_throughput;
698 : ulong startup_incremental_snapshot_total_bytes;
699 : ulong startup_incremental_snapshot_current_bytes;
700 :
701 : ulong startup_full_snapshot_slot;
702 : uint startup_full_snapshot_peer_ip_addr;
703 : ushort startup_full_snapshot_peer_port;
704 : double startup_full_snapshot_elapsed_secs;
705 : double startup_full_snapshot_remaining_secs;
706 : double startup_full_snapshot_throughput;
707 : ulong startup_full_snapshot_total_bytes;
708 : ulong startup_full_snapshot_current_bytes;
709 :
710 : ulong startup_ledger_slot;
711 : ulong startup_ledger_max_slot;
712 :
713 : ulong startup_waiting_for_supermajority_slot;
714 : ulong startup_waiting_for_supermajority_stake_pct;
715 : } startup_progress;
716 : fd_gui_boot_progress_t boot_progress;
717 : };
718 :
719 : fd_gui_boot_progress_t prev_boot_progress;
720 :
721 : int schedule_strategy;
722 :
723 : ulong identity_account_balance;
724 : ulong vote_account_balance;
725 : ulong estimated_slot_duration_nanos;
726 :
727 : ulong sock_tile_cnt;
728 : ulong net_tile_cnt;
729 : ulong quic_tile_cnt;
730 : ulong verify_tile_cnt;
731 : ulong resolh_tile_cnt;
732 : ulong resolv_tile_cnt;
733 : ulong bank_tile_cnt;
734 : ulong execle_tile_cnt;
735 : ulong execrp_tile_cnt;
736 : ulong shred_tile_cnt;
737 :
738 : ulong slot_rooted;
739 : ulong slot_optimistically_confirmed;
740 : ulong slot_completed;
741 : ulong slot_estimated;
742 : ulong slot_caught_up;
743 : ulong slot_repair;
744 : ulong slot_turbine;
745 : ulong slot_reset;
746 : ulong slot_storage;
747 : ulong active_fork_cnt;
748 :
749 : fd_gui_ephemeral_slot_t slots_max_turbine[ FD_GUI_TURBINE_SLOT_HISTORY_SZ+1UL ];
750 : fd_gui_ephemeral_slot_t slots_max_repair [ FD_GUI_REPAIR_SLOT_HISTORY_SZ +1UL ];
751 :
752 : /* catchup_* and late_votes are run-length encoded. i.e. adjacent
753 : pairs represent contiguous runs */
754 : ulong catch_up_turbine[ FD_GUI_TURBINE_CATCH_UP_HISTORY_SZ ];
755 : ulong catch_up_turbine_sz;
756 :
757 : ulong catch_up_repair[ FD_GUI_REPAIR_CATCH_UP_HISTORY_SZ ];
758 : ulong catch_up_repair_sz;
759 :
760 : ulong late_votes[ MAX_SLOTS_PER_EPOCH ];
761 : ulong late_votes_sz;
762 :
763 : ulong estimated_tps_history_idx;
764 : struct {
765 : ulong vote_failed;
766 : ulong vote_success;
767 : ulong nonvote_success;
768 : ulong nonvote_failed;
769 : } estimated_tps_history[ FD_GUI_TPS_HISTORY_SAMPLE_CNT ];
770 :
771 : fd_gui_network_stats_t network_stats_current[ 1 ];
772 : fd_gui_network_stats_t network_stats_prev[ 1 ];
773 : int network_stats_has_prev;
774 :
775 : /* EMA-smoothed network throughput (bytes/sec) with a 1-second
776 : half-life. */
777 : double ingress_ema[ FD_GUI_NET_PROTO_CNT ];
778 : double egress_ema[ FD_GUI_NET_PROTO_CNT ];
779 : long net_rate_prev_ts;
780 : int net_rate_ema_ready;
781 : fd_gui_rate_entry_t * ingress_maxq;
782 : fd_gui_rate_entry_t * egress_maxq;
783 :
784 : fd_gui_accounts_stats_t accounts_stats_reference[ 1 ];
785 : fd_gui_accounts_stats_t accounts_stats_current [ 1 ];
786 : int accounts_stats_have_reference;
787 : /* Triangular-weighted moving window over per-snap deltas. Snap
788 : cadence is ~100ms, window is FD_GUI_ACCDB_WIN_SAMPLES samples
789 : (~5s). The output rate for any metric is:
790 :
791 : rate = sum( w[i] * delta[i] ) / sum( w[i] * dt[i] )
792 :
793 : where w[i] is the triangular weight (newest sample heaviest,
794 : oldest weight=1). This is fully caught up to a new rate after
795 : the window length but smoother than a boxcar (no cliff edge as
796 : samples age out). */
797 0 : # define FD_GUI_ACCDB_WIN_SAMPLES 50UL
798 : ulong accdb_win_idx; /* next write index */
799 : ulong accdb_win_count; /* samples filled, capped at FD_GUI_ACCDB_WIN_SAMPLES */
800 : long accdb_win_dt_nanos [ FD_GUI_ACCDB_WIN_SAMPLES ];
801 :
802 : /* Aggregate delta rings (units per snap). */
803 : ulong agg_acquired_win [ FD_GUI_ACCDB_WIN_SAMPLES ];
804 : ulong agg_acquired_writable_win [ FD_GUI_ACCDB_WIN_SAMPLES ];
805 : ulong agg_bytes_read_win [ FD_GUI_ACCDB_WIN_SAMPLES ];
806 : ulong agg_bytes_copied_win [ FD_GUI_ACCDB_WIN_SAMPLES ];
807 : ulong agg_bytes_written_win [ FD_GUI_ACCDB_WIN_SAMPLES ];
808 : ulong agg_bytes_written_accdb_win[FD_GUI_ACCDB_WIN_SAMPLES ];
809 : ulong agg_read_ops_win [ FD_GUI_ACCDB_WIN_SAMPLES ];
810 : ulong agg_write_ops_win [ FD_GUI_ACCDB_WIN_SAMPLES ];
811 : ulong agg_relocated_bytes_win [ FD_GUI_ACCDB_WIN_SAMPLES ];
812 : ulong agg_misses_win [ FD_GUI_ACCDB_WIN_SAMPLES ];
813 :
814 : /* Per-class delta rings (units per snap). */
815 : ulong class_acq_win [ FD_ACCDB_CACHE_CLASS_CNT ][ FD_GUI_ACCDB_WIN_SAMPLES ];
816 : ulong class_acq_wr_win [ FD_ACCDB_CACHE_CLASS_CNT ][ FD_GUI_ACCDB_WIN_SAMPLES ];
817 : ulong class_not_found_win [ FD_ACCDB_CACHE_CLASS_CNT ][ FD_GUI_ACCDB_WIN_SAMPLES ];
818 : ulong class_evicted_win [ FD_ACCDB_CACHE_CLASS_CNT ][ FD_GUI_ACCDB_WIN_SAMPLES ];
819 : ulong class_preevicted_win [ FD_ACCDB_CACHE_CLASS_CNT ][ FD_GUI_ACCDB_WIN_SAMPLES ];
820 : ulong class_commit_new_win [ FD_ACCDB_CACHE_CLASS_CNT ][ FD_GUI_ACCDB_WIN_SAMPLES ];
821 : ulong class_commit_over_win [ FD_ACCDB_CACHE_CLASS_CNT ][ FD_GUI_ACCDB_WIN_SAMPLES ];
822 :
823 : /* Per-partition triangular-weighted windows for read/write rates.
824 : Sized to match the accdb partition pool ceiling (8192). Rates
825 : are derived in fd_gui_printf via fd_gui_accdb_weighted_rate. */
826 0 : # define FD_GUI_MAX_PARTITIONS 8192UL
827 : ulong partition_cnt; /* live count from accdb_shmem; <= FD_GUI_MAX_PARTITIONS */
828 : ulong partition_read_ops_win [ FD_GUI_MAX_PARTITIONS ][ FD_GUI_ACCDB_WIN_SAMPLES ];
829 : ulong partition_bytes_read_win [ FD_GUI_MAX_PARTITIONS ][ FD_GUI_ACCDB_WIN_SAMPLES ];
830 : ulong partition_write_ops_win [ FD_GUI_MAX_PARTITIONS ][ FD_GUI_ACCDB_WIN_SAMPLES ];
831 : ulong partition_bytes_written_win[FD_GUI_MAX_PARTITIONS ][ FD_GUI_ACCDB_WIN_SAMPLES ];
832 :
833 : /* Per-partition snapshots (most recent values, for non-rate fields:
834 : offset, layer, write_offset, bytes_freed, ticks, compaction state). */
835 : fd_accdb_shmem_partition_info_t partitions[ FD_GUI_MAX_PARTITIONS ];
836 :
837 : /* Cumulative counters from the previous snap for delta computation. */
838 : ulong partition_prev_read_ops [ FD_GUI_MAX_PARTITIONS ];
839 : ulong partition_prev_bytes_read [ FD_GUI_MAX_PARTITIONS ];
840 : ulong partition_prev_write_ops [ FD_GUI_MAX_PARTITIONS ];
841 : ulong partition_prev_bytes_written[FD_GUI_MAX_PARTITIONS ];
842 :
843 : /* Per-tile accdb stats. At init we walk the topology and assign a
844 : slot to each tile that uses the account database (execle, execrp,
845 : replay, tower, rpc, resolv, snapwr). Each slot keeps cumulative
846 : previous values for delta computation and a triangular-weighted
847 : delta ring (same cadence / weighting as the aggregate rings). */
848 : # define FD_GUI_MAX_ACCDB_TILES 64UL
849 : /* Tile kinds. Determines which subset of metrics to read. */
850 0 : # define FD_GUI_ACCDB_TILE_KIND_RW 0 /* execle, execrp, replay, tower */
851 0 : # define FD_GUI_ACCDB_TILE_KIND_RO 1 /* rpc, resolv */
852 0 : # define FD_GUI_ACCDB_TILE_KIND_SNAPWR 2 /* snapwr (direct disk writer during snapshot load) */
853 0 : # define FD_GUI_ACCDB_TILE_KIND_ACCDB 3 /* accdb tile itself (prewrite + compaction writes) */
854 : ulong accdb_tile_cnt;
855 : ushort accdb_tile_topo_idx [ FD_GUI_MAX_ACCDB_TILES ]; /* index into topo->tiles */
856 : uchar accdb_tile_kind [ FD_GUI_MAX_ACCDB_TILES ];
857 :
858 : /* Most-recent cumulative values per tile, plus the snapshot from
859 : the previous snap for delta computation. */
860 : ulong tile_cur_acquired [ FD_GUI_MAX_ACCDB_TILES ];
861 : ulong tile_cur_acquired_writable [ FD_GUI_MAX_ACCDB_TILES ];
862 : ulong tile_cur_bytes_read [ FD_GUI_MAX_ACCDB_TILES ];
863 : ulong tile_cur_bytes_copied [ FD_GUI_MAX_ACCDB_TILES ];
864 : ulong tile_cur_bytes_written [ FD_GUI_MAX_ACCDB_TILES ];
865 : ulong tile_cur_read_ops [ FD_GUI_MAX_ACCDB_TILES ];
866 : ulong tile_cur_write_ops [ FD_GUI_MAX_ACCDB_TILES ];
867 : ulong tile_cur_misses [ FD_GUI_MAX_ACCDB_TILES ];
868 : ulong tile_cur_evicted [ FD_GUI_MAX_ACCDB_TILES ];
869 : ulong tile_cur_committed [ FD_GUI_MAX_ACCDB_TILES ];
870 : ulong tile_cur_acquire_calls [ FD_GUI_MAX_ACCDB_TILES ];
871 : uchar tile_cur_status [ FD_GUI_MAX_ACCDB_TILES ]; /* 1=running, 2=shutdown */
872 :
873 : ulong tile_prev_acquired [ FD_GUI_MAX_ACCDB_TILES ];
874 : ulong tile_prev_acquired_writable[ FD_GUI_MAX_ACCDB_TILES ];
875 : ulong tile_prev_bytes_read [ FD_GUI_MAX_ACCDB_TILES ];
876 : ulong tile_prev_bytes_copied [ FD_GUI_MAX_ACCDB_TILES ];
877 : ulong tile_prev_bytes_written [ FD_GUI_MAX_ACCDB_TILES ];
878 : ulong tile_prev_read_ops [ FD_GUI_MAX_ACCDB_TILES ];
879 : ulong tile_prev_write_ops [ FD_GUI_MAX_ACCDB_TILES ];
880 : ulong tile_prev_misses [ FD_GUI_MAX_ACCDB_TILES ];
881 : ulong tile_prev_evicted [ FD_GUI_MAX_ACCDB_TILES ];
882 : ulong tile_prev_committed [ FD_GUI_MAX_ACCDB_TILES ];
883 : ulong tile_prev_acquire_calls [ FD_GUI_MAX_ACCDB_TILES ];
884 :
885 : /* Per-tile delta rings. */
886 : ulong tile_acquired_win [ FD_GUI_MAX_ACCDB_TILES ][ FD_GUI_ACCDB_WIN_SAMPLES ];
887 : ulong tile_acquired_writable_win[ FD_GUI_MAX_ACCDB_TILES ][ FD_GUI_ACCDB_WIN_SAMPLES ];
888 : ulong tile_bytes_read_win [ FD_GUI_MAX_ACCDB_TILES ][ FD_GUI_ACCDB_WIN_SAMPLES ];
889 : ulong tile_bytes_copied_win [ FD_GUI_MAX_ACCDB_TILES ][ FD_GUI_ACCDB_WIN_SAMPLES ];
890 : ulong tile_bytes_written_win [ FD_GUI_MAX_ACCDB_TILES ][ FD_GUI_ACCDB_WIN_SAMPLES ];
891 : ulong tile_read_ops_win [ FD_GUI_MAX_ACCDB_TILES ][ FD_GUI_ACCDB_WIN_SAMPLES ];
892 : ulong tile_write_ops_win [ FD_GUI_MAX_ACCDB_TILES ][ FD_GUI_ACCDB_WIN_SAMPLES ];
893 : ulong tile_misses_win [ FD_GUI_MAX_ACCDB_TILES ][ FD_GUI_ACCDB_WIN_SAMPLES ];
894 : ulong tile_evicted_win [ FD_GUI_MAX_ACCDB_TILES ][ FD_GUI_ACCDB_WIN_SAMPLES ];
895 : ulong tile_committed_win [ FD_GUI_MAX_ACCDB_TILES ][ FD_GUI_ACCDB_WIN_SAMPLES ];
896 : ulong tile_acquire_calls_win [ FD_GUI_MAX_ACCDB_TILES ][ FD_GUI_ACCDB_WIN_SAMPLES ];
897 :
898 : /* 60s-history rings for the per-tile sparkline. Each bucket is the
899 : sum of per-snap deltas that fell into that bucket window.
900 : index 0 = current bucket (in-flight), older buckets follow. When
901 : a bucket interval elapses we shift right (older buckets drop off
902 : the end) and start a new index-0 bucket. 240 buckets x 250ms =
903 : 60 second window. */
904 0 : # define FD_GUI_ACCDB_SPARKLINE_SAMPLES 240UL
905 0 : # define FD_GUI_ACCDB_SPARKLINE_BUCKET_NS 250000000L
906 : long tile_sparkline_bucket_start_nanos [ FD_GUI_MAX_ACCDB_TILES ];
907 : ulong tile_sparkline_acq_bucket [ FD_GUI_MAX_ACCDB_TILES ];
908 : ulong tile_sparkline_acq_wr_bucket [ FD_GUI_MAX_ACCDB_TILES ];
909 : /* Per-second rates (units/second) for the last N completed buckets.
910 : Newest at index 0, oldest at the end. Filled lazily as snaps
911 : complete each bucket interval. */
912 : double tile_sparkline_acq_history [ FD_GUI_MAX_ACCDB_TILES ][ FD_GUI_ACCDB_SPARKLINE_SAMPLES ];
913 : double tile_sparkline_acq_wr_history [ FD_GUI_MAX_ACCDB_TILES ][ FD_GUI_ACCDB_SPARKLINE_SAMPLES ];
914 : ulong tile_sparkline_count [ FD_GUI_MAX_ACCDB_TILES ]; /* completed buckets, capped at FD_GUI_ACCDB_SPARKLINE_SAMPLES */
915 :
916 : fd_gui_txn_waterfall_t txn_waterfall_reference[ 1 ];
917 : fd_gui_txn_waterfall_t txn_waterfall_current[ 1 ];
918 :
919 : fd_gui_tile_stats_t tile_stats_reference[ 1 ];
920 : fd_gui_tile_stats_t tile_stats_current[ 1 ];
921 :
922 : ulong progcache_history_idx;
923 : ulong progcache_hits_history [ FD_GUI_PROGCACHE_HISTORY_CNT ];
924 : ulong progcache_lookups_history[ FD_GUI_PROGCACHE_HISTORY_CNT ];
925 : ulong progcache_hits_1min;
926 : ulong progcache_lookups_1min;
927 :
928 : ulong tile_timers_snap_idx;
929 : ulong tile_timers_snap_idx_slot_start;
930 : /* Temporary storage for samples. Will be downsampled into
931 : leader history on slot end. Sized as
932 : tile_timers_snap[ FD_GUI_TILE_TIMER_SNAP_CNT ][ tile_cnt ] */
933 : fd_gui_tile_timers_t * tile_timers_snap;
934 :
935 : ulong scheduler_counts_snap_idx;
936 : ulong scheduler_counts_snap_idx_slot_start;
937 : /* Temporary storage for samples. Will be downsampled into leader history on slot end. */
938 : fd_gui_scheduler_counts_t scheduler_counts_snap[ FD_GUI_SCHEDULER_COUNT_SNAP_CNT ][ 1 ];
939 :
940 : /* Topo tile indices in display order, built once on init. */
941 : ulong tile[ FD_TOPO_MAX_TILES ];
942 : ulong tile_cnt;
943 : } summary;
944 :
945 : fd_gui_slot_t slots[ FD_GUI_SLOTS_CNT ][ 1 ];
946 :
947 : /* used for estimating slot duration */
948 : fd_gui_turbine_slot_t turbine_slots[ FD_GUI_TURBINE_RECV_TIMESTAMPS ];
949 :
950 : fd_gui_leader_slot_t leader_slots[ FD_GUI_LEADER_CNT ][ 1 ];
951 : ulong leader_slots_cnt;
952 :
953 : fd_gui_txn_t txs[ FD_GUI_TXN_HISTORY_SZ ][ 1 ];
954 : ulong pack_txn_idx; /* The pack index of the most recently received transaction */
955 :
956 : ulong tower_cnt;
957 : fd_vote_acc_vote_t tower[ FD_TOWER_VOTE_MAX ];
958 :
959 : struct {
960 : int has_block_engine;
961 : char name[ 16 ];
962 : char url[ 256 ];
963 : char ip_cstr[ 40 ]; /* IPv4 or IPv6 cstr */
964 : int status;
965 : } block_engine;
966 :
967 : struct {
968 : int has_epoch[ 2 ];
969 :
970 : struct {
971 : ulong epoch;
972 : long start_time;
973 : long end_time;
974 :
975 : ulong my_total_slots;
976 : ulong my_skipped_slots;
977 :
978 : ulong start_slot;
979 : ulong end_slot;
980 : fd_epoch_leaders_t * lsched;
981 : uchar __attribute__((aligned(FD_EPOCH_LEADERS_ALIGN))) _lsched[ FD_EPOCH_LEADERS_FOOTPRINT(MAX_COMPRESSED_STAKE_WEIGHTS, MAX_SLOTS_PER_EPOCH) ];
982 : fd_vote_stake_weight_t stakes[ MAX_COMPRESSED_STAKE_WEIGHTS ];
983 :
984 : ulong rankings_slot; /* One more than the largest slot we've processed into our rankings */
985 : fd_gui_slot_rankings_t rankings[ 1 ]; /* global slot rankings */
986 : fd_gui_slot_rankings_t my_rankings[ 1 ]; /* my slots only */
987 : } epochs[ 2 ];
988 : } epoch;
989 :
990 : struct { /* frankendancer only */
991 : ulong peer_cnt;
992 : struct fd_gui_gossip_peer peers[ FD_GUI_MAX_PEER_CNT ];
993 : } gossip;
994 :
995 : struct { /* frankendancer only */
996 : ulong vote_account_cnt;
997 : struct fd_gui_vote_account vote_accounts[ FD_GUI_MAX_PEER_CNT ];
998 : } vote_account;
999 :
1000 : struct { /* frankendancer only */
1001 : ulong info_cnt;
1002 : struct fd_gui_validator_info info[ FD_GUI_MAX_PEER_CNT ];
1003 : } validator_info;
1004 :
1005 : fd_gui_peers_ctx_t * peers; /* full-client */
1006 :
1007 : struct {
1008 : ulong leader_shred_cnt; /* A gauge counting the number of leader shreds seen on the SHRED_OUT link. Resets at
1009 : the end of a leader slot. This works because leader fecs are published in order. */
1010 : ulong staged_next_broadcast; /* staged[ staged_next_broadcast % FD_GUI_SHREDS_STAGING_SZ ] is the first shred event
1011 : that hasn't yet been broadcast to WebSocket clients */
1012 : ulong staged_head; /* staged_head % FD_GUI_SHREDS_STAGING_SZ is the first valid event in staged */
1013 : ulong staged_tail; /* staged_tail % FD_GUI_SHREDS_STAGING_SZ is one past the last valid event in staged */
1014 : fd_gui_slot_staged_shred_event_t staged [ FD_GUI_SHREDS_STAGING_SZ ];
1015 :
1016 : ulong history_slot; /* the largest slot store in history */
1017 : ulong history_tail; /* history_tail % FD_GUI_SHREDS_HISTORY_SZ is one past the last valid event in history */
1018 : fd_gui_slot_history_shred_event_t history[ FD_GUI_SHREDS_HISTORY_SZ ];
1019 :
1020 : /* scratch space for archiving staged events */
1021 : fd_gui_slot_staged_shred_event_t _staged_scratch [ FD_GUI_SHREDS_STAGING_SZ ];
1022 : } shreds; /* full client */
1023 : };
1024 :
1025 : typedef struct fd_gui fd_gui_t;
1026 :
1027 : /* fd_gui_staged_push returns a pointer to the next free staging slot
1028 : and advances staged_tail. If the ring is full staged_head is
1029 : advanced first so the oldest entry is silently dropped. */
1030 : static inline fd_gui_slot_staged_shred_event_t *
1031 0 : fd_gui_staged_push( fd_gui_t * gui ) {
1032 0 : if( FD_UNLIKELY( gui->shreds.staged_tail - gui->shreds.staged_head >= FD_GUI_SHREDS_STAGING_SZ ) ) {
1033 0 : gui->shreds.staged_head = gui->shreds.staged_tail - FD_GUI_SHREDS_STAGING_SZ + 1UL;
1034 0 : if( FD_UNLIKELY( gui->shreds.staged_next_broadcast < gui->shreds.staged_head ) ) {
1035 0 : gui->shreds.staged_next_broadcast = gui->shreds.staged_head;
1036 0 : }
1037 0 : }
1038 0 : fd_gui_slot_staged_shred_event_t * dst =
1039 0 : &gui->shreds.staged[ gui->shreds.staged_tail % FD_GUI_SHREDS_STAGING_SZ ];
1040 0 : gui->shreds.staged_tail++;
1041 0 : return dst;
1042 0 : }
1043 :
1044 : FD_PROTOTYPES_BEGIN
1045 :
1046 : FD_FN_CONST ulong
1047 : fd_gui_align( void );
1048 :
1049 : ulong
1050 : fd_gui_footprint( ulong tile_cnt );
1051 :
1052 : void *
1053 : fd_gui_new( void * shmem,
1054 : fd_http_server_t * http,
1055 : char const * version,
1056 : char const * cluster,
1057 : uchar const * identity_key,
1058 : int has_vote_key,
1059 : uchar const * vote_key,
1060 : int is_full_client,
1061 : int snapshots_enabled,
1062 : int is_voting,
1063 : int schedule_strategy,
1064 : char const * wfs_expected_bank_hash_cstr,
1065 : ushort expected_shred_version,
1066 : fd_topo_t const * topo,
1067 : fd_accdb_shmem_t const * accdb_shmem,
1068 : long now );
1069 :
1070 : fd_gui_t *
1071 : fd_gui_join( void * shmem );
1072 :
1073 : void
1074 : fd_gui_set_identity( fd_gui_t * gui,
1075 : uchar const * identity_pubkey );
1076 :
1077 : void
1078 : fd_gui_ws_open( fd_gui_t * gui,
1079 : ulong conn_id,
1080 : long now );
1081 :
1082 : int
1083 : fd_gui_ws_message( fd_gui_t * gui,
1084 : ulong ws_conn_id,
1085 : uchar const * data,
1086 : ulong data_len );
1087 :
1088 : void
1089 : fd_gui_became_leader( fd_gui_t * gui,
1090 : ulong slot,
1091 : long start_time_nanos,
1092 : long end_time_nanos,
1093 : ulong max_compute_units,
1094 : ulong max_microblocks );
1095 :
1096 : void
1097 : fd_gui_unbecame_leader( fd_gui_t * gui,
1098 : ulong _slot,
1099 : fd_done_packing_t const * done_packing,
1100 : long now );
1101 :
1102 : void
1103 : fd_gui_microblock_execution_begin( fd_gui_t * gui,
1104 : long tspub_ns,
1105 : ulong _slot,
1106 : fd_txn_e_t * txns,
1107 : ulong txn_cnt,
1108 : uint microblock_idx,
1109 : ulong pack_txn_idx );
1110 :
1111 : void
1112 : fd_gui_microblock_execution_end( fd_gui_t * gui,
1113 : long tspub_ns,
1114 : ulong bank_idx,
1115 : ulong _slot,
1116 : ulong txn_cnt,
1117 : fd_txn_p_t * txns,
1118 : ulong pack_txn_idx,
1119 : fd_txn_ns_dt_t txn_ns_dt,
1120 : ulong tips );
1121 :
1122 : int
1123 : fd_gui_poll( fd_gui_t * gui, long now );
1124 :
1125 : void
1126 : fd_gui_handle_block_engine_update( fd_gui_t * gui,
1127 : fd_bundle_block_engine_update_t const * update );
1128 :
1129 : void
1130 : fd_gui_handle_shred( fd_gui_t * gui,
1131 : ulong slot,
1132 : ulong shred_idx,
1133 : int is_turbine,
1134 : long tsorig );
1135 :
1136 : void
1137 : fd_gui_handle_leader_fec( fd_gui_t * gui,
1138 : ulong slot,
1139 : ulong fec_shred_cnt,
1140 : int is_end_of_slot,
1141 : long tsorig );
1142 :
1143 : void
1144 : fd_gui_handle_exec_txn_done( fd_gui_t * gui,
1145 : ulong slot,
1146 : ulong start_shred_idx,
1147 : ulong end_shred_idx,
1148 : long tsorig_ns,
1149 : long tspub_ns );
1150 :
1151 : void
1152 : fd_gui_handle_repair_slot( fd_gui_t * gui, ulong slot, long now );
1153 :
1154 : void
1155 : fd_gui_handle_repair_request( fd_gui_t * gui, ulong slot, ulong shred_idx, long now );
1156 :
1157 : void
1158 : fd_gui_handle_snapshot_update( fd_gui_t * gui,
1159 : fd_snapct_update_t const * msg );
1160 :
1161 : void
1162 : fd_gui_stage_snapshot_manifest( fd_gui_t * gui,
1163 : fd_snapshot_manifest_t const * manifest );
1164 :
1165 : void
1166 : fd_gui_handle_leader_schedule( fd_gui_t * gui,
1167 : fd_stake_weight_msg_t const * leader_schedule,
1168 : long now );
1169 :
1170 : void
1171 : fd_gui_handle_epoch_info( fd_gui_t * gui,
1172 : fd_epoch_info_msg_t const * epoch_info,
1173 : long now );
1174 :
1175 : void
1176 : fd_gui_handle_votes_update( fd_gui_t * gui,
1177 : fd_tower_slot_confirmed_t const * votes );
1178 :
1179 : void
1180 : fd_gui_handle_tower_update( fd_gui_t * gui,
1181 : fd_tower_slot_done_t const * msg,
1182 : long now );
1183 :
1184 : void
1185 : fd_gui_handle_replay_update( fd_gui_t * gui,
1186 : fd_replay_slot_completed_t const * slot_completed,
1187 : ulong vote_slot,
1188 : long now );
1189 :
1190 : void
1191 : fd_gui_handle_genesis_hash( fd_gui_t * gui,
1192 : fd_hash_t const * msg );
1193 :
1194 : static inline ulong
1195 0 : fd_gui_current_epoch_idx( fd_gui_t * gui ) {
1196 0 : ulong epoch_idx = ULONG_MAX;
1197 0 : ulong epoch = ULONG_MAX;
1198 0 : for( ulong i = 0UL; i<2UL; i++ ) {
1199 0 : if( FD_LIKELY( gui->epoch.has_epoch[ i ] ) ) {
1200 : /* the "current" epoch is the smaller one */
1201 0 : if( FD_LIKELY( gui->epoch.epochs[ i ].epoch<epoch ) ) {
1202 0 : epoch = gui->epoch.epochs[ i ].epoch;
1203 0 : epoch_idx = i;
1204 0 : }
1205 0 : }
1206 0 : }
1207 0 : return epoch_idx;
1208 0 : }
1209 :
1210 : static inline fd_gui_slot_t *
1211 0 : fd_gui_get_slot( fd_gui_t const * gui, ulong _slot ) {
1212 0 : fd_gui_slot_t const * slot = gui->slots[ _slot % FD_GUI_SLOTS_CNT ];
1213 0 : if( FD_UNLIKELY( slot->slot==ULONG_MAX || _slot==ULONG_MAX || slot->slot!=_slot ) ) return NULL;
1214 0 : return (fd_gui_slot_t *)slot;
1215 0 : }
1216 :
1217 : static inline fd_gui_slot_t const *
1218 0 : fd_gui_get_slot_const( fd_gui_t const * gui, ulong _slot ) {
1219 0 : return fd_gui_get_slot( gui, _slot );
1220 0 : }
1221 :
1222 : static inline fd_gui_leader_slot_t *
1223 0 : fd_gui_get_leader_slot( fd_gui_t const * gui, ulong _slot ) {
1224 0 : fd_gui_slot_t const * slot = fd_gui_get_slot( gui, _slot );
1225 0 : if( FD_UNLIKELY( !slot
1226 0 : || !slot->mine
1227 0 : || slot->leader_history_idx==ULONG_MAX
1228 0 : || slot->leader_history_idx + FD_GUI_LEADER_CNT < gui->leader_slots_cnt
1229 0 : || gui->leader_slots[ slot->leader_history_idx % FD_GUI_LEADER_CNT ]->slot!=_slot ) ) return NULL;
1230 0 : return (fd_gui_leader_slot_t *)gui->leader_slots[ slot->leader_history_idx % FD_GUI_LEADER_CNT ];
1231 0 : }
1232 :
1233 : static inline fd_gui_leader_slot_t const *
1234 0 : fd_gui_get_leader_slot_const( fd_gui_t const * gui, ulong _slot ) {
1235 0 : return fd_gui_get_leader_slot( gui, _slot );
1236 0 : }
1237 :
1238 : /* fd_gui_get_root_slot returns a handle to the closest ancestor of slot
1239 : that is a root, if available, otherwise NULL. */
1240 : static inline fd_gui_slot_t *
1241 : fd_gui_get_root_slot( fd_gui_t const * gui,
1242 0 : ulong slot ) {
1243 0 : fd_gui_slot_t * c = fd_gui_get_slot( gui, slot );
1244 0 : while( c ) {
1245 0 : if( FD_UNLIKELY( c->level>=FD_GUI_SLOT_LEVEL_ROOTED ) ) return c;
1246 0 : c = fd_gui_get_slot( gui, c->parent_slot );
1247 0 : }
1248 0 : return NULL;
1249 0 : }
1250 :
1251 : /* fd_gui_slot_is_ancestor returns 1 if anc is known to be an ancestor
1252 : of slot (on the same fork), 0 otherwise. */
1253 : static inline int
1254 : fd_gui_slot_is_ancestor( fd_gui_t const * gui,
1255 : ulong anc,
1256 0 : ulong slot ) {
1257 0 : fd_gui_slot_t * c = fd_gui_get_slot( gui, slot );
1258 0 : while( c ) {
1259 0 : if( FD_UNLIKELY( c->slot==anc ) ) return 1;
1260 0 : c = fd_gui_get_slot( gui, c->parent_slot );
1261 0 : }
1262 0 : return 0;
1263 0 : }
1264 :
1265 : /* fd_gui_get_parent_slot_on_fork returns a handle to the parent of slot
1266 : on the fork ending on frontier_slot. If slot is unknown or skipped,
1267 : the closest (by slot number) valid parent on the fork is returned.
1268 :
1269 : NULL if slot is not an ancestor of frontier slot or if the parent is
1270 : unknown. */
1271 : static inline fd_gui_slot_t *
1272 : fd_gui_get_parent_slot_on_fork( fd_gui_t const * gui,
1273 : ulong frontier_slot,
1274 0 : ulong slot ) {
1275 0 : fd_gui_slot_t * c = fd_gui_get_slot( gui, frontier_slot );
1276 0 : while( c ) {
1277 0 : if( FD_UNLIKELY( c->slot<=slot ) ) return NULL;
1278 0 : fd_gui_slot_t * p = fd_gui_get_slot( gui, c->parent_slot );
1279 0 : if( FD_UNLIKELY( p && p->slot<=slot-1UL ) ) return p;
1280 0 : c = p;
1281 0 : }
1282 0 : return NULL;
1283 0 : }
1284 :
1285 : /* fd_gui_is_skipped_on_fork returns 1 if slot is skipped on the fork
1286 : starting at anc and ending at des, 0 otherwise. */
1287 : static inline int
1288 : fd_gui_is_skipped_on_fork( fd_gui_t const * gui,
1289 : ulong anc,
1290 : ulong des,
1291 0 : ulong slot ) {
1292 0 : fd_gui_slot_t const * c = fd_gui_get_slot( gui, des );
1293 0 : while( c ) {
1294 0 : if( FD_UNLIKELY( anc==c->slot ) ) return 0; /* on the fork, not skipped */
1295 0 : fd_gui_slot_t const * p = fd_gui_get_slot( gui, c->parent_slot );
1296 0 : if( FD_UNLIKELY( p && p->slot<slot && c->slot>slot ) ) return 1; /* in-between two nodes, skipped */
1297 0 : c = p;
1298 0 : }
1299 :
1300 0 : return 0; /* slot not between anc and des, or is unknown */
1301 0 : }
1302 :
1303 : FD_PROTOTYPES_END
1304 :
1305 : #endif /* HEADER_fd_src_disco_gui_fd_gui_h */
|