Merge pull request #522 from dave-tucker/perf_link

Use bpf_link for perf_attach programs (Kprobe/Uprobe/Tracepoint etc...)
pull/523/head
Alessandro Decina 2 years ago committed by GitHub
commit d7d6442671
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -35,8 +35,8 @@ use crate::{
sys::{ sys::{
bpf_load_btf, bpf_map_freeze, bpf_map_update_elem_ptr, is_btf_datasec_supported, bpf_load_btf, bpf_map_freeze, bpf_map_update_elem_ptr, is_btf_datasec_supported,
is_btf_decl_tag_supported, is_btf_float_supported, is_btf_func_global_supported, is_btf_decl_tag_supported, is_btf_float_supported, is_btf_func_global_supported,
is_btf_func_supported, is_btf_supported, is_btf_type_tag_supported, is_prog_name_supported, is_btf_func_supported, is_btf_supported, is_btf_type_tag_supported, is_perf_link_supported,
retry_with_verifier_logs, is_prog_name_supported, retry_with_verifier_logs,
}, },
util::{bytes_of, possible_cpus, VerifierLog, POSSIBLE_CPUS}, util::{bytes_of, possible_cpus, VerifierLog, POSSIBLE_CPUS},
}; };
@ -65,50 +65,39 @@ unsafe impl<T: Pod, const N: usize> Pod for [T; N] {}
pub use aya_obj::maps::{bpf_map_def, PinningType}; pub use aya_obj::maps::{bpf_map_def, PinningType};
lazy_static! {
pub(crate) static ref FEATURES: Features = Features::new();
}
// Features implements BPF and BTF feature detection // Features implements BPF and BTF feature detection
#[derive(Default, Debug)] #[derive(Default, Debug)]
pub(crate) struct Features { pub(crate) struct Features {
pub bpf_name: bool, pub bpf_name: bool,
pub bpf_perf_link: bool,
pub btf: Option<BtfFeatures>, pub btf: Option<BtfFeatures>,
} }
impl Features { impl Features {
fn probe_features(&mut self) { fn new() -> Self {
self.bpf_name = is_prog_name_supported(); let btf = if is_btf_supported() {
debug!("[FEAT PROBE] BPF program name support: {}", self.bpf_name); Some(BtfFeatures {
btf_func: is_btf_func_supported(),
self.btf = if is_btf_supported() { btf_func_global: is_btf_func_global_supported(),
Some(BtfFeatures::default()) btf_datasec: is_btf_datasec_supported(),
btf_float: is_btf_float_supported(),
btf_decl_tag: is_btf_decl_tag_supported(),
btf_type_tag: is_btf_type_tag_supported(),
})
} else { } else {
None None
}; };
debug!("[FEAT PROBE] BTF support: {}", self.btf.is_some()); let f = Features {
bpf_name: is_prog_name_supported(),
if let Some(ref mut btf) = self.btf { bpf_perf_link: is_perf_link_supported(),
btf.btf_func = is_btf_func_supported(); btf,
debug!("[FEAT PROBE] BTF func support: {}", btf.btf_func); };
debug!("BPF Feature Detection: {:#?}", f);
btf.btf_func_global = is_btf_func_global_supported(); f
debug!(
"[FEAT PROBE] BTF global func support: {}",
btf.btf_func_global
);
btf.btf_datasec = is_btf_datasec_supported();
debug!(
"[FEAT PROBE] BTF var and datasec support: {}",
btf.btf_datasec
);
btf.btf_float = is_btf_float_supported();
debug!("[FEAT PROBE] BTF float support: {}", btf.btf_float);
btf.btf_decl_tag = is_btf_decl_tag_supported();
debug!("[FEAT PROBE] BTF decl_tag support: {}", btf.btf_decl_tag);
btf.btf_type_tag = is_btf_type_tag_supported();
debug!("[FEAT PROBE] BTF type_tag support: {}", btf.btf_type_tag);
}
} }
} }
@ -139,7 +128,6 @@ pub struct BpfLoader<'a> {
map_pin_path: Option<PathBuf>, map_pin_path: Option<PathBuf>,
globals: HashMap<&'a str, &'a [u8]>, globals: HashMap<&'a str, &'a [u8]>,
max_entries: HashMap<&'a str, u32>, max_entries: HashMap<&'a str, u32>,
features: Features,
extensions: HashSet<&'a str>, extensions: HashSet<&'a str>,
verifier_log_level: VerifierLogLevel, verifier_log_level: VerifierLogLevel,
} }
@ -169,14 +157,11 @@ impl Default for VerifierLogLevel {
impl<'a> BpfLoader<'a> { impl<'a> BpfLoader<'a> {
/// Creates a new loader instance. /// Creates a new loader instance.
pub fn new() -> BpfLoader<'a> { pub fn new() -> BpfLoader<'a> {
let mut features = Features::default();
features.probe_features();
BpfLoader { BpfLoader {
btf: Btf::from_sys_fs().ok().map(Cow::Owned), btf: Btf::from_sys_fs().ok().map(Cow::Owned),
map_pin_path: None, map_pin_path: None,
globals: HashMap::new(), globals: HashMap::new(),
max_entries: HashMap::new(), max_entries: HashMap::new(),
features,
extensions: HashSet::new(), extensions: HashSet::new(),
verifier_log_level: VerifierLogLevel::default(), verifier_log_level: VerifierLogLevel::default(),
} }
@ -360,8 +345,8 @@ impl<'a> BpfLoader<'a> {
let mut obj = Object::parse(data)?; let mut obj = Object::parse(data)?;
obj.patch_map_data(self.globals.clone())?; obj.patch_map_data(self.globals.clone())?;
let btf_fd = if let Some(ref btf) = self.features.btf { let btf_fd = if let Some(features) = &FEATURES.btf {
if let Some(btf) = obj.fixup_and_sanitize_btf(btf)? { if let Some(btf) = obj.fixup_and_sanitize_btf(features)? {
// load btf to the kernel // load btf to the kernel
Some(load_btf(btf.to_bytes())?) Some(load_btf(btf.to_bytes())?)
} else { } else {
@ -449,7 +434,7 @@ impl<'a> BpfLoader<'a> {
.programs .programs
.drain() .drain()
.map(|(name, obj)| { .map(|(name, obj)| {
let prog_name = if self.features.bpf_name { let prog_name = if FEATURES.bpf_name {
Some(name.clone()) Some(name.clone())
} else { } else {
None None

@ -6,7 +6,7 @@ use crate::{
generated::bpf_prog_type::BPF_PROG_TYPE_KPROBE, generated::bpf_prog_type::BPF_PROG_TYPE_KPROBE,
programs::{ programs::{
define_link_wrapper, load_program, define_link_wrapper, load_program,
perf_attach::{PerfLink, PerfLinkId}, perf_attach::{PerfLinkIdInner, PerfLinkInner},
probe::{attach, ProbeKind}, probe::{attach, ProbeKind},
ProgramData, ProgramError, ProgramData, ProgramError,
}, },
@ -101,8 +101,8 @@ define_link_wrapper!(
KProbeLink, KProbeLink,
/// The type returned by [KProbe::attach]. Can be passed to [KProbe::detach]. /// The type returned by [KProbe::attach]. Can be passed to [KProbe::detach].
KProbeLinkId, KProbeLinkId,
PerfLink, PerfLinkInner,
PerfLinkId PerfLinkIdInner
); );
/// The type returned when attaching a [`KProbe`] fails. /// The type returned when attaching a [`KProbe`] fails.

@ -3,11 +3,42 @@ use libc::close;
use std::os::unix::io::RawFd; use std::os::unix::io::RawFd;
use crate::{ use crate::{
programs::{probe::detach_debug_fs, Link, ProbeKind, ProgramData, ProgramError}, generated::bpf_attach_type::BPF_PERF_EVENT,
sys::perf_event_ioctl, programs::{probe::detach_debug_fs, FdLink, Link, ProbeKind, ProgramError},
PERF_EVENT_IOC_DISABLE, PERF_EVENT_IOC_ENABLE, PERF_EVENT_IOC_SET_BPF, sys::{bpf_link_create, perf_event_ioctl},
FEATURES, PERF_EVENT_IOC_DISABLE, PERF_EVENT_IOC_ENABLE, PERF_EVENT_IOC_SET_BPF,
}; };
#[derive(Debug, Hash, Eq, PartialEq)]
pub(crate) enum PerfLinkIdInner {
FdLinkId(<FdLink as Link>::Id),
PerfLinkId(<PerfLink as Link>::Id),
}
#[derive(Debug)]
pub(crate) enum PerfLinkInner {
FdLink(FdLink),
PerfLink(PerfLink),
}
impl Link for PerfLinkInner {
type Id = PerfLinkIdInner;
fn id(&self) -> Self::Id {
match self {
PerfLinkInner::FdLink(link) => PerfLinkIdInner::FdLinkId(link.id()),
PerfLinkInner::PerfLink(link) => PerfLinkIdInner::PerfLinkId(link.id()),
}
}
fn detach(self) -> Result<(), ProgramError> {
match self {
PerfLinkInner::FdLink(link) => link.detach(),
PerfLinkInner::PerfLink(link) => link.detach(),
}
}
}
/// The identifer of a PerfLink. /// The identifer of a PerfLink.
#[derive(Debug, Hash, Eq, PartialEq)] #[derive(Debug, Hash, Eq, PartialEq)]
pub struct PerfLinkId(RawFd); pub struct PerfLinkId(RawFd);
@ -41,29 +72,36 @@ impl Link for PerfLink {
} }
} }
pub(crate) fn perf_attach<T: Link + From<PerfLink>>( pub(crate) fn perf_attach(prog_fd: RawFd, fd: RawFd) -> Result<PerfLinkInner, ProgramError> {
data: &mut ProgramData<T>, if FEATURES.bpf_perf_link {
fd: RawFd, let link_fd =
) -> Result<T::Id, ProgramError> { bpf_link_create(prog_fd, fd, BPF_PERF_EVENT, None, 0).map_err(|(_, io_error)| {
perf_attach_either(data, fd, None, None) ProgramError::SyscallError {
call: "bpf_link_create".to_owned(),
io_error,
}
})? as RawFd;
Ok(PerfLinkInner::FdLink(FdLink::new(link_fd)))
} else {
perf_attach_either(prog_fd, fd, None, None)
}
} }
pub(crate) fn perf_attach_debugfs<T: Link + From<PerfLink>>( pub(crate) fn perf_attach_debugfs(
data: &mut ProgramData<T>, prog_fd: RawFd,
fd: RawFd, fd: RawFd,
probe_kind: ProbeKind, probe_kind: ProbeKind,
event_alias: String, event_alias: String,
) -> Result<T::Id, ProgramError> { ) -> Result<PerfLinkInner, ProgramError> {
perf_attach_either(data, fd, Some(probe_kind), Some(event_alias)) perf_attach_either(prog_fd, fd, Some(probe_kind), Some(event_alias))
} }
fn perf_attach_either<T: Link + From<PerfLink>>( fn perf_attach_either(
data: &mut ProgramData<T>, prog_fd: RawFd,
fd: RawFd, fd: RawFd,
probe_kind: Option<ProbeKind>, probe_kind: Option<ProbeKind>,
event_alias: Option<String>, event_alias: Option<String>,
) -> Result<T::Id, ProgramError> { ) -> Result<PerfLinkInner, ProgramError> {
let prog_fd = data.fd_or_err()?;
perf_event_ioctl(fd, PERF_EVENT_IOC_SET_BPF, prog_fd).map_err(|(_, io_error)| { perf_event_ioctl(fd, PERF_EVENT_IOC_SET_BPF, prog_fd).map_err(|(_, io_error)| {
ProgramError::SyscallError { ProgramError::SyscallError {
call: "PERF_EVENT_IOC_SET_BPF".to_owned(), call: "PERF_EVENT_IOC_SET_BPF".to_owned(),
@ -77,12 +115,9 @@ fn perf_attach_either<T: Link + From<PerfLink>>(
} }
})?; })?;
data.links.insert( Ok(PerfLinkInner::PerfLink(PerfLink {
PerfLink {
perf_fd: fd, perf_fd: fd,
probe_kind, probe_kind,
event_alias, event_alias,
} }))
.into(),
)
} }

@ -13,8 +13,9 @@ use crate::{
}, },
}, },
programs::{ programs::{
links::define_link_wrapper,
load_program, perf_attach, load_program, perf_attach,
perf_attach::{PerfLink, PerfLinkId}, perf_attach::{PerfLinkIdInner, PerfLinkInner},
ProgramData, ProgramError, ProgramData, ProgramError,
}, },
sys::perf_event_open, sys::perf_event_open,
@ -119,7 +120,7 @@ pub enum PerfEventScope {
#[derive(Debug)] #[derive(Debug)]
#[doc(alias = "BPF_PROG_TYPE_PERF_EVENT")] #[doc(alias = "BPF_PROG_TYPE_PERF_EVENT")]
pub struct PerfEvent { pub struct PerfEvent {
pub(crate) data: ProgramData<PerfLink>, pub(crate) data: ProgramData<PerfEventLink>,
} }
impl PerfEvent { impl PerfEvent {
@ -141,7 +142,7 @@ impl PerfEvent {
config: u64, config: u64,
scope: PerfEventScope, scope: PerfEventScope,
sample_policy: SamplePolicy, sample_policy: SamplePolicy,
) -> Result<PerfLinkId, ProgramError> { ) -> Result<PerfEventLinkId, ProgramError> {
let (sample_period, sample_frequency) = match sample_policy { let (sample_period, sample_frequency) = match sample_policy {
SamplePolicy::Period(period) => (period, None), SamplePolicy::Period(period) => (period, None),
SamplePolicy::Frequency(frequency) => (0, Some(frequency)), SamplePolicy::Frequency(frequency) => (0, Some(frequency)),
@ -168,13 +169,14 @@ impl PerfEvent {
io_error, io_error,
})? as i32; })? as i32;
perf_attach(&mut self.data, fd) let link = perf_attach(self.data.fd_or_err()?, fd)?;
self.data.links.insert(PerfEventLink::new(link))
} }
/// Detaches the program. /// Detaches the program.
/// ///
/// See [PerfEvent::attach]. /// See [PerfEvent::attach].
pub fn detach(&mut self, link_id: PerfLinkId) -> Result<(), ProgramError> { pub fn detach(&mut self, link_id: PerfEventLinkId) -> Result<(), ProgramError> {
self.data.links.remove(link_id) self.data.links.remove(link_id)
} }
@ -182,7 +184,16 @@ impl PerfEvent {
/// ///
/// The link will be detached on `Drop` and the caller is now responsible /// The link will be detached on `Drop` and the caller is now responsible
/// for managing its lifetime. /// for managing its lifetime.
pub fn take_link(&mut self, link_id: PerfLinkId) -> Result<PerfLink, ProgramError> { pub fn take_link(&mut self, link_id: PerfEventLinkId) -> Result<PerfEventLink, ProgramError> {
self.data.take_link(link_id) self.data.take_link(link_id)
} }
} }
define_link_wrapper!(
/// The link used by [PerfEvent] programs.
PerfEventLink,
/// The type returned by [PerfEvent::attach]. Can be passed to [PerfEvent::detach].
PerfEventLinkId,
PerfLinkInner,
PerfLinkIdInner
);

@ -8,7 +8,7 @@ use std::{
use crate::{ use crate::{
programs::{ programs::{
kprobe::KProbeError, perf_attach, perf_attach::PerfLink, perf_attach_debugfs, kprobe::KProbeError, perf_attach, perf_attach::PerfLinkInner, perf_attach_debugfs,
trace_point::read_sys_fs_trace_point_id, uprobe::UProbeError, utils::find_tracefs_path, trace_point::read_sys_fs_trace_point_id, uprobe::UProbeError, utils::find_tracefs_path,
Link, ProgramData, ProgramError, Link, ProgramData, ProgramError,
}, },
@ -37,7 +37,7 @@ impl ProbeKind {
} }
} }
pub(crate) fn attach<T: Link + From<PerfLink>>( pub(crate) fn attach<T: Link + From<PerfLinkInner>>(
program_data: &mut ProgramData<T>, program_data: &mut ProgramData<T>,
kind: ProbeKind, kind: ProbeKind,
fn_name: &str, fn_name: &str,
@ -49,13 +49,18 @@ pub(crate) fn attach<T: Link + From<PerfLink>>(
let k_ver = kernel_version().unwrap(); let k_ver = kernel_version().unwrap();
if k_ver < (4, 17, 0) { if k_ver < (4, 17, 0) {
let (fd, event_alias) = create_as_trace_point(kind, fn_name, offset, pid)?; let (fd, event_alias) = create_as_trace_point(kind, fn_name, offset, pid)?;
let link = T::from(perf_attach_debugfs(
return perf_attach_debugfs(program_data, fd, kind, event_alias); program_data.fd_or_err()?,
fd,
kind,
event_alias,
)?);
return program_data.links.insert(link);
}; };
let fd = create_as_probe(kind, fn_name, offset, pid)?; let fd = create_as_probe(kind, fn_name, offset, pid)?;
let link = T::from(perf_attach(program_data.fd_or_err()?, fd)?);
perf_attach(program_data, fd) program_data.links.insert(link)
} }
pub(crate) fn detach_debug_fs(kind: ProbeKind, event_alias: &str) -> Result<(), ProgramError> { pub(crate) fn detach_debug_fs(kind: ProbeKind, event_alias: &str) -> Result<(), ProgramError> {

@ -6,7 +6,7 @@ use crate::{
generated::bpf_prog_type::BPF_PROG_TYPE_TRACEPOINT, generated::bpf_prog_type::BPF_PROG_TYPE_TRACEPOINT,
programs::{ programs::{
define_link_wrapper, load_program, define_link_wrapper, load_program,
perf_attach::{perf_attach, PerfLink, PerfLinkId}, perf_attach::{perf_attach, PerfLinkIdInner, PerfLinkInner},
utils::find_tracefs_path, utils::find_tracefs_path,
ProgramData, ProgramError, ProgramData, ProgramError,
}, },
@ -87,7 +87,8 @@ impl TracePoint {
} }
})? as i32; })? as i32;
perf_attach(&mut self.data, fd) let link = perf_attach(self.data.fd_or_err()?, fd)?;
self.data.links.insert(TracePointLink::new(link))
} }
/// Detaches from a trace point. /// Detaches from a trace point.
@ -111,8 +112,8 @@ define_link_wrapper!(
TracePointLink, TracePointLink,
/// The type returned by [TracePoint::attach]. Can be passed to [TracePoint::detach]. /// The type returned by [TracePoint::attach]. Can be passed to [TracePoint::detach].
TracePointLinkId, TracePointLinkId,
PerfLink, PerfLinkInner,
PerfLinkId PerfLinkIdInner
); );
pub(crate) fn read_sys_fs_trace_point_id( pub(crate) fn read_sys_fs_trace_point_id(

@ -17,7 +17,7 @@ use crate::{
generated::bpf_prog_type::BPF_PROG_TYPE_KPROBE, generated::bpf_prog_type::BPF_PROG_TYPE_KPROBE,
programs::{ programs::{
define_link_wrapper, load_program, define_link_wrapper, load_program,
perf_attach::{PerfLink, PerfLinkId}, perf_attach::{PerfLinkIdInner, PerfLinkInner},
probe::{attach, ProbeKind}, probe::{attach, ProbeKind},
ProgramData, ProgramError, ProgramData, ProgramError,
}, },
@ -154,8 +154,8 @@ define_link_wrapper!(
UProbeLink, UProbeLink,
/// The type returned by [UProbe::attach]. Can be passed to [UProbe::detach]. /// The type returned by [UProbe::attach]. Can be passed to [UProbe::detach].
UProbeLinkId, UProbeLinkId,
PerfLink, PerfLinkInner,
PerfLinkId PerfLinkIdInner
); );
/// The type returned when attaching an [`UProbe`] fails. /// The type returned when attaching an [`UProbe`] fails.

@ -599,6 +599,37 @@ pub(crate) fn is_prog_name_supported() -> bool {
} }
} }
pub(crate) fn is_perf_link_supported() -> bool {
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
let u = unsafe { &mut attr.__bindgen_anon_3 };
let prog: &[u8] = &[
0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov64 r0 = 0
0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exit
];
let gpl = b"GPL\0";
u.license = gpl.as_ptr() as u64;
let insns = copy_instructions(prog).unwrap();
u.insn_cnt = insns.len() as u32;
u.insns = insns.as_ptr() as u64;
u.prog_type = bpf_prog_type::BPF_PROG_TYPE_TRACEPOINT as u32;
if let Ok(fd) = sys_bpf(bpf_cmd::BPF_PROG_LOAD, &attr) {
if let Err((code, _)) =
// Uses an invalid target FD so we get EBADF if supported.
bpf_link_create(fd as i32, -1, bpf_attach_type::BPF_PERF_EVENT, None, 0)
{
// Returns EINVAL if unsupported. EBADF if supported.
let res = code == (-libc::EBADF).into();
unsafe { libc::close(fd as i32) };
return res;
}
}
false
}
pub(crate) fn is_btf_supported() -> bool { pub(crate) fn is_btf_supported() -> bool {
let mut btf = Btf::new(); let mut btf = Btf::new();
let name_offset = btf.add_string("int".to_string()); let name_offset = btf.add_string("int".to_string());

Loading…
Cancel
Save