1mod config;
66mod cpu;
67mod io;
68mod kernel;
69
70pub use config::{PerformanceTier, RecommendedConfig};
71pub use cpu::{
72 cache_line_size, is_smt_enabled, logical_cpu_count, physical_cpu_count, CpuFeatures, SimdLevel,
73};
74pub use io::{IoUringCapabilities, MemoryInfo, StorageInfo, StorageType, XdpCapabilities};
75pub use kernel::KernelVersion;
76
77use crate::numa::NumaTopology;
78use std::path::Path;
79use std::sync::OnceLock;
80
81#[derive(Debug, Clone)]
88pub struct SystemCapabilities {
89 pub platform: Platform,
92 pub kernel_version: Option<KernelVersion>,
94
95 pub cpu_count: usize,
98 pub physical_cores: usize,
100 pub numa_nodes: usize,
102 pub cache_line_size: usize,
104 pub cpu_features: CpuFeatures,
106
107 pub io_uring: IoUringCapabilities,
110 pub xdp: XdpCapabilities,
112 pub storage: StorageInfo,
114
115 pub memory: MemoryInfo,
118}
119
120static CACHED_CAPABILITIES: OnceLock<SystemCapabilities> = OnceLock::new();
122
123impl SystemCapabilities {
124 #[must_use]
129 pub fn detect() -> &'static Self {
130 CACHED_CAPABILITIES.get_or_init(Self::detect_uncached)
131 }
132
133 #[must_use]
137 pub fn detect_uncached() -> Self {
138 let kernel_version = KernelVersion::detect();
139 let cpu_features = CpuFeatures::detect();
140 let numa_topology = NumaTopology::detect();
141
142 Self {
143 platform: Platform::detect(),
144 kernel_version,
145
146 cpu_count: logical_cpu_count(),
147 physical_cores: physical_cpu_count(),
148 numa_nodes: numa_topology.num_nodes(),
149 cache_line_size: cache_line_size(),
150 cpu_features,
151
152 io_uring: IoUringCapabilities::from_kernel_version(kernel_version.as_ref()),
153 xdp: XdpCapabilities::from_kernel_version(kernel_version.as_ref()),
154 storage: StorageInfo::default(), memory: MemoryInfo::detect(),
157 }
158 }
159
160 #[must_use]
162 pub fn detect_with_storage<P: AsRef<Path>>(data_path: P) -> Self {
163 let mut caps = Self::detect_uncached();
164 caps.storage = StorageInfo::detect(data_path);
165 caps
166 }
167
168 pub fn update_storage<P: AsRef<Path>>(&mut self, data_path: P) {
170 self.storage = StorageInfo::detect(data_path);
171 }
172
173 #[must_use]
175 pub fn recommended_config(&self) -> RecommendedConfig {
176 RecommendedConfig::from_capabilities(self)
177 }
178
179 #[must_use]
181 pub fn summary(&self) -> String {
182 use std::fmt::Write;
183
184 let mut s = String::new();
185
186 let _ = writeln!(s, "System Capabilities:");
187 let _ = writeln!(s, " Platform: {}", self.platform);
188 if let Some(ref kv) = self.kernel_version {
189 let _ = writeln!(s, " Kernel: {kv}");
190 }
191
192 let _ = writeln!(s);
193 let _ = writeln!(s, "CPU:");
194 let _ = writeln!(s, " Logical CPUs: {}", self.cpu_count);
195 let _ = writeln!(s, " Physical cores: {}", self.physical_cores);
196 let _ = writeln!(s, " NUMA nodes: {}", self.numa_nodes);
197 let _ = writeln!(s, " Cache line: {} bytes", self.cache_line_size);
198 let _ = writeln!(s, " SIMD: {}", self.cpu_features.simd_level());
199 let _ = writeln!(s, " Features: {}", self.cpu_features.summary());
200
201 let _ = writeln!(s);
202 let _ = writeln!(s, "I/O:");
203 let _ = writeln!(s, " {}", self.io_uring.summary());
204 let _ = writeln!(s, " {}", self.xdp.summary());
205 let _ = writeln!(s, " Storage: {}", self.storage.summary());
206
207 let _ = writeln!(s);
208 let _ = writeln!(s, "Memory:");
209 let _ = writeln!(s, " {}", self.memory.summary());
210
211 let recommended = self.recommended_config();
212 let _ = writeln!(s);
213 let _ = writeln!(s, "Performance Tier: {}", recommended.performance_tier());
214
215 s
216 }
217
218 #[must_use]
220 pub fn is_fully_optimized(&self) -> bool {
221 self.io_uring.is_usable() && self.io_uring.sqpoll_supported && self.cpu_count > 1
222 }
223
224 #[must_use]
226 pub fn meets_minimum_requirements(&self) -> bool {
227 self.cpu_count >= 1 && self.memory.total_memory >= 512 * 1024 * 1024
229 }
230
231 #[must_use]
233 pub fn missing_features(&self) -> Vec<&'static str> {
234 let mut missing = Vec::new();
235
236 if !self.io_uring.feature_enabled {
237 missing.push("io_uring feature not enabled (compile with --features io-uring)");
238 } else if !self.io_uring.available {
239 missing.push("io_uring not available (requires Linux 5.1+)");
240 }
241
242 if !self.io_uring.sqpoll_supported && self.io_uring.available {
243 missing.push("io_uring SQPOLL not supported (requires Linux 5.11+)");
244 }
245
246 if !self.io_uring.iopoll_supported && self.io_uring.available {
247 missing.push("io_uring IOPOLL not supported (requires Linux 5.19+ and NVMe)");
248 }
249
250 if !self.xdp.feature_enabled {
251 missing.push("XDP feature not enabled (compile with --features xdp)");
252 }
253
254 if self.numa_nodes <= 1 && self.cpu_count > 8 {
255 missing.push("NUMA topology not detected (may need hwloc feature)");
256 }
257
258 if !self.memory.huge_pages_available {
259 missing.push("Huge pages not available");
260 }
261
262 missing
263 }
264
265 pub fn log_summary(&self) {
267 tracing::info!("Platform: {}", self.platform);
268 if let Some(ref kv) = self.kernel_version {
269 tracing::info!("Kernel: {kv}");
270 }
271 tracing::info!(
272 "CPU: {} logical, {} physical, {} NUMA nodes",
273 self.cpu_count,
274 self.physical_cores,
275 self.numa_nodes
276 );
277 tracing::info!("SIMD: {}", self.cpu_features.simd_level());
278 tracing::info!("io_uring: {}", self.io_uring.summary());
279 tracing::info!("XDP: {}", self.xdp.summary());
280 tracing::info!("Memory: {:.1} GB", self.memory.total_memory_gb());
281 tracing::info!(
282 "Performance tier: {}",
283 self.recommended_config().performance_tier()
284 );
285 }
286}
287
288#[derive(Debug, Clone, Copy, PartialEq, Eq)]
290pub enum Platform {
291 Linux,
293 MacOS,
295 Windows,
297 FreeBSD,
299 Other,
301}
302
303impl Platform {
304 #[must_use]
306 pub const fn detect() -> Self {
307 #[cfg(target_os = "linux")]
308 {
309 Self::Linux
310 }
311 #[cfg(target_os = "macos")]
312 {
313 Self::MacOS
314 }
315 #[cfg(target_os = "windows")]
316 {
317 Self::Windows
318 }
319 #[cfg(target_os = "freebsd")]
320 {
321 Self::FreeBSD
322 }
323 #[cfg(not(any(
324 target_os = "linux",
325 target_os = "macos",
326 target_os = "windows",
327 target_os = "freebsd"
328 )))]
329 {
330 Self::Other
331 }
332 }
333
334 #[must_use]
336 pub const fn is_unix(&self) -> bool {
337 matches!(self, Self::Linux | Self::MacOS | Self::FreeBSD)
338 }
339
340 #[must_use]
342 pub const fn supports_io_uring(&self) -> bool {
343 matches!(self, Self::Linux)
344 }
345
346 #[must_use]
348 pub const fn supports_xdp(&self) -> bool {
349 matches!(self, Self::Linux)
350 }
351}
352
353impl std::fmt::Display for Platform {
354 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
355 match self {
356 Self::Linux => write!(f, "Linux"),
357 Self::MacOS => write!(f, "macOS"),
358 Self::Windows => write!(f, "Windows"),
359 Self::FreeBSD => write!(f, "FreeBSD"),
360 Self::Other => write!(f, "Other"),
361 }
362 }
363}
364
365#[cfg(test)]
366mod tests {
367 use super::*;
368
369 #[test]
370 fn test_system_capabilities_detect() {
371 let caps = SystemCapabilities::detect();
372
373 assert!(caps.cpu_count >= 1);
374 assert!(caps.physical_cores >= 1);
375 assert!(caps.numa_nodes >= 1);
376 assert!(caps.cache_line_size >= 32);
377 assert!(caps.memory.total_memory > 0);
378 }
379
380 #[test]
381 fn test_system_capabilities_cached() {
382 let caps1 = SystemCapabilities::detect();
384 let caps2 = SystemCapabilities::detect();
385
386 assert_eq!(caps1.cpu_count, caps2.cpu_count);
387 assert_eq!(caps1.numa_nodes, caps2.numa_nodes);
388 }
389
390 #[test]
391 fn test_system_capabilities_summary() {
392 let caps = SystemCapabilities::detect();
393 let summary = caps.summary();
394
395 assert!(summary.contains("System Capabilities"));
396 assert!(summary.contains("CPU:"));
397 assert!(summary.contains("I/O:"));
398 assert!(summary.contains("Memory:"));
399 }
400
401 #[test]
402 fn test_system_capabilities_recommended_config() {
403 let caps = SystemCapabilities::detect();
404 let config = caps.recommended_config();
405
406 assert!(config.num_cores >= 1);
407 assert!(config.arena_size > 0);
408 assert!(config.state_store_size > 0);
409 }
410
411 #[test]
412 fn test_system_capabilities_meets_minimum() {
413 let caps = SystemCapabilities::detect();
414 assert!(caps.meets_minimum_requirements());
415 }
416
417 #[test]
418 fn test_system_capabilities_missing_features() {
419 let caps = SystemCapabilities::detect();
420 let missing = caps.missing_features();
421
422 let _ = missing;
425 }
426
427 #[test]
428 fn test_platform_detect() {
429 let platform = Platform::detect();
430
431 #[cfg(target_os = "linux")]
432 assert_eq!(platform, Platform::Linux);
433
434 #[cfg(target_os = "macos")]
435 assert_eq!(platform, Platform::MacOS);
436
437 #[cfg(target_os = "windows")]
438 assert_eq!(platform, Platform::Windows);
439 }
440
441 #[test]
442 fn test_platform_is_unix() {
443 assert!(Platform::Linux.is_unix());
444 assert!(Platform::MacOS.is_unix());
445 assert!(!Platform::Windows.is_unix());
446 }
447
448 #[test]
449 fn test_platform_supports_io_uring() {
450 assert!(Platform::Linux.supports_io_uring());
451 assert!(!Platform::MacOS.supports_io_uring());
452 assert!(!Platform::Windows.supports_io_uring());
453 }
454
455 #[test]
456 fn test_platform_display() {
457 assert_eq!(format!("{}", Platform::Linux), "Linux");
458 assert_eq!(format!("{}", Platform::MacOS), "macOS");
459 assert_eq!(format!("{}", Platform::Windows), "Windows");
460 }
461
462 #[test]
463 fn test_detect_with_storage() {
464 let caps = SystemCapabilities::detect_with_storage("/");
466
467 assert!(caps.cpu_count >= 1);
468 }
470
471 #[test]
472 fn test_update_storage() {
473 let mut caps = SystemCapabilities::detect_uncached();
474 caps.update_storage("/");
475
476 }
478}