diff --git a/aya-log/CHANGELOG.md b/aya-log/CHANGELOG.md index 76a063be..5eb0e76d 100644 --- a/aya-log/CHANGELOG.md +++ b/aya-log/CHANGELOG.md @@ -12,6 +12,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - The implementation is now backed by a ring buffer rather than a perf event array. This should improve performance but increases the minimum supported kernel version to 5.8. +- Drop the built-in `tokio` dependency. Users must now BYOR (bring your own runtime). + ## v0.2.1 (2024-10-09) ### Chore diff --git a/aya-log/Cargo.toml b/aya-log/Cargo.toml index 8bd5b574..d1e7234c 100644 --- a/aya-log/Cargo.toml +++ b/aya-log/Cargo.toml @@ -21,11 +21,11 @@ aya = { path = "../aya", version = "^0.13.1", default-features = false } aya-log-common = { path = "../aya-log-common", version = "^0.1.15", default-features = false } log = { workspace = true } thiserror = { workspace = true } -tokio = { workspace = true, features = ["net", "rt"] } [dev-dependencies] env_logger = { workspace = true } testing_logger = { workspace = true } +tokio = { workspace = true, features = ["net", "rt"] } [lib] path = "src/lib.rs" diff --git a/aya-log/README.md b/aya-log/README.md index ee23c5e2..765247c9 100644 --- a/aya-log/README.md +++ b/aya-log/README.md @@ -38,7 +38,15 @@ use aya_log::EbpfLogger; env_logger::init(); // Will log using the default logger, which is TermLogger in this case -EbpfLogger::init(&mut bpf).unwrap(); +let logger = EbpfLogger::init(&mut bpf).unwrap(); +let mut logger = tokio::io::unix::AsyncFd::with_interest(logger, tokio::io::Interest::READABLE).unwrap(); +tokio::task::spawn(async move { + loop { + let mut guard = logger.readable_mut().await.unwrap(); + guard.get_inner_mut().flush(); + guard.clear_ready(); + } +}); ``` ### eBPF code diff --git a/aya-log/src/lib.rs b/aya-log/src/lib.rs index d2b1a3f2..e34c3ba6 100644 --- a/aya-log/src/lib.rs +++ b/aya-log/src/lib.rs @@ -3,8 +3,8 @@ //! This is the user space side of the [Aya] logging framework. For the eBPF //! side, see the `aya-log-ebpf` crate. //! -//! `aya-log` provides the [EbpfLogger] type, which reads log records created by -//! `aya-log-ebpf` and logs them using the [log] crate. Any logger that +//! `aya-log` provides functions which read log records created by +//! `aya-log-ebpf` and log them using the [log] crate. Any logger that //! implements the [Log] trait can be used with this crate. //! //! # Example: @@ -19,7 +19,15 @@ //! env_logger::init(); //! //! // start reading aya-log records and log them using the default logger -//! EbpfLogger::init(&mut bpf).unwrap(); +//! let logger = EbpfLogger::init(&mut bpf).unwrap(); +//! let mut logger = tokio::io::unix::AsyncFd::with_interest(logger, tokio::io::Interest::READABLE).unwrap(); +//! tokio::task::spawn(async move { +//! loop { +//! let mut guard = logger.readable_mut().await.unwrap(); +//! guard.get_inner_mut().flush(); +//! guard.clear_ready(); +//! } +//! }); //! ``` //! //! With the following eBPF code: @@ -51,8 +59,9 @@ //! use std::{ fmt::{LowerHex, UpperHex}, - io, mem, + mem, net::{Ipv4Addr, Ipv6Addr}, + os::fd::AsRawFd, ptr, str, }; @@ -84,45 +93,54 @@ unsafe impl Pod for DisplayHintWrapper {} /// Log messages generated by `aya_log_ebpf` using the [log] crate. /// /// For more details see the [module level documentation](crate). -pub struct EbpfLogger; +pub struct EbpfLogger { + ring_buf: RingBuf, + logger: T, +} + +impl AsRawFd for EbpfLogger { + fn as_raw_fd(&self) -> std::os::unix::prelude::RawFd { + let Self { + ring_buf, + logger: _, + } = self; + ring_buf.as_raw_fd() + } +} /// Log messages generated by `aya_log_ebpf` using the [log] crate. #[deprecated(since = "0.2.1", note = "Use `aya_log::EbpfLogger` instead")] -pub type BpfLogger = EbpfLogger; +pub type BpfLogger = EbpfLogger; -impl EbpfLogger { +impl EbpfLogger<&'static dyn Log> { /// Starts reading log records created with `aya-log-ebpf` and logs them /// with the default logger. See [log::logger]. - pub fn init(bpf: &mut Ebpf) -> Result { - EbpfLogger::init_with_logger(bpf, log::logger()) - } - - /// Starts reading log records created with `aya-log-ebpf` and logs them - /// with the given logger. - pub fn init_with_logger( - bpf: &mut Ebpf, - logger: T, - ) -> Result { - let map = bpf.take_map(MAP_NAME).ok_or(Error::MapNotFound)?; - Self::read_logs_async(map, logger) + pub fn init(bpf: &mut Ebpf) -> Result { + Self::init_with_logger(bpf, log::logger()) } /// Attaches to an existing `aya-log-ebpf` instance. /// /// Attaches to the logs produced by `program_id`. Can be used to read logs generated by a /// pinned program. The log records will be written to the default logger. See [log::logger]. - pub fn init_from_id(program_id: u32) -> Result { + pub fn init_from_id(program_id: u32) -> Result { Self::init_from_id_with_logger(program_id, log::logger()) } +} + +impl EbpfLogger { + /// Starts reading log records created with `aya-log-ebpf` and logs them + /// with the given logger. + pub fn init_with_logger(bpf: &mut Ebpf, logger: T) -> Result { + let map = bpf.take_map(MAP_NAME).ok_or(Error::MapNotFound)?; + Self::new(map, logger) + } /// Attaches to an existing `aya-log-ebpf` instance and logs with the given logger. /// /// Attaches to the logs produced by `program_id`. Can be used to read logs generated by a /// pinned program. The log records will be written to the given logger. - pub fn init_from_id_with_logger( - program_id: u32, - logger: T, - ) -> Result { + pub fn init_from_id_with_logger(program_id: u32, logger: T) -> Result { let program_info = loaded_programs() .filter_map(|info| info.ok()) .find(|info| info.id() == program_id) @@ -140,26 +158,21 @@ impl EbpfLogger { .ok_or(Error::MapNotFound)?; let map = MapData::from_id(map.id())?; - Self::read_logs_async(Map::RingBuf(map), logger) + Self::new(Map::RingBuf(map), logger) } - fn read_logs_async(map: Map, logger: T) -> Result { + fn new(map: Map, logger: T) -> Result { let ring_buf: RingBuf<_> = map.try_into()?; - let mut async_fd = - tokio::io::unix::AsyncFd::with_interest(ring_buf, tokio::io::Interest::READABLE) - .map_err(Error::AsyncFdNew)?; - - tokio::spawn(async move { - loop { - let mut guard = async_fd.readable_mut().await.unwrap(); - while let Some(buf) = guard.get_inner_mut().next() { - log_buf(buf.as_ref(), &logger).unwrap(); - } - guard.clear_ready(); - } - }); - Ok(EbpfLogger {}) + Ok(EbpfLogger { ring_buf, logger }) + } + + /// Reads log records from eBPF and writes them to the logger. + pub fn flush(&mut self) { + let Self { ring_buf, logger } = self; + while let Some(buf) = ring_buf.next() { + log_buf(buf.as_ref(), logger).unwrap(); + } } } @@ -429,9 +442,6 @@ pub enum Error { #[error(transparent)] MapError(#[from] MapError), - #[error("tokio::io::unix::AsyncFd::new")] - AsyncFdNew(#[source] io::Error), - #[error("program not found")] ProgramNotFound, diff --git a/test/integration-test/Cargo.toml b/test/integration-test/Cargo.toml index dd1e5447..6fb01a05 100644 --- a/test/integration-test/Cargo.toml +++ b/test/integration-test/Cargo.toml @@ -33,7 +33,12 @@ rbpf = { workspace = true } scopeguard = { workspace = true } test-case = { workspace = true } test-log = { workspace = true, features = ["log"] } -tokio = { workspace = true, features = ["macros", "rt-multi-thread", "time"] } +tokio = { workspace = true, features = [ + "macros", + "net", + "rt-multi-thread", + "time", +] } xdpilone = { workspace = true } [build-dependencies] diff --git a/test/integration-test/src/tests/log.rs b/test/integration-test/src/tests/log.rs index 01820b63..1f8630eb 100644 --- a/test/integration-test/src/tests/log.rs +++ b/test/integration-test/src/tests/log.rs @@ -1,7 +1,4 @@ -use std::{ - borrow::Cow, - sync::{Arc, Mutex}, -}; +use std::{borrow::Cow, sync::Mutex}; use aya::{Ebpf, programs::UProbe}; use aya_log::EbpfLogger; @@ -15,10 +12,10 @@ pub extern "C" fn trigger_ebpf_program() { } struct TestingLogger { - log: F, + log: Mutex, } -impl Log for TestingLogger { +impl Log for TestingLogger { fn enabled(&self, _metadata: &log::Metadata) -> bool { true } @@ -27,6 +24,7 @@ impl Log for TestingLogger { fn log(&self, record: &Record) { let Self { log } = self; + let mut log = log.lock().unwrap(); log(record); } } @@ -38,28 +36,21 @@ struct CapturedLog<'a> { pub target: Cow<'a, str>, } -#[test(tokio::test)] -async fn log() { +#[test] +fn log() { let mut bpf = Ebpf::load(crate::LOG).unwrap(); - let captured_logs = Arc::new(Mutex::new(Vec::new())); - { - let captured_logs = captured_logs.clone(); - EbpfLogger::init_with_logger( - &mut bpf, - TestingLogger { - log: move |record: &Record| { - let mut logs = captured_logs.lock().unwrap(); - logs.push(CapturedLog { - body: format!("{}", record.args()).into(), - level: record.level(), - target: record.target().to_string().into(), - }); - }, - }, - ) - .unwrap(); - } + let mut captured_logs = Vec::new(); + let logger = TestingLogger { + log: Mutex::new(|record: &Record| { + captured_logs.push(CapturedLog { + body: format!("{}", record.args()).into(), + level: record.level(), + target: record.target().to_string().into(), + }); + }), + }; + let mut logger = EbpfLogger::init_with_logger(&mut bpf, &logger).unwrap(); let prog: &mut UProbe = bpf.program_mut("test_log").unwrap().try_into().unwrap(); prog.load().unwrap(); @@ -69,21 +60,9 @@ async fn log() { // Call the function that the uprobe is attached to, so it starts logging. trigger_ebpf_program(); - let mut logs = 0; - let records = loop { - tokio::time::sleep(std::time::Duration::from_millis(100)).await; - let records = captured_logs.lock().unwrap(); - let len = records.len(); - if len == 0 { - continue; - } - if len == logs { - break records; - } - logs = len; - }; + logger.flush(); - let mut records = records.iter(); + let mut records = captured_logs.iter(); assert_eq!( records.next(), diff --git a/xtask/public-api/aya-log.txt b/xtask/public-api/aya-log.txt index 8c01ee82..08995428 100644 --- a/xtask/public-api/aya-log.txt +++ b/xtask/public-api/aya-log.txt @@ -1,6 +1,5 @@ pub mod aya_log pub enum aya_log::Error -pub aya_log::Error::AsyncFdNew(std::io::error::Error) pub aya_log::Error::MapError(aya::maps::MapError) pub aya_log::Error::MapNotFound pub aya_log::Error::ProgramError(aya::programs::ProgramError) @@ -64,34 +63,38 @@ impl core::borrow::BorrowMut for aya_log::DefaultFormatter where T: ?core: pub fn aya_log::DefaultFormatter::borrow_mut(&mut self) -> &mut T impl core::convert::From for aya_log::DefaultFormatter pub fn aya_log::DefaultFormatter::from(t: T) -> T -pub struct aya_log::EbpfLogger -impl aya_log::EbpfLogger -pub fn aya_log::EbpfLogger::init(bpf: &mut aya::bpf::Ebpf) -> core::result::Result -pub fn aya_log::EbpfLogger::init_from_id(program_id: u32) -> core::result::Result -pub fn aya_log::EbpfLogger::init_from_id_with_logger(program_id: u32, logger: T) -> core::result::Result -pub fn aya_log::EbpfLogger::init_with_logger(bpf: &mut aya::bpf::Ebpf, logger: T) -> core::result::Result -impl core::marker::Freeze for aya_log::EbpfLogger -impl core::marker::Send for aya_log::EbpfLogger -impl core::marker::Sync for aya_log::EbpfLogger -impl core::marker::Unpin for aya_log::EbpfLogger -impl core::panic::unwind_safe::RefUnwindSafe for aya_log::EbpfLogger -impl core::panic::unwind_safe::UnwindSafe for aya_log::EbpfLogger -impl core::convert::Into for aya_log::EbpfLogger where U: core::convert::From -pub fn aya_log::EbpfLogger::into(self) -> U -impl core::convert::TryFrom for aya_log::EbpfLogger where U: core::convert::Into -pub type aya_log::EbpfLogger::Error = core::convert::Infallible -pub fn aya_log::EbpfLogger::try_from(value: U) -> core::result::Result>::Error> -impl core::convert::TryInto for aya_log::EbpfLogger where U: core::convert::TryFrom -pub type aya_log::EbpfLogger::Error = >::Error -pub fn aya_log::EbpfLogger::try_into(self) -> core::result::Result>::Error> -impl core::any::Any for aya_log::EbpfLogger where T: 'static + ?core::marker::Sized -pub fn aya_log::EbpfLogger::type_id(&self) -> core::any::TypeId -impl core::borrow::Borrow for aya_log::EbpfLogger where T: ?core::marker::Sized -pub fn aya_log::EbpfLogger::borrow(&self) -> &T -impl core::borrow::BorrowMut for aya_log::EbpfLogger where T: ?core::marker::Sized -pub fn aya_log::EbpfLogger::borrow_mut(&mut self) -> &mut T -impl core::convert::From for aya_log::EbpfLogger -pub fn aya_log::EbpfLogger::from(t: T) -> T +pub struct aya_log::EbpfLogger +impl aya_log::EbpfLogger<&'static dyn log::Log> +pub fn aya_log::EbpfLogger<&'static dyn log::Log>::init(bpf: &mut aya::bpf::Ebpf) -> core::result::Result +pub fn aya_log::EbpfLogger<&'static dyn log::Log>::init_from_id(program_id: u32) -> core::result::Result +impl aya_log::EbpfLogger +pub fn aya_log::EbpfLogger::flush(&mut self) +pub fn aya_log::EbpfLogger::init_from_id_with_logger(program_id: u32, logger: T) -> core::result::Result +pub fn aya_log::EbpfLogger::init_with_logger(bpf: &mut aya::bpf::Ebpf, logger: T) -> core::result::Result +impl std::os::fd::raw::AsRawFd for aya_log::EbpfLogger +pub fn aya_log::EbpfLogger::as_raw_fd(&self) -> std::os::fd::raw::RawFd +impl core::marker::Freeze for aya_log::EbpfLogger where T: core::marker::Freeze +impl core::marker::Send for aya_log::EbpfLogger where T: core::marker::Send +impl core::marker::Sync for aya_log::EbpfLogger where T: core::marker::Sync +impl core::marker::Unpin for aya_log::EbpfLogger where T: core::marker::Unpin +impl core::panic::unwind_safe::RefUnwindSafe for aya_log::EbpfLogger where T: core::panic::unwind_safe::RefUnwindSafe +impl core::panic::unwind_safe::UnwindSafe for aya_log::EbpfLogger where T: core::panic::unwind_safe::UnwindSafe +impl core::convert::Into for aya_log::EbpfLogger where U: core::convert::From +pub fn aya_log::EbpfLogger::into(self) -> U +impl core::convert::TryFrom for aya_log::EbpfLogger where U: core::convert::Into +pub type aya_log::EbpfLogger::Error = core::convert::Infallible +pub fn aya_log::EbpfLogger::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya_log::EbpfLogger where U: core::convert::TryFrom +pub type aya_log::EbpfLogger::Error = >::Error +pub fn aya_log::EbpfLogger::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya_log::EbpfLogger where T: 'static + ?core::marker::Sized +pub fn aya_log::EbpfLogger::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya_log::EbpfLogger where T: ?core::marker::Sized +pub fn aya_log::EbpfLogger::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya_log::EbpfLogger where T: ?core::marker::Sized +pub fn aya_log::EbpfLogger::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya_log::EbpfLogger +pub fn aya_log::EbpfLogger::from(t: T) -> T pub struct aya_log::Ipv4Formatter impl aya_log::Formatter for aya_log::Ipv4Formatter where T: core::convert::Into pub fn aya_log::Ipv4Formatter::format(v: T) -> alloc::string::String @@ -312,4 +315,4 @@ impl aya_log::Formatter for aya_log::LowerHexFormatter where T: core::fmt: pub fn aya_log::LowerHexFormatter::format(v: T) -> alloc::string::String impl aya_log::Formatter for aya_log::UpperHexFormatter where T: core::fmt::UpperHex pub fn aya_log::UpperHexFormatter::format(v: T) -> alloc::string::String -pub type aya_log::BpfLogger = aya_log::EbpfLogger +pub type aya_log::BpfLogger = aya_log::EbpfLogger diff --git a/xtask/public-api/aya.txt b/xtask/public-api/aya.txt index 56dfa3cd..9fad0a43 100644 --- a/xtask/public-api/aya.txt +++ b/xtask/public-api/aya.txt @@ -608,6 +608,8 @@ pub fn aya::maps::ring_buf::RingBuf<&'a aya::maps::MapData>::try_from(map: &'a a impl<'a> core::convert::TryFrom<&'a mut aya::maps::Map> for aya::maps::ring_buf::RingBuf<&'a mut aya::maps::MapData> pub type aya::maps::ring_buf::RingBuf<&'a mut aya::maps::MapData>::Error = aya::maps::MapError pub fn aya::maps::ring_buf::RingBuf<&'a mut aya::maps::MapData>::try_from(map: &'a mut aya::maps::Map) -> core::result::Result +impl> std::os::fd::owned::AsFd for aya::maps::ring_buf::RingBuf +pub fn aya::maps::ring_buf::RingBuf::as_fd(&self) -> std::os::fd::owned::BorrowedFd<'_> impl> std::os::fd::raw::AsRawFd for aya::maps::ring_buf::RingBuf pub fn aya::maps::ring_buf::RingBuf::as_raw_fd(&self) -> std::os::fd::raw::RawFd impl core::marker::Freeze for aya::maps::ring_buf::RingBuf where T: core::marker::Freeze @@ -2220,6 +2222,8 @@ pub fn aya::maps::ring_buf::RingBuf<&'a aya::maps::MapData>::try_from(map: &'a a impl<'a> core::convert::TryFrom<&'a mut aya::maps::Map> for aya::maps::ring_buf::RingBuf<&'a mut aya::maps::MapData> pub type aya::maps::ring_buf::RingBuf<&'a mut aya::maps::MapData>::Error = aya::maps::MapError pub fn aya::maps::ring_buf::RingBuf<&'a mut aya::maps::MapData>::try_from(map: &'a mut aya::maps::Map) -> core::result::Result +impl> std::os::fd::owned::AsFd for aya::maps::ring_buf::RingBuf +pub fn aya::maps::ring_buf::RingBuf::as_fd(&self) -> std::os::fd::owned::BorrowedFd<'_> impl> std::os::fd::raw::AsRawFd for aya::maps::ring_buf::RingBuf pub fn aya::maps::ring_buf::RingBuf::as_raw_fd(&self) -> std::os::fd::raw::RawFd impl core::marker::Freeze for aya::maps::ring_buf::RingBuf where T: core::marker::Freeze