Skip to content

Commit

Permalink
Merge pull request #959 from tyrone-wu/aya/program_info_stats
Browse files Browse the repository at this point in the history
aya,aya-obj: expose run_time_ns & run_cnt fields, and add bpf_enable_stats util function
  • Loading branch information
alessandrod authored Sep 1, 2024
2 parents ab5e688 + a25f501 commit ab000ad
Show file tree
Hide file tree
Showing 6 changed files with 205 additions and 15 deletions.
25 changes: 15 additions & 10 deletions aya/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,20 +78,20 @@
)]

mod bpf;
use aya_obj::generated;
pub mod maps;
use aya_obj as obj;
pub mod pin;
pub mod programs;
pub use programs::loaded_programs;
mod sys;
pub mod sys;
pub mod util;

use std::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd, RawFd};

use aya_obj as obj;
use aya_obj::generated;
pub use bpf::*;
pub use obj::btf::{Btf, BtfError};
pub use object::Endianness;
pub use programs::loaded_programs;
#[doc(hidden)]
pub use sys::netlink_set_link_up;

Expand Down Expand Up @@ -139,6 +139,16 @@ impl MockableFd {
fd.as_ref().unwrap()
}

#[cfg(not(test))]
fn into_inner(self) -> OwnedFd {
self.fd
}

#[cfg(test)]
fn into_inner(mut self) -> OwnedFd {
self.fd.take().unwrap()
}

fn try_clone(&self) -> std::io::Result<Self> {
let fd = self.inner();
let fd = fd.try_clone()?;
Expand Down Expand Up @@ -175,13 +185,8 @@ impl FromRawFd for MockableFd {
}
}

#[cfg(test)]
impl Drop for MockableFd {
#[cfg(not(test))]
fn drop(&mut self) {
// Intentional no-op.
}

#[cfg(test)]
fn drop(&mut self) {
use std::os::fd::AsRawFd as _;

Expand Down
22 changes: 22 additions & 0 deletions aya/src/programs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1000,6 +1000,7 @@ impl_info!(
);

/// Provides information about a loaded program, like name, id and statistics
#[doc(alias = "bpf_prog_info")]
#[derive(Debug)]
pub struct ProgramInfo(bpf_prog_info);

Expand Down Expand Up @@ -1099,6 +1100,27 @@ impl ProgramInfo {
Ok(ProgramFd(fd))
}

/// The accumulated time that the program has been actively running.
///
/// This is not to be confused with the duration since the program was
/// first loaded on the host.
///
/// Note this field is only updated for as long as
/// [`enable_stats`](crate::sys::enable_stats) is enabled
/// with [`Stats::RunTime`](crate::sys::Stats::RunTime).
pub fn run_time(&self) -> Duration {
Duration::from_nanos(self.0.run_time_ns)
}

/// The accumulated execution count of the program.
///
/// Note this field is only updated for as long as
/// [`enable_stats`](crate::sys::enable_stats) is enabled
/// with [`Stats::RunTime`](crate::sys::Stats::RunTime).
pub fn run_count(&self) -> u64 {
self.0.run_cnt
}

/// Loads a program from a pinned path in bpffs.
pub fn from_pin<P: AsRef<Path>>(path: P) -> Result<Self, ProgramError> {
use std::os::unix::ffi::OsStrExt as _;
Expand Down
17 changes: 17 additions & 0 deletions aya/src/sys/bpf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use assert_matches::assert_matches;
use libc::{ENOENT, ENOSPC};
use obj::{
btf::{BtfEnum64, Enum64},
generated::bpf_stats_type,
maps::{bpf_map_def, LegacyMap},
EbpfSectionKind, VerifierLog,
};
Expand Down Expand Up @@ -1104,6 +1105,22 @@ pub(crate) fn iter_map_ids() -> impl Iterator<Item = Result<u32, SyscallError>>
iter_obj_ids(bpf_cmd::BPF_MAP_GET_NEXT_ID, "bpf_map_get_next_id")
}

/// Introduced in kernel v5.8.
pub(crate) fn bpf_enable_stats(
stats_type: bpf_stats_type,
) -> Result<crate::MockableFd, SyscallError> {
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
attr.enable_stats.type_ = stats_type as u32;

// SAFETY: BPF_ENABLE_STATS returns a new file descriptor.
unsafe { fd_sys_bpf(bpf_cmd::BPF_ENABLE_STATS, &mut attr) }.map_err(|(_, io_error)| {
SyscallError {
call: "bpf_enable_stats",
io_error,
}
})
}

pub(crate) fn retry_with_verifier_logs<T>(
max_retries: usize,
f: impl Fn(&mut [u8]) -> SysResult<T>,
Expand Down
56 changes: 55 additions & 1 deletion aya/src/sys/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! A collection of system calls for performing eBPF related operations.

mod bpf;
mod netlink;
mod perf_event;
Expand All @@ -8,7 +10,7 @@ mod fake;
use std::{
ffi::{c_int, c_void},
io, mem,
os::fd::{AsRawFd as _, BorrowedFd},
os::fd::{AsRawFd as _, BorrowedFd, OwnedFd},
};

pub(crate) use bpf::*;
Expand Down Expand Up @@ -44,6 +46,7 @@ pub(crate) enum Syscall<'a> {
},
}

/// A system call error.
#[derive(Debug, Error)]
#[error("`{call}` failed")]
pub struct SyscallError {
Expand Down Expand Up @@ -137,3 +140,54 @@ pub(crate) unsafe fn mmap(
#[cfg(test)]
TEST_MMAP_RET.with(|ret| *ret.borrow())
}

/// The type of eBPF statistic to enable.
#[non_exhaustive]
#[doc(alias = "bpf_stats_type")]
#[derive(Copy, Clone, Debug)]
pub enum Stats {
/// Tracks [`run_time`](crate::programs::ProgramInfo::run_time) and
/// [`run_count`](crate::programs::ProgramInfo::run_count) fields.
#[doc(alias = "BPF_STATS_RUN_TIME")]
RunTime,
}

impl From<Stats> for crate::generated::bpf_stats_type {
fn from(value: Stats) -> Self {
use crate::generated::bpf_stats_type::*;

match value {
Stats::RunTime => BPF_STATS_RUN_TIME,
}
}
}

/// Enable global statistics tracking for eBPF programs and returns a
/// [file descriptor](`OwnedFd`) handler.
///
/// Statistics tracking is disabled when the [file descriptor](`OwnedFd`) is
/// dropped (either automatically when the variable goes out of scope or
/// manually through [`Drop`]).
///
/// Usage:
/// 1. Obtain fd from [`enable_stats`] and bind it to a variable.
/// 2. Record the statistic of interest.
/// 3. Wait for a recorded period of time.
/// 4. Record the statistic of interest again, and calculate the difference.
/// 5. Close/release fd automatically or manually.
///
/// Introduced in kernel v5.8.
///
/// # Examples
///
/// ```no_run
/// # use aya::sys::{SyscallError};
/// use aya::sys::{enable_stats, Stats};
///
/// let _fd = enable_stats(Stats::RunTime)?;
/// # Ok::<(), SyscallError>(())
/// ```
#[doc(alias = "BPF_ENABLE_STATS")]
pub fn enable_stats(stats_type: Stats) -> Result<OwnedFd, SyscallError> {
bpf_enable_stats(stats_type.into()).map(|fd| fd.into_inner())
}
2 changes: 2 additions & 0 deletions test/integration-test/src/tests/smoke.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ fn list_loaded_programs() {
prog.verified_instruction_count();
prog.loaded_at();
prog.fd().unwrap();
prog.run_time();
prog.run_count();
}

#[test]
Expand Down
98 changes: 94 additions & 4 deletions xtask/public-api/aya.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1360,7 +1360,7 @@ pub aya::maps::MapError::PinError::error: aya::pin::PinError
pub aya::maps::MapError::PinError::name: core::option::Option<alloc::string::String>
pub aya::maps::MapError::ProgIdNotSupported
pub aya::maps::MapError::ProgramNotLoaded
pub aya::maps::MapError::SyscallError(crate::sys::SyscallError)
pub aya::maps::MapError::SyscallError(aya::sys::SyscallError)
pub aya::maps::MapError::Unsupported
pub aya::maps::MapError::Unsupported::map_type: u32
impl core::convert::From<aya::maps::MapError> for aya::EbpfError
Expand All @@ -1369,6 +1369,8 @@ impl core::convert::From<aya::maps::MapError> for aya::maps::xdp::XdpMapError
pub fn aya::maps::xdp::XdpMapError::from(source: aya::maps::MapError) -> Self
impl core::convert::From<aya::maps::MapError> for aya::programs::ProgramError
pub fn aya::programs::ProgramError::from(source: aya::maps::MapError) -> Self
impl core::convert::From<aya::sys::SyscallError> for aya::maps::MapError
pub fn aya::maps::MapError::from(source: aya::sys::SyscallError) -> Self
impl core::convert::From<aya_obj::maps::InvalidMapTypeError> for aya::maps::MapError
pub fn aya::maps::MapError::from(e: aya_obj::maps::InvalidMapTypeError) -> Self
impl core::convert::From<std::io::error::Error> for aya::maps::MapError
Expand Down Expand Up @@ -2408,7 +2410,9 @@ pub aya::pin::PinError::InvalidPinPath::error: alloc::ffi::c_str::NulError
pub aya::pin::PinError::InvalidPinPath::path: std::path::PathBuf
pub aya::pin::PinError::NoFd
pub aya::pin::PinError::NoFd::name: alloc::string::String
pub aya::pin::PinError::SyscallError(crate::sys::SyscallError)
pub aya::pin::PinError::SyscallError(aya::sys::SyscallError)
impl core::convert::From<aya::sys::SyscallError> for aya::pin::PinError
pub fn aya::pin::PinError::from(source: aya::sys::SyscallError) -> Self
impl core::error::Error for aya::pin::PinError
pub fn aya::pin::PinError::source(&self) -> core::option::Option<&(dyn core::error::Error + 'static)>
impl core::fmt::Debug for aya::pin::PinError
Expand Down Expand Up @@ -3731,7 +3735,9 @@ pub fn aya::programs::kprobe::KProbeLinkId::from(t: T) -> T
pub mod aya::programs::links
pub enum aya::programs::links::LinkError
pub aya::programs::links::LinkError::InvalidLink
pub aya::programs::links::LinkError::SyscallError(crate::sys::SyscallError)
pub aya::programs::links::LinkError::SyscallError(aya::sys::SyscallError)
impl core::convert::From<aya::sys::SyscallError> for aya::programs::links::LinkError
pub fn aya::programs::links::LinkError::from(source: aya::sys::SyscallError) -> Self
impl core::error::Error for aya::programs::links::LinkError
pub fn aya::programs::links::LinkError::source(&self) -> core::option::Option<&(dyn core::error::Error + 'static)>
impl core::fmt::Debug for aya::programs::links::LinkError
Expand Down Expand Up @@ -6824,7 +6830,7 @@ pub aya::programs::ProgramError::MapError(aya::maps::MapError)
pub aya::programs::ProgramError::NotAttached
pub aya::programs::ProgramError::NotLoaded
pub aya::programs::ProgramError::SocketFilterError(aya::programs::socket_filter::SocketFilterError)
pub aya::programs::ProgramError::SyscallError(crate::sys::SyscallError)
pub aya::programs::ProgramError::SyscallError(aya::sys::SyscallError)
pub aya::programs::ProgramError::TcError(aya::programs::tc::TcError)
pub aya::programs::ProgramError::TracePointError(aya::programs::trace_point::TracePointError)
pub aya::programs::ProgramError::UProbeError(aya::programs::uprobe::UProbeError)
Expand All @@ -6850,6 +6856,8 @@ impl core::convert::From<aya::programs::uprobe::UProbeError> for aya::programs::
pub fn aya::programs::ProgramError::from(source: aya::programs::uprobe::UProbeError) -> Self
impl core::convert::From<aya::programs::xdp::XdpError> for aya::programs::ProgramError
pub fn aya::programs::ProgramError::from(source: aya::programs::xdp::XdpError) -> Self
impl core::convert::From<aya::sys::SyscallError> for aya::programs::ProgramError
pub fn aya::programs::ProgramError::from(source: aya::sys::SyscallError) -> Self
impl core::convert::From<aya_obj::btf::btf::BtfError> for aya::programs::ProgramError
pub fn aya::programs::ProgramError::from(source: aya_obj::btf::btf::BtfError) -> Self
impl core::convert::From<std::io::error::Error> for aya::programs::ProgramError
Expand Down Expand Up @@ -7915,6 +7923,8 @@ pub fn aya::programs::ProgramInfo::memory_locked(&self) -> core::result::Result<
pub fn aya::programs::ProgramInfo::name(&self) -> &[u8]
pub fn aya::programs::ProgramInfo::name_as_str(&self) -> core::option::Option<&str>
pub fn aya::programs::ProgramInfo::program_type(&self) -> u32
pub fn aya::programs::ProgramInfo::run_count(&self) -> u64
pub fn aya::programs::ProgramInfo::run_time(&self) -> core::time::Duration
pub fn aya::programs::ProgramInfo::size_jitted(&self) -> u32
pub fn aya::programs::ProgramInfo::size_translated(&self) -> u32
pub fn aya::programs::ProgramInfo::tag(&self) -> u64
Expand Down Expand Up @@ -8660,6 +8670,86 @@ pub type aya::programs::xdp::XdpLink::Id = aya::programs::xdp::XdpLinkId
pub fn aya::programs::xdp::XdpLink::detach(self) -> core::result::Result<(), aya::programs::ProgramError>
pub fn aya::programs::xdp::XdpLink::id(&self) -> Self::Id
pub fn aya::programs::loaded_programs() -> impl core::iter::traits::iterator::Iterator<Item = core::result::Result<aya::programs::ProgramInfo, aya::programs::ProgramError>>
pub mod aya::sys
#[non_exhaustive] pub enum aya::sys::Stats
pub aya::sys::Stats::RunTime
impl core::clone::Clone for aya::sys::Stats
pub fn aya::sys::Stats::clone(&self) -> aya::sys::Stats
impl core::convert::From<aya::sys::Stats> for aya_obj::generated::linux_bindings_x86_64::bpf_stats_type
pub fn aya_obj::generated::linux_bindings_x86_64::bpf_stats_type::from(value: aya::sys::Stats) -> Self
impl core::fmt::Debug for aya::sys::Stats
pub fn aya::sys::Stats::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
impl core::marker::Copy for aya::sys::Stats
impl core::marker::Freeze for aya::sys::Stats
impl core::marker::Send for aya::sys::Stats
impl core::marker::Sync for aya::sys::Stats
impl core::marker::Unpin for aya::sys::Stats
impl core::panic::unwind_safe::RefUnwindSafe for aya::sys::Stats
impl core::panic::unwind_safe::UnwindSafe for aya::sys::Stats
impl<T, U> core::convert::Into<U> for aya::sys::Stats where U: core::convert::From<T>
pub fn aya::sys::Stats::into(self) -> U
impl<T, U> core::convert::TryFrom<U> for aya::sys::Stats where U: core::convert::Into<T>
pub type aya::sys::Stats::Error = core::convert::Infallible
pub fn aya::sys::Stats::try_from(value: U) -> core::result::Result<T, <T as core::convert::TryFrom<U>>::Error>
impl<T, U> core::convert::TryInto<U> for aya::sys::Stats where U: core::convert::TryFrom<T>
pub type aya::sys::Stats::Error = <U as core::convert::TryFrom<T>>::Error
pub fn aya::sys::Stats::try_into(self) -> core::result::Result<U, <U as core::convert::TryFrom<T>>::Error>
impl<T> alloc::borrow::ToOwned for aya::sys::Stats where T: core::clone::Clone
pub type aya::sys::Stats::Owned = T
pub fn aya::sys::Stats::clone_into(&self, target: &mut T)
pub fn aya::sys::Stats::to_owned(&self) -> T
impl<T> core::any::Any for aya::sys::Stats where T: 'static + core::marker::Sized
pub fn aya::sys::Stats::type_id(&self) -> core::any::TypeId
impl<T> core::borrow::Borrow<T> for aya::sys::Stats where T: core::marker::Sized
pub fn aya::sys::Stats::borrow(&self) -> &T
impl<T> core::borrow::BorrowMut<T> for aya::sys::Stats where T: core::marker::Sized
pub fn aya::sys::Stats::borrow_mut(&mut self) -> &mut T
impl<T> core::clone::CloneToUninit for aya::sys::Stats where T: core::clone::Clone
pub unsafe fn aya::sys::Stats::clone_to_uninit(&self, dst: *mut T)
impl<T> core::convert::From<T> for aya::sys::Stats
pub fn aya::sys::Stats::from(t: T) -> T
pub struct aya::sys::SyscallError
pub aya::sys::SyscallError::call: &'static str
pub aya::sys::SyscallError::io_error: std::io::error::Error
impl core::convert::From<aya::sys::SyscallError> for aya::maps::MapError
pub fn aya::maps::MapError::from(source: aya::sys::SyscallError) -> Self
impl core::convert::From<aya::sys::SyscallError> for aya::pin::PinError
pub fn aya::pin::PinError::from(source: aya::sys::SyscallError) -> Self
impl core::convert::From<aya::sys::SyscallError> for aya::programs::ProgramError
pub fn aya::programs::ProgramError::from(source: aya::sys::SyscallError) -> Self
impl core::convert::From<aya::sys::SyscallError> for aya::programs::links::LinkError
pub fn aya::programs::links::LinkError::from(source: aya::sys::SyscallError) -> Self
impl core::error::Error for aya::sys::SyscallError
pub fn aya::sys::SyscallError::source(&self) -> core::option::Option<&(dyn core::error::Error + 'static)>
impl core::fmt::Debug for aya::sys::SyscallError
pub fn aya::sys::SyscallError::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
impl core::fmt::Display for aya::sys::SyscallError
pub fn aya::sys::SyscallError::fmt(&self, __formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
impl core::marker::Freeze for aya::sys::SyscallError
impl core::marker::Send for aya::sys::SyscallError
impl core::marker::Sync for aya::sys::SyscallError
impl core::marker::Unpin for aya::sys::SyscallError
impl !core::panic::unwind_safe::RefUnwindSafe for aya::sys::SyscallError
impl !core::panic::unwind_safe::UnwindSafe for aya::sys::SyscallError
impl<T, U> core::convert::Into<U> for aya::sys::SyscallError where U: core::convert::From<T>
pub fn aya::sys::SyscallError::into(self) -> U
impl<T, U> core::convert::TryFrom<U> for aya::sys::SyscallError where U: core::convert::Into<T>
pub type aya::sys::SyscallError::Error = core::convert::Infallible
pub fn aya::sys::SyscallError::try_from(value: U) -> core::result::Result<T, <T as core::convert::TryFrom<U>>::Error>
impl<T, U> core::convert::TryInto<U> for aya::sys::SyscallError where U: core::convert::TryFrom<T>
pub type aya::sys::SyscallError::Error = <U as core::convert::TryFrom<T>>::Error
pub fn aya::sys::SyscallError::try_into(self) -> core::result::Result<U, <U as core::convert::TryFrom<T>>::Error>
impl<T> alloc::string::ToString for aya::sys::SyscallError where T: core::fmt::Display + core::marker::Sized
pub fn aya::sys::SyscallError::to_string(&self) -> alloc::string::String
impl<T> core::any::Any for aya::sys::SyscallError where T: 'static + core::marker::Sized
pub fn aya::sys::SyscallError::type_id(&self) -> core::any::TypeId
impl<T> core::borrow::Borrow<T> for aya::sys::SyscallError where T: core::marker::Sized
pub fn aya::sys::SyscallError::borrow(&self) -> &T
impl<T> core::borrow::BorrowMut<T> for aya::sys::SyscallError where T: core::marker::Sized
pub fn aya::sys::SyscallError::borrow_mut(&mut self) -> &mut T
impl<T> core::convert::From<T> for aya::sys::SyscallError
pub fn aya::sys::SyscallError::from(t: T) -> T
pub fn aya::sys::enable_stats(stats_type: aya::sys::Stats) -> core::result::Result<std::os::fd::owned::OwnedFd, aya::sys::SyscallError>
pub mod aya::util
pub struct aya::util::KernelVersion
impl aya::util::KernelVersion
Expand Down

0 comments on commit ab000ad

Please sign in to comment.