ghostscope_loader/
kernel_caps.rs

1use aya::maps::MapData;
2use aya_obj::{
3    generated::bpf_map_type::{BPF_MAP_TYPE_PERF_EVENT_ARRAY, BPF_MAP_TYPE_RINGBUF},
4    maps::PinningType,
5    EbpfSectionKind, Map,
6};
7use std::{fmt, sync::OnceLock};
8use tracing::{error, info, warn};
9
10/// Global kernel capabilities cache
11static KERNEL_CAPS: OnceLock<Result<KernelCapabilities, KernelCapabilityError>> = OnceLock::new();
12
13#[derive(Debug, Clone)]
14pub struct KernelCapabilityError {
15    message: String,
16}
17
18impl KernelCapabilityError {
19    fn new(message: impl Into<String>) -> Self {
20        Self {
21            message: message.into(),
22        }
23    }
24}
25
26impl fmt::Display for KernelCapabilityError {
27    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28        write!(f, "{}", self.message)
29    }
30}
31
32impl std::error::Error for KernelCapabilityError {}
33
34/// Kernel eBPF capabilities detection
35#[derive(Debug, Clone, Copy)]
36pub struct KernelCapabilities {
37    /// Whether the kernel supports BPF_MAP_TYPE_RINGBUF (requires >= 5.8)
38    pub supports_ringbuf: bool,
39    /// Whether the kernel supports BPF_MAP_TYPE_PERF_EVENT_ARRAY (requires >= 4.3)
40    pub supports_perf_event_array: bool,
41}
42
43impl KernelCapabilities {
44    /// Get global kernel capabilities (detected once on first call)
45    /// Returns an error if neither RingBuf nor PerfEventArray is supported
46    pub fn get() -> Result<&'static Self, KernelCapabilityError> {
47        match KERNEL_CAPS.get_or_init(detect_full_capabilities) {
48            Ok(capabilities) => Ok(capabilities),
49            Err(err) => Err(err.clone()),
50        }
51    }
52
53    /// Get kernel capabilities with PerfEventArray-only detection (for testing mode)
54    /// Skips RingBuf detection and only validates PerfEventArray support
55    /// Returns an error if PerfEventArray is not supported
56    pub fn get_perf_only() -> Result<&'static Self, KernelCapabilityError> {
57        match KERNEL_CAPS.get_or_init(detect_perf_only_capabilities) {
58            Ok(capabilities) => Ok(capabilities),
59            Err(err) => Err(err.clone()),
60        }
61    }
62
63    /// Check if RingBuf is supported (convenience method)
64    pub fn ringbuf_supported() -> bool {
65        Self::get()
66            .map(|caps| caps.supports_ringbuf)
67            .unwrap_or(false)
68    }
69
70    /// Check if PerfEventArray is supported (convenience method)
71    pub fn perf_event_array_supported() -> bool {
72        Self::get()
73            .map(|caps| caps.supports_perf_event_array)
74            .unwrap_or(false)
75    }
76}
77
78fn detect_full_capabilities() -> Result<KernelCapabilities, KernelCapabilityError> {
79    let supports_ringbuf = detect_ringbuf_support();
80    let supports_perf_event_array = if !supports_ringbuf {
81        detect_perf_event_array_support()
82    } else {
83        true
84    };
85
86    if supports_ringbuf {
87        info!("✓ Kernel supports RingBuf (>= 5.8)");
88    } else if supports_perf_event_array {
89        warn!("⚠️  Kernel does not support RingBuf (< 5.8)");
90        warn!("⚠️  Will use PerfEventArray as fallback");
91        info!("✓ Kernel supports PerfEventArray (>= 4.3)");
92    } else {
93        error!("❌ Kernel supports neither RingBuf nor PerfEventArray");
94        error!("❌ GhostScope requires kernel >= 4.3 for eBPF event output");
95        error!("❌ Current kernel appears to be older or eBPF is disabled");
96        return Err(KernelCapabilityError::new(
97            "Kernel lacks both RingBuf (>=5.8) and PerfEventArray (>=4.3) support. \
98             Please upgrade the kernel or enable eBPF features.",
99        ));
100    }
101
102    Ok(KernelCapabilities {
103        supports_ringbuf,
104        supports_perf_event_array,
105    })
106}
107
108fn detect_perf_only_capabilities() -> Result<KernelCapabilities, KernelCapabilityError> {
109    info!("Testing mode: Only detecting PerfEventArray support");
110    let supports_perf_event_array = detect_perf_event_array_support();
111
112    if !supports_perf_event_array {
113        error!("❌ Kernel does not support PerfEventArray");
114        error!("❌ GhostScope requires kernel >= 4.3 for eBPF event output");
115        return Err(KernelCapabilityError::new(
116            "Kernel lacks PerfEventArray support (>=4.3 required). \
117             Please upgrade the kernel or enable eBPF features.",
118        ));
119    }
120
121    info!("✓ Kernel supports PerfEventArray (>= 4.3)");
122
123    Ok(KernelCapabilities {
124        supports_ringbuf: false,
125        supports_perf_event_array,
126    })
127}
128
129/// Detect RingBuf support by attempting to create a minimal map
130fn detect_ringbuf_support() -> bool {
131    info!("Probing kernel RingBuf support by attempting map creation...");
132
133    // Create a minimal RingBuf map definition (4KB)
134    let obj_map = Map::Legacy(aya_obj::maps::LegacyMap {
135        section_index: 0,
136        section_kind: EbpfSectionKind::Maps,
137        symbol_index: None,
138        def: aya_obj::maps::bpf_map_def {
139            map_type: BPF_MAP_TYPE_RINGBUF as u32,
140            key_size: 0,       // RingBuf doesn't use key
141            value_size: 0,     // RingBuf doesn't use value
142            max_entries: 4096, // 4KB minimal size
143            map_flags: 0,
144            id: 0,
145            pinning: PinningType::None,
146        },
147        data: Vec::new(),
148    });
149
150    // Try to create the map
151    match MapData::create(obj_map, "probe_ringbuf", None) {
152        Ok(_map) => {
153            // Map was created successfully, RingBuf is supported
154            // The map will be automatically dropped and closed
155            info!("RingBuf map creation succeeded - RingBuf is supported");
156            true
157        }
158        Err(e) => {
159            // Map creation failed, likely due to unsupported map type
160            info!(
161                "RingBuf map creation failed (this is normal on kernels < 5.8): {}",
162                e
163            );
164            false
165        }
166    }
167}
168
169/// Detect PerfEventArray support by attempting to create a minimal map
170fn detect_perf_event_array_support() -> bool {
171    info!("Probing kernel PerfEventArray support by attempting map creation...");
172
173    // Create a minimal PerfEventArray map definition
174    let obj_map = Map::Legacy(aya_obj::maps::LegacyMap {
175        section_index: 0,
176        section_kind: EbpfSectionKind::Maps,
177        symbol_index: None,
178        def: aya_obj::maps::bpf_map_def {
179            map_type: BPF_MAP_TYPE_PERF_EVENT_ARRAY as u32,
180            key_size: 4,    // key is u32
181            value_size: 4,  // value is u32 (file descriptor)
182            max_entries: 0, // 0 means auto-detect number of CPUs
183            map_flags: 0,
184            id: 0,
185            pinning: PinningType::None,
186        },
187        data: Vec::new(),
188    });
189
190    // Try to create the map
191    match MapData::create(obj_map, "probe_perf_event_array", None) {
192        Ok(_map) => {
193            // Map was created successfully, PerfEventArray is supported
194            // The map will be automatically dropped and closed
195            info!("PerfEventArray map creation succeeded - PerfEventArray is supported");
196            true
197        }
198        Err(e) => {
199            // Map creation failed, likely due to unsupported map type
200            error!(
201                "PerfEventArray map creation failed (kernel may be older than 4.3): {}",
202                e
203            );
204            false
205        }
206    }
207}