Line data Source code
1 : #define _GNU_SOURCE
2 : #include "fd_boot_report.h"
3 :
4 : #include "../../ballet/pb/fd_pb_encode.h"
5 : #include "../topo/fd_topob.h"
6 : #include "../topo/fd_cpu_topo.h"
7 :
8 : /* manually include just fd_features_generated.h so we can get
9 : FD_FEATURE_SET_ID without anything else that we don't need. */
10 : #define HEADER_fd_src_flamenco_features_fd_features_h
11 : #include "../../flamenco/features/fd_features_generated.h"
12 : #undef HEADER_fd_src_flamenco_features_fd_features_h
13 :
14 : #if defined(__x86_64__)
15 : #include <cpuid.h>
16 : #endif
17 : #include <dirent.h>
18 : #include <errno.h>
19 : #include <fcntl.h>
20 : #include <stdio.h>
21 : #include <stdlib.h>
22 : #include <string.h>
23 : #include <time.h>
24 : #include <unistd.h>
25 : #if __has_include(<gnu/libc-version.h>)
26 : #include <gnu/libc-version.h>
27 : #else
28 : /* Internal musl symbol that the static libc provides. */
29 : extern char const __libc_version[];
30 : #endif
31 : #include <linux/ethtool.h>
32 : #include <linux/sockios.h>
33 : #include <net/if.h>
34 : #include <sys/ioctl.h>
35 : #include <sys/socket.h>
36 : #include <sys/statvfs.h>
37 : #include <sys/syscall.h>
38 : #include <sys/utsname.h>
39 :
40 : static int
41 : read_file( char const * path,
42 : char * buf,
43 : ulong buf_sz,
44 0 : ulong * out_len ) {
45 0 : int fd = open( path, O_RDONLY|O_CLOEXEC );
46 0 : if( FD_UNLIKELY( fd<0 ) ) { buf[ 0 ] = '\0'; if( out_len ) *out_len = 0UL; return -1; }
47 0 : ulong off = 0UL;
48 0 : while( off<buf_sz-1UL ) {
49 0 : long n = read( fd, buf+off, buf_sz-1UL-off );
50 0 : if( FD_UNLIKELY( n<0L ) ) {
51 0 : if( FD_LIKELY( errno==EINTR ) ) continue;
52 0 : close( fd );
53 0 : buf[ 0 ] = '\0'; if( out_len ) *out_len = 0UL;
54 0 : return -1;
55 0 : }
56 0 : if( !n ) break;
57 0 : off += (ulong)n;
58 0 : }
59 0 : close( fd );
60 0 : buf[ off ] = '\0';
61 0 : if( out_len ) *out_len = off;
62 0 : return 0;
63 0 : }
64 :
65 : static int
66 : read_cstr( char const * path,
67 : char * buf,
68 0 : ulong buf_sz ) {
69 0 : if( FD_UNLIKELY( read_file( path, buf, buf_sz, NULL ) ) ) return -1;
70 0 : ulong n = strlen( buf );
71 0 : while( n && (buf[ n-1UL ]=='\n' || buf[ n-1UL ]=='\r' || buf[ n-1UL ]==' ' || buf[ n-1UL ]=='\t') ) buf[ --n ] = '\0';
72 0 : return 0;
73 0 : }
74 :
75 : static int
76 : read_ulong( char const * path,
77 0 : ulong * out ) {
78 0 : char buf[ 64 ];
79 0 : if( FD_UNLIKELY( read_cstr( path, buf, sizeof(buf) ) ) ) return -1;
80 0 : char * end = NULL;
81 0 : ulong v = strtoul( buf, &end, 0 );
82 0 : if( FD_UNLIKELY( end==buf ) ) return -1;
83 0 : *out = v;
84 0 : return 0;
85 0 : }
86 :
87 : static int
88 : read_long( char const * path,
89 0 : long * out ) {
90 0 : char buf[ 64 ];
91 0 : if( FD_UNLIKELY( read_cstr( path, buf, sizeof(buf) ) ) ) return -1;
92 0 : char * end = NULL;
93 0 : long v = strtol( buf, &end, 0 );
94 0 : if( FD_UNLIKELY( end==buf ) ) return -1;
95 0 : *out = v;
96 0 : return 0;
97 0 : }
98 :
99 : /* variant index lookup: 1-based position in schema variant order, 0 if
100 : no match (caller substitutes its unknown/other value) */
101 :
102 : static int
103 : variant_idx( char const * s,
104 : char const * const * tbl,
105 0 : int cnt ) {
106 0 : for( int i=0; i<cnt; i++ ) if( !strcmp( s, tbl[ i ] ) ) return i+1;
107 0 : return 0;
108 0 : }
109 :
110 : /* build info comes from two blobs in the same line format: the
111 : $(OBJDIR)/info file embedded by fd_log (machine target and extras;
112 : generated once per build dir so its date and git status go stale on
113 : incremental builds), and fd_bin_build_info, captured at the start of
114 : every make run and installed by the link of a production binary (see
115 : _make-exe). The latter is weak: NULL in binaries linked without it. */
116 :
117 : extern char const fd_bin_build_info[] __attribute__((weak));
118 :
119 : static char const *
120 : build_info_line( char const * blob,
121 : char const * key,
122 : char * out,
123 0 : ulong out_sz ) {
124 0 : out[ 0 ] = '\0';
125 0 : char const * p = blob;
126 0 : ulong key_len = strlen( key );
127 0 : while( *p ) {
128 0 : char const * line = p;
129 0 : while( *line==' ' ) line++;
130 0 : if( !strncmp( line, key, key_len ) ) {
131 0 : char const * v = line+key_len;
132 0 : while( *v==' ' ) v++;
133 0 : ulong n = 0UL;
134 0 : while( v[ n ] && v[ n ]!='\n' && n<out_sz-1UL ) { out[ n ] = v[ n ]; n++; }
135 0 : out[ n ] = '\0';
136 0 : while( n && (out[ n-1UL ]==' '||out[ n-1UL ]=='\r') ) out[ --n ] = '\0';
137 0 : return out;
138 0 : }
139 0 : while( *p && *p!='\n' ) p++;
140 0 : if( *p ) p++;
141 0 : }
142 0 : return out;
143 0 : }
144 :
145 : static ulong
146 0 : build_timestamp_nanos( char const * blob ) {
147 0 : char date[ 64 ];
148 0 : build_info_line( blob, "# date", date, sizeof(date) );
149 0 : struct tm tm = {0};
150 0 : int tzh = 0, tzm = 0; char tzs = '+';
151 0 : if( FD_UNLIKELY( 8!=sscanf( date, "%d-%d-%d %d:%d:%d %c%2d%2d",
152 0 : &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &tzs, &tzh, &tzm )
153 0 : && 9!=sscanf( date, "%d-%d-%d %d:%d:%d %c%2d%2d",
154 0 : &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &tzs, &tzh, &tzm ) ) ) return 0UL;
155 0 : tm.tm_year -= 1900; tm.tm_mon -= 1;
156 0 : time_t t = timegm( &tm );
157 0 : if( FD_UNLIKELY( t==(time_t)-1 ) ) return 0UL;
158 0 : long off = (long)tzh*3600L + (long)tzm*60L;
159 0 : if( tzs=='-' ) off = -off;
160 0 : return (ulong)( (long)t - off )*1000000000UL;
161 0 : }
162 :
163 : static int
164 0 : build_git_dirty( char const * blob ) {
165 0 : char const * p = blob;
166 0 : while( *p ) {
167 0 : char const * line = p;
168 0 : while( *line==' ' ) line++;
169 0 : if( line[ 0 ]=='1' || line[ 0 ]=='2' || line[ 0 ]=='u' ) {
170 0 : if( line[ 1 ]==' ' ) return 1;
171 0 : }
172 0 : while( *p && *p!='\n' ) p++;
173 0 : if( *p ) p++;
174 0 : }
175 0 : return 0;
176 0 : }
177 :
178 : static void
179 0 : collect_os( fd_boot_report_t * r ) {
180 0 : struct utsname un;
181 0 : if( FD_LIKELY( 0==uname( &un ) ) ) {
182 0 : FD_TEST( fd_cstr_printf_check( r->kernel_release, sizeof(r->kernel_release), NULL, "%s", un.release ) );
183 0 : FD_TEST( fd_cstr_printf_check( r->kernel_version, sizeof(r->kernel_version), NULL, "%s", un.version ) );
184 0 : }
185 :
186 : /* vulnerabilities whose sysfs status reports an active mitigation */
187 0 : DIR * vul = opendir( "/sys/devices/system/cpu/vulnerabilities" );
188 0 : if( FD_LIKELY( vul ) ) {
189 0 : struct dirent * ent;
190 0 : while( (ent = readdir( vul )) && r->mitigations_cnt<32UL ) {
191 0 : if( ent->d_name[ 0 ]=='.' ) continue;
192 0 : char path[ 320 ], status[ 256 ];
193 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/devices/system/cpu/vulnerabilities/%s", ent->d_name ) );
194 0 : if( read_cstr( path, status, sizeof(status) ) ) continue;
195 0 : if( strncmp( status, "Mitigation", 10UL ) ) continue;
196 0 : fd_cstr_printf( r->mitigations[ r->mitigations_cnt++ ], 33UL, NULL, "%s", ent->d_name );
197 0 : }
198 0 : closedir( vul );
199 0 : }
200 :
201 0 : char osrel[ 8192 ];
202 0 : if( FD_LIKELY( !read_file( "/etc/os-release", osrel, sizeof(osrel), NULL ) ) ) {
203 0 : char * saveptr = NULL;
204 0 : for( char * line = strtok_r( osrel, "\n", &saveptr ); line; line = strtok_r( NULL, "\n", &saveptr ) ) {
205 0 : char * eq = strchr( line, '=' );
206 0 : if( !eq ) continue;
207 0 : *eq = '\0';
208 0 : char * val = eq+1;
209 0 : ulong vlen = strlen( val );
210 0 : if( vlen>=2UL && val[ 0 ]=='"' && val[ vlen-1UL ]=='"' ) { val[ vlen-1UL ] = '\0'; val++; }
211 0 : if( !strcmp( line, "ID" ) ) fd_cstr_printf( r->distro_id, sizeof(r->distro_id), NULL, "%s", val );
212 0 : else if( !strcmp( line, "VERSION_ID" ) ) fd_cstr_printf( r->distro_version_id, sizeof(r->distro_version_id), NULL, "%s", val );
213 0 : else if( !strcmp( line, "ID_LIKE" ) ) fd_cstr_printf( r->distro_id_like, sizeof(r->distro_id_like), NULL, "%s", val );
214 0 : }
215 0 : }
216 :
217 0 : #if __has_include(<gnu/libc-version.h>)
218 0 : r->libc_kind = 1; /* glibc */
219 0 : fd_cstr_printf( r->libc_version, sizeof(r->libc_version), NULL, "%s", gnu_get_libc_version() );
220 : #else
221 : r->libc_kind = 2; /* musl */
222 : fd_cstr_printf( r->libc_version, sizeof(r->libc_version), NULL, "%s", __libc_version );
223 : #endif
224 :
225 0 : static char const * const machine_tbl[] = {
226 0 : "native", "linux_gcc_x86_64", "linux_gcc_zen2", "linux_gcc_zen3", "linux_gcc_zen4",
227 0 : "linux_gcc_zen5", "linux_gcc_icelake", "linux_clang_x86_64", "linux_clang_haswell",
228 0 : "linux_clang_icelake", "linux_clang_zen2", "linux_clang_zen3", "linux_clang_zen4",
229 0 : "linux_clang_zen5" };
230 0 : char machine[ 64 ];
231 0 : build_info_line( fd_log_build_info, "# machine", machine, sizeof(machine) );
232 0 : int mt = variant_idx( machine, machine_tbl, 14 );
233 0 : r->build_machine_target = mt ? mt : 15; /* other */
234 :
235 0 : char extras[ 128 ];
236 0 : build_info_line( fd_log_build_info, "# extras", extras, sizeof(extras) );
237 0 : FD_TEST( fd_cstr_printf_check( r->build_extras, sizeof(r->build_extras), NULL, "%s", extras ) );
238 :
239 : /* date and dirty state from the link-time blob when this binary has
240 : one; the build-dir info file goes stale on incremental builds */
241 0 : char const * link_info = fd_bin_build_info ? fd_bin_build_info : fd_log_build_info;
242 0 : r->build_git_dirty = build_git_dirty( link_info );
243 :
244 0 : #if defined(__clang__)
245 0 : r->compiler = 2;
246 : #else
247 : r->compiler = 1;
248 : #endif
249 0 : fd_cstr_printf( r->compiler_version, sizeof(r->compiler_version), NULL, "%s", __VERSION__ );
250 0 : r->build_timestamp_nanos = build_timestamp_nanos( link_info );
251 :
252 0 : #if FD_HAS_SSE
253 0 : FD_TEST( fd_cstr_printf_check( r->build_features[ r->build_features_cnt++ ], 8UL, NULL, "%s", "sse" ) );
254 0 : #endif
255 0 : #if FD_HAS_AVX
256 0 : FD_TEST( fd_cstr_printf_check( r->build_features[ r->build_features_cnt++ ], 8UL, NULL, "%s", "avx" ) );
257 0 : #endif
258 0 : #if FD_HAS_AVX512
259 0 : FD_TEST( fd_cstr_printf_check( r->build_features[ r->build_features_cnt++ ], 8UL, NULL, "%s", "avx512" ) );
260 0 : #endif
261 0 : #if FD_HAS_SHANI
262 0 : FD_TEST( fd_cstr_printf_check( r->build_features[ r->build_features_cnt++ ], 8UL, NULL, "%s", "shani" ) );
263 0 : #endif
264 0 : #if FD_HAS_AESNI
265 0 : FD_TEST( fd_cstr_printf_check( r->build_features[ r->build_features_cnt++ ], 8UL, NULL, "%s", "aesni" ) );
266 0 : #endif
267 0 : #if FD_HAS_GFNI
268 0 : FD_TEST( fd_cstr_printf_check( r->build_features[ r->build_features_cnt++ ], 8UL, NULL, "%s", "gfni" ) );
269 0 : #endif
270 0 : }
271 :
272 : static char const *
273 : cpuinfo_field( char const * cpuinfo,
274 : char const * key,
275 : char * out,
276 0 : ulong out_sz ) {
277 0 : out[ 0 ] = '\0';
278 0 : char const * p = strstr( cpuinfo, key );
279 0 : if( !p ) return out;
280 0 : p = strchr( p, ':' );
281 0 : if( !p ) return out;
282 0 : p++;
283 0 : while( *p==' ' || *p=='\t' ) p++;
284 0 : ulong n = 0UL;
285 0 : while( p[ n ] && p[ n ]!='\n' && n<out_sz-1UL ) { out[ n ] = p[ n ]; n++; }
286 0 : out[ n ] = '\0';
287 0 : return out;
288 0 : }
289 :
290 : static void
291 0 : collect_cpu( fd_boot_report_t * r ) {
292 0 : struct utsname un;
293 0 : r->cpu_arch = 3; /* unknown */
294 0 : if( FD_LIKELY( 0==uname( &un ) ) ) {
295 0 : if( !strcmp ( un.machine, "x86_64" ) ) r->cpu_arch = 1;
296 0 : else if( !strncmp( un.machine, "aarch64", 7 ) || !strncmp( un.machine, "arm", 3 ) ) r->cpu_arch = 2;
297 0 : }
298 :
299 0 : # if defined(__x86_64__)
300 0 : uint eax, ebx, ecx, edx;
301 0 : if( FD_LIKELY( __get_cpuid( 0U, &eax, &ebx, &ecx, &edx ) ) ) {
302 0 : char vendor[ 13 ];
303 0 : fd_memcpy( vendor, &ebx, 4UL );
304 0 : fd_memcpy( vendor+4, &edx, 4UL );
305 0 : fd_memcpy( vendor+8, &ecx, 4UL );
306 0 : vendor[ 12 ] = '\0';
307 0 : if( !strcmp( vendor, "GenuineIntel" ) ) r->cpu_vendor = 1;
308 0 : else if( !strcmp( vendor, "AuthenticAMD" ) ) r->cpu_vendor = 2;
309 0 : else r->cpu_vendor = 3;
310 0 : } else r->cpu_vendor = 3;
311 :
312 0 : if( FD_LIKELY( __get_cpuid( 1U, &eax, &ebx, &ecx, &edx ) ) ) {
313 0 : uint base_family = (eax>>8)&0xFU;
314 0 : uint ext_family = (eax>>20)&0xFFU;
315 0 : uint base_model = (eax>>4)&0xFU;
316 0 : uint ext_model = (eax>>16)&0xFU;
317 0 : r->cpu_family = (ushort)( base_family + fd_uint_if( base_family==0xFU, ext_family, 0U ) );
318 0 : r->cpu_model_id = (ushort)( base_model | fd_uint_if( base_family==0x6U || base_family==0xFU, ext_model<<4, 0U ) );
319 0 : r->cpu_stepping = (uchar)( eax&0xFU );
320 0 : }
321 :
322 0 : uint ecx1 = 0U, ebx7 = 0U, ecx7 = 0U;
323 0 : if( FD_LIKELY( __get_cpuid( 1U, &eax, &ebx, &ecx, &edx ) ) ) ecx1 = ecx;
324 0 : if( FD_LIKELY( __get_cpuid_count( 7U, 0U, &eax, &ebx, &ecx, &edx ) ) ) { ebx7 = ebx; ecx7 = ecx; }
325 0 : if( (ecx1>>20)&1U ) FD_TEST( fd_cstr_printf_check( r->cpu_features[ r->cpu_features_cnt++ ], 8UL, NULL, "%s", "sse" ) );
326 0 : if( (ebx7>>5)&1U ) FD_TEST( fd_cstr_printf_check( r->cpu_features[ r->cpu_features_cnt++ ], 8UL, NULL, "%s", "avx" ) );
327 0 : if( ((ebx7>>16)&1U) && ((ebx7>>21)&1U) ) FD_TEST( fd_cstr_printf_check( r->cpu_features[ r->cpu_features_cnt++ ], 8UL, NULL, "%s", "avx512" ) );
328 0 : if( (ebx7>>29)&1U ) FD_TEST( fd_cstr_printf_check( r->cpu_features[ r->cpu_features_cnt++ ], 8UL, NULL, "%s", "shani" ) );
329 0 : if( (ecx1>>25)&1U ) FD_TEST( fd_cstr_printf_check( r->cpu_features[ r->cpu_features_cnt++ ], 8UL, NULL, "%s", "aesni" ) );
330 0 : if( (ecx7>>8)&1U ) FD_TEST( fd_cstr_printf_check( r->cpu_features[ r->cpu_features_cnt++ ], 8UL, NULL, "%s", "gfni" ) );
331 0 : # endif /* defined(__x86_64__) */
332 :
333 0 : char cpuinfo[ 16384 ];
334 0 : if( FD_LIKELY( !read_file( "/proc/cpuinfo", cpuinfo, sizeof(cpuinfo), NULL ) ) ) {
335 0 : char tmp[ 128 ];
336 0 : fd_cstr_printf( r->cpu_model_name, sizeof(r->cpu_model_name), NULL, "%s", cpuinfo_field( cpuinfo, "model name", tmp, sizeof(tmp) ) );
337 0 : cpuinfo_field( cpuinfo, "microcode", tmp, sizeof(tmp) );
338 0 : if( tmp[ 0 ] ) r->cpu_microcode_version = strtoul( tmp, NULL, 0 );
339 0 : }
340 :
341 : /* logical count, physical cores and sockets from the sysfs topology */
342 0 : ulong logical = 0UL;
343 0 : ulong pairs[ 4096 ]; ulong pair_cnt = 0UL; /* (package<<32)|core, deduped */
344 0 : ulong pkgs [ 64 ]; ulong pkg_cnt = 0UL;
345 0 : for( ulong cpu=0UL; cpu<4096UL; cpu++ ) {
346 0 : char path[ 128 ];
347 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/devices/system/cpu/cpu%lu/topology/physical_package_id", cpu ) );
348 0 : ulong pkg;
349 0 : if( read_ulong( path, &pkg ) ) {
350 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/devices/system/cpu/cpu%lu", cpu ) );
351 0 : if( 0!=access( path, F_OK ) ) break; /* end of cpus */
352 0 : continue; /* offline cpu */
353 0 : }
354 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/devices/system/cpu/cpu%lu/topology/core_id", cpu ) );
355 0 : long core;
356 0 : if( read_long( path, &core ) ) continue;
357 0 : logical++;
358 0 : int seen = 0;
359 0 : if( (long)pkg>=0L && core>=0L ) { /* -1 is unknown */
360 0 : ulong pair = (pkg<<32) | ((ulong)core&0xFFFFFFFFUL);
361 0 : for( ulong i=0UL; i<pair_cnt; i++ ) if( pairs[ i ]==pair ) { seen = 1; break; }
362 0 : if( !seen && pair_cnt<4096UL ) pairs[ pair_cnt++ ] = pair;
363 0 : }
364 0 : seen = 0;
365 0 : for( ulong i=0UL; i<pkg_cnt; i++ ) if( pkgs[ i ]==pkg ) { seen = 1; break; }
366 0 : if( !seen && pkg_cnt<64UL ) pkgs[ pkg_cnt++ ] = pkg;
367 0 : }
368 0 : r->cpu_logical_count = (ushort)fd_ulong_min( logical, USHORT_MAX );
369 0 : r->cpu_physical_core_count = (ushort)fd_ulong_min( pair_cnt, USHORT_MAX );
370 0 : r->cpu_socket_count = (uchar) fd_ulong_min( pkg_cnt, UCHAR_MAX );
371 :
372 0 : ulong v;
373 0 : r->smt_active = !read_ulong( "/sys/devices/system/cpu/smt/active", &v ) && v==1UL;
374 :
375 0 : char buf[ 64 ];
376 0 : static char const * const gov_tbl[] = { "performance", "powersave", "ondemand", "conservative", "schedutil", "userspace" };
377 0 : r->cpufreq_governor = 7;
378 0 : if( !read_cstr( "/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor", buf, sizeof(buf) ) ) {
379 0 : int i = variant_idx( buf, gov_tbl, 6 );
380 0 : if( i ) r->cpufreq_governor = i;
381 0 : }
382 :
383 0 : static char const * const drv_tbl[] = { "intel_pstate", "intel_cpufreq", "acpi-cpufreq", "amd-pstate", "amd-pstate-epp", "cppc_cpufreq" };
384 0 : r->cpufreq_driver = 7;
385 0 : if( !read_cstr( "/sys/devices/system/cpu/cpu0/cpufreq/scaling_driver", buf, sizeof(buf) ) ) {
386 0 : int i = variant_idx( buf, drv_tbl, 6 );
387 0 : if( i ) r->cpufreq_driver = i;
388 0 : }
389 :
390 0 : static char const * const epp_tbl[] = { "performance", "balance_performance", "balance_power", "power", "default" };
391 0 : r->cpu_energy_perf_preference = 6;
392 0 : if( !read_cstr( "/sys/devices/system/cpu/cpu0/cpufreq/energy_performance_preference", buf, sizeof(buf) ) ) {
393 0 : int i = variant_idx( buf, epp_tbl, 5 );
394 0 : if( i ) r->cpu_energy_perf_preference = i;
395 0 : }
396 :
397 0 : static char const * const idle_tbl[] = { "intel_idle", "acpi_idle", "none" };
398 0 : r->cpu_idle_driver = 4;
399 0 : if( !read_cstr( "/sys/devices/system/cpu/cpuidle/current_driver", buf, sizeof(buf) ) ) {
400 0 : int i = variant_idx( buf, idle_tbl, 3 );
401 0 : if( i ) r->cpu_idle_driver = i;
402 0 : }
403 :
404 : /* deepest idle state not disabled */
405 0 : for( ulong s=0UL; s<16UL; s++ ) {
406 0 : char path[ 128 ];
407 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/devices/system/cpu/cpu0/cpuidle/state%lu/disable", s ) );
408 0 : ulong disabled;
409 0 : if( read_ulong( path, &disabled ) ) break;
410 0 : if( disabled ) continue;
411 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/devices/system/cpu/cpu0/cpuidle/state%lu/name", s ) );
412 0 : read_cstr( path, r->cpu_max_enabled_cstate, sizeof(r->cpu_max_enabled_cstate) );
413 0 : }
414 0 : }
415 :
416 : /* Last-level cache domain per logical cpu: the level-3 cache's id under
417 : cpuN/cache/indexM, keyed with the package (ids repeat per socket) and
418 : renumbered from 0 in first-seen order. Offline cpus read USHORT_MAX;
419 : the map is left empty if any online cpu lacks its package or level-3
420 : entry, so a partial map never reads as topology. */
421 :
422 : static void
423 0 : collect_l3( fd_boot_report_t * r ) {
424 0 : long key[ 1024 ];
425 0 : ulong key_cnt = 0UL;
426 0 : for( ulong cpu=0UL; cpu<r->numa_cpu_to_node_cnt; cpu++ ) {
427 0 : char path[ 128 ];
428 0 : ulong online = 1UL; /* cpu0 has no online file: it cannot go offline */
429 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/devices/system/cpu/cpu%lu/online", cpu ) );
430 0 : if( cpu && FD_UNLIKELY( read_ulong( path, &online ) ) ) online = 1UL;
431 0 : if( !online ) { r->l3_cpu_to_domain[ cpu ] = USHORT_MAX; r->l3_cpu_to_domain_cnt = cpu+1UL; continue; }
432 0 : ulong pkg = 0UL;
433 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/devices/system/cpu/cpu%lu/topology/physical_package_id", cpu ) );
434 0 : if( FD_UNLIKELY( read_ulong( path, &pkg ) ) ) { r->l3_cpu_to_domain_cnt = 0UL; return; }
435 0 : long id = -1L;
436 0 : for( ulong index=0UL; index<8UL; index++ ) {
437 0 : ulong level, cid;
438 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/devices/system/cpu/cpu%lu/cache/index%lu/level", cpu, index ) );
439 0 : if( read_ulong( path, &level ) ) break;
440 0 : if( level!=3UL ) continue;
441 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/devices/system/cpu/cpu%lu/cache/index%lu/id", cpu, index ) );
442 0 : if( !read_ulong( path, &cid ) ) id = (long)((pkg<<32) | cid);
443 0 : break;
444 0 : }
445 0 : if( FD_UNLIKELY( id<0L ) ) { r->l3_cpu_to_domain_cnt = 0UL; return; }
446 0 : ulong d = 0UL;
447 0 : while( d<key_cnt && key[ d ]!=id ) d++;
448 0 : if( d==key_cnt ) key[ key_cnt++ ] = id;
449 0 : r->l3_cpu_to_domain[ cpu ] = (ushort)d;
450 0 : r->l3_cpu_to_domain_cnt = cpu+1UL;
451 0 : }
452 0 : }
453 :
454 : static void
455 0 : collect_cores( fd_boot_report_t * r ) {
456 0 : static ulong key[ 1024 ];
457 0 : ulong key_cnt = 0UL;
458 0 : r->cpu_to_core_cnt = r->numa_cpu_to_node_cnt;
459 0 : for( ulong cpu=0UL; cpu<r->cpu_to_core_cnt; cpu++ ) {
460 0 : char path[ 128 ];
461 0 : r->cpu_to_core[ cpu ] = USHORT_MAX;
462 0 : long pkg, core;
463 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/devices/system/cpu/cpu%lu/topology/physical_package_id", cpu ) );
464 0 : if( read_long( path, &pkg ) || pkg<0L ) continue; /* -1 is unknown */
465 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/devices/system/cpu/cpu%lu/topology/core_id", cpu ) );
466 0 : if( read_long( path, &core ) || core<0L ) continue;
467 0 : ulong id = ((ulong)pkg<<32) | ((ulong)core&0xFFFFFFFFUL);
468 0 : ulong c = 0UL;
469 0 : while( c<key_cnt && key[ c ]!=id ) c++;
470 0 : if( c==key_cnt ) key[ key_cnt++ ] = id;
471 0 : r->cpu_to_core[ cpu ] = (ushort)c;
472 0 : }
473 0 : }
474 :
475 : static void
476 : collect_affinity( fd_boot_report_t * r,
477 0 : fd_topo_tile_t const * tile ) {
478 0 : FD_STATIC_ASSERT( sizeof(tile->event.launch_cpus)==8UL*fd_cpuset_word_cnt, launch_cpus );
479 0 : fd_cpuset_t const * launch = (fd_cpuset_t const *)tile->event.launch_cpus;
480 0 : for( ulong cpu=fd_cpuset_const_iter_init( launch ); !fd_cpuset_const_iter_done( cpu ); cpu=fd_cpuset_const_iter_next( launch, cpu ) )
481 0 : r->launch_cpus[ r->launch_cpus_cnt++ ] = (ushort)cpu;
482 0 : }
483 :
484 : static ulong
485 : read_cpulist( char const * path,
486 0 : ushort * out ) {
487 0 : char list[ 16384 ]; /* worst-case alternating cpulist over 1024 cpus */
488 0 : if( read_cstr( path, list, sizeof(list) ) ) return 0UL;
489 0 : ulong cnt = 0UL;
490 0 : char * saveptr = NULL;
491 0 : for( char * range = strtok_r( list, ",", &saveptr ); range; range = strtok_r( NULL, ",", &saveptr ) ) {
492 0 : ulong lo, hi;
493 0 : if( 2==sscanf( range, "%lu-%lu", &lo, &hi ) ) {}
494 0 : else if( 1==sscanf( range, "%lu", &lo ) ) hi = lo;
495 0 : else continue;
496 0 : for( ulong cpu=lo; cpu<=hi && cnt<1024UL; cpu++ ) out[ cnt++ ] = (ushort)fd_ulong_min( cpu, USHORT_MAX );
497 0 : }
498 0 : return cnt;
499 0 : }
500 :
501 : static void
502 0 : collect_mem( fd_boot_report_t * r ) {
503 0 : char meminfo[ 8192 ];
504 0 : if( FD_LIKELY( !read_file( "/proc/meminfo", meminfo, sizeof(meminfo), NULL ) ) ) {
505 0 : char tmp[ 64 ];
506 0 : cpuinfo_field( meminfo, "MemTotal", tmp, sizeof(tmp) );
507 0 : if( tmp[ 0 ] ) r->host_memory_bytes = strtoul( tmp, NULL, 10 )*1024UL;
508 0 : cpuinfo_field( meminfo, "MemAvailable", tmp, sizeof(tmp) );
509 0 : if( tmp[ 0 ] ) r->host_memory_available_bytes = strtoul( tmp, NULL, 10 )*1024UL;
510 0 : }
511 :
512 0 : ulong max_cpu = 0UL;
513 0 : for( ulong node=0UL; node<64UL; node++ ) {
514 0 : char path[ 128 ];
515 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/devices/system/node/node%lu/cpulist", node ) );
516 0 : char list[ 16384 ]; /* worst-case alternating cpulist over 1024 cpus */
517 0 : if( read_cstr( path, list, sizeof(list) ) ) continue;
518 0 : char * saveptr = NULL;
519 0 : for( char * range = strtok_r( list, ",", &saveptr ); range; range = strtok_r( NULL, ",", &saveptr ) ) {
520 0 : ulong lo, hi;
521 0 : if( 2==sscanf( range, "%lu-%lu", &lo, &hi ) ) {}
522 0 : else if( 1==sscanf( range, "%lu", &lo ) ) hi = lo;
523 0 : else continue;
524 0 : for( ulong cpu=lo; cpu<=hi && cpu<1024UL; cpu++ ) {
525 0 : r->numa_cpu_to_node[ cpu ] = (uchar)fd_ulong_min( node, UCHAR_MAX );
526 0 : if( cpu+1UL>max_cpu ) max_cpu = cpu+1UL;
527 0 : }
528 0 : }
529 0 : }
530 0 : r->numa_cpu_to_node_cnt = max_cpu;
531 :
532 0 : r->isolated_cpus_cnt = read_cpulist( "/sys/devices/system/cpu/isolated", r->isolated_cpus );
533 0 : }
534 :
535 : static void
536 : collect_cpuset_partition( fd_boot_report_t * r,
537 0 : char const * app_name ) {
538 0 : char path[ PATH_MAX ];
539 0 : char val[ 256 ];
540 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/fs/cgroup/%s/cpuset.cpus.partition", app_name ) );
541 0 : if( read_cstr( path, val, sizeof(val) ) ) {
542 0 : r->cpuset_partition = errno==ENOENT ? 1 /* none */ : 6 /* unknown */;
543 0 : return;
544 0 : }
545 : /* "root invalid (reason)" and "isolated invalid (reason)" when rejected */
546 0 : if( strstr( val, "invalid" ) ) r->cpuset_partition = 5;
547 0 : else if( !strcmp( val, "member" ) ) r->cpuset_partition = 2;
548 0 : else if( !strcmp( val, "root" ) ) r->cpuset_partition = 3;
549 0 : else if( !strcmp( val, "isolated" ) ) r->cpuset_partition = 4;
550 0 : else r->cpuset_partition = 6; /* unknown */
551 :
552 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/fs/cgroup/%s/cpuset.cpus.effective", app_name ) );
553 0 : r->cpuset_partition_cpus_cnt = read_cpulist( path, r->cpuset_partition_cpus );
554 0 : }
555 :
556 : static char const *
557 : smbios_string( uchar const * raw,
558 : ulong raw_sz,
559 0 : uint idx ) {
560 0 : if( !idx || raw_sz<2UL ) return "";
561 0 : ulong off = raw[ 1 ]; /* formatted area length */
562 0 : for( uint i=1U; off<raw_sz; i++ ) {
563 0 : if( i==idx ) return (char const *)( raw+off );
564 0 : off += strlen( (char const *)( raw+off ) )+1UL;
565 0 : if( off<raw_sz && !raw[ off ] ) break; /* double NUL: end of strings */
566 0 : }
567 0 : return "";
568 0 : }
569 :
570 : /* decode_dimm appends the type 17 structure raw[0,raw_sz), formatted
571 : area plus string set, to the report. */
572 :
573 : static void
574 : decode_dimm( fd_boot_report_t * r,
575 : uchar const * raw,
576 0 : ulong raw_sz ) {
577 0 : if( raw_sz<2UL ) return;
578 0 : ulong fmt_len = raw[ 1 ]; /* formatted area; raw_sz also spans the string table */
579 0 : if( fmt_len>raw_sz || fmt_len<0x17UL ) return; /* need through the speed word */
580 :
581 0 : ulong size_bytes = 0UL;
582 0 : uint size_w = (uint)raw[ 0x0C ] | ((uint)raw[ 0x0D ]<<8);
583 0 : if( size_w==0U || size_w==0xFFFFU ) return; /* empty or unknown slot */
584 0 : if( size_w==0x7FFFU && fmt_len>=0x20UL ) {
585 0 : uint ext = (uint)raw[ 0x1C ] | ((uint)raw[ 0x1D ]<<8) | ((uint)raw[ 0x1E ]<<16) | ((uint)raw[ 0x1F ]<<24);
586 0 : size_bytes = (ulong)( ext&0x7FFFFFFFU )<<20;
587 0 : } else if( size_w&0x8000U ) size_bytes = (ulong)( size_w&0x7FFFU )<<10;
588 0 : else size_bytes = (ulong)size_w<<20;
589 :
590 0 : fd_boot_report_dimm_t * d = &r->dmi_dimms[ r->dmi_dimms_cnt++ ];
591 0 : d->size_bytes = size_bytes;
592 :
593 0 : switch( raw[ 0x12 ] ) {
594 0 : case 0x12: FD_TEST( fd_cstr_printf_check( d->mem_type, sizeof(d->mem_type), NULL, "%s", "DDR" ) ); break;
595 0 : case 0x13: FD_TEST( fd_cstr_printf_check( d->mem_type, sizeof(d->mem_type), NULL, "%s", "DDR2" ) ); break;
596 0 : case 0x18: FD_TEST( fd_cstr_printf_check( d->mem_type, sizeof(d->mem_type), NULL, "%s", "DDR3" ) ); break;
597 0 : case 0x1A: FD_TEST( fd_cstr_printf_check( d->mem_type, sizeof(d->mem_type), NULL, "%s", "DDR4" ) ); break;
598 0 : case 0x1E: FD_TEST( fd_cstr_printf_check( d->mem_type, sizeof(d->mem_type), NULL, "%s", "LPDDR4" ) ); break;
599 0 : case 0x22: FD_TEST( fd_cstr_printf_check( d->mem_type, sizeof(d->mem_type), NULL, "%s", "DDR5" ) ); break;
600 0 : case 0x23: FD_TEST( fd_cstr_printf_check( d->mem_type, sizeof(d->mem_type), NULL, "%s", "LPDDR5" ) ); break;
601 0 : default: FD_TEST( fd_cstr_printf_check( d->mem_type, sizeof(d->mem_type), NULL, "%s", "other" ) ); break;
602 0 : }
603 :
604 0 : d->rated_speed_mts = (ulong)raw[ 0x15 ] | ((ulong)raw[ 0x16 ]<<8);
605 0 : if( fmt_len>=0x22UL ) d->speed_mts = (ulong)raw[ 0x20 ] | ((ulong)raw[ 0x21 ]<<8);
606 0 : if( fmt_len>=0x18UL ) fd_cstr_printf( d->manufacturer, sizeof(d->manufacturer), NULL, "%s", smbios_string( raw, raw_sz, raw[ 0x17 ] ) );
607 0 : if( fmt_len>=0x1BUL ) fd_cstr_printf( d->part_number, sizeof(d->part_number), NULL, "%s", smbios_string( raw, raw_sz, raw[ 0x1A ] ) );
608 0 : ulong n = strlen( d->part_number );
609 0 : while( n && d->part_number[ n-1UL ]==' ' ) d->part_number[ --n ] = '\0';
610 0 : n = strlen( d->manufacturer );
611 0 : while( n && d->manufacturer[ n-1UL ]==' ' ) d->manufacturer[ --n ] = '\0';
612 0 : }
613 :
614 : static void
615 0 : collect_dimms( fd_boot_report_t * r ) {
616 0 : int have_entries = 0;
617 0 : for( ulong i=0UL; i<256UL && r->dmi_dimms_cnt<64UL; i++ ) {
618 0 : char path[ 128 ];
619 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/firmware/dmi/entries/17-%lu/raw", i ) );
620 0 : uchar raw[ 4096 ];
621 0 : ulong raw_sz;
622 0 : if( read_file( path, (char *)raw, sizeof(raw), &raw_sz ) ) break;
623 0 : have_entries = 1;
624 0 : decode_dimm( r, raw, raw_sz );
625 0 : }
626 0 : if( have_entries ) return;
627 :
628 : /* No dmi-sysfs module (systemd <252 with a modular build): walk the
629 : SMBIOS table the kernel always exports. SMBIOS 3 tables can be
630 : large, so stream it, keeping only the unparsed tail in the window. */
631 0 : int fd = open( "/sys/firmware/dmi/tables/DMI", O_RDONLY|O_CLOEXEC );
632 0 : if( FD_UNLIKELY( fd<0 ) ) return;
633 0 : static uchar win[ 65536 ];
634 0 : ulong win_sz = 0UL;
635 0 : int eof = 0;
636 0 : while( r->dmi_dimms_cnt<64UL ) {
637 0 : if( !eof && win_sz<sizeof(win) ) {
638 0 : long n = read( fd, win+win_sz, sizeof(win)-win_sz );
639 0 : if( FD_UNLIKELY( n<0L && errno==EINTR ) ) continue;
640 0 : if( n<=0L ) eof = 1;
641 0 : else win_sz += (ulong)n;
642 0 : }
643 :
644 : /* Parse every structure that is complete in the window */
645 0 : ulong off = 0UL;
646 0 : int done = 0;
647 0 : while( off+4UL<=win_sz && r->dmi_dimms_cnt<64UL ) {
648 0 : uchar type = win[ off ];
649 0 : ulong fmt_len = win[ off+1UL ];
650 0 : if( FD_UNLIKELY( fmt_len<4UL ) ) { done = 1; break; }
651 0 : if( off+fmt_len>win_sz ) break;
652 0 : ulong end = off+fmt_len;
653 0 : while( end+1UL<win_sz && ( win[ end ] || win[ end+1UL ] ) ) end++;
654 0 : if( end+1UL>=win_sz ) break; /* string set not complete yet */
655 0 : end += 2UL; /* past the double NUL */
656 0 : if( type==17 ) decode_dimm( r, win+off, end-off );
657 0 : off = end;
658 0 : if( type==127 ) { done = 1; break; }
659 0 : }
660 :
661 0 : if( done || ( eof && !off ) || ( !off && win_sz==sizeof(win) ) ) break; /* end, truncated, or a structure larger than the window */
662 0 : memmove( win, win+off, win_sz-off );
663 0 : win_sz -= off;
664 0 : }
665 0 : close( fd );
666 0 : }
667 :
668 : static int
669 : ethtool_ioc( int fd,
670 : char const * ifname,
671 0 : void * data ) {
672 0 : struct ifreq ifr = {0};
673 0 : FD_TEST( fd_cstr_printf_check( ifr.ifr_name, IFNAMSIZ, NULL, "%s", ifname ) );
674 0 : ifr.ifr_data = data;
675 0 : return ioctl( fd, SIOCETHTOOL, &ifr );
676 0 : }
677 :
678 : static void
679 : readlink_base( char const * path,
680 : char * out,
681 0 : ulong out_sz ) {
682 0 : out[ 0 ] = '\0';
683 0 : char target[ PATH_MAX ];
684 0 : long n = readlink( path, target, sizeof(target)-1UL );
685 0 : if( n<=0L ) return;
686 0 : target[ n ] = '\0';
687 0 : char * base = strrchr( target, '/' );
688 0 : fd_cstr_printf( out, out_sz, NULL, "%s", base ? base+1 : target );
689 0 : }
690 :
691 : static void
692 : collect_one_nic( fd_boot_report_t * r,
693 : int fd,
694 : char const * name,
695 0 : int is_bond ) {
696 0 : if( r->nic_devices_cnt>=16UL ) return;
697 0 : fd_boot_report_nic_t * nic = &r->nic_devices[ r->nic_devices_cnt++ ];
698 0 : fd_memset( nic, 0, sizeof(*nic) );
699 0 : FD_TEST( fd_cstr_printf_check( nic->name, sizeof(nic->name), NULL, "%s", name ) );
700 0 : nic->is_bond_master = is_bond;
701 :
702 0 : char buf[ 256 ];
703 0 : char path[ PATH_MAX ];
704 :
705 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/class/net/%s/device/driver", name ) );
706 0 : readlink_base( path, nic->driver, sizeof(nic->driver) );
707 :
708 0 : struct ethtool_drvinfo drv = { .cmd = ETHTOOL_GDRVINFO };
709 0 : if( !ethtool_ioc( fd, name, &drv ) ) {
710 0 : if( !nic->driver[ 0 ] ) FD_TEST( fd_cstr_printf_check( nic->driver, sizeof(nic->driver), NULL, "%s", drv.driver ) );
711 0 : FD_TEST( fd_cstr_printf_check( nic->driver_version, sizeof(nic->driver_version), NULL, "%s", drv.version ) );
712 0 : FD_TEST( fd_cstr_printf_check( nic->firmware_version, sizeof(nic->firmware_version), NULL, "%s", drv.fw_version ) );
713 0 : FD_TEST( fd_cstr_printf_check( nic->bus_info, sizeof(nic->bus_info), NULL, "%s", drv.bus_info ) );
714 0 : }
715 :
716 0 : ulong v;
717 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/class/net/%s/device/current_link_width", name ) );
718 0 : if( !read_ulong( path, &v ) ) nic->pcie_current_link_width = (uchar)fd_ulong_min( v, UCHAR_MAX );
719 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/class/net/%s/device/max_link_width", name ) );
720 0 : if( !read_ulong( path, &v ) ) nic->pcie_max_link_width = (uchar)fd_ulong_min( v, UCHAR_MAX );
721 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/class/net/%s/device/current_link_speed", name ) );
722 0 : if( !read_cstr( path, buf, sizeof(buf) ) ) fd_cstr_printf( nic->pcie_current_link_speed, sizeof(nic->pcie_current_link_speed), NULL, "%s", buf );
723 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/class/net/%s/device/max_link_speed", name ) );
724 0 : if( !read_cstr( path, buf, sizeof(buf) ) ) fd_cstr_printf( nic->pcie_max_link_speed, sizeof(nic->pcie_max_link_speed), NULL, "%s", buf );
725 :
726 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/class/net/%s/device/device", name ) );
727 0 : if( !read_ulong( path, &v ) ) nic->pci_device_id = (ushort)v;
728 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/class/net/%s/device/vendor", name ) );
729 0 : if( !read_ulong( path, &v ) ) nic->pci_vendor_id = (ushort)v;
730 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/class/net/%s/device/subsystem_vendor", name ) );
731 0 : if( !read_ulong( path, &v ) ) nic->pci_subsystem_vendor_id = (ushort)v;
732 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/class/net/%s/device/subsystem_device", name ) );
733 0 : if( !read_ulong( path, &v ) ) nic->pci_subsystem_device_id = (ushort)v;
734 :
735 0 : long l;
736 0 : nic->numa_node = ULONG_MAX;
737 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/class/net/%s/device/numa_node", name ) );
738 0 : if( !read_long( path, &l ) && l>=0L ) nic->numa_node = (ulong)l;
739 :
740 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/class/net/%s/mtu", name ) );
741 0 : read_ulong( path, &nic->mtu );
742 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/class/net/%s/speed", name ) );
743 0 : if( !read_long( path, &l ) && l>0L ) nic->link_speed_mbps = (ulong)l;
744 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/class/net/%s/carrier", name ) );
745 0 : nic->carrier = !read_ulong( path, &v ) && v==1UL;
746 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/class/net/%s/flags", name ) );
747 0 : if( !read_ulong( path, &v ) ) nic->promiscuous = !!( v&0x100UL ); /* IFF_PROMISC */
748 :
749 0 : struct ethtool_channels ch = { .cmd = ETHTOOL_GCHANNELS };
750 0 : if( !ethtool_ioc( fd, name, &ch ) ) {
751 0 : nic->channels_current = (ushort)fd_uint_min( ch.combined_count ? ch.combined_count : ch.rx_count, USHORT_MAX );
752 0 : nic->channels_max = (ushort)fd_uint_min( ch.max_combined ? ch.max_combined : ch.max_rx, USHORT_MAX );
753 0 : }
754 :
755 0 : struct ethtool_ringparam ring = { .cmd = ETHTOOL_GRINGPARAM };
756 0 : if( !ethtool_ioc( fd, name, &ring ) ) {
757 0 : nic->rx_ring_size = ring.rx_pending;
758 0 : nic->tx_ring_size = ring.tx_pending;
759 0 : nic->rx_ring_max = ring.rx_max_pending;
760 0 : nic->tx_ring_max = ring.tx_max_pending;
761 0 : }
762 :
763 : /* enabled kernel device features, under their kernel names */
764 0 : union { struct ethtool_sset_info hdr; uchar pad[ sizeof(struct ethtool_sset_info)+sizeof(uint) ]; } sset = {0};
765 0 : sset.hdr.cmd = ETHTOOL_GSSET_INFO;
766 0 : sset.hdr.sset_mask = 1UL<<ETH_SS_FEATURES;
767 0 : uint feat_cnt = 0U;
768 0 : if( !ethtool_ioc( fd, name, &sset ) && sset.hdr.sset_mask ) feat_cnt = fd_uint_min( sset.hdr.data[ 0 ], 128U );
769 0 : if( feat_cnt ) {
770 0 : union { struct ethtool_gstrings hdr; uchar pad[ sizeof(struct ethtool_gstrings)+128UL*ETH_GSTRING_LEN ]; } strs = {0};
771 0 : strs.hdr.cmd = ETHTOOL_GSTRINGS;
772 0 : strs.hdr.string_set = ETH_SS_FEATURES;
773 0 : strs.hdr.len = feat_cnt;
774 0 : union { struct ethtool_gfeatures hdr; uchar pad[ sizeof(struct ethtool_gfeatures)+4UL*sizeof(struct ethtool_get_features_block) ]; } feats = {0};
775 0 : feats.hdr.cmd = ETHTOOL_GFEATURES;
776 0 : feats.hdr.size = fd_uint_min( ( feat_cnt+31U )/32U, 4U );
777 0 : if( !ethtool_ioc( fd, name, &strs ) && !ethtool_ioc( fd, name, &feats ) ) {
778 0 : for( uint i=0U; i<feat_cnt && i/32U<feats.hdr.size && nic->offloads_cnt<64UL; i++ ) {
779 0 : if( !( feats.hdr.features[ i/32U ].active & (1U<<(i%32U)) ) ) continue;
780 0 : char fname[ ETH_GSTRING_LEN+1 ];
781 0 : fd_memcpy( fname, strs.hdr.data+i*ETH_GSTRING_LEN, ETH_GSTRING_LEN );
782 0 : fname[ ETH_GSTRING_LEN ] = '\0';
783 0 : if( !fname[ 0 ] ) continue;
784 0 : FD_TEST( fd_cstr_printf_check( nic->offloads[ nic->offloads_cnt++ ], sizeof(nic->offloads[ 0 ]), NULL, "%s", fname ) );
785 0 : }
786 0 : }
787 0 : }
788 :
789 0 : struct ethtool_rxnfc rxfh = { .cmd = ETHTOOL_GRXFH, .flow_type = UDP_V4_FLOW };
790 0 : if( !ethtool_ioc( fd, name, &rxfh ) )
791 0 : nic->rss_flow_hash_udp4 = ( rxfh.data&(RXH_L4_B_0_1|RXH_L4_B_2_3) )==(RXH_L4_B_0_1|RXH_L4_B_2_3);
792 :
793 0 : struct ethtool_coalesce coal = { .cmd = ETHTOOL_GCOALESCE };
794 0 : if( !ethtool_ioc( fd, name, &coal ) ) {
795 0 : nic->coalesce_rx_usecs = coal.rx_coalesce_usecs;
796 0 : nic->coalesce_adaptive_rx = !!coal.use_adaptive_rx_coalesce;
797 0 : }
798 :
799 0 : struct ethtool_rxfh rssh = { .cmd = ETHTOOL_GRSSH };
800 0 : if( !ethtool_ioc( fd, name, &rssh ) ) {
801 0 : if( rssh.hfunc&(1U<<0) ) FD_TEST( fd_cstr_printf_check( nic->rss_hash_func, sizeof(nic->rss_hash_func), NULL, "%s", "toeplitz" ) );
802 0 : else if( rssh.hfunc&(1U<<1) ) FD_TEST( fd_cstr_printf_check( nic->rss_hash_func, sizeof(nic->rss_hash_func), NULL, "%s", "xor" ) );
803 0 : else if( rssh.hfunc&(1U<<2) ) FD_TEST( fd_cstr_printf_check( nic->rss_hash_func, sizeof(nic->rss_hash_func), NULL, "%s", "crc32" ) );
804 0 : }
805 :
806 0 : struct ethtool_rxnfc cnt = { .cmd = ETHTOOL_GRXCLSRLCNT };
807 0 : if( !ethtool_ioc( fd, name, &cnt ) ) nic->ntuple_rule_count = cnt.rule_cnt;
808 0 : }
809 :
810 : static void
811 : collect_net( fd_boot_report_t * r,
812 0 : char const * iface ) {
813 0 : int fd = socket( AF_INET, SOCK_DGRAM|SOCK_CLOEXEC, 0 );
814 :
815 0 : DIR * dir = opendir( "/sys/class/net" );
816 0 : if( FD_LIKELY( dir ) ) {
817 0 : struct dirent * ent;
818 0 : while( (ent = readdir( dir )) ) {
819 0 : if( ent->d_name[ 0 ]=='.' ) continue;
820 0 : char path[ PATH_MAX ];
821 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/class/net/%s/device", ent->d_name ) );
822 0 : int physical = 0==access( path, F_OK );
823 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/class/net/%s/bonding", ent->d_name ) );
824 0 : int is_bond = 0==access( path, F_OK );
825 0 : if( !physical && !is_bond ) continue;
826 0 : if( fd>=0 ) collect_one_nic( r, fd, ent->d_name, is_bond );
827 0 : }
828 0 : closedir( dir );
829 0 : }
830 :
831 : /* bond membership of the bound interface, and its inventory index */
832 0 : r->bond_mode = 1; /* none */
833 0 : if( iface[ 0 ] ) {
834 0 : char path[ PATH_MAX ], buf[ 512 ];
835 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/class/net/%s/bonding/mode", iface ) );
836 0 : if( !read_cstr( path, buf, sizeof(buf) ) ) {
837 0 : if( !strncmp( buf, "active-backup", 13UL ) ) r->bond_mode = 2;
838 0 : else if( !strncmp( buf, "802.3ad", 7UL ) ) r->bond_mode = 3;
839 0 : else if( !strncmp( buf, "balance-xor", 11UL ) ) r->bond_mode = 4;
840 0 : else r->bond_mode = 5;
841 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/class/net/%s/bonding/slaves", iface ) );
842 0 : if( !read_cstr( path, buf, sizeof(buf) ) ) {
843 0 : char * saveptr = NULL;
844 0 : for( char * slave = strtok_r( buf, " ", &saveptr ); slave && r->nic_bond_slaves_cnt<16UL; slave = strtok_r( NULL, " ", &saveptr ) ) {
845 0 : FD_TEST( fd_cstr_printf_check( r->nic_bond_slaves[ r->nic_bond_slaves_cnt ], 17UL, NULL, "%s", slave ) );
846 0 : r->nic_bond_slaves_cnt++;
847 0 : }
848 0 : }
849 0 : }
850 0 : }
851 0 : r->bound_nic_idx = 255;
852 0 : for( ulong i=0UL; i<r->nic_devices_cnt; i++ )
853 0 : if( !strcmp( r->nic_devices[ i ].name, iface ) ) { r->bound_nic_idx = (uchar)i; break; }
854 :
855 0 : ulong v;
856 0 : if( !read_ulong( "/proc/sys/net/core/bpf_jit_enable", &v ) ) r->net_bpf_jit_enable = (uchar)fd_ulong_min( v, UCHAR_MAX );
857 :
858 0 : if( fd>=0 ) close( fd );
859 0 : }
860 :
861 : /* platform: dmi identity, chassis, iommu, hypervisor */
862 :
863 : static void
864 : collect_platform( fd_boot_report_t * r,
865 0 : char const * iface ) {
866 0 : read_cstr( "/sys/class/dmi/id/sys_vendor", r->dmi_sys_vendor, sizeof(r->dmi_sys_vendor) );
867 0 : read_cstr( "/sys/class/dmi/id/product_name", r->dmi_product_name, sizeof(r->dmi_product_name) );
868 0 : read_cstr( "/sys/class/dmi/id/bios_version", r->dmi_bios_version, sizeof(r->dmi_bios_version) );
869 0 : read_cstr( "/sys/class/dmi/id/bios_date", r->dmi_bios_date, sizeof(r->dmi_bios_date) );
870 0 : read_cstr( "/sys/class/dmi/id/board_vendor", r->dmi_board_vendor, sizeof(r->dmi_board_vendor) );
871 0 : read_cstr( "/sys/class/dmi/id/board_name", r->dmi_board_name, sizeof(r->dmi_board_name) );
872 :
873 0 : r->dmi_chassis_type = 1; /* unknown */
874 0 : ulong chassis;
875 0 : if( !read_ulong( "/sys/class/dmi/id/chassis_type", &chassis ) ) {
876 0 : switch( chassis ) { /* SMBIOS system enclosure types */
877 0 : case 3: case 4: case 13: case 15: r->dmi_chassis_type = 2; break; /* desktop */
878 0 : case 9: r->dmi_chassis_type = 3; break; /* laptop */
879 0 : case 10: case 14: r->dmi_chassis_type = 4; break; /* notebook */
880 0 : case 6: case 7: r->dmi_chassis_type = 5; break; /* tower */
881 0 : case 17: case 23: r->dmi_chassis_type = 6; break; /* rack_mount */
882 0 : case 28: r->dmi_chassis_type = 7; break; /* blade */
883 0 : case 29: r->dmi_chassis_type = 8; break; /* blade_enclosure */
884 0 : case 35: r->dmi_chassis_type = 9; break; /* mini_pc */
885 0 : case 36: r->dmi_chassis_type = 10; break; /* stick_pc */
886 0 : case 33: case 34: r->dmi_chassis_type = 11; break; /* embedded */
887 0 : case 24: r->dmi_chassis_type = 12; break; /* sealed_case_pc */
888 0 : case 31: r->dmi_chassis_type = 13; break; /* convertible */
889 0 : case 32: r->dmi_chassis_type = 14; break; /* detachable */
890 0 : default: break;
891 0 : }
892 0 : }
893 :
894 : /* iommu: no groups means off; the bound NIC's group default domain
895 : type distinguishes passthrough from translated */
896 0 : r->iommu_mode = 1; /* off */
897 0 : DIR * groups = opendir( "/sys/kernel/iommu_groups" );
898 0 : if( groups ) {
899 0 : struct dirent * ent;
900 0 : int have_group = 0;
901 0 : while( (ent = readdir( groups )) ) {
902 0 : if( ent->d_name[ 0 ]!='.' ) { have_group = 1; break; }
903 0 : }
904 0 : closedir( groups );
905 0 : if( have_group ) {
906 0 : r->iommu_mode = 4; /* unknown */
907 0 : char path[ PATH_MAX ], buf[ 64 ];
908 : /* a bonded interface is virtual and has no iommu group; fall
909 : back to the first member */
910 0 : for( int attempt=0; attempt<2 && r->iommu_mode==4; attempt++ ) {
911 0 : char const * nic = attempt ? ( r->nic_bond_slaves_cnt ? r->nic_bond_slaves[ 0 ] : "" ) : iface;
912 0 : if( !nic[ 0 ] ) continue;
913 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/class/net/%s/device/iommu_group/type", nic ) );
914 0 : if( read_cstr( path, buf, sizeof(buf) ) ) continue;
915 0 : if( !strcmp ( buf, "identity" ) ) r->iommu_mode = 2; /* passthrough */
916 0 : else if( !strncmp( buf, "DMA", 3UL ) ) r->iommu_mode = 3; /* translated */
917 0 : }
918 0 : }
919 0 : }
920 :
921 0 : r->hypervisor_vendor = 1; /* none */
922 0 : # if defined(__x86_64__)
923 0 : uint eax, ebx, ecx, edx;
924 0 : if( __get_cpuid( 1U, &eax, &ebx, &ecx, &edx ) && ( ecx&(1U<<31) ) ) {
925 0 : r->hypervisor_vendor = 13; /* unknown */
926 0 : __cpuid( 0x40000000U, eax, ebx, ecx, edx ); /* __get_cpuid rejects hypervisor leaves */
927 0 : char sig[ 13 ];
928 0 : fd_memcpy( sig, &ebx, 4UL );
929 0 : fd_memcpy( sig+4, &ecx, 4UL );
930 0 : fd_memcpy( sig+8, &edx, 4UL );
931 0 : sig[ 12 ] = '\0';
932 0 : if( !strncmp( sig, "KVMKVMKVM", 9UL ) ) {
933 0 : r->hypervisor_vendor = 2; /* kvm */
934 0 : if( strcasestr( r->dmi_sys_vendor, "amazon" ) ) r->hypervisor_vendor = 7; /* amazon_nitro */
935 0 : else if( strcasestr( r->dmi_product_name, "google" ) ) r->hypervisor_vendor = 8; /* kvm_gce */
936 0 : }
937 0 : else if( !strncmp( sig, "TCGTCGTCG", 9UL ) ) r->hypervisor_vendor = 3; /* qemu */
938 0 : else if( !strncmp( sig, "VMwareVMware", 12UL ) ) r->hypervisor_vendor = 4; /* vmware */
939 0 : else if( !strncmp( sig, "Microsoft Hv", 12UL ) ) r->hypervisor_vendor = 5; /* microsoft_hyperv */
940 0 : else if( !strncmp( sig, "XenVMMXenVMM", 12UL ) ) r->hypervisor_vendor = 6; /* xen */
941 0 : else if( !strncmp( sig, "bhyve bhyve", 11UL ) ) r->hypervisor_vendor = 9; /* bhyve */
942 0 : else if( strstr( sig, "prl hyperv" ) ) r->hypervisor_vendor = 10; /* parallels */
943 0 : else if( !strncmp( sig, "VBoxVBoxVBox", 12UL ) ) r->hypervisor_vendor = 11; /* virtualbox */
944 0 : if( r->hypervisor_vendor==13 && strcasestr( r->dmi_product_name, "bochs" ) ) r->hypervisor_vendor = 12;
945 0 : }
946 0 : # endif /* defined(__x86_64__) */
947 :
948 : /* virtualized hosts override the chassis form factor: a recognized
949 : cloud platform reports cloud, any other hypervisor virtual (bare
950 : metal providers leave no identifier on the machine) */
951 0 : if( r->hypervisor_vendor!=1 ) {
952 0 : static char const * const cloud_tbl[] = { "amazon", "google", "microsoft", "alibaba", "digitalocean", "vultr", "ovh", "hetzner", "oracle", "equinix", "latitude", "scaleway" };
953 0 : int cloud = 0;
954 0 : for( ulong i=0UL; i<sizeof(cloud_tbl)/sizeof(cloud_tbl[0]); i++ ) {
955 0 : if( strcasestr( r->dmi_sys_vendor, cloud_tbl[ i ] ) ||
956 0 : strcasestr( r->dmi_product_name, cloud_tbl[ i ] ) ||
957 0 : strcasestr( r->dmi_board_vendor, cloud_tbl[ i ] ) ) { cloud = 1; break; }
958 0 : }
959 0 : r->dmi_chassis_type = cloud ? 16 : 15; /* cloud : virtual */
960 0 : }
961 0 : }
962 :
963 : static void
964 : collect_container( fd_boot_report_t * r,
965 0 : char const * launch_cgroup ) {
966 0 : char cgroup[ 4096 ] = {0};
967 0 : read_file( "/proc/self/cgroup", cgroup, sizeof(cgroup), NULL );
968 :
969 0 : r->cgroup_swap_limit_bytes = ULONG_MAX;
970 :
971 0 : r->container_type = 1; /* none */
972 0 : char buf[ 128 ];
973 0 : if( !read_cstr( "/run/systemd/container", buf, sizeof(buf) ) ) {
974 0 : if( !strcmp( buf, "docker" ) ) r->container_type = 2;
975 0 : else if( !strcmp( buf, "podman" ) ) r->container_type = 3;
976 0 : else if( !strcmp( buf, "lxc" ) ) r->container_type = 6;
977 0 : else if( !strcmp( buf, "systemd-nspawn" ) ) r->container_type = 7;
978 0 : else r->container_type = 8; /* unknown runtime */
979 0 : }
980 0 : else if( 0==access( "/.dockerenv" , F_OK ) ) r->container_type = 2;
981 0 : else if( 0==access( "/run/.containerenv", F_OK ) ) r->container_type = 3;
982 0 : else if( strstr( cgroup, "cri-containerd" ) ) r->container_type = 4;
983 0 : else if( strstr( cgroup, "crio" ) ) r->container_type = 5;
984 0 : else if( strstr( cgroup, "docker" ) ) r->container_type = 2;
985 0 : else if( strstr( cgroup, "libpod" ) ) r->container_type = 3;
986 0 : else if( strstr( cgroup, "lxc" ) ) r->container_type = 6;
987 :
988 0 : r->kubernetes_pod = !!getenv( "KUBERNETES_SERVICE_HOST" ) || !!strstr( cgroup, "kubepods" );
989 :
990 : /* cgroup v2 limits of the launcher's cgroup (the event tile itself
991 : may sit in the cpuset isolation cgroup), the tightest over it and
992 : its ancestors up to the visible root, which holds limits when
993 : /sys/fs/cgroup is a namespaced view. Minima start at ULONG_MAX so
994 : a finite 0 is kept; the report reserves 0 for unlimited, so a
995 : finite limit of 0 is reported as 1. */
996 0 : if( launch_cgroup[ 0 ] ) {
997 0 : char cgpath[ PATH_MAX ];
998 0 : FD_TEST( fd_cstr_printf_check( cgpath, sizeof(cgpath), NULL, "%s", strcmp( launch_cgroup, "/" ) ? launch_cgroup : "" ) );
999 0 : ulong mem_max = ULONG_MAX, mem_high = ULONG_MAX, cpu_mc = ULONG_MAX;
1000 0 : for(;;) {
1001 0 : char path[ PATH_MAX+32 ], val[ 64 ];
1002 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/fs/cgroup%s/memory.max", cgpath ) );
1003 0 : if( !read_cstr( path, val, sizeof(val) ) && strcmp( val, "max" ) ) mem_max = fd_ulong_min( mem_max, strtoul( val, NULL, 10 ) );
1004 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/fs/cgroup%s/memory.high", cgpath ) );
1005 0 : if( !read_cstr( path, val, sizeof(val) ) && strcmp( val, "max" ) ) mem_high = fd_ulong_min( mem_high, strtoul( val, NULL, 10 ) );
1006 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/fs/cgroup%s/memory.swap.max", cgpath ) );
1007 0 : if( !read_cstr( path, val, sizeof(val) ) && strcmp( val, "max" ) ) r->cgroup_swap_limit_bytes = fd_ulong_min( r->cgroup_swap_limit_bytes, strtoul( val, NULL, 10 ) );
1008 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/fs/cgroup%s/cpu.max", cgpath ) );
1009 0 : ulong quota, period;
1010 0 : if( !read_cstr( path, val, sizeof(val) ) && 2==sscanf( val, "%lu %lu", "a, &period ) && period ) cpu_mc = fd_ulong_min( cpu_mc, quota*1000UL/period );
1011 0 : if( !cgpath[ 0 ] ) break; /* visited the root */
1012 0 : char * slash = strrchr( cgpath, '/' );
1013 0 : if( FD_UNLIKELY( !slash ) ) break;
1014 0 : *slash = '\0'; /* "/a" becomes "", the root */
1015 0 : }
1016 0 : if( mem_max !=ULONG_MAX ) r->cgroup_memory_limit_bytes = fd_ulong_max( mem_max, 1UL );
1017 0 : if( mem_high!=ULONG_MAX ) r->cgroup_memory_high_bytes = fd_ulong_max( mem_high, 1UL );
1018 0 : if( cpu_mc !=ULONG_MAX ) r->cgroup_cpu_quota_millicores = (uint)fd_ulong_min( fd_ulong_max( cpu_mc, 1UL ), UINT_MAX );
1019 0 : }
1020 :
1021 : /* cgroup v1: per-controller hierarchies, used by v1-only and hybrid
1022 : hosts; v2 wins where it already produced a value. v1 encodes
1023 : unlimited as a page-rounded LLONG_MAX and swap only as the
1024 : combined memory+swap limit */
1025 0 : for( char const * line=cgroup; *line; ) {
1026 0 : char const * nl = strchr( line, '\n' );
1027 0 : ulong llen = nl ? (ulong)( nl-line ) : strlen( line );
1028 0 : char const * c1 = memchr( line, ':', llen );
1029 0 : char const * c2 = c1 ? memchr( c1+1, ':', (ulong)( line+llen-(c1+1) ) ) : NULL;
1030 0 : if( c2 ) {
1031 0 : char ctl[ 64 ];
1032 0 : fd_cstr_printf( ctl, fd_ulong_min( (ulong)( c2-(c1+1) )+1UL, sizeof(ctl) ), NULL, "%s", c1+1 );
1033 0 : char cgpath[ PATH_MAX ];
1034 0 : fd_cstr_printf( cgpath, fd_ulong_min( (ulong)( line+llen-(c2+1) )+1UL, sizeof(cgpath) ), NULL, "%s", c2+1 );
1035 0 : int mem = 0, cpu = 0;
1036 0 : char * sp = NULL;
1037 0 : for( char * tok = strtok_r( ctl, ",", &sp ); tok; tok = strtok_r( NULL, ",", &sp ) ) {
1038 0 : mem |= !strcmp( tok, "memory" );
1039 0 : cpu |= !strcmp( tok, "cpu" );
1040 0 : }
1041 0 : char path[ PATH_MAX+64 ];
1042 0 : ulong l;
1043 0 : if( mem && !r->cgroup_memory_limit_bytes ) {
1044 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/fs/cgroup/memory%s/memory.limit_in_bytes", cgpath ) );
1045 0 : if( !read_ulong( path, &l ) && l<(1UL<<60) ) r->cgroup_memory_limit_bytes = l;
1046 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/fs/cgroup/memory%s/memory.memsw.limit_in_bytes", cgpath ) );
1047 0 : if( !read_ulong( path, &l ) && l<(1UL<<60) && r->cgroup_memory_limit_bytes ) r->cgroup_swap_limit_bytes = l-r->cgroup_memory_limit_bytes;
1048 0 : }
1049 0 : if( cpu && !r->cgroup_cpu_quota_millicores ) {
1050 0 : long quota;
1051 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/fs/cgroup/cpu%s/cpu.cfs_quota_us", cgpath ) );
1052 0 : if( !read_long( path, "a ) && quota>0L ) {
1053 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/fs/cgroup/cpu%s/cpu.cfs_period_us", cgpath ) );
1054 0 : if( !read_ulong( path, &l ) && l ) r->cgroup_cpu_quota_millicores = (uint)( (ulong)quota*1000UL/l );
1055 0 : }
1056 0 : }
1057 0 : }
1058 0 : if( !nl ) break;
1059 0 : line = nl+1;
1060 0 : }
1061 0 : }
1062 :
1063 : /* storage: block device inventory, nvme health, mdraid, and the
1064 : filesystems backing the validator's directories */
1065 :
1066 : static void
1067 0 : collect_block_devices( fd_boot_report_t * r ) {
1068 0 : DIR * dir = opendir( "/sys/block" );
1069 0 : if( FD_UNLIKELY( !dir ) ) return;
1070 0 : struct dirent * ent;
1071 0 : while( (ent = readdir( dir )) && r->block_devices_cnt<24UL ) {
1072 0 : char const * n = ent->d_name;
1073 0 : if( n[ 0 ]=='.' ) continue;
1074 0 : if( !strncmp( n, "loop", 4UL ) || !strncmp( n, "ram", 3UL ) || !strncmp( n, "zram", 4UL ) ||
1075 0 : !strncmp( n, "sr", 2UL ) || !strncmp( n, "fd", 2UL ) || !strncmp( n, "nbd", 3UL ) ||
1076 0 : !strncmp( n, "dm-", 3UL ) ) continue;
1077 :
1078 0 : char path[ PATH_MAX ], buf[ 256 ];
1079 0 : ulong v;
1080 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/block/%s/hidden", n ) );
1081 0 : if( !read_ulong( path, &v ) && v ) continue; /* an nvme multipath path disk, listed through its head */
1082 :
1083 0 : fd_boot_report_block_device_t * d = &r->block_devices[ r->block_devices_cnt++ ];
1084 0 : fd_memset( d, 0, sizeof(*d) );
1085 0 : fd_cstr_printf( d->name, sizeof(d->name), NULL, "%s", n );
1086 :
1087 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/block/%s/device/model", n ) );
1088 0 : if( !read_cstr( path, buf, sizeof(buf) ) ) fd_cstr_printf( d->model, sizeof(d->model), NULL, "%s", buf );
1089 :
1090 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/block/%s/size", n ) );
1091 0 : if( !read_ulong( path, &v ) ) d->capacity_bytes = v*512UL;
1092 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/block/%s/queue/rotational", n ) );
1093 0 : if( !read_ulong( path, &v ) ) d->rotational = !!v;
1094 :
1095 0 : if( !strncmp( n, "nvme", 4UL ) ) FD_TEST( fd_cstr_printf_check( d->transport, sizeof(d->transport), NULL, "%s", "nvme" ) );
1096 0 : else if( !strncmp( n, "vd", 2UL ) ) FD_TEST( fd_cstr_printf_check( d->transport, sizeof(d->transport), NULL, "%s", "virtio" ) );
1097 0 : else if( !strncmp( n, "xvd", 3UL ) ) FD_TEST( fd_cstr_printf_check( d->transport, sizeof(d->transport), NULL, "%s", "xen" ) );
1098 0 : else if( !strncmp( n, "mmcblk", 6UL ) ) FD_TEST( fd_cstr_printf_check( d->transport, sizeof(d->transport), NULL, "%s", "mmc" ) );
1099 0 : else if( !strncmp( n, "md", 2UL ) ) FD_TEST( fd_cstr_printf_check( d->transport, sizeof(d->transport), NULL, "%s", "md" ) );
1100 0 : else {
1101 0 : char target[ PATH_MAX ];
1102 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/block/%s", n ) );
1103 0 : long tn = readlink( path, target, sizeof(target)-1UL );
1104 0 : if( tn>0L ) target[ tn ] = '\0'; else target[ 0 ] = '\0';
1105 0 : if( strstr( target, "/usb" ) ) FD_TEST( fd_cstr_printf_check( d->transport, sizeof(d->transport), NULL, "%s", "usb" ) );
1106 0 : else if( strstr( target, "/ata" ) ) FD_TEST( fd_cstr_printf_check( d->transport, sizeof(d->transport), NULL, "%s", "sata" ) );
1107 0 : else FD_TEST( fd_cstr_printf_check( d->transport, sizeof(d->transport), NULL, "%s", "scsi" ) );
1108 0 : }
1109 :
1110 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/block/%s/queue/scheduler", n ) );
1111 0 : if( !read_cstr( path, buf, sizeof(buf) ) ) {
1112 0 : char * lb = strchr( buf, '[' );
1113 0 : char * rb = lb ? strchr( lb, ']' ) : NULL;
1114 0 : if( lb && rb ) { *rb = '\0'; fd_cstr_printf( d->scheduler, sizeof(d->scheduler), NULL, "%s", lb+1 ); }
1115 0 : else fd_cstr_printf( d->scheduler, sizeof(d->scheduler), NULL, "%s", buf );
1116 0 : }
1117 0 : }
1118 0 : closedir( dir );
1119 0 : }
1120 :
1121 : /* nvme get log page: local definitions to avoid kernel header drift */
1122 :
1123 : struct nvme_admin_cmd_ioc {
1124 : uchar opcode; uchar flags; ushort rsvd1;
1125 : uint nsid; uint cdw2; uint cdw3;
1126 : ulong metadata; ulong addr;
1127 : uint metadata_len; uint data_len;
1128 : uint cdw10; uint cdw11; uint cdw12; uint cdw13; uint cdw14; uint cdw15;
1129 : uint timeout_ms; uint result;
1130 : };
1131 :
1132 0 : #define NVME_IOCTL_ADMIN_CMD_ _IOWR( 'N', 0x41, struct nvme_admin_cmd_ioc )
1133 :
1134 : static void
1135 0 : collect_nvme( fd_boot_report_t * r ) {
1136 0 : for( ulong i=0UL; i<r->block_devices_cnt && r->nvme_devices_cnt<16UL; i++ ) {
1137 0 : char const * n = r->block_devices[ i ].name;
1138 0 : uint ctrl_num, ns_num;
1139 0 : if( 2!=sscanf( n, "nvme%un%u", &ctrl_num, &ns_num ) ) continue;
1140 :
1141 0 : fd_boot_report_nvme_t * d = &r->nvme_devices[ r->nvme_devices_cnt++ ];
1142 0 : fd_memset( d, 0, sizeof(*d) );
1143 0 : d->device_idx = (uchar)i;
1144 0 : d->namespace_size_bytes = r->block_devices[ i ].capacity_bytes;
1145 0 : d->numa_node = 255;
1146 :
1147 0 : char path[ PATH_MAX ], buf[ 256 ];
1148 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/block/%s/device/firmware_rev", n ) );
1149 0 : if( !read_cstr( path, buf, sizeof(buf) ) ) fd_cstr_printf( d->firmware_rev, sizeof(d->firmware_rev), NULL, "%s", buf );
1150 :
1151 : /* With nvme multipath the disk's device is the subsystem, not a
1152 : controller (and ctrl_num is the subsystem instance), so take the
1153 : first controller listed under the subsystem. */
1154 0 : char ctrl[ 64 ];
1155 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/block/%s/device", n ) );
1156 0 : readlink_base( path, ctrl, sizeof(ctrl) );
1157 0 : if( !strncmp( ctrl, "nvme-subsys", 11UL ) ) {
1158 0 : ctrl[ 0 ] = '\0';
1159 0 : DIR * sub = opendir( path );
1160 0 : struct dirent * e;
1161 0 : uint c;
1162 0 : char tail;
1163 0 : while( sub && (e = readdir( sub )) ) {
1164 0 : if( 1==sscanf( e->d_name, "nvme%u%c", &c, &tail ) ) {
1165 0 : FD_TEST( fd_cstr_printf_check( ctrl, sizeof(ctrl), NULL, "%s", e->d_name ) );
1166 0 : break;
1167 0 : }
1168 0 : }
1169 0 : if( sub ) closedir( sub );
1170 0 : } else if( !ctrl[ 0 ] ) {
1171 0 : FD_TEST( fd_cstr_printf_check( ctrl, sizeof(ctrl), NULL, "nvme%u", ctrl_num ) );
1172 0 : }
1173 0 : if( !ctrl[ 0 ] ) continue;
1174 :
1175 0 : long l;
1176 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/class/nvme/%s/device/numa_node", ctrl ) );
1177 0 : if( !read_long( path, &l ) && l>=0L && l<255L ) d->numa_node = (uchar)l;
1178 :
1179 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/dev/%s", ctrl ) );
1180 0 : int fd = open( path, O_RDONLY|O_CLOEXEC );
1181 0 : if( fd<0 ) continue;
1182 0 : uchar log[ 512 ] __attribute__((aligned(8))) = {0};
1183 0 : struct nvme_admin_cmd_ioc cmd = {
1184 0 : .opcode = 0x02, /* get log page */
1185 0 : .nsid = 0xFFFFFFFFU,
1186 0 : .addr = (ulong)log,
1187 0 : .data_len = 512U,
1188 0 : .cdw10 = 0x02U | ( ( 512U/4U-1U )<<16 ), /* smart / health, numdl */
1189 0 : };
1190 0 : if( 0==syscall( SYS_ioctl, fd, NVME_IOCTL_ADMIN_CMD_, &cmd ) ) {
1191 0 : d->has_health = 1;
1192 0 : d->critical_warning = log[ 0 ];
1193 0 : d->available_spare_pct = log[ 3 ];
1194 0 : d->percentage_used = log[ 5 ];
1195 0 : fd_memcpy( &d->media_errors, log+160, 8UL ); /* low 64 bits of le128 */
1196 0 : }
1197 0 : close( fd );
1198 0 : }
1199 0 : }
1200 :
1201 : static void
1202 0 : collect_mdraid( fd_boot_report_t * r ) {
1203 0 : for( ulong i=0UL; i<r->block_devices_cnt && r->mdraid_arrays_cnt<16UL; i++ ) {
1204 0 : char const * n = r->block_devices[ i ].name;
1205 0 : if( strncmp( n, "md", 2UL ) ) continue;
1206 0 : char path[ PATH_MAX ], buf[ 64 ];
1207 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/block/%s/md/level", n ) );
1208 0 : if( read_cstr( path, buf, sizeof(buf) ) ) continue;
1209 :
1210 0 : fd_boot_report_mdraid_t * d = &r->mdraid_arrays[ r->mdraid_arrays_cnt++ ];
1211 0 : fd_memset( d, 0, sizeof(*d) );
1212 0 : fd_cstr_printf( d->name, sizeof(d->name), NULL, "%s", n );
1213 0 : fd_cstr_printf( d->level, sizeof(d->level), NULL, "%s", buf );
1214 0 : ulong v;
1215 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/block/%s/md/raid_disks", n ) );
1216 0 : if( !read_ulong( path, &v ) ) d->device_count = (uchar)fd_ulong_min( v, UCHAR_MAX );
1217 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/block/%s/md/degraded", n ) );
1218 0 : if( !read_ulong( path, &v ) ) d->degraded = !!v;
1219 0 : }
1220 0 : }
1221 :
1222 : /* filesystems: resolve each configured directory to its mount via
1223 : longest-prefix match over mountinfo, dedupe mounts into the
1224 : filesystems array, and link each mount back to its disk */
1225 :
1226 : struct mount_ent {
1227 : char mount_point[ PATH_MAX ];
1228 : char fs_type[ 17 ];
1229 : char options[ 257 ];
1230 : uint dev_major;
1231 : uint dev_minor;
1232 : };
1233 :
1234 : static ulong
1235 : parse_mountinfo( struct mount_ent * mounts,
1236 : ulong max,
1237 0 : int * truncated ) {
1238 0 : static char buf[ 1048576 ];
1239 0 : ulong len;
1240 0 : *truncated = 0;
1241 0 : if( read_file( "/proc/self/mountinfo", buf, sizeof(buf), &len ) ) return 0UL;
1242 0 : if( len==sizeof(buf)-1UL ) *truncated = 1;
1243 0 : ulong cnt = 0UL;
1244 0 : char * saveptr = NULL;
1245 0 : char * line = strtok_r( buf, "\n", &saveptr );
1246 0 : for( ; line && cnt<max; line = strtok_r( NULL, "\n", &saveptr ) ) {
1247 0 : char * f[ 32 ]; ulong nf = 0UL;
1248 0 : char * sp = NULL;
1249 0 : for( char * tok = strtok_r( line, " ", &sp ); tok && nf<32UL; tok = strtok_r( NULL, " ", &sp ) ) f[ nf++ ] = tok;
1250 0 : if( nf<10UL ) continue;
1251 0 : ulong dash = 6UL;
1252 0 : while( dash<nf && strcmp( f[ dash ], "-" ) ) dash++;
1253 0 : if( dash+2UL>=nf ) continue;
1254 :
1255 0 : struct mount_ent * m = &mounts[ cnt++ ];
1256 0 : fd_memset( m, 0, sizeof(*m) );
1257 0 : sscanf( f[ 2 ], "%u:%u", &m->dev_major, &m->dev_minor );
1258 0 : fd_cstr_printf( m->mount_point, sizeof(m->mount_point), NULL, "%s", f[ 4 ] );
1259 : /* mountinfo octal-escapes space, tab, newline and backslash */
1260 0 : char * w = m->mount_point;
1261 0 : for( char const * q=m->mount_point; *q; q++ ) {
1262 0 : if( q[0]=='\\' && q[1]>='0' && q[1]<='3' && q[2]>='0' && q[2]<='7' && q[3]>='0' && q[3]<='7' ) {
1263 0 : *w++ = (char)( ((q[1]-'0')<<6) | ((q[2]-'0')<<3) | (q[3]-'0') );
1264 0 : q += 3;
1265 0 : } else *w++ = *q;
1266 0 : }
1267 0 : *w = '\0';
1268 0 : fd_cstr_printf( m->fs_type, sizeof(m->fs_type), NULL, "%s", f[ dash+1UL ] );
1269 : /* per-mount flags then superblock options; superblock option
1270 : values can embed paths, addresses and usernames (overlayfs
1271 : lowerdir, nfs addr, cifs username), so values are kept only for
1272 : allowlisted keys */
1273 0 : static char const * const opt_val_keys[] = {
1274 0 : "allocsize", "barrier", "commit", "compress", "compress-force", "data", "discard", "errors", "huge",
1275 0 : "logbsize", "logbufs", "mode", "nr_inodes", "size", "space_cache", "stripe", "subvolid", "sunit", "swidth",
1276 0 : };
1277 0 : char * out = m->options;
1278 0 : ulong rem = sizeof(m->options);
1279 0 : for( ulong s=0UL; s<2UL && nf>dash+3UL*s; s++ ) {
1280 0 : char * osp = NULL;
1281 0 : for( char * opt = strtok_r( s ? f[ dash+3UL ] : f[ 5 ], ",", &osp ); opt; opt = strtok_r( NULL, ",", &osp ) ) {
1282 0 : char const * eq = strchr( opt, '=' );
1283 0 : int redact = !!eq;
1284 0 : for( ulong k=0UL; eq && k<sizeof(opt_val_keys)/sizeof(opt_val_keys[0]); k++ ) {
1285 0 : if( (ulong)( eq-opt )==strlen( opt_val_keys[ k ] ) && !strncmp( opt, opt_val_keys[ k ], (ulong)( eq-opt ) ) ) redact = 0;
1286 0 : }
1287 0 : ulong n;
1288 0 : int fit = redact ? fd_cstr_printf_check( out, rem, &n, "%s%.*s=[redacted]", out==m->options ? "" : ",", (int)( eq-opt ), opt )
1289 0 : : fd_cstr_printf_check( out, rem, &n, "%s%s", out==m->options ? "" : ",", opt );
1290 0 : if( !fit ) { *out = '\0'; break; } /* capped at the schema length */
1291 0 : out += n; rem -= n;
1292 0 : }
1293 0 : }
1294 0 : }
1295 0 : if( line ) *truncated = 1; /* stopped at the entry cap */
1296 0 : return cnt;
1297 0 : }
1298 :
1299 : static long
1300 : mount_for_path( char const * path,
1301 : struct mount_ent const * mounts,
1302 0 : ulong mount_cnt ) {
1303 0 : long best = -1L;
1304 0 : ulong best_len = 0UL;
1305 0 : for( ulong i=0UL; i<mount_cnt; i++ ) {
1306 0 : char const * mp = mounts[ i ].mount_point;
1307 0 : ulong len = strlen( mp );
1308 0 : if( strncmp( path, mp, len ) ) continue;
1309 0 : if( len>1UL && path[ len ]!='\0' && path[ len ]!='/' ) continue;
1310 0 : if( len>=best_len ) { best = (long)i; best_len = len; }
1311 0 : }
1312 0 : return best;
1313 0 : }
1314 :
1315 : /* parent_disk resolves a block device name to its whole-disk name,
1316 : stripping a partition suffix via the sysfs hierarchy */
1317 :
1318 : static void
1319 : parent_disk( char const * dev,
1320 : char * out,
1321 0 : ulong out_sz ) {
1322 0 : char path[ PATH_MAX+32 ], resolved[ PATH_MAX ];
1323 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/class/block/%s", dev ) );
1324 0 : if( !realpath( path, resolved ) ) { fd_cstr_printf( out, out_sz, NULL, "%s", dev ); return; }
1325 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "%s/partition", resolved ) );
1326 0 : if( 0==access( path, F_OK ) ) {
1327 0 : char * base = strrchr( resolved, '/' );
1328 0 : if( base ) *base = '\0'; /* partition -> parent disk */
1329 0 : }
1330 0 : char const * name = strrchr( resolved, '/' );
1331 0 : fd_cstr_printf( out, out_sz, NULL, "%s", name ? name+1 : resolved );
1332 0 : }
1333 :
1334 : static uchar
1335 : device_idx_for_mount( fd_boot_report_t const * r,
1336 0 : struct mount_ent const * m ) {
1337 0 : char path[ PATH_MAX+32 ], resolved[ PATH_MAX ], name[ 72 ];
1338 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/dev/block/%u:%u", m->dev_major, m->dev_minor ) );
1339 0 : if( !realpath( path, resolved ) ) return 255;
1340 0 : char const * base = strrchr( resolved, '/' );
1341 0 : parent_disk( base ? base+1 : resolved, name, sizeof(name) );
1342 :
1343 : /* virtual devices not in the inventory (lvm volumes, partitioned
1344 : raid) descend through their first slave until a real device */
1345 0 : for( int depth=0; depth<4; depth++ ) {
1346 0 : for( ulong i=0UL; i<r->block_devices_cnt; i++ )
1347 0 : if( !strcmp( r->block_devices[ i ].name, name ) ) return (uchar)i;
1348 :
1349 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/class/block/%s/slaves", name ) );
1350 0 : DIR * dir = opendir( path );
1351 0 : if( !dir ) return 255;
1352 0 : char slave[ 72 ] = {0};
1353 0 : struct dirent * ent;
1354 0 : while( (ent = readdir( dir )) ) if( ent->d_name[ 0 ]!='.' ) { fd_cstr_printf( slave, sizeof(slave), NULL, "%s", ent->d_name ); break; }
1355 0 : closedir( dir );
1356 0 : if( !slave[ 0 ] ) return 255;
1357 0 : parent_disk( slave, name, sizeof(name) );
1358 0 : }
1359 0 : return 255;
1360 0 : }
1361 :
1362 : static long fs_mount_idx[ 8 ]; /* mountinfo index behind each filesystems entry */
1363 :
1364 : static uchar
1365 : fs_idx_for_path( fd_boot_report_t * r,
1366 : char const * path,
1367 : struct mount_ent const * mounts,
1368 0 : ulong mount_cnt ) {
1369 0 : if( !path[ 0 ] ) return 255;
1370 : /* canonicalize; a path not created yet attributes its deepest
1371 : existing ancestor, the same mount once it is */
1372 0 : char real[ PATH_MAX ], trunc[ PATH_MAX ];
1373 0 : FD_TEST( fd_cstr_printf_check( trunc, sizeof(trunc), NULL, "%s", path ) );
1374 0 : for(;;) {
1375 0 : if( realpath( trunc, real ) ) { path = real; break; }
1376 0 : char * slash = strrchr( trunc, '/' );
1377 0 : if( !slash ) break; /* relative and missing: unresolvable */
1378 0 : if( slash==trunc ) { if( trunc[ 1 ]=='\0' ) break; trunc[ 1 ] = '\0'; }
1379 0 : else *slash = '\0';
1380 0 : }
1381 0 : long mi = mount_for_path( path, mounts, mount_cnt );
1382 0 : if( mi<0L ) return 255;
1383 0 : struct mount_ent const * m = &mounts[ mi ];
1384 :
1385 0 : for( ulong i=0UL; i<r->filesystems_cnt; i++ ) if( fs_mount_idx[ i ]==mi ) return (uchar)i;
1386 0 : if( r->filesystems_cnt>=8UL ) return 255;
1387 0 : fs_mount_idx[ r->filesystems_cnt ] = mi;
1388 :
1389 0 : fd_boot_report_fs_t * fs = &r->filesystems[ r->filesystems_cnt ];
1390 0 : fd_memset( fs, 0, sizeof(*fs) );
1391 0 : fs->device_idx = device_idx_for_mount( r, m );
1392 0 : FD_TEST( fd_cstr_printf_check( fs->fs_type, sizeof(fs->fs_type), NULL, "%s", m->fs_type ) );
1393 0 : FD_TEST( fd_cstr_printf_check( fs->mount_options, sizeof(fs->mount_options), NULL, "%s", m->options ) );
1394 0 : struct statvfs sv;
1395 0 : if( 0==statvfs( m->mount_point, &sv ) ) {
1396 0 : fs->capacity_bytes = sv.f_blocks*sv.f_frsize;
1397 0 : fs->free_bytes = sv.f_bavail*sv.f_frsize;
1398 0 : }
1399 0 : return (uchar)r->filesystems_cnt++;
1400 0 : }
1401 :
1402 : static void
1403 : collect_filesystems( fd_boot_report_t * r,
1404 0 : fd_topo_tile_t const * tile ) {
1405 0 : static struct mount_ent mounts[ 512 ];
1406 0 : int truncated;
1407 0 : ulong mount_cnt = parse_mountinfo( mounts, 512UL, &truncated );
1408 : /* a mount past a truncated table could be the longest prefix; report
1409 : unresolved rather than a wrong ancestor */
1410 0 : if( FD_UNLIKELY( truncated ) ) mount_cnt = 0UL;
1411 :
1412 0 : r->accounts_fs_idx = fs_idx_for_path( r, tile->event.accounts_path, mounts, mount_cnt );
1413 0 : r->snapshots_fs_idx = fs_idx_for_path( r, tile->event.snapshots_path, mounts, mount_cnt );
1414 0 : r->log_fs_idx = fs_idx_for_path( r, tile->event.log_path, mounts, mount_cnt );
1415 0 : r->shredb_fs_idx = fs_idx_for_path( r, tile->event.shredb_path, mounts, mount_cnt );
1416 0 : r->guidb_fs_idx = fs_idx_for_path( r, tile->event.guidb_path, mounts, mount_cnt );
1417 0 : }
1418 :
1419 : void
1420 : fd_boot_report_collect( fd_boot_report_t * r,
1421 : fd_topo_t const * topo,
1422 0 : fd_topo_tile_t const * tile ) {
1423 0 : fd_memset( r, 0, sizeof(*r) );
1424 :
1425 : /* rendered by the app during topology construction */
1426 0 : if( FD_UNLIKELY( !topo->resolved_config_json_len ) )
1427 0 : FD_LOG_ERR(( "boot report config documents were not rendered into the topology" ));
1428 0 : FD_TEST( topo->resolved_config_json_len<sizeof(r->resolved_config_json) );
1429 0 : FD_TEST( topo->user_config_json_len <sizeof(r->user_config_json) );
1430 0 : fd_memcpy( r->resolved_config_json, topo->resolved_config_json, topo->resolved_config_json_len );
1431 0 : r->resolved_config_json_len = topo->resolved_config_json_len;
1432 0 : fd_memcpy( r->user_config_json, topo->user_config_json, topo->user_config_json_len );
1433 0 : r->user_config_json_len = topo->user_config_json_len;
1434 :
1435 0 : collect_os( r );
1436 0 : collect_cpu( r );
1437 0 : collect_mem( r );
1438 0 : collect_l3( r ); /* after collect_mem: sized by the numa cpu map */
1439 0 : collect_cores( r );
1440 0 : collect_affinity( r, tile );
1441 0 : collect_cpuset_partition( r, topo->app_name );
1442 :
1443 0 : static fd_topo_cpus_t cpus[1];
1444 0 : fd_topo_cpus_init( cpus );
1445 0 : FD_CPUSET_DECL( pool );
1446 0 : FD_CPUSET_DECL( cpuless );
1447 0 : fd_topo_cpus_floating( cpus, topo, (fd_cpuset_t const *)tile->event.launch_cpus, pool, cpuless );
1448 0 : r->float_pool = !!fd_cpuset_cnt( pool ); /* empty: the launchers pin floaters */
1449 0 : collect_dimms( r );
1450 0 : collect_net( r, tile->event.net_interface );
1451 0 : collect_platform( r, tile->event.net_interface );
1452 0 : collect_container( r, tile->event.launch_cgroup );
1453 0 : collect_block_devices( r );
1454 0 : collect_nvme( r );
1455 0 : collect_mdraid( r );
1456 0 : collect_filesystems( r, tile );
1457 : /* stamped at command start in the parent; the tile clone comes later */
1458 0 : r->process_start_time_nanos = (ulong)tile->event.boot_timestamp_nanos;
1459 0 : r->feature_set_id = FD_FEATURE_SET_ID;
1460 0 : }
1461 :
1462 : /* topology json: tiles, links and workspaces from the live topo */
1463 :
1464 : static void
1465 : render_topology_json( fd_boot_report_t * r,
1466 0 : fd_topo_t const * topo ) {
1467 0 : char * cur = r->topology_json;
1468 0 : ulong rem = sizeof(r->topology_json);
1469 :
1470 0 : #define PRINT( ... ) do { \
1471 0 : ulong _n; \
1472 0 : FD_TEST( fd_cstr_printf_check( cur, rem, &_n, __VA_ARGS__ ) ); \
1473 0 : cur += _n; rem -= _n; \
1474 0 : } while( 0 )
1475 :
1476 :
1477 0 : PRINT( "{\"tiles\":[" );
1478 0 : for( ulong i=0UL; i<topo->tile_cnt; i++ ) {
1479 0 : fd_topo_tile_t const * tile = &topo->tiles[ i ];
1480 0 : ulong cpu = tile->cpu_idx;
1481 0 : ulong core = cpu<r->cpu_to_core_cnt ? r->cpu_to_core[ cpu ] : USHORT_MAX;
1482 0 : long sibling = -1L;
1483 0 : for( ulong c=0UL; c<r->cpu_to_core_cnt && core!=USHORT_MAX && sibling<0L; c++ ) {
1484 0 : if( c!=cpu && r->cpu_to_core[ c ]==core ) sibling = (long)c;
1485 0 : }
1486 : /* another pinned tile runs concurrently on the same core */
1487 0 : int shared = 0;
1488 0 : for( ulong j=0UL; j<topo->tile_cnt && core!=USHORT_MAX && ( !tile->floats || !r->float_pool ); j++ ) {
1489 0 : fd_topo_tile_t const * other = &topo->tiles[ j ];
1490 0 : if( j==i || ( other->floats && r->float_pool ) || other->cpu_idx>=r->cpu_to_core_cnt || r->cpu_to_core[ other->cpu_idx ]!=core ) continue;
1491 0 : shared |= !fd_topob_cpu_overlap_allowed( tile, other, r->float_pool );
1492 0 : }
1493 0 : PRINT( "%s{\"kind\":\"%s\",\"kind_id\":%lu,\"cpu_idx\":%ld,\"floats\":%s,\"core_shared\":%s,\"sibling_cpu\":%ld,\"in\":[",
1494 0 : i ? "," : "", tile->name, tile->kind_id,
1495 0 : cpu<FD_TILE_MAX ? (long)cpu : -1L, tile->floats ? "true" : "false",
1496 0 : shared ? "true" : "false", sibling );
1497 0 : for( ulong in=0UL; in<tile->in_cnt; in++ ) PRINT( "%s%lu", in ? "," : "", tile->in_link_id[ in ] );
1498 0 : PRINT( "],\"out\":[" );
1499 0 : for( ulong out=0UL; out<tile->out_cnt; out++ ) PRINT( "%s%lu", out ? "," : "", tile->out_link_id[ out ] );
1500 0 : PRINT( "],\"event_link\":%ld,\"obj_cnt\":%lu}",
1501 0 : tile->event_link_id!=ULONG_MAX ? (long)tile->event_link_id : -1L, tile->uses_obj_cnt );
1502 0 : }
1503 0 : PRINT( "],\"links\":[" );
1504 0 : for( ulong i=0UL; i<topo->link_cnt; i++ ) {
1505 : /* referenced from the tile in/out lists by array position */
1506 0 : fd_topo_link_t const * link = &topo->links[ i ];
1507 0 : PRINT( "%s{\"kind\":\"%s\",\"kind_id\":%lu,\"depth\":%lu,\"mtu\":%lu,\"burst\":%lu}",
1508 0 : i ? "," : "", link->name, link->kind_id, link->depth, link->mtu, link->burst );
1509 0 : }
1510 0 : PRINT( "],\"workspaces\":[" );
1511 0 : for( ulong i=0UL; i<topo->wksp_cnt; i++ ) {
1512 0 : fd_topo_wksp_t const * wksp = &topo->workspaces[ i ];
1513 0 : PRINT( "%s{\"name\":\"%s\",\"numa_idx\":%lu,\"page_sz\":%lu,\"page_cnt\":%lu,\"footprint\":%lu,\"loose\":%lu}",
1514 0 : i ? "," : "", wksp->name, wksp->numa_idx, wksp->page_sz, wksp->page_cnt,
1515 0 : wksp->known_footprint, wksp->total_footprint-wksp->known_footprint );
1516 0 : }
1517 0 : PRINT( "]}" );
1518 :
1519 0 : #undef PRINT
1520 :
1521 0 : r->topology_json_len = (ulong)( cur-r->topology_json );
1522 0 : }
1523 :
1524 : /* protobuf encode: field numbers are 1-based positions in
1525 : schema/boot.json, tuple subfield numbers 1-based positions within
1526 : the tuple, enum values 1-based variant positions */
1527 :
1528 : static int
1529 : push_cstr( fd_pb_encoder_t * e,
1530 : uint id,
1531 0 : char const * s ) {
1532 0 : ulong len = strlen( s );
1533 0 : if( !len ) return 1;
1534 0 : char clean[ 512 ];
1535 0 : FD_TEST( len<sizeof(clean) );
1536 0 : for( ulong i=0UL; i<len; i++ ) clean[ i ] = ( s[ i ]<0x20 || s[ i ]>0x7e ) ? '?' : s[ i ];
1537 0 : return !!fd_pb_push_bytes( e, id, clean, len );
1538 0 : }
1539 :
1540 : void
1541 : fd_boot_report_publish( fd_boot_report_t * r,
1542 : fd_topo_t const * topo,
1543 : fd_circq_t * circq,
1544 0 : fd_event_client_t * client ) {
1545 0 : render_topology_json( r, topo );
1546 :
1547 0 : r->tile_count = (ushort)fd_ulong_min( topo->tile_cnt, USHORT_MAX );
1548 0 : ulong stack_bytes = topo->tile_cnt*FD_SHMEM_HUGE_PAGE_SZ*( (FD_TILE_PRIVATE_STACK_SZ/FD_SHMEM_HUGE_PAGE_SZ)+2UL );
1549 0 : ulong normal_bytes = fd_topo_normal_page_cnt( topo )*FD_SHMEM_NORMAL_PAGE_SZ;
1550 0 : r->memory_total = fd_topo_mlock( topo )+stack_bytes+normal_bytes;
1551 0 : for( ulong i=0UL; i<fd_shmem_numa_cnt(); i++ ) {
1552 0 : r->memory_gigantic_pages += fd_topo_gigantic_page_cnt( topo, i );
1553 0 : r->memory_huge_pages += fd_topo_huge_page_cnt( topo, i, 0 );
1554 0 : }
1555 0 : r->memory_normal_pages = fd_topo_normal_page_cnt( topo );
1556 :
1557 0 : uchar * buffer = fd_circq_push_back( circq, 1UL, FD_EVENT_BOOT_BUF_MAX );
1558 0 : FD_TEST( buffer );
1559 :
1560 0 : ulong event_id = fd_event_client_id_reserve( client );
1561 :
1562 0 : fd_pb_encoder_t encoder[1];
1563 0 : fd_pb_encoder_init( encoder, buffer, FD_EVENT_BOOT_BUF_MAX );
1564 :
1565 0 : int ok = 1;
1566 :
1567 0 : FD_TEST( circq->cursor_push_seq );
1568 0 : ok &= !!fd_pb_push_uint64( encoder, 1U, circq->cursor_push_seq-1UL );
1569 0 : ok &= !!fd_pb_push_uint64( encoder, 2U, event_id );
1570 0 : ok &= !!fd_pb_push_uint64( encoder, 3U, 0UL ); /* link_seq */
1571 0 : ok &= !!fd_pb_push_uint64( encoder, 4U, (ulong)fd_log_wallclock() );
1572 :
1573 0 : ok &= !!fd_pb_submsg_open( encoder, 5U ); /* Event */
1574 0 : ok &= !!fd_pb_submsg_open( encoder, 10U ); /* Boot */
1575 :
1576 0 : ok &= push_cstr( encoder, 1U, r->kernel_release );
1577 0 : ok &= push_cstr( encoder, 2U, r->kernel_version );
1578 0 : for( ulong i=0UL; i<r->mitigations_cnt; i++ ) ok &= push_cstr( encoder, 3U, r->mitigations[ i ] );
1579 0 : ok &= push_cstr( encoder, 4U, r->distro_id );
1580 0 : ok &= push_cstr( encoder, 5U, r->distro_version_id );
1581 0 : ok &= push_cstr( encoder, 6U, r->distro_id_like );
1582 0 : if( r->libc_kind ) ok &= !!fd_pb_push_int32( encoder, 7U, r->libc_kind );
1583 0 : ok &= push_cstr( encoder, 8U, r->libc_version );
1584 0 : if( r->build_machine_target ) ok &= !!fd_pb_push_int32( encoder, 9U, r->build_machine_target );
1585 0 : ok &= push_cstr( encoder, 10U, r->build_extras );
1586 0 : if( r->build_git_dirty ) ok &= !!fd_pb_push_bool( encoder, 11U, r->build_git_dirty );
1587 0 : if( r->compiler ) ok &= !!fd_pb_push_int32( encoder, 12U, r->compiler );
1588 0 : ok &= push_cstr( encoder, 13U, r->compiler_version );
1589 0 : if( r->build_timestamp_nanos ) ok &= !!fd_pb_push_uint64( encoder, 14U, r->build_timestamp_nanos );
1590 0 : for( ulong i=0UL; i<r->build_features_cnt; i++ ) ok &= push_cstr( encoder, 15U, r->build_features[ i ] );
1591 :
1592 0 : if( r->cpu_arch ) ok &= !!fd_pb_push_int32( encoder, 16U, r->cpu_arch );
1593 0 : if( r->cpu_vendor ) ok &= !!fd_pb_push_int32( encoder, 17U, r->cpu_vendor );
1594 0 : ok &= push_cstr( encoder, 18U, r->cpu_model_name );
1595 0 : if( r->cpu_family ) ok &= !!fd_pb_push_uint32( encoder, 19U, r->cpu_family );
1596 0 : if( r->cpu_model_id ) ok &= !!fd_pb_push_uint32( encoder, 20U, r->cpu_model_id );
1597 0 : if( r->cpu_stepping ) ok &= !!fd_pb_push_uint32( encoder, 21U, r->cpu_stepping );
1598 0 : if( r->cpu_microcode_version ) ok &= !!fd_pb_push_uint64( encoder, 22U, r->cpu_microcode_version );
1599 0 : if( r->cpu_logical_count ) ok &= !!fd_pb_push_uint32( encoder, 23U, r->cpu_logical_count );
1600 0 : if( r->cpu_physical_core_count ) ok &= !!fd_pb_push_uint32( encoder, 24U, r->cpu_physical_core_count );
1601 0 : if( r->cpu_socket_count ) ok &= !!fd_pb_push_uint32( encoder, 25U, r->cpu_socket_count );
1602 0 : if( r->smt_active ) ok &= !!fd_pb_push_bool( encoder, 26U, 1 );
1603 0 : if( r->cpufreq_governor ) ok &= !!fd_pb_push_int32( encoder, 27U, r->cpufreq_governor );
1604 0 : if( r->cpufreq_driver ) ok &= !!fd_pb_push_int32( encoder, 28U, r->cpufreq_driver );
1605 0 : if( r->cpu_energy_perf_preference ) ok &= !!fd_pb_push_int32( encoder, 29U, r->cpu_energy_perf_preference );
1606 0 : if( r->cpu_idle_driver ) ok &= !!fd_pb_push_int32( encoder, 30U, r->cpu_idle_driver );
1607 0 : ok &= push_cstr( encoder, 31U, r->cpu_max_enabled_cstate );
1608 :
1609 0 : if( r->host_memory_bytes ) ok &= !!fd_pb_push_uint64( encoder, 32U, r->host_memory_bytes );
1610 0 : if( r->host_memory_available_bytes ) ok &= !!fd_pb_push_uint64( encoder, 33U, r->host_memory_available_bytes );
1611 0 : for( ulong i=0UL; i<r->numa_cpu_to_node_cnt; i++ ) ok &= !!fd_pb_push_uint32( encoder, 34U, r->numa_cpu_to_node[ i ] );
1612 0 : for( ulong i=0UL; i<r->isolated_cpus_cnt; i++ ) ok &= !!fd_pb_push_uint32( encoder, 35U, r->isolated_cpus[ i ] );
1613 :
1614 0 : for( ulong i=0UL; i<r->dmi_dimms_cnt; i++ ) {
1615 0 : fd_boot_report_dimm_t const * d = &r->dmi_dimms[ i ];
1616 0 : ok &= !!fd_pb_submsg_open( encoder, 36U );
1617 0 : if( d->size_bytes ) ok &= !!fd_pb_push_uint64( encoder, 1U, d->size_bytes );
1618 0 : ok &= push_cstr( encoder, 2U, d->mem_type );
1619 0 : if( d->speed_mts ) ok &= !!fd_pb_push_uint64( encoder, 3U, d->speed_mts );
1620 0 : if( d->rated_speed_mts ) ok &= !!fd_pb_push_uint64( encoder, 4U, d->rated_speed_mts );
1621 0 : ok &= push_cstr( encoder, 5U, d->manufacturer );
1622 0 : ok &= push_cstr( encoder, 6U, d->part_number );
1623 0 : ok &= !!fd_pb_submsg_close( encoder );
1624 0 : }
1625 :
1626 0 : for( ulong i=0UL; i<r->nic_devices_cnt; i++ ) {
1627 0 : fd_boot_report_nic_t const * n = &r->nic_devices[ i ];
1628 0 : ok &= !!fd_pb_submsg_open( encoder, 37U );
1629 0 : ok &= push_cstr( encoder, 1U, n->name );
1630 0 : ok &= push_cstr( encoder, 2U, n->driver );
1631 0 : ok &= push_cstr( encoder, 3U, n->driver_version );
1632 0 : ok &= push_cstr( encoder, 4U, n->firmware_version );
1633 0 : if( n->pcie_current_link_width ) ok &= !!fd_pb_push_uint32( encoder, 5U, n->pcie_current_link_width );
1634 0 : if( n->pcie_max_link_width ) ok &= !!fd_pb_push_uint32( encoder, 6U, n->pcie_max_link_width );
1635 0 : ok &= push_cstr( encoder, 7U, n->pcie_current_link_speed );
1636 0 : ok &= push_cstr( encoder, 8U, n->pcie_max_link_speed );
1637 0 : ok &= push_cstr( encoder, 9U, n->bus_info );
1638 0 : if( n->pci_device_id ) ok &= !!fd_pb_push_uint32( encoder, 10U, n->pci_device_id );
1639 0 : if( n->pci_vendor_id ) ok &= !!fd_pb_push_uint32( encoder, 11U, n->pci_vendor_id );
1640 0 : if( n->pci_subsystem_vendor_id ) ok &= !!fd_pb_push_uint32( encoder, 12U, n->pci_subsystem_vendor_id );
1641 0 : if( n->pci_subsystem_device_id ) ok &= !!fd_pb_push_uint32( encoder, 13U, n->pci_subsystem_device_id );
1642 0 : if( n->numa_node ) ok &= !!fd_pb_push_uint64( encoder, 14U, n->numa_node );
1643 0 : if( n->mtu ) ok &= !!fd_pb_push_uint64( encoder, 15U, n->mtu );
1644 0 : if( n->link_speed_mbps ) ok &= !!fd_pb_push_uint64( encoder, 16U, n->link_speed_mbps );
1645 0 : if( n->carrier ) ok &= !!fd_pb_push_bool( encoder, 17U, 1 );
1646 0 : if( n->promiscuous ) ok &= !!fd_pb_push_bool( encoder, 18U, 1 );
1647 0 : if( n->channels_current ) ok &= !!fd_pb_push_uint32( encoder, 19U, n->channels_current );
1648 0 : if( n->channels_max ) ok &= !!fd_pb_push_uint32( encoder, 20U, n->channels_max );
1649 0 : if( n->rx_ring_size ) ok &= !!fd_pb_push_uint64( encoder, 21U, n->rx_ring_size );
1650 0 : if( n->tx_ring_size ) ok &= !!fd_pb_push_uint64( encoder, 22U, n->tx_ring_size );
1651 0 : if( n->rx_ring_max ) ok &= !!fd_pb_push_uint64( encoder, 23U, n->rx_ring_max );
1652 0 : if( n->tx_ring_max ) ok &= !!fd_pb_push_uint64( encoder, 24U, n->tx_ring_max );
1653 0 : for( ulong k=0UL; k<n->offloads_cnt; k++ ) ok &= push_cstr( encoder, 25U, n->offloads[ k ] );
1654 0 : if( n->rss_flow_hash_udp4 ) ok &= !!fd_pb_push_bool( encoder, 26U, 1 );
1655 0 : if( n->coalesce_rx_usecs ) ok &= !!fd_pb_push_uint64( encoder, 27U, n->coalesce_rx_usecs );
1656 0 : if( n->coalesce_adaptive_rx ) ok &= !!fd_pb_push_bool( encoder, 28U, 1 );
1657 0 : ok &= push_cstr( encoder, 29U, n->rss_hash_func );
1658 0 : if( n->ntuple_rule_count ) ok &= !!fd_pb_push_uint64( encoder, 30U, n->ntuple_rule_count );
1659 0 : if( n->is_bond_master ) ok &= !!fd_pb_push_bool( encoder, 31U, 1 );
1660 0 : ok &= !!fd_pb_submsg_close( encoder );
1661 0 : }
1662 :
1663 0 : for( ulong i=0UL; i<r->nic_bond_slaves_cnt; i++ ) ok &= push_cstr( encoder, 38U, r->nic_bond_slaves[ i ] );
1664 0 : if( r->bond_mode ) ok &= !!fd_pb_push_int32( encoder, 39U, r->bond_mode );
1665 0 : if( r->bound_nic_idx ) ok &= !!fd_pb_push_uint32( encoder, 40U, r->bound_nic_idx );
1666 0 : if( r->net_bpf_jit_enable ) ok &= !!fd_pb_push_uint32( encoder, 41U, r->net_bpf_jit_enable );
1667 :
1668 0 : ok &= push_cstr( encoder, 42U, r->dmi_sys_vendor );
1669 0 : ok &= push_cstr( encoder, 43U, r->dmi_product_name );
1670 0 : ok &= push_cstr( encoder, 44U, r->dmi_bios_version );
1671 0 : ok &= push_cstr( encoder, 45U, r->dmi_bios_date );
1672 0 : ok &= push_cstr( encoder, 46U, r->dmi_board_vendor );
1673 0 : ok &= push_cstr( encoder, 47U, r->dmi_board_name );
1674 0 : if( r->dmi_chassis_type ) ok &= !!fd_pb_push_int32( encoder, 48U, r->dmi_chassis_type );
1675 0 : if( r->iommu_mode ) ok &= !!fd_pb_push_int32( encoder, 49U, r->iommu_mode );
1676 0 : if( r->hypervisor_vendor ) ok &= !!fd_pb_push_int32( encoder, 50U, r->hypervisor_vendor );
1677 0 : if( r->container_type ) ok &= !!fd_pb_push_int32( encoder, 51U, r->container_type );
1678 0 : if( r->kubernetes_pod ) ok &= !!fd_pb_push_bool( encoder, 52U, 1 );
1679 0 : if( r->cgroup_memory_limit_bytes ) ok &= !!fd_pb_push_uint64( encoder, 53U, r->cgroup_memory_limit_bytes );
1680 0 : if( r->cgroup_memory_high_bytes ) ok &= !!fd_pb_push_uint64( encoder, 54U, r->cgroup_memory_high_bytes );
1681 0 : if( r->cgroup_swap_limit_bytes ) ok &= !!fd_pb_push_uint64( encoder, 55U, r->cgroup_swap_limit_bytes );
1682 0 : if( r->cgroup_cpu_quota_millicores ) ok &= !!fd_pb_push_uint32( encoder, 56U, r->cgroup_cpu_quota_millicores );
1683 :
1684 0 : for( ulong i=0UL; i<r->block_devices_cnt; i++ ) {
1685 0 : fd_boot_report_block_device_t const * d = &r->block_devices[ i ];
1686 0 : ok &= !!fd_pb_submsg_open( encoder, 57U );
1687 0 : ok &= push_cstr( encoder, 1U, d->name );
1688 0 : ok &= push_cstr( encoder, 2U, d->model );
1689 0 : if( d->capacity_bytes ) ok &= !!fd_pb_push_uint64( encoder, 3U, d->capacity_bytes );
1690 0 : if( d->rotational ) ok &= !!fd_pb_push_bool( encoder, 4U, 1 );
1691 0 : ok &= push_cstr( encoder, 5U, d->transport );
1692 0 : ok &= push_cstr( encoder, 6U, d->scheduler );
1693 0 : ok &= !!fd_pb_submsg_close( encoder );
1694 0 : }
1695 :
1696 0 : for( ulong i=0UL; i<r->nvme_devices_cnt; i++ ) {
1697 0 : fd_boot_report_nvme_t const * d = &r->nvme_devices[ i ];
1698 0 : ok &= !!fd_pb_submsg_open( encoder, 58U );
1699 0 : if( d->device_idx ) ok &= !!fd_pb_push_uint32( encoder, 1U, d->device_idx );
1700 0 : ok &= push_cstr( encoder, 2U, d->firmware_rev );
1701 0 : if( d->critical_warning ) ok &= !!fd_pb_push_uint32( encoder, 3U, d->critical_warning );
1702 0 : if( d->available_spare_pct ) ok &= !!fd_pb_push_uint32( encoder, 4U, d->available_spare_pct );
1703 0 : if( d->percentage_used ) ok &= !!fd_pb_push_uint32( encoder, 5U, d->percentage_used );
1704 0 : if( d->media_errors ) ok &= !!fd_pb_push_uint64( encoder, 6U, d->media_errors );
1705 0 : if( d->numa_node ) ok &= !!fd_pb_push_uint32( encoder, 7U, d->numa_node );
1706 0 : if( d->namespace_size_bytes ) ok &= !!fd_pb_push_uint64( encoder, 8U, d->namespace_size_bytes );
1707 0 : if( d->has_health ) ok &= !!fd_pb_push_bool( encoder, 9U, 1 );
1708 0 : ok &= !!fd_pb_submsg_close( encoder );
1709 0 : }
1710 :
1711 0 : for( ulong i=0UL; i<r->mdraid_arrays_cnt; i++ ) {
1712 0 : fd_boot_report_mdraid_t const * d = &r->mdraid_arrays[ i ];
1713 0 : ok &= !!fd_pb_submsg_open( encoder, 59U );
1714 0 : ok &= push_cstr( encoder, 1U, d->name );
1715 0 : ok &= push_cstr( encoder, 2U, d->level );
1716 0 : if( d->device_count ) ok &= !!fd_pb_push_uint32( encoder, 3U, d->device_count );
1717 0 : if( d->degraded ) ok &= !!fd_pb_push_bool( encoder, 4U, 1 );
1718 0 : ok &= !!fd_pb_submsg_close( encoder );
1719 0 : }
1720 :
1721 0 : for( ulong i=0UL; i<r->filesystems_cnt; i++ ) {
1722 0 : fd_boot_report_fs_t const * f = &r->filesystems[ i ];
1723 0 : ok &= !!fd_pb_submsg_open( encoder, 60U );
1724 0 : if( f->device_idx ) ok &= !!fd_pb_push_uint32( encoder, 1U, f->device_idx );
1725 0 : ok &= push_cstr( encoder, 2U, f->fs_type );
1726 0 : ok &= push_cstr( encoder, 3U, f->mount_options );
1727 0 : if( f->capacity_bytes ) ok &= !!fd_pb_push_uint64( encoder, 4U, f->capacity_bytes );
1728 0 : if( f->free_bytes ) ok &= !!fd_pb_push_uint64( encoder, 5U, f->free_bytes );
1729 0 : ok &= !!fd_pb_submsg_close( encoder );
1730 0 : }
1731 :
1732 0 : if( r->accounts_fs_idx ) ok &= !!fd_pb_push_uint32( encoder, 61U, r->accounts_fs_idx );
1733 0 : if( r->snapshots_fs_idx ) ok &= !!fd_pb_push_uint32( encoder, 62U, r->snapshots_fs_idx );
1734 0 : if( r->log_fs_idx ) ok &= !!fd_pb_push_uint32( encoder, 63U, r->log_fs_idx );
1735 0 : if( r->shredb_fs_idx ) ok &= !!fd_pb_push_uint32( encoder, 64U, r->shredb_fs_idx );
1736 0 : if( r->guidb_fs_idx ) ok &= !!fd_pb_push_uint32( encoder, 65U, r->guidb_fs_idx );
1737 :
1738 0 : if( r->resolved_config_json_len ) ok &= !!fd_pb_push_bytes( encoder, 66U, r->resolved_config_json, r->resolved_config_json_len );
1739 0 : if( r->user_config_json_len ) ok &= !!fd_pb_push_bytes( encoder, 67U, r->user_config_json, r->user_config_json_len );
1740 0 : if( r->topology_json_len ) ok &= !!fd_pb_push_bytes( encoder, 68U, r->topology_json, r->topology_json_len );
1741 0 : if( r->tile_count ) ok &= !!fd_pb_push_uint32( encoder, 69U, r->tile_count );
1742 0 : if( r->memory_total ) ok &= !!fd_pb_push_uint64( encoder, 70U, r->memory_total );
1743 0 : if( r->memory_gigantic_pages ) ok &= !!fd_pb_push_uint64( encoder, 71U, r->memory_gigantic_pages );
1744 0 : if( r->memory_huge_pages ) ok &= !!fd_pb_push_uint64( encoder, 72U, r->memory_huge_pages );
1745 0 : if( r->memory_normal_pages ) ok &= !!fd_pb_push_uint64( encoder, 73U, r->memory_normal_pages );
1746 0 : if( r->process_start_time_nanos ) ok &= !!fd_pb_push_uint64( encoder, 74U, r->process_start_time_nanos );
1747 0 : if( r->feature_set_id ) ok &= !!fd_pb_push_uint32( encoder, 75U, r->feature_set_id );
1748 0 : for( ulong i=0UL; i<r->l3_cpu_to_domain_cnt; i++ ) ok &= !!fd_pb_push_uint32( encoder, 76U, r->l3_cpu_to_domain[ i ] );
1749 0 : for( ulong i=0UL; i<r->cpu_features_cnt; i++ ) ok &= push_cstr( encoder, 77U, r->cpu_features[ i ] );
1750 0 : for( ulong i=0UL; i<r->cpu_to_core_cnt; i++ ) ok &= !!fd_pb_push_uint32( encoder, 78U, r->cpu_to_core[ i ] );
1751 0 : for( ulong i=0UL; i<r->launch_cpus_cnt; i++ ) ok &= !!fd_pb_push_uint32( encoder, 79U, r->launch_cpus[ i ] );
1752 0 : if( r->cpuset_partition ) ok &= !!fd_pb_push_int32( encoder, 80U, r->cpuset_partition );
1753 0 : for( ulong i=0UL; i<r->cpuset_partition_cpus_cnt; i++ ) ok &= !!fd_pb_push_uint32( encoder, 81U, r->cpuset_partition_cpus[ i ] );
1754 :
1755 0 : ok &= !!fd_pb_submsg_close( encoder );
1756 0 : ok &= !!fd_pb_submsg_close( encoder );
1757 0 : FD_TEST( ok );
1758 0 : fd_circq_resize_back( circq, fd_pb_encoder_out_sz( encoder ) );
1759 0 : }
|