Line data Source code
1 : #include "fd_gui_printf.h"
2 : #include "fd_gui_config_parse.h"
3 : #include "fd_gui_hist.h"
4 :
5 : #include "../bundle/fd_bundle_tile.h"
6 : #include "../diag/fd_diag_tile.h"
7 : #include "../../waltz/http/fd_http_server_private.h"
8 : #include "../../ballet/utf8/fd_utf8.h"
9 : #include "../../disco/fd_txn_m.h"
10 : #include "../../disco/metrics/fd_metrics.h"
11 : #include "../../flamenco/progcache/fd_progcache_cache.h"
12 : #include "../../util/bits/fd_float.h"
13 : #include "../../disco/topo/fd_topob.h"
14 :
15 : static void
16 0 : jsonp_strip_trailing_comma( fd_http_server_t * http ) {
17 0 : if( FD_LIKELY( !http->stage_err &&
18 0 : http->stage_len>=1UL &&
19 0 : http->oring[ (http->stage_off%http->oring_sz)+http->stage_len-1UL ]==(uchar)',' ) ) {
20 0 : http->stage_len--;
21 0 : }
22 0 : }
23 :
24 : static inline uchar *
25 : jsonp_put( uchar * q,
26 : char const * s,
27 0 : ulong len ) {
28 0 : fd_memcpy( q, s, len );
29 0 : return q+len;
30 0 : }
31 :
32 : static inline uchar *
33 : jsonp_put_key( uchar * q,
34 : char const * key,
35 0 : ulong key_len ) {
36 0 : *q++ = '"';
37 0 : q = jsonp_put( q, key, key_len );
38 0 : *q++ = '"';
39 0 : *q++ = ':';
40 0 : return q;
41 0 : }
42 :
43 : static inline uchar *
44 : jsonp_put_ulong( uchar * q,
45 0 : ulong value ) {
46 0 : return (uchar *)fd_cstr_append_ulong_as_text( (char *)q, ' ', '\0', value, fd_ulong_base10_dig_cnt( value ) );
47 0 : }
48 :
49 : static inline uchar *
50 : jsonp_put_long( uchar * q,
51 0 : long value ) {
52 0 : ulong mag = value<0L ? (ulong)-(value+1L)+1UL : (ulong)value;
53 0 : return (uchar *)fd_cstr_append_ulong_as_text( (char *)q, ' ', value<0L ? '-' : '\0', mag, fd_ulong_base10_dig_cnt( mag )+(ulong)(value<0L) );
54 0 : }
55 :
56 : static inline uchar *
57 : jsonp_field_start( fd_http_server_t * http,
58 : char const * key,
59 0 : ulong value_max ) {
60 0 : if( FD_UNLIKELY( http->stage_err ) ) return NULL; /* as printf: nothing more until unstaged */
61 :
62 0 : ulong key_len = key ? strlen( key ) : 0UL;
63 0 : ulong need = key_len+3UL+value_max;
64 0 : ulong avail = http->oring_sz-http->stage_len;
65 0 : ulong spec = fd_ulong_min( fd_ulong_max( need, 1024UL ), fd_ulong_max( need, avail ) );
66 0 : uchar * p = fd_http_server_append_start( http, spec );
67 0 : if( FD_UNLIKELY( !p ) ) return NULL;
68 0 : if( FD_LIKELY( key ) ) p = jsonp_put_key( p, key, key_len );
69 0 : return p;
70 0 : }
71 :
72 : static inline void
73 : jsonp_field_end( fd_http_server_t * http,
74 0 : uchar const * end ) {
75 0 : fd_http_server_append_end( http, (ulong)(end-(http->oring+(http->stage_off%http->oring_sz)+http->stage_len)) );
76 0 : }
77 :
78 : static void
79 : jsonp_open_object( fd_http_server_t * http,
80 0 : char const * key ) {
81 0 : uchar * q = jsonp_field_start( http, key, 1UL );
82 0 : if( FD_UNLIKELY( !q ) ) return;
83 0 : *q++ = '{';
84 0 : jsonp_field_end( http, q );
85 0 : }
86 :
87 : static void
88 0 : jsonp_close_object( fd_http_server_t * http ) {
89 0 : jsonp_strip_trailing_comma( http );
90 0 : uchar * q = jsonp_field_start( http, NULL, 2UL );
91 0 : if( FD_UNLIKELY( !q ) ) return;
92 0 : jsonp_field_end( http, jsonp_put( q, "},", 2UL ) );
93 0 : }
94 :
95 : static void
96 : jsonp_open_array( fd_http_server_t * http,
97 0 : char const * key ) {
98 0 : uchar * q = jsonp_field_start( http, key, 1UL );
99 0 : if( FD_UNLIKELY( !q ) ) return;
100 0 : *q++ = '[';
101 0 : jsonp_field_end( http, q );
102 0 : }
103 :
104 : static void
105 0 : jsonp_close_array( fd_http_server_t * http ) {
106 0 : jsonp_strip_trailing_comma( http );
107 0 : uchar * q = jsonp_field_start( http, NULL, 2UL );
108 0 : if( FD_UNLIKELY( !q ) ) return;
109 0 : jsonp_field_end( http, jsonp_put( q, "],", 2UL ) );
110 0 : }
111 :
112 : static void
113 : jsonp_ulong( fd_http_server_t * http,
114 : char const * key,
115 0 : ulong value ) {
116 0 : uchar * q = jsonp_field_start( http, key, 21UL );
117 0 : if( FD_UNLIKELY( !q ) ) return;
118 0 : q = jsonp_put_ulong( q, value );
119 0 : *q++ = ',';
120 0 : jsonp_field_end( http, q );
121 0 : }
122 :
123 : static void
124 : jsonp_long( fd_http_server_t * http,
125 : char const * key,
126 0 : long value ) {
127 0 : uchar * q = jsonp_field_start( http, key, 22UL );
128 0 : if( FD_UNLIKELY( !q ) ) return;
129 0 : q = jsonp_put_long( q, value );
130 0 : *q++ = ',';
131 0 : jsonp_field_end( http, q );
132 0 : }
133 :
134 : /* jsonp_put_fixed writes value with dp (2 or 4) decimals, byte for
135 : byte what printf "%.2f"/"%.4f" gives, for finite |value|<1e15 (exact
136 : 128 bit mant*10^dp>>exp, round to nearest even); returns the end of
137 : the text, or NULL for anything else (caller falls back to printf). */
138 :
139 0 : #define JSONP_FIXED_MAX (22UL) /* -999999999999999.9999 */
140 :
141 : static inline uchar *
142 : jsonp_put_fixed( uchar * q,
143 : double value,
144 0 : ulong dp ) {
145 0 : ulong bits = fd_dblbits( value );
146 0 : ulong bexp = fd_dblbits_bexp( bits );
147 0 : ulong mant = fd_dblbits_mant( bits );
148 0 : if( FD_UNLIKELY( bexp==2047UL || !(fabs( value )<1e15) ) ) return NULL;
149 0 : long shift;
150 0 : if( FD_LIKELY( bexp ) ) { mant |= 1UL<<52; shift = 1075L-(long)bexp; } /* >=3 below 1e15<2^50 */
151 0 : else shift = 1074L; /* subnormal */
152 0 : ulong pow10 = dp==2UL ? 100UL : 10000UL;
153 0 : uint128 s = (uint128)mant*pow10; /* <2^67 */
154 0 : ulong r = 0UL;
155 0 : if( FD_LIKELY( shift<128L ) ) {
156 0 : r = (ulong)(s>>shift);
157 0 : uint128 rem = s-((uint128)r<<shift);
158 0 : uint128 half = (uint128)1<<(shift-1L);
159 0 : r += (ulong)( (rem>half) | ((rem==half) & (r&1UL)) );
160 0 : } /* else s<2^67<=half: rounds to 0 */
161 0 : if( bits>>63 ) *q++ = '-';
162 0 : q = jsonp_put_ulong( q, r/pow10 );
163 0 : *q++ = '.';
164 0 : ulong frac = r%pow10;
165 0 : if( dp==4UL ) { *q++ = (uchar)('0'+frac/1000UL); *q++ = (uchar)('0'+(frac/100UL)%10UL); }
166 0 : *q++ = (uchar)('0'+(frac/10UL)%10UL);
167 0 : *q++ = (uchar)('0'+frac%10UL);
168 0 : return q;
169 0 : }
170 :
171 : static void
172 : jsonp_fixed( fd_http_server_t * http,
173 : char const * key,
174 : double value,
175 0 : ulong dp ) {
176 0 : if( FD_UNLIKELY( http->stage_err ) ) return;
177 0 : uchar * q = jsonp_field_start( http, key, JSONP_FIXED_MAX+1UL );
178 0 : if( FD_UNLIKELY( !q ) ) return;
179 0 : uchar * end = jsonp_put_fixed( q, value, dp );
180 0 : if( FD_LIKELY( end ) ) {
181 0 : *end++ = ',';
182 0 : jsonp_field_end( http, end );
183 0 : return;
184 0 : }
185 : /* nan, inf or >=1e15: printf, as before (the reserve above is the
186 : one printf makes, so nothing about the ring moved) */
187 0 : if( FD_LIKELY( key ) ) fd_http_server_printf( http, "\"%s\":%.*f,", key, (int)dp, value );
188 0 : else fd_http_server_printf( http, "%.*f,", (int)dp, value );
189 0 : }
190 :
191 : static void
192 : jsonp_double( fd_http_server_t * http,
193 : char const * key,
194 0 : double value ) {
195 0 : jsonp_fixed( http, key, value, 2UL );
196 0 : }
197 :
198 : static void
199 : jsonp_centi( fd_http_server_t * http,
200 : char const * key,
201 0 : ushort value ) {
202 0 : uchar * q = jsonp_field_start( http, key, 7UL );
203 0 : if( FD_UNLIKELY( !q ) ) return;
204 0 : q = jsonp_put_ulong( q, (ulong)value/100UL );
205 0 : *q++ = '.';
206 0 : *q++ = (uchar)('0'+(value/10U)%10U);
207 0 : *q++ = (uchar)('0'+value%10U);
208 0 : *q++ = ',';
209 0 : jsonp_field_end( http, q );
210 0 : }
211 :
212 : static void
213 : jsonp_double_4dp( fd_http_server_t * http,
214 : char const * key,
215 0 : double value ) {
216 0 : jsonp_fixed( http, key, value, 4UL );
217 0 : }
218 :
219 : static void
220 : jsonp_ulong_as_str( fd_http_server_t * http,
221 : char const * key,
222 0 : ulong value ) {
223 0 : uchar * q = jsonp_field_start( http, key, 23UL );
224 0 : if( FD_UNLIKELY( !q ) ) return;
225 0 : *q++ = '"';
226 0 : q = jsonp_put_ulong( q, value );
227 0 : *q++ = '"';
228 0 : *q++ = ',';
229 0 : jsonp_field_end( http, q );
230 0 : }
231 :
232 : static void
233 : jsonp_long_as_str( fd_http_server_t * http,
234 : char const * key,
235 0 : long value ) {
236 0 : uchar * q = jsonp_field_start( http, key, 24UL );
237 0 : if( FD_UNLIKELY( !q ) ) return;
238 0 : *q++ = '"';
239 0 : q = jsonp_put_long( q, value );
240 0 : *q++ = '"';
241 0 : *q++ = ',';
242 0 : jsonp_field_end( http, q );
243 0 : }
244 :
245 : static void
246 : jsonp_string( fd_http_server_t * http,
247 : char const * key,
248 0 : char const * value ) {
249 0 : ulong value_len = value ? strlen( value ) : 0UL;
250 0 : if( FD_UNLIKELY( value && !fd_utf8_verify( value, value_len ) ) ) value = NULL;
251 0 : uchar * q = jsonp_field_start( http, key, value ? value_len+3UL : 5UL );
252 0 : if( FD_UNLIKELY( !q ) ) return;
253 0 : if( FD_LIKELY( value ) ) {
254 0 : *q++ = '"';
255 : /* escape quotemark, reverse solidus, and control chars U+0000
256 : through U+001F just replace with a space */
257 0 : for( ulong i=0UL; i<value_len; i++ ) {
258 0 : uchar c = (uchar)value[ i ];
259 0 : *q++ = fd_uchar_if( c<0x20 || c=='"' || c=='\\', (uchar)' ', c );
260 0 : }
261 0 : *q++ = '"';
262 0 : *q++ = ',';
263 0 : } else {
264 0 : q = jsonp_put( q, "null,", 5UL );
265 0 : }
266 0 : jsonp_field_end( http, q );
267 0 : }
268 :
269 : static void
270 : jsonp_bool( fd_http_server_t * http,
271 : char const * key,
272 0 : int value ) {
273 0 : uchar * q = jsonp_field_start( http, key, 6UL );
274 0 : if( FD_UNLIKELY( !q ) ) return;
275 0 : q = value ? jsonp_put( q, "true,", 5UL ) : jsonp_put( q, "false,", 6UL );
276 0 : jsonp_field_end( http, q );
277 0 : }
278 :
279 : static void
280 : jsonp_null( fd_http_server_t * http,
281 0 : char const * key ) {
282 0 : uchar * q = jsonp_field_start( http, key, 6UL );
283 0 : if( FD_UNLIKELY( !q ) ) return;
284 0 : q = key ? jsonp_put( q, " null,", 6UL ) : jsonp_put( q, "null,", 5UL );
285 0 : jsonp_field_end( http, q );
286 0 : }
287 :
288 : static void
289 : jsonp_text( fd_http_server_t * http,
290 : char const * text,
291 0 : ulong len ) {
292 0 : uchar * q = jsonp_field_start( http, NULL, len );
293 0 : if( FD_UNLIKELY( !q ) ) return;
294 0 : jsonp_field_end( http, jsonp_put( q, text, len ) );
295 0 : }
296 :
297 : static void
298 : jsonp_open_envelope( fd_http_server_t * http,
299 : char const * topic,
300 0 : char const * key ) {
301 0 : jsonp_open_object( http, NULL );
302 0 : jsonp_string( http, "topic", topic );
303 0 : jsonp_string( http, "key", key );
304 0 : }
305 :
306 : static void
307 0 : jsonp_close_envelope( fd_http_server_t * http ) {
308 0 : jsonp_close_object( http );
309 0 : jsonp_strip_trailing_comma( http );
310 0 : }
311 :
312 : void
313 : fd_gui_printf_open_query_response_envelope( fd_http_server_t * http,
314 : char const * topic,
315 : char const * key,
316 0 : ulong id ) {
317 0 : jsonp_open_object( http, NULL );
318 0 : jsonp_string( http, "topic", topic );
319 0 : jsonp_string( http, "key", key );
320 0 : jsonp_ulong( http, "id", id );
321 0 : }
322 :
323 : void
324 0 : fd_gui_printf_close_query_response_envelope( fd_http_server_t * http ) {
325 0 : jsonp_close_object( http );
326 0 : jsonp_strip_trailing_comma( http );
327 0 : }
328 :
329 : void
330 : fd_gui_printf_null_query_response( fd_http_server_t * http,
331 : char const * topic,
332 : char const * key,
333 0 : ulong id ) {
334 0 : fd_gui_printf_open_query_response_envelope( http, topic, key, id );
335 0 : jsonp_null( http, "value" );
336 0 : fd_gui_printf_close_query_response_envelope( http );
337 0 : }
338 :
339 : void
340 0 : fd_gui_printf_version( fd_gui_t * gui ) {
341 0 : jsonp_open_envelope( gui->http, "summary", "version" );
342 0 : jsonp_string( gui->http, "value", gui->summary.version );
343 0 : jsonp_close_envelope( gui->http );
344 0 : }
345 :
346 : void
347 0 : fd_gui_printf_is_alpenglow( fd_gui_t * gui ) {
348 0 : jsonp_open_envelope( gui->http, "summary", "is_alpenglow" );
349 0 : jsonp_bool( gui->http, "value", 1 );
350 0 : jsonp_close_envelope( gui->http );
351 0 : }
352 :
353 : void
354 0 : fd_gui_printf_cluster( fd_gui_t * gui ) {
355 0 : jsonp_open_envelope( gui->http, "summary", "cluster" );
356 0 : jsonp_string( gui->http, "value", gui->summary.cluster );
357 0 : jsonp_close_envelope( gui->http );
358 0 : }
359 :
360 : void
361 0 : fd_gui_printf_commit_hash( fd_gui_t * gui ) {
362 0 : jsonp_open_envelope( gui->http, "summary", "commit_hash" );
363 0 : jsonp_string( gui->http, "value", fd_commit_ref_cstr );
364 0 : jsonp_close_envelope( gui->http );
365 0 : }
366 :
367 : void
368 0 : fd_gui_printf_identity_key( fd_gui_t * gui ) {
369 0 : jsonp_open_envelope( gui->http, "summary", "identity_key" );
370 0 : jsonp_string( gui->http, "value", gui->summary.identity_key_base58 );
371 0 : jsonp_close_envelope( gui->http );
372 0 : }
373 :
374 : void
375 0 : fd_gui_printf_vote_key( fd_gui_t * gui ) {
376 0 : jsonp_open_envelope( gui->http, "summary", "vote_key" );
377 0 : if( FD_LIKELY( gui->summary.has_vote_key ) ) jsonp_string( gui->http, "value", gui->summary.vote_key_base58 );
378 0 : else jsonp_null( gui->http, "value" );
379 0 : jsonp_close_envelope( gui->http );
380 0 : }
381 :
382 : void
383 0 : fd_gui_printf_startup_time_nanos( fd_gui_t * gui ) {
384 0 : jsonp_open_envelope( gui->http, "summary", "startup_time_nanos" );
385 0 : jsonp_long_as_str( gui->http, "value", gui->summary.startup_time_nanos );
386 0 : jsonp_close_envelope( gui->http );
387 0 : }
388 :
389 : void
390 0 : fd_gui_printf_server_time_nanos( fd_gui_t * gui, long now ) {
391 0 : jsonp_open_envelope( gui->http, "summary", "server_time_nanos" );
392 0 : jsonp_long_as_str( gui->http, "value", now );
393 0 : jsonp_close_envelope( gui->http );
394 0 : }
395 :
396 : void
397 0 : fd_gui_printf_repair_slot( fd_gui_t * gui ) {
398 0 : jsonp_open_envelope( gui->http, "summary", "repair_slot" );
399 0 : if( FD_LIKELY( gui->summary.slot_repair!=ULONG_MAX ) ) jsonp_ulong( gui->http, "value", gui->summary.slot_repair );
400 0 : else jsonp_null ( gui->http, "value" );
401 0 : jsonp_close_envelope( gui->http );
402 0 : }
403 :
404 : void
405 0 : fd_gui_printf_vote_slot( fd_gui_t * gui ) {
406 0 : jsonp_open_envelope( gui->http, "summary", "vote_slot" );
407 0 : if( FD_LIKELY( gui->summary.slot_voted!=ULONG_MAX ) ) jsonp_ulong( gui->http, "value", gui->summary.slot_voted );
408 0 : else jsonp_null ( gui->http, "value" );
409 0 : jsonp_close_envelope( gui->http );
410 0 : }
411 :
412 : void
413 0 : fd_gui_printf_turbine_slot( fd_gui_t * gui ) {
414 0 : jsonp_open_envelope( gui->http, "summary", "turbine_slot" );
415 0 : if( FD_LIKELY( gui->summary.slot_turbine!=ULONG_MAX ) ) jsonp_ulong( gui->http, "value", gui->summary.slot_turbine );
416 0 : else jsonp_null ( gui->http, "value" );
417 0 : jsonp_close_envelope( gui->http );
418 0 : }
419 :
420 : void
421 0 : fd_gui_printf_reset_slot( fd_gui_t * gui ) {
422 0 : jsonp_open_envelope( gui->http, "summary", "reset_slot" );
423 0 : if( FD_LIKELY( gui->summary.slot_reset!=ULONG_MAX ) ) jsonp_ulong( gui->http, "value", gui->summary.slot_reset );
424 0 : else jsonp_null ( gui->http, "value" );
425 0 : jsonp_close_envelope( gui->http );
426 0 : }
427 :
428 : void
429 0 : fd_gui_printf_storage_slot( fd_gui_t * gui ) {
430 0 : jsonp_open_envelope( gui->http, "summary", "storage_slot" );
431 0 : if( FD_LIKELY( gui->summary.slot_storage!=ULONG_MAX ) ) jsonp_ulong( gui->http, "value", gui->summary.slot_storage );
432 0 : else jsonp_null ( gui->http, "value" );
433 0 : jsonp_close_envelope( gui->http );
434 0 : }
435 :
436 : void
437 0 : fd_gui_printf_active_fork_cnt( fd_gui_t * gui ) {
438 0 : jsonp_open_envelope( gui->http, "summary", "active_fork_count" );
439 0 : if( FD_LIKELY( gui->summary.active_fork_cnt!=ULONG_MAX ) ) jsonp_ulong( gui->http, "value", gui->summary.active_fork_cnt );
440 0 : else jsonp_null ( gui->http, "value" );
441 0 : jsonp_close_envelope( gui->http );
442 0 : }
443 :
444 : void
445 0 : fd_gui_printf_slot_caught_up( fd_gui_t * gui ) {
446 0 : jsonp_open_envelope( gui->http, "summary", "slot_caught_up" );
447 0 : if( FD_LIKELY( gui->summary.slot_caught_up!=ULONG_MAX ) ) jsonp_ulong( gui->http, "value", gui->summary.slot_caught_up );
448 0 : else jsonp_null ( gui->http, "value" );
449 0 : jsonp_close_envelope( gui->http );
450 0 : }
451 :
452 : void
453 0 : fd_gui_printf_catch_up_history( fd_gui_t * gui ) {
454 0 : jsonp_open_envelope( gui->http, "summary", "catch_up_history" );
455 0 : jsonp_open_object( gui->http, "value" );
456 0 : jsonp_open_array( gui->http, "turbine" );
457 0 : for( ulong i=0UL; i<gui->summary.catch_up_turbine_sz; i+=2 ) {
458 0 : for( ulong j=gui->summary.catch_up_turbine[ i ]; j<=gui->summary.catch_up_turbine[ i+1UL ]; j++ ) {
459 0 : jsonp_ulong( gui->http, NULL, j );
460 0 : }
461 0 : }
462 0 : jsonp_close_array( gui->http );
463 0 : jsonp_open_array( gui->http, "repair" );
464 0 : for( ulong i=0UL; i<gui->summary.catch_up_repair_sz; i+=2 ) {
465 0 : for( ulong j=gui->summary.catch_up_repair[ i ]; j<=gui->summary.catch_up_repair[ i+1UL ]; j++ ) {
466 0 : jsonp_ulong( gui->http, NULL, j );
467 0 : }
468 0 : }
469 0 : jsonp_close_array( gui->http );
470 :
471 0 : if( FD_LIKELY( gui->summary.boot_progress.phase==FD_GUI_BOOT_PROGRESS_TYPE_CATCHING_UP ) ) {
472 0 : ulong min_slot = ULONG_MAX;
473 0 : long min_ts = LONG_MAX;
474 :
475 0 : #define SHREDS_REV_ITER( age_ns, code_archive ) \
476 0 : do { \
477 0 : if( FD_UNLIKELY( gui->summary.boot_progress.catching_up_time_nanos==0L ) ) break; \
478 0 : long _hi_ns = gui->summary.boot_progress.catching_up_time_nanos; \
479 0 : long _lo_ns = _hi_ns - (long)(age_ns); \
480 0 : fd_gui_shred_event_iter_t _it[ 1 ]; \
481 0 : fd_gui_shred_event_iter_begin( gui, _it, _lo_ns, _hi_ns ); \
482 0 : while( fd_gui_shred_event_iter_next( _it ) ) { \
483 0 : fd_gui_shred_event_t const * event = &_it->event; (void)event; \
484 0 : ulong db_event_slot = event->slot; (void)db_event_slot; \
485 0 : do { code_archive } while (0); \
486 0 : } \
487 0 : fd_gui_shred_event_iter_end( _it ); \
488 0 : } while(0);
489 :
490 0 : SHREDS_REV_ITER(
491 0 : 15000000000,
492 0 : {
493 0 : min_slot = fd_ulong_min( min_slot, db_event_slot );
494 0 : min_ts = fd_long_min( min_ts, event->event_time_ns );
495 0 : }
496 0 : )
497 :
498 0 : jsonp_open_object( gui->http, "shreds" );
499 0 : jsonp_ulong ( gui->http, "reference_slot", min_slot );
500 0 : jsonp_long_as_str( gui->http, "reference_ts", min_ts );
501 :
502 0 : jsonp_open_array( gui->http, "slot_delta" );
503 0 : SHREDS_REV_ITER(
504 0 : 15000000000L,
505 0 : { jsonp_ulong( gui->http, NULL, db_event_slot-min_slot ); }
506 0 : )
507 0 : jsonp_close_array( gui->http );
508 0 : jsonp_open_array( gui->http, "shred_idx" );
509 0 : SHREDS_REV_ITER(
510 0 : 15000000000L,
511 0 : {
512 0 : if( FD_LIKELY( event->idx!=USHORT_MAX ) ) jsonp_ulong( gui->http, NULL, event->idx );
513 0 : else jsonp_null ( gui->http, NULL );
514 0 : }
515 0 : )
516 0 : jsonp_close_array( gui->http );
517 0 : jsonp_open_array( gui->http, "event" );
518 0 : SHREDS_REV_ITER(
519 0 : 15000000000L,
520 0 : { jsonp_ulong( gui->http, NULL, event->event ); }
521 0 : )
522 0 : jsonp_close_array( gui->http );
523 0 : jsonp_open_array( gui->http, "event_ts_delta" );
524 0 : SHREDS_REV_ITER(
525 0 : 15000000000L,
526 0 : { jsonp_ulong_as_str( gui->http, NULL, (ulong)event->event_time_ns-(ulong)min_ts ); }
527 0 : )
528 0 : jsonp_close_array( gui->http );
529 0 : jsonp_close_object( gui->http );
530 0 : #undef SHREDS_REV_ITER
531 0 : } else {
532 0 : jsonp_null( gui->http, "shreds" );
533 0 : }
534 :
535 0 : jsonp_close_object( gui->http );
536 0 : jsonp_close_envelope( gui->http );
537 0 : }
538 :
539 : void
540 0 : fd_gui_printf_vote_state( fd_gui_t * gui ) {
541 0 : jsonp_open_envelope( gui->http, "summary", "vote_state" );
542 0 : switch( gui->summary.vote_state ) {
543 0 : case FD_GUI_VOTE_STATE_NON_VOTING:
544 0 : jsonp_string( gui->http, "value", "non-voting" );
545 0 : break;
546 0 : case FD_GUI_VOTE_STATE_VOTING:
547 0 : jsonp_string( gui->http, "value", "voting" );
548 0 : break;
549 0 : case FD_GUI_VOTE_STATE_DELINQUENT:
550 0 : jsonp_string( gui->http, "value", "delinquent" );
551 0 : break;
552 0 : default:
553 0 : FD_LOG_ERR(( "unknown vote state %d", gui->summary.vote_state ));
554 0 : }
555 0 : jsonp_close_envelope( gui->http );
556 0 : }
557 :
558 : /* fd_gui_skipped_history_bounds computes the half open index range
559 : [first_known_slot_idx,root_slot_idx) into an epoch's skip arrays. */
560 :
561 : static int
562 : fd_gui_skipped_history_bounds( fd_gui_t const * gui,
563 : fd_gui_epoch_t const * rec,
564 : ulong * lo_out,
565 0 : ulong * hi_out ) {
566 0 : if( FD_UNLIKELY( !rec ) ) return 0;
567 0 : if( FD_UNLIKELY( gui->summary.slot_rooted==ULONG_MAX ) ) return 0;
568 0 : if( FD_UNLIKELY( gui->summary.slot_rooted<rec->start_slot ) ) return 0;
569 :
570 0 : ulong first_replay_slot = fd_ulong_if( gui->summary.slot_caught_up!=ULONG_MAX, fd_gui_first_replay_slot( gui ), ULONG_MAX );
571 0 : ulong start_slot = fd_ulong_max( rec->start_slot, fd_ulong_if( first_replay_slot!=ULONG_MAX, first_replay_slot, 0UL ) );
572 0 : if( FD_UNLIKELY( start_slot>gui->summary.slot_rooted ) ) return 0;
573 :
574 0 : *lo_out = start_slot - rec->start_slot;
575 0 : *hi_out = fd_ulong_min( rec->slot_cnt, gui->summary.slot_rooted+1UL-rec->start_slot );
576 0 : return *lo_out<*hi_out;
577 0 : }
578 :
579 : void
580 0 : fd_gui_printf_skipped_history( fd_gui_t * gui, ulong epoch ) {
581 0 : jsonp_open_envelope( gui->http, "slot", "skipped_history" );
582 0 : jsonp_open_array( gui->http, "value" );
583 0 : fd_gui_epoch_t const * rec = fd_gui_epoch( gui, epoch );
584 0 : ulong lo, hi;
585 0 : if( FD_LIKELY( fd_gui_skipped_history_bounds( gui, rec, &lo, &hi ) ) ) {
586 0 : for( ulong i=lo; i<hi; i++ ) {
587 0 : ulong slot = rec->start_slot+i;
588 0 : if( FD_UNLIKELY( rec->skipped[ i ] && fd_gui_slot_is_mine( gui, slot ) ) ) jsonp_ulong( gui->http, NULL, slot );
589 0 : }
590 0 : }
591 0 : jsonp_close_array( gui->http );
592 0 : jsonp_close_envelope( gui->http );
593 0 : }
594 :
595 : void
596 0 : fd_gui_printf_skipped_history_cluster( fd_gui_t * gui, ulong epoch ) {
597 0 : jsonp_open_envelope( gui->http, "slot", "skipped_history_cluster" );
598 0 : jsonp_open_array( gui->http, "value" );
599 0 : fd_gui_epoch_t const * rec = fd_gui_epoch( gui, epoch );
600 0 : ulong lo, hi;
601 0 : if( FD_LIKELY( fd_gui_skipped_history_bounds( gui, rec, &lo, &hi ) ) ) {
602 0 : for( ulong i=lo; i<hi; i++ ) {
603 0 : if( FD_UNLIKELY( rec->skipped[ i ] ) ) jsonp_ulong( gui->http, NULL, rec->start_slot+i );
604 0 : }
605 0 : }
606 0 : jsonp_close_array( gui->http );
607 0 : jsonp_close_envelope( gui->http );
608 0 : }
609 :
610 : void
611 0 : fd_gui_printf_tps_history( fd_gui_t * gui ) {
612 0 : jsonp_open_envelope( gui->http, "summary", "tps_history" );
613 0 : jsonp_open_array( gui->http, "value" );
614 :
615 0 : for( ulong i=0UL; i<FD_GUI_TPS_HISTORY_SAMPLE_CNT; i++ ) {
616 0 : ulong idx = (gui->summary.estimated_tps_history_idx+i) % FD_GUI_TPS_HISTORY_SAMPLE_CNT;
617 0 : jsonp_open_array( gui->http, NULL );
618 0 : ulong success_cnt = gui->summary.estimated_tps_history[ idx ].nonvote_success
619 0 : + gui->summary.estimated_tps_history[ idx ].vote_success;
620 0 : ulong failed_cnt = gui->summary.estimated_tps_history[ idx ].nonvote_failed
621 0 : + gui->summary.estimated_tps_history[ idx ].vote_failed;
622 0 : jsonp_double( gui->http, NULL, (double)success_cnt/(double)FD_GUI_TPS_HISTORY_WINDOW_DURATION_SECONDS );
623 0 : jsonp_double( gui->http, NULL, (double)failed_cnt /(double)FD_GUI_TPS_HISTORY_WINDOW_DURATION_SECONDS );
624 0 : jsonp_close_array( gui->http );
625 0 : }
626 :
627 0 : jsonp_close_array( gui->http );
628 0 : jsonp_close_envelope( gui->http );
629 0 : }
630 :
631 : void
632 0 : fd_gui_printf_block_engine( fd_gui_t * gui ) {
633 0 : jsonp_open_envelope( gui->http, "block_engine", "update" );
634 0 : jsonp_open_object( gui->http, "value" );
635 0 : jsonp_string( gui->http, "name", gui->block_engine.name );
636 0 : jsonp_string( gui->http, "url", gui->block_engine.url );
637 0 : jsonp_string( gui->http, "ip", gui->block_engine.ip_cstr );
638 0 : if( FD_LIKELY( gui->block_engine.status==FD_BUNDLE_STATE_CONNECTING ) ) jsonp_string( gui->http, "status", "connecting" );
639 0 : else if( FD_LIKELY( gui->block_engine.status==FD_BUNDLE_STATE_CONNECTED ) ) jsonp_string( gui->http, "status", "connected" );
640 0 : else if( FD_LIKELY( gui->block_engine.status==FD_BUNDLE_STATE_SLEEPING ) ) jsonp_string( gui->http, "status", "sleeping" );
641 0 : else jsonp_string( gui->http, "status", "disconnected" );
642 0 : jsonp_close_object( gui->http );
643 0 : jsonp_close_envelope( gui->http );
644 0 : }
645 :
646 : static char const *
647 0 : fd_gui_tile_priority_cstr( fd_topo_tile_t const * tile ) {
648 0 : switch( fd_topob_tile_priority_type( tile ) ) {
649 0 : case FD_TOPOB_PRIORITY_FLOATING: return "floating";
650 0 : case FD_TOPOB_PRIORITY_STARTUP: return "startup";
651 0 : case FD_TOPOB_PRIORITY_NORMAL: return "normal";
652 0 : case FD_TOPOB_PRIORITY_CRITICAL: return "critical";
653 0 : default: return "unknown";
654 0 : }
655 0 : }
656 :
657 : void
658 0 : fd_gui_printf_tiles( fd_gui_t * gui ) {
659 0 : jsonp_open_envelope( gui->http, "summary", "tiles" );
660 0 : jsonp_open_array( gui->http, "value" );
661 0 : for( ulong i=0UL; i<gui->summary.tile_cnt; i++ ) {
662 0 : fd_topo_tile_t const * tile = &gui->topo->tiles[ gui->summary.tile[ i ] ];
663 :
664 0 : if( FD_UNLIKELY( !strncmp( tile->name, "bench", 5UL ) ) ) {
665 : /* bench tiles not reported */
666 0 : continue;
667 0 : }
668 :
669 0 : jsonp_open_object( gui->http, NULL );
670 0 : jsonp_string( gui->http, "kind", tile->name );
671 0 : jsonp_ulong( gui->http, "kind_id", tile->kind_id );
672 0 : jsonp_ulong( gui->http, "pid", fd_metrics_tile( tile->metrics )[ MIDX( GAUGE, TILE, PID ) ] );
673 0 : jsonp_string( gui->http, "priority", fd_gui_tile_priority_cstr( tile ) );
674 0 : jsonp_close_object( gui->http );
675 0 : }
676 0 : jsonp_close_array( gui->http );
677 0 : jsonp_close_envelope( gui->http );
678 0 : }
679 :
680 : void
681 0 : fd_gui_printf_schedule_strategy( fd_gui_t * gui ) {
682 0 : jsonp_open_envelope( gui->http, "summary", "schedule_strategy" );
683 0 : char mode[10];
684 0 : switch (gui->summary.schedule_strategy) {
685 0 : case 0: fd_cstr_ncpy( mode, "perf", sizeof(mode) ); break;
686 0 : case 1: fd_cstr_ncpy( mode, "balanced", sizeof(mode) ); break;
687 0 : case 2: fd_cstr_ncpy( mode, "auction", sizeof(mode) ); break;
688 0 : default: FD_LOG_ERR(("unexpected schedule_strategy %d", gui->summary.schedule_strategy));
689 0 : }
690 0 : jsonp_string( gui->http, "value", mode );
691 0 : jsonp_close_envelope( gui->http );
692 0 : }
693 :
694 : void
695 0 : fd_gui_printf_identity_balance( fd_gui_t * gui ) {
696 0 : jsonp_open_envelope( gui->http, "summary", "identity_balance" );
697 0 : jsonp_ulong_as_str( gui->http, "value", gui->summary.identity_account_balance );
698 0 : jsonp_close_envelope( gui->http );
699 0 : }
700 :
701 : void
702 0 : fd_gui_printf_vote_balance( fd_gui_t * gui ) {
703 0 : jsonp_open_envelope( gui->http, "summary", "vote_balance" );
704 0 : jsonp_ulong_as_str( gui->http, "value", gui->summary.vote_account_balance );
705 0 : jsonp_close_envelope( gui->http );
706 0 : }
707 :
708 : void
709 0 : fd_gui_printf_vote_commission( fd_gui_t * gui ) {
710 0 : jsonp_open_envelope( gui->http, "summary", "vote_commission" );
711 0 : if( FD_LIKELY( gui->summary.vote_commission!=USHORT_MAX ) ) jsonp_ulong( gui->http, "value", (ulong)gui->summary.vote_commission );
712 0 : else jsonp_null ( gui->http, "value" );
713 0 : jsonp_close_envelope( gui->http );
714 0 : }
715 :
716 : void
717 0 : fd_gui_printf_estimated_slot_duration_nanos( fd_gui_t * gui ) {
718 0 : jsonp_open_envelope( gui->http, "summary", "estimated_slot_duration_nanos" );
719 0 : jsonp_ulong( gui->http, "value", gui->summary.estimated_slot_duration_nanos );
720 0 : jsonp_close_envelope( gui->http );
721 0 : }
722 :
723 :
724 : void
725 0 : fd_gui_printf_root_slot( fd_gui_t * gui ) {
726 0 : jsonp_open_envelope( gui->http, "summary", "root_slot" );
727 0 : jsonp_ulong( gui->http, "value", fd_ulong_if( gui->summary.slot_rooted!=ULONG_MAX, gui->summary.slot_rooted, 0UL ) );
728 0 : jsonp_close_envelope( gui->http );
729 0 : }
730 :
731 : void
732 0 : fd_gui_printf_finalized_slot( fd_gui_t * gui ) {
733 0 : jsonp_open_envelope( gui->http, "summary", "finalized_slot" );
734 0 : jsonp_ulong( gui->http, "value", fd_ulong_if( gui->summary.slot_finalized!=ULONG_MAX, gui->summary.slot_finalized, 0UL ) );
735 0 : jsonp_close_envelope( gui->http );
736 0 : }
737 :
738 : void
739 0 : fd_gui_printf_notarized_slot( fd_gui_t * gui ) {
740 0 : jsonp_open_envelope( gui->http, "summary", "notarized_slot" );
741 0 : jsonp_ulong( gui->http, "value", fd_ulong_if( gui->summary.slot_notarized!=ULONG_MAX, gui->summary.slot_notarized, 0UL ) );
742 0 : jsonp_close_envelope( gui->http );
743 0 : }
744 :
745 : void
746 0 : fd_gui_printf_completed_slot( fd_gui_t * gui ) {
747 0 : jsonp_open_envelope( gui->http, "summary", "completed_slot" );
748 0 : jsonp_ulong( gui->http, "value", fd_ulong_if( gui->summary.slot_tower!=ULONG_MAX, gui->summary.slot_tower, 0UL ) );
749 0 : jsonp_close_envelope( gui->http );
750 0 : }
751 :
752 : void
753 0 : fd_gui_printf_estimated_slot( fd_gui_t * gui ) {
754 0 : jsonp_open_envelope( gui->http, "summary", "estimated_slot" );
755 0 : jsonp_ulong( gui->http, "value", fd_ulong_if( gui->summary.slot_estimated!=ULONG_MAX, gui->summary.slot_estimated, 0UL ) );
756 0 : jsonp_close_envelope( gui->http );
757 0 : }
758 :
759 : void
760 : fd_gui_printf_skip_rate( fd_gui_t * gui,
761 0 : ulong epoch ) {
762 0 : fd_gui_epoch_t const * rec = fd_gui_epoch( gui, epoch );
763 0 : jsonp_open_envelope( gui->http, "summary", "skip_rate" );
764 0 : jsonp_open_object( gui->http, "value" );
765 0 : jsonp_ulong( gui->http, "epoch", FD_LIKELY( rec ) ? rec->epoch : 0UL );
766 0 : double skip_rate = ( FD_LIKELY( rec ) && rec->my_total_slots ) ? (double)rec->my_skipped_slots/(double)rec->my_total_slots : 0.0;
767 0 : fd_http_server_printf( gui->http, "\"skip_rate\":%.7f,", skip_rate );
768 0 : jsonp_close_object( gui->http );
769 0 : jsonp_close_envelope( gui->http );
770 0 : }
771 :
772 : void
773 : fd_gui_printf_epoch( fd_gui_t * gui,
774 0 : ulong epoch ) {
775 0 : fd_gui_epoch_t const * meta = fd_gui_epoch( gui, epoch );
776 0 : jsonp_open_envelope( gui->http, "epoch", "new" );
777 0 : jsonp_open_object( gui->http, "value" );
778 0 : jsonp_ulong( gui->http, "epoch", FD_LIKELY( meta ) ? meta->epoch : 0UL );
779 0 : if( FD_LIKELY( meta && meta->start_time!=LONG_MAX ) ) jsonp_ulong_as_str( gui->http, "start_time_nanos", (ulong)meta->start_time );
780 0 : else jsonp_null( gui->http, "start_time_nanos" );
781 0 : if( FD_LIKELY( meta && meta->end_time!=LONG_MAX ) ) jsonp_ulong_as_str( gui->http, "end_time_nanos", (ulong)meta->end_time );
782 0 : else jsonp_null( gui->http, "end_time_nanos" );
783 0 : jsonp_ulong( gui->http, "start_slot", FD_LIKELY( meta ) ? meta->start_slot : 0UL );
784 0 : jsonp_ulong( gui->http, "end_slot", FD_LIKELY( meta ) ? meta->start_slot+meta->slot_cnt-1UL : 0UL );
785 0 : jsonp_open_object( gui->http, "epoch_schedule" );
786 0 : jsonp_ulong( gui->http, "slots_per_epoch", FD_LIKELY( meta ) ? meta->epoch_schedule.slots_per_epoch : 0UL );
787 0 : jsonp_ulong( gui->http, "first_normal_epoch", FD_LIKELY( meta ) ? meta->epoch_schedule.first_normal_epoch : 0UL );
788 0 : jsonp_ulong( gui->http, "first_normal_slot", FD_LIKELY( meta ) ? meta->epoch_schedule.first_normal_slot : 0UL );
789 0 : jsonp_bool( gui->http, "warmup", FD_LIKELY( meta ) ? meta->epoch_schedule.warmup : 0 );
790 0 : jsonp_close_object( gui->http );
791 0 : jsonp_ulong( gui->http, "target_slot_duration_nanos", FD_LIKELY( meta ) ? (ulong)meta->target_slot_duration_ns : LONG_MAX );
792 0 : jsonp_ulong_as_str( gui->http, "excluded_stake_lamports", 0UL );
793 0 : ulong pub_cnt = FD_LIKELY( meta ) ? meta->pub_cnt : 0UL;
794 0 : ulong stakes_cnt = FD_LIKELY( meta ) ? meta->stakes_cnt : 0UL;
795 0 : ulong sched_cnt = FD_LIKELY( meta ) ? (meta->slot_cnt+FD_EPOCH_SLOTS_PER_ROTATION-1UL)/FD_EPOCH_SLOTS_PER_ROTATION : 0UL;
796 0 : jsonp_open_array( gui->http, "staked_pubkeys" );
797 0 : for( ulong i=0UL; i<pub_cnt; i++ ) {
798 0 : char identity_base58[ FD_BASE58_ENCODED_32_SZ ];
799 0 : fd_base58_encode_32( meta->pub[ i ].uc, NULL, identity_base58 );
800 0 : jsonp_string( gui->http, NULL, identity_base58 );
801 0 : }
802 0 : jsonp_close_array( gui->http );
803 :
804 0 : jsonp_open_array( gui->http, "staked_lamports" );
805 0 : for( ulong i=0UL; i<stakes_cnt; i++ ) jsonp_ulong_as_str( gui->http, NULL, meta->stakes[ i ].stake );
806 0 : jsonp_close_array( gui->http );
807 :
808 0 : jsonp_open_array( gui->http, "leader_slots" );
809 0 : for( ulong i=0UL; i<sched_cnt; i++ ) jsonp_ulong( gui->http, NULL, meta->sched[ i ] );
810 0 : jsonp_close_array( gui->http );
811 0 : jsonp_close_object( gui->http );
812 0 : jsonp_close_envelope( gui->http );
813 0 : }
814 :
815 : static void
816 : fd_gui_printf_waterfall( fd_gui_t * gui,
817 : fd_gui_txn_waterfall_t const * prev,
818 0 : fd_gui_txn_waterfall_t const * cur ) {
819 0 : jsonp_open_object( gui->http, "waterfall" );
820 0 : jsonp_open_object( gui->http, "in" );
821 0 : jsonp_ulong( gui->http, "pack_cranked", cur->in.pack_cranked - prev->in.pack_cranked );
822 0 : jsonp_ulong( gui->http, "pack_retained", prev->out.pack_retained );
823 0 : jsonp_ulong( gui->http, "resolv_retained", prev->out.resolv_retained );
824 0 : jsonp_ulong( gui->http, "quic", cur->in.quic - prev->in.quic );
825 0 : jsonp_ulong( gui->http, "udp", cur->in.udp - prev->in.udp );
826 0 : jsonp_ulong( gui->http, "gossip", 0UL );
827 0 : jsonp_ulong( gui->http, "block_engine", cur->in.block_engine - prev->in.block_engine );
828 0 : jsonp_close_object( gui->http );
829 :
830 0 : jsonp_open_object( gui->http, "out" );
831 0 : jsonp_ulong( gui->http, "net_overrun", cur->out.net_overrun - prev->out.net_overrun );
832 0 : jsonp_ulong( gui->http, "quic_overrun", cur->out.quic_overrun - prev->out.quic_overrun );
833 0 : jsonp_ulong( gui->http, "quic_frag_drop", cur->out.quic_frag_drop - prev->out.quic_frag_drop );
834 0 : jsonp_ulong( gui->http, "quic_abandoned", cur->out.quic_abandoned - prev->out.quic_abandoned );
835 0 : jsonp_ulong( gui->http, "tpu_quic_invalid", cur->out.tpu_quic_invalid - prev->out.tpu_quic_invalid );
836 0 : jsonp_ulong( gui->http, "tpu_udp_invalid", cur->out.tpu_udp_invalid - prev->out.tpu_udp_invalid );
837 0 : jsonp_ulong( gui->http, "verify_overrun", cur->out.verify_overrun - prev->out.verify_overrun );
838 0 : jsonp_ulong( gui->http, "verify_parse", cur->out.verify_parse - prev->out.verify_parse );
839 0 : jsonp_ulong( gui->http, "verify_failed", cur->out.verify_failed - prev->out.verify_failed );
840 0 : jsonp_ulong( gui->http, "verify_duplicate", cur->out.verify_duplicate - prev->out.verify_duplicate );
841 0 : jsonp_ulong( gui->http, "dedup_duplicate", cur->out.dedup_duplicate - prev->out.dedup_duplicate );
842 0 : jsonp_ulong( gui->http, "resolv_lut_failed", cur->out.resolv_lut_failed - prev->out.resolv_lut_failed );
843 0 : jsonp_ulong( gui->http, "resolv_expired", cur->out.resolv_expired - prev->out.resolv_expired );
844 0 : jsonp_ulong( gui->http, "resolv_ancient", cur->out.resolv_ancient - prev->out.resolv_ancient );
845 0 : jsonp_ulong( gui->http, "resolv_no_ledger", cur->out.resolv_no_ledger - prev->out.resolv_no_ledger );
846 0 : jsonp_ulong( gui->http, "resolv_retained", cur->out.resolv_retained );
847 0 : jsonp_ulong( gui->http, "pack_invalid", cur->out.pack_invalid - prev->out.pack_invalid );
848 0 : jsonp_ulong( gui->http, "pack_invalid_bundle", cur->out.pack_invalid_bundle - prev->out.pack_invalid_bundle );
849 0 : jsonp_ulong( gui->http, "pack_expired", cur->out.pack_expired - prev->out.pack_expired );
850 0 : jsonp_ulong( gui->http, "pack_already_executed", cur->out.pack_already_executed - prev->out.pack_already_executed );
851 0 : jsonp_ulong( gui->http, "pack_retained", cur->out.pack_retained );
852 0 : jsonp_ulong( gui->http, "pack_wait_full", 0UL );
853 0 : jsonp_ulong( gui->http, "pack_leader_slow", cur->out.pack_leader_slow - prev->out.pack_leader_slow );
854 0 : jsonp_ulong( gui->http, "bank_invalid", cur->out.bank_invalid - prev->out.bank_invalid );
855 0 : jsonp_ulong( gui->http, "bank_nonce_already_advanced", cur->out.bank_nonce_already_advanced - prev->out.bank_nonce_already_advanced );
856 0 : jsonp_ulong( gui->http, "bank_nonce_advance_failed", cur->out.bank_nonce_advance_failed - prev->out.bank_nonce_advance_failed );
857 0 : jsonp_ulong( gui->http, "bank_nonce_wrong_blockhash", cur->out.bank_nonce_wrong_blockhash - prev->out.bank_nonce_wrong_blockhash );
858 0 : jsonp_ulong( gui->http, "block_success", cur->out.block_success - prev->out.block_success );
859 0 : jsonp_ulong( gui->http, "block_fail", cur->out.block_fail - prev->out.block_fail );
860 0 : jsonp_close_object( gui->http );
861 0 : jsonp_close_object( gui->http );
862 0 : }
863 :
864 : void
865 : fd_gui_printf_live_txn_waterfall( fd_gui_t * gui,
866 : fd_gui_txn_waterfall_t const * prev,
867 : fd_gui_txn_waterfall_t const * cur,
868 0 : ulong next_leader_slot ) {
869 0 : jsonp_open_envelope( gui->http, "summary", "live_txn_waterfall" );
870 0 : jsonp_open_object( gui->http, "value" );
871 0 : jsonp_ulong( gui->http, "next_leader_slot", next_leader_slot );
872 0 : fd_gui_printf_waterfall( gui, prev, cur );
873 0 : jsonp_close_object( gui->http );
874 0 : jsonp_close_envelope( gui->http );
875 0 : }
876 :
877 : static void
878 : fd_gui_printf_network_metrics( fd_gui_t * gui,
879 0 : fd_gui_network_stats_t const * cur ) {
880 0 : jsonp_open_array( gui->http, "ingress" );
881 0 : jsonp_ulong( gui->http, NULL, cur->in.turbine );
882 0 : jsonp_ulong( gui->http, NULL, cur->in.gossip );
883 0 : jsonp_ulong( gui->http, NULL, cur->in.tpu );
884 0 : jsonp_ulong( gui->http, NULL, cur->in.repair );
885 0 : jsonp_ulong( gui->http, NULL, cur->in.rserve );
886 0 : jsonp_ulong( gui->http, NULL, cur->in.metric );
887 0 : jsonp_ulong( gui->http, NULL, cur->in.votor );
888 0 : jsonp_close_array( gui->http );
889 0 : jsonp_open_array( gui->http, "egress" );
890 0 : jsonp_ulong( gui->http, NULL, cur->out.turbine );
891 0 : jsonp_ulong( gui->http, NULL, cur->out.gossip );
892 0 : jsonp_ulong( gui->http, NULL, cur->out.tpu );
893 0 : jsonp_ulong( gui->http, NULL, cur->out.repair );
894 0 : jsonp_ulong( gui->http, NULL, cur->out.rserve );
895 0 : jsonp_ulong( gui->http, NULL, cur->out.metric );
896 0 : jsonp_ulong( gui->http, NULL, cur->out.votor );
897 0 : jsonp_close_array( gui->http );
898 0 : jsonp_open_array( gui->http, "ingress_ema" );
899 0 : for( ulong i=0UL; i<FD_GUI_NET_PROTO_CNT; i++ ) jsonp_double( gui->http, NULL, gui->summary.ingress_ema[ i ].value );
900 0 : jsonp_close_array( gui->http );
901 0 : jsonp_open_array( gui->http, "egress_ema" );
902 0 : for( ulong i=0UL; i<FD_GUI_NET_PROTO_CNT; i++ ) jsonp_double( gui->http, NULL, gui->summary.egress_ema[ i ].value );
903 0 : jsonp_close_array( gui->http );
904 0 : fd_gui_rate_entry_t const * ingress_max_head = fd_gui_rate_deque_empty( gui->summary.ingress_maxq ) ? NULL : fd_gui_rate_deque_peek_head_const( gui->summary.ingress_maxq );
905 0 : fd_gui_rate_entry_t const * egress_max_head = fd_gui_rate_deque_empty( gui->summary.egress_maxq ) ? NULL : fd_gui_rate_deque_peek_head_const( gui->summary.egress_maxq );
906 0 : jsonp_ulong( gui->http, "ingress_max_5m", ingress_max_head ? (ulong)ingress_max_head->value : 0UL );
907 0 : jsonp_ulong( gui->http, "egress_max_5m", egress_max_head ? (ulong)egress_max_head->value : 0UL );
908 0 : }
909 :
910 : void
911 : fd_gui_printf_live_network_metrics( fd_gui_t * gui,
912 0 : fd_gui_network_stats_t const * cur ) {
913 0 : jsonp_open_envelope( gui->http, "summary", "live_network_metrics" );
914 0 : jsonp_open_object( gui->http, "value" );
915 0 : fd_gui_printf_network_metrics( gui, cur );
916 0 : jsonp_close_object( gui->http );
917 0 : jsonp_close_envelope( gui->http );
918 0 : }
919 :
920 : static void
921 : fd_gui_printf_tile_stats( fd_gui_t * gui,
922 : fd_gui_tile_stats_t const * prev,
923 0 : fd_gui_tile_stats_t const * cur ) {
924 0 : jsonp_open_object( gui->http, "tile_primary_metric" );
925 0 : jsonp_ulong( gui->http, "quic", cur->quic_conn_cnt );
926 0 : jsonp_double( gui->http, "bundle_rtt_smoothed_millis", (double)(cur->bundle_rtt_smoothed_nanos) / 1000000.0 );
927 :
928 0 : fd_histf_t bundle_rx_delay_hist_delta[ 1 ];
929 0 : fd_histf_subtract( &cur->bundle_rx_delay_hist, &prev->bundle_rx_delay_hist, bundle_rx_delay_hist_delta );
930 0 : ulong bundle_rx_delay_nanos_p90 = fd_histf_percentile( bundle_rx_delay_hist_delta, 90U, ULONG_MAX );
931 0 : jsonp_double( gui->http, "bundle_rx_delay_millis_p90", fd_double_if(bundle_rx_delay_nanos_p90==ULONG_MAX, 0.0, (double)(bundle_rx_delay_nanos_p90) / 1000000.0 ));
932 :
933 0 : if( FD_LIKELY( cur->sample_time_nanos>prev->sample_time_nanos ) ) {
934 0 : jsonp_ulong( gui->http, "net_in", (ulong)((double)(cur->net_in_rx_bytes - prev->net_in_rx_bytes) * 1000000000.0 / (double)(cur->sample_time_nanos - prev->sample_time_nanos) ));
935 0 : jsonp_ulong( gui->http, "net_out", (ulong)((double)(cur->net_out_tx_bytes - prev->net_out_tx_bytes) * 1000000000.0 / (double)(cur->sample_time_nanos - prev->sample_time_nanos) ));
936 0 : } else {
937 0 : jsonp_ulong( gui->http, "net_in", 0 );
938 0 : jsonp_ulong( gui->http, "net_out", 0 );
939 0 : }
940 0 : if( FD_LIKELY( cur->verify_total_cnt>prev->verify_total_cnt ) ) {
941 0 : jsonp_double( gui->http, "verify", (double)(cur->verify_drop_cnt-prev->verify_drop_cnt) / (double)(cur->verify_total_cnt-prev->verify_total_cnt) );
942 0 : } else {
943 0 : jsonp_double( gui->http, "verify", 0.0 );
944 0 : }
945 0 : if( FD_LIKELY( cur->dedup_total_cnt>prev->dedup_total_cnt ) ) {
946 0 : jsonp_double( gui->http, "dedup", (double)(cur->dedup_drop_cnt-prev->dedup_drop_cnt) / (double)(cur->dedup_total_cnt-prev->dedup_total_cnt) );
947 0 : } else {
948 0 : jsonp_double( gui->http, "dedup", 0.0 );
949 0 : }
950 0 : jsonp_ulong( gui->http, "bank", cur->bank_txn_exec_cnt - prev->bank_txn_exec_cnt );
951 0 : jsonp_double( gui->http, "pack", !cur->pack_buffer_capacity ? 1.0 : (double)cur->pack_buffer_cnt/(double)cur->pack_buffer_capacity );
952 0 : jsonp_double( gui->http, "poh", 0.0 );
953 0 : jsonp_double( gui->http, "shred", 0.0 );
954 0 : jsonp_double( gui->http, "store", 0.0 );
955 0 : jsonp_close_object( gui->http );
956 0 : }
957 :
958 : void
959 : fd_gui_printf_live_tile_stats( fd_gui_t * gui,
960 : fd_gui_tile_stats_t const * prev,
961 0 : fd_gui_tile_stats_t const * cur ) {
962 0 : jsonp_open_envelope( gui->http, "summary", "live_tile_primary_metric" );
963 0 : jsonp_open_object( gui->http, "value" );
964 0 : jsonp_ulong( gui->http, "next_leader_slot", 0UL );
965 0 : fd_gui_printf_tile_stats( gui, prev, cur );
966 0 : jsonp_close_object( gui->http );
967 0 : jsonp_close_envelope( gui->http );
968 0 : }
969 :
970 : static void
971 : fd_gui_printf_tile_timers( fd_gui_t * gui,
972 0 : fd_gui_tile_timers_hist_t const * packed ) {
973 0 : for( ulong i=0UL; i<gui->summary.tile_cnt; i++ ) {
974 0 : ulong t = gui->summary.tile[ i ];
975 0 : fd_topo_tile_t const * tile = &gui->topo->tiles[ t ];
976 :
977 0 : if( FD_UNLIKELY( !strncmp( tile->name, "bench", 5UL ) ) ) {
978 : /* bench tiles not reported */
979 0 : continue;
980 0 : }
981 :
982 0 : ushort ir = packed[ t ].idle_ratio;
983 : /* -1 signals "no sample this window" (NaN has no JSON representation). */
984 0 : double idle_ratio = ( ir==USHORT_MAX ) ? -1.0 : ( (double)ir / 10000.0 );
985 0 : jsonp_double( gui->http, NULL, idle_ratio );
986 0 : }
987 0 : }
988 :
989 : static void
990 : fd_gui_printf_tile_metrics( fd_gui_t * gui,
991 0 : fd_gui_tile_timers_hist_t const * packed ) {
992 0 : jsonp_open_array( gui->http, "timers" );
993 0 : for( ulong i=0UL; i<gui->summary.tile_cnt; i++ ) {
994 0 : ulong t = gui->summary.tile[ i ];
995 0 : fd_topo_tile_t const * tile = &gui->topo->tiles[ t ];
996 :
997 0 : if( FD_UNLIKELY( !strncmp( tile->name, "bench", 5UL ) ) ) {
998 : /* bench tiles not reported */
999 0 : jsonp_null( gui->http, NULL );
1000 0 : continue;
1001 0 : }
1002 :
1003 0 : if( FD_UNLIKELY( packed[ t ].timers[ 0 ]==USHORT_MAX ) ) {
1004 0 : jsonp_null( gui->http, NULL );
1005 0 : } else {
1006 0 : jsonp_open_array( gui->http, NULL );
1007 0 : for( ulong j=0UL; j<FD_METRICS_ENUM_TILE_REGIME_CNT; j++ ) {
1008 0 : jsonp_centi( gui->http, NULL, packed[ t ].timers[ j ] );
1009 0 : }
1010 0 : jsonp_close_array( gui->http );
1011 0 : }
1012 0 : }
1013 0 : jsonp_close_array( gui->http );
1014 :
1015 0 : jsonp_open_array( gui->http, "sched_timers" );
1016 0 : for( ulong i=0UL; i<gui->summary.tile_cnt; i++ ) {
1017 0 : ulong t = gui->summary.tile[ i ];
1018 0 : fd_topo_tile_t const * tile = &gui->topo->tiles[ t ];
1019 :
1020 0 : if( FD_UNLIKELY( !strncmp( tile->name, "bench", 5UL ) ) ) {
1021 : /* bench tiles not reported */
1022 0 : jsonp_null( gui->http, NULL );
1023 0 : continue;
1024 0 : }
1025 :
1026 0 : if( FD_UNLIKELY( packed[ t ].sched_timers[ 0 ]==USHORT_MAX ) ) {
1027 0 : jsonp_null( gui->http, NULL );
1028 0 : } else {
1029 0 : jsonp_open_array( gui->http, NULL );
1030 0 : for( ulong j=0UL; j<FD_METRICS_ENUM_THREAD_REGIME_CNT; j++ ) {
1031 0 : jsonp_centi( gui->http, NULL, packed[ t ].sched_timers[ j ] );
1032 0 : }
1033 0 : jsonp_close_array( gui->http );
1034 0 : }
1035 0 : }
1036 0 : jsonp_close_array( gui->http );
1037 :
1038 0 : jsonp_open_array( gui->http, "in_backp" );
1039 0 : for( ulong i=0UL; i<gui->summary.tile_cnt; i++ ) {
1040 0 : jsonp_bool( gui->http, NULL, packed[ gui->summary.tile[ i ] ].in_backp );
1041 0 : }
1042 0 : jsonp_close_array( gui->http );
1043 0 : jsonp_open_array( gui->http, "backp_msgs" );
1044 0 : for( ulong i=0UL; i<gui->summary.tile_cnt; i++ ) {
1045 0 : jsonp_ulong( gui->http, NULL, packed[ gui->summary.tile[ i ] ].backp_msgs );
1046 0 : }
1047 0 : jsonp_close_array( gui->http );
1048 0 : jsonp_open_array( gui->http, "alive" );
1049 0 : for( ulong i=0UL; i<gui->summary.tile_cnt; i++ ) {
1050 0 : jsonp_ulong( gui->http, NULL, packed[ gui->summary.tile[ i ] ].alive );
1051 0 : }
1052 0 : jsonp_close_array( gui->http );
1053 0 : jsonp_open_array( gui->http, "nvcsw" );
1054 0 : for( ulong i=0UL; i<gui->summary.tile_cnt; i++ ) {
1055 0 : jsonp_ulong( gui->http, NULL, packed[ gui->summary.tile[ i ] ].nvcsw );
1056 0 : }
1057 0 : jsonp_close_array( gui->http );
1058 0 : jsonp_open_array( gui->http, "nivcsw" );
1059 0 : for( ulong i=0UL; i<gui->summary.tile_cnt; i++ ) {
1060 0 : jsonp_ulong( gui->http, NULL, packed[ gui->summary.tile[ i ] ].nivcsw );
1061 0 : }
1062 0 : jsonp_close_array( gui->http );
1063 0 : jsonp_open_array( gui->http, "minflt" );
1064 0 : for( ulong i=0UL; i<gui->summary.tile_cnt; i++ ) {
1065 0 : jsonp_ulong( gui->http, NULL, packed[ gui->summary.tile[ i ] ].minflt );
1066 0 : }
1067 0 : jsonp_close_array( gui->http );
1068 0 : jsonp_open_array( gui->http, "majflt" );
1069 0 : for( ulong i=0UL; i<gui->summary.tile_cnt; i++ ) {
1070 0 : jsonp_ulong( gui->http, NULL, packed[ gui->summary.tile[ i ] ].majflt );
1071 0 : }
1072 0 : jsonp_close_array( gui->http );
1073 0 : jsonp_open_array( gui->http, "last_cpu" );
1074 0 : for( ulong i=0UL; i<gui->summary.tile_cnt; i++ ) {
1075 0 : jsonp_ulong( gui->http, NULL, packed[ gui->summary.tile[ i ] ].last_cpu );
1076 0 : }
1077 0 : jsonp_close_array( gui->http );
1078 0 : jsonp_open_array( gui->http, "interrupts" );
1079 0 : for( ulong i=0UL; i<gui->summary.tile_cnt; i++ ) {
1080 0 : jsonp_ulong( gui->http, NULL, packed[ gui->summary.tile[ i ] ].interrupts );
1081 0 : }
1082 0 : jsonp_close_array( gui->http );
1083 0 : jsonp_open_array( gui->http, "tlb_shootdowns" );
1084 0 : for( ulong i=0UL; i<gui->summary.tile_cnt; i++ ) {
1085 0 : jsonp_ulong( gui->http, NULL, packed[ gui->summary.tile[ i ] ].tlb_shootdowns );
1086 0 : }
1087 0 : jsonp_close_array( gui->http );
1088 0 : jsonp_open_array( gui->http, "timer_ticks" );
1089 0 : for( ulong i=0UL; i<gui->summary.tile_cnt; i++ ) {
1090 0 : jsonp_ulong( gui->http, NULL, packed[ gui->summary.tile[ i ] ].timer_ticks );
1091 0 : }
1092 0 : jsonp_close_array( gui->http );
1093 0 : jsonp_open_array( gui->http, "priority" );
1094 0 : for( ulong i=0UL; i<gui->summary.tile_cnt; i++ ) {
1095 0 : fd_topo_tile_t const * tile = &gui->topo->tiles[ gui->summary.tile[ i ] ];
1096 0 : jsonp_string( gui->http, NULL, fd_gui_tile_priority_cstr( tile ) );
1097 0 : }
1098 0 : jsonp_close_array( gui->http );
1099 0 : }
1100 :
1101 : void
1102 0 : fd_gui_printf_live_tile_timers( fd_gui_t * gui ) {
1103 0 : jsonp_open_envelope( gui->http, "summary", "live_tile_timers" );
1104 0 : jsonp_open_array( gui->http, "value" );
1105 0 : fd_gui_printf_tile_timers( gui, gui->summary.tile_timers_packed );
1106 0 : jsonp_close_array( gui->http );
1107 0 : jsonp_close_envelope( gui->http );
1108 0 : }
1109 :
1110 : void
1111 0 : fd_gui_printf_live_tile_metrics( fd_gui_t * gui ) {
1112 0 : jsonp_open_envelope( gui->http, "summary", "live_tile_metrics" );
1113 0 : jsonp_open_object( gui->http, "value" );
1114 0 : fd_gui_printf_tile_metrics( gui, gui->summary.tile_timers_packed );
1115 0 : jsonp_close_object( gui->http );
1116 0 : jsonp_close_envelope( gui->http );
1117 0 : }
1118 :
1119 : void
1120 0 : fd_gui_printf_estimated_tps( fd_gui_t * gui ) {
1121 0 : ulong idx = (gui->summary.estimated_tps_history_idx+FD_GUI_TPS_HISTORY_SAMPLE_CNT-1UL) % FD_GUI_TPS_HISTORY_SAMPLE_CNT;
1122 :
1123 0 : jsonp_open_envelope( gui->http, "summary", "estimated_tps" );
1124 0 : jsonp_open_object( gui->http, "value" );
1125 0 : ulong success_cnt = gui->summary.estimated_tps_history[ idx ].nonvote_success
1126 0 : + gui->summary.estimated_tps_history[ idx ].vote_success;
1127 0 : ulong failed_cnt = gui->summary.estimated_tps_history[ idx ].nonvote_failed
1128 0 : + gui->summary.estimated_tps_history[ idx ].vote_failed;
1129 0 : jsonp_double( gui->http, "success", (double)success_cnt/(double)FD_GUI_TPS_HISTORY_WINDOW_DURATION_SECONDS );
1130 0 : jsonp_double( gui->http, "failed", (double)failed_cnt /(double)FD_GUI_TPS_HISTORY_WINDOW_DURATION_SECONDS );
1131 0 : jsonp_close_object( gui->http );
1132 0 : jsonp_close_envelope( gui->http );
1133 0 : }
1134 :
1135 : void
1136 0 : fd_gui_printf_live_program_cache( fd_gui_t * gui ) {
1137 0 : fd_topo_t const * topo = gui->topo;
1138 :
1139 0 : ulong insertions = 0UL;
1140 0 : ulong insertion_bytes = 0UL;
1141 0 : ulong evictions = 0UL;
1142 0 : ulong eviction_bytes = 0UL;
1143 0 : ulong spills = 0UL;
1144 0 : ulong spill_bytes = 0UL;
1145 :
1146 0 : for( ulong i=0UL; i<gui->summary.execrp_tile_cnt; i++ ) {
1147 0 : fd_topo_tile_t const * execrp = &topo->tiles[ fd_topo_find_tile( topo, "execrp", i ) ];
1148 0 : volatile ulong const * metrics = fd_metrics_tile( execrp->metrics );
1149 :
1150 0 : insertions += fd_metrics_enum_group_sum( metrics+MIDX( COUNTER, EXECRP, PROGCACHE_INSERTED ), FD_METRICS_ENUM_PROGCACHE_CLASS_G_ALL_MASK );
1151 0 : insertion_bytes += metrics[ MIDX( COUNTER, EXECRP, PROGCACHE_INSERTED_BYTES ) ];
1152 0 : evictions += fd_metrics_enum_group_sum( metrics+MIDX( COUNTER, EXECRP, PROGCACHE_EVICTED ), FD_METRICS_ENUM_PROGCACHE_CLASS_G_ALL_MASK );
1153 0 : eviction_bytes += metrics[ MIDX( COUNTER, EXECRP, PROGCACHE_EVICTED_BYTES ) ];
1154 0 : spills += fd_metrics_enum_group_sum( metrics+MIDX( COUNTER, EXECRP, PROGCACHE_SPILLED ), FD_METRICS_ENUM_PROGCACHE_CLASS_G_ALL_MASK );
1155 0 : spill_bytes += metrics[ MIDX( COUNTER, EXECRP, PROGCACHE_SPILLED_BYTES ) ];
1156 0 : }
1157 :
1158 0 : ulong free_bytes = 0UL;
1159 0 : ulong size_bytes = 0UL;
1160 0 : ulong replay_idx = fd_topo_find_tile( topo, "replay", 0UL );
1161 0 : if( FD_LIKELY( replay_idx!=ULONG_MAX ) ) {
1162 0 : volatile ulong const * m0 = fd_metrics_tile( topo->tiles[ replay_idx ].metrics );
1163 0 : for( ulong c=0UL; c<FD_PROGCACHE_CACHE_CLASS_CNT; c++ ) {
1164 0 : ulong mx = m0[ MIDX( GAUGE, REPLAY, PROGCACHE_CLASS_CAPACITY ) + c ];
1165 0 : ulong us = m0[ MIDX( GAUGE, REPLAY, PROGCACHE_CLASS_USED ) + c ];
1166 0 : size_bytes += mx * fd_progcache_cache_slot_sz[ c ];
1167 0 : free_bytes += (mx - us) * fd_progcache_cache_slot_sz[ c ];
1168 0 : }
1169 0 : }
1170 :
1171 0 : jsonp_open_envelope( gui->http, "summary", "live_program_cache" );
1172 0 : jsonp_open_object( gui->http, "value" );
1173 0 : jsonp_ulong( gui->http, "hits", gui->summary.progcache_hits_1min );
1174 0 : jsonp_ulong( gui->http, "lookups", gui->summary.progcache_lookups_1min );
1175 0 : jsonp_ulong( gui->http, "insertions", insertions );
1176 0 : jsonp_ulong( gui->http, "insertion_bytes", insertion_bytes );
1177 0 : jsonp_ulong( gui->http, "evictions", evictions );
1178 0 : jsonp_ulong( gui->http, "eviction_bytes", eviction_bytes );
1179 0 : jsonp_ulong( gui->http, "spills", spills );
1180 0 : jsonp_ulong( gui->http, "spill_bytes", spill_bytes );
1181 0 : jsonp_ulong( gui->http, "free_bytes", free_bytes );
1182 0 : jsonp_ulong( gui->http, "size_bytes", size_bytes );
1183 0 : jsonp_close_object( gui->http );
1184 0 : jsonp_close_envelope( gui->http );
1185 0 : }
1186 :
1187 : static ulong
1188 : fd_gui_tile_ordinal( fd_gui_t * gui,
1189 0 : ulong topo_tile_idx ) {
1190 0 : ulong ordinal = 0UL;
1191 0 : for( ulong i=0UL; i<gui->summary.tile_cnt; i++ ) {
1192 0 : ulong idx = gui->summary.tile[ i ];
1193 0 : if( FD_UNLIKELY( !strncmp( gui->topo->tiles[ idx ].name, "bench", 5UL ) ) ) continue;
1194 0 : if( FD_LIKELY( idx==topo_tile_idx ) ) return ordinal;
1195 0 : ordinal++;
1196 0 : }
1197 0 : return ULONG_MAX;
1198 0 : }
1199 :
1200 : static char const *
1201 0 : fd_gui_system_file_category_cstr( uint category ) {
1202 0 : switch( category ) {
1203 0 : case FD_DIAG_SYSTEM_FILE_CATEGORY_ACCOUNTS: return "accounts";
1204 0 : case FD_DIAG_SYSTEM_FILE_CATEGORY_SHREDS: return "shreds";
1205 0 : case FD_DIAG_SYSTEM_FILE_CATEGORY_SNAPSHOTS: return "snapshots";
1206 0 : case FD_DIAG_SYSTEM_FILE_CATEGORY_GUI: return "gui";
1207 0 : case FD_DIAG_SYSTEM_FILE_CATEGORY_LOGS: return "logs";
1208 0 : default: return "unknown";
1209 0 : }
1210 0 : }
1211 :
1212 : void
1213 0 : fd_gui_printf_system_resources( fd_gui_t * gui ) {
1214 0 : fd_diag_system_resources_t const * system = &gui->system.resources;
1215 0 : jsonp_open_envelope( gui->http, "summary", "live_system_resources" );
1216 0 : jsonp_open_object( gui->http, "value" );
1217 0 : jsonp_open_array( gui->http, "cpus" );
1218 0 : for( ulong i=0UL; i<(ulong)system->cpu_cnt; i++ ) {
1219 0 : fd_diag_system_cpu_t const * cpu = &system->cpu[ i ];
1220 0 : jsonp_open_object( gui->http, NULL );
1221 0 : jsonp_bool( gui->http, "online", cpu->online );
1222 0 : jsonp_ulong( gui->http, "numa_node", cpu->numa_idx );
1223 0 : if( FD_LIKELY( cpu->die_idx!=USHORT_MAX ) ) jsonp_ulong( gui->http, "die_idx", cpu->die_idx );
1224 0 : else jsonp_null ( gui->http, "die_idx" );
1225 0 : if( FD_LIKELY( cpu->sibling_idx!=USHORT_MAX ) ) jsonp_ulong( gui->http, "sibling_cpu", cpu->sibling_idx );
1226 0 : else jsonp_null ( gui->http, "sibling_cpu" );
1227 0 : jsonp_open_array( gui->http, "tile_idxs" );
1228 0 : for( ulong j=0UL; j<gui->summary.tile_cnt; j++ ) {
1229 0 : ulong topo_tile_idx = gui->summary.tile[ j ];
1230 0 : fd_topo_tile_t const * tile = &gui->topo->tiles[ topo_tile_idx ];
1231 0 : if( FD_LIKELY( tile->cpu_idx!=i || tile->floats ) ) continue;
1232 0 : ulong ordinal = fd_gui_tile_ordinal( gui, topo_tile_idx );
1233 0 : if( ordinal!=ULONG_MAX ) jsonp_ulong( gui->http, NULL, ordinal );
1234 0 : }
1235 0 : jsonp_close_array( gui->http );
1236 0 : jsonp_close_object( gui->http );
1237 0 : }
1238 0 : jsonp_close_array( gui->http );
1239 0 : jsonp_open_object( gui->http, "memory" );
1240 0 : jsonp_ulong( gui->http, "available_bytes", system->mem_available_bytes );
1241 0 : jsonp_ulong( gui->http, "free_bytes", system->mem_free_bytes );
1242 0 : jsonp_open_array( gui->http, "nodes" );
1243 0 : for( ulong i=0UL; i<(ulong)system->numa_mem_cnt; i++ ) {
1244 0 : fd_diag_system_numa_mem_t const * numa = &system->numa_mem[ i ];
1245 0 : jsonp_open_object( gui->http, NULL );
1246 0 : jsonp_ulong( gui->http, "node", numa->numa_idx );
1247 0 : jsonp_ulong( gui->http, "total_bytes", numa->total_bytes );
1248 0 : jsonp_ulong( gui->http, "free_bytes", numa->free_bytes );
1249 0 : jsonp_ulong( gui->http, "shared_bytes", numa->shared_bytes );
1250 0 : jsonp_open_array( gui->http, "tiles" );
1251 0 : for( ulong j=0UL; j<(ulong)system->tile_mem_cnt; j++ ) {
1252 0 : fd_diag_system_tile_mem_t const * tile = &system->tile_mem[ j ];
1253 0 : ulong ordinal = fd_gui_tile_ordinal( gui, tile->tile_idx );
1254 0 : if( tile->numa_idx!=numa->numa_idx || ordinal==ULONG_MAX ) continue;
1255 0 : jsonp_open_object( gui->http, NULL );
1256 0 : jsonp_ulong( gui->http, "tile_idx", ordinal );
1257 0 : jsonp_ulong( gui->http, "bytes", tile->allocated_bytes );
1258 0 : jsonp_close_object( gui->http );
1259 0 : }
1260 0 : jsonp_close_array( gui->http );
1261 0 : jsonp_close_object( gui->http );
1262 0 : }
1263 0 : jsonp_close_array( gui->http );
1264 0 : jsonp_close_object( gui->http );
1265 0 : jsonp_open_array( gui->http, "disk" );
1266 0 : for( ulong i=0UL; i<(ulong)system->mount_cnt; i++ ) {
1267 0 : fd_diag_system_mount_t const * mount = &system->mount[ i ];
1268 0 : jsonp_open_object( gui->http, NULL );
1269 0 : jsonp_string( gui->http, "name", mount->path );
1270 0 : jsonp_ulong( gui->http, "total_bytes", mount->total_bytes );
1271 0 : jsonp_ulong( gui->http, "used_bytes", fd_ulong_sat_sub( mount->total_bytes, mount->free_bytes ) );
1272 0 : jsonp_open_array( gui->http, "files" );
1273 0 : for( ulong j=0UL; j<(ulong)system->file_cnt; j++ ) {
1274 0 : fd_diag_system_file_t const * file = &system->file[ j ];
1275 0 : if( file->mount_idx!=i ) continue;
1276 0 : jsonp_open_object( gui->http, NULL );
1277 0 : jsonp_string( gui->http, "path", file->path );
1278 0 : jsonp_string( gui->http, "category", fd_gui_system_file_category_cstr( file->category ) );
1279 0 : jsonp_ulong( gui->http, "bytes", file->bytes );
1280 0 : jsonp_close_object( gui->http );
1281 0 : }
1282 0 : jsonp_close_array( gui->http );
1283 0 : jsonp_close_object( gui->http );
1284 0 : }
1285 0 : jsonp_close_array( gui->http );
1286 0 : jsonp_close_object( gui->http );
1287 0 : jsonp_close_envelope( gui->http );
1288 0 : }
1289 :
1290 : void
1291 0 : fd_gui_printf_health( fd_gui_t * gui ) {
1292 0 : fd_topo_t const * topo = gui->topo;
1293 :
1294 0 : ulong diag_tile_idx = fd_topo_find_tile( topo, "diag", 0UL );
1295 :
1296 : /* Default to disabled if no diag tile */
1297 0 : ulong bundle_status = FD_DIAG_BUNDLE_STATUS_DISABLED;
1298 0 : ulong vote_status = FD_DIAG_VOTE_STATUS_DISABLED;
1299 0 : ulong replay_status = FD_DIAG_REPLAY_STATUS_DISABLED;
1300 0 : ulong turbine_status = FD_DIAG_TURBINE_STATUS_DISABLED;
1301 0 : ulong builder_status = FD_DIAG_BUILDER_STATUS_DISABLED;
1302 :
1303 0 : if( FD_LIKELY( diag_tile_idx!=ULONG_MAX ) ) {
1304 0 : volatile ulong const * metrics = fd_metrics_tile( topo->tiles[ diag_tile_idx ].metrics );
1305 0 : bundle_status = metrics[ MIDX( GAUGE, DIAG, BUNDLE_STATE ) ];
1306 0 : vote_status = metrics[ MIDX( GAUGE, DIAG, VOTE_STATE ) ];
1307 0 : replay_status = metrics[ MIDX( GAUGE, DIAG, REPLAY_STATE ) ];
1308 0 : turbine_status = metrics[ MIDX( GAUGE, DIAG, TURBINE_STATE ) ];
1309 0 : builder_status = metrics[ MIDX( GAUGE, DIAG, BUILDER_STATE ) ];
1310 0 : }
1311 :
1312 : /* Map bundle status to string */
1313 0 : char const * bundle_str;
1314 0 : switch( bundle_status ) {
1315 0 : case FD_DIAG_BUNDLE_STATUS_DISCONNECTED: bundle_str = "disconnected"; break;
1316 0 : case FD_DIAG_BUNDLE_STATUS_CONNECTING: bundle_str = "connecting"; break;
1317 0 : case FD_DIAG_BUNDLE_STATUS_CONNECTED: bundle_str = "connected"; break;
1318 0 : case FD_DIAG_BUNDLE_STATUS_SLEEPING: bundle_str = "sleeping"; break;
1319 0 : default: bundle_str = "disabled"; break;
1320 0 : }
1321 :
1322 : /* Map vote status to string */
1323 0 : char const * vote_str;
1324 0 : switch( vote_status ) {
1325 0 : case FD_DIAG_VOTE_STATUS_NOT_STARTED: vote_str = "not_started"; break;
1326 0 : case FD_DIAG_VOTE_STATUS_DELINQUENT: vote_str = "delinquent"; break;
1327 0 : case FD_DIAG_VOTE_STATUS_INADMISSIBLE: vote_str = "delinquent"; break; /* TODO: Update frontend, new recognized value */
1328 0 : case FD_DIAG_VOTE_STATUS_VOTING: vote_str = "voting"; break;
1329 0 : default: vote_str = "disabled"; break;
1330 0 : }
1331 :
1332 : /* Map replay status to string */
1333 0 : char const * replay_str;
1334 0 : switch( replay_status ) {
1335 0 : case FD_DIAG_REPLAY_STATUS_NOT_STARTED: replay_str = "not_started"; break;
1336 0 : case FD_DIAG_REPLAY_STATUS_BEHIND: replay_str = "behind"; break;
1337 0 : case FD_DIAG_REPLAY_STATUS_RUNNING: replay_str = "running"; break;
1338 0 : default: replay_str = "disabled"; break;
1339 0 : }
1340 :
1341 : /* Map turbine status to string */
1342 0 : char const * turbine_str;
1343 0 : switch( turbine_status ) {
1344 0 : case FD_DIAG_TURBINE_STATUS_NOT_STARTED: turbine_str = "not_started"; break;
1345 0 : case FD_DIAG_TURBINE_STATUS_STALLED: turbine_str = "stalled"; break;
1346 0 : case FD_DIAG_TURBINE_STATUS_REPAIR_OUTPACING: turbine_str = "repair_outpacing"; break;
1347 0 : case FD_DIAG_TURBINE_STATUS_RUNNING: turbine_str = "running"; break;
1348 0 : default: turbine_str = "disabled"; break;
1349 0 : }
1350 :
1351 0 : char const * builder_str;
1352 0 : switch( builder_status ) {
1353 0 : case FD_DIAG_BUILDER_STATUS_DISCONNECTED: builder_str = "disconnected"; break;
1354 0 : case FD_DIAG_BUILDER_STATUS_CONNECTING: builder_str = "connecting"; break;
1355 0 : case FD_DIAG_BUILDER_STATUS_UNHEALTHY: builder_str = "unhealthy"; break;
1356 0 : case FD_DIAG_BUILDER_STATUS_CONNECTED: builder_str = "connected"; break;
1357 0 : default: builder_str = "disabled"; break;
1358 0 : }
1359 :
1360 0 : jsonp_open_envelope( gui->http, "summary", "health" );
1361 0 : jsonp_open_object( gui->http, "value" );
1362 0 : jsonp_string( gui->http, "vote", vote_str );
1363 0 : jsonp_string( gui->http, "bundle", bundle_str );
1364 0 : jsonp_string( gui->http, "replay", replay_str );
1365 0 : jsonp_string( gui->http, "turbine", turbine_str );
1366 0 : jsonp_string( gui->http, "builder", builder_str );
1367 0 : jsonp_close_object( gui->http );
1368 0 : jsonp_close_envelope( gui->http );
1369 0 : }
1370 :
1371 : /* Triangular-weighted average of a delta ring. Newest sample has
1372 : weight n, oldest has weight 1. Returns sum(w*delta) / weighted_dt
1373 : where weighted_dt is precomputed (sum of w * dt_sec for the same
1374 : samples). */
1375 : static double
1376 : fd_gui_accdb_weighted_rate( ulong const * ring,
1377 : ulong next_write_idx,
1378 : ulong n,
1379 0 : double weighted_dt ) {
1380 0 : if( !n || weighted_dt<=0.0 ) return 0.0;
1381 0 : double num = 0.0;
1382 0 : for( ulong k=0UL; k<n; k++ ) {
1383 0 : double w = (double)(n - k);
1384 0 : ulong ri = (next_write_idx + FD_GUI_ACCDB_WIN_SAMPLES - 1UL - k) % FD_GUI_ACCDB_WIN_SAMPLES;
1385 0 : num += w * (double)ring[ ri ];
1386 0 : }
1387 0 : return num / weighted_dt;
1388 0 : }
1389 :
1390 : static void
1391 : fd_gui_sparkline_push( char * text,
1392 : ulong * len,
1393 : double rate,
1394 0 : int full ) {
1395 0 : ulong l = *len;
1396 0 : if( full ) {
1397 0 : char const * comma = memchr( text, ',', l );
1398 0 : ulong oldest = comma ? (ulong)(comma-text)+1UL : l;
1399 0 : memmove( text, text+oldest, l-oldest );
1400 0 : l -= oldest;
1401 0 : }
1402 0 : char ele[ 32 ];
1403 0 : ulong ele_len;
1404 0 : uchar * end = jsonp_put_fixed( (uchar *)ele, rate, 2UL );
1405 0 : if( FD_LIKELY( end ) ) { *end++ = ','; ele_len = (ulong)(end-(uchar *)ele); }
1406 0 : else FD_TEST( fd_cstr_printf_check( ele, sizeof(ele), &ele_len, "%.2f,", rate ) );
1407 0 : FD_TEST( ele_len<=FD_GUI_ACCDB_SPARKLINE_TEXT_MAX );
1408 0 : fd_memcpy( text+l, ele, ele_len );
1409 0 : *len = l+ele_len;
1410 0 : }
1411 :
1412 : void
1413 0 : fd_gui_accounts_stats_window_push( fd_gui_t * gui ) {
1414 0 : fd_gui_accounts_stats_t const * cur = gui->summary.accounts_stats_current;
1415 0 : fd_gui_accounts_stats_t const * prev = gui->summary.accounts_stats_reference;
1416 0 : int have_ref = gui->summary.accounts_stats_have_reference;
1417 :
1418 0 : long dt_nanos = have_ref ? (cur->sample_time_nanos - prev->sample_time_nanos) : 0L;
1419 :
1420 : /* Append this snap's deltas to the triangular-window rings. The
1421 : emit code below applies triangular weights to compute the smoothed
1422 : rate. Buffer is a simple ring; idx points to the next write slot. */
1423 0 : if( have_ref && dt_nanos>0L ) {
1424 0 : ulong i = gui->summary.accdb->accdb_win_idx;
1425 :
1426 0 : gui->summary.accdb->accdb_win_dt_nanos [ i ] = dt_nanos;
1427 0 : gui->summary.accdb->agg_acquired_win [ i ] = cur->acquired - prev->acquired ;
1428 0 : gui->summary.accdb->agg_acquired_writable_win [ i ] = cur->acquired_writable - prev->acquired_writable ;
1429 0 : gui->summary.accdb->agg_bytes_read_win [ i ] = cur->bytes_read - prev->bytes_read ;
1430 0 : gui->summary.accdb->agg_bytes_copied_win [ i ] = cur->bytes_copied - prev->bytes_copied ;
1431 0 : gui->summary.accdb->agg_bytes_written_win [ i ] = cur->bytes_written - prev->bytes_written ;
1432 0 : gui->summary.accdb->agg_bytes_written_accdb_win [ i ] = cur->bytes_written_accdb - prev->bytes_written_accdb ;
1433 0 : gui->summary.accdb->agg_read_ops_win [ i ] = cur->read_ops - prev->read_ops ;
1434 0 : gui->summary.accdb->agg_write_ops_win [ i ] = cur->write_ops - prev->write_ops ;
1435 0 : gui->summary.accdb->agg_relocated_bytes_win [ i ] = cur->accounts_relocated_bytes - prev->accounts_relocated_bytes;
1436 :
1437 0 : ulong agg_misses_delta = 0UL;
1438 0 : for( ulong c=0UL; c<FD_ACCDB_CACHE_CLASS_CNT; c++ ) {
1439 0 : ulong d = cur->not_found_per_class[ c ] - prev->not_found_per_class[ c ];
1440 0 : agg_misses_delta += d;
1441 0 : gui->summary.accdb->class_acq_win [ c ][ i ] = cur->acquired_per_class [ c ] - prev->acquired_per_class [ c ];
1442 0 : gui->summary.accdb->class_acq_wr_win [ c ][ i ] = cur->acquired_writable_per_class [ c ] - prev->acquired_writable_per_class [ c ];
1443 0 : gui->summary.accdb->class_not_found_win [ c ][ i ] = d;
1444 0 : gui->summary.accdb->class_evicted_win [ c ][ i ] = cur->evicted_per_class [ c ] - prev->evicted_per_class [ c ];
1445 0 : gui->summary.accdb->class_preevicted_win [ c ][ i ] = cur->preevicted_per_class [ c ] - prev->preevicted_per_class [ c ];
1446 0 : gui->summary.accdb->class_commit_new_win [ c ][ i ] = cur->committed_new_per_class [ c ] - prev->committed_new_per_class [ c ];
1447 0 : gui->summary.accdb->class_commit_over_win[ c ][ i ] = cur->committed_overwrite_per_class [ c ] - prev->committed_overwrite_per_class [ c ];
1448 0 : }
1449 0 : gui->summary.accdb->agg_misses_win[ i ] = agg_misses_delta;
1450 :
1451 : /* Per-partition deltas. Diff against prev and push into ring. */
1452 0 : for( ulong p=0UL; p<gui->summary.accdb->partition_cnt; p++ ) {
1453 0 : fd_accdb_shmem_partition_info_t const * info = &gui->summary.accdb->partitions[ p ];
1454 :
1455 : /* Pool slots get reused: when a partition is released and
1456 : re-acquired, change_partition zeroes its counters. Detect
1457 : the reset (any counter dropped below its previous value) and
1458 : treat this sample as the start of a new lifecycle — emit a
1459 : zero delta rather than letting the unsigned subtract wrap
1460 : into a giant rate. */
1461 0 : int reset = info->read_ops < gui->summary.accdb->partition_prev_read_ops [ p ] ||
1462 0 : info->bytes_read < gui->summary.accdb->partition_prev_bytes_read [ p ] ||
1463 0 : info->write_ops < gui->summary.accdb->partition_prev_write_ops [ p ] ||
1464 0 : info->bytes_written < gui->summary.accdb->partition_prev_bytes_written[ p ];
1465 0 : if( FD_UNLIKELY( reset ) ) {
1466 0 : gui->summary.accdb->partition_prev_read_ops [ p ] = info->read_ops;
1467 0 : gui->summary.accdb->partition_prev_bytes_read [ p ] = info->bytes_read;
1468 0 : gui->summary.accdb->partition_prev_write_ops [ p ] = info->write_ops;
1469 0 : gui->summary.accdb->partition_prev_bytes_written[ p ] = info->bytes_written;
1470 : /* Also wipe the historical window so an old lifecycle's
1471 : samples don't keep contributing to this slot's rate. */
1472 0 : for( ulong k=0UL; k<FD_GUI_ACCDB_WIN_SAMPLES; k++ ) {
1473 0 : gui->summary.accdb->partition_read_ops_win [ p ][ k ] = 0UL;
1474 0 : gui->summary.accdb->partition_bytes_read_win [ p ][ k ] = 0UL;
1475 0 : gui->summary.accdb->partition_write_ops_win [ p ][ k ] = 0UL;
1476 0 : gui->summary.accdb->partition_bytes_written_win[ p ][ k ] = 0UL;
1477 0 : }
1478 0 : }
1479 0 : gui->summary.accdb->partition_read_ops_win [ p ][ i ] = info->read_ops - gui->summary.accdb->partition_prev_read_ops [ p ];
1480 0 : gui->summary.accdb->partition_bytes_read_win [ p ][ i ] = info->bytes_read - gui->summary.accdb->partition_prev_bytes_read [ p ];
1481 0 : gui->summary.accdb->partition_write_ops_win [ p ][ i ] = info->write_ops - gui->summary.accdb->partition_prev_write_ops [ p ];
1482 0 : gui->summary.accdb->partition_bytes_written_win[ p ][ i ] = info->bytes_written - gui->summary.accdb->partition_prev_bytes_written[ p ];
1483 :
1484 0 : gui->summary.accdb->partition_prev_read_ops [ p ] = info->read_ops;
1485 0 : gui->summary.accdb->partition_prev_bytes_read [ p ] = info->bytes_read;
1486 0 : gui->summary.accdb->partition_prev_write_ops [ p ] = info->write_ops;
1487 0 : gui->summary.accdb->partition_prev_bytes_written[ p ] = info->bytes_written;
1488 0 : }
1489 :
1490 : /* Per-tile deltas. Cumulative values were snapped from each tile's
1491 : metric page in fd_gui_accounts_stats_snap; diff against prev and
1492 : push into the ring. */
1493 0 : for( ulong s=0UL; s<gui->summary.accdb->accdb_tile_cnt; s++ ) {
1494 0 : gui->summary.accdb->tile_acquired_win [ s ][ i ] = gui->summary.accdb->tile_cur_acquired [ s ] - gui->summary.accdb->tile_prev_acquired [ s ];
1495 0 : gui->summary.accdb->tile_acquired_writable_win[ s ][ i ] = gui->summary.accdb->tile_cur_acquired_writable[ s ] - gui->summary.accdb->tile_prev_acquired_writable[ s ];
1496 0 : gui->summary.accdb->tile_bytes_read_win [ s ][ i ] = gui->summary.accdb->tile_cur_bytes_read [ s ] - gui->summary.accdb->tile_prev_bytes_read [ s ];
1497 0 : gui->summary.accdb->tile_bytes_copied_win [ s ][ i ] = gui->summary.accdb->tile_cur_bytes_copied [ s ] - gui->summary.accdb->tile_prev_bytes_copied [ s ];
1498 0 : gui->summary.accdb->tile_bytes_written_win [ s ][ i ] = gui->summary.accdb->tile_cur_bytes_written [ s ] - gui->summary.accdb->tile_prev_bytes_written [ s ];
1499 0 : gui->summary.accdb->tile_read_ops_win [ s ][ i ] = gui->summary.accdb->tile_cur_read_ops [ s ] - gui->summary.accdb->tile_prev_read_ops [ s ];
1500 0 : gui->summary.accdb->tile_write_ops_win [ s ][ i ] = gui->summary.accdb->tile_cur_write_ops [ s ] - gui->summary.accdb->tile_prev_write_ops [ s ];
1501 0 : gui->summary.accdb->tile_misses_win [ s ][ i ] = gui->summary.accdb->tile_cur_misses [ s ] - gui->summary.accdb->tile_prev_misses [ s ];
1502 0 : gui->summary.accdb->tile_evicted_win [ s ][ i ] = gui->summary.accdb->tile_cur_evicted [ s ] - gui->summary.accdb->tile_prev_evicted [ s ];
1503 0 : gui->summary.accdb->tile_committed_win [ s ][ i ] = gui->summary.accdb->tile_cur_committed [ s ] - gui->summary.accdb->tile_prev_committed [ s ];
1504 0 : gui->summary.accdb->tile_acquire_calls_win [ s ][ i ] = gui->summary.accdb->tile_cur_acquire_calls [ s ] - gui->summary.accdb->tile_prev_acquire_calls [ s ];
1505 :
1506 0 : gui->summary.accdb->tile_prev_acquired [ s ] = gui->summary.accdb->tile_cur_acquired [ s ];
1507 0 : gui->summary.accdb->tile_prev_acquired_writable[ s ] = gui->summary.accdb->tile_cur_acquired_writable[ s ];
1508 0 : gui->summary.accdb->tile_prev_bytes_read [ s ] = gui->summary.accdb->tile_cur_bytes_read [ s ];
1509 0 : gui->summary.accdb->tile_prev_bytes_copied [ s ] = gui->summary.accdb->tile_cur_bytes_copied [ s ];
1510 0 : gui->summary.accdb->tile_prev_bytes_written [ s ] = gui->summary.accdb->tile_cur_bytes_written [ s ];
1511 0 : gui->summary.accdb->tile_prev_read_ops [ s ] = gui->summary.accdb->tile_cur_read_ops [ s ];
1512 0 : gui->summary.accdb->tile_prev_write_ops [ s ] = gui->summary.accdb->tile_cur_write_ops [ s ];
1513 0 : gui->summary.accdb->tile_prev_misses [ s ] = gui->summary.accdb->tile_cur_misses [ s ];
1514 0 : gui->summary.accdb->tile_prev_evicted [ s ] = gui->summary.accdb->tile_cur_evicted [ s ];
1515 0 : gui->summary.accdb->tile_prev_committed [ s ] = gui->summary.accdb->tile_cur_committed [ s ];
1516 0 : gui->summary.accdb->tile_prev_acquire_calls [ s ] = gui->summary.accdb->tile_cur_acquire_calls [ s ];
1517 :
1518 : /* 60s sparkline accumulator. Sum this snap's delta into the
1519 : in-flight bucket; when the bucket closes (>=250ms since it
1520 : opened), append its per-second rate to the history text and
1521 : start a new bucket with the leftover delta. */
1522 0 : ulong d_acq = gui->summary.accdb->tile_acquired_win [ s ][ i ];
1523 0 : ulong d_acq_wr = gui->summary.accdb->tile_acquired_writable_win[ s ][ i ];
1524 0 : gui->summary.accdb->tile_sparkline_acq_bucket [ s ] += d_acq;
1525 0 : gui->summary.accdb->tile_sparkline_acq_wr_bucket[ s ] += d_acq_wr;
1526 :
1527 0 : long bucket_age = cur->sample_time_nanos - gui->summary.accdb->tile_sparkline_bucket_start_nanos[ s ];
1528 0 : if( gui->summary.accdb->tile_sparkline_bucket_start_nanos[ s ]==0L ) {
1529 : /* First snap for this slot — just open a bucket. */
1530 0 : gui->summary.accdb->tile_sparkline_bucket_start_nanos[ s ] = cur->sample_time_nanos;
1531 0 : } else if( bucket_age>=FD_GUI_ACCDB_SPARKLINE_BUCKET_NS ) {
1532 : /* Close the bucket: normalize to per-second, drop the oldest
1533 : sample if full, push. */
1534 0 : double secs = (double)bucket_age / 1e9;
1535 0 : double acq_rate = (double)gui->summary.accdb->tile_sparkline_acq_bucket [ s ] / secs;
1536 0 : double acq_wr_rate = (double)gui->summary.accdb->tile_sparkline_acq_wr_bucket[ s ] / secs;
1537 0 : int full = gui->summary.accdb->tile_sparkline_count[ s ]==FD_GUI_ACCDB_SPARKLINE_SAMPLES;
1538 0 : fd_gui_sparkline_push( gui->summary.accdb->tile_sparkline_acq_text [ s ], &gui->summary.accdb->tile_sparkline_acq_text_len [ s ], acq_rate, full );
1539 0 : fd_gui_sparkline_push( gui->summary.accdb->tile_sparkline_acq_wr_text[ s ], &gui->summary.accdb->tile_sparkline_acq_wr_text_len[ s ], acq_wr_rate, full );
1540 0 : if( !full ) gui->summary.accdb->tile_sparkline_count[ s ]++;
1541 0 : gui->summary.accdb->tile_sparkline_acq_bucket [ s ] = 0UL;
1542 0 : gui->summary.accdb->tile_sparkline_acq_wr_bucket [ s ] = 0UL;
1543 0 : gui->summary.accdb->tile_sparkline_bucket_start_nanos[ s ] = cur->sample_time_nanos;
1544 0 : }
1545 0 : }
1546 :
1547 0 : gui->summary.accdb->accdb_win_idx = (i+1UL) % FD_GUI_ACCDB_WIN_SAMPLES;
1548 0 : if( gui->summary.accdb->accdb_win_count<FD_GUI_ACCDB_WIN_SAMPLES )
1549 0 : gui->summary.accdb->accdb_win_count++;
1550 0 : }
1551 0 : }
1552 :
1553 : void
1554 0 : fd_gui_printf_accounts_stats( fd_gui_t * gui ) {
1555 0 : fd_gui_accounts_stats_t const * cur = gui->summary.accounts_stats_current;
1556 :
1557 : /* Compute weighted denominator once: sum(weight * dt_sec). Newest
1558 : sample has weight n, oldest has weight 1. If unfilled, only the
1559 : count samples are used (so first-snap rate is meaningful). */
1560 0 : ulong n = gui->summary.accdb->accdb_win_count;
1561 0 : double weighted_dt = 0.0;
1562 0 : for( ulong k=0UL; k<n; k++ ) {
1563 : /* k=0 is newest, k=n-1 is oldest; weight = n - k */
1564 0 : double w = (double)(n - k);
1565 : /* idx-1-k in ring */
1566 0 : ulong ri = (gui->summary.accdb->accdb_win_idx + FD_GUI_ACCDB_WIN_SAMPLES - 1UL - k) % FD_GUI_ACCDB_WIN_SAMPLES;
1567 0 : weighted_dt += w * (double)gui->summary.accdb->accdb_win_dt_nanos[ ri ] / 1e9;
1568 0 : }
1569 :
1570 : /* Helper: weighted rate of a delta ring. */
1571 0 : # define WRATE( ring ) ( fd_gui_accdb_weighted_rate( (ring), gui->summary.accdb->accdb_win_idx, n, weighted_dt ) )
1572 :
1573 0 : double agg_acquired_rate = WRATE( gui->summary.accdb->agg_acquired_win );
1574 0 : double agg_acquired_writable_rate = WRATE( gui->summary.accdb->agg_acquired_writable_win );
1575 0 : double agg_bytes_read_rate = WRATE( gui->summary.accdb->agg_bytes_read_win );
1576 0 : double agg_bytes_copied_rate = WRATE( gui->summary.accdb->agg_bytes_copied_win );
1577 0 : double agg_bytes_written_rate = WRATE( gui->summary.accdb->agg_bytes_written_win );
1578 0 : double agg_bytes_written_accdb_rate = WRATE( gui->summary.accdb->agg_bytes_written_accdb_win );
1579 0 : double agg_read_ops_rate = WRATE( gui->summary.accdb->agg_read_ops_win );
1580 0 : double agg_write_ops_rate = WRATE( gui->summary.accdb->agg_write_ops_win );
1581 0 : double agg_relocated_bytes_rate = WRATE( gui->summary.accdb->agg_relocated_bytes_win );
1582 0 : double agg_misses_rate = WRATE( gui->summary.accdb->agg_misses_win );
1583 :
1584 0 : double agg_hit_rate = agg_acquired_rate>0.0
1585 0 : ? fmax( 0.0, 1.0 - agg_misses_rate / agg_acquired_rate )
1586 0 : : 0.0;
1587 :
1588 :
1589 0 : jsonp_open_envelope( gui->http, "accounts", "stats" );
1590 0 : jsonp_open_object( gui->http, "value" );
1591 0 : jsonp_long( gui->http, "sample_time_nanos", cur->sample_time_nanos );
1592 :
1593 0 : jsonp_open_object( gui->http, "disk" );
1594 0 : jsonp_ulong( gui->http, "accounts_total", cur->accounts_total );
1595 0 : jsonp_ulong( gui->http, "accounts_capacity", cur->accounts_capacity );
1596 0 : jsonp_ulong( gui->http, "allocated_bytes", cur->disk_allocated_bytes );
1597 0 : jsonp_ulong( gui->http, "current_bytes", cur->disk_current_bytes );
1598 0 : jsonp_ulong( gui->http, "used_bytes", cur->disk_used_bytes );
1599 0 : jsonp_close_object( gui->http );
1600 :
1601 0 : jsonp_open_object( gui->http, "compaction" );
1602 0 : jsonp_ulong( gui->http, "in_compaction", cur->in_compaction );
1603 0 : if( FD_LIKELY( gui->summary.accdb->next_compaction_partition_idx!=ULONG_MAX ) ) {
1604 0 : jsonp_double( gui->http, "next_compaction_remaining_seconds", gui->summary.accdb->next_compaction_remaining_secs );
1605 0 : jsonp_ulong( gui->http, "next_compaction_partition_idx", gui->summary.accdb->next_compaction_partition_idx );
1606 0 : } else {
1607 0 : jsonp_null( gui->http, "next_compaction_remaining_seconds" );
1608 0 : jsonp_null( gui->http, "next_compaction_partition_idx" );
1609 0 : }
1610 0 : jsonp_ulong( gui->http, "compactions_requested", cur->compactions_requested );
1611 0 : jsonp_ulong( gui->http, "compactions_completed", cur->compactions_completed );
1612 0 : jsonp_ulong( gui->http, "accounts_relocated_bytes", cur->accounts_relocated_bytes );
1613 0 : jsonp_double( gui->http, "relocated_bytes_per_sec", agg_relocated_bytes_rate );
1614 0 : jsonp_close_object( gui->http );
1615 :
1616 0 : ulong cache_size_bytes = 0UL;
1617 0 : ulong gui_tile_idx = fd_topo_find_tile( gui->topo, "gui", 0UL );
1618 0 : if( FD_LIKELY( gui_tile_idx!=ULONG_MAX ) ) {
1619 0 : cache_size_bytes = gui->topo->tiles[ gui_tile_idx ].gui.cache_size_gib * (1UL<<30);
1620 0 : }
1621 :
1622 0 : jsonp_open_object( gui->http, "cache" );
1623 0 : jsonp_double_4dp( gui->http, "hit_rate_ema", agg_hit_rate );
1624 0 : jsonp_ulong( gui->http, "size_bytes", cache_size_bytes );
1625 :
1626 0 : jsonp_open_array( gui->http, "classes" );
1627 0 : for( ulong c=0UL; c<FD_ACCDB_CACHE_CLASS_CNT; c++ ) {
1628 0 : jsonp_open_object( gui->http, NULL );
1629 0 : jsonp_ulong( gui->http, "class", c );
1630 0 : jsonp_ulong( gui->http, "used_slots", cur->cache_class_used [ c ] );
1631 0 : jsonp_ulong( gui->http, "max_slots", cur->cache_class_max [ c ] );
1632 0 : jsonp_ulong( gui->http, "reserved_slots", cur->cache_class_reserved [ c ] );
1633 0 : jsonp_ulong( gui->http, "target_used_slots", cur->cache_class_target_used [ c ] );
1634 0 : jsonp_ulong( gui->http, "low_water_used_slots", cur->cache_class_low_water_used [ c ] );
1635 0 : jsonp_ulong( gui->http, "not_found", cur->not_found_per_class [ c ] );
1636 0 : jsonp_ulong( gui->http, "evicted", cur->evicted_per_class [ c ] );
1637 0 : jsonp_ulong( gui->http, "preevicted", cur->preevicted_per_class [ c ] );
1638 0 : jsonp_ulong( gui->http, "committed_new", cur->committed_new_per_class [ c ] );
1639 0 : jsonp_ulong( gui->http, "committed_overwrite", cur->committed_overwrite_per_class [ c ] );
1640 0 : double acq_rate = WRATE( gui->summary.accdb->class_acq_win [ c ] );
1641 0 : double acq_wr_rate = WRATE( gui->summary.accdb->class_acq_wr_win [ c ] );
1642 0 : double nf_rate = WRATE( gui->summary.accdb->class_not_found_win [ c ] );
1643 0 : jsonp_double( gui->http, "not_found_per_sec", nf_rate );
1644 0 : jsonp_double( gui->http, "evicted_per_sec", WRATE( gui->summary.accdb->class_evicted_win [ c ] ) );
1645 0 : jsonp_double( gui->http, "preevicted_per_sec", WRATE( gui->summary.accdb->class_preevicted_win [ c ] ) );
1646 0 : jsonp_double( gui->http, "committed_new_per_sec", WRATE( gui->summary.accdb->class_commit_new_win [ c ] ) );
1647 0 : jsonp_double( gui->http, "committed_overwrite_per_sec", WRATE( gui->summary.accdb->class_commit_over_win[ c ] ) );
1648 : /* reads_per_sec = acquired - acquired_writable (per class);
1649 : writes_per_sec = acquired_writable (per class). */
1650 0 : jsonp_double( gui->http, "reads_per_sec", fmax( 0.0, acq_rate - acq_wr_rate ) );
1651 0 : jsonp_double( gui->http, "writes_per_sec", acq_wr_rate );
1652 0 : jsonp_double_4dp( gui->http, "hit_rate_ema", acq_rate>0.0 ? fmax( 0.0, 1.0 - nf_rate / acq_rate ) : 0.0 );
1653 0 : jsonp_close_object( gui->http );
1654 0 : }
1655 0 : jsonp_close_array( gui->http );
1656 0 : jsonp_close_object( gui->http );
1657 :
1658 : /* Per-tile breakdown. Iterate the slot table built at init. */
1659 0 : jsonp_open_array( gui->http, "tiles" );
1660 0 : for( ulong s=0UL; s<gui->summary.accdb->accdb_tile_cnt; s++ ) {
1661 0 : ulong t_idx = (ulong)gui->summary.accdb->accdb_tile_topo_idx[ s ];
1662 0 : fd_topo_tile_t const * tile = &gui->topo->tiles[ t_idx ];
1663 0 : uchar kind = gui->summary.accdb->accdb_tile_kind[ s ];
1664 :
1665 0 : if( kind==FD_GUI_ACCDB_TILE_KIND_SNAPIN && gui->summary.accdb->tile_cur_status[ s ]==2U ) continue;
1666 :
1667 0 : double t_acq_rate = WRATE( gui->summary.accdb->tile_acquired_win [ s ] );
1668 0 : double t_acq_wr_rate = WRATE( gui->summary.accdb->tile_acquired_writable_win[ s ] );
1669 0 : double t_br_rate = WRATE( gui->summary.accdb->tile_bytes_read_win [ s ] );
1670 0 : double t_bc_rate = WRATE( gui->summary.accdb->tile_bytes_copied_win [ s ] );
1671 0 : double t_bw_rate = WRATE( gui->summary.accdb->tile_bytes_written_win [ s ] );
1672 0 : double t_ro_rate = WRATE( gui->summary.accdb->tile_read_ops_win [ s ] );
1673 0 : double t_wo_rate = WRATE( gui->summary.accdb->tile_write_ops_win [ s ] );
1674 0 : double t_nf_rate = WRATE( gui->summary.accdb->tile_misses_win [ s ] );
1675 0 : double t_ev_rate = WRATE( gui->summary.accdb->tile_evicted_win [ s ] );
1676 0 : double t_cm_rate = WRATE( gui->summary.accdb->tile_committed_win [ s ] );
1677 0 : double t_ac_rate = WRATE( gui->summary.accdb->tile_acquire_calls_win [ s ] );
1678 :
1679 0 : char const * joiner;
1680 0 : switch( kind ) {
1681 0 : case FD_GUI_ACCDB_TILE_KIND_RW: joiner = "RW"; break;
1682 0 : case FD_GUI_ACCDB_TILE_KIND_SNAPIN: joiner = "RW"; break;
1683 0 : case FD_GUI_ACCDB_TILE_KIND_ACCDB: joiner = "RW"; break;
1684 0 : default: joiner = "RO"; break;
1685 0 : }
1686 :
1687 0 : jsonp_open_object( gui->http, NULL );
1688 0 : jsonp_string( gui->http, "name", tile->name );
1689 0 : jsonp_ulong( gui->http, "kind_id", tile->kind_id );
1690 0 : jsonp_string( gui->http, "joiner_type", joiner );
1691 0 : jsonp_ulong( gui->http, "status", (ulong)gui->summary.accdb->tile_cur_status[ s ] );
1692 :
1693 : /* Lifetime totals. */
1694 0 : jsonp_ulong( gui->http, "acquired", gui->summary.accdb->tile_cur_acquired [ s ] );
1695 0 : jsonp_ulong( gui->http, "bytes_read", gui->summary.accdb->tile_cur_bytes_read [ s ] );
1696 0 : jsonp_ulong( gui->http, "bytes_written", gui->summary.accdb->tile_cur_bytes_written [ s ] );
1697 :
1698 : /* Rates. */
1699 0 : jsonp_double( gui->http, "acquired_per_sec", t_acq_rate );
1700 0 : jsonp_double( gui->http, "acquired_writable_per_sec", t_acq_wr_rate );
1701 0 : jsonp_double( gui->http, "bytes_read_per_sec", t_br_rate );
1702 0 : jsonp_double( gui->http, "bytes_copied_per_sec", t_bc_rate );
1703 0 : jsonp_double( gui->http, "bytes_written_per_sec", t_bw_rate );
1704 0 : jsonp_double( gui->http, "read_ops_per_sec", t_ro_rate );
1705 0 : jsonp_double( gui->http, "write_ops_per_sec", t_wo_rate );
1706 0 : jsonp_double( gui->http, "not_found_per_sec", t_nf_rate );
1707 0 : jsonp_double( gui->http, "evicted_per_sec", t_ev_rate );
1708 0 : jsonp_double( gui->http, "committed_per_sec", t_cm_rate );
1709 0 : jsonp_double( gui->http, "acquire_calls_per_sec", t_ac_rate );
1710 :
1711 0 : jsonp_double_4dp( gui->http, "hit_rate_ema", t_acq_rate>0.0 ? fmax( 0.0, 1.0 - t_nf_rate / t_acq_rate ) : 0.0 );
1712 :
1713 : /* 60-second sparkline history, oldest first so the frontend
1714 : treats index 0 as the leftmost (oldest) sample. The text
1715 : is kept formatted; copy it. */
1716 0 : jsonp_open_array( gui->http, "acquired_history" );
1717 0 : jsonp_text( gui->http, gui->summary.accdb->tile_sparkline_acq_text[ s ], gui->summary.accdb->tile_sparkline_acq_text_len[ s ] );
1718 0 : jsonp_close_array( gui->http );
1719 0 : jsonp_open_array( gui->http, "acquired_writable_history" );
1720 0 : jsonp_text( gui->http, gui->summary.accdb->tile_sparkline_acq_wr_text[ s ], gui->summary.accdb->tile_sparkline_acq_wr_text_len[ s ] );
1721 0 : jsonp_close_array( gui->http );
1722 0 : jsonp_close_object( gui->http );
1723 0 : }
1724 0 : jsonp_close_array( gui->http );
1725 :
1726 0 : jsonp_open_object( gui->http, "io" );
1727 0 : jsonp_ulong( gui->http, "acquired", cur->acquired );
1728 0 : jsonp_ulong( gui->http, "acquired_writable", cur->acquired_writable );
1729 0 : jsonp_ulong( gui->http, "bytes_read", cur->bytes_read );
1730 0 : jsonp_ulong( gui->http, "bytes_copied", cur->bytes_copied );
1731 0 : jsonp_ulong( gui->http, "bytes_written", cur->bytes_written );
1732 0 : jsonp_ulong( gui->http, "bytes_written_accdb", cur->bytes_written_accdb );
1733 0 : jsonp_ulong( gui->http, "read_ops", cur->read_ops );
1734 0 : jsonp_ulong( gui->http, "write_ops", cur->write_ops );
1735 0 : jsonp_double( gui->http, "acquired_per_sec", agg_acquired_rate );
1736 0 : jsonp_double( gui->http, "acquired_writable_per_sec", agg_acquired_writable_rate );
1737 0 : jsonp_double( gui->http, "bytes_read_per_sec", agg_bytes_read_rate );
1738 0 : jsonp_double( gui->http, "bytes_copied_per_sec", agg_bytes_copied_rate );
1739 0 : jsonp_double( gui->http, "bytes_written_per_sec", agg_bytes_written_rate );
1740 0 : jsonp_double( gui->http, "read_ops_per_sec", agg_read_ops_rate );
1741 0 : jsonp_double( gui->http, "write_ops_per_sec", agg_write_ops_rate );
1742 : /* Prewrite ratio: fraction of bytes written that came from the
1743 : accdb tile's background work (preevict + compaction).
1744 : cur->bytes_written already includes accdb tile's writes, so
1745 : the ratio is just accdb_rate / bytes_written_rate. */
1746 0 : jsonp_double( gui->http, "prewrite_ratio",
1747 0 : agg_bytes_written_rate>0.0
1748 0 : ? fmin( 1.0, agg_bytes_written_accdb_rate / agg_bytes_written_rate )
1749 0 : : 0.0 );
1750 0 : jsonp_close_object( gui->http );
1751 :
1752 : /* Per-partition table. Only emit partitions that have been
1753 : written to (skip the cold tail of the pool that has never been
1754 : allocated). Ticks are converted to wallclock nanoseconds using
1755 : the GUI tile's locally-measured tick rate, since the accdb hot
1756 : path stamps fd_tickcount() rather than fd_log_wallclock() (no
1757 : syscall on the IO path). */
1758 0 : jsonp_open_array( gui->http, "partitions" );
1759 0 : if( FD_LIKELY( gui->accdb_shmem ) ) {
1760 0 : ulong partition_sz = fd_accdb_shmem_partition_sz( gui->accdb_shmem );
1761 0 : long now_ticks = (long)fd_tickcount();
1762 0 : double tick_per_ns = gui->tick_per_ns;
1763 0 : for( ulong p=0UL; p<gui->summary.accdb->partition_cnt; p++ ) {
1764 0 : fd_accdb_shmem_partition_info_t const * info = &gui->summary.accdb->partitions[ p ];
1765 : /* Skip partitions that have never been written and are not
1766 : currently in any compaction state. */
1767 0 : if( !info->bytes_written && !info->compaction_state ) continue;
1768 :
1769 0 : double read_ops_rate = WRATE( gui->summary.accdb->partition_read_ops_win [ p ] );
1770 0 : double bytes_read_rate = WRATE( gui->summary.accdb->partition_bytes_read_win [ p ] );
1771 0 : double write_ops_rate = WRATE( gui->summary.accdb->partition_write_ops_win [ p ] );
1772 0 : double bytes_written_rate= WRATE( gui->summary.accdb->partition_bytes_written_win[p ] );
1773 :
1774 0 : double age_seconds = 0.0;
1775 0 : double filled_seconds = 0.0;
1776 0 : if( info->created_ticks && tick_per_ns>0.0 ) {
1777 0 : age_seconds = ((double)(now_ticks - info->created_ticks)) / tick_per_ns / 1e9;
1778 0 : if( age_seconds<0.0 ) age_seconds = 0.0;
1779 0 : }
1780 0 : if( info->filled_ticks && tick_per_ns>0.0 ) {
1781 0 : filled_seconds = ((double)(now_ticks - info->filled_ticks )) / tick_per_ns / 1e9;
1782 0 : if( filled_seconds<0.0 ) filled_seconds = 0.0;
1783 0 : }
1784 :
1785 : /* Fully compacted, awaiting reclaim: present as an "Off" tier
1786 : with zeroed utilization so the row visually clears once its
1787 : data has been moved out. */
1788 0 : int is_compacted = info->compaction_state==0 &&
1789 0 : ( ( info->write_offset>0UL &&
1790 0 : info->compaction_offset>=info->write_offset ) ||
1791 0 : ( !info->write_offset && info->bytes_written>0UL ) );
1792 0 : ulong tier = is_compacted ? 255UL : (ulong)info->layer;
1793 0 : double utilization = (!is_compacted && partition_sz)
1794 0 : ? (double)info->write_offset / (double)partition_sz : 0.0;
1795 0 : double fragmentation = (!is_compacted && info->write_offset)
1796 0 : ? (double)info->bytes_freed / (double)info->write_offset : 0.0;
1797 0 : ulong used_bytes = (!is_compacted && info->write_offset > info->bytes_freed)
1798 0 : ? info->write_offset - info->bytes_freed : 0UL;
1799 0 : double used_frac = partition_sz ? (double)used_bytes / (double)partition_sz : 0.0;
1800 0 : double fragmented_frac= (!is_compacted && partition_sz) ? (double)info->bytes_freed / (double)partition_sz : 0.0;
1801 0 : double compaction_frac= (!is_compacted && partition_sz) ? (double)info->compaction_offset / (double)partition_sz : 0.0;
1802 :
1803 0 : jsonp_open_object( gui->http, NULL );
1804 0 : jsonp_ulong( gui->http, "partition_idx", p );
1805 0 : jsonp_ulong( gui->http, "file_offset", info->file_offset );
1806 0 : jsonp_ulong( gui->http, "tier", tier );
1807 0 : jsonp_ulong( gui->http, "write_offset", info->write_offset );
1808 0 : jsonp_ulong( gui->http, "bytes_freed", info->bytes_freed );
1809 0 : jsonp_ulong( gui->http, "read_ops", info->read_ops );
1810 0 : jsonp_ulong( gui->http, "bytes_read", info->bytes_read );
1811 0 : jsonp_ulong( gui->http, "write_ops", info->write_ops );
1812 0 : jsonp_ulong( gui->http, "bytes_written", info->bytes_written );
1813 0 : jsonp_double( gui->http, "read_ops_per_sec", read_ops_rate );
1814 0 : jsonp_double( gui->http, "bytes_read_per_sec", bytes_read_rate );
1815 0 : jsonp_double( gui->http, "write_ops_per_sec", write_ops_rate );
1816 0 : jsonp_double( gui->http, "bytes_written_per_sec",bytes_written_rate );
1817 0 : jsonp_double( gui->http, "utilization", utilization );
1818 0 : jsonp_double( gui->http, "fragmentation", fragmentation );
1819 0 : jsonp_double( gui->http, "used_frac", used_frac );
1820 0 : jsonp_double( gui->http, "fragmented_frac", fragmented_frac );
1821 0 : jsonp_double( gui->http, "compaction_trigger_frac", (double)FD_ACCDB_COMPACTION_THRESHOLD_PCT/100.0 );
1822 0 : jsonp_double( gui->http, "age_seconds", age_seconds );
1823 0 : jsonp_double( gui->http, "filled_seconds", filled_seconds );
1824 0 : jsonp_ulong( gui->http, "compaction_state", (ulong)info->compaction_state );
1825 0 : jsonp_double( gui->http, "compaction_frac", compaction_frac );
1826 0 : jsonp_bool( gui->http, "is_write_head", (int)info->is_write_head );
1827 0 : jsonp_close_object( gui->http );
1828 0 : }
1829 0 : }
1830 0 : jsonp_close_array( gui->http );
1831 :
1832 0 : jsonp_close_object( gui->http );
1833 0 : jsonp_close_envelope( gui->http );
1834 :
1835 0 : # undef WRATE
1836 0 : }
1837 :
1838 : static void
1839 : peers_printf_node( fd_gui_peers_ctx_t * peers,
1840 0 : ulong contact_info_table_idx ) {
1841 0 : fd_gui_peers_node_t * peer = &peers->contact_info_table[ contact_info_table_idx ];
1842 :
1843 0 : jsonp_open_object( peers->http, NULL );
1844 :
1845 0 : char identity_base58[ FD_BASE58_ENCODED_32_SZ ];
1846 0 : fd_base58_encode_32( peer->row.pubkey.uc, NULL, identity_base58 );
1847 0 : jsonp_string( peers->http, "identity_pubkey", identity_base58 );
1848 :
1849 0 : jsonp_open_object( peers->http, "gossip" );
1850 :
1851 0 : char version[ 64UL ];
1852 0 : FD_TEST( fd_gossip_version_cstr( peer->row.contact_info.version.major, peer->row.contact_info.version.minor, peer->row.contact_info.version.patch, version, sizeof( version ) ) );
1853 0 : jsonp_string( peers->http, "version", version );
1854 0 : jsonp_ulong( peers->http, "client_id", peer->row.contact_info.version.client );
1855 0 : jsonp_ulong( peers->http, "feature_set", peer->row.contact_info.version.feature_set );
1856 0 : jsonp_long( peers->http, "wallclock", peer->row.wallclock_nanos );
1857 0 : jsonp_ulong( peers->http, "shred_version", peer->row.contact_info.shred_version );
1858 0 : jsonp_open_object( peers->http, "sockets" );
1859 0 : for( ulong j=0UL; j<FD_GOSSIP_CONTACT_INFO_SOCKET_CNT; j++ ) {
1860 0 : char const * tag;
1861 0 : switch( j ) {
1862 0 : case FD_GOSSIP_CONTACT_INFO_SOCKET_GOSSIP: tag = "gossip"; break;
1863 0 : case FD_GOSSIP_CONTACT_INFO_SOCKET_SERVE_REPAIR_QUIC: tag = "serve_repair_quic"; break;
1864 0 : case FD_GOSSIP_CONTACT_INFO_SOCKET_RPC: tag = "rpc"; break;
1865 0 : case FD_GOSSIP_CONTACT_INFO_SOCKET_RPC_PUBSUB: tag = "rpc_pubsub"; break;
1866 0 : case FD_GOSSIP_CONTACT_INFO_SOCKET_SERVE_REPAIR: tag = "serve_repair"; break;
1867 0 : case FD_GOSSIP_CONTACT_INFO_SOCKET_TPU: tag = "tpu"; break;
1868 0 : case FD_GOSSIP_CONTACT_INFO_SOCKET_TPU_FORWARDS: tag = "tpu_forwards"; break;
1869 0 : case FD_GOSSIP_CONTACT_INFO_SOCKET_TPU_FORWARDS_QUIC: tag = "tpu_forwards_quic"; break;
1870 0 : case FD_GOSSIP_CONTACT_INFO_SOCKET_TPU_QUIC: tag = "tpu_quic"; break;
1871 0 : case FD_GOSSIP_CONTACT_INFO_SOCKET_TPU_VOTE: tag = "tpu_vote"; break;
1872 0 : case FD_GOSSIP_CONTACT_INFO_SOCKET_TVU: tag = "tvu"; break;
1873 0 : case FD_GOSSIP_CONTACT_INFO_SOCKET_TVU_QUIC: tag = "tvu_quic"; break;
1874 0 : case FD_GOSSIP_CONTACT_INFO_SOCKET_TPU_VOTE_QUIC: tag = "tpu_vote_quic"; break;
1875 0 : case FD_GOSSIP_CONTACT_INFO_SOCKET_ALPENGLOW: tag = "alpenglow"; break;
1876 0 : default: tag = "unknown"; break;
1877 0 : }
1878 0 : uint ip4 = peer->row.contact_info.sockets[ j ].is_ipv6 ? 0U : peer->row.contact_info.sockets[ j ].ip4;
1879 0 : ushort port = fd_ushort_bswap( peer->row.contact_info.sockets[ j ].port );
1880 0 : if( FD_LIKELY( !ip4 && !port ) ) continue;
1881 :
1882 0 : char line[ 64 ];
1883 0 : FD_TEST( fd_cstr_printf( line, sizeof( line ), NULL, FD_IP4_ADDR_FMT ":%hu", FD_IP4_ADDR_FMT_ARGS( ip4 ), port ) );
1884 0 : jsonp_string( peers->http, tag, line );
1885 0 : }
1886 0 : jsonp_close_object( peers->http );
1887 :
1888 0 : if( FD_LIKELY( peer->row.country_code_idx!=UCHAR_MAX ) ) {
1889 0 : jsonp_string( peers->http, "country_code", peers->dbip.country_code[ peer->row.country_code_idx ] );
1890 0 : } else {
1891 0 : jsonp_null( peers->http, "country_code" );
1892 0 : }
1893 :
1894 0 : if( FD_LIKELY( peer->row.city_name_idx!=UINT_MAX ) ) {
1895 0 : jsonp_string( peers->http, "city_name", peers->dbip.city_name[ peer->row.city_name_idx ] );
1896 0 : } else {
1897 0 : jsonp_null( peers->http, "city_name" );
1898 0 : }
1899 :
1900 0 : jsonp_close_object( peers->http );
1901 :
1902 0 : if( FD_LIKELY( !peer->row.has_vote_info ) ) {
1903 0 : jsonp_open_array( peers->http, "vote" );
1904 0 : jsonp_close_array( peers->http );
1905 0 : } else {
1906 0 : jsonp_open_array( peers->http, "vote" );
1907 0 : jsonp_open_object( peers->http, NULL );
1908 0 : char vote_account_base58[ FD_BASE58_ENCODED_32_SZ ];
1909 0 : fd_base58_encode_32( peer->row.vote_account.uc, NULL, vote_account_base58 );
1910 0 : jsonp_string( peers->http, "vote_account", vote_account_base58 );
1911 0 : jsonp_ulong_as_str( peers->http, "activated_stake", fd_ulong_if( peer->row.stake==ULONG_MAX, 0UL, peer->row.stake ) );
1912 0 : jsonp_ulong( peers->http, "last_vote", 0UL ); /* todo: deprecate */
1913 0 : jsonp_ulong( peers->http, "epoch_credits", 0UL ); /* todo: deprecate */
1914 0 : jsonp_ulong( peers->http, "commission", 0UL ); /* todo: deprecate */
1915 0 : jsonp_ulong( peers->http, "root_slot", 0UL ); /* todo: deprecate */
1916 0 : jsonp_bool( peers->http, "delinquent", peer->row.delinquent );
1917 0 : jsonp_close_object( peers->http );
1918 0 : jsonp_close_array( peers->http );
1919 0 : }
1920 :
1921 0 : fd_gui_config_parse_info_t * node_info = fd_gui_peers_node_info_map_ele_query( peers->node_info_map, &peer->row.pubkey, NULL, peers->node_info_pool );
1922 0 : if( FD_UNLIKELY( !node_info || node_info->cleared ) ) {
1923 0 : jsonp_string( peers->http, "info", NULL );
1924 0 : } else {
1925 0 : jsonp_open_object( peers->http, "info" );
1926 0 : jsonp_string( peers->http, "name", node_info->name );
1927 0 : jsonp_string( peers->http, "details", node_info->details );
1928 0 : jsonp_string( peers->http, "website", node_info->website );
1929 0 : jsonp_string( peers->http, "icon_url", node_info->icon_uri );
1930 0 : jsonp_string( peers->http, "keybase_username", node_info->keybase_username );
1931 0 : jsonp_close_object( peers->http );
1932 0 : }
1933 :
1934 0 : jsonp_close_object( peers->http );
1935 0 : }
1936 :
1937 : void
1938 : fd_gui_peers_printf_nodes( fd_gui_peers_ctx_t * peers,
1939 : int * actions,
1940 : ulong * idxs,
1941 0 : ulong count ) {
1942 0 : jsonp_open_envelope( peers->http, "peers", "update" );
1943 0 : jsonp_open_object( peers->http, "value" );
1944 0 : jsonp_open_array( peers->http, "add" );
1945 0 : for( ulong i=0UL; i<count; i++ ) if( FD_UNLIKELY( actions[ i ]==FD_GUI_PEERS_NODE_ADD ) ) peers_printf_node( peers, idxs[ i ] );
1946 0 : jsonp_close_array( peers->http );
1947 :
1948 0 : jsonp_open_array( peers->http, "update" );
1949 0 : for( ulong i=0UL; i<count; i++ ) if( FD_UNLIKELY( actions[ i ]==FD_GUI_PEERS_NODE_UPDATE ) ) peers_printf_node( peers, idxs[ i ] );
1950 0 : jsonp_close_array( peers->http );
1951 :
1952 0 : jsonp_open_array( peers->http, "remove" );
1953 0 : for( ulong i=0UL; i<count; i++ ) {
1954 0 : if( FD_UNLIKELY( actions[ i ]==FD_GUI_PEERS_NODE_DELETE ) ) {
1955 0 : jsonp_open_object( peers->http, NULL );
1956 0 : char identity_base58[ FD_BASE58_ENCODED_32_SZ ];
1957 0 : fd_base58_encode_32( peers->contact_info_table[ idxs[ i ] ].row.pubkey.uc, NULL, identity_base58 );
1958 0 : jsonp_string( peers->http, "identity_pubkey", identity_base58 );
1959 0 : jsonp_close_object( peers->http );
1960 0 : }
1961 0 : }
1962 0 : jsonp_close_array( peers->http );
1963 0 : jsonp_close_object( peers->http );
1964 0 : jsonp_close_envelope( peers->http );
1965 0 : }
1966 :
1967 : void
1968 0 : fd_gui_peers_printf_node_all( fd_gui_peers_ctx_t * peers ) {
1969 0 : jsonp_open_envelope( peers->http, "peers", "update" );
1970 0 : jsonp_open_object( peers->http, "value" );
1971 0 : jsonp_open_array( peers->http, "add" );
1972 0 : for( fd_gui_peers_node_pubkey_map_iter_t iter = fd_gui_peers_node_pubkey_map_iter_init( peers->node_pubkey_map, peers->contact_info_table );
1973 0 : !fd_gui_peers_node_pubkey_map_iter_done( iter, peers->node_pubkey_map, peers->contact_info_table );
1974 0 : iter = fd_gui_peers_node_pubkey_map_iter_next( iter, peers->node_pubkey_map, peers->contact_info_table ) ) {
1975 0 : fd_gui_peers_node_t const * node = fd_gui_peers_node_pubkey_map_iter_ele_const( iter, peers->node_pubkey_map, peers->contact_info_table );
1976 0 : peers_printf_node( peers, (ulong)(node-peers->contact_info_table) );
1977 0 : }
1978 0 : jsonp_close_array( peers->http );
1979 0 : jsonp_open_array( peers->http, "update" );
1980 0 : jsonp_close_array( peers->http );
1981 0 : jsonp_open_array( peers->http, "remove" );
1982 0 : jsonp_close_array( peers->http );
1983 0 : jsonp_close_object( peers->http );
1984 0 : jsonp_close_envelope( peers->http );
1985 0 : }
1986 :
1987 : static void
1988 : fd_gui_printf_ts_tile_timers( fd_gui_t * gui,
1989 : long sample_time_nanos,
1990 0 : fd_gui_tile_timers_hist_t const * packed ) {
1991 0 : jsonp_open_object( gui->http, NULL );
1992 0 : jsonp_long_as_str( gui->http, "timestamp_nanos", sample_time_nanos );
1993 0 : jsonp_open_array( gui->http, "tile_timers" );
1994 0 : fd_gui_printf_tile_timers( gui, packed );
1995 0 : jsonp_close_array( gui->http );
1996 0 : jsonp_close_object( gui->http );
1997 0 : }
1998 :
1999 : static int
2000 : fd_gui_load_leader_meta( fd_gui_t * gui,
2001 : ulong _slot,
2002 0 : fd_gui_leader_slot_t * out ) {
2003 0 : memset( out, 0, sizeof(*out) );
2004 0 : if( FD_UNLIKELY( !gui->db ) ) return 0;
2005 0 : fd_gui_leader_slot_t const * rec = fd_gui_hist_kv_get_slot_any( gui, FD_GUI_HIST_LEADER_SLOT, _slot );
2006 0 : if( FD_UNLIKELY( !rec ) ) return 0;
2007 0 : *out = *rec;
2008 0 : return 1;
2009 0 : }
2010 :
2011 : static int
2012 : fd_gui_load_block_hash( fd_gui_t * gui,
2013 : ulong _slot,
2014 0 : fd_hash_t * out ) {
2015 0 : if( FD_UNLIKELY( !gui->db ) ) return 0;
2016 :
2017 0 : fd_gui_slot_t const * replay = fd_gui_hist_kv_get_slot_any( gui, FD_GUI_HIST_SLOT, _slot );
2018 0 : if( FD_LIKELY( replay ) ) {
2019 0 : *out = replay->block_hash;
2020 0 : return 1;
2021 0 : }
2022 :
2023 0 : fd_gui_leader_slot_t const * leader = fd_gui_hist_kv_get_slot_any( gui, FD_GUI_HIST_LEADER_SLOT, _slot );
2024 0 : if( FD_LIKELY( leader ) ) {
2025 0 : *out = leader->block_hash;
2026 0 : return 1;
2027 0 : }
2028 :
2029 0 : return 0;
2030 0 : }
2031 :
2032 : static long
2033 : fd_gui_load_slot_duration( fd_gui_t * gui,
2034 : ulong _slot,
2035 0 : fd_gui_slot_t const * slot ) {
2036 0 : if( FD_UNLIKELY( slot->completed_time==LONG_MAX ) ) return LONG_MAX;
2037 :
2038 0 : long parent_completed_time = LONG_MAX;
2039 0 : fd_gui_slot_t * parent_slot = fd_gui_slot_get_canon( gui, slot->parent_slot );
2040 0 : if( FD_LIKELY( parent_slot ) ) {
2041 0 : parent_completed_time = parent_slot->completed_time;
2042 0 : } else if( FD_LIKELY( gui->db ) ) {
2043 0 : fd_gui_slot_t const * replay = fd_gui_hist_kv_get_slot_any( gui, FD_GUI_HIST_SLOT, _slot );
2044 0 : if( FD_LIKELY( replay ) ) parent_completed_time = replay->parent_completed_time;
2045 0 : }
2046 :
2047 0 : if( FD_UNLIKELY( parent_completed_time==LONG_MAX ) ) return LONG_MAX;
2048 0 : return slot->completed_time - parent_completed_time;
2049 0 : }
2050 :
2051 : static char const *
2052 : fd_gui_slot_level_cstr( fd_gui_t const * gui,
2053 : ulong _slot,
2054 0 : fd_gui_slot_t const * slot ) {
2055 0 : if( FD_UNLIKELY( slot->skip==FD_GUI_SKIP_STATUS_FINALIZED && slot->level>=FD_GUI_SLOT_LEVEL_ROOTED ) ) return "skipped";
2056 0 : if( FD_UNLIKELY( slot->level<FD_GUI_SLOT_LEVEL_OPTIMISTICALLY_CONFIRMED && fd_gui_ag_slot_is_skip_notarized( gui, _slot ) ) ) return "skip_notarized";
2057 0 : if( FD_UNLIKELY( slot->completed_time==LONG_MAX ) ) return "incomplete";
2058 :
2059 0 : switch( slot->level ) {
2060 0 : case FD_GUI_SLOT_LEVEL_INCOMPLETE: return "incomplete";
2061 0 : case FD_GUI_SLOT_LEVEL_COMPLETED: return "completed";
2062 0 : case FD_GUI_SLOT_LEVEL_OPTIMISTICALLY_CONFIRMED: return "notarized";
2063 0 : case FD_GUI_SLOT_LEVEL_ROOTED:
2064 0 : case FD_GUI_SLOT_LEVEL_FINALIZED: return "rooted";
2065 0 : default: return "unknown";
2066 0 : }
2067 0 : }
2068 :
2069 : static uchar
2070 : fd_gui_resolve_slot_voter_state( fd_gui_t * gui,
2071 : ulong _slot,
2072 0 : fd_gui_slot_t const * slot ) {
2073 0 : uchar state = slot->is_voter;
2074 0 : uchar epoch_state = fd_gui_slot_voter_state( gui, _slot );
2075 0 : if( FD_LIKELY( epoch_state!=FD_GUI_IS_VOTER_UNKNOWN ) ) state = epoch_state;
2076 0 : return state;
2077 0 : }
2078 :
2079 : static void
2080 : fd_gui_printf_slot_is_voter( fd_gui_t * gui,
2081 : ulong _slot,
2082 0 : fd_gui_slot_t const * slot ) {
2083 0 : uchar state = fd_gui_resolve_slot_voter_state( gui, _slot, slot );
2084 0 : if( FD_UNLIKELY( state==FD_GUI_IS_VOTER_UNKNOWN ) ) jsonp_null( gui->http, "is_voter" );
2085 0 : else jsonp_bool( gui->http, "is_voter", state==FD_GUI_IS_VOTER_YES );
2086 0 : }
2087 :
2088 : static void
2089 : fd_gui_printf_slot_proofs( fd_gui_t * gui,
2090 0 : fd_gui_slot_t const * slot ) {
2091 0 : switch( slot->notarization_kind ) {
2092 0 : case FD_GUI_AG_NOTAR_REGULAR: jsonp_string( gui->http, "notarization_kind", "regular" ); break;
2093 0 : case FD_GUI_AG_NOTAR_FALLBACK: jsonp_string( gui->http, "notarization_kind", "fallback" ); break;
2094 0 : default: jsonp_null ( gui->http, "notarization_kind" ); break;
2095 0 : }
2096 :
2097 0 : uchar final_kind = slot->finalization_kind;
2098 0 : if( FD_UNLIKELY( final_kind==FD_GUI_AG_FINAL_NONE && slot->skip==FD_GUI_SKIP_STATUS_FINALIZED && slot->level>=FD_GUI_SLOT_LEVEL_ROOTED ) ) final_kind = FD_GUI_AG_FINAL_IMPLICIT;
2099 :
2100 0 : switch( final_kind ) {
2101 0 : case FD_GUI_AG_FINAL_FAST: jsonp_string( gui->http, "finalization_kind", "fast" ); break;
2102 0 : case FD_GUI_AG_FINAL_SLOW: jsonp_string( gui->http, "finalization_kind", "slow" ); break;
2103 0 : case FD_GUI_AG_FINAL_IMPLICIT: jsonp_string( gui->http, "finalization_kind", "implicit" ); break;
2104 0 : default: jsonp_null ( gui->http, "finalization_kind" ); break;
2105 0 : }
2106 :
2107 0 : uchar vote_rewarded = slot->vote_rewarded;
2108 0 : uchar epoch_vote_rewarded = fd_gui_slot_vote_rewarded_state( gui, slot->slot );
2109 0 : if( FD_LIKELY( epoch_vote_rewarded!=FD_GUI_VOTE_REWARDED_UNKNOWN ) ) vote_rewarded = epoch_vote_rewarded;
2110 0 : if( FD_LIKELY( vote_rewarded==FD_GUI_VOTE_REWARDED_UNKNOWN ||
2111 0 : fd_gui_resolve_slot_voter_state( gui, slot->slot, slot )!=FD_GUI_IS_VOTER_YES ) ) {
2112 0 : jsonp_null( gui->http, "vote_rewarded" );
2113 0 : } else {
2114 0 : jsonp_bool( gui->http, "vote_rewarded", vote_rewarded==FD_GUI_VOTE_REWARDED_YES );
2115 0 : }
2116 :
2117 0 : ushort vote_count = slot->vote_count;
2118 0 : ushort epoch_vote_count = fd_gui_slot_vote_count( gui, slot->slot );
2119 0 : if( FD_LIKELY( epoch_vote_count!=USHORT_MAX ) ) vote_count = epoch_vote_count;
2120 0 : if( FD_UNLIKELY( vote_count==USHORT_MAX ) ) jsonp_null ( gui->http, "vote_count" );
2121 0 : else jsonp_ulong( gui->http, "vote_count", vote_count );
2122 0 : }
2123 :
2124 : static void
2125 : fd_gui_printf_slot_txn_counts( fd_gui_t * gui,
2126 0 : fd_gui_slot_t const * slot ) {
2127 0 : if( FD_LIKELY( slot->nonvote_success!=UINT_MAX && slot->vote_success!=UINT_MAX ) ) jsonp_ulong( gui->http, "success_transaction_cnt", (ulong)slot->nonvote_success+(ulong)slot->vote_success );
2128 0 : else jsonp_null ( gui->http, "success_transaction_cnt" );
2129 0 : if( FD_LIKELY( slot->nonvote_failed!=UINT_MAX && slot->vote_failed!=UINT_MAX ) ) jsonp_ulong( gui->http, "failed_transaction_cnt", (ulong)slot->nonvote_failed+(ulong)slot->vote_failed );
2130 0 : else jsonp_null ( gui->http, "failed_transaction_cnt" );
2131 0 : }
2132 :
2133 : void
2134 : fd_gui_printf_missed_vote_history( fd_gui_t * gui,
2135 0 : ulong epoch ) {
2136 0 : jsonp_open_envelope( gui->http, "slot", "missed_vote_history" );
2137 0 : jsonp_open_object( gui->http, "value" );
2138 0 : jsonp_open_array( gui->http, "slot" );
2139 0 : fd_gui_epoch_t const * rec = fd_gui_epoch( gui, epoch );
2140 0 : ulong lo, hi;
2141 0 : if( FD_LIKELY( fd_gui_skipped_history_bounds( gui, rec, &lo, &hi ) ) ) {
2142 0 : ulong run_start = ULONG_MAX;
2143 0 : for( ulong i=lo; i<hi; i++ ) {
2144 0 : int missed = rec->vote_rewarded[ i ]==FD_GUI_VOTE_REWARDED_NO && rec->is_voter[ i ]==FD_GUI_IS_VOTER_YES;
2145 0 : if( FD_UNLIKELY( missed && run_start==ULONG_MAX ) ) run_start = rec->start_slot+i;
2146 0 : if( FD_UNLIKELY( !missed && run_start!=ULONG_MAX ) ) {
2147 0 : jsonp_ulong( gui->http, NULL, run_start );
2148 0 : jsonp_ulong( gui->http, NULL, rec->start_slot+i-1UL );
2149 0 : run_start = ULONG_MAX;
2150 0 : }
2151 0 : }
2152 0 : if( FD_UNLIKELY( run_start!=ULONG_MAX ) ) {
2153 0 : jsonp_ulong( gui->http, NULL, run_start );
2154 0 : jsonp_ulong( gui->http, NULL, rec->start_slot+hi-1UL );
2155 0 : }
2156 0 : }
2157 0 : jsonp_close_array( gui->http );
2158 0 : jsonp_close_object( gui->http );
2159 0 : jsonp_close_envelope( gui->http );
2160 0 : }
2161 :
2162 : void
2163 : fd_gui_printf_slot( fd_gui_t * gui,
2164 : ulong _slot,
2165 0 : fd_gui_slot_t const * slot ) {
2166 0 : char const * level = fd_gui_slot_level_cstr( gui, _slot, slot );
2167 :
2168 0 : long duration_nanos = fd_gui_load_slot_duration( gui, _slot, slot );
2169 :
2170 0 : jsonp_open_envelope( gui->http, "slot", "update" );
2171 0 : jsonp_open_object( gui->http, "value" );
2172 0 : fd_gui_leader_slot_t lmeta[ 1 ];
2173 0 : int have_lmeta = fd_gui_load_leader_meta( gui, _slot, lmeta );
2174 0 : jsonp_open_object( gui->http, "publish" );
2175 0 : jsonp_ulong( gui->http, "slot", _slot );
2176 0 : jsonp_bool( gui->http, "mine", slot->mine );
2177 0 : if( FD_UNLIKELY( slot->vote_slot!=ULONG_MAX ) ) jsonp_ulong( gui->http, "vote_slot", slot->vote_slot );
2178 0 : else jsonp_null( gui->http, "vote_slot" );
2179 0 : fd_gui_printf_slot_is_voter( gui, _slot, slot );
2180 :
2181 0 : if( FD_UNLIKELY( have_lmeta && lmeta->leader_start_time!=LONG_MAX ) ) jsonp_long_as_str( gui->http, "start_timestamp_nanos", lmeta->leader_start_time );
2182 0 : else jsonp_null ( gui->http, "start_timestamp_nanos" );
2183 0 : if( FD_UNLIKELY( have_lmeta && lmeta->leader_end_time!=LONG_MAX ) ) jsonp_long_as_str( gui->http, "target_end_timestamp_nanos", lmeta->leader_end_time );
2184 0 : else jsonp_null ( gui->http, "target_end_timestamp_nanos" );
2185 :
2186 0 : if( FD_UNLIKELY( duration_nanos==LONG_MAX ) ) jsonp_null( gui->http, "duration_nanos" );
2187 0 : else jsonp_long( gui->http, "duration_nanos", duration_nanos );
2188 0 : if( FD_UNLIKELY( slot->completed_time==LONG_MAX ) ) jsonp_null( gui->http, "completed_time_nanos" );
2189 0 : else jsonp_long_as_str( gui->http, "completed_time_nanos", slot->completed_time );
2190 0 : jsonp_string( gui->http, "level", level );
2191 0 : fd_gui_printf_slot_proofs( gui, slot );
2192 0 : fd_gui_printf_slot_txn_counts( gui, slot );
2193 0 : if( FD_UNLIKELY( slot->max_compute_units==UINT_MAX ) ) jsonp_null( gui->http, "max_compute_units" );
2194 0 : else jsonp_ulong( gui->http, "max_compute_units", slot->max_compute_units );
2195 0 : if( FD_UNLIKELY( slot->compute_units==UINT_MAX ) ) jsonp_null( gui->http, "compute_units" );
2196 0 : else jsonp_ulong( gui->http, "compute_units", slot->compute_units );
2197 0 : if( FD_UNLIKELY( slot->shred_cnt==UINT_MAX ) ) jsonp_null( gui->http, "shreds" );
2198 0 : else jsonp_ulong( gui->http, "shreds", slot->shred_cnt );
2199 0 : if( FD_UNLIKELY( slot->transaction_fee==ULONG_MAX ) ) jsonp_null( gui->http, "transaction_fee" );
2200 0 : else jsonp_ulong_as_str( gui->http, "transaction_fee", slot->transaction_fee );
2201 0 : if( FD_UNLIKELY( slot->priority_fee==ULONG_MAX ) ) jsonp_null( gui->http, "priority_fee" );
2202 0 : else jsonp_ulong_as_str( gui->http, "priority_fee", slot->priority_fee );
2203 0 : if( FD_UNLIKELY( slot->tips==ULONG_MAX ) ) jsonp_null( gui->http, "tips" );
2204 0 : else jsonp_ulong_as_str( gui->http, "tips", slot->tips );
2205 0 : jsonp_close_object( gui->http );
2206 0 : jsonp_close_object( gui->http );
2207 0 : jsonp_close_envelope( gui->http );
2208 0 : }
2209 :
2210 : void
2211 : fd_gui_printf_summary_ping( fd_gui_t * gui,
2212 0 : ulong id ) {
2213 0 : jsonp_open_envelope( gui->http, "summary", "ping" );
2214 0 : jsonp_ulong( gui->http, "id", id );
2215 0 : jsonp_null( gui->http, "value" );
2216 0 : jsonp_close_envelope( gui->http );
2217 0 : }
2218 :
2219 : void
2220 : fd_gui_printf_slot_rankings_request( fd_gui_t * gui,
2221 : ulong id,
2222 0 : int mine ) {
2223 0 : fd_gui_epoch_t * rec = fd_gui_current_epoch( gui );
2224 :
2225 0 : fd_gui_slot_rankings_t * rankings = FD_LIKELY( rec )
2226 0 : ? fd_ptr_if( mine, (fd_gui_slot_rankings_t *)rec->my_rankings, (fd_gui_slot_rankings_t *)rec->rankings )
2227 0 : : NULL;
2228 :
2229 0 : jsonp_open_envelope( gui->http, "slot", "query_rankings" );
2230 0 : jsonp_ulong( gui->http, "id", id );
2231 0 : jsonp_open_object( gui->http, "value" );
2232 :
2233 0 : #define OUTPUT_RANKING_ARRAY(field) \
2234 0 : jsonp_open_array( gui->http, "slots_" FD_STRINGIFY(field) ); \
2235 0 : for( ulong i = 0UL; i<fd_ulong_if( rankings==NULL, 0UL, FD_GUI_SLOT_RANKINGS_SZ ); i++ ) { \
2236 0 : if( FD_UNLIKELY( rankings->field[ i ].slot==ULONG_MAX ) ) break; \
2237 0 : jsonp_ulong( gui->http, NULL, rankings->field[ i ].slot ); \
2238 0 : } \
2239 0 : jsonp_close_array( gui->http ); \
2240 0 : jsonp_open_array( gui->http, "vals_" FD_STRINGIFY(field) ); \
2241 0 : for( ulong i = 0UL; i<fd_ulong_if( rankings==NULL, 0UL, FD_GUI_SLOT_RANKINGS_SZ ); i++ ) { \
2242 0 : if( FD_UNLIKELY( rankings->field[ i ].slot==ULONG_MAX ) ) break; \
2243 0 : jsonp_ulong( gui->http, NULL, rankings->field[ i ].value ); \
2244 0 : } \
2245 0 : jsonp_close_array( gui->http )
2246 :
2247 0 : OUTPUT_RANKING_ARRAY( largest_tips );
2248 0 : OUTPUT_RANKING_ARRAY( largest_fees );
2249 0 : OUTPUT_RANKING_ARRAY( largest_rewards );
2250 0 : OUTPUT_RANKING_ARRAY( largest_rewards_per_cu );
2251 0 : OUTPUT_RANKING_ARRAY( largest_duration );
2252 0 : OUTPUT_RANKING_ARRAY( largest_compute_units );
2253 0 : OUTPUT_RANKING_ARRAY( largest_skipped );
2254 0 : OUTPUT_RANKING_ARRAY( smallest_tips );
2255 0 : OUTPUT_RANKING_ARRAY( smallest_fees );
2256 0 : OUTPUT_RANKING_ARRAY( smallest_rewards );
2257 0 : OUTPUT_RANKING_ARRAY( smallest_rewards_per_cu );
2258 0 : OUTPUT_RANKING_ARRAY( smallest_duration );
2259 0 : OUTPUT_RANKING_ARRAY( smallest_compute_units );
2260 0 : OUTPUT_RANKING_ARRAY( smallest_skipped );
2261 :
2262 0 : #undef OUTPUT_RANKING_ARRAY
2263 :
2264 0 : jsonp_close_object( gui->http );
2265 0 : jsonp_close_envelope( gui->http );
2266 0 : }
2267 :
2268 : void
2269 : fd_gui_printf_slot_request( fd_gui_t * gui,
2270 : ulong _slot,
2271 : ulong id,
2272 0 : fd_gui_slot_t const * slot ) {
2273 0 : char const * level = fd_gui_slot_level_cstr( gui, _slot, slot );
2274 :
2275 0 : long duration_nanos = fd_gui_load_slot_duration( gui, _slot, slot );
2276 :
2277 0 : jsonp_open_envelope( gui->http, "slot", "query" );
2278 0 : jsonp_ulong( gui->http, "id", id );
2279 0 : jsonp_open_object( gui->http, "value" );
2280 0 : fd_gui_leader_slot_t lmeta[ 1 ];
2281 0 : int have_lmeta = fd_gui_load_leader_meta( gui, _slot, lmeta );
2282 :
2283 0 : jsonp_open_object( gui->http, "publish" );
2284 0 : jsonp_ulong( gui->http, "slot", _slot );
2285 0 : jsonp_bool( gui->http, "mine", slot->mine );
2286 0 : if( FD_UNLIKELY( slot->vote_slot!=ULONG_MAX ) ) jsonp_ulong( gui->http, "vote_slot", slot->vote_slot );
2287 0 : else jsonp_null( gui->http, "vote_slot" );
2288 0 : fd_gui_printf_slot_is_voter( gui, _slot, slot );
2289 :
2290 0 : if( FD_UNLIKELY( have_lmeta && lmeta->leader_start_time!=LONG_MAX ) ) jsonp_long_as_str( gui->http, "start_timestamp_nanos", lmeta->leader_start_time );
2291 0 : else jsonp_null ( gui->http, "start_timestamp_nanos" );
2292 0 : if( FD_UNLIKELY( have_lmeta && lmeta->leader_end_time!=LONG_MAX ) ) jsonp_long_as_str( gui->http, "target_end_timestamp_nanos", lmeta->leader_end_time );
2293 0 : else jsonp_null ( gui->http, "target_end_timestamp_nanos" );
2294 :
2295 0 : jsonp_string( gui->http, "level", level );
2296 0 : fd_gui_printf_slot_proofs( gui, slot );
2297 0 : if( FD_UNLIKELY( duration_nanos==LONG_MAX ) ) jsonp_null( gui->http, "duration_nanos" );
2298 0 : else jsonp_long( gui->http, "duration_nanos", duration_nanos );
2299 0 : if( FD_UNLIKELY( slot->completed_time==LONG_MAX ) ) jsonp_null( gui->http, "completed_time_nanos" );
2300 0 : else jsonp_long_as_str( gui->http, "completed_time_nanos", slot->completed_time );
2301 0 : fd_gui_printf_slot_txn_counts( gui, slot );
2302 0 : if( FD_UNLIKELY( slot->max_compute_units==UINT_MAX ) ) jsonp_null( gui->http, "max_compute_units" );
2303 0 : else jsonp_ulong( gui->http, "max_compute_units", slot->max_compute_units );
2304 0 : if( FD_UNLIKELY( slot->compute_units==UINT_MAX ) ) jsonp_null( gui->http, "compute_units" );
2305 0 : else jsonp_ulong( gui->http, "compute_units", slot->compute_units );
2306 0 : if( FD_UNLIKELY( slot->shred_cnt==UINT_MAX ) ) jsonp_null( gui->http, "shreds" );
2307 0 : else jsonp_ulong( gui->http, "shreds", slot->shred_cnt );
2308 0 : if( FD_UNLIKELY( slot->transaction_fee==ULONG_MAX ) ) jsonp_null( gui->http, "transaction_fee" );
2309 0 : else jsonp_ulong_as_str( gui->http, "transaction_fee", slot->transaction_fee );
2310 0 : if( FD_UNLIKELY( slot->priority_fee==ULONG_MAX ) ) jsonp_null( gui->http, "priority_fee" );
2311 0 : else jsonp_ulong_as_str( gui->http, "priority_fee", slot->priority_fee );
2312 0 : if( FD_UNLIKELY( slot->tips==ULONG_MAX ) ) jsonp_null( gui->http, "tips" );
2313 0 : else jsonp_ulong_as_str( gui->http, "tips", slot->tips );
2314 0 : jsonp_close_object( gui->http );
2315 :
2316 0 : jsonp_close_object( gui->http );
2317 0 : jsonp_close_envelope( gui->http );
2318 0 : }
2319 :
2320 : #define SORT_NAME fd_gui_slot_txn_start_sort
2321 0 : #define SORT_KEY_T fd_gui_store_txn_start_t
2322 0 : #define SORT_BEFORE(a,b) ( (a).txn_idx<(b).txn_idx )
2323 : #include "../../util/tmpl/fd_sort.c"
2324 :
2325 : #define SORT_NAME fd_gui_slot_txn_end_sort
2326 0 : #define SORT_KEY_T fd_gui_store_txn_end_t
2327 0 : #define SORT_BEFORE(a,b) ( (a).txn_idx<(b).txn_idx )
2328 : #include "../../util/tmpl/fd_sort.c"
2329 :
2330 : void
2331 : fd_gui_printf_slot_transactions_request( fd_gui_t * gui,
2332 : ulong _slot,
2333 : ulong id,
2334 0 : fd_gui_slot_t const * slot ) {
2335 0 : char const * level = fd_gui_slot_level_cstr( gui, _slot, slot );
2336 :
2337 0 : long duration_nanos = fd_gui_load_slot_duration( gui, _slot, slot );
2338 :
2339 0 : jsonp_open_envelope( gui->http, "slot", "query_transactions" );
2340 0 : jsonp_ulong( gui->http, "id", id );
2341 0 : jsonp_open_object( gui->http, "value" );
2342 0 : fd_gui_leader_slot_t const * lmeta = slot->bank_seq==ULONG_MAX ? fd_gui_slot_leader_get_any( gui, _slot ) : fd_gui_slot_leader_get( gui, _slot, slot->bank_seq );
2343 0 : int have_lmeta = !!lmeta;
2344 :
2345 0 : jsonp_open_object( gui->http, "publish" );
2346 0 : jsonp_ulong( gui->http, "slot", _slot );
2347 0 : jsonp_bool( gui->http, "mine", slot->mine );
2348 0 : if( FD_UNLIKELY( slot->vote_slot!=ULONG_MAX ) ) jsonp_ulong( gui->http, "vote_slot", slot->vote_slot );
2349 0 : else jsonp_null( gui->http, "vote_slot" );
2350 0 : fd_gui_printf_slot_is_voter( gui, _slot, slot );
2351 :
2352 0 : if( FD_UNLIKELY( have_lmeta && lmeta->leader_start_time!=LONG_MAX ) ) jsonp_long_as_str( gui->http, "start_timestamp_nanos", lmeta->leader_start_time );
2353 0 : else jsonp_null ( gui->http, "start_timestamp_nanos" );
2354 0 : if( FD_UNLIKELY( have_lmeta && lmeta->leader_end_time!=LONG_MAX ) ) jsonp_long_as_str( gui->http, "target_end_timestamp_nanos", lmeta->leader_end_time );
2355 0 : else jsonp_null ( gui->http, "target_end_timestamp_nanos" );
2356 :
2357 0 : jsonp_string( gui->http, "level", level );
2358 0 : fd_gui_printf_slot_proofs( gui, slot );
2359 0 : if( FD_UNLIKELY( duration_nanos==LONG_MAX ) ) jsonp_null( gui->http, "duration_nanos" );
2360 0 : else jsonp_long( gui->http, "duration_nanos", duration_nanos );
2361 0 : if( FD_UNLIKELY( slot->completed_time==LONG_MAX ) ) jsonp_null( gui->http, "completed_time_nanos" );
2362 0 : else jsonp_long_as_str( gui->http, "completed_time_nanos", slot->completed_time );
2363 0 : fd_gui_printf_slot_txn_counts( gui, slot );
2364 0 : if( FD_UNLIKELY( slot->max_compute_units==UINT_MAX ) ) jsonp_null( gui->http, "max_compute_units" );
2365 0 : else jsonp_ulong( gui->http, "max_compute_units", slot->max_compute_units );
2366 0 : if( FD_UNLIKELY( slot->compute_units==UINT_MAX ) ) jsonp_null( gui->http, "compute_units" );
2367 0 : else jsonp_ulong( gui->http, "compute_units", slot->compute_units );
2368 0 : if( FD_UNLIKELY( slot->shred_cnt==UINT_MAX ) ) jsonp_null( gui->http, "shreds" );
2369 0 : else jsonp_ulong( gui->http, "shreds", slot->shred_cnt );
2370 0 : if( FD_UNLIKELY( slot->transaction_fee==ULONG_MAX ) ) jsonp_null( gui->http, "transaction_fee" );
2371 0 : else jsonp_ulong_as_str( gui->http, "transaction_fee", slot->transaction_fee );
2372 0 : if( FD_UNLIKELY( slot->priority_fee==ULONG_MAX ) ) jsonp_null( gui->http, "priority_fee" );
2373 0 : else jsonp_ulong_as_str( gui->http, "priority_fee", slot->priority_fee );
2374 0 : if( FD_UNLIKELY( slot->tips==ULONG_MAX ) ) jsonp_null( gui->http, "tips" );
2375 0 : else jsonp_ulong_as_str( gui->http, "tips", slot->tips );
2376 0 : jsonp_close_object( gui->http );
2377 :
2378 0 : if( FD_UNLIKELY( have_lmeta && lmeta->unbecame_leader ) ) {
2379 0 : fd_done_packing_t scheduler_stats[ 1 ];
2380 0 : fd_memcpy( scheduler_stats, lmeta->scheduler_stats, sizeof(scheduler_stats) );
2381 :
2382 0 : jsonp_open_object( gui->http, "limits" );
2383 0 : jsonp_ulong( gui->http, "used_total_block_cost", scheduler_stats->limits_usage->block_cost );
2384 0 : jsonp_ulong( gui->http, "used_total_vote_cost", 0UL ); /* deprecated */
2385 0 : jsonp_ulong( gui->http, "used_total_bytes", scheduler_stats->limits_usage->block_data_bytes );
2386 0 : jsonp_ulong( gui->http, "used_total_microblocks", scheduler_stats->limits_usage->microblocks );
2387 0 : jsonp_open_array( gui->http, "used_account_write_costs" );
2388 0 : for( ulong i = 0; i<FD_PACK_TOP_WRITERS_CNT; i++ ) {
2389 0 : if( FD_UNLIKELY( !memcmp( scheduler_stats->limits_usage->top_writers[ i ].key.b, ((fd_pubkey_t){ 0 }).uc, sizeof(fd_pubkey_t) ) ) ) break;
2390 :
2391 0 : jsonp_open_object( gui->http, NULL );
2392 0 : char account_base58[ FD_BASE58_ENCODED_32_SZ ];
2393 0 : fd_base58_encode_32( scheduler_stats->limits_usage->top_writers[ i ].key.b, NULL, account_base58 );
2394 0 : jsonp_string( gui->http, "account", account_base58 );
2395 0 : jsonp_ulong( gui->http, "cost", scheduler_stats->limits_usage->top_writers[ i ].total_cost );
2396 0 : jsonp_close_object( gui->http );
2397 0 : }
2398 0 : jsonp_close_array( gui->http );
2399 :
2400 0 : jsonp_ulong( gui->http, "max_total_block_cost", scheduler_stats->limits->max_cost_per_block );
2401 0 : jsonp_ulong( gui->http, "max_total_vote_cost", 0UL ); /* deprecated */
2402 0 : jsonp_ulong( gui->http, "max_account_write_cost", scheduler_stats->limits->max_write_cost_per_acct );
2403 0 : jsonp_ulong( gui->http, "max_total_bytes", scheduler_stats->limits->max_data_bytes_per_block );
2404 0 : jsonp_ulong( gui->http, "max_total_microblocks", lmeta->max_microblocks );
2405 0 : jsonp_close_object( gui->http );
2406 :
2407 0 : jsonp_open_object( gui->http, "scheduler_stats" );
2408 : /* block_hash is replay-sourced; prefer FD_GUI_HIST_SLOT
2409 : and fall back to the leader meta's own block_hash. */
2410 0 : fd_hash_t block_hash = lmeta->block_hash;
2411 0 : fd_gui_load_block_hash( gui, _slot, &block_hash );
2412 0 : char block_hash_base58[ FD_BASE58_ENCODED_32_SZ ];
2413 0 : fd_base58_encode_32( block_hash.uc, NULL, block_hash_base58 );
2414 0 : jsonp_string( gui->http, "block_hash", block_hash_base58 );
2415 :
2416 0 : switch( scheduler_stats->end_slot_reason ) {
2417 0 : case FD_PACK_END_SLOT_REASON_TIME: {
2418 0 : jsonp_string( gui->http, "end_slot_reason", "timeout" );
2419 0 : break;
2420 0 : }
2421 0 : case FD_PACK_END_SLOT_REASON_MICROBLOCK: {
2422 0 : jsonp_string( gui->http, "end_slot_reason", "microblock_limit" );
2423 0 : break;
2424 0 : }
2425 0 : case FD_PACK_END_SLOT_REASON_ABANDONED: {
2426 0 : jsonp_string( gui->http, "end_slot_reason", "leader_switch" );
2427 0 : break;
2428 0 : }
2429 0 : default: FD_LOG_ERR(( "unreachable" ));
2430 0 : }
2431 0 : jsonp_open_array( gui->http, "slot_schedule_counts" );
2432 0 : for( ulong i = 0; i<FD_METRICS_COUNTER_PACK_TXN_SCHEDULE_ATTEMPTED_CNT; i++ ) jsonp_ulong( gui->http, NULL, scheduler_stats->block_results[ i ] );
2433 0 : jsonp_close_array( gui->http );
2434 0 : jsonp_open_array( gui->http, "end_slot_schedule_counts" );
2435 0 : for( ulong i = 0; i<FD_METRICS_COUNTER_PACK_TXN_SCHEDULE_ATTEMPTED_CNT; i++ ) jsonp_ulong( gui->http, NULL, scheduler_stats->end_block_results[ i ] );
2436 0 : jsonp_close_array( gui->http );
2437 :
2438 0 : fd_pack_smallest_t pending_smallest = *scheduler_stats->pending_smallest;
2439 :
2440 0 : if( FD_LIKELY( pending_smallest.cus!=ULONG_MAX ) ) jsonp_ulong( gui->http, "pending_smallest_cost", pending_smallest.cus );
2441 0 : else jsonp_null( gui->http, "pending_smallest_cost" );
2442 0 : if( FD_LIKELY( pending_smallest.bytes!=ULONG_MAX ) ) jsonp_ulong( gui->http, "pending_smallest_bytes", pending_smallest.bytes );
2443 0 : else jsonp_null( gui->http, "pending_smallest_bytes" );
2444 0 : jsonp_null( gui->http, "pending_vote_smallest_cost" ); /* deprecated */
2445 0 : jsonp_null( gui->http, "pending_vote_smallest_bytes" ); /* deprecated */
2446 0 : jsonp_close_object( gui->http );
2447 :
2448 0 : } else {
2449 0 : jsonp_null( gui->http, "limits" );
2450 0 : jsonp_null( gui->http, "scheduler_stats" );
2451 0 : }
2452 :
2453 0 : int processed_all_microblocks = have_lmeta && lmeta->unbecame_leader &&
2454 0 : lmeta->microblocks_upper_bound!=UINT_MAX &&
2455 0 : lmeta->begin_microblocks==lmeta->end_microblocks &&
2456 0 : lmeta->begin_microblocks==lmeta->microblocks_upper_bound;
2457 :
2458 0 : int have_leader_window = have_lmeta &&
2459 0 : lmeta->leader_start_time!=LONG_MAX &&
2460 0 : lmeta->leader_end_time !=LONG_MAX &&
2461 0 : lmeta->leader_start_time<=lmeta->leader_end_time;
2462 :
2463 0 : fd_gui_store_txn_start_t * starts = gui->slot_txn_scratch.starts;
2464 0 : fd_gui_store_txn_end_t * ends = gui->slot_txn_scratch.ends;
2465 0 : fd_gui_slot_txn_join_t * joined = gui->slot_txn_scratch.joined;
2466 0 : ulong txn_max = gui->slot_txn_scratch.max;
2467 0 : ulong start_cnt = 0UL;
2468 0 : ulong end_cnt = 0UL;
2469 0 : ulong txn_cnt = 0UL;
2470 0 : int have_txns = gui->db && processed_all_microblocks && have_leader_window;
2471 :
2472 0 : if( FD_LIKELY( have_txns && lmeta->txn_insert_time_min_ns<=lmeta->txn_insert_time_max_ns ) ) {
2473 0 : long txn_lo_ns = lmeta->txn_insert_time_min_ns;
2474 0 : long txn_hi_ns = lmeta->txn_insert_time_max_ns;
2475 :
2476 : /* Scan the start half, keeping only this slot's records. */
2477 0 : fd_gui_hist_iter_t it;
2478 0 : if( FD_LIKELY( !fd_gui_hist_range_begin( gui, &it, FD_GUI_HIST_TXN_START, txn_lo_ns, txn_hi_ns, NULL, NULL ) ) ) {
2479 0 : while( fd_gui_hist_range_next( &it ) ) {
2480 0 : fd_gui_store_txn_start_t const * r = (fd_gui_store_txn_start_t const *)it.rec;
2481 0 : if( FD_UNLIKELY( r->slot!=_slot || r->bank_seq!=slot->bank_seq ) ) continue;
2482 0 : if( FD_UNLIKELY( start_cnt>=txn_max ) ) break;
2483 0 : starts[ start_cnt++ ] = *r;
2484 0 : }
2485 0 : fd_gui_hist_range_end( &it );
2486 0 : }
2487 :
2488 : /* Scan the end half, keeping only this slot's records. */
2489 0 : if( FD_LIKELY( !fd_gui_hist_range_begin( gui, &it, FD_GUI_HIST_TXN_END, txn_lo_ns, txn_hi_ns, NULL, NULL ) ) ) {
2490 0 : while( fd_gui_hist_range_next( &it ) ) {
2491 0 : fd_gui_store_txn_end_t const * r = (fd_gui_store_txn_end_t const *)it.rec;
2492 0 : if( FD_UNLIKELY( r->slot!=_slot || r->bank_seq!=slot->bank_seq ) ) continue;
2493 0 : if( FD_UNLIKELY( end_cnt>=txn_max ) ) break;
2494 0 : ends[ end_cnt++ ] = *r;
2495 0 : }
2496 0 : fd_gui_hist_range_end( &it );
2497 0 : }
2498 :
2499 : /* Sort both halves by txn_idx, then merge-walk to join. */
2500 0 : fd_gui_slot_txn_start_sort_inplace( starts, start_cnt );
2501 0 : fd_gui_slot_txn_end_sort_inplace ( ends, end_cnt );
2502 :
2503 0 : ulong si = 0UL;
2504 0 : ulong ei = 0UL;
2505 0 : while( si<start_cnt && ei<end_cnt ) {
2506 0 : ulong s_idx = starts[ si ].txn_idx;
2507 0 : ulong e_idx = ends [ ei ].txn_idx;
2508 0 : if( s_idx<e_idx ) si++;
2509 0 : else if( e_idx<s_idx ) ei++;
2510 0 : else {
2511 0 : joined[ txn_cnt ].start = &starts[ si ];
2512 0 : joined[ txn_cnt ].end = &ends [ ei ];
2513 0 : txn_cnt++;
2514 0 : si++;
2515 0 : ei++;
2516 0 : }
2517 0 : }
2518 0 : }
2519 :
2520 0 : if( FD_LIKELY( have_txns ) ) {
2521 0 : jsonp_open_object( gui->http, "transactions" );
2522 0 : jsonp_long_as_str( gui->http, "start_timestamp_nanos", lmeta->leader_start_time );
2523 0 : jsonp_long_as_str( gui->http, "target_end_timestamp_nanos", lmeta->leader_end_time );
2524 0 : jsonp_open_array( gui->http, "txn_mb_start_timestamps_nanos" );
2525 0 : for( ulong i=0UL; i<txn_cnt; i++) jsonp_long_as_str( gui->http, NULL, joined[ i ].start->microblock_start_ns );
2526 0 : jsonp_close_array( gui->http );
2527 0 : jsonp_open_array( gui->http, "txn_mb_end_timestamps_nanos" );
2528 0 : for( ulong i=0UL; i<txn_cnt; i++) {
2529 0 : long const mb_start = joined[ i ].start->microblock_start_ns;
2530 0 : long const mb_end = fd_long_max( joined[ i ].end->microblock_end_ns, mb_start + 1L );
2531 0 : jsonp_long_as_str( gui->http, NULL, mb_end );
2532 0 : }
2533 0 : jsonp_close_array( gui->http );
2534 0 : jsonp_open_array( gui->http, "txn_compute_units_requested" );
2535 0 : for( ulong i=0UL; i<txn_cnt; i++) jsonp_ulong( gui->http, NULL, joined[ i ].start->compute_units_requested );
2536 0 : jsonp_close_array( gui->http );
2537 0 : jsonp_open_array( gui->http, "txn_compute_units_consumed" );
2538 0 : for( ulong i=0UL; i<txn_cnt; i++) jsonp_ulong( gui->http, NULL, joined[ i ].end->compute_units_consumed );
2539 0 : jsonp_close_array( gui->http );
2540 0 : jsonp_open_array( gui->http, "txn_priority_fee" );
2541 0 : for( ulong i=0UL; i<txn_cnt; i++) jsonp_ulong_as_str( gui->http, NULL, joined[ i ].start->priority_fee );
2542 0 : jsonp_close_array( gui->http );
2543 0 : jsonp_open_array( gui->http, "txn_transaction_fee" );
2544 0 : for( ulong i=0UL; i<txn_cnt; i++) jsonp_ulong_as_str( gui->http, NULL, joined[ i ].start->transaction_fee );
2545 0 : jsonp_close_array( gui->http );
2546 0 : jsonp_open_array( gui->http, "txn_error_code" );
2547 0 : for( ulong i=0UL; i<txn_cnt; i++) jsonp_ulong( gui->http, NULL, joined[ i ].end->error_code );
2548 0 : jsonp_close_array( gui->http );
2549 0 : jsonp_open_array( gui->http, "txn_from_bundle" );
2550 0 : for( ulong i=0UL; i<txn_cnt; i++) jsonp_bool( gui->http, NULL, joined[ i ].start->flags & FD_GUI_TXN_FLAGS_FROM_BUNDLE );
2551 0 : jsonp_close_array( gui->http );
2552 0 : jsonp_open_array( gui->http, "txn_bank_idx" );
2553 0 : for( ulong i=0UL; i<txn_cnt; i++) jsonp_ulong( gui->http, NULL, joined[ i ].end->bank_idx );
2554 0 : jsonp_close_array( gui->http );
2555 0 : jsonp_open_array( gui->http, "txn_check_start_timestamps_nanos" );
2556 0 : for( ulong i=0UL; i<txn_cnt; i++) {
2557 0 : jsonp_long_as_str( gui->http, NULL, joined[ i ].start->microblock_start_ns + (long)joined[ i ].end->txn_ns_dt.check_start );
2558 0 : }
2559 0 : jsonp_close_array( gui->http );
2560 0 : jsonp_open_array( gui->http, "txn_load_start_timestamps_nanos" );
2561 0 : for( ulong i=0UL; i<txn_cnt; i++) {
2562 0 : jsonp_long_as_str( gui->http, NULL, joined[ i ].start->microblock_start_ns + (long)joined[ i ].end->txn_ns_dt.load_start );
2563 0 : }
2564 0 : jsonp_close_array( gui->http );
2565 0 : jsonp_open_array( gui->http, "txn_execute_start_timestamps_nanos" );
2566 0 : for( ulong i=0UL; i<txn_cnt; i++) {
2567 0 : jsonp_long_as_str( gui->http, NULL, joined[ i ].start->microblock_start_ns + (long)joined[ i ].end->txn_ns_dt.exec_start );
2568 0 : }
2569 0 : jsonp_close_array( gui->http );
2570 0 : jsonp_open_array( gui->http, "txn_commit_start_timestamps_nanos" );
2571 0 : for( ulong i=0UL; i<txn_cnt; i++) {
2572 0 : jsonp_long_as_str( gui->http, NULL, joined[ i ].start->microblock_start_ns + (long)joined[ i ].end->txn_ns_dt.commit_start );
2573 0 : }
2574 0 : jsonp_close_array( gui->http );
2575 0 : jsonp_open_array( gui->http, "txn_commit_end_timestamps_nanos" );
2576 0 : for( ulong i=0UL; i<txn_cnt; i++) {
2577 0 : jsonp_long_as_str( gui->http, NULL, joined[ i ].start->microblock_start_ns + (long)joined[ i ].end->txn_ns_dt.commit_end );
2578 0 : }
2579 0 : jsonp_close_array( gui->http );
2580 0 : jsonp_open_array( gui->http, "txn_arrival_timestamps_nanos" );
2581 0 : for( ulong i=0UL; i<txn_cnt; i++) jsonp_long_as_str( gui->http, NULL, joined[ i ].start->timestamp_arrival_nanos );
2582 0 : jsonp_close_array( gui->http );
2583 0 : jsonp_open_array( gui->http, "txn_tips" );
2584 0 : for( ulong i=0UL; i<txn_cnt; i++) jsonp_ulong_as_str( gui->http, NULL, joined[ i ].end->tips );
2585 0 : jsonp_close_array( gui->http );
2586 0 : jsonp_open_array( gui->http, "txn_source_ipv4" );
2587 0 : for( ulong i=0UL; i<txn_cnt; i++) {
2588 0 : char addr[ 64 ];
2589 0 : fd_cstr_printf_check( addr, sizeof(addr), NULL, FD_IP4_ADDR_FMT, FD_IP4_ADDR_FMT_ARGS( joined[ i ].start->source_ipv4 ) );
2590 0 : jsonp_string( gui->http, NULL, addr );
2591 0 : }
2592 0 : jsonp_close_array( gui->http );
2593 0 : jsonp_open_array( gui->http, "txn_source_tpu" );
2594 0 : for( ulong i=0UL; i<txn_cnt; i++) {
2595 0 : switch ( joined[ i ].start->source_tpu ) {
2596 0 : case FD_TXN_M_TPU_SOURCE_QUIC: {
2597 0 : jsonp_string( gui->http, NULL, "quic");
2598 0 : break;
2599 0 : }
2600 0 : case FD_TXN_M_TPU_SOURCE_UDP : {
2601 0 : jsonp_string( gui->http, NULL, "udp");
2602 0 : break;
2603 0 : }
2604 0 : case FD_TXN_M_TPU_SOURCE_GOSSIP: {
2605 0 : jsonp_string( gui->http, NULL, "gossip");
2606 0 : break;
2607 0 : }
2608 0 : case FD_TXN_M_TPU_SOURCE_BUNDLE: {
2609 0 : jsonp_string( gui->http, NULL, "bundle");
2610 0 : break;
2611 0 : }
2612 0 : case FD_TXN_M_TPU_SOURCE_TXSEND: {
2613 0 : jsonp_string( gui->http, NULL, "send");
2614 0 : break;
2615 0 : }
2616 0 : default: FD_LOG_ERR(("unknown tpu"));
2617 0 : }
2618 0 : }
2619 0 : jsonp_close_array( gui->http );
2620 0 : jsonp_open_array( gui->http, "txn_microblock_id" );
2621 0 : for( ulong i=0UL; i<txn_cnt; i++) jsonp_ulong( gui->http, NULL, joined[ i ].start->microblock_idx );
2622 0 : jsonp_close_array( gui->http );
2623 0 : jsonp_open_array( gui->http, "txn_landed" );
2624 0 : for( ulong i=0UL; i<txn_cnt; i++) jsonp_bool( gui->http, NULL, joined[ i ].end->flags & FD_GUI_TXN_FLAGS_LANDED_IN_BLOCK );
2625 0 : jsonp_close_array( gui->http );
2626 0 : jsonp_open_array( gui->http, "txn_signature" );
2627 0 : for( ulong i=0UL; i<txn_cnt; i++) {
2628 0 : FD_BASE58_ENCODE_64_BYTES( joined[ i ].start->signature, encoded_signature );
2629 0 : jsonp_string( gui->http, NULL, encoded_signature );
2630 0 : }
2631 0 : jsonp_close_array( gui->http );
2632 0 : jsonp_close_object( gui->http );
2633 0 : } else {
2634 0 : jsonp_null( gui->http, "transactions" );
2635 0 : }
2636 :
2637 0 : jsonp_close_object( gui->http );
2638 0 : jsonp_close_envelope( gui->http );
2639 0 : }
2640 :
2641 : void
2642 : fd_gui_printf_slot_request_detailed( fd_gui_t * gui,
2643 : ulong _slot,
2644 : ulong id,
2645 0 : fd_gui_slot_t const * slot ) {
2646 0 : char const * level = fd_gui_slot_level_cstr( gui, _slot, slot );
2647 :
2648 0 : long duration_nanos = fd_gui_load_slot_duration( gui, _slot, slot );
2649 :
2650 0 : jsonp_open_envelope( gui->http, "slot", "query_detailed" );
2651 0 : jsonp_ulong( gui->http, "id", id );
2652 0 : jsonp_open_object( gui->http, "value" );
2653 0 : fd_gui_leader_slot_t lmeta[ 1 ];
2654 0 : int have_lmeta = fd_gui_load_leader_meta( gui, _slot, lmeta );
2655 :
2656 0 : jsonp_open_object( gui->http, "publish" );
2657 0 : jsonp_ulong( gui->http, "slot", _slot );
2658 0 : jsonp_bool( gui->http, "mine", slot->mine );
2659 0 : if( FD_UNLIKELY( slot->vote_slot!=ULONG_MAX ) ) jsonp_ulong( gui->http, "vote_slot", slot->vote_slot );
2660 0 : else jsonp_null( gui->http, "vote_slot" );
2661 0 : fd_gui_printf_slot_is_voter( gui, _slot, slot );
2662 :
2663 0 : if( FD_UNLIKELY( have_lmeta && lmeta->leader_start_time!=LONG_MAX ) ) jsonp_long_as_str( gui->http, "start_timestamp_nanos", lmeta->leader_start_time );
2664 0 : else jsonp_null ( gui->http, "start_timestamp_nanos" );
2665 0 : if( FD_UNLIKELY( have_lmeta && lmeta->leader_end_time!=LONG_MAX ) ) jsonp_long_as_str( gui->http, "target_end_timestamp_nanos", lmeta->leader_end_time );
2666 0 : else jsonp_null ( gui->http, "target_end_timestamp_nanos" );
2667 :
2668 0 : jsonp_string( gui->http, "level", level );
2669 0 : fd_gui_printf_slot_proofs( gui, slot );
2670 0 : if( FD_UNLIKELY( duration_nanos==LONG_MAX ) ) jsonp_null( gui->http, "duration_nanos" );
2671 0 : else jsonp_long( gui->http, "duration_nanos", duration_nanos );
2672 0 : if( FD_UNLIKELY( slot->completed_time==LONG_MAX ) ) jsonp_null( gui->http, "completed_time_nanos" );
2673 0 : else jsonp_long_as_str( gui->http, "completed_time_nanos", slot->completed_time );
2674 0 : fd_gui_printf_slot_txn_counts( gui, slot );
2675 0 : if( FD_UNLIKELY( slot->max_compute_units==UINT_MAX ) ) jsonp_null( gui->http, "max_compute_units" );
2676 0 : else jsonp_ulong( gui->http, "max_compute_units", slot->max_compute_units );
2677 0 : if( FD_UNLIKELY( slot->compute_units==UINT_MAX ) ) jsonp_null( gui->http, "compute_units" );
2678 0 : else jsonp_ulong( gui->http, "compute_units", slot->compute_units );
2679 0 : if( FD_UNLIKELY( slot->shred_cnt==UINT_MAX ) ) jsonp_null( gui->http, "shreds" );
2680 0 : else jsonp_ulong( gui->http, "shreds", slot->shred_cnt );
2681 0 : if( FD_UNLIKELY( slot->transaction_fee==ULONG_MAX ) ) jsonp_null( gui->http, "transaction_fee" );
2682 0 : else jsonp_ulong_as_str( gui->http, "transaction_fee", slot->transaction_fee );
2683 0 : if( FD_UNLIKELY( slot->priority_fee==ULONG_MAX ) ) jsonp_null( gui->http, "priority_fee" );
2684 0 : else jsonp_ulong_as_str( gui->http, "priority_fee", slot->priority_fee );
2685 0 : if( FD_UNLIKELY( slot->tips==ULONG_MAX ) ) jsonp_null( gui->http, "tips" );
2686 0 : else jsonp_ulong_as_str( gui->http, "tips", slot->tips );
2687 0 : jsonp_close_object( gui->http );
2688 :
2689 0 : if( FD_LIKELY( gui->summary.slot_tower!=ULONG_MAX && gui->summary.slot_tower>_slot ) ) {
2690 0 : long leader_start_time = have_lmeta ? lmeta->leader_start_time : LONG_MAX;
2691 0 : long leader_end_time = have_lmeta ? lmeta->leader_end_time : LONG_MAX;
2692 0 : int have_window = have_lmeta && gui->db && leader_start_time!=LONG_MAX && leader_end_time!=LONG_MAX;
2693 :
2694 0 : fd_gui_txn_waterfall_t previous[ 1 ];
2695 0 : fd_gui_txn_waterfall_t current [ 1 ];
2696 0 : fd_gui_leader_slot_t const * current_lslot = fd_gui_slot_leader_get( gui, _slot, slot->bank_seq );
2697 0 : int have_waterfall = current_lslot && current_lslot->has_waterfall;
2698 0 : if( FD_LIKELY( have_waterfall ) ) {
2699 0 : fd_memcpy( previous, current_lslot->waterfall_reference, sizeof(previous) );
2700 0 : fd_memcpy( current, current_lslot->waterfall, sizeof(current) );
2701 0 : }
2702 0 : if( FD_LIKELY( have_waterfall ) ) fd_gui_printf_waterfall( gui, previous, current );
2703 0 : else jsonp_null( gui->http, "waterfall" );
2704 :
2705 0 : if( FD_LIKELY( have_window ) ) {
2706 0 : jsonp_open_array( gui->http, "tile_timers" );
2707 0 : fd_gui_hist_iter_t it;
2708 0 : if( FD_LIKELY( !fd_gui_hist_range_begin( gui, &it, FD_GUI_HIST_TILE_TIMERS, leader_start_time, leader_end_time, NULL, NULL ) ) ) {
2709 0 : fd_gui_tile_timers_hist_t sample[ FD_TOPO_MAX_TILES ];
2710 0 : int have_sample = 0;
2711 0 : long cur_sample_ts = LONG_MAX;
2712 0 : while( fd_gui_hist_range_next( &it ) ) {
2713 0 : fd_gui_tile_timers_hist_t const * row = (fd_gui_tile_timers_hist_t const *)it.rec;
2714 0 : if( FD_UNLIKELY( row->sample_time_nanos<leader_start_time || row->sample_time_nanos>leader_end_time ) ) continue;
2715 0 : if( FD_UNLIKELY( row->tile_idx>=FD_TOPO_MAX_TILES ) ) continue;
2716 :
2717 : /* Records are stored already-diffed and self-contained, so
2718 : each distinct sample_time_nanos is emitted directly (no
2719 : diff against the previous sample). */
2720 0 : if( have_sample && row->sample_time_nanos!=cur_sample_ts ) {
2721 0 : fd_gui_printf_ts_tile_timers( gui, cur_sample_ts, sample );
2722 0 : have_sample = 0;
2723 0 : }
2724 0 : cur_sample_ts = row->sample_time_nanos;
2725 0 : sample[ row->tile_idx ] = *row;
2726 0 : have_sample = 1;
2727 0 : }
2728 :
2729 0 : if( have_sample ) fd_gui_printf_ts_tile_timers( gui, cur_sample_ts, sample );
2730 0 : fd_gui_hist_range_end( &it );
2731 0 : }
2732 0 : jsonp_close_array( gui->http );
2733 0 : } else {
2734 0 : jsonp_null( gui->http, "tile_timers" );
2735 0 : }
2736 :
2737 0 : if( FD_LIKELY( have_window ) ) {
2738 : /* Unlike tile timers (which are counters), scheduler counts are a
2739 : gauge and we don't take a diff. */
2740 0 : jsonp_open_array( gui->http, "scheduler_counts" );
2741 0 : fd_gui_hist_iter_t it;
2742 0 : if( FD_LIKELY( !fd_gui_hist_range_begin( gui, &it, FD_GUI_HIST_SCHEDULER_COUNTS, leader_start_time, leader_end_time, NULL, NULL ) ) ) {
2743 0 : while( fd_gui_hist_range_next( &it ) ) {
2744 0 : fd_gui_scheduler_counts_t const * cur = (fd_gui_scheduler_counts_t const *)it.rec;
2745 0 : if( FD_UNLIKELY( cur->sample_time_ns<leader_start_time ||
2746 0 : cur->sample_time_ns>leader_end_time ) ) continue;
2747 0 : ulong regular = cur->regular + cur->votes;
2748 0 : jsonp_open_object( gui->http, NULL );
2749 0 : jsonp_long_as_str( gui->http, "timestamp_nanos", cur->sample_time_ns );
2750 0 : jsonp_ulong ( gui->http, "regular", regular );
2751 0 : jsonp_ulong ( gui->http, "votes", 0UL );
2752 0 : jsonp_ulong ( gui->http, "conflicting", cur->conflicting );
2753 0 : jsonp_ulong ( gui->http, "bundles", cur->bundles );
2754 0 : jsonp_ulong ( gui->http, "next_auction", cur->next_auction );
2755 0 : jsonp_close_object( gui->http );
2756 0 : }
2757 0 : fd_gui_hist_range_end( &it );
2758 0 : }
2759 0 : jsonp_close_array( gui->http );
2760 0 : } else {
2761 0 : jsonp_null( gui->http, "scheduler_counts" );
2762 0 : }
2763 :
2764 0 : int have_tile_stats = 0;
2765 0 : fd_gui_tile_stats_t tile_stats_begin[ 1 ];
2766 0 : fd_gui_tile_stats_t tile_stats_end [ 1 ];
2767 0 : if( FD_LIKELY( have_window ) ) {
2768 0 : fd_gui_hist_iter_t it;
2769 0 : if( FD_LIKELY( !fd_gui_hist_range_begin( gui, &it, FD_GUI_HIST_TILE_STATS, leader_start_time, leader_end_time, NULL, NULL ) ) ) {
2770 0 : while( fd_gui_hist_range_next( &it ) ) {
2771 0 : fd_gui_tile_stats_t cur[ 1 ];
2772 0 : fd_memcpy( cur, it.rec, sizeof(fd_gui_tile_stats_t) );
2773 0 : if( FD_UNLIKELY( cur->sample_time_nanos<leader_start_time || cur->sample_time_nanos>leader_end_time ) ) continue;
2774 0 : if( FD_UNLIKELY( !have_tile_stats ) ) *tile_stats_begin = *cur;
2775 0 : *tile_stats_end = *cur;
2776 0 : have_tile_stats = 1;
2777 0 : }
2778 0 : fd_gui_hist_range_end( &it );
2779 0 : }
2780 0 : }
2781 0 : if( FD_LIKELY( have_tile_stats ) ) fd_gui_printf_tile_stats( gui, tile_stats_begin, tile_stats_end );
2782 0 : else jsonp_null( gui->http, "tile_primary_metric" );
2783 0 : } else {
2784 0 : jsonp_null( gui->http, "waterfall" );
2785 0 : jsonp_null( gui->http, "tile_timers" );
2786 0 : jsonp_null( gui->http, "tile_primary_metric" );
2787 0 : }
2788 :
2789 0 : jsonp_close_object( gui->http );
2790 0 : jsonp_close_envelope( gui->http );
2791 0 : }
2792 :
2793 : void
2794 0 : fd_gui_printf_boot_progress( fd_gui_t * gui ) {
2795 0 : jsonp_open_envelope( gui->http, "summary", "boot_progress" );
2796 0 : jsonp_open_object( gui->http, "value" );
2797 0 : switch( gui->summary.boot_progress.phase ) {
2798 0 : case FD_GUI_BOOT_PROGRESS_TYPE_JOINING_GOSSIP: jsonp_string( gui->http, "phase", "joining_gossip" ); break;
2799 0 : case FD_GUI_BOOT_PROGRESS_TYPE_LOADING_FULL_SNAPSHOT: jsonp_string( gui->http, "phase", "loading_full_snapshot" ); break;
2800 0 : case FD_GUI_BOOT_PROGRESS_TYPE_LOADING_INCREMENTAL_SNAPSHOT: jsonp_string( gui->http, "phase", "loading_incremental_snapshot" ); break;
2801 0 : case FD_GUI_BOOT_PROGRESS_TYPE_WAITING_FOR_SUPERMAJORITY: jsonp_string( gui->http, "phase", "waiting_for_supermajority" ); break;
2802 0 : case FD_GUI_BOOT_PROGRESS_TYPE_CATCHING_UP: jsonp_string( gui->http, "phase", "catching_up" ); break;
2803 0 : case FD_GUI_BOOT_PROGRESS_TYPE_RUNNING: jsonp_string( gui->http, "phase", "running" ); break;
2804 0 : default: FD_LOG_ERR(( "unknown phase %d", gui->summary.boot_progress.phase ));
2805 0 : }
2806 :
2807 0 : if( FD_LIKELY( gui->summary.boot_progress.phase>=FD_GUI_BOOT_PROGRESS_TYPE_WAITING_FOR_SUPERMAJORITY
2808 0 : && gui->summary.boot_progress.boot_target_slot_duration_nanos!=ULONG_MAX ) ) {
2809 0 : jsonp_ulong( gui->http, "boot_target_slot_duration_nanos", gui->summary.boot_progress.boot_target_slot_duration_nanos );
2810 0 : } else {
2811 0 : jsonp_null( gui->http, "boot_target_slot_duration_nanos" );
2812 0 : }
2813 :
2814 0 : jsonp_string( gui->http, "accounts_database_path", gui->summary.accounts_database_path );
2815 0 : jsonp_string( gui->http, "gui_database_path", gui->summary.gui_database_path );
2816 :
2817 0 : jsonp_double( gui->http, "joining_gossip_elapsed_seconds", (double)(gui->summary.boot_progress.joining_gossip_time_nanos - gui->summary.startup_time_nanos) / 1e9 );
2818 :
2819 0 : #define HANDLE_SNAPSHOT_STATE(snapshot_type, snapshot_type_upper) { \
2820 0 : ulong snapshot_idx = FD_GUI_BOOT_PROGRESS_##snapshot_type_upper##_SNAPSHOT_IDX; \
2821 0 : if( FD_LIKELY( gui->summary.boot_progress.phase>=FD_GUI_BOOT_PROGRESS_TYPE_LOADING_##snapshot_type_upper##_SNAPSHOT && gui->summary.boot_progress.loading_snapshot[ snapshot_idx ].slot!=ULONG_MAX )) { \
2822 0 : jsonp_double ( gui->http, "loading_" FD_STRINGIFY(snapshot_type) "_snapshot_elapsed_seconds", (double)(gui->summary.boot_progress.loading_snapshot[ snapshot_idx ].sample_time_nanos - gui->summary.boot_progress.loading_snapshot[ snapshot_idx ].reset_time_nanos) / 1e9 ); \
2823 0 : jsonp_ulong ( gui->http, "loading_" FD_STRINGIFY(snapshot_type) "_snapshot_reset_count", gui->summary.boot_progress.loading_snapshot[ snapshot_idx ].reset_cnt ); \
2824 0 : jsonp_ulong ( gui->http, "loading_" FD_STRINGIFY(snapshot_type) "_snapshot_slot", gui->summary.boot_progress.loading_snapshot[ snapshot_idx ].slot ); \
2825 0 : jsonp_ulong_as_str( gui->http, "loading_" FD_STRINGIFY(snapshot_type) "_snapshot_total_bytes_compressed", gui->summary.boot_progress.loading_snapshot[ snapshot_idx ].total_bytes_compressed ); \
2826 0 : jsonp_ulong_as_str( gui->http, "loading_" FD_STRINGIFY(snapshot_type) "_snapshot_read_bytes_compressed", gui->summary.boot_progress.loading_snapshot[ snapshot_idx ].read_bytes_compressed ); \
2827 0 : jsonp_string ( gui->http, "loading_" FD_STRINGIFY(snapshot_type) "_snapshot_read_path", gui->summary.boot_progress.loading_snapshot[ snapshot_idx ].read_path ); \
2828 0 : jsonp_ulong_as_str( gui->http, "loading_" FD_STRINGIFY(snapshot_type) "_snapshot_decompress_bytes_decompressed", gui->summary.boot_progress.loading_snapshot[ snapshot_idx ].decompress_bytes_decompressed ); \
2829 0 : jsonp_ulong_as_str( gui->http, "loading_" FD_STRINGIFY(snapshot_type) "_snapshot_decompress_bytes_compressed", gui->summary.boot_progress.loading_snapshot[ snapshot_idx ].decompress_bytes_compressed ); \
2830 0 : jsonp_ulong_as_str( gui->http, "loading_" FD_STRINGIFY(snapshot_type) "_snapshot_insert_bytes_decompressed", gui->summary.boot_progress.loading_snapshot[ snapshot_idx ].insert_bytes_decompressed ); \
2831 0 : jsonp_ulong ( gui->http, "loading_" FD_STRINGIFY(snapshot_type) "_snapshot_insert_accounts", gui->summary.boot_progress.loading_snapshot[ snapshot_idx ].insert_accounts_current ); \
2832 0 : } else { \
2833 0 : jsonp_null( gui->http, "loading_" FD_STRINGIFY(snapshot_type) "_snapshot_elapsed_seconds" ); \
2834 0 : jsonp_null( gui->http, "loading_" FD_STRINGIFY(snapshot_type) "_snapshot_reset_count" ); \
2835 0 : jsonp_null( gui->http, "loading_" FD_STRINGIFY(snapshot_type) "_snapshot_slot" ); \
2836 0 : jsonp_null( gui->http, "loading_" FD_STRINGIFY(snapshot_type) "_snapshot_total_bytes_compressed" ); \
2837 0 : jsonp_null( gui->http, "loading_" FD_STRINGIFY(snapshot_type) "_snapshot_read_bytes_compressed" ); \
2838 0 : jsonp_null( gui->http, "loading_" FD_STRINGIFY(snapshot_type) "_snapshot_read_path" ); \
2839 0 : jsonp_null( gui->http, "loading_" FD_STRINGIFY(snapshot_type) "_snapshot_decompress_bytes_decompressed" ); \
2840 0 : jsonp_null( gui->http, "loading_" FD_STRINGIFY(snapshot_type) "_snapshot_decompress_bytes_compressed" ); \
2841 0 : jsonp_null( gui->http, "loading_" FD_STRINGIFY(snapshot_type) "_snapshot_insert_bytes_decompressed" ); \
2842 0 : jsonp_null( gui->http, "loading_" FD_STRINGIFY(snapshot_type) "_snapshot_insert_accounts" ); \
2843 0 : } \
2844 0 : }
2845 :
2846 0 : HANDLE_SNAPSHOT_STATE(full, FULL)
2847 0 : HANDLE_SNAPSHOT_STATE(incremental, INCREMENTAL)
2848 0 : #undef HANDLE_SNAPSHOT_STATE
2849 :
2850 0 : if( FD_LIKELY( gui->summary.wfs_enabled ) ) {
2851 0 : jsonp_string ( gui->http, "wait_for_supermajority_bank_hash", gui->summary.wfs_bank_hash );
2852 0 : char shred_version_str[ 8 ];
2853 0 : FD_TEST( fd_cstr_printf_check( shred_version_str, sizeof(shred_version_str), NULL, "%hu", gui->summary.expected_shred_version ) );
2854 0 : jsonp_string ( gui->http, "wait_for_supermajority_shred_version", shred_version_str );
2855 : /* Only a MATCH boot waits, so NOOP reports null not zeroes. */
2856 0 : if( FD_LIKELY( fd_gui_wfs_mode( gui )==FD_WFS_MODE_MATCH &&
2857 0 : gui->summary.boot_progress.phase>=FD_GUI_BOOT_PROGRESS_TYPE_WAITING_FOR_SUPERMAJORITY ) ) {
2858 0 : jsonp_ulong ( gui->http, "wait_for_supermajority_attempt", gui->summary.boot_progress.wfs_attempt );
2859 0 : jsonp_ulong_as_str( gui->http, "wait_for_supermajority_total_stake", gui->summary.boot_progress.wfs_total_stake );
2860 0 : jsonp_ulong_as_str( gui->http, "wait_for_supermajority_connected_stake", gui->summary.boot_progress.wfs_connected_stake );
2861 0 : jsonp_ulong ( gui->http, "wait_for_supermajority_total_peers", gui->summary.boot_progress.wfs_total_peers );
2862 0 : jsonp_ulong ( gui->http, "wait_for_supermajority_connected_peers", gui->summary.boot_progress.wfs_connected_peers );
2863 0 : } else {
2864 0 : jsonp_null( gui->http, "wait_for_supermajority_attempt" );
2865 0 : jsonp_null( gui->http, "wait_for_supermajority_total_stake" );
2866 0 : jsonp_null( gui->http, "wait_for_supermajority_connected_stake" );
2867 0 : jsonp_null( gui->http, "wait_for_supermajority_total_peers" );
2868 0 : jsonp_null( gui->http, "wait_for_supermajority_connected_peers" );
2869 0 : }
2870 0 : } else {
2871 0 : jsonp_null( gui->http, "wait_for_supermajority_bank_hash" );
2872 0 : jsonp_null( gui->http, "wait_for_supermajority_shred_version" );
2873 0 : jsonp_null( gui->http, "wait_for_supermajority_attempt" );
2874 0 : jsonp_null( gui->http, "wait_for_supermajority_total_stake" );
2875 0 : jsonp_null( gui->http, "wait_for_supermajority_connected_stake" );
2876 0 : jsonp_null( gui->http, "wait_for_supermajority_total_peers" );
2877 0 : jsonp_null( gui->http, "wait_for_supermajority_connected_peers" );
2878 0 : }
2879 :
2880 0 : if( FD_LIKELY( gui->summary.boot_progress.phase>=FD_GUI_BOOT_PROGRESS_TYPE_CATCHING_UP ) ) jsonp_double( gui->http, "catching_up_elapsed_seconds", (double)(gui->summary.boot_progress.catching_up_time_nanos - gui->summary.boot_progress.loading_snapshot[ FD_GUI_BOOT_PROGRESS_INCREMENTAL_SNAPSHOT_IDX ].sample_time_nanos) / 1e9 );
2881 0 : else jsonp_null ( gui->http, "catching_up_elapsed_seconds" );
2882 :
2883 0 : if( FD_LIKELY( gui->summary.boot_progress.phase>=FD_GUI_BOOT_PROGRESS_TYPE_CATCHING_UP
2884 0 : && gui->summary.boot_progress.catching_up_first_replay_slot!=ULONG_MAX ) ) {
2885 0 : jsonp_ulong( gui->http, "catching_up_first_replay_slot", gui->summary.boot_progress.catching_up_first_replay_slot );
2886 0 : } else {
2887 0 : jsonp_null( gui->http, "catching_up_first_replay_slot" );
2888 0 : }
2889 :
2890 0 : jsonp_close_object( gui->http );
2891 0 : jsonp_close_envelope( gui->http );
2892 0 : }
2893 :
2894 : void
2895 0 : fd_gui_printf_snapshot_transfers( fd_gui_t * gui ) {
2896 0 : jsonp_open_envelope( gui->http, "snapshot_server", "transfers" );
2897 0 : jsonp_open_array( gui->http, "value" );
2898 0 : for( ulong i=0UL; i<gui->snapsv.pending_cnt; i++ ) {
2899 0 : fd_gui_snapsv_pending_t const * pending = &gui->snapsv.pending[ i ];
2900 0 : fd_snapsv_msg_snap_t const * msg = &pending->msg;
2901 0 : char client_ip[ FD_IP6_ADDR_CSTR_MAX ];
2902 0 : fd_ip6_addr_cstr( client_ip, &msg->req_ip );
2903 0 : jsonp_open_object( gui->http, NULL );
2904 0 : jsonp_ulong ( gui->http, "tile_idx", msg->key.kind_id );
2905 0 : jsonp_ulong ( gui->http, "conn_idx", msg->key.slot_idx );
2906 0 : jsonp_ulong_as_str( gui->http, "request_seq", msg->key.req_seq );
2907 0 : jsonp_string ( gui->http, "client_ip", client_ip );
2908 0 : jsonp_ulong ( gui->http, "client_port", msg->req_port );
2909 0 : jsonp_ulong ( gui->http, "slot", msg->slot );
2910 0 : jsonp_bool ( gui->http, "incremental", msg->base_slot!=ULONG_MAX );
2911 0 : if( FD_LIKELY( msg->base_slot!=ULONG_MAX ) ) jsonp_ulong( gui->http, "base_slot", msg->base_slot );
2912 0 : else jsonp_null ( gui->http, "base_slot" );
2913 0 : jsonp_ulong_as_str( gui->http, "snapshot_size", msg->snap_sz );
2914 0 : jsonp_ulong_as_str( gui->http, "range_start", msg->req_off );
2915 0 : jsonp_ulong_as_str( gui->http, "range_size", msg->req_sz );
2916 0 : jsonp_ulong_as_str( gui->http, "bytes_sent", msg->req_cur );
2917 0 : jsonp_long_as_str ( gui->http, "start_time_nanos", msg->resp_ts );
2918 0 : jsonp_long_as_str ( gui->http, "sample_time_nanos", pending->sample_time_nanos );
2919 0 : jsonp_long_as_str ( gui->http, "expire_time_nanos", fd_long_sat_add( pending->sample_time_nanos, FD_SNAPSV_SYNC_EXPIRY ) );
2920 0 : jsonp_bool ( gui->http, "closed", pending->closed );
2921 0 : jsonp_close_object( gui->http );
2922 0 : }
2923 0 : jsonp_close_array( gui->http );
2924 0 : jsonp_close_envelope( gui->http );
2925 0 : }
2926 :
2927 : void
2928 : fd_gui_printf_peers_viewport_update( fd_gui_peers_ctx_t * peers,
2929 0 : ulong ws_conn_id ) {
2930 0 : jsonp_open_envelope( peers->http, "gossip", "view_update" );
2931 0 : jsonp_open_object( peers->http, "value" );
2932 0 : jsonp_open_array( peers->http, "changes" );
2933 0 : FD_TEST( peers->scratch.viewport_cnt<=FD_GUI_PEERS_WS_VIEWPORT_MAX_SZ );
2934 :
2935 0 : ulong start_row = peers->client_viewports[ ws_conn_id ].start_row;
2936 0 : for( ulong i=0UL; i<peers->scratch.viewport_cnt; i++ ) {
2937 0 : ulong j = start_row + i;
2938 0 : fd_gui_peers_row_t const * cur = &peers->scratch.viewport[ i ];
2939 0 : fd_gui_peers_row_t const * ref = &peers->scratch.viewport_ref[ i ];
2940 :
2941 : /* This code should be kept in sync with updates to
2942 : fd_gui_peers_live_table */
2943 0 : if( FD_UNLIKELY( cur->stake!=ref->stake ) ) {
2944 0 : jsonp_open_object( peers->http, NULL );
2945 0 : jsonp_ulong ( peers->http, "row_index", j );
2946 0 : jsonp_string( peers->http, "column_name", "Stake" );
2947 :
2948 0 : if( FD_UNLIKELY( cur->stake==ULONG_MAX ) ) jsonp_long ( peers->http, "new_value", -1 );
2949 0 : else jsonp_ulong( peers->http, "new_value", cur->stake );
2950 0 : jsonp_close_object( peers->http );
2951 0 : }
2952 :
2953 0 : if( FD_UNLIKELY( strncmp( cur->name, ref->name, sizeof(ref->name) ) ) ) {
2954 0 : jsonp_open_object( peers->http, NULL );
2955 0 : jsonp_ulong ( peers->http, "row_index", j );
2956 0 : jsonp_string( peers->http, "column_name", "Name" );
2957 0 : jsonp_string( peers->http, "new_value", cur->name );
2958 0 : jsonp_close_object( peers->http );
2959 0 : }
2960 :
2961 0 : if( FD_UNLIKELY( cur->country_code_idx!=ref->country_code_idx ) ) {
2962 0 : jsonp_open_object( peers->http, NULL );
2963 0 : jsonp_ulong ( peers->http, "row_index", j );
2964 0 : jsonp_string( peers->http, "column_name", "Country" );
2965 0 : if( FD_LIKELY( cur->country_code_idx!=UCHAR_MAX ) ) {
2966 0 : jsonp_string( peers->http, "new_value", peers->dbip.country_code[ cur->country_code_idx ] );
2967 0 : } else {
2968 0 : jsonp_null( peers->http, "new_value" );
2969 0 : }
2970 0 : jsonp_close_object( peers->http );
2971 0 : }
2972 :
2973 0 : if( FD_UNLIKELY( memcmp( cur->pubkey.uc, ref->pubkey.uc, 32UL ) ) ) {
2974 0 : jsonp_open_object( peers->http, NULL );
2975 0 : jsonp_ulong ( peers->http, "row_index", j );
2976 0 : jsonp_string( peers->http, "column_name", "Pubkey" );
2977 :
2978 0 : char pubkey_base58[ FD_BASE58_ENCODED_32_SZ ];
2979 0 : fd_base58_encode_32( cur->pubkey.uc, NULL, pubkey_base58 );
2980 0 : jsonp_string( peers->http, "new_value", pubkey_base58 );
2981 0 : jsonp_close_object( peers->http );
2982 0 : }
2983 :
2984 0 : uint ip4_after = cur->contact_info.sockets[ FD_GOSSIP_CONTACT_INFO_SOCKET_GOSSIP ].is_ipv6 ? 0U : cur->contact_info.sockets[ FD_GOSSIP_CONTACT_INFO_SOCKET_GOSSIP ].ip4;
2985 0 : uint ip4_before = ref->contact_info.sockets[ FD_GOSSIP_CONTACT_INFO_SOCKET_GOSSIP ].is_ipv6 ? 0U : ref->contact_info.sockets[ FD_GOSSIP_CONTACT_INFO_SOCKET_GOSSIP ].ip4;
2986 0 : if( FD_UNLIKELY( ip4_after!=ip4_before ) ) {
2987 0 : jsonp_open_object( peers->http, NULL );
2988 0 : jsonp_ulong ( peers->http, "row_index", j );
2989 0 : jsonp_string( peers->http, "column_name", "IP Addr" );
2990 :
2991 0 : char peer_addr[ 16 ]; /* 255.255.255.255 + '\0' */
2992 0 : FD_TEST( fd_cstr_printf_check( peer_addr, sizeof(peer_addr), NULL, FD_IP4_ADDR_FMT, FD_IP4_ADDR_FMT_ARGS( ip4_after ) ) );
2993 0 : jsonp_string( peers->http, "new_value", peer_addr );
2994 0 : jsonp_close_object( peers->http );
2995 0 : }
2996 :
2997 0 : long cur_egress_push_bps = (long)cur->gossip_tx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_TYPE_V_PUSH_IDX ].rate_ema.value;
2998 0 : long ref_egress_push_bps = (long)ref->gossip_tx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_TYPE_V_PUSH_IDX ].rate_ema.value;
2999 0 : long cur_ingress_push_bps = (long)cur->gossvf_rx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_TYPE_V_PUSH_IDX ].rate_ema.value;
3000 0 : long ref_ingress_push_bps = (long)ref->gossvf_rx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_TYPE_V_PUSH_IDX ].rate_ema.value;
3001 0 : long cur_egress_pull_response_bps = (long)cur->gossip_tx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_TYPE_V_PULL_RESPONSE_IDX ].rate_ema.value;
3002 0 : long ref_egress_pull_response_bps = (long)ref->gossip_tx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_TYPE_V_PULL_RESPONSE_IDX ].rate_ema.value;
3003 0 : long cur_ingress_pull_response_bps = (long)cur->gossvf_rx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_TYPE_V_PULL_RESPONSE_IDX ].rate_ema.value;
3004 0 : long ref_ingress_pull_response_bps = (long)ref->gossvf_rx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_TYPE_V_PULL_RESPONSE_IDX ].rate_ema.value;
3005 :
3006 0 : if( FD_UNLIKELY( ref->valid && cur_ingress_pull_response_bps!=ref_ingress_pull_response_bps ) ) {
3007 0 : jsonp_open_object( peers->http, NULL );
3008 0 : jsonp_ulong ( peers->http, "row_index", j );
3009 0 : jsonp_string( peers->http, "column_name", "Ingress Pull" );
3010 0 : jsonp_long ( peers->http, "new_value", cur_ingress_pull_response_bps );
3011 0 : jsonp_close_object( peers->http );
3012 0 : }
3013 :
3014 0 : if( FD_UNLIKELY( ref->valid && cur_ingress_push_bps!=ref_ingress_push_bps ) ) {
3015 0 : jsonp_open_object( peers->http, NULL );
3016 0 : jsonp_ulong ( peers->http, "row_index", j );
3017 0 : jsonp_string( peers->http, "column_name", "Ingress Push" );
3018 0 : jsonp_long ( peers->http, "new_value", cur_ingress_push_bps );
3019 0 : jsonp_close_object( peers->http );
3020 0 : }
3021 :
3022 0 : if( FD_UNLIKELY( ref->valid && cur_egress_pull_response_bps!=ref_egress_pull_response_bps ) ) {
3023 0 : jsonp_open_object( peers->http, NULL );
3024 0 : jsonp_ulong ( peers->http, "row_index", j );
3025 0 : jsonp_string( peers->http, "column_name", "Egress Pull" );
3026 0 : jsonp_long ( peers->http, "new_value", cur_egress_pull_response_bps );
3027 0 : jsonp_close_object( peers->http );
3028 0 : }
3029 :
3030 0 : if( FD_UNLIKELY( ref->valid && cur_egress_push_bps!=ref_egress_push_bps ) ) {
3031 0 : jsonp_open_object( peers->http, NULL );
3032 0 : jsonp_ulong ( peers->http, "row_index", j );
3033 0 : jsonp_string( peers->http, "column_name", "Egress Push" );
3034 0 : jsonp_long ( peers->http, "new_value", cur_egress_push_bps );
3035 0 : jsonp_close_object( peers->http );
3036 0 : }
3037 :
3038 0 : }
3039 0 : jsonp_close_array( peers->http );
3040 0 : jsonp_close_object( peers->http );
3041 0 : jsonp_close_envelope( peers->http );
3042 0 : }
3043 :
3044 : void
3045 : fd_gui_printf_peers_viewport_request( fd_gui_peers_ctx_t * peers,
3046 : char const * key,
3047 : ulong ws_conn_id,
3048 0 : ulong request_id ) {
3049 0 : jsonp_open_envelope( peers->http, "gossip", key );
3050 0 : jsonp_ulong( peers->http, "id", request_id );
3051 0 : jsonp_open_object( peers->http, "value" );
3052 0 : FD_TEST( peers->scratch.viewport_cnt<=FD_GUI_PEERS_WS_VIEWPORT_MAX_SZ );
3053 0 : ulong start_row = peers->client_viewports[ ws_conn_id ].start_row;
3054 0 : for( ulong i=0UL; i<peers->scratch.viewport_cnt; i++ ) {
3055 0 : ulong j = start_row + i;
3056 0 : fd_gui_peers_row_t const * cur = &peers->scratch.viewport[ i ];
3057 :
3058 0 : char row_index_cstr[ 32 ];
3059 0 : FD_TEST( fd_cstr_printf_check( row_index_cstr, sizeof(row_index_cstr), NULL, "%lu", + j ) );
3060 0 : jsonp_open_object( peers->http, row_index_cstr );
3061 : /* This code should be kept in sync with updates to
3062 : fd_gui_peers_live_table */
3063 0 : if( FD_UNLIKELY( cur->stake==ULONG_MAX ) ) jsonp_long ( peers->http, "Stake", -1 );
3064 0 : else jsonp_ulong( peers->http, "Stake", cur->stake );
3065 :
3066 0 : char pubkey_base58[ FD_BASE58_ENCODED_32_SZ ];
3067 0 : fd_base58_encode_32( cur->pubkey.uc, NULL, pubkey_base58 );
3068 0 : jsonp_string( peers->http, "Pubkey", pubkey_base58 );
3069 0 : jsonp_string( peers->http, "Name", cur->name );
3070 0 : if( FD_LIKELY( cur->country_code_idx!=UCHAR_MAX ) ) {
3071 0 : jsonp_string( peers->http, "Country", peers->dbip.country_code[ cur->country_code_idx ] );
3072 0 : } else {
3073 0 : jsonp_null( peers->http, "Country" );
3074 0 : }
3075 :
3076 0 : uint ip4 = cur->contact_info.sockets[ FD_GOSSIP_CONTACT_INFO_SOCKET_GOSSIP ].is_ipv6 ? 0U : cur->contact_info.sockets[ FD_GOSSIP_CONTACT_INFO_SOCKET_GOSSIP ].ip4;
3077 0 : char peer_addr[ 16 ]; /* 255.255.255.255 + '\0' */
3078 0 : FD_TEST( fd_cstr_printf_check( peer_addr, sizeof(peer_addr), NULL, FD_IP4_ADDR_FMT, FD_IP4_ADDR_FMT_ARGS( ip4 ) ) );
3079 0 : jsonp_string( peers->http, "IP Addr", peer_addr );
3080 :
3081 0 : long cur_egress_push_bps = (long)cur->gossip_tx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_TYPE_V_PUSH_IDX ].rate_ema.value;
3082 0 : long cur_ingress_push_bps = (long)cur->gossvf_rx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_TYPE_V_PUSH_IDX ].rate_ema.value;
3083 0 : long cur_egress_pull_response_bps = (long)cur->gossip_tx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_TYPE_V_PULL_RESPONSE_IDX ].rate_ema.value;
3084 0 : long cur_ingress_pull_response_bps = (long)cur->gossvf_rx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_TYPE_V_PULL_RESPONSE_IDX ].rate_ema.value;
3085 :
3086 0 : jsonp_long ( peers->http, "Ingress Pull", cur_ingress_pull_response_bps );
3087 0 : jsonp_long ( peers->http, "Ingress Push", cur_ingress_push_bps );
3088 0 : jsonp_long ( peers->http, "Egress Pull", cur_egress_pull_response_bps );
3089 0 : jsonp_long ( peers->http, "Egress Push", cur_egress_push_bps );
3090 :
3091 0 : jsonp_close_object( peers->http );
3092 0 : }
3093 :
3094 0 : jsonp_close_object( peers->http );
3095 0 : jsonp_close_envelope( peers->http );
3096 0 : }
3097 :
3098 : void
3099 0 : fd_gui_printf_peers_view_resize( fd_gui_peers_ctx_t * peers, ulong sz ) {
3100 0 : jsonp_open_envelope( peers->http, "gossip", "peers_size_update" );
3101 0 : jsonp_ulong( peers->http, "value", sz );
3102 0 : jsonp_close_envelope( peers->http );
3103 0 : }
3104 :
3105 : void
3106 0 : fd_gui_peers_printf_gossip_stats( fd_gui_peers_ctx_t * peers ) {
3107 0 : fd_gui_peers_gossip_stats_t * cur = peers->gossip_stats;
3108 :
3109 0 : jsonp_open_envelope( peers->http, "gossip", "network_stats" );
3110 0 : jsonp_open_object( peers->http, "value" );
3111 :
3112 0 : jsonp_open_object( peers->http, "health" );
3113 0 : jsonp_ulong ( peers->http, "num_push_messages_rx_success", cur->network_health_push_msg_rx_success );
3114 0 : jsonp_ulong ( peers->http, "num_push_messages_rx_failure", cur->network_health_push_msg_rx_failure );
3115 0 : jsonp_ulong ( peers->http, "num_push_entries_rx_success", cur->network_health_push_crds_rx_success );
3116 0 : jsonp_ulong ( peers->http, "num_push_entries_rx_failure", cur->network_health_push_crds_rx_failure );
3117 0 : jsonp_ulong ( peers->http, "num_push_entries_rx_duplicate", cur->network_health_push_crds_rx_duplicate );
3118 0 : jsonp_ulong ( peers->http, "num_pull_response_messages_rx_success", cur->network_health_pull_response_msg_rx_success );
3119 0 : jsonp_ulong ( peers->http, "num_pull_response_messages_rx_failure", cur->network_health_pull_response_msg_rx_failure );
3120 0 : jsonp_ulong ( peers->http, "num_pull_response_entries_rx_success", cur->network_health_pull_response_crds_rx_success );
3121 0 : jsonp_ulong ( peers->http, "num_pull_response_entries_rx_failure", cur->network_health_pull_response_crds_rx_failure );
3122 0 : jsonp_ulong ( peers->http, "num_pull_response_entries_rx_duplicate", cur->network_health_pull_response_crds_rx_duplicate );
3123 0 : jsonp_ulong_as_str( peers->http, "total_stake", cur->network_health_total_stake );
3124 0 : jsonp_ulong ( peers->http, "total_peers", cur->network_health_total_peers );
3125 0 : jsonp_ulong_as_str( peers->http, "connected_stake", cur->network_health_connected_stake );
3126 0 : jsonp_ulong ( peers->http, "connected_staked_peers", cur->network_health_connected_staked_peers );
3127 0 : jsonp_ulong ( peers->http, "connected_unstaked_peers", cur->network_health_connected_unstaked_peers );
3128 0 : jsonp_close_object( peers->http );
3129 :
3130 0 : jsonp_open_object( peers->http, "ingress" );
3131 :
3132 0 : jsonp_open_array( peers->http, "peer_names" );
3133 0 : for( ulong i=0UL; i<cur->network_ingress_peer_sz; i++ ) jsonp_string( peers->http, NULL, cur->network_ingress_peer_names[ i ] );
3134 0 : jsonp_close_array( peers->http );
3135 :
3136 0 : jsonp_open_array( peers->http, "peer_identities" );
3137 0 : for( ulong i=0UL; i<cur->network_ingress_peer_sz; i++ ) {
3138 0 : char identity_base58[ FD_BASE58_ENCODED_32_SZ ];
3139 0 : fd_base58_encode_32( cur->network_ingress_peer_identities[ i ].uc, NULL, identity_base58 );
3140 0 : jsonp_string( peers->http, NULL, identity_base58 );
3141 0 : }
3142 0 : jsonp_close_array( peers->http );
3143 :
3144 0 : jsonp_open_array( peers->http, "peer_throughput" );
3145 0 : for( ulong i=0UL; i<cur->network_ingress_peer_sz; i++ ) jsonp_long( peers->http, NULL, cur->network_ingress_peer_bytes_per_sec[ i ] );
3146 0 : jsonp_close_array( peers->http );
3147 0 : jsonp_long( peers->http, "total_throughput", cur->network_ingress_total_bytes_per_sec );
3148 0 : jsonp_close_object( peers->http );
3149 :
3150 0 : jsonp_open_object( peers->http, "egress" );
3151 0 : jsonp_open_array( peers->http, "peer_names" );
3152 0 : for( ulong i=0UL; i<cur->network_egress_peer_sz; i++ ) jsonp_string( peers->http, NULL, cur->network_egress_peer_names[ i ] );
3153 0 : jsonp_close_array( peers->http );
3154 :
3155 0 : jsonp_open_array( peers->http, "peer_identities" );
3156 0 : for( ulong i=0UL; i<cur->network_egress_peer_sz; i++ ) {
3157 0 : char identity_base58[ FD_BASE58_ENCODED_32_SZ ];
3158 0 : fd_base58_encode_32( cur->network_egress_peer_identities[ i ].uc, NULL, identity_base58 );
3159 0 : jsonp_string( peers->http, NULL, identity_base58 );
3160 0 : }
3161 0 : jsonp_close_array( peers->http );
3162 :
3163 0 : jsonp_open_array( peers->http, "peer_throughput" );
3164 0 : for( ulong i=0UL; i<cur->network_egress_peer_sz; i++ ) jsonp_long( peers->http, NULL, cur->network_egress_peer_bytes_per_sec[ i ] );
3165 0 : jsonp_close_array( peers->http );
3166 0 : jsonp_long( peers->http, "total_throughput", cur->network_egress_total_bytes_per_sec );
3167 0 : jsonp_close_object( peers->http );
3168 :
3169 0 : jsonp_open_object( peers->http, "storage" );
3170 : /* since these are gauges, we don't take a diff */
3171 0 : jsonp_ulong( peers->http, "capacity", cur->storage_capacity );
3172 0 : jsonp_ulong( peers->http, "expired_count", cur->storage_expired_cnt );
3173 0 : jsonp_ulong( peers->http, "evicted_count", cur->storage_evicted_cnt );
3174 0 : jsonp_open_array( peers->http, "count" );
3175 0 : for( ulong i = 0UL; i<FD_METRICS_ENUM_CRDS_VALUE_TYPE_CNT; i++ ) jsonp_ulong( peers->http, NULL, cur->storage_active_cnt[ i ] );
3176 0 : jsonp_close_array( peers->http );
3177 0 : jsonp_open_array( peers->http, "count_tx" );
3178 0 : for( ulong i = 0UL; i<FD_METRICS_ENUM_CRDS_VALUE_TYPE_CNT; i++ ) jsonp_ulong( peers->http, NULL, cur->storage_cnt_tx[ i ] );
3179 0 : jsonp_close_array( peers->http );
3180 0 : jsonp_open_array( peers->http, "bytes_tx" );
3181 0 : for( ulong i = 0UL; i<FD_METRICS_ENUM_CRDS_VALUE_TYPE_CNT; i++ ) jsonp_ulong( peers->http, NULL, cur->storage_bytes_tx[ i ] );
3182 0 : jsonp_close_array( peers->http );
3183 0 : jsonp_close_object( peers->http );
3184 0 : jsonp_open_object( peers->http, "messages" );
3185 0 : jsonp_open_array( peers->http, "num_bytes_rx" );
3186 0 : for( ulong i = 0UL; i<FD_METRICS_ENUM_GOSSIP_MESSAGE_TYPE_CNT; i++ ) jsonp_ulong( peers->http, NULL, cur->messages_bytes_rx[ i ] );
3187 0 : jsonp_close_array( peers->http );
3188 0 : jsonp_open_array( peers->http, "num_bytes_tx" );
3189 0 : for( ulong i = 0UL; i<FD_METRICS_ENUM_GOSSIP_MESSAGE_TYPE_CNT; i++ ) jsonp_ulong( peers->http, NULL, cur->messages_bytes_tx[ i ] );
3190 0 : jsonp_close_array( peers->http );
3191 0 : jsonp_open_array( peers->http, "num_messages_rx" );
3192 0 : for( ulong i = 0UL; i<FD_METRICS_ENUM_GOSSIP_MESSAGE_TYPE_CNT; i++ ) jsonp_ulong( peers->http, NULL, cur->messages_count_rx[ i ] );
3193 0 : jsonp_close_array( peers->http );
3194 0 : jsonp_open_array( peers->http, "num_messages_tx" );
3195 0 : for( ulong i = 0UL; i<FD_METRICS_ENUM_GOSSIP_MESSAGE_TYPE_CNT; i++ ) jsonp_ulong( peers->http, NULL, cur->messages_count_tx[ i ] );
3196 0 : jsonp_close_array( peers->http );
3197 0 : jsonp_close_object( peers->http );
3198 0 : jsonp_close_object( peers->http );
3199 0 : jsonp_close_envelope( peers->http );
3200 0 : }
3201 :
3202 : /* fd_gui_printf_shreds_window prints the shred events inserted in
3203 : [after_ns,before_ns] as columnar arrays: reference values then one
3204 : array per column. The events are decoded from the batches once into
3205 : gui->shred_scratch and printed from there; a window with more events
3206 : than the scratch holds (only possible for a long query_shreds range,
3207 : never the 50 ms broadcast) is decoded once per column instead. The
3208 : two produce the same bytes: both read the same batches in the same
3209 : order (see test_gui_printf). */
3210 :
3211 : static void
3212 0 : fd_gui_printf_shreds_window( fd_gui_t * gui, long after_ns, long before_ns ) {
3213 0 : fd_gui_shred_event_iter_t it[ 1 ];
3214 :
3215 : /* find the min slot / min ts across the window (for delta encoding)
3216 : and decode into the scratch while it fits */
3217 0 : fd_gui_shred_scratch_t * sc = gui->shred_scratch.ev;
3218 0 : ulong sc_max = gui->shred_scratch.max;
3219 0 : ulong cnt = 0UL;
3220 0 : int fits = 1;
3221 0 : ulong min_slot = ULONG_MAX;
3222 0 : long min_ts = LONG_MAX;
3223 0 : fd_gui_shred_event_iter_begin( gui, it, after_ns, before_ns );
3224 0 : while( fd_gui_shred_event_iter_next( it ) ) {
3225 0 : fd_gui_shred_event_t const * e = &it->event;
3226 0 : min_slot = fd_ulong_min( min_slot, e->slot );
3227 0 : min_ts = fd_long_min ( min_ts, e->event_time_ns );
3228 0 : if( FD_LIKELY( cnt<sc_max ) ) sc[ cnt ] = (fd_gui_shred_scratch_t){ .event_time_ns = e->event_time_ns, .slot = e->slot, .idx = e->idx, .event = e->event };
3229 0 : else fits = 0;
3230 0 : cnt++;
3231 0 : }
3232 0 : fd_gui_shred_event_iter_end( it );
3233 :
3234 0 : jsonp_ulong ( gui->http, "reference_slot", min_slot );
3235 0 : jsonp_long_as_str( gui->http, "reference_ts", min_ts );
3236 :
3237 0 : if( FD_LIKELY( fits ) ) {
3238 0 : jsonp_open_array( gui->http, "slot_delta" );
3239 0 : for( ulong i=0UL; i<cnt; i++ ) jsonp_ulong( gui->http, NULL, (ulong)sc[ i ].slot-min_slot );
3240 0 : jsonp_close_array( gui->http );
3241 0 : jsonp_open_array( gui->http, "shred_idx" );
3242 0 : for( ulong i=0UL; i<cnt; i++ ) {
3243 0 : if( FD_LIKELY( sc[ i ].idx!=USHORT_MAX ) ) jsonp_ulong( gui->http, NULL, sc[ i ].idx );
3244 0 : else jsonp_null ( gui->http, NULL );
3245 0 : }
3246 0 : jsonp_close_array( gui->http );
3247 0 : jsonp_open_array( gui->http, "event" );
3248 0 : for( ulong i=0UL; i<cnt; i++ ) jsonp_ulong( gui->http, NULL, sc[ i ].event );
3249 0 : jsonp_close_array( gui->http );
3250 0 : jsonp_open_array( gui->http, "event_ts_delta" );
3251 0 : for( ulong i=0UL; i<cnt; i++ ) jsonp_long_as_str( gui->http, NULL, sc[ i ].event_time_ns-min_ts );
3252 0 : jsonp_close_array( gui->http );
3253 0 : return;
3254 0 : }
3255 :
3256 0 : #define SHREDS_WINDOW_ITER( code ) \
3257 0 : do { \
3258 0 : fd_gui_shred_event_iter_t _it[ 1 ]; \
3259 0 : fd_gui_shred_event_iter_begin( gui, _it, after_ns, before_ns ); \
3260 0 : while( fd_gui_shred_event_iter_next( _it ) ) { \
3261 0 : fd_gui_shred_event_t const * e = &_it->event; (void)e; \
3262 0 : ulong db_event_slot = e->slot; (void)db_event_slot; \
3263 0 : do { code } while(0); \
3264 0 : } \
3265 0 : fd_gui_shred_event_iter_end( _it ); \
3266 0 : } while(0)
3267 :
3268 0 : jsonp_open_array( gui->http, "slot_delta" );
3269 0 : SHREDS_WINDOW_ITER( { jsonp_ulong( gui->http, NULL, db_event_slot-min_slot ); } );
3270 0 : jsonp_close_array( gui->http );
3271 0 : jsonp_open_array( gui->http, "shred_idx" );
3272 0 : SHREDS_WINDOW_ITER({
3273 0 : if( FD_LIKELY( e->idx!=USHORT_MAX ) ) jsonp_ulong( gui->http, NULL, e->idx );
3274 0 : else jsonp_null ( gui->http, NULL );
3275 0 : });
3276 0 : jsonp_close_array( gui->http );
3277 0 : jsonp_open_array( gui->http, "event" );
3278 0 : SHREDS_WINDOW_ITER( { jsonp_ulong( gui->http, NULL, e->event ); } );
3279 0 : jsonp_close_array( gui->http );
3280 0 : jsonp_open_array( gui->http, "event_ts_delta" );
3281 0 : SHREDS_WINDOW_ITER( { jsonp_long_as_str( gui->http, NULL, e->event_time_ns-min_ts ); } );
3282 0 : jsonp_close_array( gui->http );
3283 :
3284 0 : #undef SHREDS_WINDOW_ITER
3285 0 : }
3286 :
3287 : void
3288 0 : fd_gui_printf_shred_updates( fd_gui_t * gui, long after_ns, long before_ns ) {
3289 0 : jsonp_open_envelope( gui->http, "slot", "live_shreds" );
3290 0 : jsonp_open_object( gui->http, "value" );
3291 0 : fd_gui_printf_shreds_window( gui, after_ns, before_ns );
3292 0 : jsonp_close_object( gui->http );
3293 0 : jsonp_close_envelope( gui->http );
3294 0 : }
3295 :
3296 : void
3297 0 : fd_gui_printf_shred_rebroadcast( fd_gui_t * gui, long after, long before ) {
3298 0 : jsonp_open_envelope( gui->http, "slot", "live_shreds" );
3299 0 : jsonp_open_object( gui->http, "value" );
3300 0 : fd_gui_printf_shreds_window( gui, after, before );
3301 0 : jsonp_close_object( gui->http );
3302 0 : jsonp_close_envelope( gui->http );
3303 0 : }
3304 :
3305 : static void
3306 : fd_gui_timeline_limit( fd_gui_t * gui,
3307 : char const * key,
3308 0 : ulong id ) {
3309 0 : jsonp_open_envelope( gui->http, "timeline", key );
3310 0 : jsonp_ulong( gui->http, "id", id );
3311 0 : jsonp_open_object( gui->http, "error" );
3312 0 : jsonp_string( gui->http, "code", "result_limit_exceeded" );
3313 0 : jsonp_close_object( gui->http );
3314 0 : jsonp_close_envelope( gui->http );
3315 0 : }
3316 :
3317 : static int
3318 : fd_gui_timeline_bounds( fd_gui_t * gui,
3319 0 : int dbi ) {
3320 0 : long lo;
3321 0 : long hi;
3322 0 : int have = fd_gui_event_bounds( gui, dbi, &lo, &hi );
3323 0 : if( have && hi>lo ) {
3324 0 : jsonp_long_as_str( gui->http, "available_start_ns", lo );
3325 0 : jsonp_long_as_str( gui->http, "available_end_ns", hi );
3326 0 : } else {
3327 0 : jsonp_null( gui->http, "available_start_ns" );
3328 0 : jsonp_null( gui->http, "available_end_ns" );
3329 0 : }
3330 0 : return have && hi>lo;
3331 0 : }
3332 :
3333 : static int
3334 : fd_gui_timeline_slot_skipped( fd_gui_t * gui,
3335 0 : ulong slot ) {
3336 0 : if( gui->summary.slot_rooted!=ULONG_MAX && slot<gui->summary.slot_rooted ) {
3337 0 : fd_gui_epoch_t const * epoch = fd_gui_get_epoch_by_slot( gui, slot );
3338 0 : if( epoch && slot>=epoch->start_slot && slot-epoch->start_slot<epoch->slot_cnt &&
3339 0 : epoch->skipped[ slot-epoch->start_slot ] ) return 1;
3340 0 : return fd_gui_slot_get_canon_safe( gui, slot )->skip==FD_GUI_SKIP_STATUS_FINALIZED;
3341 0 : }
3342 :
3343 0 : return 0;
3344 0 : }
3345 :
3346 : #define SORT_NAME fd_gui_timeline_event_sort
3347 0 : #define SORT_KEY_T fd_gui_shred_event_t
3348 0 : #define SORT_BEFORE(a,b) ((a).slot<(b).slot)
3349 : #include "../../util/tmpl/fd_sort.c"
3350 :
3351 : static int
3352 : fd_gui_timeline_query_events( fd_gui_t * gui,
3353 : long start,
3354 : long end,
3355 : ulong id,
3356 0 : int fec ) {
3357 0 : if( FD_UNLIKELY( start<0L || end<=start || end==LONG_MAX ) ) return -1;
3358 0 : fd_gui_shred_event_t * events = gui->timeline_scratch.events;
3359 0 : fd_gui_shred_event_iter_t it;
3360 0 : if( fec ) fd_gui_fec_event_iter_begin( gui, &it, start, end );
3361 0 : else fd_gui_shred_event_hist_iter_begin( gui, &it, start, end );
3362 0 : ulong n = 0UL;
3363 0 : ulong reference_slot = ULONG_MAX;
3364 0 : long reference_ts = LONG_MAX;
3365 0 : int limit = 0;
3366 : /* Rare duplicate slot-complete markers are acceptable for monitoring;
3367 : return each recorded event in the requested window. */
3368 0 : while( fd_gui_shred_event_iter_next( &it ) ) {
3369 0 : if( FD_UNLIKELY( n==FD_GUI_TIMELINE_QUERY_SHRED_MAX ) ) {
3370 0 : limit = 1;
3371 0 : break;
3372 0 : }
3373 0 : events[ n++ ] = it.event;
3374 0 : }
3375 0 : fd_gui_shred_event_iter_end( &it );
3376 0 : if( FD_UNLIKELY( limit ) ) {
3377 0 : fd_gui_timeline_limit( gui, "query_shreds", id );
3378 0 : return 0;
3379 0 : }
3380 0 : for( ulong i=0UL; i<n; i++ ) {
3381 0 : reference_slot = fd_ulong_min( reference_slot, events[ i ].slot );
3382 0 : reference_ts = fd_long_min( reference_ts, events[ i ].event_time_ns );
3383 0 : }
3384 0 : jsonp_open_envelope( gui->http, "timeline", "query_shreds" );
3385 0 : jsonp_ulong( gui->http, "id", id );
3386 0 : jsonp_open_object( gui->http, "value" );
3387 0 : jsonp_string( gui->http, "granularity", fec ? "fec" : "shred" );
3388 0 : fd_gui_timeline_bounds( gui, fec ? FD_GUI_HIST_FEC_EVENTS : FD_GUI_HIST_SHRED_EVENTS );
3389 0 : if( n ) {
3390 0 : jsonp_ulong( gui->http, "reference_slot", reference_slot );
3391 0 : jsonp_long_as_str( gui->http, "reference_ts", reference_ts );
3392 0 : } else {
3393 0 : jsonp_null( gui->http, "reference_slot" );
3394 0 : jsonp_null( gui->http, "reference_ts" );
3395 0 : }
3396 0 : jsonp_open_array( gui->http, "slot_delta" );
3397 0 : for( ulong i=0UL; i<n; i++ ) jsonp_ulong( gui->http, NULL, events[ i ].slot-reference_slot );
3398 0 : jsonp_close_array( gui->http );
3399 0 : jsonp_open_array( gui->http, "idx" );
3400 0 : for( ulong i=0UL; i<n; i++ ) {
3401 0 : if( events[ i ].idx==USHORT_MAX ) jsonp_null( gui->http, NULL );
3402 0 : else jsonp_ulong( gui->http, NULL, events[ i ].idx );
3403 0 : }
3404 0 : jsonp_close_array( gui->http );
3405 0 : jsonp_open_array( gui->http, "event" );
3406 0 : for( ulong i=0UL; i<n; i++ ) jsonp_ulong( gui->http, NULL, events[ i ].event );
3407 0 : jsonp_close_array( gui->http );
3408 0 : jsonp_open_array( gui->http, "event_ts_delta" );
3409 0 : for( ulong i=0UL; i<n; i++ ) jsonp_long_as_str( gui->http, NULL, events[ i ].event_time_ns-reference_ts );
3410 0 : jsonp_close_array( gui->http );
3411 0 : fd_gui_timeline_event_sort_inplace( events, n );
3412 0 : jsonp_open_array( gui->http, "skipped" );
3413 0 : ulong prev = ULONG_MAX;
3414 0 : for( ulong i=0UL; i<n; i++ ) {
3415 0 : ulong slot = events[ i ].slot;
3416 0 : if( slot==prev ) continue;
3417 0 : prev = slot;
3418 0 : if( fd_gui_timeline_slot_skipped( gui, slot ) ) jsonp_ulong( gui->http, NULL, slot-reference_slot );
3419 0 : }
3420 0 : jsonp_close_array( gui->http );
3421 0 : jsonp_close_object( gui->http );
3422 0 : jsonp_close_envelope( gui->http );
3423 0 : return 0;
3424 0 : }
3425 :
3426 : int
3427 : fd_gui_printf_timeline_query_shreds( fd_gui_t * gui,
3428 : long start,
3429 : long end,
3430 0 : ulong id ) {
3431 0 : return fd_gui_timeline_query_events( gui, start, end, id, 0 );
3432 0 : }
3433 :
3434 : int
3435 : fd_gui_printf_timeline_query_fec_events( fd_gui_t * gui,
3436 : long start,
3437 : long end,
3438 0 : ulong id ) {
3439 0 : return fd_gui_timeline_query_events( gui, start, end, id, 1 );
3440 0 : }
3441 :
3442 : /* fd_gui_timeline_day_bounds returns the span of live day records.
3443 : Returns 1 and sets *start / *end if any are available, else 0. */
3444 :
3445 : static int
3446 : fd_gui_timeline_day_bounds( fd_gui_t * gui,
3447 : long * start,
3448 0 : long * end ) {
3449 0 : long first, last;
3450 0 : if( FD_UNLIKELY( !gui->db || !gui->hist ||
3451 0 : !fd_gui_store_ts_live_timestamp_bounds( gui->db, FD_GUI_HIST_TIMELINE_DAY, &first, &last ) ||
3452 0 : first<FD_GUI_TIMELINE_DAY_NS ) ) return 0;
3453 :
3454 0 : *start = first-FD_GUI_TIMELINE_DAY_NS;
3455 0 : *end = last;
3456 0 : return 1;
3457 0 : }
3458 :
3459 : /* fd_gui_timeline_epoch_in_window returns epoch e if its record is
3460 : sane and its timeline slot range overlaps [start,end), else NULL. */
3461 :
3462 : static fd_gui_epoch_t const *
3463 : fd_gui_timeline_epoch_in_window( fd_gui_t * gui,
3464 : ulong e,
3465 : long start,
3466 0 : long end ) {
3467 0 : fd_gui_epoch_t const * epoch = fd_gui_epoch( gui, e );
3468 0 : if( epoch && epoch->epoch==e && epoch->slot_cnt && epoch->slot_cnt<=MAX_SLOTS_PER_EPOCH &&
3469 0 : epoch->start_slot<=ULONG_MAX-(epoch->slot_cnt-1UL) && epoch->timeline_slot_lo_idx<=epoch->timeline_slot_hi_idx &&
3470 0 : epoch->timeline_slot_hi_idx<epoch->slot_cnt &&
3471 0 : epoch->timeline_slot_min_start_ns<end && epoch->timeline_slot_max_end_ns>start ) return epoch;
3472 0 : return NULL;
3473 0 : }
3474 :
3475 : int
3476 : fd_gui_timeline_slots_collect( fd_gui_t * gui,
3477 : long start,
3478 : long end,
3479 : fd_gui_timeline_query_slot_t * rows,
3480 : ulong max,
3481 0 : ulong * cnt ) {
3482 0 : if( FD_UNLIKELY( !gui || !cnt || start<0L || end<=start || end==LONG_MAX || (max && !rows) ) ) return -1;
3483 0 : *cnt = 0UL;
3484 0 : if( !gui->db || gui->epoch.current_epoch==ULONG_MAX ) return 0;
3485 0 : ulong hi = gui->epoch.current_epoch==ULONG_MAX-1UL ? ULONG_MAX : gui->epoch.current_epoch+1UL;
3486 0 : ulong lo = hi>FD_GUI_HIST_MAX_EPOCHS ? hi-FD_GUI_HIST_MAX_EPOCHS : 0UL;
3487 0 : for( ulong e=lo;; e++ ) {
3488 0 : fd_gui_epoch_t const * epoch = fd_gui_timeline_epoch_in_window( gui, e, start, end );
3489 0 : if( epoch ) {
3490 0 : for( ulong i=epoch->timeline_slot_lo_idx; i<=epoch->timeline_slot_hi_idx; i++ ) {
3491 0 : uchar state = epoch->timeline_slot_state[ i ];
3492 0 : long rs = epoch->timeline_slot_start_ns[ i ];
3493 0 : long re = epoch->timeline_slot_end_ns[ i ];
3494 0 : if( !(state & FD_GUI_TIMELINE_SLOT_STATE_VALID) || rs<0L || re<=rs || rs>=end || re<=start ) continue;
3495 0 : if( FD_UNLIKELY( *cnt==max ) ) return 1;
3496 0 : rows[ (*cnt)++ ] = (fd_gui_timeline_query_slot_t){
3497 0 : .slot = epoch->start_slot + i,
3498 0 : .start_ns = rs,
3499 0 : .end_ns = re,
3500 0 : .shred_cnt= epoch->timeline_slot_shred_cnt[ i ],
3501 0 : .skipped = (uchar)!!(state & FD_GUI_TIMELINE_SLOT_STATE_SKIPPED),
3502 0 : .mine = (uchar)!!(state & FD_GUI_TIMELINE_SLOT_STATE_MINE)
3503 0 : };
3504 0 : }
3505 0 : }
3506 0 : if( e==hi ) break;
3507 0 : }
3508 0 : return 0;
3509 0 : }
3510 :
3511 : int
3512 : fd_gui_timeline_events_collect( fd_gui_t * gui,
3513 : long start,
3514 : long end,
3515 : fd_gui_timeline_query_event_t * rows,
3516 : ulong max,
3517 0 : ulong * cnt ) {
3518 0 : if( FD_UNLIKELY( !gui || !cnt || start<0L || end<=start || end==LONG_MAX || (max && !rows) ) ) return -1;
3519 0 : *cnt = 0UL;
3520 0 : if( !gui->db || gui->epoch.current_epoch==ULONG_MAX ) return 0;
3521 0 : ulong hi = gui->epoch.current_epoch==ULONG_MAX-1UL ? ULONG_MAX : gui->epoch.current_epoch+1UL;
3522 0 : ulong lo = hi>FD_GUI_HIST_MAX_EPOCHS ? hi-FD_GUI_HIST_MAX_EPOCHS : 0UL;
3523 0 : for( ulong e=lo;; e++ ) {
3524 0 : fd_gui_epoch_t const * epoch = fd_gui_timeline_epoch_in_window( gui, e, start, end );
3525 0 : if( epoch ) {
3526 0 : for( ulong i=epoch->timeline_slot_lo_idx; i<=epoch->timeline_slot_hi_idx; i++ ) {
3527 0 : uchar state = epoch->timeline_slot_state[ i ];
3528 0 : long rs = epoch->timeline_slot_start_ns[ i ];
3529 0 : long re = epoch->timeline_slot_end_ns[ i ];
3530 0 : if( (state & (FD_GUI_TIMELINE_SLOT_STATE_VALID|FD_GUI_TIMELINE_SLOT_STATE_SKIPPED))!=
3531 0 : (FD_GUI_TIMELINE_SLOT_STATE_VALID|FD_GUI_TIMELINE_SLOT_STATE_SKIPPED) || rs<0L || re<=rs ) continue;
3532 0 : long mid = rs + (re-rs)/2L;
3533 0 : if( mid<start || mid>=end ) continue;
3534 : /* STATE_MINE only means our block replayed; a leader slot we never
3535 : produced must still count, so check the schedule. */
3536 0 : fd_pubkey_t const * leader = fd_gui_get_slot_leader( epoch, epoch->start_slot+i );
3537 0 : if( !leader || memcmp( leader->uc, gui->summary.identity_key->uc, 32UL ) ) continue;
3538 0 : if( FD_UNLIKELY( *cnt==max ) ) return 1;
3539 0 : rows[ (*cnt)++ ] = (fd_gui_timeline_query_event_t){
3540 0 : .slot = epoch->start_slot + i,
3541 0 : .ts = mid,
3542 0 : .event = FD_GUI_TIMELINE_EVENT_LEADER_SKIP
3543 0 : };
3544 0 : }
3545 0 : }
3546 0 : if( e==hi ) break;
3547 0 : }
3548 0 : return 0;
3549 0 : }
3550 :
3551 : static void
3552 0 : fd_gui_timeline_slot_bounds( fd_gui_t * gui ) {
3553 0 : long first = LONG_MAX;
3554 0 : long last = LONG_MIN;
3555 0 : if( gui->db && gui->epoch.current_epoch!=ULONG_MAX ) {
3556 0 : ulong hi = gui->epoch.current_epoch==ULONG_MAX-1UL ? ULONG_MAX : gui->epoch.current_epoch+1UL;
3557 0 : ulong lo = hi>FD_GUI_HIST_MAX_EPOCHS ? hi-FD_GUI_HIST_MAX_EPOCHS : 0UL;
3558 0 : for( ulong e=lo;; e++ ) {
3559 0 : fd_gui_epoch_t const * epoch = fd_gui_epoch( gui, e );
3560 0 : if( epoch && epoch->timeline_slot_lo_idx<=epoch->timeline_slot_hi_idx && epoch->timeline_slot_hi_idx<epoch->slot_cnt ) {
3561 : /* Arrival order need not match slot order, so use the hull of
3562 : every interval written rather than the range endpoints. */
3563 0 : first = fd_long_min( first, epoch->timeline_slot_min_start_ns );
3564 0 : last = fd_long_max( last, epoch->timeline_slot_max_end_ns );
3565 0 : }
3566 0 : if( e==hi ) break;
3567 0 : }
3568 0 : }
3569 0 : if( first<last ) {
3570 0 : jsonp_long_as_str( gui->http, "available_start_ns", first );
3571 0 : jsonp_long_as_str( gui->http, "available_end_ns", last );
3572 0 : } else {
3573 0 : jsonp_null( gui->http, "available_start_ns" );
3574 0 : jsonp_null( gui->http, "available_end_ns" );
3575 0 : }
3576 0 : }
3577 :
3578 : int
3579 : fd_gui_printf_timeline_query_slots( fd_gui_t * gui,
3580 : long start,
3581 : long end,
3582 0 : ulong id ) {
3583 0 : if( FD_UNLIKELY( start<0L || end<=start || end==LONG_MAX ) ) return -1;
3584 0 : fd_gui_timeline_query_slot_t * rows = gui->timeline_scratch.slots;
3585 0 : ulong n = 0UL;
3586 0 : int rc = fd_gui_timeline_slots_collect( gui, start, end, rows, FD_GUI_TIMELINE_QUERY_SLOT_MAX, &n );
3587 0 : if( FD_UNLIKELY( rc<0 ) ) goto done;
3588 0 : if( FD_UNLIKELY( rc>0 ) ) {
3589 0 : fd_gui_timeline_limit( gui, "query_slots", id );
3590 0 : rc = 0;
3591 0 : goto done;
3592 0 : }
3593 0 : long reference_ts = LONG_MAX;
3594 0 : for( ulong i=0UL; i<n; i++ ) reference_ts = fd_long_min( reference_ts, rows[ i ].start_ns );
3595 0 : jsonp_open_envelope( gui->http, "timeline", "query_slots" );
3596 0 : jsonp_ulong( gui->http, "id", id );
3597 0 : jsonp_open_object( gui->http, "value" );
3598 0 : fd_gui_timeline_slot_bounds( gui );
3599 0 : if( n ) {
3600 0 : jsonp_ulong( gui->http, "reference_slot", rows[ 0 ].slot );
3601 0 : jsonp_long_as_str( gui->http, "reference_ts", reference_ts );
3602 0 : } else {
3603 0 : jsonp_null( gui->http, "reference_slot" );
3604 0 : jsonp_null( gui->http, "reference_ts" );
3605 0 : }
3606 0 : jsonp_open_array( gui->http, "slot_delta" );
3607 0 : for( ulong i=0UL; i<n; i++ ) jsonp_ulong( gui->http, NULL, rows[ i ].slot-rows[ 0 ].slot );
3608 0 : jsonp_close_array( gui->http );
3609 0 : jsonp_open_array( gui->http, "start_ts_delta" );
3610 0 : for( ulong i=0UL; i<n; i++ ) jsonp_long_as_str( gui->http, NULL, rows[ i ].start_ns-reference_ts );
3611 0 : jsonp_close_array( gui->http );
3612 0 : jsonp_open_array( gui->http, "end_ts_delta" );
3613 0 : for( ulong i=0UL; i<n; i++ ) jsonp_long_as_str( gui->http, NULL, rows[ i ].end_ns-reference_ts );
3614 0 : jsonp_close_array( gui->http );
3615 0 : jsonp_open_array( gui->http, "shred_cnt" );
3616 0 : for( ulong i=0UL; i<n; i++ ) {
3617 0 : if( FD_UNLIKELY( rows[ i ].shred_cnt==UINT_MAX ) ) jsonp_null( gui->http, NULL );
3618 0 : else jsonp_ulong( gui->http, NULL, rows[ i ].shred_cnt );
3619 0 : }
3620 0 : jsonp_close_array( gui->http );
3621 0 : jsonp_open_array( gui->http, "skipped" );
3622 0 : for( ulong i=0UL; i<n; i++ ) {
3623 0 : if( rows[ i ].skipped ) jsonp_ulong( gui->http, NULL, rows[ i ].slot-rows[ 0 ].slot );
3624 0 : }
3625 0 : jsonp_close_array( gui->http );
3626 0 : jsonp_open_array( gui->http, "mine" );
3627 0 : for( ulong i=0UL; i<n; i++ ) {
3628 0 : if( rows[ i ].mine ) jsonp_ulong( gui->http, NULL, rows[ i ].slot-rows[ 0 ].slot );
3629 0 : }
3630 0 : jsonp_close_array( gui->http );
3631 0 : jsonp_close_object( gui->http );
3632 0 : jsonp_close_envelope( gui->http );
3633 0 : done:
3634 0 : return rc;
3635 0 : }
3636 :
3637 : int
3638 : fd_gui_printf_timeline_query_events( fd_gui_t * gui,
3639 : long start,
3640 : long end,
3641 0 : ulong id ) {
3642 0 : if( FD_UNLIKELY( start<0L || end<=start || end==LONG_MAX ) ) return -1;
3643 0 : fd_gui_timeline_query_event_t * rows = gui->timeline_scratch.slot_events;
3644 0 : ulong n = 0UL;
3645 0 : int rc = fd_gui_timeline_events_collect( gui, start, end, rows, FD_GUI_TIMELINE_QUERY_EVENT_MAX, &n );
3646 0 : if( FD_UNLIKELY( rc<0 ) ) return rc;
3647 0 : if( FD_UNLIKELY( rc>0 ) ) {
3648 0 : fd_gui_timeline_limit( gui, "query_events", id );
3649 0 : return 0;
3650 0 : }
3651 0 : jsonp_open_envelope( gui->http, "timeline", "query_events" );
3652 0 : jsonp_ulong( gui->http, "id", id );
3653 0 : jsonp_open_object( gui->http, "value" );
3654 0 : fd_gui_timeline_slot_bounds( gui );
3655 0 : jsonp_open_array( gui->http, "event" );
3656 0 : for( ulong i=0UL; i<n; i++ ) jsonp_ulong( gui->http, NULL, rows[ i ].event );
3657 0 : jsonp_close_array( gui->http );
3658 0 : jsonp_open_array( gui->http, "slot" );
3659 0 : for( ulong i=0UL; i<n; i++ ) jsonp_ulong( gui->http, NULL, rows[ i ].slot );
3660 0 : jsonp_close_array( gui->http );
3661 0 : jsonp_open_array( gui->http, "ts" );
3662 0 : for( ulong i=0UL; i<n; i++ ) jsonp_long_as_str( gui->http, NULL, rows[ i ].ts );
3663 0 : jsonp_close_array( gui->http );
3664 0 : jsonp_close_object( gui->http );
3665 0 : jsonp_close_envelope( gui->http );
3666 0 : return 0;
3667 0 : }
3668 :
3669 : /* fd_gui_timeline_duration_query calculates the slot duration of each
3670 : bucket. */
3671 :
3672 : static void
3673 : fd_gui_timeline_duration_query( fd_gui_timeline_query_bucket_t * buckets,
3674 0 : ulong count ) {
3675 0 : for( ulong i=0UL; i<count; i++ ) {
3676 0 : ulong wall = buckets[ i ].fields[ FD_GUI_TIMELINE_FIELD_DURATION_WALL ];
3677 0 : ulong slots = buckets[ i ].fields[ FD_GUI_TIMELINE_FIELD_DURATION_SLOTS ];
3678 0 : if( wall==ULONG_MAX || slots==ULONG_MAX || !slots ) continue;
3679 0 : int sh = fd_int_max( fd_ulong_find_msb_w_default( wall, 0 )-38, 0 ); /* wall>>sh<2^39, so wall*FD_GUI_TIMELINE_SLOT_UNIT<2^63 */
3680 0 : wall >>= sh;
3681 0 : slots >>= sh;
3682 0 : buckets[ i ].slot_duration = FD_LIKELY( slots ) ? ( wall*FD_GUI_TIMELINE_SLOT_UNIT + slots/2UL ) / slots : ULONG_MAX-1UL;
3683 0 : }
3684 0 : }
3685 :
3686 : int
3687 : fd_gui_printf_timeline_query_agg( fd_gui_t * gui,
3688 : char const * key,
3689 : char const * granularity,
3690 : ulong g,
3691 : long reference,
3692 : ulong count,
3693 0 : ulong id ) {
3694 0 : if( FD_UNLIKELY( reference<0L || !count || count>FD_GUI_TIMELINE_QUERY_MAX_BUCKETS || g>=FD_GUI_TIMELINE_GRANULARITY_CNT ) ) return -1;
3695 0 : int stored;
3696 0 : ulong merge_cnt;
3697 0 : if( FD_UNLIKELY( fd_gui_timeline_tier( key, g, &stored, &merge_cnt ) ) ) return -1;
3698 0 : ulong ns = fd_gui_timeline_granularity_ns( g );
3699 0 : if( FD_UNLIKELY( (ulong)reference+count*ns>=(ulong)LONG_MAX ) ) return -1; /* window end must be representable */
3700 :
3701 0 : fd_gui_timeline_query_bucket_t * b = gui->timeline_scratch.buckets;
3702 0 : for( ulong i=0UL; i<count; i++ ) {
3703 0 : memset( b[ i ].fields, 0xFF, sizeof(b[ i ].fields) );
3704 0 : b[ i ].slot_duration = ULONG_MAX;
3705 0 : b[ i ].has_day = 0;
3706 0 : }
3707 :
3708 : /* The buckets are visited in ascending time order, so the record for
3709 : the previous bucket's day is reused until the query crosses into
3710 : the next day. */
3711 :
3712 0 : ulong step = fd_gui_timeline_stored_granularity_ns[ stored ];
3713 0 : ulong cur_day_idx = ULONG_MAX;
3714 0 : fd_gui_timeline_day_t const * cur_day = NULL;
3715 0 : for( ulong i=0UL; i<count; i++ ) {
3716 0 : for( ulong j=0UL; j<merge_cnt; j++ ) {
3717 0 : ulong ts = (ulong)reference + i*ns + j*step;
3718 0 : ulong day_idx = ts/(ulong)FD_GUI_TIMELINE_DAY_NS;
3719 0 : if( FD_UNLIKELY( day_idx!=cur_day_idx ) ) {
3720 0 : cur_day_idx = day_idx;
3721 0 : cur_day = fd_gui_timeline_day_get( gui, day_idx );
3722 0 : }
3723 0 : if( FD_UNLIKELY( !cur_day ) ) continue;
3724 0 : b[ i ].has_day = 1;
3725 0 : ulong idx = (ts%(ulong)FD_GUI_TIMELINE_DAY_NS) / step;
3726 0 : for( int f=0; f<FD_GUI_TIMELINE_FIELD_CNT; f++ ) {
3727 0 : b[ i ].fields[ f ] = fd_gui_timeline_combine( b[ i ].fields[ f ], fd_gui_timeline_field_get( cur_day, (int)stored, f, idx ), f );
3728 0 : }
3729 0 : }
3730 0 : }
3731 0 : if( !strcmp( key, "query_agg_shreds" ) ) fd_gui_timeline_duration_query( b, count );
3732 0 : if( !strcmp( key, "query_agg_slots" ) ) {
3733 : /* Counts are zero, not unknown, wherever a day record exists. */
3734 0 : int counts[ 3 ] = { FD_GUI_TIMELINE_FIELD_SKIPPED, FD_GUI_TIMELINE_FIELD_MINE, FD_GUI_TIMELINE_FIELD_MINE_SKIPPED };
3735 0 : for( ulong i=0UL; i<count; i++ ) {
3736 0 : if( !b[ i ].has_day ) continue;
3737 0 : for( int j=0; j<3; j++ ) b[ i ].fields[ counts[ j ] ] = fd_ulong_if( b[ i ].fields[ counts[ j ] ]==ULONG_MAX, 0UL, b[ i ].fields[ counts[ j ] ] );
3738 : /* An identity switch between replay and rooting can count a skip
3739 : that mine missed. */
3740 0 : b[ i ].fields[ FD_GUI_TIMELINE_FIELD_MINE_SKIPPED ] = fd_ulong_min( b[ i ].fields[ FD_GUI_TIMELINE_FIELD_MINE_SKIPPED ], b[ i ].fields[ FD_GUI_TIMELINE_FIELD_MINE ] );
3741 0 : }
3742 0 : }
3743 :
3744 0 : jsonp_open_envelope( gui->http, "timeline", key );
3745 0 : jsonp_ulong( gui->http, "id", id );
3746 0 : jsonp_open_object( gui->http, "value" );
3747 0 : jsonp_string( gui->http, "granularity", granularity );
3748 0 : jsonp_long_as_str( gui->http, "reference_ts_ns", reference );
3749 0 : long avail_start, avail_end;
3750 0 : if( FD_LIKELY( fd_gui_timeline_day_bounds( gui, &avail_start, &avail_end ) ) ) {
3751 0 : jsonp_long_as_str( gui->http, "available_start_ns", avail_start );
3752 0 : jsonp_long_as_str( gui->http, "available_end_ns", avail_end );
3753 0 : } else {
3754 0 : jsonp_null( gui->http, "available_start_ns" );
3755 0 : jsonp_null( gui->http, "available_end_ns" );
3756 0 : }
3757 0 : #define AGG_ARRAY(name,field,string) do { \
3758 0 : jsonp_open_array( gui->http, name ); \
3759 0 : for( ulong i=0UL; i<count; i++ ) { \
3760 0 : ulong v = b[ i ].fields[ FD_GUI_TIMELINE_FIELD_##field ]; \
3761 0 : if( v==ULONG_MAX ) jsonp_null( gui->http, NULL ); \
3762 0 : else if( string ) jsonp_ulong_as_str( gui->http, NULL, v ); \
3763 0 : else jsonp_ulong( gui->http, NULL, v ); \
3764 0 : } \
3765 0 : jsonp_close_array( gui->http ); \
3766 0 : } while(0)
3767 0 : if( !strcmp( key, "query_agg_slots" ) ) {
3768 0 : AGG_ARRAY( "start_slot", START_SLOT, 0 );
3769 0 : AGG_ARRAY( "end_slot", END_SLOT, 0 );
3770 0 : AGG_ARRAY( "skipped", SKIPPED, 0 );
3771 0 : AGG_ARRAY( "mine", MINE, 0 );
3772 0 : AGG_ARRAY( "mine_skipped", MINE_SKIPPED, 0 );
3773 0 : } else if( !strcmp( key, "query_agg_shreds" ) ) {
3774 0 : AGG_ARRAY( "turbine", TURBINE, 0 );
3775 0 : AGG_ARRAY( "repair", REPAIR, 0 );
3776 0 : AGG_ARRAY( "reconstructed", RECONSTRUCTED, 0 );
3777 0 : AGG_ARRAY( "published", PUBLISHED, 0 );
3778 0 : jsonp_open_array( gui->http, "slot_duration" );
3779 0 : for( ulong i=0UL; i<count; i++ ) {
3780 0 : if( b[ i ].slot_duration==ULONG_MAX ) jsonp_null( gui->http, NULL );
3781 0 : else jsonp_ulong( gui->http, NULL, b[ i ].slot_duration );
3782 0 : }
3783 0 : jsonp_close_array( gui->http );
3784 0 : } else if( !strcmp( key, "query_agg_compute" ) ) {
3785 0 : AGG_ARRAY( "compute_units", COMPUTE_UNITS, 0 );
3786 0 : ulong max = ULONG_MAX;
3787 0 : for( ulong i=0UL; i<count; i++ ) {
3788 0 : max = fd_gui_timeline_combine( max, b[ i ].fields[ FD_GUI_TIMELINE_FIELD_MAX_COMPUTE ], FD_GUI_TIMELINE_FIELD_MAX_COMPUTE );
3789 0 : }
3790 0 : if( max==ULONG_MAX ) jsonp_null( gui->http, "max_compute_units" );
3791 0 : else jsonp_ulong( gui->http, "max_compute_units", max );
3792 0 : } else if( !strcmp( key, "query_agg_revenue" ) ) {
3793 0 : AGG_ARRAY( "txn_fees", TXN_FEES, 1 );
3794 0 : AGG_ARRAY( "prio_fees", PRIO_FEES, 1 );
3795 0 : AGG_ARRAY( "tips", TIPS, 1 );
3796 0 : } else if( !strcmp( key, "query_agg_txn" ) ) {
3797 0 : AGG_ARRAY( "success_nonvote_transactions", NONVOTE_SUCCESS, 0 );
3798 0 : AGG_ARRAY( "failed_nonvote_transactions", NONVOTE_FAILED, 0 );
3799 0 : AGG_ARRAY( "success_vote_transactions", VOTE_SUCCESS, 0 );
3800 0 : AGG_ARRAY( "failed_vote_transactions", VOTE_FAILED, 0 );
3801 0 : }
3802 0 : #undef AGG_ARRAY
3803 0 : jsonp_close_object( gui->http );
3804 0 : jsonp_close_envelope( gui->http );
3805 0 : return 0;
3806 0 : }
3807 :
3808 : /* fd_gui_tile_from_ordinal is the inverse of fd_gui_tile_ordinal,
3809 : returning ULONG_MAX for an ordinal past the reported tiles. */
3810 :
3811 : static ulong
3812 : fd_gui_tile_from_ordinal( fd_gui_t * gui,
3813 0 : ulong ordinal ) {
3814 0 : for( ulong i=0UL; i<gui->summary.tile_cnt; i++ ) {
3815 0 : ulong idx = gui->summary.tile[ i ];
3816 0 : if( FD_UNLIKELY( !strncmp( gui->topo->tiles[ idx ].name, "bench", 5UL ) ) ) continue;
3817 0 : if( !ordinal-- ) return idx;
3818 0 : }
3819 0 : return ULONG_MAX;
3820 0 : }
3821 :
3822 : int
3823 : fd_gui_printf_timeline_query_agg_cpu( fd_gui_t * gui,
3824 : char const * granularity,
3825 : ulong g,
3826 : long reference,
3827 : ulong count,
3828 : ulong const * ordinals,
3829 : ulong tile_cnt,
3830 0 : ulong id ) {
3831 0 : if( FD_UNLIKELY( reference<0L || !count || count>FD_GUI_TIMELINE_QUERY_MAX_BUCKETS || g>=FD_GUI_TIMELINE_GRANULARITY_CNT ) ) return -1;
3832 0 : if( FD_UNLIKELY( !tile_cnt || tile_cnt>FD_TOPO_MAX_TILES ) ) return -1;
3833 0 : ulong tiles[ FD_TOPO_MAX_TILES ];
3834 0 : for( ulong t=0UL; t<tile_cnt; t++ ) {
3835 0 : tiles[ t ] = fd_gui_tile_from_ordinal( gui, ordinals[ t ] );
3836 0 : if( FD_UNLIKELY( tiles[ t ]==ULONG_MAX ) ) return -1;
3837 0 : for( ulong u=0UL; u<t; u++ ) if( FD_UNLIKELY( tiles[ u ]==tiles[ t ] ) ) return -1;
3838 0 : }
3839 :
3840 0 : int stored;
3841 0 : ulong merge_cnt;
3842 0 : if( FD_UNLIKELY( fd_gui_timeline_tier( "query_agg_cpu", g, &stored, &merge_cnt ) ) ) return -1;
3843 0 : ulong ns = fd_gui_timeline_granularity_ns( g );
3844 0 : ulong step = fd_gui_timeline_stored_granularity_ns[ stored ];
3845 0 : if( FD_UNLIKELY( (ulong)reference%ns ) ) return -1; /* buckets must not cross a day */
3846 0 : if( FD_UNLIKELY( (ulong)reference+count*ns>=(ulong)LONG_MAX ) ) return -1; /* window end must be representable */
3847 :
3848 0 : long avail_start = 0L, avail_end = 0L;
3849 0 : ulong lo_day, hi_day;
3850 0 : int avail = fd_gui_timeline_tile_day_bounds( gui, &lo_day, &hi_day );
3851 0 : if( FD_LIKELY( avail ) ) {
3852 0 : avail_start = (long)(lo_day*(ulong)FD_GUI_TIMELINE_DAY_NS);
3853 0 : avail_end = (long)((hi_day+1UL)*(ulong)FD_GUI_TIMELINE_DAY_NS); /* records only exist for representable days */
3854 0 : }
3855 :
3856 0 : jsonp_open_envelope( gui->http, "timeline", "query_agg_cpu" );
3857 0 : jsonp_ulong( gui->http, "id", id );
3858 0 : jsonp_open_object( gui->http, "value" );
3859 0 : jsonp_string( gui->http, "granularity", granularity );
3860 0 : jsonp_long_as_str( gui->http, "reference_ts_ns", reference );
3861 0 : if( FD_LIKELY( avail ) ) {
3862 0 : jsonp_long_as_str( gui->http, "available_start_ns", avail_start );
3863 0 : jsonp_long_as_str( gui->http, "available_end_ns", avail_end );
3864 0 : } else {
3865 0 : jsonp_null( gui->http, "available_start_ns" );
3866 0 : jsonp_null( gui->http, "available_end_ns" );
3867 0 : }
3868 0 : jsonp_open_array( gui->http, "utilization" );
3869 0 : for( ulong t=0UL; t<tile_cnt; t++ ) {
3870 : /* Each requested tile is merged from its own day records; a missing
3871 : record leaves that tile's buckets empty. The buckets are visited
3872 : in ascending time order, so a day's record is looked up once. */
3873 0 : memset( gui->timeline_scratch.cpu, 0, count*sizeof(gui->timeline_scratch.cpu[ 0 ]) );
3874 0 : ulong cur_day_idx = ULONG_MAX;
3875 0 : fd_gui_timeline_tile_day_t const * cur_day = NULL;
3876 0 : for( ulong i=0UL; i<count; i++ ) {
3877 : /* Query buckets are aligned multiples of the tier and days are
3878 : multiples of every query granularity, so a bucket's tier
3879 : buckets are contiguous within one day record. */
3880 0 : ulong ts = (ulong)reference + i*ns;
3881 0 : ulong day_idx = ts/(ulong)FD_GUI_TIMELINE_DAY_NS;
3882 0 : if( FD_UNLIKELY( day_idx!=cur_day_idx ) ) {
3883 0 : cur_day_idx = day_idx;
3884 0 : cur_day = fd_gui_timeline_tile_day_get( gui, day_idx, tiles[ t ] );
3885 0 : }
3886 0 : if( FD_UNLIKELY( !cur_day ) ) continue;
3887 0 : ulong idx = (ts%(ulong)FD_GUI_TIMELINE_DAY_NS) / step;
3888 0 : uint sum = 0U;
3889 0 : uint cnt = 0U;
3890 0 : if( stored==FD_GUI_TIMELINE_GRANULARITY_25MS ) {
3891 0 : uchar const * v = cur_day->util_25ms+idx;
3892 0 : for( ulong j=0UL; j<merge_cnt; j++ ) {
3893 0 : if( v[ j ]>FD_GUI_CPU_UTIL_MAX ) continue;
3894 0 : sum += v[ j ];
3895 0 : cnt++;
3896 0 : }
3897 0 : } else {
3898 0 : for( ulong j=0UL; j<merge_cnt; j++ ) {
3899 0 : ulong s, c;
3900 0 : fd_gui_timeline_cpu_get( cur_day, stored, idx+j, &s, &c );
3901 0 : sum += (uint)s;
3902 0 : cnt += (uint)c;
3903 0 : }
3904 0 : }
3905 0 : gui->timeline_scratch.cpu[ i ].sum = sum;
3906 0 : gui->timeline_scratch.cpu[ i ].cnt = cnt;
3907 0 : }
3908 :
3909 0 : jsonp_open_array( gui->http, NULL );
3910 0 : for( ulong i=0UL; i<count; i++ ) {
3911 0 : ulong sum = gui->timeline_scratch.cpu[ i ].sum;
3912 0 : ulong cnt = gui->timeline_scratch.cpu[ i ].cnt;
3913 0 : if( !cnt ) jsonp_null( gui->http, NULL );
3914 0 : else jsonp_ulong( gui->http, NULL, (sum + cnt/2UL)/cnt );
3915 0 : }
3916 0 : jsonp_close_array( gui->http );
3917 0 : }
3918 0 : jsonp_close_array( gui->http );
3919 0 : jsonp_close_object( gui->http );
3920 0 : jsonp_close_envelope( gui->http );
3921 0 : return 0;
3922 0 : }
3923 :
3924 : void
3925 : fd_gui_peers_printf_wfs_add( fd_gui_peers_ctx_t * peers,
3926 : ulong const * idxs,
3927 0 : ulong cnt ) {
3928 0 : jsonp_open_envelope( peers->http, "wait_for_supermajority", "peer_add" );
3929 0 : jsonp_open_array( peers->http, "value" );
3930 0 : for( ulong i=0UL; i<cnt; i++ ) {
3931 0 : fd_gui_wfs_peer_t * wp = &peers->wfs_peers[ idxs[ i ] ];
3932 0 : char identity_base58[ FD_BASE58_ENCODED_32_SZ ];
3933 0 : fd_base58_encode_32( wp->identity_key.uc, NULL, identity_base58 );
3934 0 : jsonp_string( peers->http, NULL, identity_base58 );
3935 0 : }
3936 0 : jsonp_close_array( peers->http );
3937 0 : jsonp_close_envelope( peers->http );
3938 0 : }
3939 :
3940 : void
3941 : fd_gui_peers_printf_wfs_remove( fd_gui_peers_ctx_t * peers,
3942 : ulong const * idxs,
3943 0 : ulong cnt ) {
3944 0 : jsonp_open_envelope( peers->http, "wait_for_supermajority", "peer_remove" );
3945 0 : jsonp_open_array( peers->http, "value" );
3946 0 : for( ulong i=0UL; i<cnt; i++ ) {
3947 0 : fd_gui_wfs_peer_t * wp = &peers->wfs_peers[ idxs[ i ] ];
3948 0 : char identity_base58[ FD_BASE58_ENCODED_32_SZ ];
3949 0 : fd_base58_encode_32( wp->identity_key.uc, NULL, identity_base58 );
3950 0 : jsonp_string( peers->http, NULL, identity_base58 );
3951 0 : }
3952 0 : jsonp_close_array( peers->http );
3953 0 : jsonp_close_envelope( peers->http );
3954 0 : }
3955 :
3956 : void
3957 0 : fd_gui_peers_printf_wfs_stakes( fd_gui_peers_ctx_t * peers ) {
3958 0 : jsonp_open_envelope( peers->http, "wait_for_supermajority", "stakes" );
3959 0 : jsonp_open_object( peers->http, "value" );
3960 :
3961 0 : jsonp_open_array( peers->http, "staked_pubkeys" );
3962 0 : for( ulong i=0UL; i<peers->wfs_peers_cnt; i++ ) {
3963 0 : char identity_base58[ FD_BASE58_ENCODED_32_SZ ];
3964 0 : fd_base58_encode_32( peers->wfs_peers[ i ].identity_key.uc, NULL, identity_base58 );
3965 0 : jsonp_string( peers->http, NULL, identity_base58 );
3966 0 : }
3967 0 : jsonp_close_array( peers->http );
3968 :
3969 0 : jsonp_open_array( peers->http, "staked_lamports" );
3970 0 : for( ulong i=0UL; i<peers->wfs_peers_cnt; i++ ) {
3971 0 : jsonp_ulong_as_str( peers->http, NULL, peers->wfs_peers[ i ].stake );
3972 0 : }
3973 0 : jsonp_close_array( peers->http );
3974 :
3975 0 : jsonp_open_array( peers->http, "infos" );
3976 0 : for( ulong i=0UL; i<peers->wfs_peers_cnt; i++ ) {
3977 0 : fd_gui_config_parse_info_t * info =
3978 0 : fd_gui_peers_node_info_map_ele_query(
3979 0 : peers->node_info_map, &peers->wfs_peers[ i ].identity_key, NULL, peers->node_info_pool );
3980 0 : if( info && !info->cleared ) {
3981 0 : jsonp_open_object( peers->http, NULL );
3982 0 : jsonp_string( peers->http, "name", info->name );
3983 0 : jsonp_string( peers->http, "details", info->details );
3984 0 : jsonp_string( peers->http, "website", info->website );
3985 0 : jsonp_string( peers->http, "icon_url", info->icon_uri );
3986 0 : jsonp_string( peers->http, "keybase_username", info->keybase_username );
3987 0 : jsonp_close_object( peers->http );
3988 0 : } else {
3989 0 : jsonp_null( peers->http, NULL );
3990 0 : }
3991 0 : }
3992 0 : jsonp_close_array( peers->http );
3993 :
3994 0 : jsonp_close_object( peers->http );
3995 0 : jsonp_close_envelope( peers->http );
3996 0 : }
|