diff --git a/aya-bpf-macros/src/lib.rs b/aya-bpf-macros/src/lib.rs index 2f1a2f0d..8a353ed7 100644 --- a/aya-bpf-macros/src/lib.rs +++ b/aya-bpf-macros/src/lib.rs @@ -521,7 +521,9 @@ pub fn sk_lookup(attrs: TokenStream, item: TokenStream) -> TokenStream { /// use aya_bpf::{macros::usdt, programs::UsdtContext}; /// /// #[usdt] -/// pub fn tick(_ctx: UsdtContext) -> u32 { +/// pub fn tick(ctx: UsdtContext) -> u32 { +/// let arg = ctx.arg(0); +/// // Use aya-log to print the value to userspace /// return 0 /// } /// ``` diff --git a/aya-common/src/lib.rs b/aya-common/src/lib.rs index c1f9e692..7adbe24f 100644 --- a/aya-common/src/lib.rs +++ b/aya-common/src/lib.rs @@ -4,13 +4,16 @@ pub const USDT_MAX_SPEC_COUNT: u32 = 256; pub const USDT_MAX_IP_COUNT: u32 = 4 * USDT_MAX_SPEC_COUNT; pub const USDT_MAX_ARG_COUNT: usize = 12; -/// The type of argument in a USDT program +/// The type of argument in a USDT program. #[repr(u32)] #[derive(Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "user", derive(Debug))] pub enum UsdtArgType { + /// Value is Constant. Const, + /// Value is stored in a Register. Reg, + /// Value is stored in a Register and requires dereferencing. RegDeref, } @@ -20,31 +23,37 @@ impl Default for UsdtArgType { } } -/// The specifcation of an argument in a USDT program +/// The specifcation of an argument in a USDT program. #[repr(C)] #[derive(Copy, Clone, Default, PartialEq, Eq)] #[cfg_attr(feature = "user", derive(Debug))] pub struct UsdtArgSpec { - /// scalar interpreted depending on arg_type + /// Meaning of val_off differs based on `arg_type`. + /// If Constant, this holds the scalar value of unknow type, up to u64 in size. + /// If RegDeref, this contains an offset which is an i64. pub val_off: u64, - /// arg location case + /// Type of Argument. pub arg_type: UsdtArgType, - /// offset of referenced register within struct pt_regs + /// Offset of the register within the BPF context pub reg_off: i16, - /// whether arg should be interpreted as signed value + /// Whether the value should be interpreted as signed pub arg_signed: bool, - /// number of bits that need to be cleared and, optionally, + /// Number of bits that need to be cleared and, optionally, /// sign-extended to cast arguments that are 1, 2, or 4 bytes - /// long into final 8-byte u64/s64 value returned to user + /// long into final 8-byte u64/s64 value returned to user. pub arg_bitshift: i8, } +/// The specification of a USDT #[repr(C)] #[derive(Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "user", derive(Debug))] pub struct UsdtSpec { + /// Specification used to access arguments. pub args: [UsdtArgSpec; USDT_MAX_ARG_COUNT], + /// User supplied cookie since the BPF Attach Cookie is used internally. pub cookie: u64, + /// Number of args in this tracepoint pub arg_count: i16, } diff --git a/aya/src/programs/usdt.rs b/aya/src/programs/usdt.rs index aef69ab1..a933bbb6 100644 --- a/aya/src/programs/usdt.rs +++ b/aya/src/programs/usdt.rs @@ -36,7 +36,50 @@ pub const USDT_SPEC_MAP: &str = "__bpf_usdt_specs"; /// Name of the map used for USDT to IP mappings. pub const USDT_IP_TO_SPEC_MAP: &str = "__bpf_usdt_ip_to_spec_id"; -/// A user statically-defined tracepoint +/// A user statically-defined tracepoint program. +/// +/// USDT programs are the fastest of the userspace tracing programs and can be +/// used to trace events instrumented libraries or binaries. Unlike uprobes and +/// uretprobes that have access to all CPU registers, USDTs provide a structured +/// specification for accessing the arguments for each tracepoint. When compliled +/// a single tracepoint may have mutliple different entrypoints in the same program. +/// In order to simply access to arguments from eBPF, Aya keeps state in 2 maps: +/// +/// - [`USDT_SPEC_MAP`] which keeps track of USDT specifications +/// - [`USDT_IP_TO_SPEC_MAP`] which keeps track of Instructio Pointers to USDT specs. +/// +/// The last map is not used on kernels which support the BPF Attach Cookie feature. +/// +/// # Minimum kernel version +/// +/// While support was added to the kenel in 4.19, Aya depends on a feature that +/// allows the kernel to manage semaphore reference counting which was added in +/// 4.20. +/// +/// The minimum supported kernel version is 4.20. +/// +/// # Examples +/// +/// ```no_run +/// # let mut bpf = Bpf::load_file("ebpf_programs.o")?; +/// use aya::{Bpf, programs::{Usdt, usdt::{USDT_SPEC_MAP, USDT_IP_TO_SPEC_MAP}}}; +/// use aya::maps::{Array, HashMap}; +/// use std::convert::TryInto; +/// +/// let spec_map = Array::try_from(bpf.map_mut(USDT_SPEC_MAP).unwrap())?; +/// let ip_to_spec_map = HashMap::try_from(bpf.map_mut(USDT_IP_TO_SPEC_MAP).unwrap())?; +/// let program: &mut Usdt = bpf.program_mut("usdt").unwrap().try_into()?; +/// program.load()?; +/// program.attach( +/// spec_map, +/// ip_to_spec_map, +/// "clock", +/// "loop", +/// "/path/to/target/debug/clock", +/// Some(12345), +/// )?; +/// # Ok::<(), aya::BpfError>(()) +/// ``` #[derive(Debug)] #[doc(alias = "BPF_PROG_TYPE_KPROBE")] pub struct Usdt { @@ -51,13 +94,19 @@ impl Usdt { /// Attaches the program. /// - /// Attaches the uprobe to the tracepoint `tp_provider`/`tp_name` defined in the `target`. + /// Attaches the USDT to the tracepoint with a matching `tp_provider` and `tp_name` + /// in the `target`. + /// /// If `pid` is not `None`, the program executes only when the target - /// function is executed by the given `pid`. + /// function is executed by the given `pid`. This is only supported in kernels which + /// provide the BPF Attach Cookie feature. /// /// The `target` argument can be an absolute path to a binary or library, or /// a library name (eg: `"libc"`). /// + /// Since there a single tracepoint can have multiple entrypoints, a single `UsdtLinkId` + /// may be comprised of multiple links. + /// /// The returned value can be used to detach, see [Usdt::detach]. pub fn attach>( &mut self, @@ -158,11 +207,11 @@ impl Usdt { /// The identifer of a MultiPerfLink. #[derive(Debug, Hash, Eq, PartialEq)] -pub struct MultiPerfLinkId(Vec); +pub(crate) struct MultiPerfLinkId(Vec); -/// The attachment type of USDT programs. +// A wrapper around multiple PerfLinkInner #[derive(Debug)] -pub struct MultiPerfLink { +pub(crate) struct MultiPerfLink { perf_links: Vec, } @@ -191,7 +240,7 @@ define_link_wrapper!( MultiPerfLinkId ); -/// The type returned when attaching an [`UProbe`] fails. +/// The type returned when attaching a [`Usdt`] fails. #[derive(Debug, Error)] pub enum UsdtError { /// There was an error parsing `/etc/ld.so.cache`. @@ -384,6 +433,7 @@ fn find_segment_by_address>( }) } +// A resolved Usdt target. #[derive(Debug)] pub(crate) struct UsdtTarget { abs_ip: u64, @@ -393,6 +443,7 @@ pub(crate) struct UsdtTarget { spec: UsdtSpec, } +// A parsed note from an ELF stapsdt note. #[derive(Debug)] pub(crate) struct UsdtNote { loc_addr: u64, diff --git a/bpf/aya-bpf/src/programs/usdt.rs b/bpf/aya-bpf/src/programs/usdt.rs index 8cf136b1..4c742d04 100644 --- a/bpf/aya-bpf/src/programs/usdt.rs +++ b/bpf/aya-bpf/src/programs/usdt.rs @@ -29,23 +29,27 @@ pub struct UsdtContext { } impl UsdtContext { + /// Creates a new Usdtcontext. pub fn new(ctx: *mut c_void) -> UsdtContext { UsdtContext { regs: ctx as *mut pt_regs, } } + /// Access the register that holds the next instruction pointer. #[inline(always)] fn ip(&self) -> Option { T::from_ip(unsafe { &*self.regs }) } + /// Access the spec_id from the BPF Attach Cookie. #[cfg(feature = "cookie")] #[inline(always)] fn spec_id(&self) -> Result { unsafe { Ok(aya_bpf_bindings::helpers::bpf_get_attach_cookie(self.as_ptr()) as u32) } } + /// Access the spec_id using the `USDT_IP_TO_SPEC_ID` map #[cfg(not(feature = "cookie"))] #[inline(always)] fn spec_id(&self) -> Result { @@ -54,6 +58,10 @@ impl UsdtContext { Ok(*spec) } + /// Returns the value of the USDT argument `n` as a u64. + /// + /// This uses the USDT_SPEC_MAP to determine the correct specification to use in order + /// to read the value of argument `n` from the eBPF Context. #[inline(always)] pub fn arg(&self, n: usize) -> Result { if n > USDT_MAX_ARG_COUNT {