Line data Source code
1 : #define _GNU_SOURCE
2 :
3 : #include "fd_diag_tile.h"
4 :
5 : #include "../fd_clock_tile.h"
6 :
7 : #include "../bundle/fd_bundle_tile.h"
8 : #include "../metrics/fd_metrics.h"
9 : #include "../stem/fd_stem.h"
10 : #include "../topo/fd_topo.h"
11 : #include "../topo/fd_cpu_topo.h"
12 : #include "../../util/tile/fd_tile_private.h"
13 : #include "../../util/io/fd_io.h"
14 :
15 : #include <dirent.h>
16 : #include <fcntl.h>
17 : #include <errno.h>
18 : #include <stdlib.h>
19 : #include <sys/types.h> /* SEEK_SET */
20 : #include <sys/stat.h>
21 : #include <sys/syscall.h>
22 : #include <sys/vfs.h>
23 : #include <linux/futex.h>
24 : #include <time.h>
25 : #include <unistd.h>
26 :
27 : #include "fd_proc_interrupts.h"
28 : #include "generated/fd_diag_tile_seccomp.h"
29 :
30 : /* Raw state values exported by the metrics (states= enums) */
31 : FD_STATIC_ASSERT( FD_DIAG_BUNDLE_STATUS_DISCONNECTED==FD_METRICS_ENUM_DIAG_BUNDLE_STATE_V_DISCONNECTED_IDX, metrics_state );
32 : FD_STATIC_ASSERT( FD_DIAG_BUNDLE_STATUS_CONNECTING==FD_METRICS_ENUM_DIAG_BUNDLE_STATE_V_CONNECTING_IDX, metrics_state );
33 : FD_STATIC_ASSERT( FD_DIAG_BUNDLE_STATUS_CONNECTED==FD_METRICS_ENUM_DIAG_BUNDLE_STATE_V_CONNECTED_IDX, metrics_state );
34 : FD_STATIC_ASSERT( FD_DIAG_BUNDLE_STATUS_SLEEPING==FD_METRICS_ENUM_DIAG_BUNDLE_STATE_V_SLEEPING_IDX, metrics_state );
35 : FD_STATIC_ASSERT( FD_DIAG_BUNDLE_STATUS_DISABLED==FD_METRICS_ENUM_DIAG_BUNDLE_STATE_V_DISABLED_IDX, metrics_state );
36 :
37 : /* Each procfs source is read at the rate its consumers need: the CPU
38 : regime split every report (100 ms), /proc/<tid>/sched (only feeds
39 : cumulative counters, ~10 us a read) once a second, /proc/interrupts
40 : (~5 ms on 96 CPUs) every five. */
41 :
42 0 : #define REPORT_INTERVAL_MILLIS (100L)
43 0 : #define SCHED_REPORT_INTERVAL_NANOS (1000000000L)
44 0 : #define IRQ_REPORT_INTERVAL_NANOS (5000000000L)
45 0 : #define SYSTEM_REPORT_INTERVAL_NANOS (30000000000L)
46 :
47 : /* Hardware sensors, read with the IRQ report */
48 : #define DIAG_SENSOR_MAX (128UL)
49 0 : #define DIAG_RAPL_MAX (8UL)
50 0 : #define DIAG_EDAC_MC_MAX (64UL) /* memory controllers */
51 :
52 0 : #define DIAG_SENSOR_CPU_PACKAGE (0U)
53 0 : #define DIAG_SENSOR_NIC (1U)
54 0 : #define DIAG_SENSOR_NVME (2U)
55 0 : #define DIAG_SENSOR_POWER (3U)
56 0 : #define DIAG_SENSOR_ECC_CE (4U)
57 0 : #define DIAG_SENSOR_ECC_UE (5U)
58 : #define DIAG_SENSOR_KIND_CNT (6U)
59 :
60 0 : #define DIAG_WKSP_TILE_IDX_SHARED (ULONG_MAX)
61 :
62 :
63 : struct fd_diag_tile {
64 : fd_clock_tile_t clock[1];
65 : long next_report_nanos;
66 : long next_sched_report_nanos;
67 : long next_irq_report_nanos;
68 :
69 : ulong tile_cnt;
70 : int is_voting;
71 :
72 : struct {
73 : ulong bundle_tile_idx[ FD_TILE_MAX ];
74 : ulong bundle_cnt;
75 : ulong shred_tile_idx[ FD_TILE_MAX ];
76 : ulong shred_cnt;
77 : ulong votor_idx;
78 : ulong replay_idx;
79 : } tiles;
80 :
81 : ulong starttime_nanos[ FD_TILE_MAX ];
82 : long first_seen_died[ FD_TILE_MAX ];
83 :
84 : int stat_fds[ FD_TILE_MAX ];
85 : int sched_fds[ FD_TILE_MAX ];
86 : int schedstat_fds[ FD_TILE_MAX ];
87 :
88 : ulong irq_cnt[ FD_METRICS_ENUM_SOFTIRQ_TYPE_CNT ][ FD_TILE_MAX ];
89 : fd_cpuset_t cpu_live_tile[ fd_cpuset_word_cnt ]; /* CPUs hosting a live pinned tile */
90 : ulong on_tile_softirq[ FD_METRICS_ENUM_SOFTIRQ_TYPE_CNT ]; /* accumulated while the CPU hosts a live tile */
91 : ulong on_tile_device_irq;
92 : int proc_interrupts_fd;
93 : int proc_softirqs_fd;
94 : int proc_stat_fd;
95 : int proc_meminfo_fd;
96 : int proc_loadavg_fd;
97 : int proc_vmstat_fd;
98 : int psi_fd[ 3 ]; /* /proc/pressure/{cpu,memory,io}, -1 if absent */
99 : ulong host_cpu_baseline[ FD_METRICS_ENUM_SYSTEM_CPU_STATE_CNT ]; /* /proc/stat ticks at diag start */
100 : ulong host_cpu_ticks [ FD_METRICS_ENUM_SYSTEM_CPU_STATE_CNT ]; /* highest ticks since then published */
101 : ulong device_irq_boot [ FD_TILE_MAX ]; /* /proc/interrupts at diag start */
102 : ulong device_irq_baseline[ FD_TILE_MAX ]; /* at the last sweep */
103 : ulong tlb_baseline[ FD_TILE_MAX ];
104 : ulong loc_baseline[ FD_TILE_MAX ];
105 : ulong irq_ticks_baseline[ FD_TILE_MAX ];
106 : ulong softirq_boot [ FD_METRICS_ENUM_SOFTIRQ_TYPE_CNT ][ FD_TILE_MAX ];
107 : ulong softirq_baseline[ FD_METRICS_ENUM_SOFTIRQ_TYPE_CNT ][ FD_TILE_MAX ];
108 :
109 : ulong volatile * metrics [ FD_TILE_MAX ];
110 : ushort cpu_to_tile[ FD_TILE_MAX ]; /* the one live tile pinned to the CPU, USHORT_MAX if none or shared */
111 : ushort tile_cpu [ FD_TILE_MAX ]; /* pinned CPU of each tile, USHORT_MAX if floating */
112 : fd_cpuset_t irq_rebase [ fd_cpuset_word_cnt ]; /* owner changed, irq_metrics takes new baselines */
113 : fd_cpuset_t tick_rebase[ fd_cpuset_word_cnt ]; /* owner changed, interrupt_metrics takes new baselines */
114 : ulong owner_irq_baseline [ FD_TILE_MAX ]; /* per-CPU tile baselines, start at the boot baselines */
115 : ulong owner_tlb_baseline [ FD_TILE_MAX ];
116 : ulong owner_loc_baseline [ FD_TILE_MAX ];
117 : ulong owner_tick_baseline[ FD_TILE_MAX ];
118 :
119 : struct {
120 : ulong cnt;
121 : int fd [ DIAG_SENSOR_MAX ]; /* hwmon inputs and EDAC counts */
122 : uint kind[ DIAG_SENSOR_MAX ]; /* DIAG_SENSOR_* */
123 :
124 : ulong rapl_cnt;
125 : int rapl_fd [ DIAG_RAPL_MAX ]; /* powercap package energy_uj */
126 : ulong rapl_range[ DIAG_RAPL_MAX ]; /* energy_uj wraps at this */
127 : ulong rapl_prev [ DIAG_RAPL_MAX ];
128 : long rapl_prev_nanos; /* 0 until a baseline is taken */
129 : } sensor;
130 :
131 : int gui_enabled;
132 : long next_system_report_nanos;
133 : ushort numa_entry_idx[ FD_DIAG_SYSTEM_TILE_MAX ][ FD_DIAG_SYSTEM_NUMA_MAX ];
134 : struct {
135 : ulong cnt;
136 : struct {
137 : char name[ FD_SHMEM_NAME_MAX ];
138 : ulong tile_idx;
139 : ulong numa_idx;
140 : ulong bytes;
141 : ushort numa_slot;
142 : } wksp[ FD_TOPO_MAX_WKSPS ];
143 : ushort stack_numa_slot[ FD_DIAG_SYSTEM_TILE_MAX ];
144 : } memory;
145 : struct {
146 : ulong cnt;
147 : struct {
148 : ushort idx;
149 : int meminfo_fd;
150 : } node[ FD_DIAG_SYSTEM_NUMA_MAX ];
151 : } numa;
152 :
153 : struct {
154 : void * mem;
155 : ulong idx;
156 : ulong chunk0;
157 : ulong wmark;
158 : ulong chunk;
159 : } system_out;
160 : fd_diag_system_resources_t system_resources;
161 :
162 : struct {
163 : int fd; /* O_PATH reference to an anonymous regular file */
164 : ulong mnt_id; /* mount ID from /proc/self/fdinfo */
165 : char path[ PATH_MAX ];
166 : } mounts[ FD_DIAG_SYSTEM_FILE_MAX ];
167 : ulong mount_cnt;
168 :
169 : struct {
170 : char path[ PATH_MAX ];
171 : uint category;
172 : uint mount_idx;
173 : int data_fd;
174 : ulong volatile * metric;
175 : } files[ FD_DIAG_SYSTEM_FILE_MAX ];
176 : ulong file_cnt;
177 :
178 : struct {
179 : ulong prev_reset_slot;
180 : long reset_slot_changed_ns;
181 : ulong prev_turbine_slot;
182 : long turbine_slot_changed_ns;
183 :
184 : ulong snapshot_turbine_bytes;
185 : ulong snapshot_repair_bytes;
186 : long byte_snapshot_ns;
187 : int repair_outpacing;
188 : } check_engine;
189 : };
190 :
191 : typedef struct fd_diag_tile fd_diag_tile_t;
192 :
193 : FD_FN_CONST static inline ulong
194 0 : scratch_align( void ) {
195 0 : return alignof(fd_diag_tile_t);
196 0 : }
197 :
198 : FD_FN_PURE static inline ulong
199 0 : scratch_footprint( fd_topo_tile_t const * tile ) {
200 0 : (void)tile;
201 0 : return sizeof(fd_diag_tile_t);
202 0 : }
203 :
204 : static void
205 0 : during_housekeeping( fd_diag_tile_t * ctx ) {
206 0 : if( FD_UNLIKELY( fd_clock_tile_recal_due( ctx->clock ) ) ) fd_clock_tile_recal( ctx->clock );
207 0 : }
208 :
209 : static inline long
210 0 : next_deadline( fd_diag_tile_t * ctx ) {
211 0 : return fd_clock_tile_wallclock_to_tickcount( ctx->clock, ctx->next_report_nanos );
212 0 : }
213 :
214 : static int
215 : read_stat_file( int fd,
216 : ulong ns_per_tick,
217 0 : volatile ulong * metrics ) {
218 0 : if( FD_UNLIKELY( -1==lseek( fd, 0, SEEK_SET ) ) ) FD_LOG_ERR(( "lseek failed (%i-%s)", errno, strerror( errno ) ));
219 :
220 0 : char contents[ 4096 ] = {0};
221 0 : ulong contents_len = 0UL;
222 :
223 0 : while( 1 ) {
224 0 : if( FD_UNLIKELY( contents_len>=sizeof( contents ) ) ) FD_LOG_ERR(( "stat contents overflow" ));
225 0 : long n = read( fd, contents + contents_len, sizeof( contents ) - contents_len );
226 0 : if( FD_UNLIKELY( -1==n ) ) {
227 0 : if( FD_UNLIKELY( errno==ESRCH ) ) return 1;
228 0 : FD_LOG_ERR(( "read failed (%i-%s)", errno, strerror( errno ) ));
229 0 : }
230 0 : if( FD_LIKELY( 0==n ) ) break;
231 0 : contents_len += (ulong)n;
232 0 : }
233 :
234 : /* Parse stat file: fields are space-separated.
235 : Field 10 (1-indexed) = minflt, field 12 = majflt,
236 : field 14 = utime, field 15 = stime (all in clock ticks). */
237 0 : char * saveptr;
238 0 : char * token = strtok_r( contents, " ", &saveptr );
239 0 : ulong field_idx = 0UL;
240 :
241 0 : while( token ) {
242 0 : if( FD_UNLIKELY( 9UL==field_idx ) ) {
243 0 : char * endptr;
244 0 : ulong minflt = strtoul( token, &endptr, 10 );
245 0 : if( FD_UNLIKELY( *endptr!='\0' || minflt==ULONG_MAX ) ) FD_LOG_ERR(( "strtoul failed for minflt" ));
246 0 : metrics[ FD_METRICS_COUNTER_TILE_PAGE_FAULT_MINOR_OFF ] = minflt;
247 0 : } else if( FD_UNLIKELY( 11UL==field_idx ) ) {
248 0 : char * endptr;
249 0 : ulong majflt = strtoul( token, &endptr, 10 );
250 0 : if( FD_UNLIKELY( *endptr!='\0' || majflt==ULONG_MAX ) ) FD_LOG_ERR(( "strtoul failed for majflt" ));
251 0 : metrics[ FD_METRICS_COUNTER_TILE_PAGE_FAULT_MAJOR_OFF ] = majflt;
252 0 : } else if( FD_UNLIKELY( 13UL==field_idx ) ) {
253 0 : char * endptr;
254 0 : ulong utime_ticks = strtoul( token, &endptr, 10 );
255 0 : if( FD_UNLIKELY( *endptr!='\0' || utime_ticks==ULONG_MAX ) ) FD_LOG_ERR(( "strtoul failed for utime" ));
256 0 : metrics[ FD_METRICS_COUNTER_TILE_THREAD_DURATION_NANOS_USER_OFF ] = utime_ticks*ns_per_tick;
257 0 : } else if( FD_UNLIKELY( 14UL==field_idx ) ) {
258 0 : char * endptr;
259 0 : ulong stime_ticks = strtoul( token, &endptr, 10 );
260 0 : if( FD_UNLIKELY( *endptr!='\0' || stime_ticks==ULONG_MAX ) ) FD_LOG_ERR(( "strtoul failed for stime" ));
261 0 : metrics[ FD_METRICS_COUNTER_TILE_THREAD_DURATION_NANOS_SYSTEM_OFF ] = stime_ticks*ns_per_tick;
262 0 : } else if( FD_UNLIKELY( 38UL==field_idx ) ) {
263 0 : char * endptr;
264 0 : ulong last_cpu = strtoul( token, &endptr, 10 );
265 0 : if( FD_UNLIKELY( *endptr!='\0' || last_cpu==ULONG_MAX ) ) FD_LOG_ERR(( "strtoul failed for processor" ));
266 0 : metrics[ FD_METRICS_GAUGE_TILE_LAST_CPU_INDEX_OFF ] = last_cpu;
267 0 : break; /* No need to parse stat further */
268 0 : }
269 0 : token = strtok_r( NULL, " ", &saveptr );
270 0 : field_idx++;
271 0 : }
272 :
273 0 : if( FD_UNLIKELY( field_idx!=38UL ) ) FD_LOG_ERR(( "failed to parse /proc/<pid>/task/<tid>/stat" ));
274 :
275 0 : return 0;
276 0 : }
277 :
278 : /* read_schedstat_file reads /proc/<pid>/task/<tid>/schedstat: run_delay
279 : is the counter /proc/<tid>/sched prints as wait_sum, in nanoseconds. */
280 :
281 : static int
282 : read_schedstat_file( int fd,
283 0 : volatile ulong * metrics ) {
284 0 : if( FD_UNLIKELY( -1==lseek( fd, 0, SEEK_SET ) ) ) FD_LOG_ERR(( "lseek failed (%i-%s)", errno, strerror( errno ) ));
285 :
286 0 : char contents[ 128 ] = {0};
287 0 : ulong contents_len = 0UL;
288 :
289 0 : while( 1 ) {
290 0 : if( FD_UNLIKELY( contents_len>=sizeof( contents ) ) ) FD_LOG_ERR(( "schedstat contents overflow" ));
291 0 : long n = read( fd, contents + contents_len, sizeof( contents ) - contents_len );
292 0 : if( FD_UNLIKELY( -1==n ) ) {
293 0 : if( FD_UNLIKELY( errno==EINTR ) ) continue;
294 0 : if( FD_UNLIKELY( errno==ESRCH ) ) return 1;
295 0 : FD_LOG_ERR(( "read failed (%i-%s)", errno, strerror( errno ) ));
296 0 : }
297 0 : if( FD_LIKELY( 0==n ) ) break;
298 0 : contents_len += (ulong)n;
299 0 : }
300 :
301 0 : char * endptr;
302 0 : ulong run_time = strtoul( contents, &endptr, 10 );
303 0 : if( FD_UNLIKELY( ' '!=*endptr || run_time==ULONG_MAX ) ) FD_LOG_ERR(( "failed to parse schedstat sum_exec_runtime" ));
304 0 : ulong run_delay = strtoul( endptr+1, &endptr, 10 );
305 0 : if( FD_UNLIKELY( ' '!=*endptr || run_delay==ULONG_MAX ) ) FD_LOG_ERR(( "failed to parse schedstat run_delay" ));
306 0 : metrics[ FD_METRICS_COUNTER_TILE_THREAD_DURATION_NANOS_WAIT_OFF ] = run_delay;
307 0 : return 0;
308 0 : }
309 :
310 : static int
311 : read_sched_file( int fd,
312 0 : volatile ulong * metrics ) {
313 0 : if( FD_UNLIKELY( -1==lseek( fd, 0, SEEK_SET ) ) ) FD_LOG_ERR(( "lseek failed (%i-%s)", errno, strerror( errno ) ));
314 :
315 0 : char contents[ 8192 ] = {0};
316 0 : ulong contents_len = 0UL;
317 :
318 0 : while( 1 ) {
319 0 : if( FD_UNLIKELY( contents_len>=sizeof( contents ) ) ) FD_LOG_ERR(( "sched contents overflow" ));
320 0 : long n = read( fd, contents + contents_len, sizeof( contents ) - contents_len );
321 0 : if( FD_UNLIKELY( -1==n ) ) {
322 0 : if( FD_UNLIKELY( errno==ESRCH ) ) return 1;
323 0 : FD_LOG_ERR(( "read failed (%i-%s)", errno, strerror( errno ) ));
324 0 : }
325 0 : if( FD_LIKELY( 0==n ) ) break;
326 0 : contents_len += (ulong)n;
327 0 : }
328 :
329 0 : int found_voluntary = 0;
330 0 : int found_involuntary = 0;
331 :
332 0 : char * line = contents;
333 0 : while( 1 ) {
334 0 : char * next_line = strchr( line, '\n' );
335 0 : if( FD_UNLIKELY( NULL==next_line ) ) break;
336 0 : *next_line = '\0';
337 :
338 0 : if( FD_UNLIKELY( !strncmp( line, "nr_voluntary_switches", 21UL ) ) ) {
339 0 : char * colon = strchr( line, ':' );
340 0 : if( FD_LIKELY( colon ) ) {
341 0 : char * value = colon + 1;
342 0 : while( ' '==*value || '\t'==*value ) value++;
343 0 : char * endptr;
344 0 : ulong voluntary_switches = strtoul( value, &endptr, 10 );
345 0 : if( FD_UNLIKELY( '\0'!=*endptr ) ) FD_LOG_ERR(( "unexpected char after nr_voluntary_switches" ));
346 0 : if( FD_UNLIKELY( voluntary_switches==ULONG_MAX ) ) FD_LOG_ERR(( "strtoul overflow for nr_voluntary_switches" ));
347 0 : metrics[ FD_METRICS_COUNTER_TILE_CONTEXT_SWITCH_VOLUNTARY_OFF ] = voluntary_switches;
348 0 : found_voluntary = 1;
349 0 : }
350 0 : } else if( FD_UNLIKELY( !strncmp( line, "nr_involuntary_switches", 23UL ) ) ) {
351 0 : char * colon = strchr( line, ':' );
352 0 : if( FD_LIKELY( colon ) ) {
353 0 : char * value = colon + 1;
354 0 : while( ' '==*value || '\t'==*value ) value++;
355 0 : char * endptr;
356 0 : ulong involuntary_switches = strtoul( value, &endptr, 10 );
357 0 : if( FD_UNLIKELY( '\0'!=*endptr ) ) FD_LOG_ERR(( "unexpected char after nr_involuntary_switches" ));
358 0 : if( FD_UNLIKELY( involuntary_switches==ULONG_MAX ) ) FD_LOG_ERR(( "strtoul overflow for nr_involuntary_switches" ));
359 0 : metrics[ FD_METRICS_COUNTER_TILE_CONTEXT_SWITCH_INVOLUNTARY_OFF ] = involuntary_switches;
360 0 : found_involuntary = 1;
361 0 : }
362 0 : }
363 :
364 0 : line = next_line + 1;
365 0 : }
366 :
367 0 : if( FD_UNLIKELY( !found_voluntary ) ) FD_LOG_ERR(( "nr_voluntary_switches not found in sched file" ));
368 0 : if( FD_UNLIKELY( !found_involuntary ) ) FD_LOG_ERR(( "nr_involuntary_switches not found in sched file" ));
369 :
370 0 : return 0;
371 0 : }
372 :
373 : static ulong
374 0 : check_builder_status( fd_diag_tile_t * ctx ) {
375 0 : (void)ctx;
376 0 : return FD_DIAG_BUILDER_STATUS_DISABLED;
377 0 : }
378 :
379 : static void
380 0 : check_engine_metric( fd_diag_tile_t * ctx, long now ) {
381 0 : static ulong const replay_distance_threshold = 12UL;
382 0 : static long const replay_stall_threshold_ns = 12L*1000L*1000L*1000L;
383 0 : static long const turbine_stall_threshold_ns = 12L*1000L*1000L*1000L;
384 0 : static long const turbine_byte_cmp_window_ns = 12L*1000L*1000L*1000L;
385 :
386 0 : ulong bundle_cnt = ctx->tiles.bundle_cnt;
387 0 : ulong bundle_status = FD_DIAG_BUNDLE_STATUS_DISABLED;
388 0 : if( FD_LIKELY( bundle_cnt ) ) {
389 : /* Find the best state across all bundle tiles.
390 : Priority: connected > sleeping > connecting > disconnected */
391 0 : int any_connected = 0;
392 0 : int any_sleeping = 0;
393 0 : int any_connecting = 0;
394 0 : for( ulong i=0UL; i<bundle_cnt; i++ ) {
395 0 : volatile ulong * m = ctx->metrics[ ctx->tiles.bundle_tile_idx[ i ] ];
396 0 : ulong state = m[ FD_METRICS_GAUGE_BUNDLE_CONN_STATE_OFF ];
397 0 : if( FD_LIKELY( state==FD_BUNDLE_STATE_CONNECTED ) ) any_connected = 1;
398 0 : else if( state==FD_BUNDLE_STATE_SLEEPING ) any_sleeping = 1;
399 0 : else if( state==FD_BUNDLE_STATE_CONNECTING ) any_connecting = 1;
400 0 : }
401 0 : if( any_connected ) bundle_status = FD_DIAG_BUNDLE_STATUS_CONNECTED;
402 0 : else if( any_sleeping ) bundle_status = FD_DIAG_BUNDLE_STATUS_SLEEPING;
403 0 : else if( any_connecting ) bundle_status = FD_DIAG_BUNDLE_STATUS_CONNECTING;
404 0 : else bundle_status = FD_DIAG_BUNDLE_STATUS_DISCONNECTED;
405 0 : }
406 :
407 0 : ulong votor_idx = ctx->tiles.votor_idx;
408 0 : ulong vote_status = FD_DIAG_VOTE_STATUS_DISABLED;
409 :
410 0 : if( FD_UNLIKELY( ctx->is_voting && votor_idx!=ULONG_MAX ) ) {
411 0 : ulong replay_idx_ag = ctx->tiles.replay_idx;
412 0 : if( FD_UNLIKELY( ctx->metrics[ votor_idx ][ FD_METRICS_GAUGE_TILE_STATE_OFF ]!=1UL || replay_idx_ag==ULONG_MAX ) ) {
413 0 : vote_status = FD_DIAG_VOTE_STATUS_NOT_STARTED;
414 0 : } else if( FD_UNLIKELY( ctx->metrics[ votor_idx ][ FD_METRICS_GAUGE_VOTOR_RANK_OFF ]==ULONG_MAX ) ) {
415 : /* Not in the rank map, so
416 : we cannot be delinquent: report non-voting. */
417 0 : vote_status = FD_DIAG_VOTE_STATUS_DISABLED;
418 0 : } else {
419 0 : volatile ulong * m = ctx->metrics[ replay_idx_ag ];
420 0 : ulong vote_slot = m[ FD_METRICS_GAUGE_REPLAY_LAST_REWARDED_VOTE_SLOT_OFF ];
421 0 : ulong replay_slot = m[ FD_METRICS_GAUGE_REPLAY_RESET_SLOT_OFF ];
422 0 : if( FD_UNLIKELY( !vote_slot || !replay_slot ) ) {
423 0 : vote_status = FD_DIAG_VOTE_STATUS_NOT_STARTED;
424 0 : } else {
425 0 : int current = fd_int_if( replay_slot>=128UL, vote_slot+128UL>replay_slot, vote_slot>0UL );
426 0 : vote_status = fd_ulong_if( current, FD_DIAG_VOTE_STATUS_VOTING, FD_DIAG_VOTE_STATUS_DELINQUENT );
427 0 : }
428 0 : }
429 0 : }
430 :
431 : /* Votes land but earn nothing: the stake is not admitted. */
432 0 : if( FD_UNLIKELY( vote_status==FD_DIAG_VOTE_STATUS_VOTING &&
433 0 : ctx->tiles.replay_idx!=ULONG_MAX &&
434 0 : ctx->metrics[ ctx->tiles.replay_idx ][ FD_METRICS_GAUGE_REPLAY_IS_VOTE_ACCOUNT_INADMISSIBLE_OFF ] ) ) {
435 0 : vote_status = FD_DIAG_VOTE_STATUS_INADMISSIBLE;
436 0 : }
437 :
438 0 : ulong replay_idx = ctx->tiles.replay_idx;
439 0 : int replay_running = replay_idx!=ULONG_MAX && ctx->metrics[ replay_idx ][ FD_METRICS_GAUGE_TILE_STATE_OFF ]==1UL;
440 0 : ulong replay_status = FD_DIAG_REPLAY_STATUS_DISABLED;
441 0 : if( FD_LIKELY( replay_idx!=ULONG_MAX ) ) {
442 0 : if( FD_UNLIKELY( !replay_running ) ) {
443 0 : replay_status = FD_DIAG_REPLAY_STATUS_NOT_STARTED;
444 0 : } else {
445 0 : volatile ulong * m = ctx->metrics[ replay_idx ];
446 0 : ulong turbine_slot = m[ FD_METRICS_GAUGE_REPLAY_LAST_REASSEMBLY_DEQUEUED_SLOT_OFF ];
447 0 : ulong reset_slot = m[ FD_METRICS_GAUGE_REPLAY_RESET_SLOT_OFF ];
448 0 : if( FD_UNLIKELY( reset_slot!=ctx->check_engine.prev_reset_slot ) ) {
449 0 : ctx->check_engine.prev_reset_slot = reset_slot;
450 0 : ctx->check_engine.reset_slot_changed_ns = now;
451 0 : }
452 0 : if( FD_UNLIKELY( (turbine_slot==0UL) || (reset_slot==0UL) ) ) {
453 0 : replay_status = FD_DIAG_REPLAY_STATUS_NOT_STARTED;
454 0 : } else if( FD_UNLIKELY( ((turbine_slot>reset_slot) && (turbine_slot-reset_slot>replay_distance_threshold)) ||
455 0 : (now-ctx->check_engine.reset_slot_changed_ns>replay_stall_threshold_ns) ) ) {
456 0 : replay_status = FD_DIAG_REPLAY_STATUS_BEHIND;
457 0 : } else {
458 0 : replay_status = FD_DIAG_REPLAY_STATUS_RUNNING;
459 0 : }
460 0 : }
461 0 : }
462 :
463 0 : ulong shred_cnt = ctx->tiles.shred_cnt;
464 0 : ulong turbine_status = FD_DIAG_TURBINE_STATUS_DISABLED;
465 0 : if( FD_LIKELY( replay_idx!=ULONG_MAX && shred_cnt>0UL ) ) {
466 0 : if( FD_UNLIKELY( !replay_running ) ) {
467 0 : turbine_status = FD_DIAG_TURBINE_STATUS_NOT_STARTED;
468 0 : } else {
469 0 : int all_shred_running = 1;
470 0 : ulong cur_turbine_bytes = 0UL, cur_repair_bytes = 0UL;
471 0 : for( ulong i=0UL; i<shred_cnt; i++ ) {
472 0 : volatile ulong * sm = ctx->metrics[ ctx->tiles.shred_tile_idx[ i ] ];
473 0 : cur_turbine_bytes += sm[ FD_METRICS_COUNTER_SHRED_TURBINE_RX_BYTES_OFF ];
474 0 : cur_repair_bytes += sm[ FD_METRICS_COUNTER_SHRED_REPAIR_RX_BYTES_OFF ];
475 0 : if( FD_UNLIKELY( sm[ FD_METRICS_GAUGE_TILE_STATE_OFF ]!=1UL ) ) {
476 0 : all_shred_running = 0;
477 0 : break;
478 0 : }
479 0 : }
480 0 : if( FD_UNLIKELY( !all_shred_running ) ) {
481 0 : turbine_status = FD_DIAG_TURBINE_STATUS_NOT_STARTED;
482 0 : } else {
483 0 : ulong turbine_slot = ctx->metrics[ replay_idx ][ FD_METRICS_GAUGE_REPLAY_LAST_REASSEMBLY_DEQUEUED_SLOT_OFF ];
484 0 : if( FD_UNLIKELY( turbine_slot!=ctx->check_engine.prev_turbine_slot ) ) {
485 0 : ctx->check_engine.prev_turbine_slot = turbine_slot;
486 0 : ctx->check_engine.turbine_slot_changed_ns = now;
487 0 : }
488 0 : if( FD_UNLIKELY( now-ctx->check_engine.byte_snapshot_ns>=turbine_byte_cmp_window_ns ) ) {
489 0 : ctx->check_engine.repair_outpacing = (cur_repair_bytes-ctx->check_engine.snapshot_repair_bytes)>(cur_turbine_bytes-ctx->check_engine.snapshot_turbine_bytes);
490 0 : ctx->check_engine.snapshot_turbine_bytes = cur_turbine_bytes;
491 0 : ctx->check_engine.snapshot_repair_bytes = cur_repair_bytes;
492 0 : ctx->check_engine.byte_snapshot_ns = now;
493 0 : }
494 :
495 0 : if( FD_UNLIKELY( turbine_slot==0UL ) ) {
496 0 : turbine_status = FD_DIAG_TURBINE_STATUS_NOT_STARTED;
497 0 : } else if( FD_UNLIKELY( now-ctx->check_engine.turbine_slot_changed_ns>turbine_stall_threshold_ns ) ) {
498 0 : turbine_status = FD_DIAG_TURBINE_STATUS_STALLED;
499 0 : } else if( FD_UNLIKELY( ctx->check_engine.repair_outpacing ) ) {
500 0 : turbine_status = FD_DIAG_TURBINE_STATUS_REPAIR_OUTPACING;
501 0 : } else {
502 0 : turbine_status = FD_DIAG_TURBINE_STATUS_RUNNING;
503 0 : }
504 0 : }
505 0 : }
506 0 : }
507 :
508 0 : FD_MGAUGE_SET( DIAG, BUNDLE_STATE, bundle_status );
509 0 : FD_MGAUGE_SET( DIAG, VOTE_STATE, vote_status );
510 0 : FD_MGAUGE_SET( DIAG, REPLAY_STATE, replay_status );
511 0 : FD_MGAUGE_SET( DIAG, TURBINE_STATE, turbine_status );
512 0 : FD_MGAUGE_SET( DIAG, BUILDER_STATE, check_builder_status( ctx ) );
513 0 : }
514 :
515 : static void
516 : cpu_owner_refresh( fd_diag_tile_t * ctx,
517 0 : fd_topo_t const * topo ) {
518 0 : ushort owner[ FD_TILE_MAX ];
519 0 : ushort cnt [ FD_TILE_MAX ] = {0};
520 0 : fd_cpuset_new( ctx->cpu_live_tile );
521 0 : for( ulong i=0UL; i<ctx->tile_cnt; i++ ) {
522 0 : if( topo ) {
523 0 : ulong cpu_idx = topo->tiles[ i ].cpu_idx;
524 0 : ctx->tile_cpu[ i ] = (ushort)fd_ulong_if( cpu_idx<FD_TILE_MAX && !topo->tiles[ i ].floats, cpu_idx, USHORT_MAX );
525 0 : }
526 0 : ulong cpu_idx = ctx->tile_cpu[ i ];
527 0 : if( cpu_idx==USHORT_MAX || ( !topo && 2UL==ctx->metrics[ i ][ FD_METRICS_GAUGE_TILE_STATE_OFF ] ) ) continue;
528 0 : owner[ cpu_idx ] = (ushort)i;
529 0 : cnt [ cpu_idx ]++;
530 0 : fd_cpuset_insert( ctx->cpu_live_tile, cpu_idx );
531 0 : }
532 0 : for( ulong c=0UL; c<FD_TILE_MAX; c++ ) {
533 0 : ushort o = cnt[ c ]==1 ? owner[ c ] : USHORT_MAX;
534 0 : if( FD_LIKELY( o==ctx->cpu_to_tile[ c ] ) ) continue;
535 0 : ctx->cpu_to_tile[ c ] = o;
536 0 : if( !topo ) { /* the new owner counts from its next sample */
537 0 : fd_cpuset_insert( ctx->irq_rebase, c );
538 0 : fd_cpuset_insert( ctx->tick_rebase, c );
539 0 : }
540 0 : }
541 0 : }
542 :
543 : static void
544 0 : irq_metrics( fd_diag_tile_t * ctx ) {
545 0 : if( FD_UNLIKELY( -1==lseek( ctx->proc_softirqs_fd, 0, SEEK_SET ) ) ) FD_LOG_ERR(( "lseek failed (%i-%s)", errno, strerror( errno ) ));
546 0 : ulong softirq_cpu_cnt = fd_proc_softirqs_sum( ctx->proc_softirqs_fd, ctx->irq_cnt );
547 0 : if( FD_UNLIKELY( !softirq_cpu_cnt ) ) return; /* parse fail */
548 :
549 0 : ulong volatile * softirq_total = &fd_metrics_tl[ MIDX( COUNTER, DIAG, SYSTEM_SOFTIRQ ) ];
550 0 : ulong volatile * softirq_undesired = &fd_metrics_tl[ MIDX( COUNTER, DIAG, SOFTIRQ_ON_TILE_CPU ) ];
551 0 : for( ulong j=0UL; j<FD_METRICS_ENUM_SOFTIRQ_TYPE_CNT; j++ ) {
552 0 : ulong tot_cnt = 0UL;
553 0 : for( ulong i=0UL; i<softirq_cpu_cnt; i++ ) {
554 0 : tot_cnt += fd_ulong_sat_sub( ctx->irq_cnt[ j ][ i ], ctx->softirq_boot[ j ][ i ] );
555 0 : ulong delta = fd_ulong_sat_sub( ctx->irq_cnt[ j ][ i ], ctx->softirq_baseline[ j ][ i ] );
556 0 : if( fd_cpuset_test( ctx->cpu_live_tile, i ) ) ctx->on_tile_softirq[ j ] += delta;
557 0 : ctx->softirq_baseline[ j ][ i ] = ctx->irq_cnt[ j ][ i ];
558 0 : }
559 0 : softirq_total [ j ] = tot_cnt;
560 0 : softirq_undesired[ j ] = ctx->on_tile_softirq[ j ];
561 0 : }
562 :
563 0 : ulong * cpu_irq = ctx->irq_cnt[ 0 ]; /* re-use as scratch memory */
564 0 : ulong * cpu_tlb = ctx->irq_cnt[ 1 ];
565 0 : ulong * cpu_loc = ctx->irq_cnt[ 2 ];
566 0 : if( FD_UNLIKELY( -1==lseek( ctx->proc_interrupts_fd, 0, SEEK_SET ) ) ) FD_LOG_ERR(( "lseek failed (%i-%s)", errno, strerror( errno ) ));
567 0 : ulong cpu_cnt = fd_proc_interrupts_read( ctx->proc_interrupts_fd, cpu_irq, cpu_tlb, cpu_loc );
568 0 : if( FD_UNLIKELY( !cpu_cnt ) ) return; /* parse fail */
569 :
570 0 : ulong tot_cnt = 0UL;
571 0 : for( ulong i=0UL; i<cpu_cnt; i++ ) {
572 0 : tot_cnt += fd_ulong_sat_sub( cpu_irq[ i ], ctx->device_irq_boot[ i ] );
573 0 : ulong delta = fd_ulong_sat_sub( cpu_irq[ i ], ctx->device_irq_baseline[ i ] );
574 0 : if( fd_cpuset_test( ctx->cpu_live_tile, i ) ) ctx->on_tile_device_irq += delta;
575 0 : ctx->device_irq_baseline[ i ] = cpu_irq[ i ];
576 0 : }
577 0 : FD_MCNT_SET( DIAG, SYSTEM_DEVICE_IRQ, tot_cnt );
578 0 : FD_MCNT_SET( DIAG, DEVICE_IRQ_ON_TILE_CPU, ctx->on_tile_device_irq );
579 :
580 0 : for( ulong i=0UL; i<cpu_cnt; i++ ) {
581 0 : ulong tile_id = ctx->cpu_to_tile[ i ];
582 0 : if( tile_id==USHORT_MAX ) continue;
583 0 : if( FD_UNLIKELY( fd_cpuset_test( ctx->irq_rebase, i ) ) ) {
584 0 : ctx->owner_irq_baseline[ i ] = cpu_irq[ i ];
585 0 : ctx->owner_tlb_baseline[ i ] = cpu_tlb[ i ];
586 0 : ctx->owner_loc_baseline[ i ] = cpu_loc[ i ];
587 0 : fd_cpuset_remove( ctx->irq_rebase, i );
588 0 : }
589 0 : ctx->metrics[ tile_id ][ FD_METRICS_COUNTER_TILE_DEVICE_IRQ_OFF ] = fd_ulong_sat_sub( cpu_irq[ i ], ctx->owner_irq_baseline[ i ] );
590 0 : ctx->metrics[ tile_id ][ FD_METRICS_COUNTER_TILE_TLB_SHOOTDOWN_OFF ] = fd_ulong_sat_sub( cpu_tlb[ i ], ctx->owner_tlb_baseline[ i ] );
591 0 : ctx->metrics[ tile_id ][ FD_METRICS_COUNTER_TILE_TIMER_IRQ_OFF ] = fd_ulong_sat_sub( cpu_loc[ i ], ctx->owner_loc_baseline[ i ] );
592 0 : }
593 0 : }
594 :
595 : /* interrupt_metrics reads per-CPU irq+softirq+steal tick counts from
596 : /proc/stat and publishes them as the INTERRUPT CPU regime for
597 : fixed tiles. On kernels with CONFIG_IRQ_TIME_ACCOUNTING (near
598 : universal), these buckets are disjoint from utime/stime so
599 : idle = lifetime - user - system - wait - interrupt
600 : is exact up to sampling granularity; without it, the irq columns
601 : undercount and interrupt reads near zero (degrades gracefully). */
602 :
603 : static void
604 0 : interrupt_metrics( fd_diag_tile_t * ctx ) {
605 0 : ulong cpu_ticks[ FD_TILE_MAX ];
606 0 : if( FD_UNLIKELY( -1==lseek( ctx->proc_stat_fd, 0, SEEK_SET ) ) ) FD_LOG_ERR(( "lseek failed (%i-%s)", errno, strerror( errno ) ));
607 0 : ulong cpu_cnt = fd_proc_stat_irq_ticks( ctx->proc_stat_fd, cpu_ticks );
608 0 : if( FD_UNLIKELY( !cpu_cnt ) ) return; /* parse fail */
609 :
610 0 : for( ulong i=0UL; i<cpu_cnt; i++ ) {
611 0 : ulong tile_id = ctx->cpu_to_tile[ i ];
612 0 : if( tile_id==USHORT_MAX ) continue;
613 0 : if( FD_UNLIKELY( fd_cpuset_test( ctx->tick_rebase, i ) ) ) {
614 0 : ctx->owner_tick_baseline[ i ] = cpu_ticks[ i ];
615 0 : fd_cpuset_remove( ctx->tick_rebase, i );
616 0 : }
617 : /* CLK_TCK is always 100, so 1 tick = 10ms = 10,000,000 ns */
618 0 : ulong since = fd_ulong_sat_sub( cpu_ticks[ i ], ctx->owner_tick_baseline[ i ] );
619 0 : ctx->metrics[ tile_id ][ FD_METRICS_COUNTER_TILE_THREAD_DURATION_NANOS_INTERRUPT_OFF ] = since*10000000UL;
620 0 : }
621 0 : }
622 :
623 : static ulong
624 : read_text( int fd,
625 : char * buf,
626 0 : ulong buf_sz ) {
627 0 : if( FD_UNLIKELY( -1==lseek( fd, 0, SEEK_SET ) ) ) FD_LOG_ERR(( "lseek failed (%i-%s)", errno, strerror( errno ) ));
628 0 : ulong len;
629 0 : int err = fd_io_read( fd, buf, buf_sz-1UL, buf_sz-1UL, &len );
630 0 : if( FD_UNLIKELY( err>0 ) ) FD_LOG_ERR(( "fd_io_read failed (%i-%s)", err, fd_io_strerror( err ) ));
631 0 : buf[ len ] = '\0';
632 0 : return len;
633 0 : }
634 :
635 : static void
636 : add_numa_bytes( fd_diag_tile_t * ctx,
637 : ulong tile_idx,
638 : ulong numa_slot,
639 0 : ulong bytes ) {
640 0 : if( FD_UNLIKELY( tile_idx==DIAG_WKSP_TILE_IDX_SHARED ) ) {
641 0 : ctx->system_resources.numa_mem[ numa_slot ].shared_bytes += bytes;
642 0 : return;
643 0 : }
644 :
645 0 : ushort * entry_idx = &ctx->numa_entry_idx[ tile_idx ][ numa_slot ];
646 0 : if( FD_UNLIKELY( *entry_idx==USHORT_MAX ) ) {
647 0 : FD_TEST( ctx->system_resources.tile_mem_cnt<FD_DIAG_SYSTEM_TILE_MEM_MAX );
648 0 : *entry_idx = (ushort)ctx->system_resources.tile_mem_cnt++;
649 0 : fd_diag_system_tile_mem_t * entry = &ctx->system_resources.tile_mem[ *entry_idx ];
650 0 : entry->tile_idx = (ushort)tile_idx;
651 0 : entry->numa_idx = ctx->numa.node[ numa_slot ].idx;
652 0 : }
653 0 : ctx->system_resources.tile_mem[ *entry_idx ].allocated_bytes += bytes;
654 0 : }
655 :
656 : static void
657 0 : add_configured_memory_bytes( fd_diag_tile_t * ctx ) {
658 : /* Workspaces and tile stacks are hugetlbfs-backed, so their configured
659 : sizes are also their resident physical-memory footprint. */
660 0 : for( ulong i=0UL; i<ctx->memory.cnt; i++ ) {
661 0 : if( FD_UNLIKELY( ctx->memory.wksp[ i ].numa_slot==USHORT_MAX ) ) continue;
662 0 : add_numa_bytes( ctx,
663 0 : ctx->memory.wksp[ i ].tile_idx,
664 0 : ctx->memory.wksp[ i ].numa_slot,
665 0 : ctx->memory.wksp[ i ].bytes );
666 0 : }
667 0 : for( ulong tile_idx=0UL; tile_idx<ctx->tile_cnt; tile_idx++ ) {
668 0 : ushort numa_slot = ctx->memory.stack_numa_slot[ tile_idx ];
669 0 : if( FD_UNLIKELY( numa_slot==USHORT_MAX ) ) continue;
670 0 : add_numa_bytes( ctx, tile_idx, numa_slot, FD_TILE_PRIVATE_STACK_SZ );
671 0 : }
672 0 : }
673 :
674 : static void
675 0 : sample_disk( fd_diag_tile_t * ctx ) {
676 0 : ctx->system_resources.mount_cnt = (uint)ctx->mount_cnt;
677 0 : ctx->system_resources.file_cnt = (uint)ctx->file_cnt;
678 :
679 0 : for( ulong i=0UL; i<ctx->mount_cnt; i++ ) {
680 0 : struct statfs st;
681 0 : if( FD_UNLIKELY( fstatfs( ctx->mounts[ i ].fd, &st ) ) ) FD_LOG_ERR(( "fstatfs failed (%i-%s)", errno, strerror( errno ) ));
682 0 : ulong block_sz = (ulong)( st.f_frsize ? st.f_frsize : st.f_bsize );
683 0 : fd_diag_system_mount_t * mount = &ctx->system_resources.mount[ i ];
684 0 : fd_cstr_ncpy( mount->path, ctx->mounts[ i ].path, sizeof(mount->path) );
685 0 : mount->total_bytes = (ulong)st.f_blocks*block_sz;
686 0 : mount->free_bytes = (ulong)st.f_bfree *block_sz;
687 0 : mount->available_bytes = (ulong)st.f_bavail*block_sz;
688 0 : }
689 :
690 0 : for( ulong i=0UL; i<ctx->file_cnt; i++ ) {
691 0 : fd_diag_system_file_t * file = &ctx->system_resources.file[ i ];
692 0 : fd_cstr_ncpy( file->path, ctx->files[ i ].path, sizeof(file->path) );
693 0 : file->category = ctx->files[ i ].category;
694 0 : file->mount_idx = ctx->files[ i ].mount_idx;
695 0 : if( ctx->files[ i ].metric ) file->bytes = *ctx->files[ i ].metric;
696 0 : else if( ctx->files[ i ].data_fd>=0 ) {
697 0 : struct stat st;
698 0 : if( FD_UNLIKELY( syscall( SYS_fstat, ctx->files[ i ].data_fd, &st ) ) ) FD_LOG_ERR(( "fstat failed (%i-%s)", errno, strerror( errno ) ));
699 0 : file->bytes = (ulong)st.st_size;
700 0 : }
701 0 : }
702 0 : }
703 :
704 : static ulong
705 : read_meminfo_kib( char const * buf,
706 0 : char const * key ) {
707 0 : char const * p = strstr( buf, key );
708 0 : if( FD_UNLIKELY( !p ) ) return 0UL;
709 0 : p += strlen( key );
710 0 : while( *p==' ' || *p=='\t' || *p==':' ) p++;
711 0 : return strtoul( p, NULL, 10 );
712 0 : }
713 :
714 : static void
715 : sample_system( fd_diag_tile_t * ctx,
716 0 : long now ) {
717 0 : ctx->next_system_report_nanos = now + SYSTEM_REPORT_INTERVAL_NANOS;
718 :
719 0 : ctx->system_resources.mem_available_bytes = 0UL;
720 0 : ctx->system_resources.mem_free_bytes = 0UL;
721 0 : ctx->system_resources.numa_mem_cnt = (uint)ctx->numa.cnt;
722 0 : ctx->system_resources.tile_mem_cnt = 0U;
723 0 : ctx->system_resources.mount_cnt = 0U;
724 0 : ctx->system_resources.file_cnt = 0U;
725 0 : fd_memset( ctx->system_resources.numa_mem, 0, sizeof(ctx->system_resources.numa_mem) );
726 0 : fd_memset( ctx->system_resources.tile_mem, 0, sizeof(ctx->system_resources.tile_mem) );
727 0 : fd_memset( ctx->system_resources.mount, 0, sizeof(ctx->system_resources.mount) );
728 0 : fd_memset( ctx->system_resources.file, 0, sizeof(ctx->system_resources.file) );
729 0 : fd_memset( ctx->numa_entry_idx, 0xFF, sizeof(ctx->numa_entry_idx) );
730 0 : char meminfo[ 4096 ];
731 0 : if( FD_LIKELY( read_text( ctx->proc_meminfo_fd, meminfo, sizeof(meminfo) ) ) ) {
732 0 : ctx->system_resources.mem_available_bytes = read_meminfo_kib( meminfo, "MemAvailable" )<<10;
733 0 : ctx->system_resources.mem_free_bytes = read_meminfo_kib( meminfo, "MemFree" )<<10;
734 0 : }
735 0 : for( ulong i=0UL; i<ctx->numa.cnt; i++ ) {
736 0 : ulong numa_idx = ctx->numa.node[ i ].idx;
737 0 : fd_diag_system_numa_mem_t * numa = &ctx->system_resources.numa_mem[ i ];
738 0 : numa->numa_idx = (ushort)numa_idx;
739 :
740 0 : if( FD_UNLIKELY( !read_text( ctx->numa.node[ i ].meminfo_fd, meminfo, sizeof(meminfo) ) ) ) continue;
741 0 : char key[ 64 ];
742 0 : FD_TEST( fd_cstr_printf_check( key, sizeof(key), NULL, "Node %lu MemTotal", numa_idx ) );
743 0 : numa->total_bytes = read_meminfo_kib( meminfo, key )<<10;
744 0 : FD_TEST( fd_cstr_printf_check( key, sizeof(key), NULL, "Node %lu MemFree", numa_idx ) );
745 0 : numa->free_bytes = read_meminfo_kib( meminfo, key )<<10;
746 0 : }
747 0 : add_configured_memory_bytes( ctx );
748 0 : sample_disk( ctx );
749 0 : ctx->system_resources.sample_time_nanos = (ulong)now;
750 0 : }
751 :
752 : static void
753 : publish_system( fd_diag_tile_t * ctx,
754 : fd_stem_context_t * stem,
755 0 : long now ) {
756 0 : if( FD_LIKELY( now<ctx->next_system_report_nanos ) ) return;
757 0 : sample_system( ctx, now );
758 :
759 0 : fd_memcpy( fd_chunk_to_laddr( ctx->system_out.mem, ctx->system_out.chunk ),
760 0 : &ctx->system_resources, sizeof(fd_diag_system_resources_t) );
761 0 : fd_stem_publish( stem, ctx->system_out.idx, sizeof(fd_diag_system_resources_t),
762 0 : ctx->system_out.chunk, sizeof(fd_diag_system_resources_t), 0UL, 0UL,
763 0 : (ulong)fd_frag_meta_ts_comp( fd_tickcount() ) );
764 0 : ctx->system_out.chunk = fd_dcache_compact_next( ctx->system_out.chunk,
765 0 : sizeof(fd_diag_system_resources_t), ctx->system_out.chunk0, ctx->system_out.wmark );
766 0 : }
767 :
768 : static int
769 : sensor_read( int fd,
770 0 : long * out ) {
771 0 : char buf[ 32 ];
772 0 : if( FD_UNLIKELY( -1==lseek( fd, 0, SEEK_SET ) ) ) FD_LOG_ERR(( "lseek failed (%i-%s)", errno, strerror( errno ) ));
773 0 : long n = read( fd, buf, sizeof(buf)-1UL );
774 0 : if( FD_UNLIKELY( n<=0 ) ) return -1;
775 0 : buf[ n ] = '\0';
776 0 : *out = strtol( buf, NULL, 10 );
777 0 : return 0;
778 0 : }
779 :
780 : static int
781 : read_optional( int fd,
782 : char * buf,
783 0 : ulong buf_sz ) {
784 0 : if( FD_UNLIKELY( -1==lseek( fd, 0, SEEK_SET ) ) ) FD_LOG_ERR(( "lseek failed (%i-%s)", errno, strerror( errno ) ));
785 0 : ulong len = 0UL;
786 0 : while( len<buf_sz-1UL ) { /* procfs returns at most a page per read */
787 0 : long n = read( fd, buf+len, buf_sz-1UL-len );
788 0 : if( FD_UNLIKELY( n<0L && errno==EINTR ) ) continue;
789 0 : if( FD_UNLIKELY( n<0L ) ) return -1;
790 0 : if( !n ) break;
791 0 : len += (ulong)n;
792 0 : }
793 0 : buf[ len ] = '\0';
794 0 : return 0;
795 0 : }
796 :
797 : static ulong
798 : psi_total_usec( char const * buf,
799 0 : char const * level ) {
800 0 : char const * p = strstr( buf, level ); /* cpu has no full line before Linux 5.13 */
801 0 : if( FD_UNLIKELY( !p ) ) return 0UL;
802 0 : p = strstr( p, "total=" );
803 0 : if( FD_UNLIKELY( !p ) ) return 0UL;
804 0 : return strtoul( p+6, NULL, 10 );
805 0 : }
806 :
807 : static int
808 : host_cpu_ticks( fd_diag_tile_t * ctx,
809 : char * buf,
810 : ulong buf_sz,
811 0 : ulong * cpu ) {
812 0 : if( FD_UNLIKELY( !read_text( ctx->proc_stat_fd, buf, buf_sz ) || strncmp( buf, "cpu ", 4UL ) ) ) return -1;
813 0 : char * p = buf+4;
814 0 : ulong f[ 8 ];
815 0 : for( ulong i=0UL; i<8UL; i++ ) f[ i ] = strtoul( p, &p, 10 ); /* user nice system idle iowait irq softirq steal */
816 0 : cpu[ FD_METRICS_ENUM_SYSTEM_CPU_STATE_V_USER_IDX ] = f[ 0 ]+f[ 1 ];
817 0 : cpu[ FD_METRICS_ENUM_SYSTEM_CPU_STATE_V_SYSTEM_IDX ] = f[ 2 ];
818 0 : cpu[ FD_METRICS_ENUM_SYSTEM_CPU_STATE_V_IDLE_IDX ] = f[ 3 ];
819 0 : cpu[ FD_METRICS_ENUM_SYSTEM_CPU_STATE_V_IO_WAIT_IDX ] = f[ 4 ];
820 0 : cpu[ FD_METRICS_ENUM_SYSTEM_CPU_STATE_V_INTERRUPT_IDX ] = f[ 5 ]+f[ 6 ];
821 0 : cpu[ FD_METRICS_ENUM_SYSTEM_CPU_STATE_V_STEAL_IDX ] = f[ 7 ];
822 0 : return 0;
823 0 : }
824 :
825 : static void
826 0 : host_metrics( fd_diag_tile_t * ctx ) {
827 0 : char buf[ 16384 ]; /* /proc/vmstat is ~4 KiB and grows with kernel counters */
828 :
829 0 : if( FD_LIKELY( ctx->proc_loadavg_fd>=0 && !read_optional( ctx->proc_loadavg_fd, buf, sizeof(buf) ) ) ) {
830 0 : char * p = buf;
831 0 : ulong load[ FD_METRICS_ENUM_LOAD_AVERAGE_TYPE_CNT ];
832 0 : for( ulong i=0UL; i<FD_METRICS_ENUM_LOAD_AVERAGE_TYPE_CNT; i++ ) { double l = strtod( p, &p ); load[ i ] = l>0.0 ? (ulong)( l*100.0 + 0.5 ) : 0UL; }
833 0 : FD_MGAUGE_ENUM_COPY( DIAG, SYSTEM_LOAD_CENTI, load );
834 0 : FD_MGAUGE_SET( DIAG, THREAD_RUNNABLE_COUNT, strtoul( p, NULL, 10 ) ); /* "running/total" */
835 0 : }
836 :
837 0 : ulong cpu[ FD_METRICS_ENUM_SYSTEM_CPU_STATE_CNT ];
838 0 : if( FD_LIKELY( !host_cpu_ticks( ctx, buf, sizeof(buf), cpu ) ) ) {
839 : /* Since diag start, so nanos cannot overflow. iowait can go down
840 : (proc(5)), clamp in ticks so Prometheus never sees a reset. */
841 0 : for( ulong i=0UL; i<FD_METRICS_ENUM_SYSTEM_CPU_STATE_CNT; i++ ) {
842 0 : ctx->host_cpu_ticks[ i ] = fd_ulong_max( fd_ulong_sat_sub( cpu[ i ], ctx->host_cpu_baseline[ i ] ), ctx->host_cpu_ticks[ i ] );
843 0 : cpu[ i ] = ctx->host_cpu_ticks[ i ]*10000000UL; /* CLK_TCK is 100 */
844 0 : }
845 0 : FD_MCNT_ENUM_COPY( DIAG, SYSTEM_CPU_DURATION_NANOS, cpu );
846 0 : }
847 :
848 0 : if( FD_LIKELY( read_text( ctx->proc_meminfo_fd, buf, sizeof(buf) ) ) ) {
849 0 : FD_MGAUGE_SET( DIAG, MEMORY_AVAILABLE_BYTES, read_meminfo_kib( buf, "MemAvailable" )<<10 );
850 0 : FD_MGAUGE_SET( DIAG, MEMORY_FREE_BYTES, read_meminfo_kib( buf, "MemFree" )<<10 );
851 0 : }
852 :
853 0 : if( FD_LIKELY( ctx->proc_vmstat_fd>=0 && !read_optional( ctx->proc_vmstat_fd, buf, sizeof(buf) ) ) ) {
854 0 : ulong page_sz = (ulong)FD_SHMEM_NORMAL_PAGE_SZ;
855 0 : FD_MCNT_SET( DIAG, SWAP_IN_BYTES, read_meminfo_kib( buf, "\npswpin " )*page_sz );
856 0 : FD_MCNT_SET( DIAG, SWAP_OUT_BYTES, read_meminfo_kib( buf, "\npswpout " )*page_sz );
857 0 : FD_MCNT_SET( DIAG, SYSTEM_PAGE_FAULT_MAJOR, read_meminfo_kib( buf, "\npgmajfault " ) );
858 0 : FD_MCNT_SET( DIAG, OOM_KILLED, read_meminfo_kib( buf, "\noom_kill " ) );
859 0 : }
860 :
861 0 : if( ctx->psi_fd[ 0 ]>=0 && !read_optional( ctx->psi_fd[ 0 ], buf, sizeof(buf) ) ) {
862 0 : FD_MCNT_SET( DIAG, CPU_PRESSURE_DURATION_NANOS, psi_total_usec( buf, "some" )*1000UL );
863 0 : }
864 0 : if( ctx->psi_fd[ 1 ]>=0 && !read_optional( ctx->psi_fd[ 1 ], buf, sizeof(buf) ) ) {
865 0 : FD_MCNT_SET( DIAG, MEMORY_PRESSURE_DURATION_NANOS, psi_total_usec( buf, "some" )*1000UL );
866 0 : FD_MCNT_SET( DIAG, MEMORY_PRESSURE_FULL_DURATION_NANOS, psi_total_usec( buf, "full" )*1000UL );
867 0 : }
868 0 : if( ctx->psi_fd[ 2 ]>=0 && !read_optional( ctx->psi_fd[ 2 ], buf, sizeof(buf) ) ) {
869 0 : FD_MCNT_SET( DIAG, IO_PRESSURE_DURATION_NANOS, psi_total_usec( buf, "some" )*1000UL );
870 0 : FD_MCNT_SET( DIAG, IO_PRESSURE_FULL_DURATION_NANOS, psi_total_usec( buf, "full" )*1000UL );
871 0 : }
872 0 : }
873 :
874 : static void
875 : sensor_metrics( fd_diag_tile_t * ctx,
876 0 : long now ) {
877 0 : long val [ DIAG_SENSOR_KIND_CNT ] = {0}; /* hottest temperature, summed power and errors */
878 0 : int failed[ DIAG_SENSOR_KIND_CNT ] = {0};
879 0 : for( ulong i=0UL; i<ctx->sensor.cnt; i++ ) {
880 0 : long v;
881 0 : uint kind = ctx->sensor.kind[ i ];
882 0 : if( FD_UNLIKELY( sensor_read( ctx->sensor.fd[ i ], &v ) ) ) { failed[ kind ] = 1; continue; }
883 0 : if( kind>=DIAG_SENSOR_POWER ) val[ kind ] += fd_long_max( v, 0L );
884 0 : else val[ kind ] = fd_long_max( val[ kind ], v );
885 0 : }
886 0 : FD_MGAUGE_SET( DIAG, CPU_PACKAGE_MILLICELSIUS, (ulong)val[ DIAG_SENSOR_CPU_PACKAGE ] );
887 0 : FD_MGAUGE_SET( DIAG, NIC_MILLICELSIUS, (ulong)val[ DIAG_SENSOR_NIC ] );
888 0 : FD_MGAUGE_SET( DIAG, NVME_MILLICELSIUS, (ulong)val[ DIAG_SENSOR_NVME ] );
889 0 : FD_MGAUGE_SET( DIAG, SYSTEM_POWER_MICROWATTS, (ulong)val[ DIAG_SENSOR_POWER ] );
890 0 : if( FD_LIKELY( !failed[ DIAG_SENSOR_ECC_CE ] ) ) FD_MCNT_SET( DIAG, MEMORY_ECC_CORRECTED, (ulong)val[ DIAG_SENSOR_ECC_CE ] );
891 0 : if( FD_LIKELY( !failed[ DIAG_SENSOR_ECC_UE ] ) ) FD_MCNT_SET( DIAG, MEMORY_ECC_UNCORRECTED, (ulong)val[ DIAG_SENSOR_ECC_UE ] );
892 :
893 : /* Package power is the energy counter's rate since the last read */
894 0 : if( !ctx->sensor.rapl_cnt ) return;
895 0 : ulong energy = 0UL;
896 0 : for( ulong i=0UL; i<ctx->sensor.rapl_cnt; i++ ) {
897 0 : long e;
898 0 : if( FD_UNLIKELY( sensor_read( ctx->sensor.rapl_fd[ i ], &e ) || e<0L ) ) {
899 0 : ctx->sensor.rapl_prev_nanos = 0L; /* rebaseline */
900 0 : FD_MGAUGE_SET( DIAG, CPU_POWER_MICROWATTS, 0UL );
901 0 : return;
902 0 : }
903 0 : ulong cur = (ulong)e;
904 0 : ulong prev = ctx->sensor.rapl_prev[ i ];
905 0 : energy += cur>=prev ? cur-prev : cur+ctx->sensor.rapl_range[ i ]-prev;
906 0 : ctx->sensor.rapl_prev[ i ] = cur;
907 0 : }
908 0 : long dt = now - ctx->sensor.rapl_prev_nanos;
909 0 : if( FD_LIKELY( ctx->sensor.rapl_prev_nanos && dt>0L ) ) FD_MGAUGE_SET( DIAG, CPU_POWER_MICROWATTS, (ulong)( (double)energy*1e9/(double)dt ) );
910 0 : ctx->sensor.rapl_prev_nanos = now;
911 0 : }
912 :
913 : static void
914 : before_credit( fd_diag_tile_t * ctx,
915 : fd_stem_context_t * stem,
916 0 : int * charge_busy ) {
917 0 : (void)stem;
918 :
919 0 : long now = fd_clock_tile_now( ctx->clock );
920 0 : if( FD_UNLIKELY( now<ctx->next_report_nanos ) ) return;
921 0 : ctx->next_report_nanos += REPORT_INTERVAL_MILLIS*1000L*1000L;
922 :
923 0 : *charge_busy = 1;
924 0 : if( FD_UNLIKELY( ctx->gui_enabled ) ) publish_system( ctx, stem, now );
925 :
926 0 : struct timespec boottime;
927 0 : if( FD_UNLIKELY( -1==clock_gettime( CLOCK_BOOTTIME, &boottime ) ) ) FD_LOG_ERR(( "clock_gettime(CLOCK_BOOTTIME) failed (%i-%s)", errno, strerror( errno ) ));
928 0 : ulong now_since_boot_nanos = (ulong)boottime.tv_sec*1000000000UL + (ulong)boottime.tv_nsec;
929 :
930 0 : cpu_owner_refresh( ctx, NULL );
931 0 : interrupt_metrics( ctx ); /* before idle computation below, which subtracts it */
932 :
933 0 : int sched_due = now>=ctx->next_sched_report_nanos;
934 0 : if( FD_UNLIKELY( sched_due ) ) ctx->next_sched_report_nanos = now + SCHED_REPORT_INTERVAL_NANOS;
935 :
936 0 : for( ulong i=0UL; i<ctx->tile_cnt; i++ ) {
937 0 : if( FD_UNLIKELY( -1==ctx->stat_fds[ i ] ) ) continue;
938 :
939 : /* CLK_TCK is typically 100, so 1 tick = 10ms = 10,000,000 ns */
940 0 : int process_died1 = read_stat_file( ctx->stat_fds[ i ], 10000000UL, ctx->metrics[ i ] );
941 0 : int process_died2 = -1!=ctx->schedstat_fds[ i ] && read_schedstat_file( ctx->schedstat_fds[ i ], ctx->metrics[ i ] );
942 0 : int process_died3 = sched_due && read_sched_file( ctx->sched_fds[ i ], ctx->metrics[ i ] );
943 :
944 0 : if( FD_UNLIKELY( process_died1 || process_died2 || process_died3 ) ) {
945 0 : ctx->stat_fds[ i ] = -1;
946 0 : continue;
947 0 : }
948 :
949 0 : ulong task_lifetime_nanos = now_since_boot_nanos - ctx->starttime_nanos[ i ];
950 0 : ulong user_nanos = ctx->metrics[ i ][ FD_METRICS_COUNTER_TILE_THREAD_DURATION_NANOS_USER_OFF ];
951 0 : ulong system_nanos = ctx->metrics[ i ][ FD_METRICS_COUNTER_TILE_THREAD_DURATION_NANOS_SYSTEM_OFF ];
952 0 : ulong wait_nanos = ctx->metrics[ i ][ FD_METRICS_COUNTER_TILE_THREAD_DURATION_NANOS_WAIT_OFF ];
953 0 : ulong interrupt_nanos = ctx->metrics[ i ][ FD_METRICS_COUNTER_TILE_THREAD_DURATION_NANOS_INTERRUPT_OFF ];
954 0 : ulong busy_nanos = user_nanos+system_nanos+wait_nanos+interrupt_nanos;
955 0 : ulong idle_nanos = (task_lifetime_nanos>busy_nanos) ? (task_lifetime_nanos-busy_nanos) : 0UL;
956 :
957 : /* Counter can't go backwards in Prometheus else it thinks the
958 : application restarted. Use max to ensure monotonicity. */
959 0 : ctx->metrics[ i ][ FD_METRICS_COUNTER_TILE_THREAD_DURATION_NANOS_IDLE_OFF ] = fd_ulong_max( idle_nanos, ctx->metrics[ i ][ FD_METRICS_COUNTER_TILE_THREAD_DURATION_NANOS_IDLE_OFF ] );
960 0 : }
961 :
962 0 : for( ulong i=0UL; i<ctx->tile_cnt; i++ ) {
963 0 : if( FD_LIKELY( -1!=ctx->stat_fds[ i ] ) ) continue;
964 :
965 : /* The tile died, but it's a tile which is allowed to shutdown, so
966 : just stop updating metrics for it. */
967 0 : if( FD_LIKELY( 2UL==ctx->metrics[ i ][ FD_METRICS_GAUGE_TILE_STATE_OFF ] ) ) continue;
968 :
969 : /* Supervisor is going to bring the whole process tree down if any
970 : of the target PIDs died, so we can ignore this and wait. */
971 0 : if( FD_UNLIKELY( !ctx->first_seen_died[ i ] ) ) {
972 0 : ctx->first_seen_died[ i ] = now;
973 0 : } else if( FD_LIKELY( ctx->first_seen_died[ i ]==LONG_MAX ) ) {
974 : /* We already reported this, so we can ignore it. */
975 0 : } else if( FD_UNLIKELY( now-ctx->first_seen_died[ i ] < 10L*1000L*1000L*1000L ) ) {
976 : /* Wait 10 seconds for supervisor to kill us before reporting WARNING */
977 0 : } else {
978 0 : FD_LOG_WARNING(( "cannot get metrics for dead tile idx %lu", i ));
979 0 : ctx->first_seen_died[ i ] = LONG_MAX;
980 0 : }
981 0 : }
982 :
983 0 : check_engine_metric( ctx, now );
984 0 : if( FD_UNLIKELY( now>=ctx->next_irq_report_nanos ) ) {
985 0 : ctx->next_irq_report_nanos = now + IRQ_REPORT_INTERVAL_NANOS;
986 0 : irq_metrics( ctx );
987 0 : sensor_metrics( ctx, now );
988 0 : host_metrics( ctx );
989 0 : }
990 0 : }
991 :
992 : /* Disk mount discovery ************************************************/
993 :
994 : static int
995 : fd_mount_id( int fd,
996 0 : ulong * mnt_id ) {
997 0 : char fdinfo_path[ 64 ];
998 0 : if( FD_UNLIKELY( !fd_cstr_printf_check( fdinfo_path, sizeof(fdinfo_path), NULL, "/proc/self/fdinfo/%d", fd ) ) ) return EINVAL;
999 :
1000 0 : int fdinfo_fd = open( fdinfo_path, O_RDONLY|O_CLOEXEC );
1001 0 : if( FD_UNLIKELY( fdinfo_fd<0 ) ) return errno;
1002 :
1003 0 : char buf[ 256 ];
1004 0 : ulong buf_sz = 0UL;
1005 0 : int err = fd_io_read( fdinfo_fd, buf, sizeof(buf)-1UL, sizeof(buf)-1UL, &buf_sz );
1006 0 : if( FD_UNLIKELY( err<0 ) ) err = 0; /* EOF before the buffer filled */
1007 0 : if( FD_UNLIKELY( close( fdinfo_fd ) && !err ) ) err = errno;
1008 0 : if( FD_UNLIKELY( err ) ) return err;
1009 0 : buf[ buf_sz ] = '\0';
1010 :
1011 0 : char const * line = buf;
1012 0 : while( line ) {
1013 0 : if( FD_UNLIKELY( !strncmp( line, "mnt_id:", 7UL ) ) ) {
1014 0 : char * end;
1015 0 : ulong id = strtoul( line+7UL, &end, 10 );
1016 0 : if( FD_UNLIKELY( end==line+7UL || (*end!='\n' && *end!='\0') || id==ULONG_MAX ) ) return EINVAL;
1017 0 : *mnt_id = id;
1018 0 : return 0;
1019 0 : }
1020 0 : line = strchr( line, '\n' );
1021 0 : if( line ) line++;
1022 0 : }
1023 0 : return ENOENT;
1024 0 : }
1025 :
1026 : static int
1027 : resolve_path_prefix( char const * path,
1028 0 : char resolved[ static PATH_MAX ] ) {
1029 0 : char candidate[ PATH_MAX ];
1030 0 : fd_cstr_ncpy( candidate, path, sizeof(candidate) );
1031 0 : while( !realpath( candidate, resolved ) ) {
1032 0 : char * slash = strrchr( candidate, '/' );
1033 0 : if( FD_UNLIKELY( !slash ) ) return 0;
1034 0 : if( slash==candidate ) slash[ 1 ] = '\0';
1035 0 : else *slash = '\0';
1036 0 : }
1037 0 : return 1;
1038 0 : }
1039 :
1040 : static int
1041 : open_disk_probe_dir( char const * resolved,
1042 : char probe_dir[ static PATH_MAX ],
1043 0 : ulong * mnt_id ) {
1044 : /* `probe_dir` is on the target mount used only to create the probe
1045 : file; the descriptor for `probe_dir` is not retained. */
1046 0 : int path_fd = open( resolved, O_PATH|O_CLOEXEC );
1047 0 : if( FD_UNLIKELY( path_fd<0 ) ) return -1;
1048 :
1049 0 : struct stat path_st;
1050 0 : if( FD_UNLIKELY( fstat( path_fd, &path_st ) ) ) {
1051 0 : close( path_fd );
1052 0 : return -1;
1053 0 : }
1054 :
1055 0 : ulong path_mnt_id;
1056 0 : int err = fd_mount_id( path_fd, &path_mnt_id );
1057 0 : if( FD_UNLIKELY( err ) ) {
1058 0 : close( path_fd );
1059 0 : FD_LOG_WARNING(( "reading mount ID for `%s` failed (%i-%s); omitting filesystem from system resource reporting",
1060 0 : resolved, err, strerror( err ) ));
1061 0 : return -1;
1062 0 : }
1063 :
1064 0 : fd_cstr_ncpy( probe_dir, resolved, PATH_MAX );
1065 0 : if( FD_UNLIKELY( !S_ISDIR( path_st.st_mode ) ) ) {
1066 0 : char * slash = strrchr( probe_dir, '/' );
1067 0 : if( FD_UNLIKELY( !slash ) ) {
1068 0 : close( path_fd );
1069 0 : return -1;
1070 0 : }
1071 0 : if( slash==probe_dir ) slash[ 1 ] = '\0';
1072 0 : else *slash = '\0';
1073 0 : }
1074 :
1075 0 : int dir_fd = open( probe_dir, O_PATH|O_DIRECTORY|O_CLOEXEC );
1076 0 : if( FD_UNLIKELY( dir_fd<0 ) ) {
1077 0 : int open_err = errno;
1078 0 : close( path_fd );
1079 0 : FD_LOG_WARNING(( "open `%s` for disk-capacity probe failed (%i-%s); omitting filesystem from system resource reporting",
1080 0 : probe_dir, open_err, strerror( open_err ) ));
1081 0 : return -1;
1082 0 : }
1083 :
1084 0 : ulong dir_mnt_id;
1085 0 : err = fd_mount_id( dir_fd, &dir_mnt_id );
1086 0 : if( FD_UNLIKELY( err ) ) {
1087 0 : close( path_fd );
1088 0 : close( dir_fd );
1089 0 : FD_LOG_WARNING(( "reading mount ID for `%s` failed (%i-%s); omitting filesystem from system resource reporting",
1090 0 : probe_dir, err, strerror( err ) ));
1091 0 : return -1;
1092 0 : }
1093 0 : close( path_fd );
1094 :
1095 : /* A regular file can itself be a bind mount. In that case its
1096 : containing directory is not on the mount we need to sample,
1097 : and there is nowhere on that mount to create an anonymous file. */
1098 0 : if( FD_UNLIKELY( path_mnt_id!=dir_mnt_id ) ) {
1099 0 : close( dir_fd );
1100 0 : FD_LOG_WARNING(( "cannot create anonymous disk-capacity probe for non-directory mount `%s`; omitting filesystem from system resource reporting",
1101 0 : resolved ));
1102 0 : return -1;
1103 0 : }
1104 :
1105 0 : *mnt_id = dir_mnt_id;
1106 0 : return dir_fd;
1107 0 : }
1108 :
1109 : static int
1110 : resolve_mount_path( char const * probe_dir,
1111 : ulong mnt_id,
1112 0 : char mount_path[ static PATH_MAX ] ) {
1113 0 : fd_cstr_ncpy( mount_path, probe_dir, PATH_MAX );
1114 0 : while( strcmp( mount_path, "/" ) ) {
1115 0 : char parent[ PATH_MAX ];
1116 0 : fd_cstr_ncpy( parent, mount_path, sizeof(parent) );
1117 0 : char * slash = strrchr( parent, '/' );
1118 0 : if( slash==parent ) slash[ 1 ] = '\0';
1119 0 : else *slash = '\0';
1120 :
1121 0 : int parent_fd = open( parent, O_PATH|O_DIRECTORY|O_CLOEXEC );
1122 0 : if( FD_UNLIKELY( parent_fd<0 ) ) {
1123 0 : int open_err = errno;
1124 0 : FD_LOG_WARNING(( "open `%s` while resolving mount point failed (%i-%s); omitting filesystem from system resource reporting",
1125 0 : parent, open_err, strerror( open_err ) ));
1126 0 : return 0;
1127 0 : }
1128 0 : ulong parent_mnt_id;
1129 0 : int err = fd_mount_id( parent_fd, &parent_mnt_id );
1130 0 : close( parent_fd );
1131 0 : if( FD_UNLIKELY( err ) ) {
1132 0 : FD_LOG_WARNING(( "reading mount ID for `%s` failed (%i-%s); omitting filesystem from system resource reporting",
1133 0 : parent, err, strerror( err ) ));
1134 0 : return 0;
1135 0 : }
1136 0 : if( parent_mnt_id!=mnt_id ) break;
1137 0 : fd_cstr_ncpy( mount_path, parent, PATH_MAX );
1138 0 : }
1139 0 : return 1;
1140 0 : }
1141 :
1142 : static int
1143 : create_disk_probe( int dir_fd,
1144 : char const * probe_dir,
1145 0 : ulong mnt_id ) {
1146 : /* Retain no directory fds after sandboxing. O_EXCL ensures
1147 : that the anonymous inode cannot later be linked into the filesystem;
1148 : reopening it O_PATH also removes the temporary read/write authority. */
1149 0 : int tmp_fd = openat( dir_fd, ".", O_TMPFILE|O_EXCL|O_RDWR|O_CLOEXEC, S_IRUSR|S_IWUSR );
1150 0 : if( FD_UNLIKELY( tmp_fd<0 ) ) {
1151 0 : int err = errno;
1152 0 : FD_LOG_WARNING(( "anonymous disk-capacity probe in `%s` failed (%i-%s); omitting filesystem from system resource reporting",
1153 0 : probe_dir, err, strerror( err ) ));
1154 0 : return -1;
1155 0 : }
1156 :
1157 0 : char tmp_path[ 64 ];
1158 0 : FD_TEST( fd_cstr_printf_check( tmp_path, sizeof(tmp_path), NULL, "/proc/self/fd/%d", tmp_fd ) );
1159 0 : int mount_fd = open( tmp_path, O_PATH|O_CLOEXEC );
1160 0 : if( FD_UNLIKELY( mount_fd<0 ) ) {
1161 0 : int err = errno;
1162 0 : close( tmp_fd );
1163 0 : FD_LOG_WARNING(( "reopening anonymous disk-capacity probe in `%s` as O_PATH failed (%i-%s); omitting filesystem from system resource reporting",
1164 0 : probe_dir, err, strerror( err ) ));
1165 0 : return -1;
1166 0 : }
1167 :
1168 0 : struct stat probe_st;
1169 0 : ulong probe_mnt_id;
1170 0 : int probe_err = 0;
1171 0 : if( FD_UNLIKELY( fstat( mount_fd, &probe_st ) ) ) probe_err = errno;
1172 0 : else if( FD_UNLIKELY( !S_ISREG( probe_st.st_mode ) || probe_st.st_nlink ) ) probe_err = EINVAL;
1173 0 : else probe_err = fd_mount_id( mount_fd, &probe_mnt_id );
1174 0 : if( FD_UNLIKELY( !probe_err && probe_mnt_id!=mnt_id ) ) probe_err = EXDEV;
1175 0 : if( FD_UNLIKELY( probe_err ) ) {
1176 0 : close( mount_fd );
1177 0 : close( tmp_fd );
1178 0 : FD_LOG_WARNING(( "anonymous disk-capacity probe in `%s` failed validation (%i-%s); omitting filesystem from system resource reporting",
1179 0 : probe_dir, probe_err, strerror( probe_err ) ));
1180 0 : return -1;
1181 0 : }
1182 0 : close( tmp_fd );
1183 0 : return mount_fd;
1184 0 : }
1185 :
1186 : static uint
1187 : add_disk_mount( fd_diag_tile_t * ctx,
1188 0 : char const * path ) {
1189 0 : if( FD_UNLIKELY( !path[ 0 ] ) ) return UINT_MAX;
1190 :
1191 : /* Find longest prefix of `path` that is a real path. */
1192 0 : char resolved[ PATH_MAX ];
1193 0 : if( FD_UNLIKELY( !resolve_path_prefix( path, resolved ) ) ) return UINT_MAX;
1194 :
1195 : /* Find candidate dir at or above `resolved` where O_TMPFILE can create a probe file. */
1196 0 : char probe_dir[ PATH_MAX ];
1197 0 : ulong mnt_id;
1198 0 : int dir_fd = open_disk_probe_dir( resolved, probe_dir, &mnt_id );
1199 0 : if( FD_UNLIKELY( dir_fd<0 ) ) return UINT_MAX;
1200 :
1201 : /* Share one probe file among all reported paths on the same mount. */
1202 0 : for( ulong i=0UL; i<ctx->mount_cnt; i++ ) {
1203 0 : if( ctx->mounts[ i ].mnt_id==mnt_id ) {
1204 0 : close( dir_fd );
1205 0 : return (uint)i;
1206 0 : }
1207 0 : }
1208 0 : if( FD_UNLIKELY( ctx->mount_cnt>=FD_DIAG_SYSTEM_FILE_MAX ) ) {
1209 0 : close( dir_fd );
1210 0 : return UINT_MAX;
1211 0 : }
1212 :
1213 : /* Get the path of the mount `path` is on. */
1214 0 : char mount_path[ PATH_MAX ];
1215 0 : if( FD_UNLIKELY( !resolve_mount_path( probe_dir, mnt_id, mount_path ) ) ) {
1216 0 : close( dir_fd );
1217 0 : return UINT_MAX;
1218 0 : }
1219 :
1220 : /* Create the probe O_PATH descriptor kept after sandboxing. */
1221 0 : int mount_fd = create_disk_probe( dir_fd, probe_dir, mnt_id );
1222 0 : close( dir_fd );
1223 0 : if( FD_UNLIKELY( mount_fd<0 ) ) return UINT_MAX;
1224 :
1225 0 : ulong idx = ctx->mount_cnt++;
1226 0 : ctx->mounts[ idx ].fd = mount_fd;
1227 0 : ctx->mounts[ idx ].mnt_id = mnt_id;
1228 0 : fd_cstr_ncpy( ctx->mounts[ idx ].path, mount_path, sizeof(ctx->mounts[ idx ].path) );
1229 0 : return (uint)idx;
1230 0 : }
1231 :
1232 : static int
1233 : resolve_file_path( char const * path,
1234 : int data_fd,
1235 0 : char resolved[ static PATH_MAX ] ) {
1236 0 : char fd_path[ 64 ];
1237 0 : if( FD_LIKELY( data_fd>=0 ) ) {
1238 0 : struct stat st;
1239 0 : if( FD_UNLIKELY( fstat( data_fd, &st ) || !S_ISREG( st.st_mode ) ) ) return 0;
1240 0 : FD_TEST( fd_cstr_printf_check( fd_path, sizeof(fd_path), NULL, "/proc/self/fd/%d", data_fd ) );
1241 0 : path = fd_path;
1242 0 : }
1243 0 : if( FD_UNLIKELY( !path || !path[ 0 ] ) ) return 0;
1244 :
1245 0 : if( FD_LIKELY( realpath( path, resolved ) ) ) return 1;
1246 0 : if( FD_LIKELY( path[ 0 ]=='/' ) ) {
1247 0 : if( FD_UNLIKELY( strlen( path )>=PATH_MAX ) ) return 0;
1248 0 : fd_cstr_ncpy( resolved, path, PATH_MAX );
1249 0 : return 1;
1250 0 : }
1251 :
1252 0 : char cwd[ PATH_MAX ];
1253 0 : if( FD_UNLIKELY( !getcwd( cwd, sizeof(cwd) ) ) ) return 0;
1254 0 : return fd_cstr_printf_check( resolved, PATH_MAX, NULL, "%s/%s", cwd, path );
1255 0 : }
1256 :
1257 : static void
1258 : add_file( fd_diag_tile_t * ctx,
1259 : uint category,
1260 : char const * path,
1261 : int data_fd,
1262 0 : ulong volatile * metric ) {
1263 0 : char resolved[ PATH_MAX ];
1264 0 : if( FD_UNLIKELY( !resolve_file_path( path, data_fd, resolved ) ) ) return;
1265 :
1266 0 : uint mount_idx = add_disk_mount( ctx, resolved );
1267 0 : if( FD_UNLIKELY( mount_idx==UINT_MAX || ctx->file_cnt>=FD_DIAG_SYSTEM_FILE_MAX ) ) return;
1268 0 : ulong idx = ctx->file_cnt++;
1269 0 : fd_cstr_ncpy( ctx->files[ idx ].path, resolved, sizeof(ctx->files[ idx ].path) );
1270 0 : ctx->files[ idx ].category = category;
1271 0 : ctx->files[ idx ].mount_idx = mount_idx;
1272 0 : ctx->files[ idx ].data_fd = data_fd;
1273 0 : ctx->files[ idx ].metric = metric;
1274 0 : }
1275 :
1276 : static int
1277 : sysfs_read_cstr( char const * dir,
1278 : char const * file,
1279 : char * out,
1280 0 : ulong out_sz ) {
1281 0 : char path[ PATH_MAX ];
1282 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "%s/%s", dir, file ) );
1283 0 : int fd = open( path, O_RDONLY|O_CLOEXEC );
1284 0 : if( fd<0 ) return -1;
1285 0 : long n = read( fd, out, out_sz-1UL );
1286 0 : close( fd );
1287 0 : if( n<=0 ) return -1;
1288 0 : out[ n ] = '\0';
1289 0 : out[ strcspn( out, "\n" ) ] = '\0';
1290 0 : return 0;
1291 0 : }
1292 :
1293 : static void
1294 : sensor_add( fd_diag_tile_t * ctx,
1295 : char const * dir,
1296 : char const * file,
1297 0 : uint kind ) {
1298 0 : if( FD_UNLIKELY( ctx->sensor.cnt>=DIAG_SENSOR_MAX ) ) {
1299 0 : FD_LOG_WARNING(( "more than %lu hardware sensors, ignoring %s/%s", DIAG_SENSOR_MAX, dir, file ));
1300 0 : return;
1301 0 : }
1302 :
1303 0 : char path[ PATH_MAX ];
1304 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "%s/%s", dir, file ) );
1305 0 : int fd = open( path, O_RDONLY|O_CLOEXEC );
1306 0 : if( FD_UNLIKELY( fd<0 ) ) return;
1307 0 : ctx->sensor.fd [ ctx->sensor.cnt ] = fd;
1308 0 : ctx->sensor.kind[ ctx->sensor.cnt ] = kind;
1309 0 : ctx->sensor.cnt++;
1310 0 : }
1311 :
1312 : static void
1313 0 : sensors_init( fd_diag_tile_t * ctx ) {
1314 0 : ctx->sensor.cnt = 0UL;
1315 0 : ctx->sensor.rapl_cnt = 0UL;
1316 :
1317 0 : int dir_fd = open( "/sys/class/hwmon", O_RDONLY|O_DIRECTORY|O_CLOEXEC );
1318 0 : for(;;) {
1319 0 : if( FD_UNLIKELY( dir_fd<0 ) ) break;
1320 0 : uchar buf[ 4096UL ] __attribute__((aligned(alignof(struct dirent))));
1321 0 : long dents_sz = syscall( SYS_getdents64, dir_fd, buf, sizeof(buf) );
1322 0 : if( FD_UNLIKELY( dents_sz<0L ) ) FD_LOG_WARNING(( "getdents64(/sys/class/hwmon) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
1323 0 : if( dents_sz<=0L ) break;
1324 :
1325 0 : ulong off = 0UL;
1326 0 : while( off<(ulong)dents_sz ) {
1327 0 : struct dirent const * ent = (struct dirent const *)(buf+off);
1328 0 : FD_TEST( ent->d_reclen && off+(ulong)ent->d_reclen<=(ulong)dents_sz );
1329 0 : off += ent->d_reclen;
1330 0 : if( strncmp( ent->d_name, "hwmon", 5UL ) ) continue;
1331 0 : char hw[ PATH_MAX ];
1332 0 : char name[ 32 ];
1333 0 : FD_TEST( fd_cstr_printf_check( hw, sizeof(hw), NULL, "/sys/class/hwmon/%s", ent->d_name ) );
1334 0 : if( sysfs_read_cstr( hw, "name", name, sizeof(name) ) ) continue;
1335 :
1336 0 : if( !strcmp( name, "k10temp" ) || !strcmp( name, "coretemp" ) ) {
1337 0 : for( ulong i=1UL; i<512UL; i++ ) { /* coretemp numbers inputs sparsely by core id */
1338 0 : char file [ 32 ];
1339 0 : char label[ 32 ];
1340 0 : FD_TEST( fd_cstr_printf_check( file, sizeof(file), NULL, "temp%lu_label", i ) );
1341 0 : if( sysfs_read_cstr( hw, file, label, sizeof(label) ) ) continue;
1342 0 : if( strcmp( label, "Tctl" ) && strncmp( label, "Package id", 10UL ) ) continue;
1343 0 : FD_TEST( fd_cstr_printf_check( file, sizeof(file), NULL, "temp%lu_input", i ) );
1344 0 : sensor_add( ctx, hw, file, DIAG_SENSOR_CPU_PACKAGE );
1345 0 : }
1346 0 : }
1347 0 : else if( !strncmp( name, "mlx5", 4UL ) ) sensor_add( ctx, hw, "temp1_input", DIAG_SENSOR_NIC ); /* OFED appends the PCI address */
1348 0 : else if( !strncmp( name, "ice", 3UL ) ) sensor_add( ctx, hw, "temp1_input", DIAG_SENSOR_NIC ); /* out-of-tree drivers append the PCI address */
1349 0 : else if( !strcmp( name, "nvme" ) ) sensor_add( ctx, hw, "temp1_input", DIAG_SENSOR_NVME );
1350 0 : else if( !strcmp( name, "power_meter" ) ) sensor_add( ctx, hw, "power1_average", DIAG_SENSOR_POWER );
1351 0 : }
1352 0 : }
1353 0 : if( FD_LIKELY( dir_fd>=0 ) ) close( dir_fd );
1354 :
1355 0 : for( ulong i=0UL; i<DIAG_EDAC_MC_MAX; i++ ) {
1356 0 : char mc[ 64 ];
1357 0 : FD_TEST( fd_cstr_printf_check( mc, sizeof(mc), NULL, "/sys/devices/system/edac/mc/mc%lu", i ) );
1358 0 : if( access( mc, F_OK ) ) continue; /* ids can have gaps */
1359 0 : sensor_add( ctx, mc, "ce_count", DIAG_SENSOR_ECC_CE );
1360 0 : sensor_add( ctx, mc, "ue_count", DIAG_SENSOR_ECC_UE );
1361 0 : }
1362 :
1363 0 : for( ulong i=0UL; i<DIAG_RAPL_MAX; i++ ) {
1364 0 : char rapl [ 64 ];
1365 0 : char name [ 32 ];
1366 0 : char range[ 32 ];
1367 0 : FD_TEST( fd_cstr_printf_check( rapl, sizeof(rapl), NULL, "/sys/class/powercap/intel-rapl:%lu", i ) );
1368 0 : if( sysfs_read_cstr( rapl, "name", name, sizeof(name) ) ) break;
1369 0 : if( strncmp( name, "package", 7UL ) ) continue; /* psys on some Intel parts */
1370 0 : if( sysfs_read_cstr( rapl, "max_energy_range_uj", range, sizeof(range) ) ) continue;
1371 0 : ulong range_uj = strtoul( range, NULL, 10 );
1372 0 : if( FD_UNLIKELY( !range_uj ) ) continue;
1373 0 : char path[ 96 ];
1374 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "%s/energy_uj", rapl ) );
1375 0 : int fd = open( path, O_RDONLY|O_CLOEXEC );
1376 0 : if( FD_UNLIKELY( fd<0 ) ) continue;
1377 0 : ctx->sensor.rapl_fd [ ctx->sensor.rapl_cnt ] = fd;
1378 0 : ctx->sensor.rapl_range[ ctx->sensor.rapl_cnt ] = range_uj;
1379 0 : ctx->sensor.rapl_cnt++;
1380 0 : }
1381 0 : ctx->sensor.rapl_prev_nanos = 0L;
1382 :
1383 0 : FD_LOG_INFO(( "diag found %lu hardware sensors and %lu powercap packages", ctx->sensor.cnt, ctx->sensor.rapl_cnt ));
1384 0 : }
1385 :
1386 : static void
1387 : privileged_init( fd_topo_t const * topo,
1388 0 : fd_topo_tile_t const * tile ) {
1389 0 : void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
1390 :
1391 0 : FD_SCRATCH_ALLOC_INIT( l, scratch );
1392 0 : fd_diag_tile_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_diag_tile_t), sizeof(fd_diag_tile_t) );
1393 0 : memset( ctx->host_cpu_ticks, 0, sizeof(ctx->host_cpu_ticks) );
1394 :
1395 0 : FD_TEST( topo->tile_cnt<=FD_DIAG_SYSTEM_TILE_MAX );
1396 :
1397 0 : FD_TEST( 100L == sysconf( _SC_CLK_TCK ) );
1398 :
1399 0 : ctx->tile_cnt = topo->tile_cnt;
1400 0 : ctx->gui_enabled = fd_topo_find_tile_out_link( topo, tile, "diag_gui", 0UL )!=ULONG_MAX;
1401 0 : ctx->memory.cnt = topo->wksp_cnt;
1402 0 : FD_TEST( ctx->memory.cnt<=FD_TOPO_MAX_WKSPS );
1403 0 : for( ulong wksp_idx=0UL; wksp_idx<ctx->memory.cnt; wksp_idx++ ) {
1404 0 : fd_topo_wksp_t const * wksp = &topo->workspaces[ wksp_idx ];
1405 0 : FD_TEST( fd_cstr_printf_check( ctx->memory.wksp[ wksp_idx ].name,
1406 0 : sizeof(ctx->memory.wksp[ wksp_idx ].name), NULL,
1407 0 : "%s_%s.wksp", topo->app_name, wksp->name ) );
1408 0 : ctx->memory.wksp[ wksp_idx ].numa_idx = wksp->numa_idx;
1409 0 : ctx->memory.wksp[ wksp_idx ].bytes = wksp->page_cnt*wksp->page_sz;
1410 0 : ctx->memory.wksp[ wksp_idx ].numa_slot = USHORT_MAX;
1411 :
1412 0 : ushort owners[ FD_DIAG_SYSTEM_TILE_MAX ];
1413 0 : ulong owner_cnt = 0UL;
1414 0 : for( ulong tile_idx=0UL; tile_idx<topo->tile_cnt; tile_idx++ ) {
1415 0 : fd_topo_tile_t const * owner = &topo->tiles[ tile_idx ];
1416 0 : if( topo->objs[ owner->tile_obj_id ].wksp_id!=wksp_idx ) continue;
1417 0 : owners[ owner_cnt++ ] = (ushort)tile_idx;
1418 0 : }
1419 0 : if( FD_LIKELY( owner_cnt==1UL ) ) {
1420 0 : ctx->memory.wksp[ wksp_idx ].tile_idx = owners[ 0 ];
1421 0 : continue;
1422 0 : }
1423 0 : if( FD_UNLIKELY( owner_cnt>1UL ) ) {
1424 0 : ctx->memory.wksp[ wksp_idx ].tile_idx = DIAG_WKSP_TILE_IDX_SHARED;
1425 0 : continue;
1426 0 : }
1427 :
1428 0 : owner_cnt = 0UL;
1429 0 : for( ulong link_idx=0UL; link_idx<topo->link_cnt; link_idx++ ) {
1430 0 : fd_topo_link_t const * link = &topo->links[ link_idx ];
1431 0 : if( topo->objs[ link->mcache_obj_id ].wksp_id!=wksp_idx &&
1432 0 : ( !link->mtu || topo->objs[ link->dcache_obj_id ].wksp_id!=wksp_idx ) ) continue;
1433 0 : ulong producer_idx = fd_topo_find_link_producer( topo, link );
1434 0 : if( producer_idx==ULONG_MAX ) continue;
1435 0 : int found = 0;
1436 0 : for( ulong owner_idx=0UL; owner_idx<owner_cnt; owner_idx++ )
1437 0 : found |= owners[ owner_idx ]==(ushort)producer_idx;
1438 0 : if( !found ) owners[ owner_cnt++ ] = (ushort)producer_idx;
1439 0 : }
1440 0 : if( FD_LIKELY( owner_cnt==1UL ) ) {
1441 0 : ctx->memory.wksp[ wksp_idx ].tile_idx = owners[ 0 ];
1442 0 : continue;
1443 0 : }
1444 0 : if( FD_UNLIKELY( owner_cnt>1UL ) ) {
1445 0 : ctx->memory.wksp[ wksp_idx ].tile_idx = DIAG_WKSP_TILE_IDX_SHARED;
1446 0 : continue;
1447 0 : }
1448 :
1449 0 : for( ulong tile_idx=0UL; tile_idx<topo->tile_cnt; tile_idx++ ) {
1450 0 : fd_topo_tile_t const * owner = &topo->tiles[ tile_idx ];
1451 0 : int uses_writable = 0;
1452 0 : for( ulong obj_idx=0UL; obj_idx<owner->uses_obj_cnt; obj_idx++ ) {
1453 0 : ulong obj_id = owner->uses_obj_id[ obj_idx ];
1454 0 : uses_writable |= topo->objs[ obj_id ].wksp_id==wksp_idx &&
1455 0 : owner->uses_obj_mode[ obj_idx ]==FD_SHMEM_JOIN_MODE_READ_WRITE;
1456 0 : }
1457 0 : if( uses_writable ) owners[ owner_cnt++ ] = (ushort)tile_idx;
1458 0 : }
1459 0 : ctx->memory.wksp[ wksp_idx ].tile_idx = owner_cnt==1UL ? owners[ 0 ] : DIAG_WKSP_TILE_IDX_SHARED;
1460 0 : }
1461 0 : for( ulong tile_idx=0UL; tile_idx<FD_DIAG_SYSTEM_TILE_MAX; tile_idx++ )
1462 0 : ctx->memory.stack_numa_slot[ tile_idx ] = USHORT_MAX;
1463 0 : ctx->mount_cnt = 0UL;
1464 0 : ctx->file_cnt = 0UL;
1465 0 : for( ulong i=0UL; i<FD_TILE_MAX; i++ ) {
1466 0 : ctx->stat_fds[ i ] = -1;
1467 0 : ctx->sched_fds[ i ] = -1;
1468 0 : ctx->schedstat_fds[ i ] = -1;
1469 0 : }
1470 0 : for( ulong i=0UL; i<FD_DIAG_SYSTEM_NUMA_MAX; i++ ) ctx->numa.node[ i ].meminfo_fd = -1;
1471 :
1472 0 : for( ulong i=0UL; i<topo->tile_cnt; i++ ) {
1473 0 : ulong * metrics = fd_metrics_join( fd_topo_obj_laddr( topo, topo->tiles[ i ].metrics_obj_id ) );
1474 :
1475 0 : for(;;) {
1476 0 : ulong pid, tid;
1477 0 : if( FD_UNLIKELY( tile->id==i ) ) {
1478 0 : pid = fd_sandbox_getpid();
1479 0 : tid = fd_sandbox_gettid();
1480 0 : } else {
1481 0 : pid = fd_metrics_tile( metrics )[ FD_METRICS_GAUGE_TILE_PID_OFF ];
1482 0 : tid = fd_metrics_tile( metrics )[ FD_METRICS_GAUGE_TILE_TID_OFF ];
1483 0 : if( FD_UNLIKELY( !pid || !tid ) ) {
1484 0 : FD_SPIN_PAUSE();
1485 0 : continue;
1486 0 : }
1487 0 : }
1488 :
1489 0 : ctx->metrics[ i ] = fd_metrics_tile( metrics );
1490 :
1491 0 : char path[ 64UL ];
1492 0 : FD_TEST( fd_cstr_printf_check( path, sizeof( path ), NULL, "/proc/%lu/task/%lu/stat", pid, tid ) );
1493 0 : ctx->stat_fds[ i ] = open( path, O_RDONLY );
1494 0 : if( FD_UNLIKELY( -1==ctx->stat_fds[ i ] ) ) {
1495 : /* Might be a tile that's allowed to shutdown already did so
1496 : before we got to here, due to a race condition. Just
1497 : proceed, we will not be able to get metrics for the shut
1498 : down process. */
1499 0 : if( FD_LIKELY( 2UL!=ctx->metrics[ i ][ FD_METRICS_GAUGE_TILE_STATE_OFF ] ) ) FD_LOG_ERR(( "open stat failed (%i-%s)", errno, strerror( errno ) ));
1500 0 : break;
1501 0 : }
1502 :
1503 0 : FD_TEST( fd_cstr_printf_check( path, sizeof( path ), NULL, "/proc/%lu/task/%lu/sched", pid, tid ) );
1504 0 : ctx->sched_fds[ i ] = open( path, O_RDONLY );
1505 0 : if( FD_UNLIKELY( -1==ctx->sched_fds[ i ] ) ) {
1506 0 : if( FD_LIKELY( 2UL!=ctx->metrics[ i ][ FD_METRICS_GAUGE_TILE_STATE_OFF ] ) ) FD_LOG_ERR(( "open sched failed (%i-%s)", errno, strerror( errno ) ));
1507 0 : ctx->stat_fds[ i ] = -1;
1508 0 : break;
1509 0 : }
1510 :
1511 : /* Absent on kernels without CONFIG_SCHED_INFO: wait then reads 0,
1512 : as it did without CONFIG_SCHEDSTATS. */
1513 0 : FD_TEST( fd_cstr_printf_check( path, sizeof( path ), NULL, "/proc/%lu/task/%lu/schedstat", pid, tid ) );
1514 0 : ctx->schedstat_fds[ i ] = open( path, O_RDONLY );
1515 0 : if( FD_UNLIKELY( -1==ctx->schedstat_fds[ i ] && errno!=ENOENT ) ) {
1516 0 : if( FD_LIKELY( 2UL!=ctx->metrics[ i ][ FD_METRICS_GAUGE_TILE_STATE_OFF ] ) ) FD_LOG_ERR(( "open schedstat failed (%i-%s)", errno, strerror( errno ) ));
1517 0 : ctx->stat_fds[ i ] = -1;
1518 0 : }
1519 0 : break;
1520 0 : }
1521 0 : }
1522 :
1523 0 : ctx->proc_interrupts_fd = open( "/proc/interrupts", O_RDONLY );
1524 0 : if( FD_UNLIKELY( -1==ctx->proc_interrupts_fd ) ) FD_LOG_ERR(( "open(/proc/interrupts) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
1525 :
1526 0 : ctx->proc_softirqs_fd = open( "/proc/softirqs", O_RDONLY );
1527 0 : if( FD_UNLIKELY( -1==ctx->proc_softirqs_fd ) ) FD_LOG_ERR(( "open(/proc/softirqs) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
1528 :
1529 0 : ctx->proc_stat_fd = open( "/proc/stat", O_RDONLY );
1530 0 : if( FD_UNLIKELY( -1==ctx->proc_stat_fd ) ) FD_LOG_ERR(( "open(/proc/stat) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
1531 :
1532 0 : ctx->proc_meminfo_fd = open( "/proc/meminfo", O_RDONLY );
1533 0 : if( FD_UNLIKELY( -1==ctx->proc_meminfo_fd ) ) FD_LOG_ERR(( "open(/proc/meminfo) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
1534 :
1535 0 : ctx->proc_loadavg_fd = open( "/proc/loadavg", O_RDONLY );
1536 0 : ctx->proc_vmstat_fd = open( "/proc/vmstat", O_RDONLY );
1537 0 : static char const * const psi_path[ 3 ] = { "/proc/pressure/cpu", "/proc/pressure/memory", "/proc/pressure/io" };
1538 0 : for( ulong i=0UL; i<3UL; i++ ) ctx->psi_fd[ i ] = open( psi_path[ i ], O_RDONLY );
1539 :
1540 0 : sensors_init( ctx );
1541 :
1542 0 : ctx->numa.cnt = 0UL;
1543 0 : if( FD_UNLIKELY( !ctx->gui_enabled ) ) return;
1544 :
1545 0 : fd_topo_cpus_t cpus[ 1 ];
1546 0 : fd_topo_cpus_init( cpus );
1547 0 : ctx->system_resources.cpu_cnt = (uint)fd_ulong_min( cpus->cpu_cnt, FD_DIAG_SYSTEM_CPU_MAX );
1548 0 : for( ulong i=0UL; i<ctx->system_resources.cpu_cnt; i++ ) {
1549 0 : fd_diag_system_cpu_init( &ctx->system_resources.cpu[ i ], &cpus->cpu[ i ] );
1550 0 : }
1551 :
1552 0 : for( ulong numa_idx=0UL; numa_idx<cpus->numa_node_cnt && ctx->numa.cnt<FD_DIAG_SYSTEM_NUMA_MAX; numa_idx++ ) {
1553 0 : char path[ 128 ];
1554 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/devices/system/node/node%lu/meminfo", numa_idx ) );
1555 0 : int fd = open( path, O_RDONLY );
1556 0 : if( FD_UNLIKELY( fd<0 ) ) {
1557 0 : if( FD_UNLIKELY( errno!=ENOENT ) ) FD_LOG_WARNING(( "open `%s` failed (%i-%s)", path, errno, strerror( errno ) ));
1558 0 : continue;
1559 0 : }
1560 0 : ctx->numa.node[ ctx->numa.cnt ].idx = (ushort)numa_idx;
1561 0 : ctx->numa.node[ ctx->numa.cnt ].meminfo_fd = fd;
1562 0 : ctx->numa.cnt++;
1563 0 : }
1564 :
1565 0 : for( ulong wksp_idx=0UL; wksp_idx<ctx->memory.cnt; wksp_idx++ ) {
1566 0 : ulong numa_slot = 0UL;
1567 0 : while( numa_slot<ctx->numa.cnt && ctx->numa.node[ numa_slot ].idx!=ctx->memory.wksp[ wksp_idx ].numa_idx ) numa_slot++;
1568 0 : if( FD_UNLIKELY( numa_slot==ctx->numa.cnt ) ) {
1569 0 : FD_LOG_WARNING(( "workspace `%s` is assigned to unavailable NUMA node %lu; omitting it from system memory reporting",
1570 0 : ctx->memory.wksp[ wksp_idx ].name, ctx->memory.wksp[ wksp_idx ].numa_idx ));
1571 0 : continue;
1572 0 : }
1573 0 : ctx->memory.wksp[ wksp_idx ].numa_slot = (ushort)numa_slot;
1574 0 : }
1575 :
1576 0 : for( ulong tile_idx=0UL; tile_idx<topo->tile_cnt; tile_idx++ ) {
1577 : /* Keep this placement rule in sync with initialize_stacks(). */
1578 0 : ulong stack_cpu_idx = topo->tiles[ tile_idx ].cpu_idx<65535UL ? topo->tiles[ tile_idx ].cpu_idx : 0UL;
1579 0 : FD_TEST( stack_cpu_idx<cpus->cpu_cnt );
1580 0 : ulong stack_numa_idx = cpus->cpu[ stack_cpu_idx ].numa_node;
1581 0 : ulong numa_slot = 0UL;
1582 0 : while( numa_slot<ctx->numa.cnt && ctx->numa.node[ numa_slot ].idx!=stack_numa_idx ) numa_slot++;
1583 0 : if( FD_UNLIKELY( numa_slot==ctx->numa.cnt ) ) {
1584 0 : FD_LOG_WARNING(( "stack for tile %s:%lu is assigned to unavailable NUMA node %lu; omitting it from system memory reporting",
1585 0 : topo->tiles[ tile_idx ].name, topo->tiles[ tile_idx ].kind_id, stack_numa_idx ));
1586 0 : continue;
1587 0 : }
1588 0 : ctx->memory.stack_numa_slot[ tile_idx ] = (ushort)numa_slot;
1589 0 : }
1590 :
1591 0 : ulong accdb_idx = fd_topo_find_tile( topo, "accdb", 0UL );
1592 0 : if( tile->diag.accounts_path[ 0 ] && accdb_idx!=ULONG_MAX )
1593 0 : add_file( ctx, FD_DIAG_SYSTEM_FILE_CATEGORY_ACCOUNTS, tile->diag.accounts_path, -1, ctx->metrics[ accdb_idx ] + FD_METRICS_GAUGE_ACCDB_DISK_ALLOCATED_BYTES_OFF );
1594 0 : ulong rserve_idx = fd_topo_find_tile( topo, "rserve", 0UL );
1595 0 : if( tile->diag.shreds_path[ 0 ] && rserve_idx!=ULONG_MAX )
1596 0 : add_file( ctx, FD_DIAG_SYSTEM_FILE_CATEGORY_SHREDS, tile->diag.shreds_path, -1, ctx->metrics[ rserve_idx ] + FD_METRICS_GAUGE_RSERVE_DISK_PEAK_BYTES_OFF );
1597 0 : ulong snapmk_idx = fd_topo_find_tile( topo, "snapmk", 0UL );
1598 0 : if( tile->diag.snapshots_path[ 0 ] && snapmk_idx!=ULONG_MAX )
1599 0 : add_file( ctx, FD_DIAG_SYSTEM_FILE_CATEGORY_SNAPSHOTS, tile->diag.snapshots_path, -1, ctx->metrics[ snapmk_idx ] + FD_METRICS_GAUGE_SNAPMK_DISK_ALLOCATED_BYTES_OFF );
1600 0 : ulong gui_idx = fd_topo_find_tile( topo, "gui", 0UL );
1601 0 : if( gui_idx!=ULONG_MAX )
1602 0 : add_file( ctx, FD_DIAG_SYSTEM_FILE_CATEGORY_GUI, tile->diag.gui_path, -1, ctx->metrics[ gui_idx ] + FD_METRICS_GAUGE_GUI_DISK_ALLOCATED_BYTES_OFF );
1603 0 : int logfile_fd = fd_log_private_logfile_fd();
1604 0 : if( logfile_fd>=0 ) add_file( ctx, FD_DIAG_SYSTEM_FILE_CATEGORY_LOGS, tile->diag.log_path, logfile_fd, NULL );
1605 0 : }
1606 :
1607 : /* Read starttime (field 22) from stat file. Returns 0 on success, 1 if
1608 : process died (ESRCH). */
1609 :
1610 : static int
1611 : read_starttime( int fd,
1612 : ulong ns_per_tick,
1613 0 : ulong * out_starttime_nanos ) {
1614 0 : char contents[ 4096 ] = {0};
1615 0 : ulong contents_len = 0UL;
1616 :
1617 0 : while( 1 ) {
1618 0 : if( FD_UNLIKELY( contents_len>=sizeof( contents ) ) ) FD_LOG_ERR(( "stat contents overflow" ));
1619 0 : long n = read( fd, contents + contents_len, sizeof( contents ) - contents_len );
1620 0 : if( FD_UNLIKELY( -1==n ) ) {
1621 0 : if( FD_UNLIKELY( errno==ESRCH ) ) return 1;
1622 0 : FD_LOG_ERR(( "read stat failed (%i-%s)", errno, strerror( errno ) ));
1623 0 : }
1624 0 : if( FD_LIKELY( 0L==n ) ) break;
1625 0 : contents_len += (ulong)n;
1626 0 : }
1627 :
1628 : /* Parse field 22 (starttime) from stat file */
1629 0 : char * saveptr;
1630 0 : char * token = strtok_r( contents, " ", &saveptr );
1631 0 : ulong field_idx = 0UL;
1632 :
1633 0 : while( token && field_idx<21UL ) {
1634 0 : token = strtok_r( NULL, " ", &saveptr );
1635 0 : field_idx++;
1636 0 : }
1637 :
1638 0 : if( FD_UNLIKELY( !token || field_idx!=21UL ) ) FD_LOG_ERR(( "starttime (field 22) not found in stat" ));
1639 :
1640 0 : char * endptr;
1641 0 : ulong starttime_ticks = strtoul( token, &endptr, 10 );
1642 0 : if( FD_UNLIKELY( *endptr!=' ' && *endptr!='\0' ) ) FD_LOG_ERR(( "strtoul failed for starttime" ));
1643 0 : if( FD_UNLIKELY( starttime_ticks==ULONG_MAX ) ) FD_LOG_ERR(( "strtoul overflow for starttime" ));
1644 :
1645 0 : *out_starttime_nanos = starttime_ticks * ns_per_tick;
1646 0 : return 0;
1647 0 : }
1648 :
1649 : static void
1650 : unprivileged_init( fd_topo_t const * topo,
1651 0 : fd_topo_tile_t const * tile ) {
1652 0 : fd_diag_tile_t * ctx = fd_topo_obj_laddr( topo, tile->tile_obj_id );
1653 :
1654 0 : memset( ctx->first_seen_died, 0, sizeof( ctx->first_seen_died ) );
1655 0 : fd_clock_tile_init( ctx->clock );
1656 0 : ctx->next_report_nanos = fd_clock_tile_now( ctx->clock );
1657 0 : ctx->next_sched_report_nanos = ctx->next_report_nanos;
1658 0 : ctx->next_irq_report_nanos = ctx->next_report_nanos;
1659 0 : ctx->next_system_report_nanos = ctx->next_report_nanos;
1660 0 : sensor_metrics( ctx, ctx->next_report_nanos ); /* RAPL baseline, so the first report has package power */
1661 0 : if( FD_UNLIKELY( ctx->gui_enabled ) ) {
1662 0 : ulong out_idx = fd_topo_find_tile_out_link( topo, tile, "diag_gui", 0UL );
1663 0 : FD_TEST( out_idx!=ULONG_MAX );
1664 0 : fd_topo_link_t const * link = &topo->links[ tile->out_link_id[ out_idx ] ];
1665 0 : ctx->system_out.idx = out_idx;
1666 0 : ctx->system_out.mem = topo->workspaces[ topo->objs[ link->dcache_obj_id ].wksp_id ].wksp;
1667 0 : ctx->system_out.chunk0 = fd_dcache_compact_chunk0( ctx->system_out.mem, link->dcache );
1668 0 : ctx->system_out.wmark = fd_dcache_compact_wmark ( ctx->system_out.mem, link->dcache, link->mtu );
1669 0 : ctx->system_out.chunk = ctx->system_out.chunk0;
1670 0 : }
1671 :
1672 : /* Snapshot the cumulative-since-boot /proc interrupt/softirq counters
1673 : so the metrics we report are counted since process startup. */
1674 0 : memset( ctx->softirq_baseline, 0, sizeof( ctx->softirq_baseline ) );
1675 0 : memset( ctx->device_irq_baseline, 0, sizeof( ctx->device_irq_baseline ) );
1676 0 : memset( ctx->tlb_baseline, 0, sizeof( ctx->tlb_baseline ) );
1677 0 : memset( ctx->loc_baseline, 0, sizeof( ctx->loc_baseline ) );
1678 0 : if( FD_UNLIKELY( -1==lseek( ctx->proc_softirqs_fd, 0, SEEK_SET ) ) ) FD_LOG_ERR(( "lseek failed (%i-%s)", errno, strerror( errno ) ));
1679 0 : ulong softirq_cpu_cnt = fd_proc_softirqs_sum( ctx->proc_softirqs_fd, ctx->softirq_baseline );
1680 0 : if( FD_UNLIKELY( !softirq_cpu_cnt ) ) FD_LOG_WARNING(( "failed to read softirq baseline from /proc/softirqs" ));
1681 :
1682 0 : if( FD_UNLIKELY( -1==lseek( ctx->proc_interrupts_fd, 0, SEEK_SET ) ) ) FD_LOG_ERR(( "lseek failed (%i-%s)", errno, strerror( errno ) ));
1683 0 : ulong interrupt_cpu_cnt = fd_proc_interrupts_read( ctx->proc_interrupts_fd,
1684 0 : ctx->device_irq_baseline,
1685 0 : ctx->tlb_baseline,
1686 0 : ctx->loc_baseline );
1687 0 : if( FD_UNLIKELY( !interrupt_cpu_cnt ) ) FD_LOG_WARNING(( "failed to read IRQ baselines from /proc/interrupts" ));
1688 :
1689 0 : memset( ctx->irq_ticks_baseline, 0, sizeof( ctx->irq_ticks_baseline ) );
1690 0 : if( FD_UNLIKELY( -1==lseek( ctx->proc_stat_fd, 0, SEEK_SET ) ) ) FD_LOG_ERR(( "lseek failed (%i-%s)", errno, strerror( errno ) ));
1691 0 : ulong stat_cpu_cnt = fd_proc_stat_irq_ticks( ctx->proc_stat_fd, ctx->irq_ticks_baseline );
1692 0 : if( FD_UNLIKELY( !stat_cpu_cnt ) ) FD_LOG_WARNING(( "failed to read irq tick baseline from /proc/stat" ));
1693 0 : char stat_buf[ 16384 ];
1694 0 : if( FD_UNLIKELY( host_cpu_ticks( ctx, stat_buf, sizeof(stat_buf), ctx->host_cpu_baseline ) ) ) memset( ctx->host_cpu_baseline, 0, sizeof(ctx->host_cpu_baseline) );
1695 0 : memcpy( ctx->softirq_boot, ctx->softirq_baseline, sizeof(ctx->softirq_boot) );
1696 0 : memcpy( ctx->device_irq_boot, ctx->device_irq_baseline, sizeof(ctx->device_irq_boot) );
1697 0 : memset( ctx->on_tile_softirq, 0, sizeof(ctx->on_tile_softirq) );
1698 0 : ctx->on_tile_device_irq = 0UL;
1699 0 : memcpy( ctx->owner_irq_baseline, ctx->device_irq_baseline, sizeof(ctx->owner_irq_baseline) );
1700 0 : memcpy( ctx->owner_tlb_baseline, ctx->tlb_baseline, sizeof(ctx->owner_tlb_baseline) );
1701 0 : memcpy( ctx->owner_loc_baseline, ctx->loc_baseline, sizeof(ctx->owner_loc_baseline) );
1702 0 : memcpy( ctx->owner_tick_baseline, ctx->irq_ticks_baseline, sizeof(ctx->owner_tick_baseline) );
1703 0 : fd_cpuset_new( ctx->irq_rebase );
1704 0 : fd_cpuset_new( ctx->tick_rebase );
1705 :
1706 : /* Read starttime (field 22) once at init for idle time calculation.
1707 : CLK_TCK is always 100, so 1 tick = 10ms = 10,000,000 ns. */
1708 0 : for( ulong i=0UL; i<ctx->tile_cnt; i++ ) {
1709 0 : if( FD_LIKELY( -1!=ctx->stat_fds[ i ] ) ) {
1710 0 : int died = read_starttime( ctx->stat_fds[ i ], 10000000UL, &ctx->starttime_nanos[ i ] );
1711 0 : if( FD_UNLIKELY( died ) ) ctx->stat_fds[ i ] = -1;
1712 0 : }
1713 0 : }
1714 :
1715 0 : memset( &ctx->check_engine, 0, sizeof(ctx->check_engine) );
1716 :
1717 0 : ctx->tiles.bundle_cnt = fd_topo_tile_name_cnt( topo, "bundle" );
1718 0 : for( ulong i=0UL; i<ctx->tiles.bundle_cnt; i++ ) ctx->tiles.bundle_tile_idx[ i ] = fd_topo_find_tile( topo, "bundle", i );
1719 0 : ctx->tiles.shred_cnt = fd_topo_tile_name_cnt( topo, "shred" );
1720 0 : for( ulong i=0UL; i<ctx->tiles.shred_cnt; i++ ) ctx->tiles.shred_tile_idx[ i ] = fd_topo_find_tile( topo, "shred", i );
1721 0 : ctx->tiles.votor_idx = fd_topo_find_tile( topo, "votor", 0UL );
1722 0 : ctx->tiles.replay_idx = fd_topo_find_tile( topo, "replay", 0UL );
1723 :
1724 0 : for( ulong i=0UL; i<FD_TILE_MAX; i++ ) ctx->cpu_to_tile[ i ] = USHORT_MAX;
1725 0 : cpu_owner_refresh( ctx, topo );
1726 :
1727 0 : ctx->is_voting = tile->diag.is_voting;
1728 0 : ctx->check_engine.reset_slot_changed_ns = ctx->next_report_nanos;
1729 0 : ctx->check_engine.turbine_slot_changed_ns = ctx->next_report_nanos;
1730 0 : ctx->check_engine.byte_snapshot_ns = ctx->next_report_nanos;
1731 0 : }
1732 :
1733 : static ulong
1734 : populate_allowed_seccomp( fd_topo_t const * topo,
1735 : fd_topo_tile_t const * tile,
1736 : ulong out_cnt,
1737 0 : struct sock_filter * out ) {
1738 0 : (void)topo;
1739 0 : (void)tile;
1740 :
1741 0 : populate_sock_filter_policy_fd_diag_tile( out_cnt, out, (uint)fd_log_private_logfile_fd() );
1742 0 : return sock_filter_policy_fd_diag_tile_instr_cnt;
1743 0 : }
1744 :
1745 : static ulong
1746 : populate_allowed_fds( fd_topo_t const * topo,
1747 : fd_topo_tile_t const * tile,
1748 : ulong out_fds_cnt,
1749 0 : int * out_fds ) {
1750 0 : fd_diag_tile_t * ctx = fd_topo_obj_laddr( topo, tile->tile_obj_id );
1751 :
1752 0 : int logfile_fd = fd_log_private_logfile_fd();
1753 0 : ulong required_fds = 10UL+3UL*ctx->tile_cnt+ctx->numa.cnt+ctx->mount_cnt+ctx->sensor.cnt+ctx->sensor.rapl_cnt+(ulong)(-1!=logfile_fd);
1754 0 : for( ulong i=0UL; i<ctx->file_cnt; i++ )
1755 0 : required_fds += (ulong)( ctx->files[ i ].data_fd>=0 && ctx->files[ i ].data_fd!=logfile_fd );
1756 0 : if( FD_UNLIKELY( out_fds_cnt<required_fds ) ) FD_LOG_ERR(( "out_fds_cnt %lu", out_fds_cnt ));
1757 :
1758 0 : ulong out_cnt = 0UL;
1759 0 : out_fds[ out_cnt++ ] = 2; /* stderr */
1760 0 : if( FD_LIKELY( -1!=logfile_fd ) )
1761 0 : out_fds[ out_cnt++ ] = logfile_fd; /* logfile */
1762 0 : out_fds[ out_cnt++ ] = ctx->proc_interrupts_fd; /* /proc/interrupts */
1763 0 : out_fds[ out_cnt++ ] = ctx->proc_softirqs_fd; /* /proc/softirqs */
1764 0 : out_fds[ out_cnt++ ] = ctx->proc_stat_fd; /* /proc/stat */
1765 0 : out_fds[ out_cnt++ ] = ctx->proc_meminfo_fd; /* /proc/meminfo */
1766 0 : if( ctx->proc_loadavg_fd>=0 ) out_fds[ out_cnt++ ] = ctx->proc_loadavg_fd; /* /proc/loadavg */
1767 0 : if( ctx->proc_vmstat_fd >=0 ) out_fds[ out_cnt++ ] = ctx->proc_vmstat_fd; /* /proc/vmstat */
1768 0 : for( ulong i=0UL; i<3UL; i++ ) if( ctx->psi_fd[ i ]>=0 ) out_fds[ out_cnt++ ] = ctx->psi_fd[ i ]; /* /proc/pressure */
1769 0 : for( ulong i=0UL; i<ctx->tile_cnt; i++ ) {
1770 0 : if( -1!=ctx->stat_fds[ i ] ) out_fds[ out_cnt++ ] = ctx->stat_fds[ i ]; /* /proc/<pid>/task/<tid>/stat */
1771 0 : if( -1!=ctx->sched_fds[ i ] ) out_fds[ out_cnt++ ] = ctx->sched_fds[ i ]; /* /proc/<pid>/task/<tid>/sched */
1772 0 : if( -1!=ctx->schedstat_fds[ i ] ) out_fds[ out_cnt++ ] = ctx->schedstat_fds[ i ]; /* /proc/<pid>/task/<tid>/schedstat */
1773 0 : }
1774 0 : for( ulong i=0UL; i<ctx->numa.cnt; i++ )
1775 0 : if( ctx->numa.node[ i ].meminfo_fd>=0 ) out_fds[ out_cnt++ ] = ctx->numa.node[ i ].meminfo_fd;
1776 0 : for( ulong i=0UL; i<ctx->mount_cnt; i++ ) out_fds[ out_cnt++ ] = ctx->mounts[ i ].fd;
1777 0 : for( ulong i=0UL; i<ctx->sensor.cnt; i++ ) out_fds[ out_cnt++ ] = ctx->sensor.fd [ i ]; /* hwmon */
1778 0 : for( ulong i=0UL; i<ctx->sensor.rapl_cnt; i++ ) out_fds[ out_cnt++ ] = ctx->sensor.rapl_fd[ i ]; /* powercap energy_uj */
1779 0 : for( ulong i=0UL; i<ctx->file_cnt; i++ )
1780 0 : if( ctx->files[ i ].data_fd>=0 && ctx->files[ i ].data_fd!=logfile_fd )
1781 0 : out_fds[ out_cnt++ ] = ctx->files[ i ].data_fd;
1782 0 : return out_cnt;
1783 0 : }
1784 :
1785 0 : #define STEM_BURST (1UL)
1786 0 : #define STEM_LAZY ((long)10e6) /* 10ms */
1787 : #define STEM_ALWAYS_PARK 1
1788 :
1789 0 : #define STEM_CALLBACK_CONTEXT_TYPE fd_diag_tile_t
1790 0 : #define STEM_CALLBACK_CONTEXT_ALIGN alignof(fd_diag_tile_t)
1791 :
1792 0 : #define STEM_CALLBACK_NEXT_DEADLINE next_deadline
1793 0 : #define STEM_CALLBACK_DURING_HOUSEKEEPING during_housekeeping
1794 0 : #define STEM_CALLBACK_BEFORE_CREDIT before_credit
1795 :
1796 : #include "../../disco/stem/fd_stem.c"
1797 :
1798 :
1799 : fd_topo_run_tile_t fd_tile_diag = {
1800 : .name = "diag",
1801 : .populate_allowed_seccomp = populate_allowed_seccomp,
1802 : .populate_allowed_fds = populate_allowed_fds,
1803 : .scratch_align = scratch_align,
1804 : .scratch_footprint = scratch_footprint,
1805 : .privileged_init = privileged_init,
1806 : .unprivileged_init = unprivileged_init,
1807 : .run = stem_run,
1808 : };
|