diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 63ff8adc2..a68ffba7e 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -45,7 +45,7 @@ dependencies = [ "bytestring", "derive_more 2.0.1", "encoding_rs", - "foldhash", + "foldhash 0.1.5", "futures-core", "http 0.2.12", "httparse", @@ -143,7 +143,7 @@ dependencies = [ "cfg-if", "derive_more 2.0.1", "encoding_rs", - "foldhash", + "foldhash 0.1.5", "futures-core", "futures-util", "impl-more", @@ -365,7 +365,7 @@ dependencies = [ "objc2-foundation", "parking_lot", "percent-encoding", - "windows-sys 0.59.0", + "windows-sys 0.52.0", "x11rb", ] @@ -680,34 +680,33 @@ dependencies = [ [[package]] name = "aya" version = "0.13.1" -source = "git+https://github.com/aya-rs/aya#630a76711701a2f489f41d9d91076cc9bebb2675" +source = "git+https://github.com/aya-rs/aya#30182463bdb6cce592477e66659d5f66f846cfcf" dependencies = [ "assert_matches", "aya-obj", "bitflags 2.9.1", "bytes", - "hashbrown 0.15.3", + "hashbrown 0.16.0", "libc", "log", - "object", + "object 0.37.3", "once_cell", "thiserror 2.0.16", - "tokio", ] [[package]] name = "aya-build" version = "0.1.2" -source = "git+https://github.com/aya-rs/aya#630a76711701a2f489f41d9d91076cc9bebb2675" +source = "git+https://github.com/aya-rs/aya#30182463bdb6cce592477e66659d5f66f846cfcf" dependencies = [ "anyhow", - "cargo_metadata", + "cargo_metadata 0.22.0", ] [[package]] name = "aya-ebpf" version = "0.1.1" -source = "git+https://github.com/aya-rs/aya#630a76711701a2f489f41d9d91076cc9bebb2675" +source = "git+https://github.com/aya-rs/aya#30182463bdb6cce592477e66659d5f66f846cfcf" dependencies = [ "aya-ebpf-bindings", "aya-ebpf-cty", @@ -718,7 +717,7 @@ dependencies = [ [[package]] name = "aya-ebpf-bindings" version = "0.1.1" -source = "git+https://github.com/aya-rs/aya#630a76711701a2f489f41d9d91076cc9bebb2675" +source = "git+https://github.com/aya-rs/aya#30182463bdb6cce592477e66659d5f66f846cfcf" dependencies = [ "aya-ebpf-cty", ] @@ -726,12 +725,12 @@ dependencies = [ [[package]] name = "aya-ebpf-cty" version = "0.2.2" -source = "git+https://github.com/aya-rs/aya#630a76711701a2f489f41d9d91076cc9bebb2675" +source = "git+https://github.com/aya-rs/aya#30182463bdb6cce592477e66659d5f66f846cfcf" [[package]] name = "aya-ebpf-macros" version = "0.1.1" -source = "git+https://github.com/aya-rs/aya#630a76711701a2f489f41d9d91076cc9bebb2675" +source = "git+https://github.com/aya-rs/aya#30182463bdb6cce592477e66659d5f66f846cfcf" dependencies = [ "proc-macro2", "proc-macro2-diagnostics", @@ -742,20 +741,18 @@ dependencies = [ [[package]] name = "aya-log" version = "0.2.1" -source = "git+https://github.com/aya-rs/aya#630a76711701a2f489f41d9d91076cc9bebb2675" +source = "git+https://github.com/aya-rs/aya#30182463bdb6cce592477e66659d5f66f846cfcf" dependencies = [ "aya", "aya-log-common", - "bytes", "log", "thiserror 2.0.16", - "tokio", ] [[package]] name = "aya-log-common" version = "0.1.15" -source = "git+https://github.com/aya-rs/aya#630a76711701a2f489f41d9d91076cc9bebb2675" +source = "git+https://github.com/aya-rs/aya#30182463bdb6cce592477e66659d5f66f846cfcf" dependencies = [ "num_enum", ] @@ -763,7 +760,7 @@ dependencies = [ [[package]] name = "aya-log-ebpf" version = "0.1.1" -source = "git+https://github.com/aya-rs/aya#630a76711701a2f489f41d9d91076cc9bebb2675" +source = "git+https://github.com/aya-rs/aya#30182463bdb6cce592477e66659d5f66f846cfcf" dependencies = [ "aya-ebpf", "aya-log-common", @@ -773,7 +770,7 @@ dependencies = [ [[package]] name = "aya-log-ebpf-macros" version = "0.1.0" -source = "git+https://github.com/aya-rs/aya#630a76711701a2f489f41d9d91076cc9bebb2675" +source = "git+https://github.com/aya-rs/aya#30182463bdb6cce592477e66659d5f66f846cfcf" dependencies = [ "aya-log-common", "aya-log-parser", @@ -785,7 +782,7 @@ dependencies = [ [[package]] name = "aya-log-parser" version = "0.1.13" -source = "git+https://github.com/aya-rs/aya#630a76711701a2f489f41d9d91076cc9bebb2675" +source = "git+https://github.com/aya-rs/aya#30182463bdb6cce592477e66659d5f66f846cfcf" dependencies = [ "aya-log-common", ] @@ -793,12 +790,12 @@ dependencies = [ [[package]] name = "aya-obj" version = "0.2.1" -source = "git+https://github.com/aya-rs/aya#630a76711701a2f489f41d9d91076cc9bebb2675" +source = "git+https://github.com/aya-rs/aya#30182463bdb6cce592477e66659d5f66f846cfcf" dependencies = [ "bytes", - "hashbrown 0.15.3", + "hashbrown 0.16.0", "log", - "object", + "object 0.37.3", "thiserror 2.0.16", ] @@ -826,7 +823,7 @@ dependencies = [ "cfg-if", "libc", "miniz_oxide", - "object", + "object 0.36.7", "rustc-demangle", "windows-targets 0.52.6", ] @@ -1105,11 +1102,11 @@ dependencies = [ [[package]] name = "camino" -version = "1.1.9" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" +checksum = "e1de8bc0aa9e9385ceb3bf0c152e3a9b9544f6c4a912c8ae504e80c1f0368603" dependencies = [ - "serde", + "serde_core", ] [[package]] @@ -1131,6 +1128,31 @@ dependencies = [ "serde", ] +[[package]] +name = "cargo-platform" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "122ec45a44b270afd1402f351b782c676b173e3c3fb28d86ff7ebfb4d86a4ee4" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-util-schemas" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dc1a6f7b5651af85774ae5a34b4e8be397d9cf4bc063b7e6dbd99a841837830" +dependencies = [ + "semver", + "serde", + "serde-untagged", + "serde-value", + "thiserror 2.0.16", + "toml 0.8.22", + "unicode-xid", + "url", +] + [[package]] name = "cargo_metadata" version = "0.19.2" @@ -1138,7 +1160,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba" dependencies = [ "camino", - "cargo-platform", + "cargo-platform 0.1.9", + "semver", + "serde", + "serde_json", + "thiserror 2.0.16", +] + +[[package]] +name = "cargo_metadata" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c3f56c207c76c07652489840ff98687dcf213de178ac0974660d6fefeaf5ec6" +dependencies = [ + "camino", + "cargo-platform 0.3.1", + "cargo-util-schemas", "semver", "serde", "serde_json", @@ -2259,7 +2296,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -2741,6 +2778,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "foreign-types" version = "0.5.0" @@ -3331,7 +3374,17 @@ checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" dependencies = [ "allocator-api2", "equivalent", - "foldhash", + "foldhash 0.1.5", +] + +[[package]] +name = "hashbrown" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +dependencies = [ + "equivalent", + "foldhash 0.2.0", ] [[package]] @@ -4793,7 +4846,7 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ - "proc-macro-crate 3.3.0", + "proc-macro-crate 1.3.1", "proc-macro2", "quote", "syn 2.0.106", @@ -5018,6 +5071,15 @@ name = "object" version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + +[[package]] +name = "object" +version = "0.37.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" dependencies = [ "crc32fast", "hashbrown 0.15.3", @@ -5162,6 +5224,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "ordered-float" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" +dependencies = [ + "num-traits", +] + [[package]] name = "ordered-stream" version = "0.2.0" @@ -5817,7 +5888,7 @@ dependencies = [ "once_cell", "socket2 0.5.10", "tracing", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -6207,7 +6278,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.4.15", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -6220,7 +6291,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.9.4", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -6664,6 +6735,16 @@ dependencies = [ "typeid", ] +[[package]] +name = "serde-value" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" +dependencies = [ + "ordered-float", + "serde", +] + [[package]] name = "serde_assert" version = "0.7.1" @@ -7789,7 +7870,7 @@ checksum = "41a3852fdf9a4f8fbeaa63dc3e9a85284dd6ef7200751f0bd66ceee30c93f212" dependencies = [ "anyhow", "brotli", - "cargo_metadata", + "cargo_metadata 0.19.2", "ctor", "dunce", "glob", @@ -7852,7 +7933,7 @@ dependencies = [ "getrandom 0.3.3", "once_cell", "rustix 1.0.7", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -8627,7 +8708,7 @@ checksum = "c6d968cb62160c11f2573e6be724ef8b1b18a277aededd17033f8a912d73e2b4" dependencies = [ "anyhow", "camino", - "cargo_metadata", + "cargo_metadata 0.19.2", "clap", "uniffi_bindgen", "uniffi_core", @@ -8651,7 +8732,7 @@ dependencies = [ "anyhow", "askama", "camino", - "cargo_metadata", + "cargo_metadata 0.19.2", "fs-err", "glob", "goblin", @@ -9204,7 +9285,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.48.0", ] [[package]] diff --git a/rust/deny.toml b/rust/deny.toml index 75f825247..2768ddff0 100644 --- a/rust/deny.toml +++ b/rust/deny.toml @@ -232,10 +232,13 @@ deny = [ skip = [ "base64", "bitflags", + "cargo-platform", + "cargo_metadata", "core-foundation", "core-graphics", "core-graphics-types", "derive_more", + "foldhash", "getrandom", "hashbrown", "heck", @@ -244,6 +247,7 @@ skip = [ "libloading", "linux-raw-sys", "nix", + "object", "phf", "phf_codegen", "phf_generator", diff --git a/rust/relay/ebpf-turn-router/src/main.rs b/rust/relay/ebpf-turn-router/src/main.rs index 10f7e42c7..32d7cdf18 100644 --- a/rust/relay/ebpf-turn-router/src/main.rs +++ b/rust/relay/ebpf-turn-router/src/main.rs @@ -34,13 +34,13 @@ pub fn handle_turn(ctx: aya_ebpf::programs::XdpContext) -> u32 { | Error::UdpChecksumMissing | Error::Ipv4PacketWithOptions), ) => { - debug!(&ctx, target: "eBPF", "^^^ pass packet to userspace: {}", e); + debug!(&ctx, target: "eBPF", "^^^ pass packet to userspace: {}", e.as_str()); xdp_action::XDP_PASS } // In a double symmetric NAT setup, it is easily possible for packets to arrive from IPs that don't have channel bindings. Err(e @ Error::NoEntry(_)) => { - debug!(&ctx,target: "eBPF", "XXX drop packet: {}", e); + debug!(&ctx,target: "eBPF", "XXX drop packet: {}", e.as_str()); xdp_action::XDP_DROP } @@ -48,9 +48,9 @@ pub fn handle_turn(ctx: aya_ebpf::programs::XdpContext) -> u32 { e @ (Error::ArrayIndexOutOfBounds | Error::IpAddrUnset | Error::BadChannelDataLength - | Error::XdpAdjustHeadFailed(_)), + | Error::XdpAdjustHeadFailed), ) => { - warn!(&ctx,target: "eBPF", "XXX drop packet: {}", e); + warn!(&ctx,target: "eBPF", "XXX drop packet: {}", e.as_str()); xdp_action::XDP_DROP } diff --git a/rust/relay/ebpf-turn-router/src/try_handle_turn/adjust_head.rs b/rust/relay/ebpf-turn-router/src/try_handle_turn/adjust_head.rs index 3b48ec68e..0a0d9c93d 100644 --- a/rust/relay/ebpf-turn-router/src/try_handle_turn/adjust_head.rs +++ b/rust/relay/ebpf-turn-router/src/try_handle_turn/adjust_head.rs @@ -7,7 +7,7 @@ pub fn adjust_head(ctx: &XdpContext, size: i32) -> Result<(), Error> { // SAFETY: The attach mode and NIC driver support headroom adjustment by `size` bytes. let ret = unsafe { bpf_xdp_adjust_head(ctx.ctx, size) }; if ret < 0 { - return Err(Error::XdpAdjustHeadFailed(ret)); + return Err(Error::XdpAdjustHeadFailed); } Ok(()) diff --git a/rust/relay/ebpf-turn-router/src/try_handle_turn/error.rs b/rust/relay/ebpf-turn-router/src/try_handle_turn/error.rs index 68a89f9a8..70e89e5c3 100644 --- a/rust/relay/ebpf-turn-router/src/try_handle_turn/error.rs +++ b/rust/relay/ebpf-turn-router/src/try_handle_turn/error.rs @@ -1,5 +1,3 @@ -use core::num::NonZeroUsize; - #[derive(Debug, Clone, Copy)] pub enum Error { ArrayIndexOutOfBounds, @@ -14,7 +12,7 @@ pub enum Error { NotAChannelDataMessage, BadChannelDataLength, NoEntry(SupportedChannel), - XdpAdjustHeadFailed(i64), + XdpAdjustHeadFailed, } #[derive(Debug, Clone, Copy)] @@ -25,11 +23,10 @@ pub enum SupportedChannel { Chan6ToUdp, } -impl aya_log_ebpf::WriteToBuf for Error { +impl Error { #[inline(always)] - fn write(self, buf: &mut [u8]) -> Option { - // Use a simpler match structure to help the verifier - let msg = match self { + pub fn as_str(&self) -> &'static str { + match self { Error::ArrayIndexOutOfBounds => "Array index is out of bounds", Error::IpAddrUnset => "IP address has not been configured", Error::UdpChecksumMissing => "UDP checksum is missing", @@ -47,54 +44,9 @@ impl aya_log_ebpf::WriteToBuf for Error { SupportedChannel::Udp6ToChan => "No entry in UDPv6 to channel IPv4 or IPv6 map", SupportedChannel::Chan6ToUdp => "No entry in channel IPv6 to UDPv4 or UDPv6 map", }, - Error::XdpAdjustHeadFailed(ret) => { - // Handle this case separately to avoid complex control flow - let mut written = 0; - written += "Failed to adjust tail: ".write(buf)?.get(); - written += errno_to_str(ret).write(buf)?.get(); - return NonZeroUsize::new(written); - } - }; - - msg.write(buf) + Error::XdpAdjustHeadFailed => "Failed to adjust tail", + } } } impl aya_log_ebpf::macro_support::DefaultFormatter for Error {} - -/// Helper function to map Linux/eBPF error codes to human-readable strings -/// This avoids integer formatting which can cause pointer arithmetic verifier issues -#[inline(always)] -fn errno_to_str(errno: i64) -> &'static str { - match errno { - -1 => "EPERM (Operation not permitted)", - -2 => "ENOENT (No such file or directory)", - -3 => "ESRCH (No such process)", - -4 => "EINTR (Interrupted system call)", - -5 => "EIO (I/O error)", - -6 => "ENXIO (No such device or address)", - -7 => "E2BIG (Argument list too long)", - -8 => "ENOEXEC (Exec format error)", - -9 => "EBADF (Bad file number)", - -10 => "ECHILD (No child processes)", - -11 => "EAGAIN (Try again)", - -12 => "ENOMEM (Out of memory)", - -13 => "EACCES (Permission denied)", - -14 => "EFAULT (Bad address)", - -16 => "EBUSY (Device or resource busy)", - -17 => "EEXIST (File exists)", - -19 => "ENODEV (No such device)", - -22 => "EINVAL (Invalid argument)", - -24 => "EMFILE (Too many open files)", - -28 => "ENOSPC (No space left on device)", - -32 => "EPIPE (Broken pipe)", - -34 => "ERANGE (Math result not representable)", - -61 => "ENODATA (No data available)", - -75 => "EOVERFLOW (Value too large for defined data type)", - -84 => "EILSEQ (Illegal byte sequence)", - -90 => "EMSGSIZE (Message too long)", - -95 => "ENOTSUP (Operation not supported)", - -105 => "ENOBUFS (No buffer space available)", - _ => "Unknown error", - } -} diff --git a/rust/relay/server/Cargo.toml b/rust/relay/server/Cargo.toml index 3ecc71859..f12df5cd1 100644 --- a/rust/relay/server/Cargo.toml +++ b/rust/relay/server/Cargo.toml @@ -50,7 +50,7 @@ url = { workspace = true } uuid = { workspace = true, features = ["v4"] } [target.'cfg(target_os = "linux")'.dependencies] -aya = { workspace = true, features = ["tokio"] } +aya = { workspace = true } aya-log = { workspace = true } ebpf-shared = { workspace = true, features = ["std"] } diff --git a/rust/relay/server/build.rs b/rust/relay/server/build.rs index 348a22aca..fc990c951 100644 --- a/rust/relay/server/build.rs +++ b/rust/relay/server/build.rs @@ -9,7 +9,7 @@ fn main() -> anyhow::Result<()> { .context("MetadataCommand::exec")? .packages .into_iter() - .find(|Package { name, .. }| name == "ebpf-turn-router") + .find(|Package { name, .. }| name.as_str() == "ebpf-turn-router") .context("`ebpf-turn-router` package not found")?; aya_build::build_ebpf( diff --git a/rust/relay/server/src/ebpf/linux.rs b/rust/relay/server/src/ebpf/linux.rs index 065604a30..41a332cc7 100644 --- a/rust/relay/server/src/ebpf/linux.rs +++ b/rust/relay/server/src/ebpf/linux.rs @@ -1,9 +1,12 @@ -use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr}; +use std::{ + io, + net::{Ipv4Addr, Ipv6Addr, SocketAddr}, +}; use anyhow::{Context as _, Result}; use aya::{ Pod, - maps::{AsyncPerfEventArray, HashMap, MapData, PerCpuArray, PerCpuValues}, + maps::{HashMap, MapData, PerCpuArray, PerCpuValues, PerfEventArray}, programs::{Xdp, XdpFlags}, }; use aya_log::EbpfLogger; @@ -12,6 +15,7 @@ use ebpf_shared::{ ClientAndChannelV4, ClientAndChannelV6, PortAndPeerV4, PortAndPeerV6, StatsEvent, }; use stun_codec::rfc5766::attributes::ChannelNumber; +use tokio::io::{Interest, unix::AsyncFd}; use crate::ebpf::AttachMode; @@ -28,7 +32,7 @@ pub struct Program { ebpf: aya::Ebpf, #[expect(dead_code, reason = "We are just keeping it alive.")] - stats: AsyncPerfEventArray, + stats: PerfEventArray, } impl Program { @@ -44,7 +48,22 @@ impl Program { env!("OUT_DIR"), "/ebpf-turn-router-main" )))?; - let _ = EbpfLogger::init(&mut ebpf); + let logger = EbpfLogger::init(&mut ebpf)?; + let mut logger = AsyncFd::with_interest(logger, Interest::READABLE)?; + tokio::task::spawn(async move { + loop { + let mut guard = match logger.readable_mut().await { + Ok(guard) => guard, + Err(e) => { + tracing::warn!("Failed to wait for logger to be readable: {e}"); + return; + } + }; + guard.get_inner_mut().flush(); + guard.clear_ready(); + } + }); + let program: &mut Xdp = ebpf .program_mut("handle_turn") .context("No program")? @@ -60,7 +79,7 @@ impl Program { .attach(interface, xdp_flags) .with_context(|| format!("Failed to attached to interface {interface}"))?; - let mut stats = AsyncPerfEventArray::try_from( + let mut stats = PerfEventArray::try_from( ebpf.take_map("STATS") .context("`STATS` perf array not found")?, )?; @@ -76,7 +95,8 @@ impl Program { .context("Failed to determine number of CPUs")? { // open a separate perf buffer for each cpu - let mut stats_array_buf = stats.open(cpu_id, Some(PAGE_COUNT))?; + let stats_array_buf = stats.open(cpu_id, Some(PAGE_COUNT))?; + let mut fd = AsyncFd::new(stats_array_buf)?; tracing::debug!(%cpu_id, "Subscribing to stats events from eBPF kernel"); @@ -90,7 +110,17 @@ impl Program { .collect::>(); loop { - let events = match stats_array_buf.read_events(&mut buffers).await { + let events = fd + .async_io_mut(Interest::READABLE, |fd| { + if !fd.readable() { + return Err(io::Error::from(io::ErrorKind::WouldBlock)); + } + + fd.read_events(&mut buffers).map_err(io::Error::other) + }) + .await; + + let events = match events { Ok(events) => events, Err(e) => { tracing::warn!("Failed to read perf events: {e}");